Unravel Engine C++ Reference
Loading...
Searching...
No Matches
ik_solvers.cpp
Go to the documentation of this file.
1#include "ik_solvers.h"
4
5#include <hpp/small_vector.hpp>
6#include <algorithm>
7#include <cmath>
8#include <glm/gtc/epsilon.hpp>
9
10namespace unravel
11{
12template<typename T>
13using ik_vector = hpp::small_vector<T>;
14
15namespace
16{
17auto has_facing_correction(const math::quat& correction) -> bool
18{
19 return !math::all(math::epsilonEqual(correction,
20 math::identity<math::quat>(),
21 math::epsilon<float>()));
22}
23
26auto remap_ik_rotation_delta(const math::quat& delta, const math::quat& correction) -> math::quat
27{
28 if(!has_facing_correction(correction))
29 {
30 return delta;
31 }
32
33 return math::normalize(correction * delta * glm::conjugate(correction));
34}
35
36auto rotation_between_directions(const math::vec3& from,
37 const math::vec3& to,
38 const math::quat& correction) -> math::quat
39{
40 math::vec3 from_dir = from;
41 math::vec3 to_dir = to;
42
43 if(has_facing_correction(correction))
44 {
45 const math::quat inv_correction = glm::inverse(correction);
46 from_dir = inv_correction * from_dir;
47 to_dir = inv_correction * to_dir;
48 }
49
50 return remap_ik_rotation_delta(math::from_to_rotation(from_dir, to_dir), correction);
51}
52} // namespace
53
54auto find_facing_adjustment_rotation(entt::handle entity) -> math::quat
55{
56 entt::handle current = entity;
57 while(current)
58 {
59 if(auto* model = current.try_get<model_component>())
60 {
61 return model->get_facing_adjustment_rotation();
62 }
63
64 auto* trans = current.try_get<transform_component>();
65 if(!trans)
66 {
67 break;
68 }
69
70 current = trans->get_parent();
71 }
72
73 return math::identity<math::quat>();
74}
75
76auto bones_collect(entt::handle end_effector, size_t num_bones_in_chain) -> ik_vector<transform_component*>
77{
78 bool skinned = end_effector.all_of<bone_component>();
79
81 transform_component* current = end_effector.try_get<transform_component>();
82 chain.push_back(current);
83
84 // Collect bones from the end effector upward.
85 while(current != nullptr && chain.size() < num_bones_in_chain + 1)
86 {
87 auto parent = current->get_parent();
88 if(parent)
89 {
90 current = parent.try_get<transform_component>();
91
92 if(skinned)
93 {
94 if(auto bone = parent.try_get<bone_component>())
95 {
96 if(bone->bone_index == 0)
97 {
98 break;
99 }
100
101 chain.push_back(current);
102 }
103 }
104 else
105 {
106 chain.push_back(current);
107 }
108 }
109 else
110 {
111 break;
112 }
113 }
114 // The chain was built from end effector upward; reverse it to have it from root to end effector.
115 std::reverse(chain.begin(), chain.end());
116 return chain;
117}
118
119// Computes the global position of the bone's "end" (tip).
120auto get_end_position(transform_component* comp) -> math::vec3
121{
122 return comp->get_position_global();
123}
124
125// -----------------------------------------------------------------------------
126// Pole constraint helper.
127//
128// Pivots every intermediate joint in `positions` around the (root -> end) axis
129// so that it lies in the half-plane that contains the `pole` vector. This is
130// the canonical way to disambiguate "which side does the knee bend to" in a
131// position-based IK solver: the chain remains the same length and the end
132// effector stays on target, but the bend direction is forced to match the pole.
133//
134// Pass a zero-length pole to disable the constraint.
135// -----------------------------------------------------------------------------
136void apply_pole_constraint(ik_vector<math::vec3>& positions, const math::vec3& pole)
137{
138 if(glm::dot(pole, pole) < 1e-10f)
139 {
140 return;
141 }
142
143 const size_t n = positions.size();
144 if(n < 3)
145 {
146 return;
147 }
148
149 const math::vec3 root = positions.front();
150 const math::vec3 end = positions.back();
151
152 math::vec3 axis = end - root;
153 const float axis_len = math::length(axis);
154 if(axis_len < 1e-5f)
155 {
156 return;
157 }
158 axis /= axis_len;
159
160 // Component of the pole direction perpendicular to the root->end axis.
161 // That's the target "knee side" half-plane direction.
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)
166 {
167 // Pole is collinear with the leg: can't disambiguate. Leave chain alone.
168 return;
169 }
170 pole_perp /= pole_perp_len;
171
172 for(size_t i = 1; i + 1 < n; ++i)
173 {
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);
179 if(perp_len < 1e-5f)
180 {
181 continue;
182 }
183 // Keep the joint's distance from the root->end line but rotate it onto
184 // the pole side.
185 positions[i] = root + along_vec + pole_perp * perp_len;
186 }
187}
188
189// -----------------------------------------------------------------------------
190// Re-derive bone rotations from a set of target joint positions.
191//
192// Shared by FABRIK (after its forward/backward pass) and by CCD's pole
193// post-processing. For each bone we compute the shortest-arc rotation that
194// aligns the current bone direction with the desired direction implied by the
195// new joint positions, then convert that correction into local space and
196// compose it with the bone's existing local rotation.
197// -----------------------------------------------------------------------------
199 const ik_vector<math::vec3>& positions,
200 const math::quat& facing_correction)
201{
202 const size_t n = chain.size();
203 for(size_t i = 0; i + 1 < n; ++i)
204 {
205 transform_component* bone = chain[i];
206 transform_component* child = chain[i + 1];
207
208 const math::vec3 current_pos = bone->get_position_global();
209 const math::vec3 child_pos = child->get_position_global();
210 math::vec3 current_dir = child_pos - current_pos;
211 math::vec3 desired_dir = positions[i + 1] - positions[i];
212
213 if(math::length(current_dir) < 1e-5f || math::length(desired_dir) < 1e-5f)
214 {
215 continue;
216 }
217
218 current_dir = math::normalize(current_dir);
219 desired_dir = math::normalize(desired_dir);
220
221 const float dot = glm::clamp(glm::dot(current_dir, desired_dir), -1.f, 1.f);
222 if(dot > 0.9999f)
223 {
224 continue;
225 }
226
227 const math::quat rotation_delta =
228 rotation_between_directions(current_dir, desired_dir, facing_correction);
229
230 auto parent = bone->get_parent();
231 transform_component* parent_trans = parent ? parent.try_get<transform_component>() : nullptr;
232 const math::quat parent_global_rot =
233 (parent_trans) ? parent_trans->get_rotation_global() : math::identity<math::quat>();
234
235 const math::quat local_rotation_delta = glm::inverse(parent_global_rot) * rotation_delta * parent_global_rot;
236 bone->set_rotation_local(math::normalize(local_rotation_delta * bone->get_rotation_local()));
237 }
238}
239
240// Advanced CCD IK solver with unreachable target handling and non-linear weighting.
242 math::vec3 target,
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
249{
250 transform_component* end_effector = chain.back();
251 const size_t chain_size = chain.size();
252
253 // ----- Unreachable Target Handling -----
254
255 // We approximate bone lengths as the distance from each bone to the end effector.
256 float max_reach = 0.f;
257 for(size_t i = 0; i < chain.size() - 1; ++i)
258 {
259 math::vec3 diff = chain[i + 1]->get_position_global() - chain[i]->get_position_global();
260 max_reach += math::length(diff);
261 }
262 // Clamp the target if it lies outside the reachable sphere.
263 math::vec3 base_position = chain.front()->get_position_global();
264 math::vec3 target_dir = target - base_position;
265 float target_dist = math::length(target_dir);
266 if(target_dist > max_reach)
267 {
268 target_dir = math::normalize(target_dir);
269 target = base_position + target_dir * (max_reach - 0.001f);
270 }
271 // ------------------------------------------
272
273 // Main CCD iteration loop.
274 for(int iter = 0; iter < maxIterations; ++iter)
275 {
276 // Traverse the chain from the bone before the end effector to the root.
277 for(int i = static_cast<int>(chain_size) - 2; i >= 0; --i)
278 {
279 transform_component* bone = chain[i];
280 math::vec3 bone_pos = bone->get_position_global();
281 math::vec3 current_end_pos = get_end_position(end_effector);
282
283 // Compute direction vectors from the current bone to the end effector and target.
284 math::vec3 to_end = current_end_pos - bone_pos;
285 math::vec3 to_target = target - bone_pos;
286
287 float len_to_end = math::length(to_end);
288 float len_to_target = math::length(to_target);
289
290 // Skip this bone if either vector is degenerate.
291 if(len_to_end < math::epsilon<float>() || len_to_target < math::epsilon<float>())
292 {
293 continue;
294 }
295
296 to_end = math::normalize(to_end);
297 to_target = math::normalize(to_target);
298
299 // Calculate the angle between the current direction and the target direction.
300 float cos_angle = math::clamp(math::dot(to_end, to_target), -1.0f, 1.0f);
301 float angle = math::acos(cos_angle);
302
303 // Skip tiny adjustments.
304 if(std::fabs(angle) < 1e-3f)
305 {
306 continue;
307 }
308
309 // Determine the rotation axis in global space.
310 math::vec3 rotation_axis = math::cross(to_end, to_target);
311 if(math::length(rotation_axis) < 1e-4f)
312 {
313 continue;
314 }
315 rotation_axis = math::normalize(rotation_axis);
316
317 // ----- Dynamic Damping -----
318 // Scale the rotation angle based on the current global error.
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;
322 // ---------------------------
323
324 math::quat rotation_delta =
325 remap_ik_rotation_delta(math::angleAxis(damped_angle, rotation_axis), facing_correction);
326
327 auto parent = bone->get_parent();
328 transform_component* parent_trans = parent ? parent.try_get<transform_component>() : nullptr;
329 math::quat parent_global_rot =
330 (parent_trans) ? parent_trans->get_rotation_global() : math::identity<math::quat>();
331 math::quat local_rotation_delta = glm::inverse(parent_global_rot) * rotation_delta * parent_global_rot;
332
333 // ----- Non-Linear Weighting -----
334 // Bones closer to the end effector have more influence.
335 // Using a non-linear weighting function allows more control over influence falloff.
336 float t = float(i + 1) / float(chain_size); // Linear ratio [0,1]
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);
340 // ---------------------------------
341
342 // (Optional: Here you could enforce joint limits on the resulting rotation.)
343
344 // Update the bone's local rotation.
345 bone->set_rotation_local(math::normalize(weighted_local_rotation_delta * bone->get_rotation_local()));
346
347 // Check overall error after applying the rotation.
348 current_end_pos = get_end_position(end_effector);
349 float current_error = math::length(target - current_end_pos);
350 if(current_error < threshold)
351 {
352 // Target reached; still enforce the pole constraint below before returning.
353 iter = maxIterations;
354 break;
355 }
356 }
357 }
358
359 // Post-process: enforce the pole constraint by pivoting intermediate joints
360 // into the (root, end, pole) half-plane and re-deriving rotations.
361 if(glm::dot(pole, pole) > 1e-10f && chain_size >= 3)
362 {
363 ik_vector<math::vec3> positions(chain_size);
364 for(size_t i = 0; i < chain_size; ++i)
365 {
366 positions[i] = chain[i]->get_position_global();
367 }
368 apply_pole_constraint(positions, pole);
369 update_rotations_from_positions(chain, positions, facing_correction);
370 }
371
372 const float final_error = math::length(target - get_end_position(end_effector));
373 return final_error < threshold;
374}
375
376// FABRIK IK Solver (Advanced)
377// Uses the chain’s rest configuration to compute per‐bone rest directions,
378// then iteratively updates joint positions and finally adjusts bone rotations
379// so that each bone’s tip aligns with its new child joint position.
380//
381// The chain is provided as a vector of transform_component pointers,
382// ordered from the root (index 0) to the end effector (last element).
383//
384// NOTE: This implementation assumes that, before IK, the bone positions
385// in the chain (obtained via get_position_global()) reflect the rest pose.
386// If your system stores a separate rest offset (or tip offset), you can substitute that.
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
393{
394 const size_t n = chain.size();
395 if(n < 2)
396 return false; // Need at least two joints
397
398 // STEP 1: Capture the original (rest) joint positions.
399 ik_vector<math::vec3> orig_positions(n);
400 for(size_t i = 0; i < n; ++i)
401 {
402 orig_positions[i] = chain[i]->get_position_global();
403 }
404
405 // STEP 2: Initialize the working positions for IK.
406 ik_vector<math::vec3> positions = orig_positions;
407
408 // Compute bone lengths from the rest positions.
409 ik_vector<float> bone_lengths(n - 1, 0.f);
410 float total_length = 0.f;
411 for(size_t i = 0; i < n - 1; ++i)
412 {
413 bone_lengths[i] = math::length(orig_positions[i + 1] - orig_positions[i]);
414 total_length += bone_lengths[i];
415 }
416
417 // Store the root position.
418 const math::vec3 root_pos = positions[0];
419
420 // STEP 3: Handle unreachable target.
421 if(math::length(target - root_pos) > total_length)
422 {
423 // Target is unreachable: stretch the chain toward the target.
424 math::vec3 dir = math::normalize(target - root_pos);
425 for(size_t i = 0; i < n - 1; ++i)
426 {
427 positions[i + 1] = positions[i] + dir * bone_lengths[i];
428 }
429 }
430 else
431 {
432 // Target is reachable: perform iterative forward and backward passes.
433 for(int iter = 0; iter < max_iterations; ++iter)
434 {
435 // BACKWARD REACHING: Set the end effector to the target.
436 positions[n - 1] = target;
437 for(int i = static_cast<int>(n) - 2; i >= 0; --i)
438 {
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];
442 }
443
444 // FORWARD REACHING: Reset the root and move joints forward.
445 positions[0] = root_pos;
446 for(size_t i = 0; i < n - 1; ++i)
447 {
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];
451 }
452
453 // Check if the end effector is within threshold of the target.
454 if(math::length(positions[n - 1] - target) < threshold)
455 {
456 break;
457 }
458 }
459 }
460
461 // STEP 3.5: Enforce the pole constraint before deriving rotations.
462 // Done after position convergence so the end effector stays at the target.
463 apply_pole_constraint(positions, pole);
464
465 update_rotations_from_positions(chain, positions, facing_correction);
466
467 return true;
468}
469
470// -----------------------------------------------------------------------------
471// Analytical two-bone IK solver.
472//
473// Given three joints (start / mid / end), a target and a world-space pole, we
474// solve the knee/elbow position directly via the law of cosines. Unlike the
475// previous implementation this:
476// * Always writes the result when weight > 0 (the old version silently threw
477// away the solution whenever the target was reachable).
478// * Uses the pole vector correctly as a bending-plane hint (the old version
479// passed the pole as both the pole AND the bend axis, which are supposed
480// to be perpendicular, producing garbage).
481// * Requires no fallback to FABRIK.
482//
483// A zero-length pole keeps the mid joint in its current bending half-plane.
484// -----------------------------------------------------------------------------
486 transform_component* mid_joint,
487 transform_component* end_joint,
488 const math::vec3& target,
489 const math::vec3& pole,
490 const math::quat& facing_correction,
491 float weight,
492 float soften) -> bool
493{
494 if(weight <= 0.f)
495 {
496 return false;
497 }
498
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();
502
503 const float l1 = math::length(b - a);
504 const float l2 = math::length(c - b);
505 if(l1 < 1e-5f || l2 < 1e-5f)
506 {
507 return false;
508 }
509
510 math::vec3 at = target - a;
511 float d = math::length(at);
512 if(d < 1e-5f)
513 {
514 return false;
515 }
516
517 // Clamp target distance to the analytically solvable range. `soften` pulls
518 // the maximum reach in slightly so full extension never produces a locked
519 // knee (which tends to look bad and introduces numerical noise).
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);
524
525 const math::vec3 at_dir = at / math::length(at);
526 const math::vec3 c_new = a + at_dir * d;
527
528 // Cosine law for the hip angle between AC and AB.
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));
532
533 // Select the bend direction: pole side if provided, else keep the current
534 // bend direction to avoid popping.
535 auto perpendicularize = [&](const math::vec3& v) -> math::vec3
536 {
537 return v - glm::dot(v, at_dir) * at_dir;
538 };
539
540 math::vec3 knee_dir(0.f);
541 bool resolved = false;
542
543 if(glm::dot(pole, pole) > 1e-6f)
544 {
545 math::vec3 pp = perpendicularize(pole - a);
546 const float ppl = math::length(pp);
547 if(ppl > 1e-5f)
548 {
549 knee_dir = pp / ppl;
550 resolved = true;
551 }
552 }
553
554 if(!resolved)
555 {
556 math::vec3 cur = perpendicularize(b - a);
557 const float cl = math::length(cur);
558 if(cl > 1e-5f)
559 {
560 knee_dir = cur / cl;
561 resolved = true;
562 }
563 }
564
565 if(!resolved)
566 {
567 // Last-ditch fallback when the current pose is perfectly collinear.
568 knee_dir = math::cross(at_dir, math::vec3(0, 1, 0));
569 if(math::length(knee_dir) < 1e-5f)
570 {
571 knee_dir = math::cross(at_dir, math::vec3(1, 0, 0));
572 }
573 knee_dir = math::normalize(knee_dir);
574 }
575
576 const math::vec3 b_new = a + at_dir * (l1 * cos_a) + knee_dir * (l1 * sin_a);
577
578 // Save original local rotations so we can blend by weight.
579 const math::quat start_local_orig = start_joint->get_rotation_local();
580 const math::quat mid_local_orig = mid_joint->get_rotation_local();
581
583 chain.push_back(start_joint);
584 chain.push_back(mid_joint);
585 chain.push_back(end_joint);
586
587 ik_vector<math::vec3> positions;
588 positions.push_back(a);
589 positions.push_back(b_new);
590 positions.push_back(c_new);
591
592 update_rotations_from_positions(chain, positions, facing_correction);
593
594 if(weight < 1.f)
595 {
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)));
600 }
601
602 const float final_error = math::length(target - end_joint->get_position_global());
603 return final_error < 0.01f;
604}
605
606//--------------------------------------
607// Public API entry points.
608//--------------------------------------
609
610auto ik_set_position_ccd(entt::handle end_effector,
611 const math::vec3& target,
612 const math::vec3& pole,
613 size_t num_bones_in_chain,
614 int max_iterations,
615 float threshold) -> bool
616{
617 auto bones = bones_collect(end_effector, num_bones_in_chain);
618 const auto facing_correction = find_facing_adjustment_rotation(end_effector);
619 return ccdik_advanced(bones, target, pole, facing_correction, threshold, max_iterations);
620}
621
622auto ik_set_position_fabrik(entt::handle end_effector,
623 const math::vec3& target,
624 const math::vec3& pole,
625 size_t num_bones_in_chain,
626 int max_iterations,
627 float threshold) -> bool
628{
629 auto bones = bones_collect(end_effector, num_bones_in_chain);
630 const auto facing_correction = find_facing_adjustment_rotation(end_effector);
631 return fabrik(bones, target, pole, facing_correction, threshold, max_iterations);
632}
633
634auto ik_set_position_two_bone(entt::handle end_effector,
635 const math::vec3& target,
636 const math::vec3& pole,
637 float weight,
638 float soften) -> bool
639{
640 // The analytical two-bone solver converges in a single call. If the chain
641 // could not be built (e.g. the end effector has fewer than two parents) we
642 // fall back to FABRIK, which at least still honors the pole.
643 auto bones = bones_collect(end_effector, 2);
644 const auto facing_correction = find_facing_adjustment_rotation(end_effector);
645 if(bones.size() == 3)
646 {
647 return solve_two_bone_ik(bones[0],
648 bones[1],
649 bones[2],
650 target,
651 pole,
652 facing_correction,
653 weight,
654 soften);
655 }
656
657 return fabrik(bones, target, pole, facing_correction, 0.001f, 10);
658}
659
660auto ik_look_at_position(entt::handle end_effector, const math::vec3& target, float weight) -> bool
661{
662 auto bones = bones_collect(end_effector, 0);
663
664 auto bone = bones.front();
665
666 const auto facing_correction = find_facing_adjustment_rotation(end_effector);
667
668 // 1) compute the desired “look at” rotation
669 math::vec3 eye = bone->get_position_global();
670 math::transform lookM = math::lookAt(eye, target, bone->get_y_axis_global());
671 lookM = math::inverse(lookM);
672 math::quat desired = lookM.get_rotation();
673
674 if(has_facing_correction(facing_correction))
675 {
676 desired = math::normalize(desired * glm::inverse(facing_correction));
677 }
678
679 // 2) fetch current rotation
680 math::quat current = bone->get_rotation_global();
681
682 // 3) slerp toward desired by boneWeight
683 math::quat blended = math::slerp(current, desired, weight);
684
685 // 4) apply
686 bone->set_rotation_global(blended);
687
688 // bone->look_at(target, bone->get_y_axis_global());
689 return true;
690}
691
692auto ik_get_facing_adjustment_rotation(entt::handle end_effector) -> math::quat
693{
694 return find_facing_adjustment_rotation(end_effector);
695}
696} // namespace unravel
entt::handle b
entt::handle a
General purpose transformation class designed to maintain each component of the transformation separa...
Definition transform.hpp:27
auto get_rotation() const noexcept -> const quat_t &
Get the rotation component.
Class that contains core data for meshes.
Structure describing a LOD group (set of meshes), LOD transitions, and their materials.
Definition model.h:275
Component that handles transformations (position, rotation, scale, etc.) in the ACE framework.
auto get_rotation_local() const noexcept -> const math::quat &
Gets the local rotation.
auto get_position_global() const noexcept -> const math::vec3 &
TRANSLATION.
auto get_rotation_global() const noexcept -> const math::quat &
ROTATION.
void set_rotation_local(const math::quat &rotation) noexcept
Sets the local rotation.
auto get_parent() const noexcept -> entt::handle
RELATIONSHIP.
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 * >
entt::handle entity