11 solver_.setKeepTrace(keep);
18 throw Exception(
"Solver3D.setMaxIterations: iterations must be >= 0");
21 solver_.setMaxIterations(
static_cast<unsigned>(
iterations));
28 throw Exception(
"Solver3D.setTolerance: tolerance must be >= 0");
31 solver_.setTolerance(tol);
38 solver_.setForce(force);
44 if (influence < 0.f || influence > 1.f) {
45 throw Exception(
"Solver3D.setInfluence: influence must be in [0, 1]");
47 influence_ = influence;
56 throw Exception(
"Solver3D.addTarget: boneId must be >= 0");
58 solver_.addTarget(
static_cast<unsigned>(boneId), ::ik::vec3{
x,
y,
z}, weight);
62 return static_cast<int>(solver_.targets().size());
67 throw Exception(
"Solver3D.solve: skeleton is null");
70 std::vector<::ik::vec3> original;
71 if (influence_ < 1.f) {
72 original =
state.position_;
74 const bool reached = solver_.solve(skeleton->
native(),
state);
75 if (influence_ < 1.f) {
76 const float t = influence_;
77 for (
size_t i = 0; i <
state.position_.size(); ++i) {
78 state.position_[i] = original[i] + (
state.position_[i] - original[i]) * t;
82 applyTipRotation(skeleton);
88 throw Exception(
"Solver3D.step: skeleton is null");
91 std::vector<::ik::vec3> original;
92 if (influence_ < 1.f) {
93 original =
state.position_;
95 solver_.step(skeleton->
native(), skeleton->
state(), dt);
96 if (influence_ < 1.f) {
97 const float t = influence_;
98 for (
size_t i = 0; i <
state.position_.size(); ++i) {
99 state.position_[i] = original[i] + (
state.position_[i] - original[i]) * t;
103 applyTipRotation(skeleton);
110 options.
force = force_;
113 options.
pole = pole_;
115 return solveChainImpl(skeleton, rootBoneId, tipBoneId, options);
124 options.
pole = pole_;
128 f = std::min(1.f, std::max(0.f, dt));
130 f = std::min(1.f,
f * std::max(0.f, dt));
133 solveChainImpl(skeleton, rootBoneId, tipBoneId, options);
136bool Solver3D::solveChainImpl(
Skeleton3D *skeleton,
int rootBoneId,
int tipBoneId,
139 throw Exception(
"Solver3D.solveChain: skeleton is null");
141 if (rootBoneId < 0 || tipBoneId < 0) {
142 throw Exception(
"Solver3D.solveChain: bone ids must be >= 0");
145 if (rootBoneId >= count || tipBoneId >= count) {
146 throw Exception(
"Solver3D.solveChain: bone id out of range");
148 if (rootBoneId == tipBoneId) {
149 throw Exception(
"Solver3D.solveChain: root and tip must differ");
154 if (
b == rootBoneId) {
161 throw Exception(
"Solver3D.solveChain: tipBoneId is not a descendant of rootBoneId");
164 detail::solveChain<3>(skeleton->
native(), skeleton->
state(),
165 static_cast<unsigned>(rootBoneId),
166 static_cast<unsigned>(tipBoneId), solver_.targets(), options);
167 applyTipRotation(skeleton);
172 pole_ = ::ik::vec3{
x,
y,
z};
173 poleWeight_ = std::min(1.f, std::max(0.f, weight));
188 throw Exception(
"Solver3D.setTipRotation: boneId must be >= 0");
193 tipRotWeight_ = std::min(1.f, std::max(0.f, weight));
205void Solver3D::applyTipRotation(
Skeleton3D *skeleton) {
206 if (!skeleton || tipRotWeight_ <= 0.f || tipBoneId_ < 0) {
209 ::ik::skeleton3d &sk = skeleton->
native();
210 if (sk.bones().empty()) {
211 sk.topologicalSort();
213 if (
static_cast<size_t>(tipBoneId_) >= sk.bones().size()) {
214 throw Exception(
"Solver3D.applyTipRotation: bone id out of range");
216 ::ik::bone3d *
b = sk.bones()[
static_cast<size_t>(tipBoneId_)];
221 const float w = tipRotWeight_;
222 ::ik::vec2 rot =
b->rotation(
state);
223 rot[0] = rot[0] + ::ik::wrap_angle(tipYaw_ - rot[0]) *
w;
224 rot[1] = rot[1] + ::ik::wrap_angle(tipPitch_ - rot[1]) *
w;
227 const ::ik::vec3 *gp =
nullptr;
229 if (
b->parent->parent) {
230 gp_pos =
b->parent->parent->position(
state);
233 const ::ik::vec3 parent_fwd = ::ik::detail::parent_forward_of(
234 b->parent->orientation(
state),
b->parent->position(
state), gp);
235 b->orientation(
state) = ::ik::detail::direction_from_local_angles<3>(parent_fwd, rot);
JobSystemThreadPool::State * state
Script-facing 3D skeleton + pose state (ik::skeleton3d + ik::ecs3d). Local angles are yaw/pitch in th...
::ik::skeleton3d & native()
int getParent(int boneId) const
void addTarget(int boneId, float x, float y, float z, float weight=1.f)
void setTipRotation(int boneId, float yaw, float pitch, float weight)
Tip orientation override: after solving, blends the tip bone's local yaw/pitch toward the given angle...
float getTipRotationWeight() const
float getPoleWeight() const
void setTolerance(float tol)
bool solve(Skeleton3D *skeleton)
void setInfluence(float influence)
Overall pose blend: 0 = keep the input pose, 1 = full solve (default).
int getMaxIterations() const
void stepChain(Skeleton3D *skeleton, int rootBoneId, int tipBoneId, float dt=1.f)
void setForce(float force)
bool solveChain(Skeleton3D *skeleton, int rootBoneId, int tipBoneId)
FABRIK solve restricted to the bone chain rootBoneId..tipBoneId. The chain root stays pinned and bone...
float getInfluence() const
void setMaxIterations(int iterations)
void step(Skeleton3D *skeleton, float dt=1.f)
int getTargetCount() const
bool getKeepTrace() const
void setPole(float x, float y, float z, float weight)
Pole vector (magnet / hint): pulls the middle joints of a chain toward a point so the limb bends in t...
float getTolerance() const
void setKeepTrace(bool keep)
Options shared by the chain-scoped FABRIK solve used by Solver2D / Solver3D. These mirror the knobs o...