From f7d11073936edbb9f409c2537597fb2f3a8cabf8 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 19 Jun 2016 18:15:32 +0900 Subject: [PATCH] Initial support of `animation`. --- README.md | 2 +- loader_example.cc | 164 +++++++++++++++++++++++++++++++-------------- tiny_gltf_loader.h | 159 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 272 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 7ad6c58..5d6a945 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ ## TODOs * [ ] Support multiple scenes in `.gltf` -* [ ] Parse `animation`, `sampler` +* [ ] Parse `sampler` * [ ] Compression/decompression(Open3DGC, etc) * [ ] Support `extensions` and `extras` property * [ ] HDR image? diff --git a/loader_example.cc b/loader_example.cc index e79edf7..09e523b 100644 --- a/loader_example.cc +++ b/loader_example.cc @@ -2,8 +2,8 @@ #define STB_IMAGE_IMPLEMENTATION #include "tiny_gltf_loader.h" -#include #include +#include #include static std::string GetFilePathExtension(const std::string &FileName) { @@ -179,7 +179,7 @@ static std::string Indent(int indent) { return s; } -static std::string PrintParameterValue(const tinygltf::Parameter& param) { +static std::string PrintParameterValue(const tinygltf::Parameter ¶m) { if (!param.number_array.empty()) { return PrintFloatArray(param.number_array); } else { @@ -222,16 +222,14 @@ static void DumpStringMap(const std::map &map, std::map::const_iterator it(map.begin()); std::map::const_iterator itEnd(map.end()); for (; it != itEnd; it++) { - std::cout << Indent(indent) << it->first << ": " << it->second - << std::endl; + std::cout << Indent(indent) << it->first << ": " << it->second << std::endl; } } static void DumpPrimitive(const tinygltf::Primitive &primitive, int indent) { std::cout << Indent(indent) << "material : " << primitive.material << std::endl; - std::cout << Indent(indent) << "indices : " << primitive.indices - << std::endl; + std::cout << Indent(indent) << "indices : " << primitive.indices << std::endl; std::cout << Indent(indent) << "mode : " << PrintMode(primitive.mode) << "(" << primitive.mode << ")" << std::endl; std::cout << Indent(indent) @@ -242,16 +240,13 @@ static void DumpPrimitive(const tinygltf::Primitive &primitive, int indent) { static void DumpTechniqueParameter(const tinygltf::TechniqueParameter ¶m, int indent) { - std::cout << Indent(indent) << "count : " << param.count - << std::endl; - std::cout << Indent(indent) << "node : " << param.node - << std::endl; - std::cout << Indent(indent) << "semantic : " << param.semantic - << std::endl; + std::cout << Indent(indent) << "count : " << param.count << std::endl; + std::cout << Indent(indent) << "node : " << param.node << std::endl; + std::cout << Indent(indent) << "semantic : " << param.semantic << std::endl; std::cout << Indent(indent) << "type : " << PrintParameterType(param.type) << std::endl; - std::cout << Indent(indent) << "value : " - << PrintParameterValue(param.value) << std::endl; + std::cout << Indent(indent) + << "value : " << PrintParameterValue(param.value) << std::endl; } static void Dump(const tinygltf::Scene &scene) { @@ -288,8 +283,10 @@ static void Dump(const tinygltf::Scene &scene) { } { - std::map::const_iterator it(scene.meshes.begin()); - std::map::const_iterator itEnd(scene.meshes.end()); + std::map::const_iterator it( + scene.meshes.begin()); + std::map::const_iterator itEnd( + scene.meshes.end()); std::cout << "meshes(item=" << scene.meshes.size() << ")" << std::endl; for (; it != itEnd; it++) { std::cout << Indent(1) << "name : " << it->second.name << std::endl; @@ -304,10 +301,11 @@ static void Dump(const tinygltf::Scene &scene) { } { - std::map::const_iterator it(scene.accessors.begin()); + std::map::const_iterator it( + scene.accessors.begin()); std::map::const_iterator itEnd( scene.accessors.end()); - std::cout << "accessos(items=" << scene.accessors.size() << ")" + std::cout << "accessors(items=" << scene.accessors.size() << ")" << std::endl; for (; it != itEnd; it++) { std::cout << Indent(1) << "name : " << it->first << std::endl; @@ -343,6 +341,64 @@ static void Dump(const tinygltf::Scene &scene) { } } + { + std::map::const_iterator it( + scene.animations.begin()); + std::map::const_iterator itEnd( + scene.animations.end()); + std::cout << "animations(items=" << scene.animations.size() << ")" + << std::endl; + for (; it != itEnd; it++) { + std::cout << Indent(1) << "name : " << it->first << std::endl; + + std::cout << Indent(1) << "channels : [ " << std::endl; + for (size_t i = 0; i < it->second.channels.size(); i++) { + std::cout << Indent(2) + << "sampler : " << it->second.channels[i].sampler + << std::endl; + std::cout << Indent(2) + << "target.id : " << it->second.channels[i].target_id + << std::endl; + std::cout << Indent(2) + << "target.path : " << it->second.channels[i].target_path + << std::endl; + std::cout << ((i != (it->second.channels.size() - 1)) ? " , " : ""); + } + std::cout << " ]" << std::endl; + + std::map::const_iterator + samplerIt(it->second.samplers.begin()); + std::map::const_iterator + samplerItEnd(it->second.samplers.end()); + std::cout << Indent(1) << "samplers(items=" << it->second.samplers.size() + << ")" << std::endl; + for (; samplerIt != samplerItEnd; samplerIt++) { + std::cout << Indent(1) << "name : " << samplerIt->first + << std::endl; + std::cout << Indent(2) << "input : " << samplerIt->second.input + << std::endl; + std::cout << Indent(2) + << "interpolation : " << samplerIt->second.interpolation + << std::endl; + std::cout << Indent(2) << "output : " << samplerIt->second.output + << std::endl; + } + + { + std::cout << Indent(1) + << "parameters(items=" << it->second.parameters.size() << ")" + << std::endl; + tinygltf::ParameterMap::const_iterator p(it->second.parameters.begin()); + tinygltf::ParameterMap::const_iterator pEnd( + it->second.parameters.end()); + for (; p != pEnd; p++) { + std::cout << Indent(2) << p->first << ": " + << PrintParameterValue(p->second) << std::endl; + } + } + } + } + { std::map::const_iterator it( scene.bufferViews.begin()); @@ -358,14 +414,17 @@ static void Dump(const tinygltf::Scene &scene) { << std::endl; std::cout << Indent(2) << "byteOffset : " << it->second.byteOffset << std::endl; - std::cout << Indent(2) << "target : " << PrintTarget(it->second.target) + std::cout << Indent(2) + << "target : " << PrintTarget(it->second.target) << std::endl; } } { - std::map::const_iterator it(scene.buffers.begin()); - std::map::const_iterator itEnd(scene.buffers.end()); + std::map::const_iterator it( + scene.buffers.begin()); + std::map::const_iterator itEnd( + scene.buffers.end()); std::cout << "buffers(items=" << scene.buffers.size() << ")" << std::endl; for (; it != itEnd; it++) { std::cout << Indent(1) << "name : " << it->first << std::endl; @@ -375,7 +434,8 @@ static void Dump(const tinygltf::Scene &scene) { } { - std::map::const_iterator it(scene.materials.begin()); + std::map::const_iterator it( + scene.materials.begin()); std::map::const_iterator itEnd( scene.materials.end()); std::cout << "materials(items=" << scene.materials.size() << ")" @@ -397,8 +457,10 @@ static void Dump(const tinygltf::Scene &scene) { } { - std::map::const_iterator it(scene.nodes.begin()); - std::map::const_iterator itEnd(scene.nodes.end()); + std::map::const_iterator it( + scene.nodes.begin()); + std::map::const_iterator itEnd( + scene.nodes.end()); std::cout << "nodes(items=" << scene.nodes.size() << ")" << std::endl; for (; it != itEnd; it++) { std::cout << Indent(1) << "name : " << it->first << std::endl; @@ -408,8 +470,10 @@ static void Dump(const tinygltf::Scene &scene) { } { - std::map::const_iterator it(scene.images.begin()); - std::map::const_iterator itEnd(scene.images.end()); + std::map::const_iterator it( + scene.images.begin()); + std::map::const_iterator itEnd( + scene.images.end()); std::cout << "images(items=" << scene.images.size() << ")" << std::endl; for (; it != itEnd; it++) { std::cout << Indent(1) << "name : " << it->first << std::endl; @@ -424,8 +488,10 @@ static void Dump(const tinygltf::Scene &scene) { } { - std::map::const_iterator it(scene.textures.begin()); - std::map::const_iterator itEnd(scene.textures.end()); + std::map::const_iterator it( + scene.textures.begin()); + std::map::const_iterator itEnd( + scene.textures.end()); std::cout << "textures(items=" << scene.textures.size() << ")" << std::endl; for (; it != itEnd; it++) { std::cout << Indent(1) << "name : " << it->first << std::endl; @@ -446,15 +512,16 @@ static void Dump(const tinygltf::Scene &scene) { { std::map::const_iterator it( - scene.shaders.begin()); + scene.shaders.begin()); std::map::const_iterator itEnd( - scene.shaders.end()); + scene.shaders.end()); std::cout << "shaders(items=" << scene.shaders.size() << ")" << std::endl; for (; it != itEnd; it++) { std::cout << Indent(1) << "name (id) : " << it->first << std::endl; - std::cout << Indent(2) << "type : " - << PrintShaderType(it->second.type) << std::endl; + std::cout << Indent(2) + << "type : " << PrintShaderType(it->second.type) + << std::endl; std::cout << Indent(2) << "name (json) : " << it->second.name << std::endl; @@ -464,9 +531,9 @@ static void Dump(const tinygltf::Scene &scene) { shader_source.resize(shader_source.size() + it->second.source.size()); std::vector::const_iterator sourceIt( - it->second.source.begin()); + it->second.source.begin()); std::vector::const_iterator sourceItEnd( - it->second.source.end()); + it->second.source.end()); for (; sourceIt != sourceItEnd; ++sourceIt) { shader_source += *sourceIt; @@ -474,16 +541,16 @@ static void Dump(const tinygltf::Scene &scene) { shader_source += Indent(3); } } - std::cout << Indent(2) << "source :\n" << shader_source - << std::endl; + std::cout << Indent(2) << "source :\n" + << shader_source << std::endl; } } { std::map::const_iterator it( - scene.programs.begin()); + scene.programs.begin()); std::map::const_iterator itEnd( - scene.programs.end()); + scene.programs.end()); std::cout << "programs(items=" << scene.programs.size() << ")" << std::endl; for (; it != itEnd; it++) { @@ -501,9 +568,9 @@ static void Dump(const tinygltf::Scene &scene) { { std::map::const_iterator it( - scene.techniques.begin()); + scene.techniques.begin()); std::map::const_iterator itEnd( - scene.techniques.end()); + scene.techniques.end()); std::cout << "techniques(items=" << scene.techniques.size() << ")" << std::endl; @@ -516,15 +583,16 @@ static void Dump(const tinygltf::Scene &scene) { std::cout << Indent(2) << "name (json) : " << it->second.name << std::endl; - std::cout << Indent(2) << "parameters(items=" - << it->second.parameters.size() << ")" << std::endl; + std::cout << Indent(2) + << "parameters(items=" << it->second.parameters.size() << ")" + << std::endl; std::map::const_iterator - paramIt(it->second.parameters.begin()); + paramIt(it->second.parameters.begin()); std::map::const_iterator - paramItEnd(it->second.parameters.end()); + paramItEnd(it->second.parameters.end()); - for(; paramIt != paramItEnd; ++paramIt) { + for (; paramIt != paramItEnd; ++paramIt) { std::cout << Indent(3) << "name : " << paramIt->first << std::endl; DumpTechniqueParameter(paramIt->second, 4); } @@ -535,13 +603,11 @@ static void Dump(const tinygltf::Scene &scene) { DumpStringMap(it->second.attributes, 3); - std::cout << Indent(2) - << "uniforms(items=" << it->second.uniforms.size() << ")" - << std::endl; + std::cout << Indent(2) << "uniforms(items=" << it->second.uniforms.size() + << ")" << std::endl; DumpStringMap(it->second.uniforms, 3); } } - } int main(int argc, char **argv) { diff --git a/tiny_gltf_loader.h b/tiny_gltf_loader.h index 0120a51..a38c5f8 100644 --- a/tiny_gltf_loader.h +++ b/tiny_gltf_loader.h @@ -31,7 +31,8 @@ // // // Version: -// - v0.9.4 Support parsing `shader`, `program` and `tecnique` thanks to @lukesanantonio +// - v0.9.4 Support parsing `shader`, `program` and `tecnique` thanks to +// @lukesanantonio // - v0.9.3 Support binary glTF // - v0.9.2 Support parsing `texture` // - v0.9.1 Support loading glTF asset from memory @@ -130,6 +131,25 @@ typedef struct { typedef std::map ParameterMap; +typedef struct { + std::string sampler; + std::string target_id; + std::string target_path; +} AnimationChannel; + +typedef struct { + std::string input; + std::string interpolation; + std::string output; +} AnimationSampler; + +typedef struct { + std::string name; + std::vector channels; + std::map samplers; + ParameterMap parameters; +} Animation; + typedef struct { std::string name; int width; @@ -278,6 +298,7 @@ class Scene { ~Scene() {} std::map accessors; + std::map animations; std::map buffers; std::map bufferViews; std::map materials; @@ -1249,8 +1270,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, if (err) { std::stringstream ss; ss << "Invalid `byteLength'. Must be equal or less than binary size: " - "`byteLength' = " << byteLength - << ", binary size = " << bin_size << std::endl; + "`byteLength' = " + << byteLength << ", binary size = " << bin_size << std::endl; (*err) += ss.str(); } return false; @@ -1720,6 +1741,121 @@ static bool ParseTechnique(Technique *technique, std::string *err, return true; } +static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err, + const picojson::object &o) { + if (!ParseStringProperty(&channel->sampler, err, o, "sampler", true)) { + if (err) { + (*err) += "`sampler` field is missing in animation channels\n"; + } + return false; + } + + picojson::object::const_iterator targetIt = o.find("target"); + if ((targetIt != o.end()) && (targetIt->second).is()) { + const picojson::object &target_object = + (targetIt->second).get(); + + if (!ParseStringProperty(&channel->target_id, err, target_object, "id", + true)) { + if (err) { + (*err) += "`id` field is missing in animation.channels.target\n"; + } + return false; + } + + if (!ParseStringProperty(&channel->target_path, err, target_object, "path", + true)) { + if (err) { + (*err) += "`path` field is missing in animation.channels.target\n"; + } + return false; + } + } + + return true; +} + +static bool ParseAnimation(Animation *animation, std::string *err, + const picojson::object &o) { + { + picojson::object::const_iterator channelsIt = o.find("channels"); + if ((channelsIt != o.end()) && (channelsIt->second).is()) { + const picojson::array &channelArray = + (channelsIt->second).get(); + for (size_t i = 0; i < channelArray.size(); i++) { + AnimationChannel channel; + if (ParseAnimationChannel(&channel, err, + channelArray[i].get())) { + // Only add the channel if the parsing succeeds. + animation->channels.push_back(channel); + } + } + } + } + + { + picojson::object::const_iterator samplerIt = o.find("samplers"); + if ((samplerIt != o.end()) && (samplerIt->second).is()) { + const picojson::object &sampler_object = + (samplerIt->second).get(); + + picojson::object::const_iterator it = sampler_object.begin(); + picojson::object::const_iterator itEnd = sampler_object.end(); + + for (; it != itEnd; it++) { + // Skip non-objects + if (!it->second.is()) continue; + + const picojson::object &s = it->second.get(); + + AnimationSampler sampler; + if (!ParseStringProperty(&sampler.input, err, s, "input", true)) { + if (err) { + (*err) += "`input` field is missing in animation.sampler\n"; + } + return false; + } + if (!ParseStringProperty(&sampler.interpolation, err, s, + "interpolation", true)) { + if (err) { + (*err) += "`interpolation` field is missing in animation.sampler\n"; + } + return false; + } + if (!ParseStringProperty(&sampler.output, err, s, "output", true)) { + if (err) { + (*err) += "`output` field is missing in animation.sampler\n"; + } + return false; + } + + animation->samplers[it->first] = sampler; + } + } + } + + picojson::object::const_iterator parametersIt = o.find("parameters"); + if ((parametersIt != o.end()) && + (parametersIt->second).is()) { + const picojson::object ¶meters_object = + (parametersIt->second).get(); + + picojson::object::const_iterator it(parameters_object.begin()); + picojson::object::const_iterator itEnd(parameters_object.end()); + + for (; it != itEnd; it++) { + Parameter param; + if (ParseParameterProperty(¶m, err, parameters_object, it->first, + false)) { + animation->parameters[it->first] = param; + } + } + } + ParseStringProperty(&animation->name, err, o, "name", false); + + return true; +} + bool TinyGLTFLoader::LoadFromString(Scene *scene, std::string *err, const char *str, unsigned int length, const std::string &base_dir) { @@ -2041,6 +2177,23 @@ bool TinyGLTFLoader::LoadFromString(Scene *scene, std::string *err, } } + // 14. Parse Animation + if (v.contains("animations") && v.get("animations").is()) { + const picojson::object &root = v.get("animations").get(); + + picojson::object::const_iterator it(root.begin()); + picojson::object::const_iterator itEnd(root.end()); + for (; it != itEnd; ++it) { + Animation animation; + if (!ParseAnimation(&animation, err, + (it->second).get())) { + return false; + } + + scene->animations[it->first] = animation; + } + } + return true; }