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