载入中...
搜索中...
未找到
MotionDatabase.cpp
浏览该文件的文档.
4
5#include "common/Exception.h"
6
7#include <cmath>
8
9namespace eve::animation {
10
11MotionDatabase::MotionDatabase(AnimSkeleton *skeleton) : skeleton_(skeleton) {
12 if (!skeleton_) throw Exception("MotionDatabase: skeleton is null");
13 scratchPose_.resize(skeleton_->getBoneCount());
14 // Default: use every non-root bone if none specified before bake.
15}
16
17void MotionDatabase::addFeatureBone(int boneIndex) {
18 if (boneIndex < 0 || boneIndex >= skeleton_->getBoneCount()) {
19 throw Exception("MotionDatabase.addFeatureBone: invalid bone %d", boneIndex);
20 }
21 for (int b : featureBones_) {
22 if (b == boneIndex) return;
23 }
24 featureBones_.push_back(boneIndex);
25 baked_ = false;
26}
27
29 const int id = skeleton_->findBone(name);
30 if (id < 0) throw Exception("MotionDatabase.addFeatureBoneByName: unknown '%s'", name.c_str());
32}
33
34void MotionDatabase::setRootBone(int boneIndex) {
35 if (boneIndex < 0 || boneIndex >= skeleton_->getBoneCount()) {
36 throw Exception("MotionDatabase.setRootBone: invalid bone %d", boneIndex);
37 }
38 rootBone_ = boneIndex;
39 baked_ = false;
40}
41
42void MotionDatabase::setRootBoneByName(const std::string &name) {
43 const int id = skeleton_->findBone(name);
44 if (id < 0) throw Exception("MotionDatabase.setRootBoneByName: unknown '%s'", name.c_str());
45 setRootBone(id);
46}
47
49 if (!clip) throw Exception("MotionDatabase.addClip: clip is null");
50 clips_.push_back(clip);
51 baked_ = false;
52}
53
54AnimClip *MotionDatabase::getClip(int clipIndex) const {
55 if (clipIndex < 0 || clipIndex >= getClipCount()) {
56 throw Exception("MotionDatabase.getClip: invalid index %d", clipIndex);
57 }
58 return clips_[static_cast<size_t>(clipIndex)];
59}
60
61void MotionDatabase::computeFeatureSize() {
62 // vel(2) + trajPos*3(6) + trajFacing(2) + bones*3
63 featureSize_ = 2 + 6 + 2 + static_cast<int>(featureBones_.size()) * 3;
64}
65
66float MotionDatabase::yawFromQuat(float /*x*/, float y, float /*z*/, float w) {
67 // Yaw from quaternion (Y-up), assuming mostly planar rotation.
68 return std::atan2(2.f * (w * y), 1.f - 2.f * (y * y));
69}
70
71int MotionDatabase::getFeatureBone(int index) const {
72 if (index < 0 || index >= getFeatureBoneCount()) {
73 throw Exception("MotionDatabase.getFeatureBone: invalid index %d", index);
74 }
75 return featureBones_[static_cast<size_t>(index)];
76}
77
78void MotionDatabase::extractFeature(AnimClip *clip, float time, float dtSample,
79 std::vector<float> &out, float &rootX, float &rootZ,
80 float &rootYaw, float &velX, float &velZ) const {
81 out.assign(static_cast<size_t>(featureSize_), 0.f);
82 clip->sample(time, &scratchPose_, skeleton_);
83 scratchPose_.computeWorld(skeleton_);
84
85 const int root = rootBone_;
86 rootX = scratchPose_.getWorldPositionX(root);
87 rootZ = scratchPose_.getWorldPositionZ(root);
88 rootYaw = yawFromQuat(scratchPose_.getWorldRotationX(root),
89 scratchPose_.getWorldRotationY(root),
90 scratchPose_.getWorldRotationZ(root),
91 scratchPose_.getWorldRotationW(root));
92
93 // Velocity from nearby sample.
94 const float t1 = time + std::max(dtSample, 1e-3f);
95 AnimPose next;
96 clip->sample(t1, &next, skeleton_);
97 next.computeWorld(skeleton_);
98 const float nX = next.getWorldPositionX(root);
99 const float nZ = next.getWorldPositionZ(root);
100 const float dtt = std::max(dtSample, 1e-3f);
101 velX = (nX - rootX) / dtt;
102 velZ = (nZ - rootZ) / dtt;
103
104 float cs = std::cos(rootYaw);
105 float sn = std::sin(rootYaw);
106 // Character-space: rotate world xz by -yaw
107 auto toLocal = [&](float wx, float wz, float &lx, float &lz) {
108 const float dx = wx - rootX;
109 const float dz = wz - rootZ;
110 lx = dx * cs + dz * sn;
111 lz = -dx * sn + dz * cs;
112 };
113
114 out[0] = velX * cs + velZ * sn;
115 out[1] = -velX * sn + velZ * cs;
116
117 const float horizons[3] = {0.33f, 0.66f, 1.0f};
118 for (int h = 0; h < 3; ++h) {
119 AnimPose fut;
120 clip->sample(time + horizons[h], &fut, skeleton_);
121 fut.computeWorld(skeleton_);
122 float lx, lz;
123 toLocal(fut.getWorldPositionX(root), fut.getWorldPositionZ(root), lx, lz);
124 out[2 + h * 2] = lx;
125 out[2 + h * 2 + 1] = lz;
126 }
127
128 {
129 AnimPose fut;
130 clip->sample(time + 1.0f, &fut, skeleton_);
131 fut.computeWorld(skeleton_);
132 const float fyaw = yawFromQuat(fut.getWorldRotationX(root), fut.getWorldRotationY(root),
133 fut.getWorldRotationZ(root), fut.getWorldRotationW(root));
134 float fx, fz;
135 yawToForward(fyaw - rootYaw, fx, fz);
136 out[8] = fx;
137 out[9] = fz;
138 }
139
140 int base = 10;
141 for (int bone : featureBones_) {
142 float lx, lz;
143 const float wy = scratchPose_.getWorldPositionY(bone);
144 toLocal(scratchPose_.getWorldPositionX(bone), scratchPose_.getWorldPositionZ(bone), lx,
145 lz);
146 out[static_cast<size_t>(base)] = lx;
147 out[static_cast<size_t>(base + 1)] = wy;
148 out[static_cast<size_t>(base + 2)] = lz;
149 base += 3;
150 }
151}
152
154 if (clips_.empty()) throw Exception("MotionDatabase.bake: no clips");
155 if (featureBones_.empty()) {
156 // Default: all bones except root.
157 for (int i = 1; i < skeleton_->getBoneCount(); ++i) addFeatureBone(i);
158 if (featureBones_.empty() && skeleton_->getBoneCount() > 0) addFeatureBone(0);
159 }
160 computeFeatureSize();
161 frames_.clear();
162
163 for (int ci = 0; ci < getClipCount(); ++ci) {
164 AnimClip *clip = clips_[static_cast<size_t>(ci)];
165 const float rate = clip->getSampleRate() > 0.f ? clip->getSampleRate() : 30.f;
166 const float dt = 1.f / rate;
167 const float dur = clip->getDuration();
168 if (dur <= 0.f) {
169 Frame f;
170 f.clipIndex = ci;
171 f.time = 0.f;
172 extractFeature(clip, 0.f, dt, f.feature, f.rootX, f.rootZ, f.rootYaw, f.velX, f.velZ);
173 frames_.push_back(std::move(f));
174 continue;
175 }
176 for (float t = 0.f; t < dur - 1e-5f; t += dt) {
177 Frame f;
178 f.clipIndex = ci;
179 f.time = t;
180 extractFeature(clip, t, dt, f.feature, f.rootX, f.rootZ, f.rootYaw, f.velX, f.velZ);
181 frames_.push_back(std::move(f));
182 }
183 }
184 baked_ = true;
185}
186
187void MotionDatabase::requireBaked() const {
188 if (!baked_) throw Exception("MotionDatabase: not baked; call bake() first");
189}
190
192 requireBaked();
193 if (index < 0 || index >= getFrameCount()) {
194 throw Exception("MotionDatabase: invalid frame %d", index);
195 }
196 return frames_[static_cast<size_t>(index)];
197}
198
199float MotionDatabase::getFrameTime(int frameIndex) const { return frameAt(frameIndex).time; }
200
201int MotionDatabase::getFrameClipIndex(int frameIndex) const {
202 return frameAt(frameIndex).clipIndex;
203}
204
205void MotionDatabase::getFeature(int frameIndex, float *out, int outCount) const {
206 const Frame &f = frameAt(frameIndex);
207 if (!out || outCount < featureSize_) {
208 throw Exception("MotionDatabase.getFeature: buffer too small");
209 }
210 for (int i = 0; i < featureSize_; ++i) out[i] = f.feature[static_cast<size_t>(i)];
211}
212
213} // namespace eve::animation
int y
Definition Grass.cpp:135
int h
int w
uint32_t b
float f
const char * name
Definition RockMesh.cpp:21
Keyframed skeletal animation clip (local TRS tracks per bone). Script type: AnimClip.
Definition AnimClip.h:17
float getDuration() const
Definition AnimClip.h:29
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 getSampleRate() const
Definition AnimClip.h:36
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
Definition AnimPose.h:15
float getWorldRotationX(int boneIndex) const
Definition AnimPose.cpp:180
float getWorldPositionZ(int boneIndex) const
Definition AnimPose.cpp:176
float getWorldPositionX(int boneIndex) const
Definition AnimPose.cpp:168
float getWorldPositionY(int boneIndex) const
Definition AnimPose.cpp:172
float getWorldRotationW(int boneIndex) const
Definition AnimPose.cpp:192
float getWorldRotationZ(int boneIndex) const
Definition AnimPose.cpp:188
float getWorldRotationY(int boneIndex) const
Definition AnimPose.cpp:184
void computeWorld(const AnimSkeleton *skeleton)
Compute world transforms from local pose + skeleton hierarchy. World values readable via getWorld* af...
Definition AnimPose.cpp:151
void resize(int boneCount)
Definition AnimPose.cpp:44
3D bone hierarchy + bind-pose local TRS for skeletal animation. Independent of ik::Skeleton3D (FABRIK...
int findBone(const std::string &name) const
void setRootBoneByName(const std::string &name)
void getFeature(int frameIndex, float *out, int outCount) const
Copy feature vector into out[0..featureSize).
void addFeatureBone(int boneIndex)
Include bone world position in pose features (by index).
float getFrameTime(int frameIndex) const
AnimClip * getClip(int clipIndex) const
const Frame & frameAt(int index) const
void addFeatureBoneByName(const std::string &name)
void bake()
Bake all clips into searchable frames. Call after addClip / feature bones.
int getFeatureBone(int index) const
void setRootBone(int boneIndex)
Bone used for trajectory / velocity features (default 0). Mixamo clips typically want hips (mixamorig...
MotionDatabase(AnimSkeleton *skeleton)
int getFrameClipIndex(int frameIndex) const
void yawToForward(float yaw, float &fx, float &fz)
Rotate unit +Z by yaw (radians) around Y — used for planar facing.
Definition AnimMath.h:86