1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions 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 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF 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 | |
58 | using namespace Assimp; |
59 | using 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 | |
66 | static 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 |
81 | void 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 |
94 | MD2Importer::MD2Importer() |
95 | : configFrameID(), |
96 | m_pcHeader(), |
97 | mBuffer(), |
98 | fileSize() |
99 | {} |
100 | |
101 | // ------------------------------------------------------------------------------------------------ |
102 | // Destructor, private as well |
103 | MD2Importer::~MD2Importer() |
104 | {} |
105 | |
106 | // ------------------------------------------------------------------------------------------------ |
107 | // Returns whether the class can handle the format of the given file. |
108 | bool 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 |
125 | const aiImporterDesc* MD2Importer::GetInfo () const |
126 | { |
127 | return &desc; |
128 | } |
129 | |
130 | // ------------------------------------------------------------------------------------------------ |
131 | // Setup configuration properties |
132 | void 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 |
144 | void MD2Importer::( ) |
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. |
217 | void 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 | |