1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2016, 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
47 // internal headers
48 #include "MD5Loader.h"
49 #include "MaterialSystem.h"
50 #include "fast_atof.h"
51 #include "ParsingUtils.h"
52 #include "StringComparison.h"
53 #include <assimp/DefaultLogger.hpp>
54 #include <assimp/mesh.h>
55
56
57
58 using namespace Assimp;
59 using namespace Assimp::MD5;
60
61 // ------------------------------------------------------------------------------------------------
62 // Parse the segment structure fo a MD5 file
MD5Parser(char * _buffer,unsigned int _fileSize)63 MD5Parser::MD5Parser(char* _buffer, unsigned int _fileSize )
64 {
65 ai_assert(NULL != _buffer && 0 != _fileSize);
66
67 buffer = _buffer;
68 fileSize = _fileSize;
69 lineNumber = 0;
70
71 DefaultLogger::get()->debug("MD5Parser begin");
72
73 // parse the file header
74 ParseHeader();
75
76 // and read all sections until we're finished
77 bool running = true;
78 while (running) {
79 mSections.push_back(Section());
80 Section& sec = mSections.back();
81 if(!ParseSection(sec)) {
82 break;
83 }
84 }
85
86 if ( !DefaultLogger::isNullLogger()) {
87 char szBuffer[128]; // should be sufficiently large
88 ::ai_snprintf(szBuffer,128,"MD5Parser end. Parsed %i sections",(int)mSections.size());
89 DefaultLogger::get()->debug(szBuffer);
90 }
91 }
92
93 // ------------------------------------------------------------------------------------------------
94 // Report error to the log stream
ReportError(const char * error,unsigned int line)95 /*static*/ AI_WONT_RETURN void MD5Parser::ReportError (const char* error, unsigned int line)
96 {
97 char szBuffer[1024];
98 ::ai_snprintf(szBuffer, 1024, "[MD5] Line %u: %s",line,error);
99 throw DeadlyImportError(szBuffer);
100 }
101
102 // ------------------------------------------------------------------------------------------------
103 // Report warning to the log stream
ReportWarning(const char * warn,unsigned int line)104 /*static*/ void MD5Parser::ReportWarning (const char* warn, unsigned int line)
105 {
106 char szBuffer[1024];
107 ::sprintf(szBuffer,"[MD5] Line %u: %s",line,warn);
108 DefaultLogger::get()->warn(szBuffer);
109 }
110
111 // ------------------------------------------------------------------------------------------------
112 // Parse and validate the MD5 header
ParseHeader()113 void MD5Parser::ParseHeader()
114 {
115 // parse and validate the file version
116 SkipSpaces();
117 if (!TokenMatch(buffer,"MD5Version",10)) {
118 ReportError("Invalid MD5 file: MD5Version tag has not been found");
119 }
120 SkipSpaces();
121 unsigned int iVer = ::strtoul10(buffer,(const char**)&buffer);
122 if (10 != iVer) {
123 ReportError("MD5 version tag is unknown (10 is expected)");
124 }
125 SkipLine();
126
127 // print the command line options to the console
128 // FIX: can break the log length limit, so we need to be careful
129 char* sz = buffer;
130 while (!IsLineEnd( *buffer++));
131 DefaultLogger::get()->info(std::string(sz,std::min((uintptr_t)MAX_LOG_MESSAGE_LENGTH, (uintptr_t)(buffer-sz))));
132 SkipSpacesAndLineEnd();
133 }
134
135 // ------------------------------------------------------------------------------------------------
136 // Recursive MD5 parsing function
ParseSection(Section & out)137 bool MD5Parser::ParseSection(Section& out)
138 {
139 // store the current line number for use in error messages
140 out.iLineNumber = lineNumber;
141
142 // first parse the name of the section
143 char* sz = buffer;
144 while (!IsSpaceOrNewLine( *buffer))buffer++;
145 out.mName = std::string(sz,(uintptr_t)(buffer-sz));
146 SkipSpaces();
147
148 bool running = true;
149 while (running) {
150 if ('{' == *buffer) {
151 // it is a normal section so read all lines
152 buffer++;
153 bool run = true;
154 while (run)
155 {
156 if (!SkipSpacesAndLineEnd()) {
157 return false; // seems this was the last section
158 }
159 if ('}' == *buffer) {
160 buffer++;
161 break;
162 }
163
164 out.mElements.push_back(Element());
165 Element& elem = out.mElements.back();
166
167 elem.iLineNumber = lineNumber;
168 elem.szStart = buffer;
169
170 // terminate the line with zero
171 while (!IsLineEnd( *buffer))buffer++;
172 if (*buffer) {
173 ++lineNumber;
174 *buffer++ = '\0';
175 }
176 }
177 break;
178 }
179 else if (!IsSpaceOrNewLine(*buffer)) {
180 // it is an element at global scope. Parse its value and go on
181 sz = buffer;
182 while (!IsSpaceOrNewLine( *buffer++));
183 out.mGlobalValue = std::string(sz,(uintptr_t)(buffer-sz));
184 continue;
185 }
186 break;
187 }
188 return SkipSpacesAndLineEnd();
189 }
190
191 // ------------------------------------------------------------------------------------------------
192 // Some dirty macros just because they're so funny and easy to debug
193
194 // skip all spaces ... handle EOL correctly
195 #define AI_MD5_SKIP_SPACES() if(!SkipSpaces(&sz)) \
196 MD5Parser::ReportWarning("Unexpected end of line",elem.iLineNumber);
197
198 // read a triple float in brackets: (1.0 1.0 1.0)
199 #define AI_MD5_READ_TRIPLE(vec) \
200 AI_MD5_SKIP_SPACES(); \
201 if ('(' != *sz++) \
202 MD5Parser::ReportWarning("Unexpected token: ( was expected",elem.iLineNumber); \
203 AI_MD5_SKIP_SPACES(); \
204 sz = fast_atoreal_move<float>(sz,(float&)vec.x); \
205 AI_MD5_SKIP_SPACES(); \
206 sz = fast_atoreal_move<float>(sz,(float&)vec.y); \
207 AI_MD5_SKIP_SPACES(); \
208 sz = fast_atoreal_move<float>(sz,(float&)vec.z); \
209 AI_MD5_SKIP_SPACES(); \
210 if (')' != *sz++) \
211 MD5Parser::ReportWarning("Unexpected token: ) was expected",elem.iLineNumber);
212
213 // parse a string, enclosed in quotation marks or not
214 #define AI_MD5_PARSE_STRING(out) \
215 bool bQuota = (*sz == '\"'); \
216 const char* szStart = sz; \
217 while (!IsSpaceOrNewLine(*sz))++sz; \
218 const char* szEnd = sz; \
219 if (bQuota) { \
220 szStart++; \
221 if ('\"' != *(szEnd-=1)) { \
222 MD5Parser::ReportWarning("Expected closing quotation marks in string", \
223 elem.iLineNumber); \
224 continue; \
225 } \
226 } \
227 out.length = (size_t)(szEnd - szStart); \
228 ::memcpy(out.data,szStart,out.length); \
229 out.data[out.length] = '\0';
230
231 // ------------------------------------------------------------------------------------------------
232 // .MD5MESH parsing function
MD5MeshParser(SectionList & mSections)233 MD5MeshParser::MD5MeshParser(SectionList& mSections)
234 {
235 DefaultLogger::get()->debug("MD5MeshParser begin");
236
237 // now parse all sections
238 for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter){
239 if ( (*iter).mName == "numMeshes") {
240 mMeshes.reserve(::strtoul10((*iter).mGlobalValue.c_str()));
241 }
242 else if ( (*iter).mName == "numJoints") {
243 mJoints.reserve(::strtoul10((*iter).mGlobalValue.c_str()));
244 }
245 else if ((*iter).mName == "joints") {
246 // "origin" -1 ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000000 0.707107 )
247 for (const auto & elem : (*iter).mElements){
248 mJoints.push_back(BoneDesc());
249 BoneDesc& desc = mJoints.back();
250
251 const char* sz = elem.szStart;
252 AI_MD5_PARSE_STRING(desc.mName);
253 AI_MD5_SKIP_SPACES();
254
255 // negative values, at least -1, is allowed here
256 desc.mParentIndex = (int)strtol10(sz,&sz);
257
258 AI_MD5_READ_TRIPLE(desc.mPositionXYZ);
259 AI_MD5_READ_TRIPLE(desc.mRotationQuat); // normalized quaternion, so w is not there
260 }
261 }
262 else if ((*iter).mName == "mesh") {
263 mMeshes.push_back(MeshDesc());
264 MeshDesc& desc = mMeshes.back();
265
266 for (const auto & elem : (*iter).mElements){
267 const char* sz = elem.szStart;
268
269 // shader attribute
270 if (TokenMatch(sz,"shader",6)) {
271 AI_MD5_SKIP_SPACES();
272 AI_MD5_PARSE_STRING(desc.mShader);
273 }
274 // numverts attribute
275 else if (TokenMatch(sz,"numverts",8)) {
276 AI_MD5_SKIP_SPACES();
277 desc.mVertices.resize(strtoul10(sz));
278 }
279 // numtris attribute
280 else if (TokenMatch(sz,"numtris",7)) {
281 AI_MD5_SKIP_SPACES();
282 desc.mFaces.resize(strtoul10(sz));
283 }
284 // numweights attribute
285 else if (TokenMatch(sz,"numweights",10)) {
286 AI_MD5_SKIP_SPACES();
287 desc.mWeights.resize(strtoul10(sz));
288 }
289 // vert attribute
290 // "vert 0 ( 0.394531 0.513672 ) 0 1"
291 else if (TokenMatch(sz,"vert",4)) {
292 AI_MD5_SKIP_SPACES();
293 const unsigned int idx = ::strtoul10(sz,&sz);
294 AI_MD5_SKIP_SPACES();
295 if (idx >= desc.mVertices.size())
296 desc.mVertices.resize(idx+1);
297
298 VertexDesc& vert = desc.mVertices[idx];
299 if ('(' != *sz++)
300 MD5Parser::ReportWarning("Unexpected token: ( was expected",elem.iLineNumber);
301 AI_MD5_SKIP_SPACES();
302 sz = fast_atoreal_move<float>(sz,(float&)vert.mUV.x);
303 AI_MD5_SKIP_SPACES();
304 sz = fast_atoreal_move<float>(sz,(float&)vert.mUV.y);
305 AI_MD5_SKIP_SPACES();
306 if (')' != *sz++)
307 MD5Parser::ReportWarning("Unexpected token: ) was expected",elem.iLineNumber);
308 AI_MD5_SKIP_SPACES();
309 vert.mFirstWeight = ::strtoul10(sz,&sz);
310 AI_MD5_SKIP_SPACES();
311 vert.mNumWeights = ::strtoul10(sz,&sz);
312 }
313 // tri attribute
314 // "tri 0 15 13 12"
315 else if (TokenMatch(sz,"tri",3)) {
316 AI_MD5_SKIP_SPACES();
317 const unsigned int idx = strtoul10(sz,&sz);
318 if (idx >= desc.mFaces.size())
319 desc.mFaces.resize(idx+1);
320
321 aiFace& face = desc.mFaces[idx];
322 face.mIndices = new unsigned int[face.mNumIndices = 3];
323 for (unsigned int i = 0; i < 3;++i) {
324 AI_MD5_SKIP_SPACES();
325 face.mIndices[i] = strtoul10(sz,&sz);
326 }
327 }
328 // weight attribute
329 // "weight 362 5 0.500000 ( -3.553583 11.893474 9.719339 )"
330 else if (TokenMatch(sz,"weight",6)) {
331 AI_MD5_SKIP_SPACES();
332 const unsigned int idx = strtoul10(sz,&sz);
333 AI_MD5_SKIP_SPACES();
334 if (idx >= desc.mWeights.size())
335 desc.mWeights.resize(idx+1);
336
337 WeightDesc& weight = desc.mWeights[idx];
338 weight.mBone = strtoul10(sz,&sz);
339 AI_MD5_SKIP_SPACES();
340 sz = fast_atoreal_move<float>(sz,weight.mWeight);
341 AI_MD5_READ_TRIPLE(weight.vOffsetPosition);
342 }
343 }
344 }
345 }
346 DefaultLogger::get()->debug("MD5MeshParser end");
347 }
348
349 // ------------------------------------------------------------------------------------------------
350 // .MD5ANIM parsing function
MD5AnimParser(SectionList & mSections)351 MD5AnimParser::MD5AnimParser(SectionList& mSections)
352 {
353 DefaultLogger::get()->debug("MD5AnimParser begin");
354
355 fFrameRate = 24.0f;
356 mNumAnimatedComponents = UINT_MAX;
357 for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter) {
358 if ((*iter).mName == "hierarchy") {
359 // "sheath" 0 63 6
360 for (const auto & elem : (*iter).mElements) {
361 mAnimatedBones.push_back ( AnimBoneDesc () );
362 AnimBoneDesc& desc = mAnimatedBones.back();
363
364 const char* sz = elem.szStart;
365 AI_MD5_PARSE_STRING(desc.mName);
366 AI_MD5_SKIP_SPACES();
367
368 // parent index - negative values are allowed (at least -1)
369 desc.mParentIndex = ::strtol10(sz,&sz);
370
371 // flags (highest is 2^6-1)
372 AI_MD5_SKIP_SPACES();
373 if(63 < (desc.iFlags = ::strtoul10(sz,&sz))){
374 MD5Parser::ReportWarning("Invalid flag combination in hierarchy section",elem.iLineNumber);
375 }
376 AI_MD5_SKIP_SPACES();
377
378 // index of the first animation keyframe component for this joint
379 desc.iFirstKeyIndex = ::strtoul10(sz,&sz);
380 }
381 }
382 else if((*iter).mName == "baseframe") {
383 // ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000242 0.707107 )
384 for (const auto & elem : (*iter).mElements) {
385 const char* sz = elem.szStart;
386
387 mBaseFrames.push_back ( BaseFrameDesc () );
388 BaseFrameDesc& desc = mBaseFrames.back();
389
390 AI_MD5_READ_TRIPLE(desc.vPositionXYZ);
391 AI_MD5_READ_TRIPLE(desc.vRotationQuat);
392 }
393 }
394 else if((*iter).mName == "frame") {
395 if (!(*iter).mGlobalValue.length()) {
396 MD5Parser::ReportWarning("A frame section must have a frame index",(*iter).iLineNumber);
397 continue;
398 }
399
400 mFrames.push_back ( FrameDesc () );
401 FrameDesc& desc = mFrames.back();
402 desc.iIndex = strtoul10((*iter).mGlobalValue.c_str());
403
404 // we do already know how much storage we will presumably need
405 if (UINT_MAX != mNumAnimatedComponents) {
406 desc.mValues.reserve(mNumAnimatedComponents);
407 }
408
409 // now read all elements (continuous list of floats)
410 for (const auto & elem : (*iter).mElements){
411 const char* sz = elem.szStart;
412 while (SkipSpacesAndLineEnd(&sz)) {
413 float f;sz = fast_atoreal_move<float>(sz,f);
414 desc.mValues.push_back(f);
415 }
416 }
417 }
418 else if((*iter).mName == "numFrames") {
419 mFrames.reserve(strtoul10((*iter).mGlobalValue.c_str()));
420 }
421 else if((*iter).mName == "numJoints") {
422 const unsigned int num = strtoul10((*iter).mGlobalValue.c_str());
423 mAnimatedBones.reserve(num);
424
425 // try to guess the number of animated components if that element is not given
426 if (UINT_MAX == mNumAnimatedComponents) {
427 mNumAnimatedComponents = num * 6;
428 }
429 }
430 else if((*iter).mName == "numAnimatedComponents") {
431 mAnimatedBones.reserve( strtoul10((*iter).mGlobalValue.c_str()));
432 }
433 else if((*iter).mName == "frameRate") {
434 fast_atoreal_move<float>((*iter).mGlobalValue.c_str(),fFrameRate);
435 }
436 }
437 DefaultLogger::get()->debug("MD5AnimParser end");
438 }
439
440 // ------------------------------------------------------------------------------------------------
441 // .MD5CAMERA parsing function
MD5CameraParser(SectionList & mSections)442 MD5CameraParser::MD5CameraParser(SectionList& mSections)
443 {
444 DefaultLogger::get()->debug("MD5CameraParser begin");
445 fFrameRate = 24.0f;
446
447 for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter) {
448 if ((*iter).mName == "numFrames") {
449 frames.reserve(strtoul10((*iter).mGlobalValue.c_str()));
450 }
451 else if ((*iter).mName == "frameRate") {
452 fFrameRate = fast_atof ((*iter).mGlobalValue.c_str());
453 }
454 else if ((*iter).mName == "numCuts") {
455 cuts.reserve(strtoul10((*iter).mGlobalValue.c_str()));
456 }
457 else if ((*iter).mName == "cuts") {
458 for (const auto & elem : (*iter).mElements){
459 cuts.push_back(strtoul10(elem.szStart)+1);
460 }
461 }
462 else if ((*iter).mName == "camera") {
463 for (const auto & elem : (*iter).mElements){
464 const char* sz = elem.szStart;
465
466 frames.push_back(CameraAnimFrameDesc());
467 CameraAnimFrameDesc& cur = frames.back();
468 AI_MD5_READ_TRIPLE(cur.vPositionXYZ);
469 AI_MD5_READ_TRIPLE(cur.vRotationQuat);
470 AI_MD5_SKIP_SPACES();
471 cur.fFOV = fast_atof(sz);
472 }
473 }
474 }
475 DefaultLogger::get()->debug("MD5CameraParser end");
476 }
477
478