Parse skins & animations in gltf parser

This commit is contained in:
Nikita Lisitsa 2023-03-25 19:50:32 +03:00
parent 3bb0b53daf
commit d930ef6d93
3 changed files with 185 additions and 55 deletions

View file

@ -4,6 +4,7 @@
#include <psemek/gfx/color.hpp>
#include <psemek/geom/vector.hpp>
#include <psemek/geom/quaternion.hpp>
#include <psemek/geom/easing.hpp>
#include <vector>
#include <string>
@ -19,9 +20,11 @@ namespace psemek::gfx
struct node
{
std::string name;
std::optional<std::size_t> parent;
std::optional<std::size_t> mesh;
std::optional<std::size_t> light; // KHR_lights_punctual
std::optional<std::size_t> skin;
std::vector<std::size_t> children;
std::optional<std::size_t> light; // KHR_lights_punctual
geom::vector<float, 3> translation;
geom::quaternion<float> rotation;
@ -57,12 +60,56 @@ namespace psemek::gfx
std::string uri;
};
struct skin
{
std::vector<std::size_t> joints;
std::optional<std::size_t> 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<channel> channels;
std::vector<sampler> 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<mesh> meshes;
std::vector<material> materials;
std::vector<texture> textures;
std::vector<skin> skins;
std::vector<animation> animations;
std::vector<accessor> accessors;
std::vector<buffer_view> buffer_views;
std::vector<buffer> 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);
}

View file

@ -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<void const *>(view.offset));
gl::VertexAttribPointer(attribute.first, attribute_size(accessor.type), accessor.component_type, accessor.normalized, 0, reinterpret_cast<void const *>(view.offset));
}
target_mesh.push_back({&drawable, primitive.material});

View file

@ -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<float>::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<float>::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");
}
}
}