Antkeeper  0.0.1
ccd-ik-solver.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
22 #include <engine/debug/log.hpp>
23 #include <stdexcept>
24 
26  m_ik_rig{&ik_rig}
27 {
28  // Get reference to skeleton
29  const auto& skeleton = *m_ik_rig->get_skeletal_mesh().get_pose().get_skeleton();
30 
31  // Validate and count number of bones in bone chain
32  std::size_t bone_count = 1;
33  for (bone_index_type bone_index = effector_bone_index; bone_index != root_bone_index; ++bone_count)
34  {
35  const auto parent_bone = skeleton.get_bone_parent(bone_index);
36  if (parent_bone == bone_index)
37  {
38  throw std::invalid_argument("Invalid bone chain");
39  }
40 
41  bone_index = parent_bone;
42  }
43 
44  // Allocate and store bone indices
45  m_bone_indices.resize(bone_count);
46  m_bone_indices.front() = effector_bone_index;
47  for (std::size_t i = 1; i < bone_count; ++i)
48  {
49  m_bone_indices[i] = skeleton.get_bone_parent(m_bone_indices[i - 1]);
50  }
51 }
52 
54 {
55  // Get reference to skeletal mesh and its pose from the parent IK rig
56  auto& skeletal_mesh = m_ik_rig->get_skeletal_mesh();
57  auto& pose = skeletal_mesh.get_pose();
58 
59  // Get pose-space transform of end effector bone
60  const auto& ps_effector_bone_transform = pose.get_absolute_transform(m_bone_indices.front());
61 
62  // Transform goal position into pose-space
63  const auto ps_goal_center = m_goal_center * skeletal_mesh.get_transform();
64 
65  for (std::size_t i = 0; i < m_max_iterations; ++i)
66  {
67  for (std::size_t j = 0; j < m_bone_indices.size(); ++j)
68  {
69  // Transform end effector position into pose-space
70  const auto ps_effector_position = ps_effector_bone_transform * m_effector_position;
71 
72  // Check if end effector is within goal radius
73  const auto sqr_distance = math::sqr_distance(ps_effector_position, ps_goal_center);
74  if (sqr_distance <= m_sqr_goal_radius)
75  {
76  return;
77  }
78 
79  // Get index of current bone
80  const auto bone_index = m_bone_indices[j];
81 
82  // Get pose-space and bone-space transforms of current bone
83  const auto& ps_bone_transform = pose.get_absolute_transform(bone_index);
84  const auto& bs_bone_transform = pose.get_relative_transform(bone_index);
85 
86  // Find pose-space direction vector from current bone to effector
87  const auto ps_effector_direction = math::normalize(ps_effector_position - ps_bone_transform.translation);
88 
89  // Find pose-space direction vector from current bone to center of goal
90  const auto ps_goal_direction = math::normalize(ps_goal_center - ps_bone_transform.translation);
91 
92  // Find rotation for current bone that brings effector closer to goal
93  auto bone_rotation = math::normalize(math::rotation(ps_effector_direction, ps_goal_direction, 1e-5f) * bs_bone_transform.rotation);
94 
95  // Apply current bone constraints to rotation
96  if (auto* constraint = m_ik_rig->get_constraint(bone_index))
97  {
98  constraint->solve(bone_rotation);
99  }
100 
101  // Rotate current bone
102  pose.set_relative_rotation(bone_index, bone_rotation);
103 
104  // Update pose
105  //pose.update(bone_index, j + 1);
106  pose.update();
107  }
108  }
109 }
std::uint16_t bone_index_type
Bone index type.
Definition: bone.hpp:31
ccd_ik_solver(ik_rig &ik_rig, bone_index_type root_bone_index, bone_index_type effector_bone_index)
Constructs a CCD IK solver.
void solve() override
Sets the maximum number of solving iterations.
const ik_constraint * get_constraint(bone_index_type index) const
Returns the IK constraint of a bone.
Definition: ik-rig.hpp:76
const scene::skeletal_mesh & get_skeletal_mesh() const noexcept
Returns the skeleton with which the IK rig is associated.
Definition: ik-rig.hpp:44
Base class for skeleton poses.
Definition: pose.hpp:32
const skeleton * get_skeleton() const noexcept
Returns the skeleton with which the pose is associated.
Definition: pose.hpp:104
void update()
Updates the pose after one or more relative transforms have been changed.
Definition: pose.cpp:31
const bone_transform_type & get_relative_transform(bone_index_type index) const
Returns the relative transform describing a bone pose.
Definition: pose.hpp:116
const bone_transform_type & get_absolute_transform(bone_index_type index) const
Returns the absolute transform describing a bone pose.
Definition: pose.hpp:128
void set_relative_rotation(bone_index_type index, const bone_transform_type::quaternion_type &rotation)
Sets the relative rotation of a bone pose.
Definition: pose.hpp:87
const animation_pose & get_pose() const noexcept
Returns the animation pose of the skeletal mesh.
Skeletal animation skeleton.
Definition: skeleton.hpp:35
bone_index_type get_bone_parent(bone_index_type child_index) const
Returns the index of the parent of a bone.
Definition: skeleton.hpp:160
constexpr T sqr_distance(const vector< T, N > &p0, const vector< T, N > &p1) noexcept
Calculates the square distance between two points.
Definition: vector.hpp:1514
quaternion< T > normalize(const quaternion< T > &q)
Normalizes a quaternion.
Definition: quaternion.hpp:679
quaternion< T > rotation(const vec3< T > &from, const vec3< T > &to, T tolerance=T{1e-6})
Constructs a quaternion representing the minimum rotation from one direction to another direction.
Definition: quaternion.hpp:691