载入中...
搜索中...
未找到
AnimImporter.cpp
浏览该文件的文档.
4
5#include "common/Exception.h"
6
7#include <assimp/anim.h>
8#include <assimp/matrix4x4.h>
9#include <assimp/quaternion.h>
10#include <assimp/scene.h>
11#include <assimp/vector3.h>
12
13#include <fstream>
14#include <sstream>
15
16namespace eve::animation {
17
18namespace {
19
20void decomposeBind(const aiMatrix4x4 &m, float &px, float &py, float &pz, float &qx, float &qy,
21 float &qz, float &qw, float &sx, float &sy, float &sz) {
22 aiVector3D scaling, position;
23 aiQuaternion rotation;
24 m.Decompose(scaling, rotation, position);
25 px = position.x;
26 py = position.y;
27 pz = position.z;
28 qx = rotation.x;
29 qy = rotation.y;
30 qz = rotation.z;
31 qw = rotation.w;
32 sx = scaling.x;
33 sy = scaling.y;
34 sz = scaling.z;
35}
36
37float ticksToSeconds(const aiAnimation *anim, double ticks) {
38 double tps = anim->mTicksPerSecond;
39 if (tps <= 1e-8) tps = 25.0;
40 return static_cast<float>(ticks / tps);
41}
42
43std::string trim(const std::string &s) {
44 size_t a = 0;
45 while (a < s.size() && (s[a] == ' ' || s[a] == '\t' || s[a] == '\r')) ++a;
46 size_t b = s.size();
47 while (b > a && (s[b - 1] == ' ' || s[b - 1] == '\t' || s[b - 1] == '\r')) --b;
48 return s.substr(a, b - a);
49}
50
51} // namespace
52
53void AnimImporter::collectBones(const aiNode *node, int parent, AnimSkeleton *skeleton,
54 std::vector<const aiNode *> &order) {
55 if (!node) return;
56 const std::string name = node->mName.C_Str();
57 const std::string boneName = name.empty() ? ("node_" + std::to_string(order.size())) : name;
58 const int id = skeleton->addBone(boneName, parent);
59 order.push_back(node);
60
61 float px, py, pz, qx, qy, qz, qw, sx, sy, sz;
62 decomposeBind(node->mTransformation, px, py, pz, qx, qy, qz, qw, sx, sy, sz);
63 skeleton->setBindPosition(id, px, py, pz);
64 skeleton->setBindRotation(id, qx, qy, qz, qw);
65 skeleton->setBindScale(id, sx, sy, sz);
66
67 for (unsigned i = 0; i < node->mNumChildren; ++i) {
68 collectBones(node->mChildren[i], id, skeleton, order);
69 }
70}
71
73 if (!scene || !scene->mRootNode) {
74 throw Exception("AnimImporter.loadSkeleton: invalid scene");
75 }
76 auto *skeleton = new AnimSkeleton();
77 std::vector<const aiNode *> order;
78 collectBones(scene->mRootNode, -1, skeleton, order);
79 if (skeleton->getBoneCount() == 0) {
80 delete skeleton;
81 throw Exception("AnimImporter.loadSkeleton: no bones");
82 }
83 return skeleton;
84}
85
86AnimClip *AnimImporter::loadClip(const aiScene *scene, const AnimSkeleton *skeleton,
87 int animIndex) {
88 if (!scene || !skeleton) {
89 throw Exception("AnimImporter.loadClip: null scene/skeleton");
90 }
91 if (animIndex < 0 || animIndex >= getAnimationCount(scene)) {
92 throw Exception("AnimImporter.loadClip: invalid anim index %d", animIndex);
93 }
94 const aiAnimation *anim = scene->mAnimations[animIndex];
95 auto *clip = new AnimClip(anim->mName.length ? anim->mName.C_Str() : "anim");
96 clip->setDuration(ticksToSeconds(anim, anim->mDuration));
97 clip->setLoop(true);
98 clip->setSampleRate(30.f);
99
100 for (unsigned c = 0; c < anim->mNumChannels; ++c) {
101 const aiNodeAnim *channel = anim->mChannels[c];
102 if (!channel) continue;
103 const int bone = skeleton->findBone(channel->mNodeName.C_Str());
104 if (bone < 0) continue;
105
106 for (unsigned i = 0; i < channel->mNumPositionKeys; ++i) {
107 const aiVectorKey &k = channel->mPositionKeys[i];
108 clip->addPositionKey(bone, ticksToSeconds(anim, k.mTime), k.mValue.x, k.mValue.y,
109 k.mValue.z);
110 }
111 for (unsigned i = 0; i < channel->mNumRotationKeys; ++i) {
112 const aiQuatKey &k = channel->mRotationKeys[i];
113 clip->addRotationKey(bone, ticksToSeconds(anim, k.mTime), k.mValue.x, k.mValue.y,
114 k.mValue.z, k.mValue.w);
115 }
116 for (unsigned i = 0; i < channel->mNumScalingKeys; ++i) {
117 const aiVectorKey &k = channel->mScalingKeys[i];
118 clip->addScaleKey(bone, ticksToSeconds(anim, k.mTime), k.mValue.x, k.mValue.y,
119 k.mValue.z);
120 }
121 }
122 return clip;
123}
124
125int AnimImporter::getAnimationCount(const aiScene *scene) {
126 if (!scene) return 0;
127 return static_cast<int>(scene->mNumAnimations);
128}
129
130std::string AnimImporter::getAnimationName(const aiScene *scene, int animIndex) {
131 if (!scene || animIndex < 0 || animIndex >= getAnimationCount(scene)) return {};
132 const aiAnimation *a = scene->mAnimations[animIndex];
133 return a->mName.length ? a->mName.C_Str() : std::string("anim") + std::to_string(animIndex);
134}
135
136std::string AnimImporter::exportEva(const AnimSkeleton *skeleton, const AnimClip *clip) {
137 if (!skeleton || !clip) throw Exception("AnimImporter.exportEva: null argument");
138 std::ostringstream out;
139 out << "EVA 1\n";
140 out << "skeleton " << skeleton->getBoneCount() << "\n";
141 for (int i = 0; i < skeleton->getBoneCount(); ++i) {
142 out << "bone " << i << " " << skeleton->getParent(i) << " " << skeleton->getBoneName(i)
143 << "\n";
144 out << "bind " << i << " " << skeleton->getBindPositionX(i) << " "
145 << skeleton->getBindPositionY(i) << " " << skeleton->getBindPositionZ(i) << " "
146 << skeleton->getBindRotationX(i) << " " << skeleton->getBindRotationY(i) << " "
147 << skeleton->getBindRotationZ(i) << " " << skeleton->getBindRotationW(i) << " "
148 << skeleton->getBindScaleX(i) << " " << skeleton->getBindScaleY(i) << " "
149 << skeleton->getBindScaleZ(i) << "\n";
150 }
151 out << "clip " << clip->getName() << "\n";
152 out << "duration " << clip->getDuration() << "\n";
153 out << "loop " << (clip->getLoop() ? 1 : 0) << "\n";
154 out << "rate " << clip->getSampleRate() << "\n";
155 for (int b = 0; b < skeleton->getBoneCount(); ++b) {
156 const int np = clip->getPositionKeyCount(b);
157 const int nr = clip->getRotationKeyCount(b);
158 const int ns = clip->getScaleKeyCount(b);
159 if (np + nr + ns == 0) continue;
160 out << "track " << b << " " << np << " " << nr << " " << ns << "\n";
161 for (int i = 0; i < np; ++i) {
162 out << "p " << clip->getPositionKeyTime(b, i) << " " << clip->getPositionKeyX(b, i)
163 << " " << clip->getPositionKeyY(b, i) << " " << clip->getPositionKeyZ(b, i) << "\n";
164 }
165 for (int i = 0; i < nr; ++i) {
166 out << "r " << clip->getRotationKeyTime(b, i) << " " << clip->getRotationKeyX(b, i)
167 << " " << clip->getRotationKeyY(b, i) << " " << clip->getRotationKeyZ(b, i) << " "
168 << clip->getRotationKeyW(b, i) << "\n";
169 }
170 for (int i = 0; i < ns; ++i) {
171 out << "s " << clip->getScaleKeyTime(b, i) << " " << clip->getScaleKeyX(b, i) << " "
172 << clip->getScaleKeyY(b, i) << " " << clip->getScaleKeyZ(b, i) << "\n";
173 }
174 }
175 out << "end\n";
176 return out.str();
177}
178
179void AnimImporter::importEva(const std::string &text, AnimSkeleton **skeletonOut,
180 AnimClip **clipOut) {
181 if (!skeletonOut || !clipOut) throw Exception("AnimImporter.importEva: null out");
182 *skeletonOut = nullptr;
183 *clipOut = nullptr;
184
185 auto *skeleton = new AnimSkeleton();
186 AnimClip *clip = nullptr;
187 int curBone = -1;
188
189 std::istringstream in(text);
190 std::string line;
191 try {
192 while (std::getline(in, line)) {
193 line = trim(line);
194 if (line.empty() || line[0] == '#') continue;
195 std::istringstream ls(line);
196 std::string tag;
197 ls >> tag;
198 if (tag == "EVA") {
199 int ver = 0;
200 ls >> ver;
201 if (ver != 1) throw Exception("AnimImporter.importEva: unsupported version");
202 } else if (tag == "skeleton") {
203 // informational
204 } else if (tag == "bone") {
205 int idx = -1, parent = -1;
206 std::string name;
207 ls >> idx >> parent;
208 std::getline(ls, name);
209 name = trim(name);
210 if (name.empty()) throw Exception("AnimImporter.importEva: empty bone name");
211 const int id = skeleton->addBone(name, parent);
212 if (id != idx) {
213 throw Exception("AnimImporter.importEva: bone index mismatch");
214 }
215 } else if (tag == "bind") {
216 int idx = -1;
217 float px, py, pz, qx, qy, qz, qw, sx, sy, sz;
218 ls >> idx >> px >> py >> pz >> qx >> qy >> qz >> qw >> sx >> sy >> sz;
219 skeleton->setBindPosition(idx, px, py, pz);
220 skeleton->setBindRotation(idx, qx, qy, qz, qw);
221 skeleton->setBindScale(idx, sx, sy, sz);
222 } else if (tag == "clip") {
223 std::string name;
224 std::getline(ls, name);
225 name = trim(name);
226 clip = new AnimClip(name.empty() ? "clip" : name);
227 } else if (tag == "duration") {
228 if (!clip) throw Exception("AnimImporter.importEva: duration before clip");
229 float d = 0.f;
230 ls >> d;
231 clip->setDuration(d);
232 } else if (tag == "loop") {
233 if (!clip) throw Exception("AnimImporter.importEva: loop before clip");
234 int v = 1;
235 ls >> v;
236 clip->setLoop(v != 0);
237 } else if (tag == "rate") {
238 if (!clip) throw Exception("AnimImporter.importEva: rate before clip");
239 float r = 30.f;
240 ls >> r;
241 clip->setSampleRate(r);
242 } else if (tag == "track") {
243 if (!clip) throw Exception("AnimImporter.importEva: track before clip");
244 int np = 0, nr = 0, ns = 0;
245 ls >> curBone >> np >> nr >> ns;
246 (void)np;
247 (void)nr;
248 (void)ns;
249 } else if (tag == "p") {
250 float t, x, y, z;
251 ls >> t >> x >> y >> z;
252 clip->addPositionKey(curBone, t, x, y, z);
253 } else if (tag == "r") {
254 float t, x, y, z, w;
255 ls >> t >> x >> y >> z >> w;
256 clip->addRotationKey(curBone, t, x, y, z, w);
257 } else if (tag == "s") {
258 float t, x, y, z;
259 ls >> t >> x >> y >> z;
260 clip->addScaleKey(curBone, t, x, y, z);
261 } else if (tag == "end") {
262 break;
263 }
264 }
265 if (!clip) throw Exception("AnimImporter.importEva: missing clip");
266 *skeletonOut = skeleton;
267 *clipOut = clip;
268 } catch (...) {
269 delete skeleton;
270 delete clip;
271 throw;
272 }
273}
274
275void AnimImporter::importEvaFile(const std::string &path, AnimSkeleton **skeletonOut,
276 AnimClip **clipOut) {
277 std::ifstream in(path);
278 if (!in) throw Exception("AnimImporter.importEvaFile: cannot open %s", path.c_str());
279 std::ostringstream ss;
280 ss << in.rdbuf();
281 importEva(ss.str(), skeletonOut, clipOut);
282}
283
284} // namespace eve::animation
int line
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
int x
Definition Grass.cpp:135
int w
std::vector< Colorf > px
uint32_t a
uint32_t b
uint32_t c
int idx
const char * name
Definition RockMesh.cpp:21
int d
int v
int parent
Definition TreeMesh.cpp:175
float m[16]
uint32_t s
Definition Weather.cpp:28
Keyframed skeletal animation clip (local TRS tracks per bone). Script type: AnimClip.
Definition AnimClip.h:17
float getDuration() const
Definition AnimClip.h:29
bool getLoop() const
Definition AnimClip.h:32
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
float getSampleRate() const
Definition AnimClip.h:36
void addPositionKey(int boneIndex, float time, float x, float y, float z)
Definition AnimClip.cpp:35
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
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
std::string getName() const
Definition AnimClip.h:26
float getRotationKeyY(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:124
float getPositionKeyY(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:107
float getScaleKeyX(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:141
float getScaleKeyZ(int boneIndex, int keyIndex) const
Definition AnimClip.cpp:149
void setLoop(bool loop)
Definition AnimClip.h:31
static int getAnimationCount(const aiScene *scene)
static AnimClip * loadClip(const aiScene *scene, const AnimSkeleton *skeleton, int animIndex=0)
Load clip by index; maps node-name channels onto skeleton bones.
static void importEvaFile(const std::string &path, AnimSkeleton **skeletonOut, AnimClip **clipOut)
Load from .eva file path.
static std::string getAnimationName(const aiScene *scene, int animIndex)
static std::string exportEva(const AnimSkeleton *skeleton, const AnimClip *clip)
Serialize skeleton + clip to a compact text format (no mesh). Used to ship Mixamo-derived test fixtur...
static AnimSkeleton * loadSkeleton(const aiScene *scene)
Build skeleton from the scene node hierarchy (depth-first).
static void importEva(const std::string &text, AnimSkeleton **skeletonOut, AnimClip **clipOut)
Load skeleton+clip from exportEva text (new'd; caller owns).
3D bone hierarchy + bind-pose local TRS for skeletal animation. Independent of ik::Skeleton3D (FABRIK...
float getBindPositionY(int boneIndex) const
float getBindScaleX(int boneIndex) const
int getParent(int boneIndex) const
float getBindPositionZ(int boneIndex) const
float getBindRotationW(int boneIndex) const
float getBindScaleY(int boneIndex) const
float getBindRotationY(int boneIndex) const
float getBindScaleZ(int boneIndex) const
float getBindPositionX(int boneIndex) const
float getBindRotationZ(int boneIndex) const
float getBindRotationX(int boneIndex) const
std::string getBoneName(int boneIndex) const
int findBone(const std::string &name) const
NodeDesc node(std::string id, std::vector< NodeDesc > children, std::string name)
Definition NodeDesc.cpp:214