/src/assimp/code/AssetLib/MD5/MD5Parser.cpp
Line | Count | Source |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2025, assimp team |
7 | | |
8 | | All rights reserved. |
9 | | |
10 | | Redistribution and use of this software in source and binary forms, |
11 | | with or without modification, are permitted provided that the following |
12 | | conditions are met: |
13 | | |
14 | | * Redistributions of source code must retain the above |
15 | | copyright notice, this list of conditions and the |
16 | | following disclaimer. |
17 | | |
18 | | * Redistributions in binary form must reproduce the above |
19 | | copyright notice, this list of conditions and the |
20 | | following disclaimer in the documentation and/or other |
21 | | materials provided with the distribution. |
22 | | |
23 | | * Neither the name of the assimp team, nor the names of its |
24 | | contributors may be used to endorse or promote products |
25 | | derived from this software without specific prior |
26 | | written permission of the assimp team. |
27 | | |
28 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
29 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
30 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
31 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
32 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
33 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
34 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
35 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
36 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
37 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
38 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
39 | | --------------------------------------------------------------------------- |
40 | | */ |
41 | | |
42 | | /** @file MD5Parser.cpp |
43 | | * @brief Implementation of the MD5 parser class |
44 | | */ |
45 | | |
46 | | // internal headers |
47 | | #include "MD5Loader.h" |
48 | | #include "Material/MaterialSystem.h" |
49 | | |
50 | | #include <assimp/ParsingUtils.h> |
51 | | #include <assimp/StringComparison.h> |
52 | | #include <assimp/fast_atof.h> |
53 | | #include <assimp/mesh.h> |
54 | | #include <assimp/DefaultLogger.hpp> |
55 | | |
56 | | using namespace Assimp; |
57 | | using namespace Assimp::MD5; |
58 | | |
59 | | // ------------------------------------------------------------------------------------------------ |
60 | | // Parse the segment structure for an MD5 file |
61 | 65 | MD5Parser::MD5Parser(char *_buffer, unsigned int _fileSize) : buffer(_buffer), bufferEnd(nullptr), fileSize(_fileSize), lineNumber(0) { |
62 | 65 | ai_assert(nullptr != _buffer); |
63 | 65 | ai_assert(0 != _fileSize); |
64 | | |
65 | 65 | bufferEnd = buffer + fileSize; |
66 | 65 | ASSIMP_LOG_DEBUG("MD5Parser begin"); |
67 | | |
68 | | // parse the file header |
69 | 65 | ParseHeader(); |
70 | | |
71 | | // and read all sections until we're finished |
72 | 65 | bool running = true; |
73 | 33.3k | while (running) { |
74 | 33.3k | mSections.emplace_back(); |
75 | 33.3k | Section &sec = mSections.back(); |
76 | 33.3k | if (!ParseSection(sec)) { |
77 | 65 | break; |
78 | 65 | } |
79 | 33.3k | } |
80 | | |
81 | 65 | if (!DefaultLogger::isNullLogger()) { |
82 | 0 | char szBuffer[128]; // should be sufficiently large |
83 | 0 | ::ai_snprintf(szBuffer, 128, "MD5Parser end. Parsed %i sections", (int)mSections.size()); |
84 | 0 | ASSIMP_LOG_DEBUG(szBuffer); |
85 | 0 | } |
86 | 65 | } |
87 | | |
88 | | // ------------------------------------------------------------------------------------------------ |
89 | | // Report error to the log stream |
90 | 0 | AI_WONT_RETURN void MD5Parser::ReportError(const char *error, unsigned int line) { |
91 | 0 | char szBuffer[1024]; |
92 | 0 | ::ai_snprintf(szBuffer, 1024, "[MD5] Line %u: %s", line, error); |
93 | 0 | throw DeadlyImportError(szBuffer); |
94 | 0 | } |
95 | | |
96 | | // ------------------------------------------------------------------------------------------------ |
97 | | // Report warning to the log stream |
98 | 5.60k | void MD5Parser::ReportWarning(const char *warn, unsigned int line) { |
99 | 5.60k | char szBuffer[1024]; |
100 | 5.60k | ::snprintf(szBuffer, sizeof(szBuffer), "[MD5] Line %u: %s", line, warn); |
101 | 5.60k | ASSIMP_LOG_WARN(szBuffer); |
102 | 5.60k | } |
103 | | |
104 | | // ------------------------------------------------------------------------------------------------ |
105 | | // Parse and validate the MD5 header |
106 | 65 | void MD5Parser::ParseHeader() { |
107 | | // parse and validate the file version |
108 | 65 | SkipSpaces(); |
109 | 65 | if (!TokenMatch(buffer, "MD5Version", 10)) { |
110 | 0 | ReportError("Invalid MD5 file: MD5Version tag has not been found"); |
111 | 0 | } |
112 | 65 | SkipSpaces(); |
113 | 65 | unsigned int iVer = ::strtoul10(buffer, (const char **)&buffer); |
114 | 65 | if (10 != iVer) { |
115 | 0 | ReportError("MD5 version tag is unknown (10 is expected)"); |
116 | 0 | } |
117 | 65 | SkipLine(); |
118 | | |
119 | | // print the command line options to the console |
120 | 65 | char *sz = buffer; |
121 | 1.19k | while (buffer < bufferEnd) { |
122 | 1.19k | if (IsLineEnd(*buffer++)) { |
123 | 65 | break; |
124 | 65 | } |
125 | 1.19k | } |
126 | | |
127 | 65 | if (buffer == bufferEnd) { |
128 | 0 | return; |
129 | 0 | } |
130 | | |
131 | 65 | ASSIMP_LOG_INFO(std::string(sz, std::min((uintptr_t)MAX_LOG_MESSAGE_LENGTH, (uintptr_t)(buffer - sz)))); |
132 | 65 | SkipSpacesAndLineEnd(); |
133 | 65 | } |
134 | | |
135 | | // ------------------------------------------------------------------------------------------------ |
136 | | // Recursive MD5 parsing function |
137 | 33.3k | bool MD5Parser::ParseSection(Section &out) { |
138 | | // store the current line number for use in error messages |
139 | 33.3k | out.iLineNumber = lineNumber; |
140 | | |
141 | | // first parse the name of the section |
142 | 33.3k | char *sz = buffer; |
143 | 497k | while (!IsSpaceOrNewLine(*buffer)) { |
144 | 464k | ++buffer; |
145 | 464k | if (buffer == bufferEnd) { |
146 | 18 | return false; |
147 | 18 | } |
148 | 464k | } |
149 | 33.3k | out.mName = std::string(sz, (uintptr_t)(buffer - sz)); |
150 | 53.5k | while (IsSpace(*buffer)) { |
151 | 20.1k | ++buffer; |
152 | 20.1k | if (buffer == bufferEnd) { |
153 | 2 | return false; |
154 | 2 | } |
155 | 20.1k | } |
156 | | |
157 | 33.3k | bool running = true; |
158 | 75.5k | while (running) { |
159 | 75.5k | if ('{' == *buffer) { |
160 | | // it is a normal section so read all lines |
161 | 255 | ++buffer; |
162 | 255 | if (buffer == bufferEnd) { |
163 | 0 | return false; |
164 | 0 | } |
165 | 255 | bool run = true; |
166 | 28.2k | while (run) { |
167 | 76.3k | while (IsSpaceOrNewLine(*buffer)) { |
168 | 48.1k | ++buffer; |
169 | 48.1k | if (buffer == bufferEnd) { |
170 | 8 | return false; |
171 | 8 | } |
172 | 48.1k | } |
173 | 28.2k | if ('\0' == *buffer) { |
174 | 0 | return false; // seems this was the last section |
175 | 0 | } |
176 | 28.2k | if ('}' == *buffer) { |
177 | 224 | ++buffer; |
178 | 224 | break; |
179 | 224 | } |
180 | | |
181 | 27.9k | out.mElements.emplace_back(); |
182 | 27.9k | Element &elem = out.mElements.back(); |
183 | | |
184 | 27.9k | elem.iLineNumber = lineNumber; |
185 | 27.9k | elem.szStart = buffer; |
186 | 27.9k | elem.end = bufferEnd; |
187 | | |
188 | | // terminate the line with zero |
189 | 468k | while (!IsLineEnd(*buffer)) { |
190 | 440k | ++buffer; |
191 | 440k | if (buffer == bufferEnd) { |
192 | 23 | return false; |
193 | 23 | } |
194 | 440k | } |
195 | 27.9k | if (*buffer) { |
196 | 15.1k | ++lineNumber; |
197 | 15.1k | *buffer++ = '\0'; |
198 | 15.1k | if (buffer == bufferEnd) { |
199 | 0 | return false; |
200 | 0 | } |
201 | 15.1k | } |
202 | 27.9k | } |
203 | 224 | break; |
204 | 75.2k | } else if (!IsSpaceOrNewLine(*buffer)) { |
205 | | // it is an element at global scope. Parse its value and go on |
206 | 42.1k | sz = buffer; |
207 | 562k | while (!IsSpaceOrNewLine(*buffer++)) { |
208 | 520k | if (buffer == bufferEnd) { |
209 | 8 | return false; |
210 | 8 | } |
211 | 520k | } |
212 | 42.1k | out.mGlobalValue = std::string(sz, (uintptr_t)(buffer - sz)); |
213 | 42.1k | continue; |
214 | 42.1k | } |
215 | 33.1k | break; |
216 | 75.5k | } |
217 | 33.3k | if (buffer == bufferEnd) { |
218 | 2 | return false; |
219 | 2 | } |
220 | 95.8k | while (IsSpaceOrNewLine(*buffer)) { |
221 | 62.5k | if (buffer == bufferEnd) { |
222 | 4 | break; |
223 | 4 | } |
224 | 62.5k | ++buffer; |
225 | 62.5k | } |
226 | 33.3k | return '\0' != *buffer; |
227 | 33.3k | } |
228 | | |
229 | | // skip all spaces ... handle EOL correctly |
230 | 10.6k | inline void AI_MD5_SKIP_SPACES(const char **sz, const char *bufferEnd, int linenumber) { |
231 | 10.6k | if (!SkipSpaces(sz, bufferEnd)) { |
232 | 5.49k | MD5Parser::ReportWarning("Unexpected end of line", linenumber); |
233 | 5.49k | } |
234 | 10.6k | } |
235 | | |
236 | | // read a triple float in brackets: (1.0 1.0 1.0) |
237 | 1 | inline void AI_MD5_READ_TRIPLE(aiVector3D &vec, const char **sz, const char *bufferEnd, int linenumber) { |
238 | 1 | AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber); |
239 | 1 | if ('(' != **sz) { |
240 | 1 | MD5Parser::ReportWarning("Unexpected token: ( was expected", linenumber); |
241 | 1 | if (*sz == bufferEnd) |
242 | 0 | return; |
243 | 1 | ++*sz; |
244 | 1 | } |
245 | 1 | if (*sz == bufferEnd) |
246 | 0 | return; |
247 | 1 | ++*sz; |
248 | 1 | AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber); |
249 | 1 | *sz = fast_atoreal_move(*sz, vec.x); |
250 | 1 | AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber); |
251 | 1 | *sz = fast_atoreal_move(*sz, vec.y); |
252 | 1 | AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber); |
253 | 1 | *sz = fast_atoreal_move(*sz, vec.z); |
254 | 1 | AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber); |
255 | 1 | if (')' != **sz) { |
256 | 0 | MD5Parser::ReportWarning("Unexpected token: ) was expected", linenumber); |
257 | 0 | } |
258 | 1 | if (*sz == bufferEnd) |
259 | 0 | return; |
260 | 1 | ++*sz; |
261 | 1 | } |
262 | | |
263 | | // parse a string, enclosed in quotation marks or not |
264 | 0 | inline bool AI_MD5_PARSE_STRING(const char **sz, const char *bufferEnd, aiString &out, int linenumber) { |
265 | 0 | bool bQuota = (**sz == '\"'); |
266 | 0 | const char *szStart = *sz; |
267 | 0 | while (!IsSpaceOrNewLine(**sz)) { |
268 | 0 | ++*sz; |
269 | 0 | if (*sz == bufferEnd) break; |
270 | 0 | } |
271 | 0 | const char *szEnd = *sz; |
272 | 0 | if (bQuota) { |
273 | 0 | szStart++; |
274 | 0 | if ('\"' != *(szEnd -= 1)) { |
275 | 0 | MD5Parser::ReportWarning("Expected closing quotation marks in string", linenumber); |
276 | 0 | ++*sz; |
277 | 0 | } |
278 | 0 | } |
279 | 0 | out.length = (ai_uint32)(szEnd - szStart); |
280 | 0 | if (out.length >= AI_MAXLEN) |
281 | 0 | out.length = AI_MAXLEN - 1; |
282 | 0 | ::memcpy(out.data, szStart, out.length); |
283 | 0 | out.data[out.length] = '\0'; |
284 | 0 |
|
285 | 0 | return true; |
286 | 0 | } |
287 | | |
288 | | // parse a string, enclosed in quotation marks |
289 | 2.81k | inline void AI_MD5_PARSE_STRING_IN_QUOTATION(const char **sz, const char *bufferEnd, aiString &out) { |
290 | 2.81k | out.length = 0u; |
291 | 38.3k | while (('\"' != **sz && '\0' != **sz) && *sz != bufferEnd) { |
292 | 35.5k | ++*sz; |
293 | 35.5k | } |
294 | 2.81k | if ('\0' != **sz) { |
295 | 1.01k | const char *szStart = ++(*sz); |
296 | | |
297 | 2.02k | while (('\"' != **sz && '\0' != **sz) && *sz != bufferEnd) { |
298 | 1.01k | ++*sz; |
299 | 1.01k | } |
300 | 1.01k | if ('\0' != **sz) { |
301 | 1.01k | const char *szEnd = *sz; |
302 | 1.01k | ++*sz; |
303 | 1.01k | out.length = (ai_uint32)(szEnd - szStart); |
304 | 1.01k | if (out.length >= AI_MAXLEN) |
305 | 0 | out.length = AI_MAXLEN - 1; |
306 | 1.01k | ::memcpy(out.data, szStart, out.length); |
307 | 1.01k | } |
308 | 1.01k | } |
309 | 2.81k | out.data[out.length] = '\0'; |
310 | 2.81k | } |
311 | | |
312 | | // ------------------------------------------------------------------------------------------------ |
313 | | // .MD5MESH parsing function |
314 | 33 | MD5MeshParser::MD5MeshParser(SectionArray &mSections) { |
315 | 33 | ASSIMP_LOG_DEBUG("MD5MeshParser begin"); |
316 | | |
317 | | // now parse all sections |
318 | 16.8k | for (SectionArray::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) { |
319 | 16.8k | if ((*iter).mName == "numMeshes") { |
320 | 0 | mMeshes.reserve(::strtoul10((*iter).mGlobalValue.c_str())); |
321 | 16.8k | } else if ((*iter).mName == "numJoints") { |
322 | 685 | mJoints.reserve(::strtoul10((*iter).mGlobalValue.c_str())); |
323 | 16.1k | } else if ((*iter).mName == "joints") { |
324 | | // "origin" -1 ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000000 0.707107 ) |
325 | 63 | for (const auto &elem : (*iter).mElements) { |
326 | 1 | mJoints.emplace_back(); |
327 | 1 | BoneDesc &desc = mJoints.back(); |
328 | | |
329 | 1 | const char *sz = elem.szStart; |
330 | 1 | AI_MD5_PARSE_STRING_IN_QUOTATION(&sz, elem.end, desc.mName); |
331 | | |
332 | 1 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
333 | | |
334 | | // negative values, at least -1, is allowed here |
335 | 1 | desc.mParentIndex = (int)strtol10(sz, &sz); |
336 | | |
337 | 1 | AI_MD5_READ_TRIPLE(desc.mPositionXYZ, &sz, elem.end, elem.iLineNumber); |
338 | 1 | AI_MD5_READ_TRIPLE(desc.mRotationQuat, &sz, elem.end, elem.iLineNumber); // normalized quaternion, so w is not there |
339 | 1 | } |
340 | 16.1k | } else if ((*iter).mName == "mesh") { |
341 | 415 | mMeshes.emplace_back(); |
342 | 415 | MeshDesc &desc = mMeshes.back(); |
343 | | |
344 | 11.0k | for (const auto &elem : (*iter).mElements) { |
345 | 11.0k | const char *sz = elem.szStart; |
346 | | |
347 | | // shader attribute |
348 | 11.0k | if (TokenMatch(sz, "shader", 6)) { |
349 | 1 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
350 | 1 | AI_MD5_PARSE_STRING_IN_QUOTATION(&sz, elem.end, desc.mShader); |
351 | 1 | } |
352 | | // numverts attribute |
353 | 11.0k | else if (TokenMatch(sz, "numverts", 8)) { |
354 | 0 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
355 | 0 | desc.mVertices.resize(strtoul10(sz)); |
356 | 0 | } |
357 | | // numtris attribute |
358 | 11.0k | else if (TokenMatch(sz, "numtris", 7)) { |
359 | 204 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
360 | 204 | desc.mFaces.resize(strtoul10(sz)); |
361 | 204 | } |
362 | | // numweights attribute |
363 | 10.8k | else if (TokenMatch(sz, "numweights", 10)) { |
364 | 0 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
365 | 0 | desc.mWeights.resize(strtoul10(sz)); |
366 | 0 | } |
367 | | // vert attribute |
368 | | // "vert 0 ( 0.394531 0.513672 ) 0 1" |
369 | 10.8k | else if (TokenMatch(sz, "vert", 4)) { |
370 | 1 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
371 | 1 | const unsigned int idx = ::strtoul10(sz, &sz); |
372 | 1 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
373 | 1 | if (idx >= desc.mVertices.size()) |
374 | 1 | desc.mVertices.resize(idx + 1); |
375 | | |
376 | 1 | VertexDesc &vert = desc.mVertices[idx]; |
377 | 1 | if ('(' != *sz++) |
378 | 1 | MD5Parser::ReportWarning("Unexpected token: ( was expected", elem.iLineNumber); |
379 | 1 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
380 | 1 | sz = fast_atoreal_move(sz, vert.mUV.x); |
381 | 1 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
382 | 1 | sz = fast_atoreal_move(sz, vert.mUV.y); |
383 | 1 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
384 | 1 | if (')' != *sz++) |
385 | 1 | MD5Parser::ReportWarning("Unexpected token: ) was expected", elem.iLineNumber); |
386 | 1 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
387 | 1 | vert.mFirstWeight = ::strtoul10(sz, &sz); |
388 | 1 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
389 | 1 | vert.mNumWeights = ::strtoul10(sz, &sz); |
390 | 1 | } |
391 | | // tri attribute |
392 | | // "tri 0 15 13 12" |
393 | 10.8k | else if (TokenMatch(sz, "tri", 3)) { |
394 | 494 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
395 | 494 | const unsigned int idx = strtoul10(sz, &sz); |
396 | 494 | if (idx >= desc.mFaces.size()) { |
397 | 128 | desc.mFaces.resize(idx + 1); |
398 | 128 | } |
399 | | |
400 | 494 | aiFace &face = desc.mFaces[idx]; |
401 | 494 | if (face.mNumIndices != 3) { |
402 | 158 | delete [] face.mIndices; |
403 | 158 | face.mIndices = new unsigned int[face.mNumIndices = 3]; |
404 | 158 | } |
405 | 1.97k | for (unsigned int i = 0; i < 3; ++i) { |
406 | 1.48k | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
407 | 1.48k | face.mIndices[i] = strtoul10(sz, &sz); |
408 | 1.48k | } |
409 | 494 | } |
410 | | // weight attribute |
411 | | // "weight 362 5 0.500000 ( -3.553583 11.893474 9.719339 )" |
412 | 10.3k | else if (TokenMatch(sz, "weight", 6)) { |
413 | 0 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
414 | 0 | const unsigned int idx = strtoul10(sz, &sz); |
415 | 0 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
416 | 0 | if (idx >= desc.mWeights.size()) |
417 | 0 | desc.mWeights.resize(idx + 1); |
418 | |
|
419 | 0 | WeightDesc &weight = desc.mWeights[idx]; |
420 | 0 | weight.mBone = strtoul10(sz, &sz); |
421 | 0 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
422 | 0 | sz = fast_atoreal_move(sz, weight.mWeight); |
423 | 0 | AI_MD5_READ_TRIPLE(weight.vOffsetPosition, &sz, elem.end, elem.iLineNumber); |
424 | 0 | } |
425 | 11.0k | } |
426 | 415 | } |
427 | 16.8k | } |
428 | 33 | ASSIMP_LOG_DEBUG("MD5MeshParser end"); |
429 | 33 | } |
430 | | |
431 | | // ------------------------------------------------------------------------------------------------ |
432 | | // .MD5ANIM parsing function |
433 | 32 | MD5AnimParser::MD5AnimParser(SectionArray &mSections) { |
434 | 32 | ASSIMP_LOG_DEBUG("MD5AnimParser begin"); |
435 | | |
436 | 32 | fFrameRate = 24.0f; |
437 | 32 | mNumAnimatedComponents = UINT_MAX; |
438 | 16.5k | for (SectionArray::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) { |
439 | 16.5k | if ((*iter).mName == "hierarchy") { |
440 | | // "sheath" 0 63 6 |
441 | 2.80k | for (const auto &elem : (*iter).mElements) { |
442 | 2.80k | mAnimatedBones.emplace_back(); |
443 | 2.80k | AnimBoneDesc &desc = mAnimatedBones.back(); |
444 | | |
445 | 2.80k | const char *sz = elem.szStart; |
446 | 2.80k | AI_MD5_PARSE_STRING_IN_QUOTATION(&sz, elem.end, desc.mName); |
447 | 2.80k | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
448 | | |
449 | | // parent index - negative values are allowed (at least -1) |
450 | 2.80k | desc.mParentIndex = ::strtol10(sz, &sz); |
451 | | |
452 | | // flags (highest is 2^6-1) |
453 | 2.80k | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
454 | 2.80k | if (63 < (desc.iFlags = ::strtoul10(sz, &sz))) { |
455 | 99 | MD5Parser::ReportWarning("Invalid flag combination in hierarchy section", elem.iLineNumber); |
456 | 99 | } |
457 | 2.80k | AI_MD5_SKIP_SPACES(& sz, elem.end, elem.iLineNumber); |
458 | | |
459 | | // index of the first animation keyframe component for this joint |
460 | 2.80k | desc.iFirstKeyIndex = ::strtoul10(sz, &sz); |
461 | 2.80k | } |
462 | 16.5k | } else if ((*iter).mName == "baseframe") { |
463 | | // ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000242 0.707107 ) |
464 | 0 | for (const auto &elem : (*iter).mElements) { |
465 | 0 | const char *sz = elem.szStart; |
466 | |
|
467 | 0 | mBaseFrames.emplace_back(); |
468 | 0 | BaseFrameDesc &desc = mBaseFrames.back(); |
469 | |
|
470 | 0 | AI_MD5_READ_TRIPLE(desc.vPositionXYZ, &sz, elem.end, elem.iLineNumber); |
471 | 0 | AI_MD5_READ_TRIPLE(desc.vRotationQuat, &sz, elem.end, elem.iLineNumber); |
472 | 0 | } |
473 | 16.5k | } else if ((*iter).mName == "frame") { |
474 | 1.34k | if (!(*iter).mGlobalValue.length()) { |
475 | 0 | MD5Parser::ReportWarning("A frame section must have a frame index", (*iter).iLineNumber); |
476 | 0 | continue; |
477 | 0 | } |
478 | | |
479 | 1.34k | mFrames.emplace_back(); |
480 | 1.34k | FrameDesc &desc = mFrames.back(); |
481 | 1.34k | desc.iIndex = strtoul10((*iter).mGlobalValue.c_str()); |
482 | | |
483 | | // we do already know how much storage we will presumably need |
484 | 1.34k | if (UINT_MAX != mNumAnimatedComponents) { |
485 | 1.10k | desc.mValues.reserve(mNumAnimatedComponents); |
486 | 1.10k | } |
487 | | |
488 | | // now read all elements (continuous list of floats) |
489 | 1.34k | for (const auto &elem : (*iter).mElements) { |
490 | 2 | const char *sz = elem.szStart; |
491 | 4 | while (SkipSpacesAndLineEnd(&sz, elem.end)) { |
492 | 2 | float f; |
493 | 2 | sz = fast_atoreal_move(sz, f); |
494 | 2 | desc.mValues.push_back(f); |
495 | 2 | } |
496 | 2 | } |
497 | 15.1k | } else if ((*iter).mName == "numFrames") { |
498 | 0 | mFrames.reserve(strtoul10((*iter).mGlobalValue.c_str())); |
499 | 15.1k | } else if ((*iter).mName == "numJoints") { |
500 | 659 | const unsigned int num = strtoul10((*iter).mGlobalValue.c_str()); |
501 | 659 | mAnimatedBones.reserve(num); |
502 | | |
503 | | // try to guess the number of animated components if that element is not given |
504 | 659 | if (UINT_MAX == mNumAnimatedComponents) { |
505 | 5 | mNumAnimatedComponents = num * 6; |
506 | 5 | } |
507 | 14.4k | } else if ((*iter).mName == "numAnimatedComponents") { |
508 | 0 | mAnimatedBones.reserve(strtoul10((*iter).mGlobalValue.c_str())); |
509 | 14.4k | } else if ((*iter).mName == "frameRate") { |
510 | 0 | fast_atoreal_move((*iter).mGlobalValue.c_str(), fFrameRate); |
511 | 0 | } |
512 | 16.5k | } |
513 | 32 | ASSIMP_LOG_DEBUG("MD5AnimParser end"); |
514 | 32 | } |
515 | | |
516 | | // ------------------------------------------------------------------------------------------------ |
517 | | // .MD5CAMERA parsing function |
518 | 0 | MD5CameraParser::MD5CameraParser(SectionArray &mSections) { |
519 | 0 | ASSIMP_LOG_DEBUG("MD5CameraParser begin"); |
520 | 0 | fFrameRate = 24.0f; |
521 | |
|
522 | 0 | for (SectionArray::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) { |
523 | 0 | if ((*iter).mName == "numFrames") { |
524 | 0 | frames.reserve(strtoul10((*iter).mGlobalValue.c_str())); |
525 | 0 | } else if ((*iter).mName == "frameRate") { |
526 | 0 | fFrameRate = fast_atof((*iter).mGlobalValue.c_str()); |
527 | 0 | } else if ((*iter).mName == "numCuts") { |
528 | 0 | cuts.reserve(strtoul10((*iter).mGlobalValue.c_str())); |
529 | 0 | } else if ((*iter).mName == "cuts") { |
530 | 0 | for (const auto &elem : (*iter).mElements) { |
531 | 0 | cuts.push_back(strtoul10(elem.szStart) + 1); |
532 | 0 | } |
533 | 0 | } else if ((*iter).mName == "camera") { |
534 | 0 | for (const auto &elem : (*iter).mElements) { |
535 | 0 | const char *sz = elem.szStart; |
536 | |
|
537 | 0 | frames.emplace_back(); |
538 | 0 | CameraAnimFrameDesc &cur = frames.back(); |
539 | 0 | AI_MD5_READ_TRIPLE(cur.vPositionXYZ, &sz, elem.end, elem.iLineNumber); |
540 | 0 | AI_MD5_READ_TRIPLE(cur.vRotationQuat, &sz, elem.end, elem.iLineNumber); |
541 | 0 | AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber); |
542 | 0 | cur.fFOV = fast_atof(sz); |
543 | 0 | } |
544 | 0 | } |
545 | 0 | } |
546 | | ASSIMP_LOG_DEBUG("MD5CameraParser end"); |
547 | 0 | } |