1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2021, assimp team
7 
8 
9 
10 All rights reserved.
11 
12 Redistribution and use of this software in source and binary forms,
13 with or without modification, are permitted provided that the following
14 conditions are met:
15 
16 * Redistributions of source code must retain the above
17   copyright notice, this list of conditions and the
18   following disclaimer.
19 
20 * Redistributions in binary form must reproduce the above
21   copyright notice, this list of conditions and the
22   following disclaimer in the documentation and/or other
23   materials provided with the distribution.
24 
25 * Neither the name of the assimp team, nor the names of its
26   contributors may be used to endorse or promote products
27   derived from this software without specific prior
28   written permission of the assimp team.
29 
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 ---------------------------------------------------------------------------
42 */
43 
44 
45 #ifndef ASSIMP_BUILD_NO_MD2_IMPORTER
46 
47 /** @file Implementation of the MD2 importer class */
48 #include "MD2Loader.h"
49 #include <assimp/ByteSwapper.h>
50 #include "MD2NormalTable.h" // shouldn't be included by other units
51 #include <assimp/DefaultLogger.hpp>
52 #include <assimp/Importer.hpp>
53 #include <assimp/IOSystem.hpp>
54 #include <assimp/scene.h>
55 #include <assimp/importerdesc.h>
56 #include <assimp/StringUtils.h>
57 
58 #include <memory>
59 
60 using namespace Assimp;
61 using namespace Assimp::MD2;
62 
63 // helper macro to determine the size of an array
64 #if (!defined ARRAYSIZE)
65 #   define ARRAYSIZE(_array) (int(sizeof(_array) / sizeof(_array[0])))
66 #endif
67 
68 static const aiImporterDesc desc = {
69     "Quake II Mesh Importer",
70     "",
71     "",
72     "",
73     aiImporterFlags_SupportBinaryFlavour,
74     0,
75     0,
76     0,
77     0,
78     "md2"
79 };
80 
81 // ------------------------------------------------------------------------------------------------
82 // Helper function to lookup a normal in Quake 2's precalculated table
LookupNormalIndex(uint8_t iNormalIndex,aiVector3D & vOut)83 void MD2::LookupNormalIndex(uint8_t iNormalIndex,aiVector3D& vOut)
84 {
85     // make sure the normal index has a valid value
86     if (iNormalIndex >= ARRAYSIZE(g_avNormals)) {
87         ASSIMP_LOG_WARN("Index overflow in Quake II normal vector list");
88         iNormalIndex = ARRAYSIZE(g_avNormals) - 1;
89     }
90     vOut = *((const aiVector3D*)(&g_avNormals[iNormalIndex]));
91 }
92 
93 
94 // ------------------------------------------------------------------------------------------------
95 // Constructor to be privately used by Importer
MD2Importer()96 MD2Importer::MD2Importer()
97     : configFrameID(),
98     m_pcHeader(),
99     mBuffer(),
100     fileSize()
101 {}
102 
103 // ------------------------------------------------------------------------------------------------
104 // Destructor, private as well
~MD2Importer()105 MD2Importer::~MD2Importer()
106 {}
107 
108 // ------------------------------------------------------------------------------------------------
109 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const110 bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
111 {
112     const std::string extension = GetExtension(pFile);
113     if (extension == "md2")
114         return true;
115 
116     // if check for extension is not enough, check for the magic tokens
117     if (!extension.length() || checkSig) {
118         uint32_t tokens[1];
119         tokens[0] = AI_MD2_MAGIC_NUMBER_LE;
120         return CheckMagicToken(pIOHandler,pFile,tokens,1);
121     }
122     return false;
123 }
124 
125 // ------------------------------------------------------------------------------------------------
126 // Get a list of all extensions supported by this loader
GetInfo() const127 const aiImporterDesc* MD2Importer::GetInfo () const
128 {
129     return &desc;
130 }
131 
132 // ------------------------------------------------------------------------------------------------
133 // Setup configuration properties
SetupProperties(const Importer * pImp)134 void MD2Importer::SetupProperties(const Importer* pImp)
135 {
136     // The
137     // AI_CONFIG_IMPORT_MD2_KEYFRAME option overrides the
138     // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
139     configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD2_KEYFRAME,-1);
140     if(static_cast<unsigned int>(-1) == configFrameID){
141         configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
142     }
143 }
144 // ------------------------------------------------------------------------------------------------
145 // Validate the file header
ValidateHeader()146 void MD2Importer::ValidateHeader( )
147 {
148     // check magic number
149     if (m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE &&
150         m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE)
151     {
152         throw DeadlyImportError("Invalid MD2 magic word: expected IDP2, found ",
153                                 ai_str_toprintable((char *)&m_pcHeader->magic, 4));
154     }
155 
156     // check file format version
157     if (m_pcHeader->version != 8)
158         ASSIMP_LOG_WARN( "Unsupported MD2 file version. Continuing happily ...");
159 
160     // check some values whether they are valid
161     if (0 == m_pcHeader->numFrames)
162         throw DeadlyImportError( "Invalid MD2 file: NUM_FRAMES is 0");
163 
164     if (m_pcHeader->offsetEnd > (uint32_t)fileSize)
165         throw DeadlyImportError( "Invalid MD2 file: File is too small");
166 
167     if (m_pcHeader->numSkins > AI_MAX_ALLOC(MD2::Skin)) {
168         throw DeadlyImportError("Invalid MD2 header: Too many skins, would overflow");
169     }
170 
171     if (m_pcHeader->numVertices > AI_MAX_ALLOC(MD2::Vertex)) {
172         throw DeadlyImportError("Invalid MD2 header: Too many vertices, would overflow");
173     }
174 
175     if (m_pcHeader->numTexCoords > AI_MAX_ALLOC(MD2::TexCoord)) {
176         throw DeadlyImportError("Invalid MD2 header: Too many texcoords, would overflow");
177     }
178 
179     if (m_pcHeader->numTriangles > AI_MAX_ALLOC(MD2::Triangle)) {
180         throw DeadlyImportError("Invalid MD2 header: Too many triangles, would overflow");
181     }
182 
183     if (m_pcHeader->numFrames > AI_MAX_ALLOC(MD2::Frame)) {
184         throw DeadlyImportError("Invalid MD2 header: Too many frames, would overflow");
185     }
186 
187     // -1 because Frame already contains one
188     unsigned int frameSize = sizeof (MD2::Frame) + (m_pcHeader->numVertices - 1) * sizeof(MD2::Vertex);
189 
190     if (m_pcHeader->offsetSkins     + m_pcHeader->numSkins * sizeof (MD2::Skin)         >= fileSize ||
191         m_pcHeader->offsetTexCoords + m_pcHeader->numTexCoords * sizeof (MD2::TexCoord) >= fileSize ||
192         m_pcHeader->offsetTriangles + m_pcHeader->numTriangles * sizeof (MD2::Triangle) >= fileSize ||
193         m_pcHeader->offsetFrames    + m_pcHeader->numFrames * frameSize                 >= fileSize ||
194         m_pcHeader->offsetEnd           > fileSize)
195     {
196         throw DeadlyImportError("Invalid MD2 header: Some offsets are outside the file");
197     }
198 
199     if (m_pcHeader->numSkins > AI_MD2_MAX_SKINS)
200         ASSIMP_LOG_WARN("The model contains more skins than Quake 2 supports");
201     if ( m_pcHeader->numFrames > AI_MD2_MAX_FRAMES)
202         ASSIMP_LOG_WARN("The model contains more frames than Quake 2 supports");
203     if (m_pcHeader->numVertices > AI_MD2_MAX_VERTS)
204         ASSIMP_LOG_WARN("The model contains more vertices than Quake 2 supports");
205 
206     if (m_pcHeader->numFrames <= configFrameID )
207         throw DeadlyImportError("MD2: The requested frame (", configFrameID, ") does not exist in the file");
208 }
209 
210 // ------------------------------------------------------------------------------------------------
211 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)212 void MD2Importer::InternReadFile( const std::string& pFile,
213     aiScene* pScene, IOSystem* pIOHandler)
214 {
215     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
216 
217     // Check whether we can read from the file
218     if (file.get() == nullptr) {
219         throw DeadlyImportError("Failed to open MD2 file ", pFile, "");
220     }
221 
222     // check whether the md3 file is large enough to contain
223     // at least the file header
224     fileSize = (unsigned int)file->FileSize();
225     if (fileSize < sizeof(MD2::Header)) {
226         throw DeadlyImportError("MD2 File is too small");
227     }
228 
229     std::vector<uint8_t> mBuffer2(fileSize);
230     file->Read(&mBuffer2[0], 1, fileSize);
231     mBuffer = &mBuffer2[0];
232 
233     m_pcHeader = (BE_NCONST MD2::Header*)mBuffer;
234 
235 #ifdef AI_BUILD_BIG_ENDIAN
236 
237     ByteSwap::Swap4(&m_pcHeader->frameSize);
238     ByteSwap::Swap4(&m_pcHeader->magic);
239     ByteSwap::Swap4(&m_pcHeader->numFrames);
240     ByteSwap::Swap4(&m_pcHeader->numGlCommands);
241     ByteSwap::Swap4(&m_pcHeader->numSkins);
242     ByteSwap::Swap4(&m_pcHeader->numTexCoords);
243     ByteSwap::Swap4(&m_pcHeader->numTriangles);
244     ByteSwap::Swap4(&m_pcHeader->numVertices);
245     ByteSwap::Swap4(&m_pcHeader->offsetEnd);
246     ByteSwap::Swap4(&m_pcHeader->offsetFrames);
247     ByteSwap::Swap4(&m_pcHeader->offsetGlCommands);
248     ByteSwap::Swap4(&m_pcHeader->offsetSkins);
249     ByteSwap::Swap4(&m_pcHeader->offsetTexCoords);
250     ByteSwap::Swap4(&m_pcHeader->offsetTriangles);
251     ByteSwap::Swap4(&m_pcHeader->skinHeight);
252     ByteSwap::Swap4(&m_pcHeader->skinWidth);
253     ByteSwap::Swap4(&m_pcHeader->version);
254 
255 #endif
256 
257     ValidateHeader();
258 
259     // there won't be more than one mesh inside the file
260     pScene->mNumMaterials = 1;
261     pScene->mRootNode = new aiNode();
262     pScene->mRootNode->mNumMeshes = 1;
263     pScene->mRootNode->mMeshes = new unsigned int[1];
264     pScene->mRootNode->mMeshes[0] = 0;
265     pScene->mMaterials = new aiMaterial*[1];
266     pScene->mMaterials[0] = new aiMaterial();
267     pScene->mNumMeshes = 1;
268     pScene->mMeshes = new aiMesh*[1];
269 
270     aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
271     pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
272 
273     // navigate to the begin of the current frame data
274 	BE_NCONST MD2::Frame* pcFrame = (BE_NCONST MD2::Frame*) ((uint8_t*)
275 		m_pcHeader + m_pcHeader->offsetFrames + (m_pcHeader->frameSize * configFrameID));
276 
277     // navigate to the begin of the triangle data
278     MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*)
279         m_pcHeader + m_pcHeader->offsetTriangles);
280 
281     // navigate to the begin of the tex coords data
282     BE_NCONST MD2::TexCoord* pcTexCoords = (BE_NCONST MD2::TexCoord*) ((uint8_t*)
283         m_pcHeader + m_pcHeader->offsetTexCoords);
284 
285     // navigate to the begin of the vertex data
286     BE_NCONST MD2::Vertex* pcVerts = (BE_NCONST MD2::Vertex*) (pcFrame->vertices);
287 
288 #ifdef AI_BUILD_BIG_ENDIAN
289     for (uint32_t i = 0; i< m_pcHeader->numTriangles; ++i)
290     {
291         for (unsigned int p = 0; p < 3;++p)
292         {
293             ByteSwap::Swap2(& pcTriangles[i].textureIndices[p]);
294             ByteSwap::Swap2(& pcTriangles[i].vertexIndices[p]);
295         }
296     }
297     for (uint32_t i = 0; i < m_pcHeader->offsetTexCoords;++i)
298     {
299         ByteSwap::Swap2(& pcTexCoords[i].s);
300         ByteSwap::Swap2(& pcTexCoords[i].t);
301     }
302     ByteSwap::Swap4( & pcFrame->scale[0] );
303     ByteSwap::Swap4( & pcFrame->scale[1] );
304     ByteSwap::Swap4( & pcFrame->scale[2] );
305     ByteSwap::Swap4( & pcFrame->translate[0] );
306     ByteSwap::Swap4( & pcFrame->translate[1] );
307     ByteSwap::Swap4( & pcFrame->translate[2] );
308 #endif
309 
310     pcMesh->mNumFaces = m_pcHeader->numTriangles;
311     pcMesh->mFaces = new aiFace[m_pcHeader->numTriangles];
312 
313     // allocate output storage
314     pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3;
315     pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
316     pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
317 
318     // Not sure whether there are MD2 files without texture coordinates
319     // NOTE: texture coordinates can be there without a texture,
320     // but a texture can't be there without a valid UV channel
321     aiMaterial* pcHelper = (aiMaterial*)pScene->mMaterials[0];
322     const int iMode = (int)aiShadingMode_Gouraud;
323     pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
324 
325     if (m_pcHeader->numTexCoords && m_pcHeader->numSkins)
326     {
327         // navigate to the first texture associated with the mesh
328         const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)m_pcHeader +
329             m_pcHeader->offsetSkins);
330 
331         aiColor3D clr;
332         clr.b = clr.g = clr.r = 1.0f;
333         pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
334         pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
335 
336         clr.b = clr.g = clr.r = 0.05f;
337         pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
338 
339         if (pcSkins->name[0])
340         {
341             aiString szString;
342             const ai_uint32 iLen = (ai_uint32) ::strlen(pcSkins->name);
343             ::memcpy(szString.data,pcSkins->name,iLen);
344             szString.data[iLen] = '\0';
345             szString.length = iLen;
346 
347             pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
348         }
349         else{
350             ASSIMP_LOG_WARN("Texture file name has zero length. It will be skipped.");
351         }
352     }
353     else    {
354         // apply a default material
355         aiColor3D clr;
356         clr.b = clr.g = clr.r = 0.6f;
357         pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
358         pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
359 
360         clr.b = clr.g = clr.r = 0.05f;
361         pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
362 
363         aiString szName;
364         szName.Set(AI_DEFAULT_MATERIAL_NAME);
365         pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
366 
367         aiString sz;
368 
369         // TODO: Try to guess the name of the texture file from the model file name
370 
371         sz.Set("$texture_dummy.bmp");
372         pcHelper->AddProperty(&sz,AI_MATKEY_TEXTURE_DIFFUSE(0));
373     }
374 
375 
376     // now read all triangles of the first frame, apply scaling and translation
377     unsigned int iCurrent = 0;
378 
379     float fDivisorU = 1.0f,fDivisorV = 1.0f;
380     if (m_pcHeader->numTexCoords)   {
381         // allocate storage for texture coordinates, too
382         pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
383         pcMesh->mNumUVComponents[0] = 2;
384 
385         // check whether the skin width or height are zero (this would
386         // cause a division through zero)
387         if (!m_pcHeader->skinWidth) {
388             ASSIMP_LOG_ERROR("MD2: No valid skin width given");
389         }
390         else fDivisorU = (float)m_pcHeader->skinWidth;
391         if (!m_pcHeader->skinHeight){
392             ASSIMP_LOG_ERROR("MD2: No valid skin height given");
393         }
394         else fDivisorV = (float)m_pcHeader->skinHeight;
395     }
396 
397     for (unsigned int i = 0; i < (unsigned int)m_pcHeader->numTriangles;++i)    {
398         // Allocate the face
399         pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3];
400         pScene->mMeshes[0]->mFaces[i].mNumIndices = 3;
401 
402         // copy texture coordinates
403         // check whether they are different from the previous value at this index.
404         // In this case, create a full separate set of vertices/normals/texcoords
405         for (unsigned int c = 0; c < 3;++c,++iCurrent)  {
406 
407             // validate vertex indices
408             unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
409             if (iIndex >= m_pcHeader->numVertices)  {
410                 ASSIMP_LOG_ERROR("MD2: Vertex index is outside the allowed range");
411                 iIndex = m_pcHeader->numVertices-1;
412             }
413 
414             // read x,y, and z component of the vertex
415             aiVector3D& vec = pcMesh->mVertices[iCurrent];
416 
417             vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
418             vec.x += pcFrame->translate[0];
419 
420             vec.y = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1];
421             vec.y += pcFrame->translate[1];
422 
423             vec.z = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2];
424             vec.z += pcFrame->translate[2];
425 
426             // read the normal vector from the precalculated normal table
427             aiVector3D& vNormal = pcMesh->mNormals[iCurrent];
428             LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal);
429 
430             if (m_pcHeader->numTexCoords)   {
431                 // validate texture coordinates
432                 iIndex = pcTriangles[i].textureIndices[c];
433                 if (iIndex >= m_pcHeader->numTexCoords) {
434                     ASSIMP_LOG_ERROR("MD2: UV index is outside the allowed range");
435                     iIndex = m_pcHeader->numTexCoords-1;
436                 }
437 
438                 aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent];
439 
440                 // the texture coordinates are absolute values but we
441                 // need relative values between 0 and 1
442                 pcOut.x = pcTexCoords[iIndex].s / fDivisorU;
443                 pcOut.y = 1.f-pcTexCoords[iIndex].t / fDivisorV;
444             }
445             pScene->mMeshes[0]->mFaces[i].mIndices[c] = iCurrent;
446         }
447         // flip the face order
448         std::swap( pScene->mMeshes[0]->mFaces[i].mIndices[0], pScene->mMeshes[0]->mFaces[i].mIndices[2] );
449     }
450     // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
451     pScene->mRootNode->mTransformation = aiMatrix4x4(
452             1.f, 0.f, 0.f, 0.f,
453             0.f, 0.f, 1.f, 0.f,
454             0.f, -1.f, 0.f, 0.f,
455             0.f, 0.f, 0.f, 1.f);
456 }
457 
458 #endif // !! ASSIMP_BUILD_NO_MD2_IMPORTER
459