From a7953b4bddc679658ee5713cd109fc84f0dec3a7 Mon Sep 17 00:00:00 2001 From: Riyyi Date: Sun, 7 Feb 2021 01:51:46 +0100 Subject: [PATCH] Add gltf node parsing --- inferno/src/inferno/render/gltf.cpp | 113 +++++++++++++++++++++------- inferno/src/inferno/render/gltf.h | 55 +++++++++----- 2 files changed, 121 insertions(+), 47 deletions(-) diff --git a/inferno/src/inferno/render/gltf.cpp b/inferno/src/inferno/render/gltf.cpp index 38ff640..30411e4 100644 --- a/inferno/src/inferno/render/gltf.cpp +++ b/inferno/src/inferno/render/gltf.cpp @@ -1,11 +1,11 @@ -#include "inferno/util/json.h" +#include // std::copy + #include "nlohmann/json.hpp" #include "inferno/assert.h" #include "inferno/io/gltffile.h" #include "inferno/io/log.h" #include "inferno/render/gltf.h" -#include namespace Inferno { @@ -22,22 +22,11 @@ namespace Inferno { // 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(); - } + ASSERT(Json::hasProperty(json, "asset"), "GlTF model missing required property 'asset'"); + auto asset = json["asset"]; + ASSERT(asset.is_object(), "Gltf model invalid property type 'asset'"); + + parseAsset(&m_model.asset, asset); // Scene // --------------------------------- @@ -45,7 +34,7 @@ namespace Inferno { if (Json::hasProperty(json, "scenes")) { auto scenes = json["scenes"]; - ASSERT(scenes.is_array(), "GlTF model property 'scenes' invalid type"); + ASSERT(scenes.is_array(), "Gltf model invalid property type 'scenes'"); for (auto& [key, object] : scenes.items()) { glTF::Scene scene; @@ -57,13 +46,25 @@ namespace Inferno { // Node // --------------------------------- + if (Json::hasProperty(json, "nodes")) { + + auto nodes = json["nodes"]; + ASSERT(nodes.is_array(), "Gltf model invalid property type 'nodes'"); + + for (auto& [key, object] : nodes.items()) { + glTF::Node node; + parseNode(&node, key, object); + m_model.nodes.emplace_back(std::move(node)); + } + } + // Mesh // --------------------------------- if (Json::hasProperty(json, "meshes")) { auto meshes = json["meshes"]; - ASSERT(meshes.is_array(), "GlTF model property 'meshes' invalid type"); + ASSERT(meshes.is_array(), "Gltf model invalid property type 'meshes'"); for (auto& [key, object] : meshes.items()) { glTF::Mesh mesh; @@ -78,7 +79,7 @@ namespace Inferno { if (Json::hasProperty(json, "accessors")) { auto accessors = json["accessors"]; - ASSERT(accessors.is_array(), "GlTF model property 'accessors' invalid type"); + ASSERT(accessors.is_array(), "Gltf model invalid property type 'accessors'"); for (auto& [key, object] : accessors.items()) { glTF::Accessor accessor; @@ -93,7 +94,7 @@ namespace Inferno { if (Json::hasProperty(json, "bufferViews")) { auto bufferViews = json["bufferViews"]; - ASSERT(bufferViews.is_array(), "GlTF model property 'bufferViews' invalid type"); + ASSERT(bufferViews.is_array(), "Gltf model invalid property type 'bufferViews'"); for (auto& [key, object] : bufferViews.items()) { glTF::BufferView bufferView; @@ -108,7 +109,7 @@ namespace Inferno { if (Json::hasProperty(json, "buffers")) { auto buffers = json["buffers"]; - ASSERT(buffers.is_array(), "GlTF model property 'buffers' invalid type"); + ASSERT(buffers.is_array(), "Gltf model invalid property type 'buffers'"); for (auto& [key, object] : buffers.items()) { glTF::Buffer buffer; @@ -122,6 +123,24 @@ namespace Inferno { { } + void Gltf::parseAsset(glTF::Asset* asset, const json& object) + { + auto copyright = Json::parseStringProperty(object, "copyright", false); + + auto generator = Json::parseStringProperty(object, "generator", false); + + auto version = Json::parseStringProperty(object, "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(object, "minVersion", false); + + if (copyright) asset->copyright = copyright.value(); + if (generator) asset->generator = generator.value(); + if (version) asset->version = version.value(); + if (minVersion) asset->minVersion = minVersion.value(); + } + void Gltf::parseScene(glTF::Scene* scene, const std::string& key, const json& object) { auto nodes = Json::parseDoubleArrayProperty(object, "nodes", false); @@ -132,10 +151,50 @@ namespace Inferno { scene->name = name ? name.value() : key; } + void Gltf::parseNode(glTF::Node* node, const std::string& key, const json& object) + { + auto camera = Json::parseUnsignedProperty(object, "camera", false); + + auto children = Json::parseUnsignedArrayProperty(object, "children", false); + ASSERT(!children || children.value().size() > 0, "Gltf node '{}' empty property 'children'", key); + + auto skin = Json::parseUnsignedProperty(object, "skin", false); + + auto matrix = Json::parseDoubleArrayProperty(object, "matrix", false); + ASSERT(!matrix || matrix.value().size() == 16, "Gltf node '{}' property 'matrix' invalid size", key); + + auto mesh = Json::parseUnsignedProperty(object, "mesh", false); + + auto rotation = Json::parseDoubleArrayProperty(object, "rotation", false); + ASSERT(!rotation || rotation.value().size() == 4, "Gltf node '{}' property 'rotation' invalid size", key); + + auto scale = Json::parseDoubleArrayProperty(object, "scale", false); + ASSERT(!scale || scale.value().size() == 3, "Gltf node '{}' property 'scale' invalid size", key); + + auto translation = Json::parseDoubleArrayProperty(object, "translation", false); + ASSERT(!translation || translation.value().size() == 3, "Gltf node '{}' property 'translation' invalid size", key); + + auto weights = Json::parseDoubleArrayProperty(object, "weights", false); + ASSERT(!weights || weights.value().size() > 0, "Gltf node '{}' empty property 'weights'", key); + + auto name = Json::parseStringProperty(object, "name", false); + + if (camera) node->camera = camera.value(); + if (children) node->children = children.value(); + if (skin) node->skin = skin.value(); + if (matrix) std::copy(matrix.value().begin(), matrix.value().end(), node->matrix.begin()); + if (mesh) node->mesh = mesh.value(); + if (rotation) std::copy(rotation.value().begin(), rotation.value().end(), node->rotation.begin()); + if (scale) std::copy(scale.value().begin(), scale.value().end(), node->scale.begin()); + if (translation) std::copy(translation.value().begin(), translation.value().end(), node->translation.begin()); + if (weights) node->weights = weights.value(); + node->name = name ? name.value() : key; + } + 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); + ASSERT(attributes && attributes.value().size() > 0, "Gltf primitive '{}' empty property 'attributes'", key); auto indices = Json::parseUnsignedProperty(object, "indices", false); @@ -181,7 +240,7 @@ namespace Inferno { } auto weights = Json::parseDoubleArrayProperty(object, "weights", false); - ASSERT(!weights || weights.value().size() > 0, "Gltf mesh '{}' empty 'weights' property", key); + ASSERT(!weights || weights.value().size() > 0, "Gltf mesh '{}' empty property 'weights'", key); auto name = Json::parseStringProperty(object, "name", false); @@ -207,10 +266,10 @@ namespace Inferno { 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); + ASSERT(!max || max.value().size() > 0, "Gltf accessor '{}' empty property 'max'", key); auto min = Json::parseDoubleArrayProperty(object, "min", false); - ASSERT(!min || min.value().size() > 0, "Gltf accessor '{}' empty 'min' property", key); + ASSERT(!min || min.value().size() > 0, "Gltf accessor '{}' empty property 'min'", key); auto name = Json::parseStringProperty(object, "name", false); diff --git a/inferno/src/inferno/render/gltf.h b/inferno/src/inferno/render/gltf.h index 976d739..ac56053 100644 --- a/inferno/src/inferno/render/gltf.h +++ b/inferno/src/inferno/render/gltf.h @@ -3,7 +3,6 @@ #include // uint32_t #include // std::shared_ptr -#include #include // std::string #include // std::unordered_map #include // std::vector @@ -14,57 +13,70 @@ namespace Inferno { namespace glTF { + // Type specifications // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#objects - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scene + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#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#scenes struct Scene { std::vector nodes; std::string name; }; - // 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#nodes-and-hierarchy + struct Node { + uint32_t camera; + std::vector children; + uint32_t skin; + std::array matrix { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; // Identity matrix + uint32_t mesh; + std::array rotation { 0, 0, 0, 1 }; + std::array scale { 1, 1, 1 }; + std::array translation { 0, 0, 0 }; + std::vector weights; + std::string name; }; - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#primitive struct Primitive { - std::map attributes; // required + std::map attributes; // Required uint32_t indices; uint32_t material; unsigned char mode { 4 }; std::vector> targets; }; - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#mesh + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#meshes struct Mesh { - std::vector primitives; // required + std::vector primitives; // Required std::vector 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 + uint32_t componentType; // Required bool normalized { false }; - uint32_t count; // required - std::string type; // required + uint32_t count; // Required + std::string type; // Required std::vector max; std::vector min; std::string name; }; - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#bufferview + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#buffers-and-buffer-views struct BufferView { - uint32_t buffer; // required + uint32_t buffer; // Required uint32_t byteOffset { 0 }; - uint32_t byteLength; // required + uint32_t byteLength; // Required uint32_t byteStride; uint32_t target; std::string name; @@ -72,7 +84,7 @@ namespace Inferno { struct Buffer { std::string uri; - uint32_t byteLength; // required + uint32_t byteLength; // Required std::string name; }; @@ -80,6 +92,7 @@ namespace Inferno { Asset asset; std::vector scenes; + std::vector nodes; std::vector meshes; std::vector accessors; std::vector bufferViews; @@ -98,7 +111,9 @@ namespace Inferno { inline const glTF::Model& model() const { return m_model; } private: + static void parseAsset(glTF::Asset* asset, const json& object); static void parseScene(glTF::Scene* scene, const std::string& key, const json& object); + static void parseNode(glTF::Node* node, const std::string& key, const json& object); 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);