Riyyi
4 years ago
2 changed files with 369 additions and 0 deletions
@ -0,0 +1,239 @@ |
|||||||
|
#include "inferno/util/json.h" |
||||||
|
#include "nlohmann/json.hpp" |
||||||
|
|
||||||
|
#include "inferno/assert.h" |
||||||
|
#include "inferno/io/gltffile.h" |
||||||
|
#include "inferno/io/log.h" |
||||||
|
#include "inferno/render/gltf.h" |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
namespace Inferno { |
||||||
|
|
||||||
|
Gltf::Gltf(const std::string& path) |
||||||
|
: m_path(path) |
||||||
|
{ |
||||||
|
auto gltf = GlTFFile::read(path); |
||||||
|
ASSERT(gltf.first && gltf.second, "GlTF model was incomplete '{}'", path); |
||||||
|
|
||||||
|
json json = json::parse(gltf.first.get()); |
||||||
|
|
||||||
|
// Properties
|
||||||
|
|
||||||
|
// Asset
|
||||||
|
// ---------------------------------
|
||||||
|
|
||||||
|
{ |
||||||
|
ASSERT(Json::hasProperty(json, "asset"), "GlTF model missing required property 'asset'"); |
||||||
|
auto asset = json["asset"]; |
||||||
|
|
||||||
|
auto copyright = Json::parseStringProperty(asset, "copyright", false); |
||||||
|
auto generator = Json::parseStringProperty(asset, "generator", false); |
||||||
|
auto version = Json::parseStringProperty(asset, "version", true); |
||||||
|
ASSERT(version, "GlTF model missing required property 'version'"); |
||||||
|
ASSERT(version.value().compare("2.0") == 0, "GlTF version unsupported '{}'", version.value()); |
||||||
|
auto minVersion = Json::parseStringProperty(asset, "minVersion", false); |
||||||
|
|
||||||
|
if (copyright) m_model.asset.copyright = copyright.value(); |
||||||
|
if (generator) m_model.asset.generator = generator.value(); |
||||||
|
if (version) m_model.asset.version = version.value(); |
||||||
|
if (minVersion) m_model.asset.minVersion = minVersion.value(); |
||||||
|
} |
||||||
|
|
||||||
|
// Scene
|
||||||
|
// ---------------------------------
|
||||||
|
|
||||||
|
// Node
|
||||||
|
// ---------------------------------
|
||||||
|
|
||||||
|
// Mesh
|
||||||
|
// ---------------------------------
|
||||||
|
|
||||||
|
if (Json::hasProperty(json, "meshes")) { |
||||||
|
|
||||||
|
auto meshes = json["meshes"]; |
||||||
|
ASSERT(meshes.is_array(), "GlTF model property 'meshes' invalid type"); |
||||||
|
|
||||||
|
for (auto& [key, object] : meshes.items()) { |
||||||
|
glTF::Mesh mesh; |
||||||
|
parseMesh(&mesh, key, object); |
||||||
|
m_model.meshes.emplace_back(std::move(mesh)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Accessor
|
||||||
|
// ---------------------------------
|
||||||
|
|
||||||
|
if (Json::hasProperty(json, "accessors")) { |
||||||
|
|
||||||
|
auto accessors = json["accessors"]; |
||||||
|
ASSERT(accessors.is_array(), "GlTF model property 'accessors' invalid type"); |
||||||
|
|
||||||
|
for (auto& [key, object] : accessors.items()) { |
||||||
|
glTF::Accessor accessor; |
||||||
|
parseAccessor(&accessor, key, object); |
||||||
|
m_model.accessors.emplace_back(std::move(accessor)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Bufferview
|
||||||
|
// ---------------------------------
|
||||||
|
|
||||||
|
if (Json::hasProperty(json, "bufferViews")) { |
||||||
|
|
||||||
|
auto bufferViews = json["bufferViews"]; |
||||||
|
ASSERT(bufferViews.is_array(), "GlTF model property 'bufferViews' invalid type"); |
||||||
|
|
||||||
|
for (auto& [key, object] : bufferViews.items()) { |
||||||
|
glTF::BufferView bufferView; |
||||||
|
parseBufferView(&bufferView, key, object); |
||||||
|
m_model.bufferViews.emplace_back(std::move(bufferView)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Buffer
|
||||||
|
// ---------------------------------
|
||||||
|
|
||||||
|
if (Json::hasProperty(json, "buffers")) { |
||||||
|
|
||||||
|
auto buffers = json["buffers"]; |
||||||
|
ASSERT(buffers.is_array(), "GlTF model property 'buffers' invalid type"); |
||||||
|
|
||||||
|
for (auto& [key, object] : buffers.items()) { |
||||||
|
glTF::Buffer buffer; |
||||||
|
parseBuffer(&buffer, key, object); |
||||||
|
m_model.buffers.emplace_back(std::move(buffer)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Gltf::~Gltf() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void Gltf::parsePrimitive(glTF::Primitive* primitive, const std::string& key, const json& object) |
||||||
|
{ |
||||||
|
auto attributes = Json::parseUnsignedObjectProperty(object, "attributes", true); |
||||||
|
ASSERT(attributes && attributes.value().size() > 0, "GlTF primitive '{}' invalid property 'attributes'", key); |
||||||
|
auto indices = Json::parseUnsignedProperty(object, "indices", false); |
||||||
|
auto material = Json::parseUnsignedProperty(object, "material", false); |
||||||
|
auto mode = Json::parseUnsignedProperty(object, "mode", false); |
||||||
|
|
||||||
|
if (Json::hasProperty(object, "targets")) { |
||||||
|
|
||||||
|
auto targets = object["targets"]; |
||||||
|
ASSERT(targets.is_array(), "GlTF primitive '{}' property 'targets' invalid type", key); |
||||||
|
|
||||||
|
for (auto& targetObject : targets) { |
||||||
|
|
||||||
|
std::map<std::string, uint32_t> target; |
||||||
|
|
||||||
|
for (auto& [key, propertyValue] : targetObject.items()) { |
||||||
|
auto value = Json::getPropertyValue<uint32_t>(propertyValue, json::value_t::number_unsigned); |
||||||
|
if (value) target.emplace(std::move(key), value.value()); |
||||||
|
} |
||||||
|
|
||||||
|
ASSERT(target.size() > 0, "GlTF primitive '{}' empty 'target' object", key); |
||||||
|
primitive->targets.emplace_back(std::move(target)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (attributes) primitive->attributes = attributes.value(); |
||||||
|
if (indices) primitive->indices = indices.value(); |
||||||
|
if (material) primitive->material = material.value(); |
||||||
|
if (mode) primitive->mode = static_cast<unsigned char>(mode.value()); |
||||||
|
} |
||||||
|
|
||||||
|
void Gltf::parseMesh(glTF::Mesh* mesh, const std::string& key, const json& object) |
||||||
|
{ |
||||||
|
ASSERT(Json::hasProperty(object, "primitives"), "GlTF mesh '{}' missing required property 'primitives'", key); |
||||||
|
auto primitives = object["primitives"]; |
||||||
|
ASSERT(primitives.is_array(), "GlTF mesh '{}' property 'primitives' invalid type", key); |
||||||
|
for (auto& primitiveObject : primitives) { |
||||||
|
glTF::Primitive primitive; |
||||||
|
parsePrimitive(&primitive, key, primitiveObject); |
||||||
|
mesh->primitives.emplace_back(std::move(primitive)); |
||||||
|
} |
||||||
|
|
||||||
|
auto weights = Json::parseDoubleArrayProperty(object, "weights", false); |
||||||
|
ASSERT(!weights || weights.value().size() > 0, "GlTF mesh '{}' empty 'weights' property", key); |
||||||
|
|
||||||
|
auto name = Json::parseStringProperty(object, "name", false); |
||||||
|
|
||||||
|
if (weights) mesh->weights = weights.value(); |
||||||
|
mesh->name = name ? name.value() : key; |
||||||
|
} |
||||||
|
|
||||||
|
void Gltf::parseAccessor(glTF::Accessor* accessor, const std::string& key, const json& object) |
||||||
|
{ |
||||||
|
auto bufferView = Json::parseUnsignedProperty(object, "bufferView", false); |
||||||
|
|
||||||
|
auto byteOffset = Json::parseUnsignedProperty(object, "byteOffset", false); |
||||||
|
|
||||||
|
auto componentType = Json::parseUnsignedProperty(object, "componentType", true); |
||||||
|
ASSERT(componentType, "GlTF accessor '{}' missing required property 'componentType'", key); |
||||||
|
|
||||||
|
auto normalized = Json::parseBoolProperty(object, "normalized", false); |
||||||
|
|
||||||
|
auto count = Json::parseUnsignedProperty(object, "count", true); |
||||||
|
ASSERT(count, "GlTF accessor '{}' missing required property 'count'", key); |
||||||
|
|
||||||
|
auto type = Json::parseStringProperty(object, "type", true); |
||||||
|
ASSERT(type, "GlTF accessor '{}' missing required property 'type'", key); |
||||||
|
|
||||||
|
auto max = Json::parseDoubleArrayProperty(object, "max", false); |
||||||
|
ASSERT(!max || max.value().size() > 0, "GlTF accessor '{}' empty 'max' property", key); |
||||||
|
|
||||||
|
auto min = Json::parseDoubleArrayProperty(object, "min", false); |
||||||
|
ASSERT(!min || min.value().size() > 0, "GlTF accessor '{}' empty 'min' property", key); |
||||||
|
|
||||||
|
auto name = Json::parseStringProperty(object, "name", false); |
||||||
|
|
||||||
|
if (bufferView) accessor->bufferView = bufferView.value(); |
||||||
|
if (byteOffset) accessor->byteOffset = byteOffset.value(); |
||||||
|
if (normalized) accessor->normalized = normalized.value(); |
||||||
|
if (count) accessor->count = count.value(); |
||||||
|
if (type) accessor->type = type.value(); |
||||||
|
if (max) accessor->max = max.value(); |
||||||
|
if (min) accessor->min = min.value(); |
||||||
|
accessor->name = name ? name.value() : key; |
||||||
|
} |
||||||
|
|
||||||
|
void Gltf::parseBufferView(glTF::BufferView* bufferView, const std::string& key, const json& object) |
||||||
|
{ |
||||||
|
auto buffer = Json::parseUnsignedProperty(object, "buffer", false); |
||||||
|
ASSERT(buffer, "GlTF bufferView '{}' missing required property 'buffer'", key); |
||||||
|
|
||||||
|
auto byteOffset = Json::parseUnsignedProperty(object, "byteOffset", false); |
||||||
|
|
||||||
|
auto byteLength = Json::parseUnsignedProperty(object, "byteLength", true); |
||||||
|
ASSERT(byteLength, "GlTF bufferView '{}' missing required property 'byteLength'", key); |
||||||
|
|
||||||
|
auto byteStride = Json::parseUnsignedProperty(object, "byteStride", false); |
||||||
|
|
||||||
|
auto target = Json::parseUnsignedProperty(object, "target", false); |
||||||
|
|
||||||
|
auto name = Json::parseStringProperty(object, "name", false); |
||||||
|
|
||||||
|
if (buffer) bufferView->buffer = buffer.value(); |
||||||
|
if (byteOffset) bufferView->byteOffset = byteOffset.value(); |
||||||
|
if (byteLength) bufferView->byteLength = byteLength.value(); |
||||||
|
if (byteStride) bufferView->byteStride = byteStride.value(); |
||||||
|
if (target) bufferView->target = target.value(); |
||||||
|
bufferView->name = name ? name.value() : key; |
||||||
|
} |
||||||
|
|
||||||
|
void Gltf::parseBuffer(glTF::Buffer* buffer, const std::string& key, const json& object) |
||||||
|
{ |
||||||
|
auto uri = Json::parseStringProperty(object, "buffer", false); |
||||||
|
|
||||||
|
auto byteLength = Json::parseUnsignedProperty(object, "byteLength", true); |
||||||
|
ASSERT(byteLength, "GlTF buffer '{}' missing required property 'byteLength'", key); |
||||||
|
|
||||||
|
auto name = Json::parseStringProperty(object, "name", false); |
||||||
|
|
||||||
|
if (uri) buffer->uri = uri.value(); |
||||||
|
if (byteLength) buffer->byteLength = byteLength.value(); |
||||||
|
buffer->name = name ? name.value() : key; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace Inferno
|
@ -0,0 +1,130 @@ |
|||||||
|
#ifndef GLTF_H |
||||||
|
#define GLTF_H |
||||||
|
|
||||||
|
#include <cstdint> // uint32_t |
||||||
|
#include <memory> // std::shared_ptr |
||||||
|
#include <stdint.h> |
||||||
|
#include <string> // std::string |
||||||
|
#include <unordered_map> // std::unordered_map |
||||||
|
#include <vector> // std::vector |
||||||
|
|
||||||
|
#include "inferno/util/json.h" |
||||||
|
|
||||||
|
namespace Inferno { |
||||||
|
|
||||||
|
namespace glTF { |
||||||
|
|
||||||
|
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#objects
|
||||||
|
|
||||||
|
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-asset
|
||||||
|
struct Asset { |
||||||
|
std::string copyright; |
||||||
|
std::string generator; |
||||||
|
std::string version; // required
|
||||||
|
std::string minVersion; |
||||||
|
}; |
||||||
|
|
||||||
|
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#primitive
|
||||||
|
struct Primitive { |
||||||
|
std::map<std::string, uint32_t> attributes; // required
|
||||||
|
uint32_t indices; |
||||||
|
uint32_t material; |
||||||
|
unsigned char mode { 4 }; |
||||||
|
std::vector<std::map<std::string, uint32_t>> targets; |
||||||
|
}; |
||||||
|
|
||||||
|
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#mesh
|
||||||
|
struct Mesh { |
||||||
|
std::vector<Primitive> primitives; // required
|
||||||
|
std::vector<double> weights; |
||||||
|
std::string name; |
||||||
|
}; |
||||||
|
|
||||||
|
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessors
|
||||||
|
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessor
|
||||||
|
struct Accessor { |
||||||
|
uint32_t bufferView; |
||||||
|
uint32_t byteOffset { 0 }; |
||||||
|
uint32_t componentType; // required
|
||||||
|
bool normalized { false }; |
||||||
|
uint32_t count; // required
|
||||||
|
std::string type; // required
|
||||||
|
std::vector<double> max; |
||||||
|
std::vector<double> min; |
||||||
|
std::string name; |
||||||
|
}; |
||||||
|
|
||||||
|
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#bufferview
|
||||||
|
struct BufferView { |
||||||
|
uint32_t buffer; // required
|
||||||
|
uint32_t byteOffset { 0 }; |
||||||
|
uint32_t byteLength; // required
|
||||||
|
uint32_t byteStride; |
||||||
|
uint32_t target; |
||||||
|
std::string name; |
||||||
|
}; |
||||||
|
|
||||||
|
struct Buffer { |
||||||
|
std::string uri; |
||||||
|
uint32_t byteLength; // required
|
||||||
|
std::string name; |
||||||
|
}; |
||||||
|
|
||||||
|
struct Model { |
||||||
|
Asset asset; |
||||||
|
|
||||||
|
std::vector<Mesh> meshes; |
||||||
|
std::vector<Accessor> accessors; |
||||||
|
std::vector<BufferView> bufferViews; |
||||||
|
std::vector<Buffer> buffers; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace glTF
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
class Gltf { |
||||||
|
public: |
||||||
|
Gltf(const std::string& path); |
||||||
|
virtual ~Gltf(); |
||||||
|
|
||||||
|
inline const glTF::Model& model() const { return m_model; } |
||||||
|
|
||||||
|
private: |
||||||
|
static void parsePrimitive(glTF::Primitive* primitive, const std::string& key, const json& object); |
||||||
|
static void parseMesh(glTF::Mesh* mesh, const std::string& key, const json& object); |
||||||
|
static void parseAccessor(glTF::Accessor* accessor, const std::string& key, const json& object); |
||||||
|
static void parseBufferView(glTF::BufferView* bufferView, const std::string& key, const json& object); |
||||||
|
static void parseBuffer(glTF::Buffer* buffer, const std::string& key, const json& object); |
||||||
|
|
||||||
|
std::string m_path; |
||||||
|
glTF::Model m_model; |
||||||
|
}; |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
class GltfManager { |
||||||
|
public: |
||||||
|
void initialize(); |
||||||
|
void destroy(); |
||||||
|
|
||||||
|
void add(const std::string& path, const std::shared_ptr<Gltf>& gltf); |
||||||
|
std::shared_ptr<Gltf> load(const std::string& path); |
||||||
|
std::shared_ptr<Gltf> get(const std::string& path); |
||||||
|
bool exists(const std::string& path); |
||||||
|
|
||||||
|
void remove(const std::string& path); |
||||||
|
void remove(const std::shared_ptr<Gltf>& gltf); |
||||||
|
|
||||||
|
static inline GltfManager& the() { return *s_instance; } |
||||||
|
|
||||||
|
private: |
||||||
|
std::unordered_map<std::string, std::shared_ptr<Gltf>> m_gltfList; |
||||||
|
|
||||||
|
static GltfManager* s_instance; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace Inferno
|
||||||
|
|
||||||
|
|
||||||
|
#endif // GLTF_H
|
Loading…
Reference in new issue