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);
109 options.max_iterations =
static_cast<unsigned>(maxIterations_);
111 options.influence = influence_;
114 options.pole_weight = poleWeight_;
115 return solveChainImpl(skeleton, rootBoneId, tipBoneId,
options);
122 options.influence = influence_;
125 options.pole_weight = poleWeight_;
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;
225 b->rotation(state) = rot;
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);
const SquirrelValueOptions & options
EVENGINE_API_FOUNDATION public API.
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
Parent bone id, or -1 for the root.
int getBoneCount() const
Number of bones currently in the skeleton.
void addTarget(int boneId, float x, float y, float z, float weight=1.f)
Adds an end-effector target for a bone.
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
Configured tip rotation blend weight.
float getPoleWeight() const
Configured pole-vector weight in [0,1].
float getForce() const
Configured soft-IK force.
void setTolerance(float tol)
End-effector distance tolerance for success.
void clearTrace()
Clears recorded solve trace samples.
void clearTipRotation()
Clears tip orientation override.
bool solve(Skeleton3D *skeleton)
Runs FABRIK until tolerance or max iterations.
void setInfluence(float influence)
Overall pose blend: 0 = keep the input pose, 1 = full solve (default).
void clearPole()
Disables the pole-vector hint.
int getMaxIterations() const
Configured maximum FABRIK iterations.
void stepChain(Skeleton3D *skeleton, int rootBoneId, int tipBoneId, float dt=1.f)
Time-scaled chain-scoped FABRIK step.
void setForce(float force)
Soft-IK blend toward the target (0 = hard IK).
bool solveChain(Skeleton3D *skeleton, int rootBoneId, int tipBoneId)
FABRIK solve restricted to the bone chain rootBoneId..tipBoneId. The chain root stays pinned and bone...
void clearTargets()
Removes all IK targets.
float getInfluence() const
Pose blend weight (0 = keep input, 1 = full solve).
bool hasPole() const
True when a pole vector is configured.
void setMaxIterations(int iterations)
Maximum FABRIK iterations per solve.
void step(Skeleton3D *skeleton, float dt=1.f)
Runs a time-scaled FABRIK step (dt scales iteration effort).
int getTargetCount() const
Number of configured targets.
int getTraceSize() const
Number of recorded trace samples.
bool getKeepTrace() const
Whether solve tracing is enabled.
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
Configured end-effector tolerance.
void setKeepTrace(bool keep)
When true, records intermediate solve samples.
Options shared by the chain-scoped FABRIK solve used by Solver2D / Solver3D. These mirror the knobs o...