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