From fc47633c42b681e3b2c4061a5ef6e757ae13f043 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 13 Jul 2021 11:55:46 +0300 Subject: [PATCH] Add bone armature helpers --- libs/gfx/include/psemek/gfx/armature.hpp | 121 +++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 libs/gfx/include/psemek/gfx/armature.hpp diff --git a/libs/gfx/include/psemek/gfx/armature.hpp b/libs/gfx/include/psemek/gfx/armature.hpp new file mode 100644 index 00000000..1461713d --- /dev/null +++ b/libs/gfx/include/psemek/gfx/armature.hpp @@ -0,0 +1,121 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace psemek::gfx +{ + + struct bone + { + static constexpr std::uint32_t null = static_cast(-1); + + std::uint32_t parent = null; + geom::vector offset = geom::vector::zero(); + geom::matrix axes = geom::matrix::identity(); + }; + + template + struct bone_transform + { + geom::quaternion rotation; + T scale; + geom::vector translation; + + static bone_transform identity(); + + static bone_transform from_rotation(geom::quaternion const & rotation); + static bone_transform from_scale(T const & scale); + static bone_transform from_translation(geom::vector const & translation); + + geom::matrix matrix() const; + }; + + template + bone_transform bone_transform::identity() + { + return {geom::quaternion::identity(), static_cast(1), geom::vector::zero()}; + } + + template + bone_transform bone_transform::from_rotation(geom::quaternion const & rotation) + { + return {rotation, static_cast(1), geom::vector::zero()}; + } + + template + bone_transform bone_transform::from_scale(T const & scale) + { + return {geom::quaternion::identity(), scale, geom::vector::zero()}; + } + + template + bone_transform bone_transform::from_translation(geom::vector const & translation) + { + return {geom::quaternion::identity(), static_cast(1), translation}; + } + + template + geom::matrix bone_transform::matrix() const + { + return geom::affine_transform(geom::quaternion_rotation(geom::quaternion(std::sqrt(scale) * rotation.coords)), translation).affine_matrix(); + } + + template + bone_transform operator * (bone_transform const & m1, bone_transform const & m2) + { + // (1, t1) * (S1, 0) * (R1, 0) * (1, t2) * (S2, 0) * (R2, 0) = + // (1, t1) * (S1, 0) * (1, R1 * t2) * (R1, 0) * (S2, 0) * (R2, 0) = + // (1, t1) * (1, S1 * R1 * t2) * (S1, 0) * (R1, 0) * (S2, 0) * (R2, 0) = + // (1, t1) * (1, S1 * R1 * t2) * (S1, 0) * (S2, 0) * (R1, 0) * (R2, 0) = + // (1, t1 + S1 * R1 * t2) * (S1 * S2, 0) * (R1 * R2, 0) + + return bone_transform{m1.rotation * m2.rotation, m1.scale * m2.scale, m1.translation + m1.scale * geom::rotate(m1.rotation, m2.translation)}; + } + + template + bone_transform lerp(bone_transform const & m1, bone_transform const & m2, T t) + { + return {geom::slerp(m1.rotation, m2.rotation, t), std::exp(geom::lerp(std::log(m1.scale), std::log(m2.scale), t)), geom::lerp(m1.translation, m2.translation, t)}; + } + + template + using pose = std::vector>; + + template + void compile(Pose const & local_pose, Bones const & bones, bone_transform const & transform, pose & result) + { + result.resize(local_pose.size()); + + for (std::size_t b = 0; b < bones.size(); ++b) + { + result[b] = + gfx::bone_transform{geom::quaternion::identity(), 1.f, bones[b].offset} + * gfx::bone_transform{geom::quaternion::rotation(bones[b].axes), 1.f, {0.f, 0.f, 0.f}} + * local_pose[b] + * gfx::bone_transform{geom::inverse(geom::quaternion::rotation(bones[b].axes)), 1.f, {0.f, 0.f, 0.f}} + * gfx::bone_transform{geom::quaternion::identity(), 1.f, -bones[b].offset} + ; + + auto p = bones[b].parent; + if (p == gfx::bone::null) + result[b] = transform * result[b]; + else + result[b] = result[p] * result[b]; + } + } + + template + pose compile(Pose const & local_pose, Bones const & bones, bone_transform const & transform) + { + pose result; + compile(local_pose, bones, transform, result); + return result; + } + +}