Add gltf node parsing
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
#include "inferno/util/json.h"
|
#include <algorithm> // std::copy
|
||||||
|
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
|
|
||||||
#include "inferno/assert.h"
|
#include "inferno/assert.h"
|
||||||
#include "inferno/io/gltffile.h"
|
#include "inferno/io/gltffile.h"
|
||||||
#include "inferno/io/log.h"
|
#include "inferno/io/log.h"
|
||||||
#include "inferno/render/gltf.h"
|
#include "inferno/render/gltf.h"
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
@@ -22,22 +22,11 @@ namespace Inferno {
|
|||||||
// Asset
|
// Asset
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
|
|
||||||
{
|
ASSERT(Json::hasProperty(json, "asset"), "GlTF model missing required property 'asset'");
|
||||||
ASSERT(Json::hasProperty(json, "asset"), "GlTF model missing required property 'asset'");
|
auto asset = json["asset"];
|
||||||
auto asset = json["asset"];
|
ASSERT(asset.is_object(), "Gltf model invalid property type 'asset'");
|
||||||
|
|
||||||
auto copyright = Json::parseStringProperty(asset, "copyright", false);
|
parseAsset(&m_model.asset, asset);
|
||||||
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
|
// Scene
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
@@ -45,7 +34,7 @@ namespace Inferno {
|
|||||||
if (Json::hasProperty(json, "scenes")) {
|
if (Json::hasProperty(json, "scenes")) {
|
||||||
|
|
||||||
auto scenes = 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()) {
|
for (auto& [key, object] : scenes.items()) {
|
||||||
glTF::Scene scene;
|
glTF::Scene scene;
|
||||||
@@ -57,13 +46,25 @@ namespace Inferno {
|
|||||||
// Node
|
// 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
|
// Mesh
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
|
|
||||||
if (Json::hasProperty(json, "meshes")) {
|
if (Json::hasProperty(json, "meshes")) {
|
||||||
|
|
||||||
auto meshes = 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()) {
|
for (auto& [key, object] : meshes.items()) {
|
||||||
glTF::Mesh mesh;
|
glTF::Mesh mesh;
|
||||||
@@ -78,7 +79,7 @@ namespace Inferno {
|
|||||||
if (Json::hasProperty(json, "accessors")) {
|
if (Json::hasProperty(json, "accessors")) {
|
||||||
|
|
||||||
auto accessors = 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()) {
|
for (auto& [key, object] : accessors.items()) {
|
||||||
glTF::Accessor accessor;
|
glTF::Accessor accessor;
|
||||||
@@ -93,7 +94,7 @@ namespace Inferno {
|
|||||||
if (Json::hasProperty(json, "bufferViews")) {
|
if (Json::hasProperty(json, "bufferViews")) {
|
||||||
|
|
||||||
auto bufferViews = 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()) {
|
for (auto& [key, object] : bufferViews.items()) {
|
||||||
glTF::BufferView bufferView;
|
glTF::BufferView bufferView;
|
||||||
@@ -108,7 +109,7 @@ namespace Inferno {
|
|||||||
if (Json::hasProperty(json, "buffers")) {
|
if (Json::hasProperty(json, "buffers")) {
|
||||||
|
|
||||||
auto buffers = 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()) {
|
for (auto& [key, object] : buffers.items()) {
|
||||||
glTF::Buffer buffer;
|
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)
|
void Gltf::parseScene(glTF::Scene* scene, const std::string& key, const json& object)
|
||||||
{
|
{
|
||||||
auto nodes = Json::parseDoubleArrayProperty(object, "nodes", false);
|
auto nodes = Json::parseDoubleArrayProperty(object, "nodes", false);
|
||||||
@@ -132,10 +151,50 @@ namespace Inferno {
|
|||||||
scene->name = name ? name.value() : key;
|
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)
|
void Gltf::parsePrimitive(glTF::Primitive* primitive, const std::string& key, const json& object)
|
||||||
{
|
{
|
||||||
auto attributes = Json::parseUnsignedObjectProperty(object, "attributes", true);
|
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);
|
auto indices = Json::parseUnsignedProperty(object, "indices", false);
|
||||||
|
|
||||||
@@ -181,7 +240,7 @@ namespace Inferno {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto weights = Json::parseDoubleArrayProperty(object, "weights", false);
|
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);
|
auto name = Json::parseStringProperty(object, "name", false);
|
||||||
|
|
||||||
@@ -207,10 +266,10 @@ namespace Inferno {
|
|||||||
ASSERT(type, "Gltf accessor '{}' missing required property 'type'", key);
|
ASSERT(type, "Gltf accessor '{}' missing required property 'type'", key);
|
||||||
|
|
||||||
auto max = Json::parseDoubleArrayProperty(object, "max", false);
|
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);
|
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);
|
auto name = Json::parseStringProperty(object, "name", false);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <cstdint> // uint32_t
|
#include <cstdint> // uint32_t
|
||||||
#include <memory> // std::shared_ptr
|
#include <memory> // std::shared_ptr
|
||||||
#include <stdint.h>
|
|
||||||
#include <string> // std::string
|
#include <string> // std::string
|
||||||
#include <unordered_map> // std::unordered_map
|
#include <unordered_map> // std::unordered_map
|
||||||
#include <vector> // std::vector
|
#include <vector> // std::vector
|
||||||
@@ -14,57 +13,70 @@ namespace Inferno {
|
|||||||
|
|
||||||
namespace glTF {
|
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#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 {
|
struct Scene {
|
||||||
std::vector<uint32_t> nodes;
|
std::vector<uint32_t> nodes;
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-asset
|
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy
|
||||||
struct Asset {
|
struct Node {
|
||||||
std::string copyright;
|
uint32_t camera;
|
||||||
std::string generator;
|
std::vector<uint32_t> children;
|
||||||
std::string version; // required
|
uint32_t skin;
|
||||||
std::string minVersion;
|
std::array<double, 16> matrix { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; // Identity matrix
|
||||||
|
uint32_t mesh;
|
||||||
|
std::array<double, 4> rotation { 0, 0, 0, 1 };
|
||||||
|
std::array<double, 3> scale { 1, 1, 1 };
|
||||||
|
std::array<double, 3> translation { 0, 0, 0 };
|
||||||
|
std::vector<double> weights;
|
||||||
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#primitive
|
|
||||||
struct Primitive {
|
struct Primitive {
|
||||||
std::map<std::string, uint32_t> attributes; // required
|
std::map<std::string, uint32_t> attributes; // Required
|
||||||
uint32_t indices;
|
uint32_t indices;
|
||||||
uint32_t material;
|
uint32_t material;
|
||||||
unsigned char mode { 4 };
|
unsigned char mode { 4 };
|
||||||
std::vector<std::map<std::string, uint32_t>> targets;
|
std::vector<std::map<std::string, uint32_t>> 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 {
|
struct Mesh {
|
||||||
std::vector<Primitive> primitives; // required
|
std::vector<Primitive> primitives; // Required
|
||||||
std::vector<double> weights;
|
std::vector<double> weights;
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessors
|
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessors
|
||||||
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessor
|
|
||||||
struct Accessor {
|
struct Accessor {
|
||||||
uint32_t bufferView;
|
uint32_t bufferView;
|
||||||
uint32_t byteOffset { 0 };
|
uint32_t byteOffset { 0 };
|
||||||
uint32_t componentType; // required
|
uint32_t componentType; // Required
|
||||||
bool normalized { false };
|
bool normalized { false };
|
||||||
uint32_t count; // required
|
uint32_t count; // Required
|
||||||
std::string type; // required
|
std::string type; // Required
|
||||||
std::vector<double> max;
|
std::vector<double> max;
|
||||||
std::vector<double> min;
|
std::vector<double> min;
|
||||||
std::string name;
|
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 {
|
struct BufferView {
|
||||||
uint32_t buffer; // required
|
uint32_t buffer; // Required
|
||||||
uint32_t byteOffset { 0 };
|
uint32_t byteOffset { 0 };
|
||||||
uint32_t byteLength; // required
|
uint32_t byteLength; // Required
|
||||||
uint32_t byteStride;
|
uint32_t byteStride;
|
||||||
uint32_t target;
|
uint32_t target;
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -72,7 +84,7 @@ namespace Inferno {
|
|||||||
|
|
||||||
struct Buffer {
|
struct Buffer {
|
||||||
std::string uri;
|
std::string uri;
|
||||||
uint32_t byteLength; // required
|
uint32_t byteLength; // Required
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -80,6 +92,7 @@ namespace Inferno {
|
|||||||
Asset asset;
|
Asset asset;
|
||||||
|
|
||||||
std::vector<Scene> scenes;
|
std::vector<Scene> scenes;
|
||||||
|
std::vector<Node> nodes;
|
||||||
std::vector<Mesh> meshes;
|
std::vector<Mesh> meshes;
|
||||||
std::vector<Accessor> accessors;
|
std::vector<Accessor> accessors;
|
||||||
std::vector<BufferView> bufferViews;
|
std::vector<BufferView> bufferViews;
|
||||||
@@ -98,7 +111,9 @@ namespace Inferno {
|
|||||||
inline const glTF::Model& model() const { return m_model; }
|
inline const glTF::Model& model() const { return m_model; }
|
||||||
|
|
||||||
private:
|
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 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 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 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 parseAccessor(glTF::Accessor* accessor, const std::string& key, const json& object);
|
||||||
|
|||||||
Reference in New Issue
Block a user