/src/assimp/code/AssetLib/BVH/BVHLoader.cpp
Line | Count | Source |
1 | | /** Implementation of the BVH loader */ |
2 | | /* |
3 | | --------------------------------------------------------------------------- |
4 | | Open Asset Import Library (assimp) |
5 | | --------------------------------------------------------------------------- |
6 | | |
7 | | Copyright (c) 2006-2025, assimp team |
8 | | |
9 | | All rights reserved. |
10 | | |
11 | | Redistribution and use of this software in source and binary forms, |
12 | | with or without modification, are permitted provided that the following |
13 | | conditions are met: |
14 | | |
15 | | * Redistributions of source code must retain the above |
16 | | copyright notice, this list of conditions and the |
17 | | following disclaimer. |
18 | | |
19 | | * Redistributions in binary form must reproduce the above |
20 | | copyright notice, this list of conditions and the |
21 | | following disclaimer in the documentation and/or other |
22 | | materials provided with the distribution. |
23 | | |
24 | | * Neither the name of the assimp team, nor the names of its |
25 | | contributors may be used to endorse or promote products |
26 | | derived from this software without specific prior |
27 | | written permission of the assimp team. |
28 | | |
29 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 | | --------------------------------------------------------------------------- |
41 | | */ |
42 | | |
43 | | #ifndef ASSIMP_BUILD_NO_BVH_IMPORTER |
44 | | |
45 | | #include "BVHLoader.h" |
46 | | #include <assimp/SkeletonMeshBuilder.h> |
47 | | #include <assimp/TinyFormatter.h> |
48 | | #include <assimp/fast_atof.h> |
49 | | #include <assimp/importerdesc.h> |
50 | | #include <assimp/scene.h> |
51 | | #include <assimp/IOSystem.hpp> |
52 | | #include <assimp/Importer.hpp> |
53 | | #include <map> |
54 | | #include <memory> |
55 | | |
56 | | namespace Assimp { |
57 | | |
58 | | using namespace Assimp::Formatter; |
59 | | |
60 | | static constexpr aiImporterDesc desc = { |
61 | | "BVH Importer (MoCap)", |
62 | | "", |
63 | | "", |
64 | | "", |
65 | | aiImporterFlags_SupportTextFlavour, |
66 | | 0, |
67 | | 0, |
68 | | 0, |
69 | | 0, |
70 | | "bvh" |
71 | | }; |
72 | | |
73 | | // ------------------------------------------------------------------------------------------------ |
74 | | // Aborts the file reading with an exception |
75 | | template <typename... T> |
76 | 0 | AI_WONT_RETURN void BVHLoader::ThrowException(T &&...args) { |
77 | 0 | throw DeadlyImportError(mFileName, ":", mLine, " - ", args...); |
78 | 0 | } Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [36]>(char const (&) [36]) Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [44]>(char const (&) [44]) Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [27]>(char const (&) [27]) Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [32], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]>(char const (&) [32], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]) Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [40], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]>(char const (&) [40], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]) Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [41], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [2], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]>(char const (&) [41], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [2], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]) Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [18], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]>(char const (&) [18], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]) Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [28], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]>(char const (&) [28], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]) Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [44], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]>(char const (&) [44], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]) Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [51], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [2], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]>(char const (&) [51], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [2], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]) Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [52]>(char const (&) [52]) Unexecuted instantiation: void Assimp::BVHLoader::ThrowException<char const (&) [46], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]>(char const (&) [46], std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const (&) [3]) |
79 | | |
80 | | // ------------------------------------------------------------------------------------------------ |
81 | | // Constructor to be privately used by Importer |
82 | | BVHLoader::BVHLoader() : |
83 | | mLine(), |
84 | | mAnimTickDuration(), |
85 | | mAnimNumFrames(), |
86 | 1.19k | noSkeletonMesh() { |
87 | | // empty |
88 | 1.19k | } |
89 | | |
90 | | // ------------------------------------------------------------------------------------------------ |
91 | | // Returns whether the class can handle the format of the given file. |
92 | 487 | bool BVHLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { |
93 | 487 | static const char *tokens[] = { "HIERARCHY" }; |
94 | 487 | return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); |
95 | 487 | } |
96 | | |
97 | | // ------------------------------------------------------------------------------------------------ |
98 | 0 | void BVHLoader::SetupProperties(const Importer *pImp) { |
99 | 0 | noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0; |
100 | 0 | } |
101 | | |
102 | | // ------------------------------------------------------------------------------------------------ |
103 | | // Loader meta information |
104 | 1.19k | const aiImporterDesc *BVHLoader::GetInfo() const { |
105 | 1.19k | return &desc; |
106 | 1.19k | } |
107 | | |
108 | | // ------------------------------------------------------------------------------------------------ |
109 | | // Imports the given file into the given scene structure. |
110 | 0 | void BVHLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { |
111 | 0 | mFileName = pFile; |
112 | | |
113 | | // read file into memory |
114 | 0 | std::unique_ptr<IOStream> file(pIOHandler->Open(pFile)); |
115 | 0 | if (file == nullptr) { |
116 | 0 | throw DeadlyImportError("Failed to open file ", pFile, "."); |
117 | 0 | } |
118 | | |
119 | 0 | size_t fileSize = file->FileSize(); |
120 | 0 | if (fileSize == 0) { |
121 | 0 | throw DeadlyImportError("File is too small."); |
122 | 0 | } |
123 | | |
124 | 0 | mBuffer.resize(fileSize); |
125 | 0 | file->Read(&mBuffer.front(), 1, fileSize); |
126 | | |
127 | | // start reading |
128 | 0 | mReader = mBuffer.begin(); |
129 | 0 | mLine = 1; |
130 | 0 | ReadStructure(pScene); |
131 | |
|
132 | 0 | if (!noSkeletonMesh) { |
133 | | // build a dummy mesh for the skeleton so that we see something at least |
134 | 0 | SkeletonMeshBuilder meshBuilder(pScene); |
135 | 0 | } |
136 | | |
137 | | // construct an animation from all the motion data we read |
138 | 0 | CreateAnimation(pScene); |
139 | 0 | } |
140 | | |
141 | | // ------------------------------------------------------------------------------------------------ |
142 | | // Reads the file |
143 | 0 | void BVHLoader::ReadStructure(aiScene *pScene) { |
144 | | // first comes hierarchy |
145 | 0 | std::string header = GetNextToken(); |
146 | 0 | if (header != "HIERARCHY") |
147 | 0 | ThrowException("Expected header string \"HIERARCHY\"."); |
148 | 0 | ReadHierarchy(pScene); |
149 | | |
150 | | // then comes the motion data |
151 | 0 | std::string motion = GetNextToken(); |
152 | 0 | if (motion != "MOTION") |
153 | 0 | ThrowException("Expected beginning of motion data \"MOTION\"."); |
154 | 0 | ReadMotion(pScene); |
155 | 0 | } |
156 | | |
157 | | // ------------------------------------------------------------------------------------------------ |
158 | | // Reads the hierarchy |
159 | 0 | void BVHLoader::ReadHierarchy(aiScene *pScene) { |
160 | 0 | std::string root = GetNextToken(); |
161 | 0 | if (root != "ROOT") |
162 | 0 | ThrowException("Expected root node \"ROOT\"."); |
163 | | |
164 | | // Go read the hierarchy from here |
165 | 0 | pScene->mRootNode = ReadNode(); |
166 | 0 | } |
167 | | |
168 | | // ------------------------------------------------------------------------------------------------ |
169 | | // Reads a node and recursively its children and returns the created node; |
170 | 0 | aiNode *BVHLoader::ReadNode() { |
171 | | // first token is name |
172 | 0 | std::string nodeName = GetNextToken(); |
173 | 0 | if (nodeName.empty() || nodeName == "{") |
174 | 0 | ThrowException("Expected node name, but found \"", nodeName, "\"."); |
175 | | |
176 | | // then an opening brace should follow |
177 | 0 | std::string openBrace = GetNextToken(); |
178 | 0 | if (openBrace != "{") |
179 | 0 | ThrowException("Expected opening brace \"{\", but found \"", openBrace, "\"."); |
180 | | |
181 | | // Create a node |
182 | 0 | aiNode *node = new aiNode(nodeName); |
183 | 0 | std::vector<aiNode *> childNodes; |
184 | | |
185 | | // and create an bone entry for it |
186 | 0 | mNodes.emplace_back(node); |
187 | 0 | Node &internNode = mNodes.back(); |
188 | | |
189 | | // now read the node's contents |
190 | 0 | std::string siteToken; |
191 | 0 | while (true) { |
192 | 0 | std::string token = GetNextToken(); |
193 | | |
194 | | // node offset to parent node |
195 | 0 | if (token == "OFFSET") |
196 | 0 | ReadNodeOffset(node); |
197 | 0 | else if (token == "CHANNELS") |
198 | 0 | ReadNodeChannels(internNode); |
199 | 0 | else if (token == "JOINT") { |
200 | | // child node follows |
201 | 0 | aiNode *child = ReadNode(); |
202 | 0 | child->mParent = node; |
203 | 0 | childNodes.push_back(child); |
204 | 0 | } else if (token == "End") { |
205 | | // The real symbol is "End Site". Second part comes in a separate token |
206 | 0 | siteToken.clear(); |
207 | 0 | siteToken = GetNextToken(); |
208 | 0 | if (siteToken != "Site") |
209 | 0 | ThrowException("Expected \"End Site\" keyword, but found \"", token, " ", siteToken, "\"."); |
210 | |
|
211 | 0 | aiNode *child = ReadEndSite(nodeName); |
212 | 0 | child->mParent = node; |
213 | 0 | childNodes.push_back(child); |
214 | 0 | } else if (token == "}") { |
215 | | // we're done with that part of the hierarchy |
216 | 0 | break; |
217 | 0 | } else { |
218 | | // everything else is a parse error |
219 | 0 | ThrowException("Unknown keyword \"", token, "\"."); |
220 | 0 | } |
221 | 0 | } |
222 | | |
223 | | // add the child nodes if there are any |
224 | 0 | if (childNodes.size() > 0) { |
225 | 0 | node->mNumChildren = static_cast<unsigned int>(childNodes.size()); |
226 | 0 | node->mChildren = new aiNode *[node->mNumChildren]; |
227 | 0 | std::copy(childNodes.begin(), childNodes.end(), node->mChildren); |
228 | 0 | } |
229 | | |
230 | | // and return the sub-hierarchy we built here |
231 | 0 | return node; |
232 | 0 | } |
233 | | |
234 | | // ------------------------------------------------------------------------------------------------ |
235 | | // Reads an end node and returns the created node. |
236 | 0 | aiNode *BVHLoader::ReadEndSite(const std::string &pParentName) { |
237 | | // check opening brace |
238 | 0 | std::string openBrace = GetNextToken(); |
239 | 0 | if (openBrace != "{") |
240 | 0 | ThrowException("Expected opening brace \"{\", but found \"", openBrace, "\"."); |
241 | | |
242 | | // Create a node |
243 | 0 | aiNode *node = new aiNode("EndSite_" + pParentName); |
244 | | |
245 | | // now read the node's contents. Only possible entry is "OFFSET" |
246 | 0 | std::string token; |
247 | 0 | while (true) { |
248 | 0 | token.clear(); |
249 | 0 | token = GetNextToken(); |
250 | | |
251 | | // end node's offset |
252 | 0 | if (token == "OFFSET") { |
253 | 0 | ReadNodeOffset(node); |
254 | 0 | } else if (token == "}") { |
255 | | // we're done with the end node |
256 | 0 | break; |
257 | 0 | } else { |
258 | | // everything else is a parse error |
259 | 0 | ThrowException("Unknown keyword \"", token, "\"."); |
260 | 0 | } |
261 | 0 | } |
262 | | |
263 | | // and return the sub-hierarchy we built here |
264 | 0 | return node; |
265 | 0 | } |
266 | | // ------------------------------------------------------------------------------------------------ |
267 | | // Reads a node offset for the given node |
268 | 0 | void BVHLoader::ReadNodeOffset(aiNode *pNode) { |
269 | | // Offset consists of three floats to read |
270 | 0 | aiVector3D offset; |
271 | 0 | offset.x = GetNextTokenAsFloat(); |
272 | 0 | offset.y = GetNextTokenAsFloat(); |
273 | 0 | offset.z = GetNextTokenAsFloat(); |
274 | | |
275 | | // build a transformation matrix from it |
276 | 0 | pNode->mTransformation = aiMatrix4x4(1.0f, 0.0f, 0.0f, offset.x, |
277 | 0 | 0.0f, 1.0f, 0.0f, offset.y, |
278 | 0 | 0.0f, 0.0f, 1.0f, offset.z, |
279 | 0 | 0.0f, 0.0f, 0.0f, 1.0f); |
280 | 0 | } |
281 | | |
282 | | // ------------------------------------------------------------------------------------------------ |
283 | | // Reads the animation channels for the given node |
284 | 0 | void BVHLoader::ReadNodeChannels(BVHLoader::Node &pNode) { |
285 | | // number of channels. Use the float reader because we're lazy |
286 | 0 | float numChannelsFloat = GetNextTokenAsFloat(); |
287 | 0 | unsigned int numChannels = (unsigned int)numChannelsFloat; |
288 | |
|
289 | 0 | for (unsigned int a = 0; a < numChannels; a++) { |
290 | 0 | std::string channelToken = GetNextToken(); |
291 | |
|
292 | 0 | if (channelToken == "Xposition") |
293 | 0 | pNode.mChannels.push_back(Channel_PositionX); |
294 | 0 | else if (channelToken == "Yposition") |
295 | 0 | pNode.mChannels.push_back(Channel_PositionY); |
296 | 0 | else if (channelToken == "Zposition") |
297 | 0 | pNode.mChannels.push_back(Channel_PositionZ); |
298 | 0 | else if (channelToken == "Xrotation") |
299 | 0 | pNode.mChannels.push_back(Channel_RotationX); |
300 | 0 | else if (channelToken == "Yrotation") |
301 | 0 | pNode.mChannels.push_back(Channel_RotationY); |
302 | 0 | else if (channelToken == "Zrotation") |
303 | 0 | pNode.mChannels.push_back(Channel_RotationZ); |
304 | 0 | else |
305 | 0 | ThrowException("Invalid channel specifier \"", channelToken, "\"."); |
306 | 0 | } |
307 | 0 | } |
308 | | |
309 | | // ------------------------------------------------------------------------------------------------ |
310 | | // Reads the motion data |
311 | 0 | void BVHLoader::ReadMotion(aiScene * /*pScene*/) { |
312 | | // Read number of frames |
313 | 0 | std::string tokenFrames = GetNextToken(); |
314 | 0 | if (tokenFrames != "Frames:") |
315 | 0 | ThrowException("Expected frame count \"Frames:\", but found \"", tokenFrames, "\"."); |
316 | |
|
317 | 0 | float numFramesFloat = GetNextTokenAsFloat(); |
318 | 0 | mAnimNumFrames = (unsigned int)numFramesFloat; |
319 | | |
320 | | // Read frame duration |
321 | 0 | std::string tokenDuration1 = GetNextToken(); |
322 | 0 | std::string tokenDuration2 = GetNextToken(); |
323 | 0 | if (tokenDuration1 != "Frame" || tokenDuration2 != "Time:") |
324 | 0 | ThrowException("Expected frame duration \"Frame Time:\", but found \"", tokenDuration1, " ", tokenDuration2, "\"."); |
325 | |
|
326 | 0 | mAnimTickDuration = GetNextTokenAsFloat(); |
327 | | |
328 | | // resize value vectors for each node |
329 | 0 | for (std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it) |
330 | 0 | it->mChannelValues.reserve(it->mChannels.size() * mAnimNumFrames); |
331 | | |
332 | | // now read all the data and store it in the corresponding node's value vector |
333 | 0 | for (unsigned int frame = 0; frame < mAnimNumFrames; ++frame) { |
334 | | // on each line read the values for all nodes |
335 | 0 | for (std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it) { |
336 | | // get as many values as the node has channels |
337 | 0 | for (unsigned int c = 0; c < it->mChannels.size(); ++c) |
338 | 0 | it->mChannelValues.push_back(GetNextTokenAsFloat()); |
339 | 0 | } |
340 | | |
341 | | // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | | // ------------------------------------------------------------------------------------------------ |
346 | | // Retrieves the next token |
347 | 0 | std::string BVHLoader::GetNextToken() { |
348 | | // skip any preceding whitespace |
349 | 0 | while (mReader != mBuffer.end()) { |
350 | 0 | if (!isspace((unsigned char)*mReader)) |
351 | 0 | break; |
352 | | |
353 | | // count lines |
354 | 0 | if (*mReader == '\n') |
355 | 0 | mLine++; |
356 | |
|
357 | 0 | ++mReader; |
358 | 0 | } |
359 | | |
360 | | // collect all chars till the next whitespace. BVH is easy in respect to that. |
361 | 0 | std::string token; |
362 | 0 | while (mReader != mBuffer.end()) { |
363 | 0 | if (isspace((unsigned char)*mReader)) |
364 | 0 | break; |
365 | | |
366 | 0 | token.push_back(*mReader); |
367 | 0 | ++mReader; |
368 | | |
369 | | // little extra logic to make sure braces are counted correctly |
370 | 0 | if (token == "{" || token == "}") |
371 | 0 | break; |
372 | 0 | } |
373 | | |
374 | | // empty token means end of file, which is just fine |
375 | 0 | return token; |
376 | 0 | } |
377 | | |
378 | | // ------------------------------------------------------------------------------------------------ |
379 | | // Reads the next token as a float |
380 | 0 | float BVHLoader::GetNextTokenAsFloat() { |
381 | 0 | std::string token = GetNextToken(); |
382 | 0 | if (token.empty()) |
383 | 0 | ThrowException("Unexpected end of file while trying to read a float"); |
384 | | |
385 | | // check if the float is valid by testing if the atof() function consumed every char of the token |
386 | 0 | const char *ctoken = token.c_str(); |
387 | 0 | float result = 0.0f; |
388 | 0 | ctoken = fast_atoreal_move(ctoken, result); |
389 | |
|
390 | 0 | if (ctoken != token.c_str() + token.length()) |
391 | 0 | ThrowException("Expected a floating point number, but found \"", token, "\"."); |
392 | |
|
393 | 0 | return result; |
394 | 0 | } |
395 | | |
396 | | // ------------------------------------------------------------------------------------------------ |
397 | | // Constructs an animation for the motion data and stores it in the given scene |
398 | 0 | void BVHLoader::CreateAnimation(aiScene *pScene) { |
399 | | // create the animation |
400 | 0 | pScene->mNumAnimations = 1; |
401 | 0 | pScene->mAnimations = new aiAnimation *[1]; |
402 | 0 | aiAnimation *anim = new aiAnimation; |
403 | 0 | pScene->mAnimations[0] = anim; |
404 | | |
405 | | // put down the basic parameters |
406 | 0 | anim->mName.Set("Motion"); |
407 | 0 | anim->mTicksPerSecond = 1.0 / double(mAnimTickDuration); |
408 | 0 | anim->mDuration = double(mAnimNumFrames - 1); |
409 | | |
410 | | // now generate the tracks for all nodes |
411 | 0 | anim->mNumChannels = static_cast<unsigned int>(mNodes.size()); |
412 | 0 | anim->mChannels = new aiNodeAnim *[anim->mNumChannels]; |
413 | | |
414 | | // FIX: set the array elements to nullptr to ensure proper deletion if an exception is thrown |
415 | 0 | for (unsigned int i = 0; i < anim->mNumChannels; ++i) |
416 | 0 | anim->mChannels[i] = nullptr; |
417 | |
|
418 | 0 | for (unsigned int a = 0; a < anim->mNumChannels; a++) { |
419 | 0 | const Node &node = mNodes[a]; |
420 | 0 | const std::string nodeName = std::string(node.mNode->mName.data); |
421 | 0 | aiNodeAnim *nodeAnim = new aiNodeAnim; |
422 | 0 | anim->mChannels[a] = nodeAnim; |
423 | 0 | nodeAnim->mNodeName.Set(nodeName); |
424 | 0 | std::map<BVHLoader::ChannelType, int> channelMap; |
425 | | |
426 | | // Build map of channels |
427 | 0 | for (unsigned int channel = 0; channel < node.mChannels.size(); ++channel) { |
428 | 0 | channelMap[node.mChannels[channel]] = channel; |
429 | 0 | } |
430 | | |
431 | | // translational part, if given |
432 | 0 | if (node.mChannels.size() == 6) { |
433 | 0 | nodeAnim->mNumPositionKeys = mAnimNumFrames; |
434 | 0 | nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames]; |
435 | 0 | aiVectorKey *poskey = nodeAnim->mPositionKeys; |
436 | 0 | for (unsigned int fr = 0; fr < mAnimNumFrames; ++fr) { |
437 | 0 | poskey->mTime = double(fr); |
438 | | |
439 | | // Now compute all translations |
440 | 0 | for (BVHLoader::ChannelType channel = Channel_PositionX; channel <= Channel_PositionZ; channel = (BVHLoader::ChannelType)(channel + 1)) { |
441 | | // Find channel in node |
442 | 0 | std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel); |
443 | |
|
444 | 0 | if (mapIter == channelMap.end()) |
445 | 0 | throw DeadlyImportError("Missing position channel in node ", nodeName); |
446 | 0 | else { |
447 | 0 | int channelIdx = mapIter->second; |
448 | 0 | switch (channel) { |
449 | 0 | case Channel_PositionX: |
450 | 0 | poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channelIdx]; |
451 | 0 | break; |
452 | 0 | case Channel_PositionY: |
453 | 0 | poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channelIdx]; |
454 | 0 | break; |
455 | 0 | case Channel_PositionZ: |
456 | 0 | poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channelIdx]; |
457 | 0 | break; |
458 | | |
459 | 0 | default: |
460 | 0 | break; |
461 | 0 | } |
462 | 0 | } |
463 | 0 | } |
464 | 0 | ++poskey; |
465 | 0 | } |
466 | 0 | } else { |
467 | | // if no translation part is given, put a default sequence |
468 | 0 | aiVector3D nodePos(node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4); |
469 | 0 | nodeAnim->mNumPositionKeys = 1; |
470 | 0 | nodeAnim->mPositionKeys = new aiVectorKey[1]; |
471 | 0 | nodeAnim->mPositionKeys[0].mTime = 0.0; |
472 | 0 | nodeAnim->mPositionKeys[0].mValue = nodePos; |
473 | 0 | } |
474 | | |
475 | | // rotation part. Always present. First find value offsets |
476 | 0 | { |
477 | | |
478 | | // Then create the number of rotation keys |
479 | 0 | nodeAnim->mNumRotationKeys = mAnimNumFrames; |
480 | 0 | nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames]; |
481 | 0 | aiQuatKey *rotkey = nodeAnim->mRotationKeys; |
482 | 0 | for (unsigned int fr = 0; fr < mAnimNumFrames; ++fr) { |
483 | 0 | aiMatrix4x4 temp; |
484 | 0 | aiMatrix3x3 rotMatrix; |
485 | 0 | for (unsigned int channelIdx = 0; channelIdx < node.mChannels.size(); ++channelIdx) { |
486 | 0 | switch (node.mChannels[channelIdx]) { |
487 | 0 | case Channel_RotationX: { |
488 | 0 | const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f; |
489 | 0 | aiMatrix4x4::RotationX(angle, temp); |
490 | 0 | rotMatrix *= aiMatrix3x3(temp); |
491 | 0 | } break; |
492 | 0 | case Channel_RotationY: { |
493 | 0 | const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f; |
494 | 0 | aiMatrix4x4::RotationY(angle, temp); |
495 | 0 | rotMatrix *= aiMatrix3x3(temp); |
496 | 0 | } break; |
497 | 0 | case Channel_RotationZ: { |
498 | 0 | const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f; |
499 | 0 | aiMatrix4x4::RotationZ(angle, temp); |
500 | 0 | rotMatrix *= aiMatrix3x3(temp); |
501 | 0 | } break; |
502 | 0 | default: |
503 | 0 | break; |
504 | 0 | } |
505 | 0 | } |
506 | 0 | rotkey->mTime = double(fr); |
507 | 0 | rotkey->mValue = aiQuaternion(rotMatrix); |
508 | 0 | ++rotkey; |
509 | 0 | } |
510 | 0 | } |
511 | | |
512 | | // scaling part. Always just a default track |
513 | 0 | { |
514 | 0 | nodeAnim->mNumScalingKeys = 1; |
515 | 0 | nodeAnim->mScalingKeys = new aiVectorKey[1]; |
516 | 0 | nodeAnim->mScalingKeys[0].mTime = 0.0; |
517 | 0 | nodeAnim->mScalingKeys[0].mValue.Set(1.0f, 1.0f, 1.0f); |
518 | 0 | } |
519 | 0 | } |
520 | 0 | } |
521 | | |
522 | | } // namespace Assimp |
523 | | |
524 | | #endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER |