载入中...
搜索中...
未找到
AnimPose.cpp
浏览该文件的文档.
2#include <array>
5
6#include "common/Exception.h"
7
8#if defined(__SSE2__) || defined(_M_X64) || defined(_M_IX86_FP)
9#include <immintrin.h>
10#endif
11
12namespace eve::animation {
13
14namespace {
15
16struct Quat {
17 float x = 0.f, y = 0.f, z = 0.f, w = 1.f;
18};
19
20Quat quatMul(const Quat& a, const Quat& b) {
21 return {a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
22 a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w, a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z};
23}
24
25Quat quatInverse(const Quat& q) { return {-q.x, -q.y, -q.z, q.w}; }
26
27bool fromTo(float ax, float ay, float az, float bx, float by, float bz, Quat& out) {
28 const float al = std::sqrt(ax * ax + ay * ay + az * az);
29 const float bl = std::sqrt(bx * bx + by * by + bz * bz);
30 if (al < 1e-6f || bl < 1e-6f) return false;
31 ax /= al;
32 ay /= al;
33 az /= al;
34 bx /= bl;
35 by /= bl;
36 bz /= bl;
37 const float dot = clampf(ax * bx + ay * by + az * bz, -1.f, 1.f);
38 if (dot < -0.9999f) {
39 float ox = std::fabs(ax) < 0.8f ? 1.f : 0.f;
40 float oy = std::fabs(ax) < 0.8f ? 0.f : 1.f;
41 float oz = 0.f;
42 const float cx = ay * oz - az * oy;
43 const float cy = az * ox - ax * oz;
44 const float cz = ax * oy - ay * ox;
45 const float cl = std::sqrt(cx * cx + cy * cy + cz * cz);
46 out = {cx / cl, cy / cl, cz / cl, 0.f};
47 return true;
48 }
49 const float cx = ay * bz - az * by;
50 const float cy = az * bx - ax * bz;
51 const float cz = ax * by - ay * bx;
52 out = {cx, cy, cz, 1.f + dot};
53 const float ql = std::sqrt(out.x * out.x + out.y * out.y + out.z * out.z + out.w * out.w);
54 out.x /= ql;
55 out.y /= ql;
56 out.z /= ql;
57 out.w /= ql;
58 return true;
59}
60
61Quat worldToLocalRotation(const AnimSkeleton* skeleton, const AnimPose* pose, int bone, const Quat& world) {
62 const int parent = skeleton->getParent(bone);
63 if (parent < 0) return world;
64 const auto& p = pose->world(parent);
65 return quatMul(quatInverse({p.qx, p.qy, p.qz, p.qw}), world);
66}
67
68void blendLocalRotation(TransformTRS& local, const Quat& target, float weight) {
69 slerpQuat(local.qx, local.qy, local.qz, local.qw, target.x, target.y, target.z, target.w, clampf(weight, 0.f, 1.f),
70 local.qx, local.qy, local.qz, local.qw);
71}
72
73
74} // namespace
75
76AnimPose::AnimPose(int boneCount) { resize(boneCount); }
77
78void AnimPose::resize(int boneCount) {
79 if (boneCount < 0) throw Exception("AnimPose.resize: boneCount must be >= 0");
80 if (boneCount == getBoneCount()) return;
81 locals_.assign(static_cast<size_t>(boneCount), TransformTRS::identity());
82 worlds_.assign(static_cast<size_t>(boneCount), TransformTRS::identity());
83}
84
85void AnimPose::requireBone(int boneIndex) const {
86 if (boneIndex < 0 || boneIndex >= getBoneCount()) {
87 throw Exception("AnimPose: invalid bone index %d", boneIndex);
88 }
89}
90
91void AnimPose::copyFrom(const AnimPose* other) {
92 if (!other) throw Exception("AnimPose.copyFrom: other is null");
93 locals_ = other->locals_;
94 worlds_ = other->worlds_;
95}
96
97void AnimPose::blendFrom(const AnimPose* a, const AnimPose* b, float t) {
98 if (!a || !b) throw Exception("AnimPose.blendFrom: pose is null");
99 if (a->getBoneCount() != b->getBoneCount()) {
100 throw Exception("AnimPose.blendFrom: bone count mismatch");
101 }
102 resize(a->getBoneCount());
103 for (int i = 0; i < getBoneCount(); ++i) {
104 const TransformTRS& av = a->locals_[static_cast<size_t>(i)];
105 const TransformTRS& bv = b->locals_[static_cast<size_t>(i)];
106 TransformTRS& out = locals_[static_cast<size_t>(i)];
107#if defined(__SSE2__) || defined(_M_X64) || defined(_M_IX86_FP)
108 const __m128 weight = _mm_set1_ps(clampf(t, 0.f, 1.f));
109 const __m128 oneMinus = _mm_sub_ps(_mm_set1_ps(1.f), weight);
110 alignas(16) float values[4];
111 _mm_store_ps(values, _mm_add_ps(_mm_mul_ps(_mm_setr_ps(av.px, av.py, av.pz, 0.f), oneMinus),
112 _mm_mul_ps(_mm_setr_ps(bv.px, bv.py, bv.pz, 0.f), weight)));
113 out.px = values[0]; out.py = values[1]; out.pz = values[2];
114 _mm_store_ps(values, _mm_add_ps(_mm_mul_ps(_mm_setr_ps(av.sx, av.sy, av.sz, 0.f), oneMinus),
115 _mm_mul_ps(_mm_setr_ps(bv.sx, bv.sy, bv.sz, 0.f), weight)));
116 out.sx = values[0]; out.sy = values[1]; out.sz = values[2];
117#else
118 out.px = lerpf(av.px, bv.px, t); out.py = lerpf(av.py, bv.py, t); out.pz = lerpf(av.pz, bv.pz, t);
119 out.sx = lerpf(av.sx, bv.sx, t); out.sy = lerpf(av.sy, bv.sy, t); out.sz = lerpf(av.sz, bv.sz, t);
120#endif
121 slerpQuat(av.qx, av.qy, av.qz, av.qw, bv.qx, bv.qy, bv.qz, bv.qw, clampf(t, 0.f, 1.f),
122 out.qx, out.qy, out.qz, out.qw);
123 }
124}
125
127 requireBone(boneIndex);
128 return locals_[static_cast<size_t>(boneIndex)];
129}
130
131const TransformTRS& AnimPose::local(int boneIndex) const {
132 requireBone(boneIndex);
133 return locals_[static_cast<size_t>(boneIndex)];
134}
135
136void AnimPose::setLocalPosition(int boneIndex, float x, float y, float z) {
137 requireBone(boneIndex);
138 auto& t = locals_[static_cast<size_t>(boneIndex)];
139 t.px = x;
140 t.py = y;
141 t.pz = z;
142}
143
144void AnimPose::setLocalRotation(int boneIndex, float x, float y, float z, float w) {
145 requireBone(boneIndex);
146 auto& t = locals_[static_cast<size_t>(boneIndex)];
147 t.qx = x;
148 t.qy = y;
149 t.qz = z;
150 t.qw = w;
151 t.normalizeRotation();
152}
153
154void AnimPose::setLocalScale(int boneIndex, float x, float y, float z) {
155 requireBone(boneIndex);
156 auto& t = locals_[static_cast<size_t>(boneIndex)];
157 t.sx = x;
158 t.sy = y;
159 t.sz = z;
160}
161
162float AnimPose::getLocalPositionX(int boneIndex) const {
163 requireBone(boneIndex);
164 return locals_[static_cast<size_t>(boneIndex)].px;
165}
166float AnimPose::getLocalPositionY(int boneIndex) const {
167 requireBone(boneIndex);
168 return locals_[static_cast<size_t>(boneIndex)].py;
169}
170float AnimPose::getLocalPositionZ(int boneIndex) const {
171 requireBone(boneIndex);
172 return locals_[static_cast<size_t>(boneIndex)].pz;
173}
174float AnimPose::getLocalRotationX(int boneIndex) const {
175 requireBone(boneIndex);
176 return locals_[static_cast<size_t>(boneIndex)].qx;
177}
178float AnimPose::getLocalRotationY(int boneIndex) const {
179 requireBone(boneIndex);
180 return locals_[static_cast<size_t>(boneIndex)].qy;
181}
182float AnimPose::getLocalRotationZ(int boneIndex) const {
183 requireBone(boneIndex);
184 return locals_[static_cast<size_t>(boneIndex)].qz;
185}
186float AnimPose::getLocalRotationW(int boneIndex) const {
187 requireBone(boneIndex);
188 return locals_[static_cast<size_t>(boneIndex)].qw;
189}
190float AnimPose::getLocalScaleX(int boneIndex) const {
191 requireBone(boneIndex);
192 return locals_[static_cast<size_t>(boneIndex)].sx;
193}
194float AnimPose::getLocalScaleY(int boneIndex) const {
195 requireBone(boneIndex);
196 return locals_[static_cast<size_t>(boneIndex)].sy;
197}
198float AnimPose::getLocalScaleZ(int boneIndex) const {
199 requireBone(boneIndex);
200 return locals_[static_cast<size_t>(boneIndex)].sz;
201}
202
204 if (!skeleton) throw Exception("AnimPose.computeWorld: skeleton is null");
205 if (skeleton->getBoneCount() != getBoneCount()) {
206 throw Exception("AnimPose.computeWorld: bone count mismatch");
207 }
208 worlds_.resize(locals_.size());
209 for (int i = 0; i < getBoneCount(); ++i) {
210 const int parent = skeleton->getParent(i);
211 if (parent < 0) {
212 worlds_[static_cast<size_t>(i)] = locals_[static_cast<size_t>(i)];
213 } else {
214 worlds_[static_cast<size_t>(i)] =
215 detail::mulTRS(worlds_[static_cast<size_t>(parent)], locals_[static_cast<size_t>(i)]);
216 }
217 }
218}
219
220bool AnimPose::aimBone(const AnimSkeleton* skeleton, int boneIndex, float targetX, float targetY, float targetZ,
221 float weight) {
222 if (!skeleton) throw Exception("AnimPose.aimBone: skeleton is null");
223 requireBone(boneIndex);
224 computeWorld(skeleton);
225 const auto& bone = world(boneIndex);
226 const Mat4 mat = Mat4::fromTRS(bone);
227 Quat delta;
228 if (!fromTo(mat.m[8], mat.m[9], mat.m[10], targetX - bone.px, targetY - bone.py, targetZ - bone.pz, delta)) {
229 return false;
230 }
231 const Quat desiredWorld = quatMul(delta, {bone.qx, bone.qy, bone.qz, bone.qw});
232 blendLocalRotation(local(boneIndex), worldToLocalRotation(skeleton, this, boneIndex, desiredWorld), weight);
233 computeWorld(skeleton);
234 return true;
235}
236
237bool AnimPose::solveTwoBoneIK(const AnimSkeleton* skeleton, int rootBone, int midBone, int tipBone, float targetX,
238 float targetY, float targetZ, float weight) {
239 if (!skeleton) throw Exception("AnimPose.solveTwoBoneIK: skeleton is null");
240 requireBone(midBone);
241 computeWorld(skeleton);
242 const auto pole = world(midBone);
243 return solveTwoBoneIKPoleImpl(skeleton, rootBone, midBone, tipBone, targetX, targetY, targetZ, pole.px, pole.py,
244 pole.pz, weight);
245}
246
247bool AnimPose::solveTwoBoneIKPoleImpl(const AnimSkeleton* skeleton, int rootBone, int midBone, int tipBone,
248 float targetX, float targetY, float targetZ, float poleX, float poleY,
249 float poleZ, float weight) {
250 if (!skeleton) throw Exception("AnimPose.solveTwoBoneIK: skeleton is null");
251 requireBone(rootBone);
252 requireBone(midBone);
253 requireBone(tipBone);
254 if (skeleton->getParent(midBone) != rootBone || skeleton->getParent(tipBone) != midBone) return false;
255
256 if (!std::isfinite(targetX) || !std::isfinite(targetY) || !std::isfinite(targetZ) || !std::isfinite(poleX) ||
257 !std::isfinite(poleY) || !std::isfinite(poleZ) || !std::isfinite(weight))
258 throw Exception("AnimPose.solveTwoBoneIK: nonfinite target or weight");
259 computeWorld(skeleton);
260 using Vector = std::array<double, 3>;
261 auto position = [](const TransformTRS& t) -> Vector { return {t.px, t.py, t.pz}; };
262 auto sub = [](Vector a, Vector b) -> Vector { return {a[0] - b[0], a[1] - b[1], a[2] - b[2]}; };
263 auto dot = [](Vector a, Vector b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; };
264 auto cross = [](Vector a, Vector b) -> Vector {
265 return {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]};
266 };
267 const Vector a = position(world(rootBone)), b = position(world(midBone)), c = position(world(tipBone));
268 const Vector ab = sub(b, a), bc = sub(c, b);
269 const double upper = std::sqrt(dot(ab, ab)), lower = std::sqrt(dot(bc, bc));
270 if (upper < 1e-7 || lower < 1e-7) return false;
271 Vector direction = sub(Vector{targetX, targetY, targetZ}, a);
272 double distance = std::sqrt(dot(direction, direction));
273 if (distance < 1e-7) {
274 direction = sub(c, a);
275 distance = std::sqrt(dot(direction, direction));
276 }
277 if (distance < 1e-7) {
278 direction = ab;
279 distance = upper;
280 }
281 for (auto& value : direction) value /= distance;
282 const double requested =
283 std::sqrt(dot(sub(Vector{targetX, targetY, targetZ}, a), sub(Vector{targetX, targetY, targetZ}, a)));
284 const double reach = std::clamp(requested, std::max(std::abs(upper - lower), 1e-7), upper + lower);
285 const double along = (upper * upper - lower * lower + reach * reach) / (2 * reach);
286 const double height = std::sqrt(std::max(upper * upper - along * along, 0.0));
287 Vector pole = sub(Vector{poleX, poleY, poleZ}, a);
288 const double projected = dot(pole, direction);
289 for (int i = 0; i < 3; ++i) pole[i] -= projected * direction[i];
290 double poleLength = std::sqrt(dot(pole, pole));
291 if (poleLength < 1e-7) {
292 pole = cross(cross(ab, bc), direction);
293 poleLength = std::sqrt(dot(pole, pole));
294 }
295 if (poleLength < 1e-7) {
296 // A fully straight chain has no bend plane. Choose the least parallel
297 // axis deterministically; subsequent calls retain the resulting plane.
298 int axis = 0;
299 for (int i = 1; i < 3; ++i)
300 if (std::abs(direction[i]) < std::abs(direction[axis])) axis = i;
301 pole = {0, 0, 0};
302 pole[axis] = 1;
303 const double projection = dot(pole, direction);
304 for (int i = 0; i < 3; ++i) pole[i] -= projection * direction[i];
305 poleLength = std::sqrt(dot(pole, pole));
306 }
307 Vector knee{}, tip{};
308 for (int i = 0; i < 3; ++i) {
309 knee[i] = a[i] + direction[i] * along + pole[i] * height / poleLength;
310 tip[i] = a[i] + direction[i] * reach;
311 }
312 const auto originalRoot = local(rootBone), originalMid = local(midBone);
313 Quat delta;
314 if (!fromTo(float(ab[0]), float(ab[1]), float(ab[2]), float(knee[0] - a[0]), float(knee[1] - a[1]),
315 float(knee[2] - a[2]), delta))
316 return false;
317 const auto root = world(rootBone);
318 const Quat rootRotation =
319 worldToLocalRotation(skeleton, this, rootBone, quatMul(delta, {root.qx, root.qy, root.qz, root.qw}));
320 blendLocalRotation(local(rootBone), rootRotation, 1.f);
321 computeWorld(skeleton);
322 const auto mid = world(midBone), end = world(tipBone);
323 if (fromTo(end.px - mid.px, end.py - mid.py, end.pz - mid.pz, float(tip[0] - mid.px), float(tip[1] - mid.py),
324 float(tip[2] - mid.pz), delta)) {
325 const Quat midRotation =
326 worldToLocalRotation(skeleton, this, midBone, quatMul(delta, {mid.qx, mid.qy, mid.qz, mid.qw}));
327 blendLocalRotation(local(midBone), midRotation, 1.f);
328 }
329 const auto solvedRoot = local(rootBone), solvedMid = local(midBone);
330 local(rootBone) = originalRoot;
331 local(midBone) = originalMid;
332 blendLocalRotation(local(rootBone), {solvedRoot.qx, solvedRoot.qy, solvedRoot.qz, solvedRoot.qw}, weight);
333 blendLocalRotation(local(midBone), {solvedMid.qx, solvedMid.qy, solvedMid.qz, solvedMid.qw}, weight);
334 computeWorld(skeleton);
335 return true;
336}
337
338float AnimPose::getWorldPositionX(int boneIndex) const {
339 requireBone(boneIndex);
340 return worlds_[static_cast<size_t>(boneIndex)].px;
341}
342float AnimPose::getWorldPositionY(int boneIndex) const {
343 requireBone(boneIndex);
344 return worlds_[static_cast<size_t>(boneIndex)].py;
345}
346float AnimPose::getWorldPositionZ(int boneIndex) const {
347 requireBone(boneIndex);
348 return worlds_[static_cast<size_t>(boneIndex)].pz;
349}
350float AnimPose::getWorldRotationX(int boneIndex) const {
351 requireBone(boneIndex);
352 return worlds_[static_cast<size_t>(boneIndex)].qx;
353}
354float AnimPose::getWorldRotationY(int boneIndex) const {
355 requireBone(boneIndex);
356 return worlds_[static_cast<size_t>(boneIndex)].qy;
357}
358float AnimPose::getWorldRotationZ(int boneIndex) const {
359 requireBone(boneIndex);
360 return worlds_[static_cast<size_t>(boneIndex)].qz;
361}
362float AnimPose::getWorldRotationW(int boneIndex) const {
363 requireBone(boneIndex);
364 return worlds_[static_cast<size_t>(boneIndex)].qw;
365}
366
367const TransformTRS& AnimPose::world(int boneIndex) const {
368 requireBone(boneIndex);
369 return worlds_[static_cast<size_t>(boneIndex)];
370}
371
372float AnimPose::getWorldMatrixElement(int boneIndex, int elementIndex) const {
373 requireBone(boneIndex);
374 if (elementIndex < 0 || elementIndex > 15) {
375 throw Exception("AnimPose.getWorldMatrixElement: elementIndex must be 0..15");
376 }
377 const Mat4 mat = Mat4::fromTRS(worlds_[static_cast<size_t>(boneIndex)]);
378 return mat.m[elementIndex];
379}
380
381void AnimPose::getWorldMatrix(int boneIndex, float* out16) const {
382 requireBone(boneIndex);
383 if (!out16) throw Exception("AnimPose.getWorldMatrix: out16 is null");
384 const Mat4 mat = Mat4::fromTRS(worlds_[static_cast<size_t>(boneIndex)]);
385 for (int i = 0; i < 16; ++i) out16[i] = mat.m[i];
386}
387
388} // namespace eve::animation
LogicalId target
double value
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int tip
Definition AnimSmr.cpp:121
int root
Definition AnimSmr.cpp:119
int mid
Definition AnimSmr.cpp:120
eve::EntitySpatialPose pose
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
Vec3 projected
Definition CaveMesh.cpp:122
int bz
Definition CaveMesh.cpp:114
int ax
Definition CaveMesh.cpp:113
int ay
Definition CaveMesh.cpp:113
int bx
Definition CaveMesh.cpp:114
int az
Definition CaveMesh.cpp:113
int by
Definition CaveMesh.cpp:114
float py
float pz
glm::vec4 p[6]
std::map< std::string, Var > values
std::array< double, 10 > q
std::uint32_t ab
std::int32_t c
std::vector< Colorf > px
std::uint32_t height
std::string local
std::array< float, 3 > position
std::uint32_t bone
std::int32_t parent
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
World3D * world
float t
RoadLaneDirection direction
float targetX
double oy
double ox
float qy
float qx
float qw
float qz
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
Definition AnimPose.h:17
float getLocalRotationX(int boneIndex) const
Returns the local rotation x.
Definition AnimPose.cpp:174
float getWorldRotationX(int boneIndex) const
Returns the world rotation x.
Definition AnimPose.cpp:350
float getWorldPositionZ(int boneIndex) const
Returns the world position z.
Definition AnimPose.cpp:346
AnimPose()=default
Anim pose.
float getLocalScaleZ(int boneIndex) const
Returns the local scale z.
Definition AnimPose.cpp:198
float getLocalPositionZ(int boneIndex) const
Returns the local position z.
Definition AnimPose.cpp:170
void blendFrom(const AnimPose *a, const AnimPose *b, float t)
Blend from.
Definition AnimPose.cpp:97
int getBoneCount() const
Returns the bone count.
Definition AnimPose.h:36
float getWorldMatrixElement(int boneIndex, int elementIndex) const
Column-major 4x4 world matrix for boneIndex after computeWorld(). elementIndex in [0,...
Definition AnimPose.cpp:372
float getLocalPositionX(int boneIndex) const
Returns the local position x.
Definition AnimPose.cpp:162
float getLocalRotationW(int boneIndex) const
Returns the local rotation w.
Definition AnimPose.cpp:186
float getWorldPositionX(int boneIndex) const
Returns the world position x.
Definition AnimPose.cpp:338
void setLocalScale(int boneIndex, float x, float y, float z)
Sets the local scale.
Definition AnimPose.cpp:154
const TransformTRS & world(int boneIndex) const
World.
Definition AnimPose.cpp:367
float getLocalScaleX(int boneIndex) const
Returns the local scale x.
Definition AnimPose.cpp:190
float getWorldPositionY(int boneIndex) const
Returns the world position y.
Definition AnimPose.cpp:342
void getWorldMatrix(int boneIndex, float *out16) const
Write 16 floats (column-major) into out16 (must not be null).
Definition AnimPose.cpp:381
void setLocalRotation(int boneIndex, float x, float y, float z, float w)
Sets the local rotation.
Definition AnimPose.cpp:144
float getWorldRotationW(int boneIndex) const
Returns the world rotation w.
Definition AnimPose.cpp:362
float getWorldRotationZ(int boneIndex) const
Returns the world rotation z.
Definition AnimPose.cpp:358
float getWorldRotationY(int boneIndex) const
Returns the world rotation y.
Definition AnimPose.cpp:354
float getLocalScaleY(int boneIndex) const
Returns the local scale y.
Definition AnimPose.cpp:194
TransformTRS & local(int boneIndex)
Local.
Definition AnimPose.cpp:126
float getLocalRotationZ(int boneIndex) const
Returns the local rotation z.
Definition AnimPose.cpp:182
void setLocalPosition(int boneIndex, float x, float y, float z)
Sets the local position.
Definition AnimPose.cpp:136
float getLocalPositionY(int boneIndex) const
Returns the local position y.
Definition AnimPose.cpp:166
void copyFrom(const AnimPose *other)
Copies from.
Definition AnimPose.cpp:91
float getLocalRotationY(int boneIndex) const
Returns the local rotation y.
Definition AnimPose.cpp:178
bool aimBone(const AnimSkeleton *skeleton, int boneIndex, float targetX, float targetY, float targetZ, float weight=1.f)
Rotate a bone so its local +Z axis aims at a world-space target.
Definition AnimPose.cpp:220
void computeWorld(const AnimSkeleton *skeleton)
Compute world transforms from local pose + skeleton hierarchy. World values readable via getWorld* af...
Definition AnimPose.cpp:203
void resize(int boneCount)
Resize.
Definition AnimPose.cpp:78
bool solveTwoBoneIK(const AnimSkeleton *skeleton, int rootBone, int midBone, int tipBone, float targetX, float targetY, float targetZ, float weight=1.f)
Analytically solve a root-mid-tip chain toward a world-space target.
Definition AnimPose.cpp:237
3D bone hierarchy + bind-pose local TRS for skeletal animation. Independent of ik::Skeleton3D (FABRIK...
int getBoneCount() const
Returns the bone count.
int getParent(int boneIndex) const
Returns the parent.
TransformTRS mulTRS(const TransformTRS &parent, const TransformTRS &local)
Mul trs.
void slerpQuat(float ax, float ay, float az, float aw, float bx, float by, float bz, float bw, float t, float &ox, float &oy, float &oz, float &ow)
Slerp quat.
Definition AnimMath.h:43
float lerpf(float a, float b, float t)
Lerpf.
Definition AnimMath.h:40
float clampf(float v, float lo, float hi)
Clampf.
Definition AnimMath.h:34
double dot(const Vec2 &a, const Vec2 &b)
Dot.
Definition UrbanTypes.h:38
double cross(const Vec2 &a, const Vec2 &b)
Cross.
Definition UrbanTypes.h:36
int axis(int64_t a, size_t rank)
Axis.
Column-major 4x4 matrix (OpenGL / glTF / Assimp-compatible layout). Elements: m[col * 4 + row].
Definition AnimMath.h:156
static Mat4 fromTRS(const TransformTRS &t)
From trs.
Definition AnimMath.h:163
Local TRS used by skeletal animation (quaternion xyzw).
Definition AnimMath.h:9
static TransformTRS identity()
Identity.
Definition AnimMath.h:15