载入中...
搜索中...
未找到
AnimClip.cpp
浏览该文件的文档.
5
6#include "common/Exception.h"
7
8#include <algorithm>
9#include <cmath>
10#include <utility>
11
12namespace eve::animation {
13
14AnimClip::AnimClip(std::string name) : name_(std::move(name)) {}
15
17
18void AnimClip::setDuration(float seconds) {
19 if (seconds < 0.f) throw Exception("AnimClip.setDuration: duration must be >= 0");
20 duration_ = seconds;
21}
22
24 if (hz <= 0.f) throw Exception("AnimClip.setSampleRate: hz must be > 0");
25 sampleRate_ = hz;
26}
27
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);
32 }
33}
34
35void AnimClip::addPositionKey(int boneIndex, float time, float x, float y, float z) {
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;
43}
44
45void AnimClip::addRotationKey(int boneIndex, float time, float x, float y, float z, float w) {
46 if (time < 0.f) throw Exception("AnimClip.addRotationKey: time must be >= 0");
47 ensureBone(boneIndex);
48 QuatKey k{time, x, y, z, w};
49 TransformTRS tmp;
50 tmp.qx = x;
51 tmp.qy = y;
52 tmp.qz = z;
53 tmp.qw = w;
55 k.x = tmp.qx;
56 k.y = tmp.qy;
57 k.z = tmp.qz;
58 k.w = tmp.qw;
59 auto &keys = tracks_[static_cast<size_t>(boneIndex)].rotations;
60 keys.push_back(k);
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;
64}
65
66void AnimClip::addScaleKey(int boneIndex, float time, float x, float y, float z) {
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;
74}
75
76int AnimClip::getPositionKeyCount(int boneIndex) const {
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());
79}
80
81int AnimClip::getRotationKeyCount(int boneIndex) const {
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());
84}
85
86int AnimClip::getScaleKeyCount(int boneIndex) const {
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());
89}
90
91namespace {
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);
95 }
96}
97} // namespace
98
99float AnimClip::getPositionKeyTime(int boneIndex, int keyIndex) const {
100 requireKey(boneIndex, keyIndex, getPositionKeyCount(boneIndex), "position");
101 return tracks_[static_cast<size_t>(boneIndex)].positions[static_cast<size_t>(keyIndex)].t;
102}
103float AnimClip::getPositionKeyX(int boneIndex, int keyIndex) const {
104 requireKey(boneIndex, keyIndex, getPositionKeyCount(boneIndex), "position");
105 return tracks_[static_cast<size_t>(boneIndex)].positions[static_cast<size_t>(keyIndex)].x;
106}
107float AnimClip::getPositionKeyY(int boneIndex, int keyIndex) const {
108 requireKey(boneIndex, keyIndex, getPositionKeyCount(boneIndex), "position");
109 return tracks_[static_cast<size_t>(boneIndex)].positions[static_cast<size_t>(keyIndex)].y;
110}
111float AnimClip::getPositionKeyZ(int boneIndex, int keyIndex) const {
112 requireKey(boneIndex, keyIndex, getPositionKeyCount(boneIndex), "position");
113 return tracks_[static_cast<size_t>(boneIndex)].positions[static_cast<size_t>(keyIndex)].z;
114}
115
116float AnimClip::getRotationKeyTime(int boneIndex, int keyIndex) const {
117 requireKey(boneIndex, keyIndex, getRotationKeyCount(boneIndex), "rotation");
118 return tracks_[static_cast<size_t>(boneIndex)].rotations[static_cast<size_t>(keyIndex)].t;
119}
120float AnimClip::getRotationKeyX(int boneIndex, int keyIndex) const {
121 requireKey(boneIndex, keyIndex, getRotationKeyCount(boneIndex), "rotation");
122 return tracks_[static_cast<size_t>(boneIndex)].rotations[static_cast<size_t>(keyIndex)].x;
123}
124float AnimClip::getRotationKeyY(int boneIndex, int keyIndex) const {
125 requireKey(boneIndex, keyIndex, getRotationKeyCount(boneIndex), "rotation");
126 return tracks_[static_cast<size_t>(boneIndex)].rotations[static_cast<size_t>(keyIndex)].y;
127}
128float AnimClip::getRotationKeyZ(int boneIndex, int keyIndex) const {
129 requireKey(boneIndex, keyIndex, getRotationKeyCount(boneIndex), "rotation");
130 return tracks_[static_cast<size_t>(boneIndex)].rotations[static_cast<size_t>(keyIndex)].z;
131}
132float AnimClip::getRotationKeyW(int boneIndex, int keyIndex) const {
133 requireKey(boneIndex, keyIndex, getRotationKeyCount(boneIndex), "rotation");
134 return tracks_[static_cast<size_t>(boneIndex)].rotations[static_cast<size_t>(keyIndex)].w;
135}
136
137float AnimClip::getScaleKeyTime(int boneIndex, int keyIndex) const {
138 requireKey(boneIndex, keyIndex, getScaleKeyCount(boneIndex), "scale");
139 return tracks_[static_cast<size_t>(boneIndex)].scales[static_cast<size_t>(keyIndex)].t;
140}
141float AnimClip::getScaleKeyX(int boneIndex, int keyIndex) const {
142 requireKey(boneIndex, keyIndex, getScaleKeyCount(boneIndex), "scale");
143 return tracks_[static_cast<size_t>(boneIndex)].scales[static_cast<size_t>(keyIndex)].x;
144}
145float AnimClip::getScaleKeyY(int boneIndex, int keyIndex) const {
146 requireKey(boneIndex, keyIndex, getScaleKeyCount(boneIndex), "scale");
147 return tracks_[static_cast<size_t>(boneIndex)].scales[static_cast<size_t>(keyIndex)].y;
148}
149float AnimClip::getScaleKeyZ(int boneIndex, int keyIndex) const {
150 requireKey(boneIndex, keyIndex, getScaleKeyCount(boneIndex), "scale");
151 return tracks_[static_cast<size_t>(boneIndex)].scales[static_cast<size_t>(keyIndex)].z;
152}
153
154void AnimClip::applyPlanarRootMotion(int boneIndex, float speedX, float speedZ) {
155 ensureBone(boneIndex);
156 auto &keys = tracks_[static_cast<size_t>(boneIndex)].positions;
157 if (keys.empty()) {
158 // Create start/end keys from zero so motion matching sees a trajectory.
159 keys.push_back({0.f, 0.f, 0.f, 0.f});
160 keys.push_back({duration_, speedX * duration_, 0.f, speedZ * duration_});
161 return;
162 }
163 for (auto &k : keys) {
164 k.x += speedX * k.t;
165 k.z += speedZ * k.t;
166 }
167}
168
169float AnimClip::wrapTime(float time) const {
170 if (duration_ <= 1e-8f) return 0.f;
171 if (loop_) {
172 time = std::fmod(time, duration_);
173 if (time < 0.f) time += duration_;
174 return time;
175 }
176 return clampf(time, 0.f, duration_);
177}
178
179void AnimClip::sampleVec3(const std::vector<Vec3Key> &keys, float time, float &x, float &y,
180 float &z, bool &ok) {
181 ok = false;
182 if (keys.empty()) return;
183 ok = true;
184 if (time <= keys.front().t || keys.size() == 1) {
185 x = keys.front().x;
186 y = keys.front().y;
187 z = keys.front().z;
188 return;
189 }
190 if (time >= keys.back().t) {
191 x = keys.back().x;
192 y = keys.back().y;
193 z = keys.back().z;
194 return;
195 }
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;
202 x = lerpf(a.x, b.x, t);
203 y = lerpf(a.y, b.y, t);
204 z = lerpf(a.z, b.z, t);
205 return;
206 }
207 }
208 x = keys.back().x;
209 y = keys.back().y;
210 z = keys.back().z;
211}
212
213void AnimClip::sampleQuat(const std::vector<QuatKey> &keys, float time, float &x, float &y,
214 float &z, float &w, bool &ok) {
215 ok = false;
216 if (keys.empty()) return;
217 ok = true;
218 if (time <= keys.front().t || keys.size() == 1) {
219 x = keys.front().x;
220 y = keys.front().y;
221 z = keys.front().z;
222 w = keys.front().w;
223 return;
224 }
225 if (time >= keys.back().t) {
226 x = keys.back().x;
227 y = keys.back().y;
228 z = keys.back().z;
229 w = keys.back().w;
230 return;
231 }
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);
239 return;
240 }
241 }
242 x = keys.back().x;
243 y = keys.back().y;
244 z = keys.back().z;
245 w = keys.back().w;
246}
247
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)];
252 bool ok = false;
253 float x, y, z, w;
254 sampleVec3(tr.positions, time, x, y, z, ok);
255 if (ok) {
256 out.px = x;
257 out.py = y;
258 out.pz = z;
259 }
260 sampleQuat(tr.rotations, time, x, y, z, w, ok);
261 if (ok) {
262 out.qx = x;
263 out.qy = y;
264 out.qz = z;
265 out.qw = w;
266 }
267 sampleVec3(tr.scales, time, x, y, z, ok);
268 if (ok) {
269 out.sx = x;
270 out.sy = y;
271 out.sz = z;
272 }
273 return out;
274}
275
276void AnimClip::sample(float time, AnimPose *out, const AnimSkeleton *skeleton) const {
277 if (!out) throw Exception("AnimClip.sample: pose is null");
278 const int boneCount = skeleton ? skeleton->getBoneCount()
279 : static_cast<int>(tracks_.size());
280 out->resize(boneCount);
281 time = wrapTime(time);
282 for (int i = 0; i < boneCount; ++i) {
283 const TransformTRS &fb =
284 skeleton ? skeleton->bindLocal(i) : TransformTRS::identity();
285 out->local(i) = sampleBone(i, time, fb);
286 }
287}
288
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_);
295}
296
297} // namespace eve::animation
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
int x
Definition Grass.cpp:135
int w
uint32_t a
uint32_t b
float tr
const char * name
Definition RockMesh.cpp:21
static void unregister(AnimClip *clip)
Forget a clip everywhere (called from ~AnimClip).
Keyframed skeletal animation clip (local TRS tracks per bone). Script type: AnimClip.
Definition AnimClip.h:17
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:276
float getPositionKeyZ(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:111
float getRotationKeyW(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:132
void addScaleKey(int boneIndex, float time, float x, float y, float z)
Definition AnimClip.cpp:66
float getScaleKeyY(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:145
void setDuration(float seconds)
Definition AnimClip.cpp:18
float getPositionKeyTime(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:99
void addPositionKey(int boneIndex, float time, float x, float y, float z)
Definition AnimClip.cpp:35
void adopt(AnimClip &other)
Replace this clip's content with other's (payload moved, not copied). Used by hot reload so registere...
Definition AnimClip.cpp:289
float getRotationKeyTime(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:116
int getRotationKeyCount(int boneIndex) const
Definition AnimClip.cpp:81
float getScaleKeyTime(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:137
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...
Definition AnimClip.cpp:154
float getRotationKeyZ(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:128
int getScaleKeyCount(int boneIndex) const
Definition AnimClip.cpp:86
void setSampleRate(float hz)
Sample rate hint used by MotionDatabase baking (Hz).
Definition AnimClip.cpp:23
float getPositionKeyX(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:103
int getPositionKeyCount(int boneIndex) const
Definition AnimClip.cpp:76
void addRotationKey(int boneIndex, float time, float x, float y, float z, float w)
Definition AnimClip.cpp:45
float getRotationKeyX(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:120
float getRotationKeyY(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:124
float getPositionKeyY(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:107
AnimClip(std::string name="")
Definition AnimClip.cpp:14
float wrapTime(float time) const
Wrap or clamp time according to loop flag.
Definition AnimClip.cpp:169
float getScaleKeyX(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:141
float getScaleKeyZ(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:149
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
Definition AnimPose.h:15
TransformTRS & local(int boneIndex)
Definition AnimPose.cpp:74
void resize(int boneCount)
Definition AnimPose.cpp:44
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)
Definition AnimMath.h:37
float lerpf(float a, float b, float t)
Definition AnimMath.h:35
float clampf(float v, float lo, float hi)
Definition AnimMath.h:31
Local TRS used by skeletal animation (quaternion xyzw).
Definition AnimMath.h:9
static TransformTRS identity()
Definition AnimMath.h:14