19 if (seconds < 0.f)
throw Exception(
"AnimClip.setDuration: duration must be >= 0");
24 if (hz <= 0.f)
throw Exception(
"AnimClip.setSampleRate: hz must be > 0");
28void AnimClip::ensureBone(
int boneIndex) {
29 if (boneIndex < 0)
throw Exception(
"AnimClip: invalid bone index %d", boneIndex);
30 if (boneIndex >=
static_cast<int>(tracks_.size())) {
31 tracks_.resize(
static_cast<size_t>(boneIndex) + 1);
36 if (time < 0.f)
throw Exception(
"AnimClip.addPositionKey: time must be >= 0");
37 ensureBone(boneIndex);
38 auto &keys = tracks_[
static_cast<size_t>(boneIndex)].positions;
39 keys.push_back({time,
x,
y,
z});
40 std::sort(keys.begin(), keys.end(),
41 [](
const Vec3Key &
a,
const Vec3Key &
b) { return a.t < b.t; });
42 if (time > duration_) duration_ = time;
46 if (time < 0.f)
throw Exception(
"AnimClip.addRotationKey: time must be >= 0");
47 ensureBone(boneIndex);
48 QuatKey k{time,
x,
y,
z,
w};
59 auto &keys = tracks_[
static_cast<size_t>(boneIndex)].rotations;
61 std::sort(keys.begin(), keys.end(),
62 [](
const QuatKey &
a,
const QuatKey &
b) { return a.t < b.t; });
63 if (time > duration_) duration_ = time;
67 if (time < 0.f)
throw Exception(
"AnimClip.addScaleKey: time must be >= 0");
68 ensureBone(boneIndex);
69 auto &keys = tracks_[
static_cast<size_t>(boneIndex)].scales;
70 keys.push_back({time,
x,
y,
z});
71 std::sort(keys.begin(), keys.end(),
72 [](
const Vec3Key &
a,
const Vec3Key &
b) { return a.t < b.t; });
73 if (time > duration_) duration_ = time;
77 if (boneIndex < 0 || boneIndex >=
static_cast<int>(tracks_.size()))
return 0;
78 return static_cast<int>(tracks_[
static_cast<size_t>(boneIndex)].positions.size());
82 if (boneIndex < 0 || boneIndex >=
static_cast<int>(tracks_.size()))
return 0;
83 return static_cast<int>(tracks_[
static_cast<size_t>(boneIndex)].rotations.size());
87 if (boneIndex < 0 || boneIndex >=
static_cast<int>(tracks_.size()))
return 0;
88 return static_cast<int>(tracks_[
static_cast<size_t>(boneIndex)].scales.size());
92void requireKey(
int boneIndex,
int keyIndex,
int count,
const char *what) {
93 if (boneIndex < 0 || keyIndex < 0 || keyIndex >= count) {
94 throw Exception(
"AnimClip: invalid %s key bone=%d index=%d", what, boneIndex, keyIndex);
101 return tracks_[
static_cast<size_t>(boneIndex)].positions[
static_cast<size_t>(keyIndex)].t;
105 return tracks_[
static_cast<size_t>(boneIndex)].positions[
static_cast<size_t>(keyIndex)].x;
109 return tracks_[
static_cast<size_t>(boneIndex)].positions[
static_cast<size_t>(keyIndex)].y;
113 return tracks_[
static_cast<size_t>(boneIndex)].positions[
static_cast<size_t>(keyIndex)].z;
118 return tracks_[
static_cast<size_t>(boneIndex)].rotations[
static_cast<size_t>(keyIndex)].t;
122 return tracks_[
static_cast<size_t>(boneIndex)].rotations[
static_cast<size_t>(keyIndex)].x;
126 return tracks_[
static_cast<size_t>(boneIndex)].rotations[
static_cast<size_t>(keyIndex)].y;
130 return tracks_[
static_cast<size_t>(boneIndex)].rotations[
static_cast<size_t>(keyIndex)].z;
134 return tracks_[
static_cast<size_t>(boneIndex)].rotations[
static_cast<size_t>(keyIndex)].w;
139 return tracks_[
static_cast<size_t>(boneIndex)].scales[
static_cast<size_t>(keyIndex)].t;
143 return tracks_[
static_cast<size_t>(boneIndex)].scales[
static_cast<size_t>(keyIndex)].x;
147 return tracks_[
static_cast<size_t>(boneIndex)].scales[
static_cast<size_t>(keyIndex)].y;
151 return tracks_[
static_cast<size_t>(boneIndex)].scales[
static_cast<size_t>(keyIndex)].z;
155 ensureBone(boneIndex);
156 auto &keys = tracks_[
static_cast<size_t>(boneIndex)].positions;
159 keys.push_back({0.f, 0.f, 0.f, 0.f});
160 keys.push_back({duration_, speedX * duration_, 0.f, speedZ * duration_});
163 for (
auto &k : keys) {
170 if (duration_ <= 1e-8f)
return 0.f;
172 time = std::fmod(time, duration_);
173 if (time < 0.f) time += duration_;
176 return clampf(time, 0.f, duration_);
179void AnimClip::sampleVec3(
const std::vector<Vec3Key> &keys,
float time,
float &
x,
float &
y,
180 float &
z,
bool &
ok) {
182 if (keys.empty())
return;
184 if (time <= keys.front().t || keys.size() == 1) {
190 if (time >= keys.back().t) {
196 for (
size_t i = 0; i + 1 < keys.size(); ++i) {
197 const auto &
a = keys[i];
198 const auto &
b = keys[i + 1];
199 if (time >=
a.t && time <=
b.t) {
200 const float den =
b.t -
a.t;
201 const float t = den > 1e-8f ? (time -
a.t) / den : 0.f;
213void AnimClip::sampleQuat(
const std::vector<QuatKey> &keys,
float time,
float &
x,
float &
y,
214 float &
z,
float &
w,
bool &
ok) {
216 if (keys.empty())
return;
218 if (time <= keys.front().t || keys.size() == 1) {
225 if (time >= keys.back().t) {
232 for (
size_t i = 0; i + 1 < keys.size(); ++i) {
233 const auto &
a = keys[i];
234 const auto &
b = keys[i + 1];
235 if (time >=
a.t && time <=
b.t) {
236 const float den =
b.t -
a.t;
237 const float t = den > 1e-8f ? (time -
a.t) / den : 0.f;
238 slerpQuat(
a.x,
a.y,
a.z,
a.w,
b.x,
b.y,
b.z,
b.w, t,
x,
y,
z,
w);
248TransformTRS AnimClip::sampleBone(
int boneIndex,
float time,
const TransformTRS &fallback)
const {
249 TransformTRS out = fallback;
250 if (boneIndex < 0 || boneIndex >=
static_cast<int>(tracks_.size()))
return out;
251 const BoneTrack &
tr = tracks_[
static_cast<size_t>(boneIndex)];
254 sampleVec3(
tr.positions, time,
x,
y,
z,
ok);
260 sampleQuat(
tr.rotations, time,
x,
y,
z,
w,
ok);
267 sampleVec3(
tr.scales, time,
x,
y,
z,
ok);
277 if (!out)
throw Exception(
"AnimClip.sample: pose is null");
278 const int boneCount = skeleton ? skeleton->
getBoneCount()
279 :
static_cast<int>(tracks_.size());
282 for (
int i = 0; i < boneCount; ++i) {
285 out->
local(i) = sampleBone(i, time, fb);
290 std::swap(name_, other.name_);
291 std::swap(duration_, other.duration_);
292 std::swap(loop_, other.loop_);
293 std::swap(sampleRate_, other.sampleRate_);
294 std::swap(tracks_, other.tracks_);
static void unregister(AnimClip *clip)
Forget a clip everywhere (called from ~AnimClip).
Keyframed skeletal animation clip (local TRS tracks per bone). Script type: AnimClip.
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...
float getPositionKeyZ(int boneIndex, int keyIndex) const
float getRotationKeyW(int boneIndex, int keyIndex) const
void addScaleKey(int boneIndex, float time, float x, float y, float z)
float getScaleKeyY(int boneIndex, int keyIndex) const
void setDuration(float seconds)
float getPositionKeyTime(int boneIndex, int keyIndex) const
void addPositionKey(int boneIndex, float time, float x, float y, float z)
void adopt(AnimClip &other)
Replace this clip's content with other's (payload moved, not copied). Used by hot reload so registere...
float getRotationKeyTime(int boneIndex, int keyIndex) const
int getRotationKeyCount(int boneIndex) const
float getScaleKeyTime(int boneIndex, int keyIndex) const
void applyPlanarRootMotion(int boneIndex, float speedX, float speedZ)
Bake planar root motion onto an existing position track (or create one). For each position key at tim...
float getRotationKeyZ(int boneIndex, int keyIndex) const
int getScaleKeyCount(int boneIndex) const
void setSampleRate(float hz)
Sample rate hint used by MotionDatabase baking (Hz).
float getPositionKeyX(int boneIndex, int keyIndex) const
int getPositionKeyCount(int boneIndex) const
void addRotationKey(int boneIndex, float time, float x, float y, float z, float w)
float getRotationKeyX(int boneIndex, int keyIndex) const
float getRotationKeyY(int boneIndex, int keyIndex) const
float getPositionKeyY(int boneIndex, int keyIndex) const
AnimClip(std::string name="")
float wrapTime(float time) const
Wrap or clamp time according to loop flag.
float getScaleKeyX(int boneIndex, int keyIndex) const
float getScaleKeyZ(int boneIndex, int keyIndex) const
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
TransformTRS & local(int boneIndex)
void resize(int boneCount)
3D bone hierarchy + bind-pose local TRS for skeletal animation. Independent of ik::Skeleton3D (FABRIK...
const TransformTRS & bindLocal(int boneIndex) const
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)
float lerpf(float a, float b, float t)
float clampf(float v, float lo, float hi)