载入中...
搜索中...
未找到
ChainSolver.h
浏览该文件的文档.
1#pragma once
2
3#include "ik.hpp"
4
5#include <algorithm>
6#include <cmath>
7#include <cstddef>
8#include <vector>
9
10namespace eve::ik {
11namespace detail {
12
17template <unsigned D>
19 float tolerance = 1e-3f;
20 unsigned max_iterations = 16;
21 float force = 0.f; // 0 = hard IK; (0,1] blends the effector toward the target
22 float influence = 1.f; // 0..1 blend of the solved pose over the input pose
23
24 bool use_pole = false; // 3D only: pull middle joints toward a pole position
25 ::ik::vec3 pole{};
26 float pole_weight = 1.f;
27};
28
29namespace {
30
32template <unsigned D>
33void placeOnLine(::ik::vec<float, D>& point, const ::ik::vec<float, D>& anchor,
34 float boneLength) {
35 ::ik::vec<float, D> dir = point - anchor;
36 float len = ::ik::length(dir);
37 if (len <= 1e-8f) {
38 dir = ::ik::detail::default_forward<D>();
39 len = 1.0f;
40 }
41 point = anchor + dir * (boneLength / len);
42}
43
46template <unsigned D>
47std::vector<::ik::bone<D>*> buildChain(::ik::skeleton<D>& sk, unsigned root_id,
48 unsigned tip_id) {
49 if (sk.bones().empty()) {
50 sk.topologicalSort();
51 }
52 const auto& bones = sk.bones();
53 if (root_id >= bones.size() || tip_id >= bones.size()) {
54 return {};
55 }
56
57 std::vector<::ik::bone<D>*> chain;
58 for (::ik::bone<D>* b = bones[tip_id]; b != nullptr; b = b->parent) {
59 chain.push_back(b);
60 if (b->id == root_id) {
61 break;
62 }
63 }
64 if (chain.empty() || chain.back()->id != root_id || chain.size() < 2) {
65 return {};
66 }
67 std::reverse(chain.begin(), chain.end());
68 return chain;
69}
70
73template <unsigned D>
74void reachTarget(::ik::ecs<D>& state, const std::vector<::ik::bone<D>*>& chain,
75 unsigned tipIndex, const ::ik::vec<float, D>& desired) {
76 const ::ik::vec<float, D> root_pos = chain[0]->position(state);
77
78 float chain_length = 0.f;
79 for (unsigned i = 1; i <= tipIndex; ++i) {
80 chain_length += chain[i]->length;
81 }
82
83 if (::ik::distance(root_pos, desired) >= chain_length) {
84 const ::ik::vec<float, D> dir = ::ik::detail::safe_normalize(desired - root_pos);
85 for (unsigned i = 1; i <= tipIndex; ++i) {
86 chain[i]->position(state) = chain[i - 1]->position(state) + dir * chain[i]->length;
87 }
88 return;
89 }
90
91 // Backward reaching: place the effector and walk toward the root.
92 chain[tipIndex]->position(state) = desired;
93 for (int i = static_cast<int>(tipIndex); i >= 1; --i) {
94 placeOnLine(chain[static_cast<size_t>(i) - 1]->position(state),
95 chain[static_cast<size_t>(i)]->position(state),
96 chain[static_cast<size_t>(i)]->length);
97 }
98
99 // Forward reaching: pin the chain root.
100 chain[0]->position(state) = root_pos;
101 for (unsigned i = 1; i <= tipIndex; ++i) {
102 placeOnLine(chain[i]->position(state), chain[i - 1]->position(state),
103 chain[i]->length);
104 }
105}
106
109template <unsigned D>
110void applyPole(::ik::ecs<D>& state, const std::vector<::ik::bone<D>*>& chain,
111 const ::ik::vec<float, D>& goal, const ::ik::vec3& pole, float weight) {
112 if constexpr (D == 3) {
113 if (weight <= 0.f || chain.size() < 2) {
114 return;
115 }
116 const ::ik::vec3 root_pos = chain[0]->position(state);
117 const ::ik::vec3 to_goal = goal - root_pos;
118 const float goal_len = ::ik::length(to_goal);
119 if (goal_len <= 1e-8f) {
120 return;
121 }
122 const ::ik::vec3 axis = to_goal / goal_len;
123
124 const ::ik::vec3 to_pole = pole - root_pos;
125 const ::ik::vec3 proj = to_pole - axis * ::ik::dot(to_pole, axis);
126 if (::ik::length_squared(proj) <= 1e-8f) {
127 return; // pole lies on the root->goal axis: no bend direction to enforce
128 }
129 const ::ik::vec3 pole_dir = ::ik::detail::safe_normalize(proj);
130
131 for (size_t i = 1; i + 1 < chain.size(); ++i) {
132 const ::ik::vec3 v = chain[i]->position(state) - root_pos;
133 const float along = ::ik::dot(v, axis);
134 ::ik::vec3 radial = v - axis * along;
135 const float radial_len = ::ik::length(radial);
136 if (radial_len <= 1e-8f) {
137 continue; // joint is on the axis; there is nothing to bend
138 }
139 const ::ik::vec3 dir = radial / radial_len;
140 const ::ik::vec3 new_dir =
141 ::ik::detail::safe_normalize(dir + (pole_dir - dir) * weight);
142 chain[i]->position(state) = root_pos + axis * along + new_dir * radial_len;
143 }
144 }
145}
146
148template <unsigned D>
149void applyChainConstraints(::ik::ecs<D>& state,
150 const std::vector<::ik::bone<D>*>& chain) {
151 if (chain.size() < 2) {
152 return;
153 }
154 std::vector<::ik::bone<D>*> rest(chain.begin() + 1, chain.end());
155 ::ik::detail::apply_angle_constraints<D>(rest, state);
156}
157
158} // namespace
159
169template <unsigned D>
170bool solveChain(::ik::skeleton<D>& sk, ::ik::ecs<D>& state, unsigned root_id,
171 unsigned tip_id, const std::vector<typename ::ik::solver<D>::target>& targets,
172 const ChainOptions<D>& opt) {
173 std::vector<::ik::bone<D>*> chain = buildChain(sk, root_id, tip_id);
174 if (chain.size() < 2) {
175 return false;
176 }
177
178 struct ChainTarget {
179 unsigned index = 0;
180 const typename ::ik::solver<D>::target *target = nullptr;
181 };
182 std::vector<ChainTarget> chain_targets;
183 for (const auto& t : targets) {
184 for (size_t i = 0; i < chain.size(); ++i) {
185 if (chain[i]->id == t.bone_id) {
186 chain_targets.push_back({static_cast<unsigned>(i), &t});
187 break;
188 }
189 }
190 }
191 if (chain_targets.empty()) {
192 return true;
193 }
194
195 // The furthest target drives the pole direction.
196 const ChainTarget* goal = &chain_targets.front();
197 for (const auto& ct : chain_targets) {
198 if (ct.index > goal->index) {
199 goal = &ct;
200 }
201 }
202
203 std::vector<::ik::vec<float, D>> original_pos(chain.size());
204 for (size_t i = 0; i < chain.size(); ++i) {
205 original_pos[i] = chain[i]->position(state);
206 }
207
208 const float pole_weight = std::min(1.f, std::max(0.f, opt.pole_weight));
209 const unsigned iterations = opt.max_iterations == 0 ? 1u : opt.max_iterations;
210 bool reached = false;
211 for (unsigned iter = 0; iter < iterations; ++iter) {
212 for (const auto& ct : chain_targets) {
213 ::ik::vec<float, D> desired = ct.target->position;
214 if (opt.force > 0.f && opt.force < 1.f) {
215 const ::ik::vec<float, D> cur = chain[ct.index]->position(state);
216 desired = cur + (desired - cur) * (opt.force * ct.target->weight);
217 }
218 reachTarget(state, chain, ct.index, desired);
219 }
220
221 if (opt.use_pole && pole_weight > 0.f) {
222 ::ik::vec<float, D> goal_pos = goal->target->position;
223 if (opt.force > 0.f && opt.force < 1.f) {
224 const ::ik::vec<float, D> cur = chain[goal->index]->position(state);
225 goal_pos = cur + (goal_pos - cur) * (opt.force * goal->target->weight);
226 }
227 applyPole(state, chain, goal_pos, opt.pole, pole_weight);
228 }
229
230 applyChainConstraints(state, chain);
231 sk.update_rotations(state);
232
233 reached = true;
234 for (const auto& ct : chain_targets) {
235 if (::ik::distance(chain[ct.index]->position(state), ct.target->position) >
236 opt.tolerance) {
237 reached = false;
238 break;
239 }
240 }
241 if (reached) {
242 break;
243 }
244 }
245
246 if (opt.influence < 1.f) {
247 const float t = std::min(1.f, std::max(0.f, opt.influence));
248 for (size_t i = 0; i < chain.size(); ++i) {
249 const auto & orig = original_pos[i];
250 ::ik::vec<float, D>& pos = chain[i]->position(state);
251 pos = orig + (pos - orig) * t;
252 }
253 sk.update_rotations(state);
254 }
255 return reached;
256}
257
258} // namespace detail
259} // namespace eve::ik
JobSystemThreadPool::State * state
uint32_t b
glm::mat4 proj
int v
int iterations
Definition TreeMesh.cpp:193
V3 dir
Definition TreeMesh.cpp:121
bool solveChain(::ik::skeleton< D > &sk, ::ik::ecs< D > &state, unsigned root_id, unsigned tip_id, const std::vector< typename ::ik::solver< D >::target > &targets, const ChainOptions< D > &opt)
FABRIK solve restricted to the bone chain root_id..tip_id. Unlike the plain solver (which pins the sk...
Options shared by the chain-scoped FABRIK solve used by Solver2D / Solver3D. These mirror the knobs o...
Definition ChainSolver.h:18