diff --git a/libs/gfx/include/psemek/gfx/gltf_parser.hpp b/libs/gfx/include/psemek/gfx/gltf_parser.hpp index ce52d9a9..9ffa25bd 100644 --- a/libs/gfx/include/psemek/gfx/gltf_parser.hpp +++ b/libs/gfx/include/psemek/gfx/gltf_parser.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -19,9 +20,11 @@ namespace psemek::gfx struct node { std::string name; + std::optional parent; std::optional mesh; - std::optional light; // KHR_lights_punctual + std::optional skin; std::vector children; + std::optional light; // KHR_lights_punctual geom::vector translation; geom::quaternion rotation; @@ -57,12 +60,56 @@ namespace psemek::gfx std::string uri; }; + struct skin + { + std::vector joints; + std::optional inverse_bind_matrices; // accessor + }; + + struct animation + { + struct channel + { + std::size_t sampler; + std::size_t target; // node + + enum path_t + { + scale, + rotation, + translation, + } path; + }; + + struct sampler + { + std::size_t input; // keyframes accessor + std::size_t output; // values accessor + geom::easing_type interpolation; + }; + + std::string name; + std::vector channels; + std::vector samplers; + }; + struct accessor { std::size_t buffer_view; std::size_t component_type; std::size_t count; - std::size_t type; + + enum type_t + { + scalar, + vec2, + vec3, + vec4, + mat2, + mat3, + mat4, + } type; + bool normalized; }; @@ -83,7 +130,7 @@ namespace psemek::gfx // KHR_lights_punctual struct light { - enum + enum type_t { directional, point, @@ -100,6 +147,8 @@ namespace psemek::gfx std::vector meshes; std::vector materials; std::vector textures; + std::vector skins; + std::vector animations; std::vector accessors; std::vector buffer_views; std::vector buffers; @@ -111,4 +160,6 @@ namespace psemek::gfx gltf_asset parse_gltf(io::istream && stream); + std::size_t attribute_size(gltf_asset::accessor::type_t type); + } diff --git a/libs/gfx/source/gltf_mesh.cpp b/libs/gfx/source/gltf_mesh.cpp index b2625df1..165f5e69 100644 --- a/libs/gfx/source/gltf_mesh.cpp +++ b/libs/gfx/source/gltf_mesh.cpp @@ -120,7 +120,7 @@ namespace psemek::gfx gl::BindBuffer(gl::ARRAY_BUFFER, buffers_[view.buffer].id()); gl::EnableVertexAttribArray(attribute.first); - gl::VertexAttribPointer(attribute.first, accessor.type, accessor.component_type, accessor.normalized, 0, reinterpret_cast(view.offset)); + gl::VertexAttribPointer(attribute.first, attribute_size(accessor.type), accessor.component_type, accessor.normalized, 0, reinterpret_cast(view.offset)); } target_mesh.push_back({&drawable, primitive.material}); diff --git a/libs/gfx/source/gltf_parser.cpp b/libs/gfx/source/gltf_parser.cpp index 1775d888..a642d5de 100644 --- a/libs/gfx/source/gltf_parser.cpp +++ b/libs/gfx/source/gltf_parser.cpp @@ -45,16 +45,22 @@ namespace psemek::gfx } } - std::size_t parse_accessor_type(std::string const & type) + auto parse_accessor_type(std::string const & type) { if (type == "SCALAR") - return 1; + return gltf_asset::accessor::scalar; if (type == "VEC2") - return 2; + return gltf_asset::accessor::vec2; if (type == "VEC3") - return 3; + return gltf_asset::accessor::vec3; if (type == "VEC4") - return 4; + return gltf_asset::accessor::vec4; + if (type == "MAT2") + return gltf_asset::accessor::mat2; + if (type == "MAT3") + return gltf_asset::accessor::mat3; + if (type == "MAT4") + return gltf_asset::accessor::mat4; throw std::runtime_error(util::to_string("Unknown accessor component type: ", type)); } @@ -82,13 +88,16 @@ namespace psemek::gfx if (!supported_extensions.contains(extension.GetString())) throw std::runtime_error("glTF extension " + std::string(extension.GetString()) + " is not supported"); - for (auto const & node : document["nodes"].GetArray()) + if (document.HasMember("nodes")) for (auto const & node : document["nodes"].GetArray()) { auto & target = result.nodes.emplace_back(); target.name = node["name"].GetString(); if (node.HasMember("mesh")) target.mesh = node["mesh"].GetUint64(); + if (node.HasMember("skin")) + target.skin = node["skin"].GetUint64(); + target.translation = {0.f, 0.f, 0.f}; if (node.HasMember("translation")) { @@ -129,7 +138,7 @@ namespace psemek::gfx } } - for (auto const & mesh : document["meshes"].GetArray()) + if (document.HasMember("meshes")) for (auto const & mesh : document["meshes"].GetArray()) { auto & target = result.meshes.emplace_back(); for (auto const & primitive : mesh["primitives"].GetArray()) @@ -149,7 +158,7 @@ namespace psemek::gfx } } - for (auto const & material : document["materials"].GetArray()) + if (document.HasMember("materials")) for (auto const & material : document["materials"].GetArray()) { auto & target = result.materials.emplace_back(); @@ -189,13 +198,67 @@ namespace psemek::gfx } } - for (auto const & image : document["images"].GetArray()) + if (document.HasMember("images")) for (auto const & image : document["images"].GetArray()) { auto & target = result.textures.emplace_back(); target.uri = image["uri"].GetString(); } - for (auto const & accessor : document["accessors"].GetArray()) + if (document.HasMember("skins")) for (auto const & skin : document["skins"].GetArray()) + { + auto & target = result.skins.emplace_back(); + + for (auto const & joint : skin["joints"].GetArray()) + target.joints.push_back(joint.GetUint64()); + + if (skin.HasMember("inverseBindMatrices")) + target.inverse_bind_matrices = skin["inverseBindMatrices"].GetUint64(); + } + + if (document.HasMember("animations")) for (auto const & animation : document["animations"].GetArray()) + { + auto & target = result.animations.emplace_back(); + + if (animation.HasMember("name")) + target.name = animation["name"].GetString(); + + for (auto const & channel : animation["channels"].GetArray()) + { + auto & channel_target = target.channels.emplace_back(); + + channel_target.sampler = channel["sampler"].GetUint64(); + channel_target.target = channel["target"]["node"].GetUint64(); + std::string path{channel["target"]["path"].GetString()}; + if (path == "scale") + channel_target.path = gltf_asset::animation::channel::scale; + else if (path == "rotation") + channel_target.path = gltf_asset::animation::channel::rotation; + else if (path == "translation") + channel_target.path = gltf_asset::animation::channel::translation; + else + throw std::runtime_error("Unknown glTF animation target path: " + path); + } + + for (auto const & sampler : animation["samplers"].GetArray()) + { + auto & sampler_target = target.samplers.emplace_back(); + + sampler_target.input = sampler["input"].GetUint64(); + sampler_target.output = sampler["output"].GetUint64(); + + std::string interpolation{sampler["interpolation"].GetString()}; + if (interpolation == "STEP") + sampler_target.interpolation = geom::easing_type::constant_left; + else if (interpolation == "LINEAR") + sampler_target.interpolation = geom::easing_type::linear; + else if (interpolation == "CUBICSPLINE") + sampler_target.interpolation = geom::easing_type::cubic; + else + throw std::runtime_error("Unknown glTF animation interpolation type: " + interpolation); + } + } + + if (document.HasMember("accessors")) for (auto const & accessor : document["accessors"].GetArray()) { auto & target = result.accessors.emplace_back(); @@ -210,7 +273,7 @@ namespace psemek::gfx target.normalized = false; } - for (auto const & buffer_view : document["bufferViews"].GetArray()) + if (document.HasMember("bufferViews")) for (auto const & buffer_view : document["bufferViews"].GetArray()) { auto & target = result.buffer_views.emplace_back(); @@ -222,7 +285,7 @@ namespace psemek::gfx target.stride = buffer_view["byteStride"].GetUint64(); } - for (auto const & buffer : document["buffers"].GetArray()) + if (document.HasMember("buffers")) for (auto const & buffer : document["buffers"].GetArray()) { auto & target = result.buffers.emplace_back(); @@ -230,56 +293,59 @@ namespace psemek::gfx target.uri = buffer["uri"].GetString(); } - if (document.HasMember("extensions")) - for (auto const & extension : document["extensions"].GetObject()) + if (document.HasMember("extensions")) for (auto const & extension : document["extensions"].GetObject()) + { + if (extension.name == "KHR_lights_punctual") { - if (extension.name == "KHR_lights_punctual") + for (auto const & light : extension.value["lights"].GetArray()) { - for (auto const & light : extension.value["lights"].GetArray()) + auto & target = result.lights.emplace_back(); + target.color = {1.f, 1.f, 1.f}; + target.intensity = 1.f; + target.range = std::numeric_limits::infinity(); + target.cone_angle = {0.f, geom::pi / 4.f}; + + std::string type = light["type"].GetString(); + + if (type == "directional") + target.type = gltf_asset::light::directional; + else if (type == "point") + target.type = gltf_asset::light::point; + else if (type == "spot") + target.type = gltf_asset::light::spot; + else + throw std::runtime_error("unknown light type: " + type); + + if (light.HasMember("color")) { - auto & target = result.lights.emplace_back(); - target.color = {1.f, 1.f, 1.f}; - target.intensity = 1.f; - target.range = std::numeric_limits::infinity(); - target.cone_angle = {0.f, geom::pi / 4.f}; + auto const & color = light["color"].GetArray(); + for (std::size_t i = 0; i < 3; ++i) + target.color[i] = color[i].GetFloat(); + } - std::string type = light["type"].GetString(); + if (light.HasMember("intensity")) + target.intensity = light["intensity"].GetFloat(); - if (type == "directional") - target.type = gltf_asset::light::directional; - else if (type == "point") - target.type = gltf_asset::light::point; - else if (type == "spot") - target.type = gltf_asset::light::spot; - else - throw std::runtime_error("unknown light type: " + type); + if (light.HasMember("range")) + target.range = light["range"].GetFloat(); - if (light.HasMember("color")) - { - auto const & color = light["color"].GetArray(); - for (std::size_t i = 0; i < 3; ++i) - target.color[i] = color[i].GetFloat(); - } + if (target.type == gltf_asset::light::spot) + { + auto const & spot = light["spot"].GetObject(); - if (light.HasMember("intensity")) - target.intensity = light["intensity"].GetFloat(); + if (spot.HasMember("innerConeAngle")) + target.cone_angle.min = spot["innerConeAngle"].GetFloat(); - if (light.HasMember("range")) - target.range = light["range"].GetFloat(); - - if (target.type == gltf_asset::light::spot) - { - auto const & spot = light["spot"].GetObject(); - - if (spot.HasMember("innerConeAngle")) - target.cone_angle.min = spot["innerConeAngle"].GetFloat(); - - if (spot.HasMember("outerConeAngle")) - target.cone_angle.max = spot["outerConeAngle"].GetFloat(); - } + if (spot.HasMember("outerConeAngle")) + target.cone_angle.max = spot["outerConeAngle"].GetFloat(); } } } + } + + for (std::size_t i = 0; i < result.nodes.size(); ++i) + for (auto child : result.nodes[i].children) + result.nodes[child].parent = i; for (std::size_t i = 0; i < result.nodes.size(); ++i) result.node_index[result.nodes[i].name] = i; @@ -290,4 +356,17 @@ namespace psemek::gfx return result; } + std::size_t attribute_size(gltf_asset::accessor::type_t type) + { + using type_t = gltf_asset::accessor::type_t; + switch (type) + { + case type_t::scalar: return 1; + case type_t::vec2: return 2; + case type_t::vec3: return 3; + case type_t::vec4: return 4; + default: throw std::runtime_error("unsupported attribute type"); + } + } + }