载入中...
搜索中...
未找到
AnimSmr.cpp
浏览该文件的文档.
1#include "animation/AnimSmr.h"
3#include "common/Capability.h"
4
8
9#include "common/Exception.h"
10
11#include <algorithm>
12#include <cctype>
13#include <cmath>
14#include <vector>
15
16namespace eve::animation {
17namespace {
18
19std::string normalizeToken(const std::string& name) {
20 const size_t separator = name.find_last_of(":|/");
21 const size_t begin = separator == std::string::npos ? 0 : separator + 1;
22 std::string out;
23 out.reserve(name.size() - begin);
24 for (size_t i = begin; i < name.size(); ++i) {
25 const unsigned char c = static_cast<unsigned char>(name[i]);
26 if (std::isalnum(c)) out.push_back(static_cast<char>(std::tolower(c)));
27 }
28 return out;
29}
30
31bool tokenContains(const std::string& token, const char* needle) { return token.find(needle) != std::string::npos; }
32
33AnimSmrBodyPart classifyBone(const std::string& boneName) {
34 const std::string token = normalizeToken(boneName);
35 if (tokenContains(token, "head") || tokenContains(token, "neck")) return AnimSmrBodyPart::Head;
36 if (tokenContains(token, "arm") || tokenContains(token, "hand") || tokenContains(token, "wrist") ||
37 tokenContains(token, "elbow") || tokenContains(token, "shoulder") || tokenContains(token, "clavicle") ||
38 tokenContains(token, "finger") || tokenContains(token, "forearm"))
40 if (tokenContains(token, "leg") || tokenContains(token, "foot") || tokenContains(token, "toe") ||
41 tokenContains(token, "ankle") || tokenContains(token, "knee") || tokenContains(token, "upleg") ||
42 tokenContains(token, "thigh") || tokenContains(token, "calf"))
44 if (tokenContains(token, "spine") || tokenContains(token, "chest") || tokenContains(token, "torso") ||
45 tokenContains(token, "hips") || tokenContains(token, "pelvis") || tokenContains(token, "abdomen") ||
46 tokenContains(token, "ribcage") || tokenContains(token, "belly") || token == "root" ||
47 tokenContains(token, "hip"))
50}
51
52bool partsInteract(AnimSmrBodyPart a, AnimSmrBodyPart b) {
53 if (a > b) std::swap(a, b);
54 if (a == AnimSmrBodyPart::Arm &&
56 return true;
57 if (a == AnimSmrBodyPart::Leg && b == AnimSmrBodyPart::Leg) return true;
58 if (a == AnimSmrBodyPart::Torso && b == AnimSmrBodyPart::Head) return true;
59 return false;
60}
61
62struct Vec3 {
63 float x = 0.f, y = 0.f, z = 0.f;
64};
65
66Vec3 add(const Vec3& a, const Vec3& b) { return {a.x + b.x, a.y + b.y, a.z + b.z}; }
67Vec3 sub(const Vec3& a, const Vec3& b) { return {a.x - b.x, a.y - b.y, a.z - b.z}; }
68Vec3 mul(const Vec3& a, float s) { return {a.x * s, a.y * s, a.z * s}; }
69float length(const Vec3& a) { return std::sqrt(a.x * a.x + a.y * a.y + a.z * a.z); }
70
71Vec3 transformPoint(const TransformTRS& world, float lx, float ly, float lz) {
72 const float x = world.qx, y = world.qy, z = world.qz, w = world.qw;
73 const float x2 = x + x, y2 = y + y, z2 = z + z;
74 const float xx = x * x2, xy = x * y2, xz = x * z2;
75 const float yy = y * y2, yz = y * z2, zz = z * z2;
76 const float wx = w * x2, wy = w * y2, wz = w * z2;
77 const float r00 = (1.f - (yy + zz)) * world.sx;
78 const float r10 = (xy + wz) * world.sx;
79 const float r20 = (xz - wy) * world.sx;
80 const float r01 = (xy - wz) * world.sy;
81 const float r11 = (1.f - (xx + zz)) * world.sy;
82 const float r21 = (yz + wx) * world.sy;
83 const float r02 = (xz + wy) * world.sz;
84 const float r12 = (yz - wx) * world.sz;
85 const float r22 = (1.f - (xx + yy)) * world.sz;
86 return {world.px + r00 * lx + r01 * ly + r02 * lz, world.py + r10 * lx + r11 * ly + r12 * lz,
87 world.pz + r20 * lx + r21 * ly + r22 * lz};
88}
89
90float skeletonExtent(const AnimSkeleton* skeleton) {
91 AnimPose bind;
92 skeleton->applyBindPose(&bind);
93 bind.computeWorld(skeleton);
94 if (skeleton->getBoneCount() == 0) return 1.f;
95 const TransformTRS& root = bind.world(0);
96 float maxR = 0.f;
97 for (int bone = 1; bone < skeleton->getBoneCount(); ++bone) {
98 const TransformTRS& world = bind.world(bone);
99 maxR = std::max(maxR, length(sub({world.px, world.py, world.pz}, {root.px, root.py, root.pz})));
100 }
101 return std::max(maxR, 1e-3f);
102}
103
104int findBoneFlexible(const AnimSkeleton* skeleton, const std::string& name) {
105 if (!skeleton || name.empty()) return -1;
106 const int exact = skeleton->findBone(name);
107 if (exact >= 0) return exact;
108 const std::string want = normalizeToken(name);
109 int hit = -1;
110 for (int bone = 0; bone < skeleton->getBoneCount(); ++bone) {
111 if (normalizeToken(skeleton->getBoneName(bone)) != want) continue;
112 if (hit >= 0) return -1;
113 hit = bone;
114 }
115 return hit;
116}
117
118struct ResolvedChain {
119 int root = -1;
120 int mid = -1;
121 int tip = -1;
122};
123
124std::vector<ResolvedChain> resolveChains(const AnimSkeleton* skeleton,
125 const std::vector<AnimSmrIkChain>& configuredChains) {
126 std::vector<ResolvedChain> out;
127 for (const AnimSmrIkChain& chain : configuredChains) {
128 ResolvedChain resolved{findBoneFlexible(skeleton, chain.rootBone), findBoneFlexible(skeleton, chain.midBone),
129 findBoneFlexible(skeleton, chain.tipBone)};
130 if (resolved.root >= 0 && resolved.mid >= 0 && resolved.tip >= 0) out.push_back(resolved);
131 }
132 if (!out.empty()) return out;
133
134 auto tryPush = [&](const char* root, const char* mid, const char* tip) {
135 ResolvedChain resolved{findBoneFlexible(skeleton, root), findBoneFlexible(skeleton, mid),
136 findBoneFlexible(skeleton, tip)};
137 if (resolved.root >= 0 && resolved.mid >= 0 && resolved.tip >= 0) out.push_back(resolved);
138 };
139 tryPush("LeftShoulder", "LeftElbow", "LeftHand");
140 tryPush("RightShoulder", "RightElbow", "RightHand");
141 tryPush("LeftUpLeg", "LeftLeg", "LeftFoot");
142 tryPush("RightUpLeg", "RightLeg", "RightFoot");
143 tryPush("leftarm", "leftforearm", "lefthand");
144 tryPush("rightarm", "rightforearm", "righthand");
145 tryPush("leftupleg", "leftleg", "leftfoot");
146 tryPush("rightupleg", "rightleg", "rightfoot");
147 tryPush("shoulder_l", "elbow_l", "hand_l");
148 tryPush("shoulder_r", "elbow_r", "hand_r");
149 tryPush("thigh_l", "calf_l", "foot_l");
150 tryPush("thigh_r", "calf_r", "foot_r");
151 return out;
152}
153
154int chainForTip(const std::vector<ResolvedChain>& chains, int tipBone) {
155 for (int i = 0; i < static_cast<int>(chains.size()); ++i) {
156 if (chains[static_cast<size_t>(i)].tip == tipBone) return i;
157 }
158 return -1;
159}
160
161struct InteractionPair {
162 int observer = -1;
163 int subject = -1;
164 Vec3 relative{};
165};
166
167void collectInteractions(const AnimSmrSensorCloud& cloud, const std::vector<float>& xyz, float contactDistance,
168 std::vector<InteractionPair>& out) {
169 out.clear();
170 const int count = cloud.getSensorCount();
171 for (int i = 0; i < count; ++i) {
172 const AnimSmrBodyPart partI = cloud.getSensorPart(i);
173 for (int j = i + 1; j < count; ++j) {
174 const AnimSmrBodyPart partJ = cloud.getSensorPart(j);
175 if (!partsInteract(partI, partJ)) continue;
176 int observer = i;
177 int subject = j;
178 if (partJ == AnimSmrBodyPart::Torso && partI != AnimSmrBodyPart::Torso) {
179 observer = j;
180 subject = i;
181 } else if (partI == AnimSmrBodyPart::Torso && partJ != AnimSmrBodyPart::Torso) {
182 observer = i;
183 subject = j;
184 }
185 const Vec3 po{xyz[static_cast<size_t>(observer) * 3], xyz[static_cast<size_t>(observer) * 3 + 1],
186 xyz[static_cast<size_t>(observer) * 3 + 2]};
187 const Vec3 pj{xyz[static_cast<size_t>(subject) * 3], xyz[static_cast<size_t>(subject) * 3 + 1],
188 xyz[static_cast<size_t>(subject) * 3 + 2]};
189 const Vec3 delta = sub(pj, po);
190 if (length(delta) > contactDistance) continue;
191 out.push_back({observer, subject, delta});
192 }
193 }
194}
195
196int matchSensor(const AnimSmrSensorCloud& sourceCloud, int sourceIndex, const AnimSmrSensorCloud& targetCloud) {
197 const AnimSmrSensor& source = sourceCloud.getSensor(sourceIndex);
198 int best = -1;
199 for (int i = 0; i < targetCloud.getSensorCount(); ++i) {
200 const AnimSmrSensor& target = targetCloud.getSensor(i);
201 if (target.part != source.part) continue;
202 if (target.semanticKey == source.semanticKey) return i;
203 const size_t sourceHash = source.semanticKey.find('#');
204 const size_t targetHash = target.semanticKey.find('#');
205 if (sourceHash != std::string::npos && targetHash != std::string::npos &&
206 source.semanticKey.substr(sourceHash) == target.semanticKey.substr(targetHash)) {
207 if (best < 0) best = i;
208 }
209 }
210 if (best >= 0) return best;
211 for (int i = 0; i < targetCloud.getSensorCount(); ++i) {
212 if (targetCloud.getSensorPart(i) == source.part) return i;
213 }
214 return -1;
215}
216
217void writeRotationKey(AnimClip& clip, int boneIndex, int keyIndex, float time, const TransformTRS& local) {
218 if (keyIndex >= 0 && keyIndex < clip.getRotationKeyCount(boneIndex)) {
219 clip.setRotationKey(boneIndex, keyIndex, time, local.qx, local.qy, local.qz, local.qw);
220 return;
221 }
222 clip.addRotationKey(boneIndex, time, local.qx, local.qy, local.qz, local.qw);
223}
224
225} // namespace
226
227
229 int pointsPerRing) {
230 if (!skeleton) throw Exception("AnimSmrSensorCloud.fromSkeletonDense: skeleton is null");
231 if (ringsPerBone < 1 || pointsPerRing < 3)
232 throw Exception("AnimSmrSensorCloud.fromSkeletonDense: ringsPerBone>=1 and pointsPerRing>=3 required");
233
234 AnimSmrSensorCloud cloud = fromSkeleton(skeleton);
235 std::vector<int> firstChild(static_cast<size_t>(skeleton->getBoneCount()), -1);
236 for (int bone = 0; bone < skeleton->getBoneCount(); ++bone) {
237 const int parent = skeleton->getParent(bone);
238 if (parent >= 0 && firstChild[static_cast<size_t>(parent)] < 0) firstChild[static_cast<size_t>(parent)] = bone;
239 }
240
241 for (int bone = 0; bone < skeleton->getBoneCount(); ++bone) {
242 const int child = firstChild[static_cast<size_t>(bone)];
243 if (child < 0) continue;
244 const TransformTRS& childBind = skeleton->bindLocal(child);
245 const float len =
246 std::sqrt(childBind.px * childBind.px + childBind.py * childBind.py + childBind.pz * childBind.pz);
247 if (len < 1e-5f) continue;
248 const float radius = len * 0.08f;
249 // Build a stable orthonormal frame around the bone axis.
250 float ax = childBind.px / len, ay = childBind.py / len, az = childBind.pz / len;
251 float bx = 0.f, by = 1.f, bz = 0.f;
252 if (std::fabs(ay) > 0.9f) {
253 bx = 1.f;
254 by = 0.f;
255 bz = 0.f;
256 }
257 float cx = ay * bz - az * by, cy = az * bx - ax * bz, cz = ax * by - ay * bx;
258 float cl = std::sqrt(cx * cx + cy * cy + cz * cz);
259 if (cl < 1e-5f) continue;
260 cx /= cl;
261 cy /= cl;
262 cz /= cl;
263 float dx = ay * cz - az * cy, dy = az * cx - ax * cz, dz = ax * cy - ay * cx;
264 const std::string token = normalizeToken(skeleton->getBoneName(bone));
265 const AnimSmrBodyPart part = classifyBone(skeleton->getBoneName(bone));
266 for (int ring = 0; ring < ringsPerBone; ++ring) {
267 const float t = (static_cast<float>(ring) + 1.f) / (static_cast<float>(ringsPerBone) + 1.f);
268 for (int sample = 0; sample < pointsPerRing; ++sample) {
269 const float angle = 6.28318530718f * static_cast<float>(sample) / static_cast<float>(pointsPerRing);
270 const float ca = std::cos(angle), sa = std::sin(angle);
271 AnimSmrSensor sensor;
272 sensor.boneIndex = bone;
273 sensor.part = part;
274 sensor.localX = childBind.px * t + (cx * ca + dx * sa) * radius;
275 sensor.localY = childBind.py * t + (cy * ca + dy * sa) * radius;
276 sensor.localZ = childBind.pz * t + (cz * ca + dz * sa) * radius;
277 sensor.semanticKey = token + "#r" + std::to_string(ring) + "p" + std::to_string(sample);
278 cloud.sensors_.push_back(sensor);
279 }
280 }
281 }
282 return cloud;
283}
284
286 if (!skeleton) throw Exception("AnimSmrSensorCloud.fromSkeleton: skeleton is null");
287
288 AnimSmrSensorCloud cloud;
289 std::vector<int> firstChild(static_cast<size_t>(skeleton->getBoneCount()), -1);
290 for (int bone = 0; bone < skeleton->getBoneCount(); ++bone) {
291 const int parent = skeleton->getParent(bone);
292 if (parent >= 0 && firstChild[static_cast<size_t>(parent)] < 0) firstChild[static_cast<size_t>(parent)] = bone;
293 }
294
295 for (int bone = 0; bone < skeleton->getBoneCount(); ++bone) {
296 const std::string token = normalizeToken(skeleton->getBoneName(bone));
297 const AnimSmrBodyPart part = classifyBone(skeleton->getBoneName(bone));
298 AnimSmrSensor joint;
299 joint.boneIndex = bone;
300 joint.part = part;
301 joint.semanticKey = token + "#0";
302 cloud.sensors_.push_back(joint);
303
304 const int child = firstChild[static_cast<size_t>(bone)];
305 if (child < 0) continue;
306 const TransformTRS& childBind = skeleton->bindLocal(child);
309 mid.part = part;
310 mid.localX = childBind.px * 0.5f;
311 mid.localY = childBind.py * 0.5f;
312 mid.localZ = childBind.pz * 0.5f;
313 mid.semanticKey = token + "#1";
314 cloud.sensors_.push_back(mid);
315 }
316 return cloud;
317}
318
320 if (index < 0 || index >= getSensorCount()) throw Exception("AnimSmrSensorCloud.getSensor: index out of range");
321 return sensors_[static_cast<size_t>(index)];
322}
323
325
326void AnimSmrSensorCloud::evaluateWorldPositions(const AnimPose* pose, std::vector<float>& outXYZ) const {
327 if (!pose) throw Exception("AnimSmrSensorCloud.evaluateWorldPositions: pose is null");
328 outXYZ.resize(static_cast<size_t>(getSensorCount()) * 3);
329 for (int i = 0; i < getSensorCount(); ++i) {
330 const AnimSmrSensor& sensor = sensors_[static_cast<size_t>(i)];
331 if (sensor.boneIndex < 0 || sensor.boneIndex >= pose->getBoneCount()) {
332 outXYZ[static_cast<size_t>(i) * 3] = 0.f;
333 outXYZ[static_cast<size_t>(i) * 3 + 1] = 0.f;
334 outXYZ[static_cast<size_t>(i) * 3 + 2] = 0.f;
335 continue;
336 }
337 const Vec3 world = transformPoint(pose->world(sensor.boneIndex), sensor.localX, sensor.localY, sensor.localZ);
338 outXYZ[static_cast<size_t>(i) * 3] = world.x;
339 outXYZ[static_cast<size_t>(i) * 3 + 1] = world.y;
340 outXYZ[static_cast<size_t>(i) * 3 + 2] = world.z;
341 }
342}
343
344int smrRefineRetargetedClip(const AnimClip& sourceClip, AnimClip& targetClip, const AnimSkeleton* sourceSkeleton,
345 const AnimSkeleton* targetSkeleton, AnimRetargetProfile& profile) {
346 if (!sourceSkeleton || !targetSkeleton) throw Exception("smrRefineRetargetedClip: skeleton is null");
347
348 if (!profile.skinnedInteractionPreserve_) {
349 profile.interactionCorrectionCount_ = 0;
350 profile.neuralInferenceCount_ = 0;
351 return 0;
352 }
353
354 profile.neuralInferenceCount_ = 0;
355 if (profile.neuralRetargetEnabled_) {
356 if (auto* neural = cap::query<ISmrNeuralRetarget>()) {
357 if (neural->isReady(&profile)) {
359 request.sourceClip = &sourceClip;
360 request.targetClip = &targetClip;
361 request.sourceSkeleton = sourceSkeleton;
362 request.targetSkeleton = targetSkeleton;
363 request.profile = &profile;
364 auto neuralResult = neural->retarget(request);
365 if (neuralResult.ok()) {
366 profile.interactionCorrectionCount_ = neuralResult.value().framesWritten;
367 profile.neuralInferenceCount_ = std::max(1, neuralResult.value().framesWritten);
368 return profile.interactionCorrectionCount_;
369 }
370 }
371 }
372 }
373
374 const AnimSmrSensorCloud sourceCloud = AnimSmrSensorCloud::fromSkeleton(sourceSkeleton);
375 const AnimSmrSensorCloud targetCloud = AnimSmrSensorCloud::fromSkeleton(targetSkeleton);
376 const std::vector<ResolvedChain> chains = resolveChains(targetSkeleton, profile.interactionIkChains_);
377 if (chains.empty() || sourceCloud.getSensorCount() == 0 || targetCloud.getSensorCount() == 0) {
378 profile.interactionCorrectionCount_ = 0;
379 return 0;
380 }
381
382 const float sourceExtent = skeletonExtent(sourceSkeleton);
383 const float targetExtent = skeletonExtent(targetSkeleton);
384 const float sizeScale = targetExtent / sourceExtent;
385 const float contactDistance =
386 std::max(1e-4f, profile.interactionContactThreshold_ > 0.f ? profile.interactionContactThreshold_
387 : sourceExtent * 0.08f);
388 const float weight = clampf(profile.interactionCorrectionWeight_, 0.f, 1.f);
389
390 const float duration = targetClip.getDuration();
391 const float sampleRate = std::max(targetClip.getSampleRate(), 1.f);
392 const int frameCount = duration <= 0.f ? 1 : std::max(1, static_cast<int>(std::ceil(duration * sampleRate)));
393
394 AnimPose sourcePose;
395 AnimPose targetPose;
396 std::vector<float> sourceXYZ;
397 std::vector<float> targetXYZ;
398 std::vector<InteractionPair> pairs;
399 int corrections = 0;
400
401 for (int frame = 0; frame <= frameCount; ++frame) {
402 if (duration <= 0.f && frame > 0) break;
403 const float time =
404 duration <= 0.f ? 0.f : duration * static_cast<float>(frame) / static_cast<float>(frameCount);
405 sourceClip.sample(time, &sourcePose, sourceSkeleton);
406 sourcePose.computeWorld(sourceSkeleton);
407 targetClip.sample(time, &targetPose, targetSkeleton);
408 targetPose.computeWorld(targetSkeleton);
409
410 sourceCloud.evaluateWorldPositions(&sourcePose, sourceXYZ);
411 targetCloud.evaluateWorldPositions(&targetPose, targetXYZ);
412 collectInteractions(sourceCloud, sourceXYZ, contactDistance, pairs);
413
414 bool frameChanged = false;
415 for (const InteractionPair& pair : pairs) {
416 const int targetObserver = matchSensor(sourceCloud, pair.observer, targetCloud);
417 const int targetSubject = matchSensor(sourceCloud, pair.subject, targetCloud);
418 if (targetObserver < 0 || targetSubject < 0) continue;
419
420 const AnimSmrSensor& subjectSensor = targetCloud.getSensor(targetSubject);
421 const int chainIndex = chainForTip(chains, subjectSensor.boneIndex);
422 if (chainIndex < 0) continue;
423
424 const Vec3 observerWorld{targetXYZ[static_cast<size_t>(targetObserver) * 3],
425 targetXYZ[static_cast<size_t>(targetObserver) * 3 + 1],
426 targetXYZ[static_cast<size_t>(targetObserver) * 3 + 2]};
427 const Vec3 desiredSubject = add(observerWorld, mul(pair.relative, sizeScale));
428
429 const TransformTRS& tipWorld = targetPose.world(subjectSensor.boneIndex);
430 const Vec3 tipPos{tipWorld.px, tipWorld.py, tipWorld.pz};
431 const Vec3 sensorWorld{targetXYZ[static_cast<size_t>(targetSubject) * 3],
432 targetXYZ[static_cast<size_t>(targetSubject) * 3 + 1],
433 targetXYZ[static_cast<size_t>(targetSubject) * 3 + 2]};
434 const Vec3 tipTarget = sub(desiredSubject, sub(sensorWorld, tipPos));
435
436 const ResolvedChain& chain = chains[static_cast<size_t>(chainIndex)];
437 if (targetPose.solveTwoBoneIK(targetSkeleton, chain.root, chain.mid, chain.tip, tipTarget.x, tipTarget.y,
438 tipTarget.z, weight)) {
439 writeRotationKey(targetClip, chain.root, frame, time, targetPose.local(chain.root));
440 writeRotationKey(targetClip, chain.mid, frame, time, targetPose.local(chain.mid));
441 writeRotationKey(targetClip, chain.tip, frame, time, targetPose.local(chain.tip));
442 targetPose.computeWorld(targetSkeleton);
443 targetCloud.evaluateWorldPositions(&targetPose, targetXYZ);
444 frameChanged = true;
445 }
446 }
447 if (frameChanged) ++corrections;
448 }
449
450 profile.interactionCorrectionCount_ = corrections;
451 return corrections;
452}
453
454} // namespace eve::animation
LogicalId target
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
float duration
int tip
Definition AnimSmr.cpp:121
int observer
Definition AnimSmr.cpp:162
int root
Definition AnimSmr.cpp:119
int mid
Definition AnimSmr.cpp:120
Vec3 relative
Definition AnimSmr.cpp:164
int subject
Definition AnimSmr.cpp:163
eve::EntitySpatialPose pose
const std::string & s
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
int bz
Definition CaveMesh.cpp:114
int ax
Definition CaveMesh.cpp:113
int ay
Definition CaveMesh.cpp:113
int bx
Definition CaveMesh.cpp:114
float length
Definition CaveMesh.cpp:94
int az
Definition CaveMesh.cpp:113
int by
Definition CaveMesh.cpp:114
const GltfImportRequest & request
glm::vec4 clip
std::int32_t c
std::string local
std::uint32_t bone
std::int32_t parent
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
World3D * world
float radius
bool hit
float begin
float t
uint64_t token
float dz
float dy
float dx
std::uint32_t count
std::map< Cell, int > best
uint32_t index
const UnitySourceAsset & source
float wz
float wx
float angle
float wy
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
Keyframed skeletal animation clip (local TRS tracks per bone). Script type: AnimClip.
Definition AnimClip.h:145
float getDuration() const
Returns the duration.
Definition AnimClip.h:163
void sample(float time, AnimPose *out, const AnimSkeleton *skeleton=nullptr) const
Sample local pose at time (seconds). If skeleton non-null, missing tracks fall back to bind pose; oth...
Definition AnimClip.cpp:575
float getSampleRate() const
Returns the sample rate.
Definition AnimClip.h:173
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
Definition AnimPose.h:17
const TransformTRS & world(int boneIndex) const
World.
Definition AnimPose.cpp:367
TransformTRS & local(int boneIndex)
Local.
Definition AnimPose.cpp:126
void computeWorld(const AnimSkeleton *skeleton)
Compute world transforms from local pose + skeleton hierarchy. World values readable via getWorld* af...
Definition AnimPose.cpp:203
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
Settings and diagnostics for offline skeletal animation retargeting.
Definition AnimClip.h:27
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.
const TransformTRS & bindLocal(int boneIndex) const
Binds local.
std::string getBoneName(int boneIndex) const
Returns the bone name.
Bone-attached sensor cloud for skinned-motion-retarget interaction queries. Script type: AnimSmrSenso...
Definition AnimSmr.h:53
AnimSmrBodyPart getSensorPart(int index) const
Returns the sensor part.
Definition AnimSmr.cpp:324
void evaluateWorldPositions(const AnimPose *pose, std::vector< float > &outXYZ) const
Evaluate world-space sensor positions for a pose that already has computeWorld() applied.
Definition AnimSmr.cpp:326
int getSensorCount() const
Returns the sensor count.
Definition AnimSmr.h:72
static AnimSmrSensorCloud fromSkeleton(const AnimSkeleton *skeleton)
Build sensors for every bone: one at the joint and one mid-segment toward the first child.
Definition AnimSmr.cpp:285
static AnimSmrSensorCloud fromSkeletonDense(const AnimSkeleton *skeleton, int ringsPerBone, int pointsPerRing)
Build denser MeshRet-style SCS rings around each bone segment.
Definition AnimSmr.cpp:228
const AnimSmrSensor & getSensor(int index) const
Returns the sensor.
Definition AnimSmr.cpp:319
AnimSmrBodyPart
Coarse body-part tag used to select MeshRet-style interaction pairs.
Definition AnimSmr.h:18
float clampf(float v, float lo, float hi)
Clampf.
Definition AnimMath.h:34
int smrRefineRetargetedClip(const AnimClip &sourceClip, AnimClip &targetClip, const AnimSkeleton *sourceSkeleton, const AnimSkeleton *targetSkeleton, AnimRetargetProfile &profile)
Refine an FK-retargeted clip so source body-part proximity/contact is preserved on the target.
Definition AnimSmr.cpp:344
WidgetDesc separator(std::string id)
Horizontal separator line.
Definition Widget.cpp:345
One semantically consistent sensor attached to a bone (classical SCS stand-in).
Definition AnimSmr.h:33
std::string semanticKey
Normalized bone name + sample id for cross-skeleton match.
Definition AnimSmr.h:39
Inputs for neural MeshRet-style skinned motion retarget.
Local TRS used by skeletal animation (quaternion xyzw).
Definition AnimMath.h:9