5#include <hpp/small_vector.hpp>
8#include <glm/gtc/epsilon.hpp>
17auto has_facing_correction(
const math::quat& correction) ->
bool
19 return !math::all(math::epsilonEqual(correction,
20 math::identity<math::quat>(),
21 math::epsilon<float>()));
26auto remap_ik_rotation_delta(
const math::quat& delta,
const math::quat& correction) -> math::quat
28 if(!has_facing_correction(correction))
33 return math::normalize(correction * delta * glm::conjugate(correction));
36auto rotation_between_directions(
const math::vec3& from,
38 const math::quat& correction) -> math::quat
40 math::vec3 from_dir = from;
41 math::vec3 to_dir = to;
43 if(has_facing_correction(correction))
45 const math::quat inv_correction = glm::inverse(correction);
46 from_dir = inv_correction * from_dir;
47 to_dir = inv_correction * to_dir;
56 entt::handle current =
entity;
61 return model->get_facing_adjustment_rotation();
73 return math::identity<math::quat>();
82 chain.push_back(current);
85 while(current !=
nullptr && chain.size() < num_bones_in_chain + 1)
96 if(bone->bone_index == 0)
101 chain.push_back(current);
106 chain.push_back(current);
115 std::reverse(chain.begin(), chain.end());
122 return comp->get_position_global();
138 if(glm::dot(pole, pole) < 1e-10f)
143 const size_t n = positions.size();
149 const math::vec3 root = positions.front();
150 const math::vec3 end = positions.back();
152 math::vec3 axis = end - root;
153 const float axis_len = math::length(axis);
162 math::vec3 pole_dir = pole - root;
163 math::vec3 pole_perp = pole_dir - glm::dot(pole_dir, axis) * axis;
164 const float pole_perp_len = math::length(pole_perp);
165 if(pole_perp_len < 1e-5f)
170 pole_perp /= pole_perp_len;
172 for(
size_t i = 1; i + 1 < n; ++i)
174 const math::vec3 rel = positions[i] - root;
175 const float along = glm::dot(rel, axis);
176 const math::vec3 along_vec = along * axis;
177 const math::vec3 perp = rel - along_vec;
178 const float perp_len = math::length(perp);
185 positions[i] = root + along_vec + pole_perp * perp_len;
200 const math::quat& facing_correction)
202 const size_t n = chain.size();
203 for(
size_t i = 0; i + 1 < n; ++i)
210 math::vec3 current_dir = child_pos - current_pos;
211 math::vec3 desired_dir = positions[i + 1] - positions[i];
213 if(math::length(current_dir) < 1e-5f || math::length(desired_dir) < 1e-5f)
218 current_dir = math::normalize(current_dir);
219 desired_dir = math::normalize(desired_dir);
221 const float dot = glm::clamp(glm::dot(current_dir, desired_dir), -1.f, 1.f);
227 const math::quat rotation_delta =
228 rotation_between_directions(current_dir, desired_dir, facing_correction);
232 const math::quat parent_global_rot =
235 const math::quat local_rotation_delta = glm::inverse(parent_global_rot) * rotation_delta * parent_global_rot;
243 const math::vec3& pole,
244 const math::quat& facing_correction,
245 float threshold = 0.001f,
246 int maxIterations = 10,
247 float damping_error_threshold = 0.5f,
248 float weight_exponent = 1.0f) ->
bool
251 const size_t chain_size = chain.size();
256 float max_reach = 0.f;
257 for(
size_t i = 0; i < chain.size() - 1; ++i)
259 math::vec3 diff = chain[i + 1]->get_position_global() - chain[i]->get_position_global();
260 max_reach += math::length(diff);
264 math::vec3 target_dir = target - base_position;
265 float target_dist = math::length(target_dir);
266 if(target_dist > max_reach)
268 target_dir = math::normalize(target_dir);
269 target = base_position + target_dir * (max_reach - 0.001f);
274 for(
int iter = 0; iter < maxIterations; ++iter)
277 for(
int i =
static_cast<int>(chain_size) - 2; i >= 0; --i)
284 math::vec3 to_end = current_end_pos - bone_pos;
285 math::vec3 to_target = target - bone_pos;
287 float len_to_end = math::length(to_end);
288 float len_to_target = math::length(to_target);
291 if(len_to_end < math::epsilon<float>() || len_to_target < math::epsilon<float>())
296 to_end = math::normalize(to_end);
297 to_target = math::normalize(to_target);
300 float cos_angle = math::clamp(math::dot(to_end, to_target), -1.0f, 1.0f);
301 float angle = math::acos(cos_angle);
304 if(std::fabs(angle) < 1e-3f)
310 math::vec3 rotation_axis = math::cross(to_end, to_target);
311 if(math::length(rotation_axis) < 1e-4f)
315 rotation_axis = math::normalize(rotation_axis);
319 float global_error = math::length(target - current_end_pos);
320 float damping_factor = math::clamp(global_error / damping_error_threshold, 0.0f, 1.0f);
321 float damped_angle = angle * damping_factor;
324 math::quat rotation_delta =
325 remap_ik_rotation_delta(math::angleAxis(damped_angle, rotation_axis), facing_correction);
329 math::quat parent_global_rot =
331 math::quat local_rotation_delta = glm::inverse(parent_global_rot) * rotation_delta * parent_global_rot;
336 float t = float(i + 1) / float(chain_size);
337 float weight = std::pow(t, weight_exponent);
338 math::quat weighted_local_rotation_delta =
339 glm::slerp(math::identity<math::quat>(), local_rotation_delta, weight);
349 float current_error = math::length(target - current_end_pos);
350 if(current_error < threshold)
353 iter = maxIterations;
361 if(glm::dot(pole, pole) > 1e-10f && chain_size >= 3)
364 for(
size_t i = 0; i < chain_size; ++i)
366 positions[i] = chain[i]->get_position_global();
372 const float final_error = math::length(target -
get_end_position(end_effector));
373 return final_error < threshold;
388 const math::vec3& target,
389 const math::vec3& pole,
390 const math::quat& facing_correction,
391 float threshold = 0.001f,
392 int max_iterations = 10) ->
bool
394 const size_t n = chain.size();
400 for(
size_t i = 0; i < n; ++i)
402 orig_positions[i] = chain[i]->get_position_global();
410 float total_length = 0.f;
411 for(
size_t i = 0; i < n - 1; ++i)
413 bone_lengths[i] = math::length(orig_positions[i + 1] - orig_positions[i]);
414 total_length += bone_lengths[i];
418 const math::vec3 root_pos = positions[0];
421 if(math::length(target - root_pos) > total_length)
424 math::vec3 dir = math::normalize(target - root_pos);
425 for(
size_t i = 0; i < n - 1; ++i)
427 positions[i + 1] = positions[i] + dir * bone_lengths[i];
433 for(
int iter = 0; iter < max_iterations; ++iter)
436 positions[n - 1] = target;
437 for(
int i =
static_cast<int>(n) - 2; i >= 0; --i)
439 float r = math::length(positions[i + 1] - positions[i]);
440 float lambda = bone_lengths[i] / r;
441 positions[i] = (1 - lambda) * positions[i + 1] + lambda * positions[i];
445 positions[0] = root_pos;
446 for(
size_t i = 0; i < n - 1; ++i)
448 float r = math::length(positions[i + 1] - positions[i]);
449 float lambda = bone_lengths[i] / r;
450 positions[i + 1] = (1 - lambda) * positions[i] + lambda * positions[i + 1];
454 if(math::length(positions[n - 1] - target) < threshold)
488 const math::vec3& target,
489 const math::vec3& pole,
490 const math::quat& facing_correction,
492 float soften) ->
bool
499 const math::vec3
a = start_joint->get_position_global();
500 const math::vec3
b = mid_joint->get_position_global();
501 const math::vec3 c = end_joint->get_position_global();
503 const float l1 = math::length(
b -
a);
504 const float l2 = math::length(c -
b);
505 if(l1 < 1e-5f || l2 < 1e-5f)
510 math::vec3 at = target -
a;
511 float d = math::length(at);
520 const float soft_t = glm::clamp(soften, 0.f, 1.f);
521 const float max_d = (l1 + l2) * (1.f - 0.001f * soft_t);
522 const float min_d = std::max(std::fabs(l1 - l2) * 1.001f, 1e-4f);
523 d = glm::clamp(d, min_d, max_d);
525 const math::vec3 at_dir = at / math::length(at);
526 const math::vec3 c_new =
a + at_dir * d;
529 float cos_a = (l1 * l1 + d * d - l2 * l2) / (2.f * l1 * d);
530 cos_a = glm::clamp(cos_a, -1.f, 1.f);
531 const float sin_a = std::sqrt(std::max(0.f, 1.f - cos_a * cos_a));
535 auto perpendicularize = [&](
const math::vec3&
v) -> math::vec3
537 return v - glm::dot(
v, at_dir) * at_dir;
540 math::vec3 knee_dir(0.f);
541 bool resolved =
false;
543 if(glm::dot(pole, pole) > 1e-6f)
545 math::vec3 pp = perpendicularize(pole -
a);
546 const float ppl = math::length(pp);
556 math::vec3 cur = perpendicularize(
b -
a);
557 const float cl = math::length(cur);
568 knee_dir = math::cross(at_dir, math::vec3(0, 1, 0));
569 if(math::length(knee_dir) < 1e-5f)
571 knee_dir = math::cross(at_dir, math::vec3(1, 0, 0));
573 knee_dir = math::normalize(knee_dir);
576 const math::vec3 b_new =
a + at_dir * (l1 * cos_a) + knee_dir * (l1 * sin_a);
579 const math::quat start_local_orig = start_joint->get_rotation_local();
580 const math::quat mid_local_orig = mid_joint->get_rotation_local();
583 chain.push_back(start_joint);
584 chain.push_back(mid_joint);
585 chain.push_back(end_joint);
588 positions.push_back(
a);
589 positions.push_back(b_new);
590 positions.push_back(c_new);
596 start_joint->set_rotation_local(
597 math::normalize(glm::slerp(start_local_orig, start_joint->get_rotation_local(), weight)));
598 mid_joint->set_rotation_local(
599 math::normalize(glm::slerp(mid_local_orig, mid_joint->get_rotation_local(), weight)));
602 const float final_error = math::length(target - end_joint->get_position_global());
603 return final_error < 0.01f;
611 const math::vec3& target,
612 const math::vec3& pole,
613 size_t num_bones_in_chain,
615 float threshold) ->
bool
617 auto bones =
bones_collect(end_effector, num_bones_in_chain);
619 return ccdik_advanced(bones, target, pole, facing_correction, threshold, max_iterations);
623 const math::vec3& target,
624 const math::vec3& pole,
625 size_t num_bones_in_chain,
627 float threshold) ->
bool
629 auto bones =
bones_collect(end_effector, num_bones_in_chain);
631 return fabrik(bones, target, pole, facing_correction, threshold, max_iterations);
635 const math::vec3& target,
636 const math::vec3& pole,
638 float soften) ->
bool
645 if(bones.size() == 3)
657 return fabrik(bones, target, pole, facing_correction, 0.001f, 10);
664 auto bone = bones.front();
669 math::vec3 eye = bone->get_position_global();
670 math::transform lookM = math::lookAt(eye, target, bone->get_y_axis_global());
674 if(has_facing_correction(facing_correction))
676 desired = math::normalize(desired * glm::inverse(facing_correction));
680 math::quat current = bone->get_rotation_global();
683 math::quat blended = math::slerp(current, desired, weight);
686 bone->set_rotation_global(blended);
Class that contains core data for meshes.
Structure describing a LOD group (set of meshes), LOD transitions, and their materials.
auto from_to_rotation(const glm::vec3 &from, const glm::vec3 &to) -> glm::quat
auto inverse(transform_t< T, Q > const &t) noexcept -> transform_t< T, Q >
auto solve_two_bone_ik(transform_component *start_joint, transform_component *mid_joint, transform_component *end_joint, const math::vec3 &target, const math::vec3 &pole, const math::quat &facing_correction, float weight, float soften) -> bool
void apply_pole_constraint(ik_vector< math::vec3 > &positions, const math::vec3 &pole)
auto ik_set_position_two_bone(entt::handle end_effector, const math::vec3 &target, const math::vec3 &pole, float weight, float soften) -> bool
auto find_facing_adjustment_rotation(entt::handle entity) -> math::quat
auto ik_set_position_fabrik(entt::handle end_effector, const math::vec3 &target, const math::vec3 &pole, size_t num_bones_in_chain, int max_iterations, float threshold) -> bool
auto ccdik_advanced(ik_vector< transform_component * > &chain, math::vec3 target, const math::vec3 &pole, const math::quat &facing_correction, float threshold=0.001f, int maxIterations=10, float damping_error_threshold=0.5f, float weight_exponent=1.0f) -> bool
void update_rotations_from_positions(ik_vector< transform_component * > &chain, const ik_vector< math::vec3 > &positions, const math::quat &facing_correction)
auto ik_set_position_ccd(entt::handle end_effector, const math::vec3 &target, const math::vec3 &pole, size_t num_bones_in_chain, int max_iterations, float threshold) -> bool
auto fabrik(ik_vector< transform_component * > &chain, const math::vec3 &target, const math::vec3 &pole, const math::quat &facing_correction, float threshold=0.001f, int max_iterations=10) -> bool
auto ik_look_at_position(entt::handle end_effector, const math::vec3 &target, float weight) -> bool
hpp::small_vector< T > ik_vector
auto ik_get_facing_adjustment_rotation(entt::handle end_effector) -> math::quat
Returns the armature root local rotation for the model owning this bone.
auto get_end_position(transform_component *comp) -> math::vec3
auto bones_collect(entt::handle end_effector, size_t num_bones_in_chain) -> ik_vector< transform_component * >