vrshoot
view libs/assimp/MDLLoader.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line source
1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
6 Copyright (c) 2006-2012, assimp team
8 All rights reserved.
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:
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
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.
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.
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 */
42 /** @file MDLLoader.cpp
43 * @brief Implementation of the main parts of the MDL importer class
44 * *TODO* Cleanup and further testing of some parts necessary
45 */
47 // internal headers
48 #include "AssimpPCH.h"
49 #ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
51 #include "MDLLoader.h"
52 #include "MDLDefaultColorMap.h"
53 #include "MD2FileData.h"
55 using namespace Assimp;
57 static const aiImporterDesc desc = {
58 "Quake Mesh / 3D GameStudio Mesh Importer",
59 "",
60 "",
61 "",
62 aiImporterFlags_SupportBinaryFlavour,
63 0,
64 0,
65 7,
66 0,
67 "mdl"
68 };
70 // ------------------------------------------------------------------------------------------------
71 // Ugly stuff ... nevermind
72 #define _AI_MDL7_ACCESS(_data, _index, _limit, _type) \
73 (*((const _type*)(((const char*)_data) + _index * _limit)))
75 #define _AI_MDL7_ACCESS_PTR(_data, _index, _limit, _type) \
76 ((BE_NCONST _type*)(((const char*)_data) + _index * _limit))
78 #define _AI_MDL7_ACCESS_VERT(_data, _index, _limit) \
79 _AI_MDL7_ACCESS(_data,_index,_limit,MDL::Vertex_MDL7)
81 // ------------------------------------------------------------------------------------------------
82 // Constructor to be privately used by Importer
83 MDLImporter::MDLImporter()
84 {}
86 // ------------------------------------------------------------------------------------------------
87 // Destructor, private as well
88 MDLImporter::~MDLImporter()
89 {}
91 // ------------------------------------------------------------------------------------------------
92 // Returns whether the class can handle the format of the given file.
93 bool MDLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
94 {
95 const std::string extension = GetExtension(pFile);
97 // if check for extension is not enough, check for the magic tokens
98 if (extension == "mdl" || !extension.length() || checkSig) {
99 uint32_t tokens[8];
100 tokens[0] = AI_MDL_MAGIC_NUMBER_LE_HL2a;
101 tokens[1] = AI_MDL_MAGIC_NUMBER_LE_HL2b;
102 tokens[2] = AI_MDL_MAGIC_NUMBER_LE_GS7;
103 tokens[3] = AI_MDL_MAGIC_NUMBER_LE_GS5b;
104 tokens[4] = AI_MDL_MAGIC_NUMBER_LE_GS5a;
105 tokens[5] = AI_MDL_MAGIC_NUMBER_LE_GS4;
106 tokens[6] = AI_MDL_MAGIC_NUMBER_LE_GS3;
107 tokens[7] = AI_MDL_MAGIC_NUMBER_LE;
108 return CheckMagicToken(pIOHandler,pFile,tokens,8,0);
109 }
110 return false;
111 }
113 // ------------------------------------------------------------------------------------------------
114 // Setup configuration properties
115 void MDLImporter::SetupProperties(const Importer* pImp)
116 {
117 configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MDL_KEYFRAME,-1);
119 // The
120 // AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the
121 // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
122 if(static_cast<unsigned int>(-1) == configFrameID) {
123 configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
124 }
126 // AI_CONFIG_IMPORT_MDL_COLORMAP - pallette file
127 configPalette = pImp->GetPropertyString(AI_CONFIG_IMPORT_MDL_COLORMAP,"colormap.lmp");
128 }
130 // ------------------------------------------------------------------------------------------------
131 // Get a list of all supported extensions
132 const aiImporterDesc* MDLImporter::GetInfo () const
133 {
134 return &desc;
135 }
137 // ------------------------------------------------------------------------------------------------
138 // Imports the given file into the given scene structure.
139 void MDLImporter::InternReadFile( const std::string& pFile,
140 aiScene* _pScene, IOSystem* _pIOHandler)
141 {
142 pScene = _pScene;
143 pIOHandler = _pIOHandler;
144 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
146 // Check whether we can read from the file
147 if( file.get() == NULL) {
148 throw DeadlyImportError( "Failed to open MDL file " + pFile + ".");
149 }
151 // This should work for all other types of MDL files, too ...
152 // the quake header is one of the smallest, afaik
153 iFileSize = (unsigned int)file->FileSize();
154 if( iFileSize < sizeof(MDL::Header)) {
155 throw DeadlyImportError( "MDL File is too small.");
156 }
158 // Allocate storage and copy the contents of the file to a memory buffer
159 std::vector<unsigned char> buffer(iFileSize+1);
160 mBuffer = &buffer[0];
161 file->Read( (void*)mBuffer, 1, iFileSize);
163 // Append a binary zero to the end of the buffer.
164 // this is just for safety that string parsing routines
165 // find the end of the buffer ...
166 mBuffer[iFileSize] = '\0';
167 const uint32_t iMagicWord = *((uint32_t*)mBuffer);
169 // Determine the file subtype and call the appropriate member function
171 // Original Quake1 format
172 if (AI_MDL_MAGIC_NUMBER_BE == iMagicWord || AI_MDL_MAGIC_NUMBER_LE == iMagicWord) {
173 DefaultLogger::get()->debug("MDL subtype: Quake 1, magic word is IDPO");
174 iGSFileVersion = 0;
175 InternReadFile_Quake1();
176 }
177 // GameStudio A<old> MDL2 format - used by some test models that come with 3DGS
178 else if (AI_MDL_MAGIC_NUMBER_BE_GS3 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS3 == iMagicWord) {
179 DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A2, magic word is MDL2");
180 iGSFileVersion = 2;
181 InternReadFile_Quake1();
182 }
183 // GameStudio A4 MDL3 format
184 else if (AI_MDL_MAGIC_NUMBER_BE_GS4 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS4 == iMagicWord) {
185 DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A4, magic word is MDL3");
186 iGSFileVersion = 3;
187 InternReadFile_3DGS_MDL345();
188 }
189 // GameStudio A5+ MDL4 format
190 else if (AI_MDL_MAGIC_NUMBER_BE_GS5a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS5a == iMagicWord) {
191 DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A4, magic word is MDL4");
192 iGSFileVersion = 4;
193 InternReadFile_3DGS_MDL345();
194 }
195 // GameStudio A5+ MDL5 format
196 else if (AI_MDL_MAGIC_NUMBER_BE_GS5b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS5b == iMagicWord) {
197 DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A5, magic word is MDL5");
198 iGSFileVersion = 5;
199 InternReadFile_3DGS_MDL345();
200 }
201 // GameStudio A7 MDL7 format
202 else if (AI_MDL_MAGIC_NUMBER_BE_GS7 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS7 == iMagicWord) {
203 DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A7, magic word is MDL7");
204 iGSFileVersion = 7;
205 InternReadFile_3DGS_MDL7();
206 }
207 // IDST/IDSQ Format (CS:S/HL^2, etc ...)
208 else if (AI_MDL_MAGIC_NUMBER_BE_HL2a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2a == iMagicWord ||
209 AI_MDL_MAGIC_NUMBER_BE_HL2b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2b == iMagicWord)
210 {
211 DefaultLogger::get()->debug("MDL subtype: Source(tm) Engine, magic word is IDST/IDSQ");
212 iGSFileVersion = 0;
213 InternReadFile_HL2();
214 }
215 else {
216 // print the magic word to the log file
217 throw DeadlyImportError( "Unknown MDL subformat " + pFile +
218 ". Magic word (" + std::string((char*)&iMagicWord,4) + ") is not known");
219 }
221 // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
222 pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
223 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
225 // delete the file buffer and cleanup
226 AI_DEBUG_INVALIDATE_PTR(mBuffer);
227 AI_DEBUG_INVALIDATE_PTR(pIOHandler);
228 AI_DEBUG_INVALIDATE_PTR(pScene);
229 }
231 // ------------------------------------------------------------------------------------------------
232 // Check whether we're still inside the valid file range
233 void MDLImporter::SizeCheck(const void* szPos)
234 {
235 if (!szPos || (const unsigned char*)szPos > this->mBuffer + this->iFileSize)
236 {
237 throw DeadlyImportError("Invalid MDL file. The file is too small "
238 "or contains invalid data.");
239 }
240 }
242 // ------------------------------------------------------------------------------------------------
243 // Just for debgging purposes
244 void MDLImporter::SizeCheck(const void* szPos, const char* szFile, unsigned int iLine)
245 {
246 ai_assert(NULL != szFile);
247 if (!szPos || (const unsigned char*)szPos > mBuffer + iFileSize)
248 {
249 // remove a directory if there is one
250 const char* szFilePtr = ::strrchr(szFile,'\\');
251 if (!szFilePtr) {
252 if(!(szFilePtr = ::strrchr(szFile,'/')))
253 szFilePtr = szFile;
254 }
255 if (szFilePtr)++szFilePtr;
257 char szBuffer[1024];
258 ::sprintf(szBuffer,"Invalid MDL file. The file is too small "
259 "or contains invalid data (File: %s Line: %i)",szFilePtr,iLine);
261 throw DeadlyImportError(szBuffer);
262 }
263 }
265 // ------------------------------------------------------------------------------------------------
266 // Validate a quake file header
267 void MDLImporter::ValidateHeader_Quake1(const MDL::Header* pcHeader)
268 {
269 // some values may not be NULL
270 if (!pcHeader->num_frames)
271 throw DeadlyImportError( "[Quake 1 MDL] There are no frames in the file");
273 if (!pcHeader->num_verts)
274 throw DeadlyImportError( "[Quake 1 MDL] There are no vertices in the file");
276 if (!pcHeader->num_tris)
277 throw DeadlyImportError( "[Quake 1 MDL] There are no triangles in the file");
279 // check whether the maxima are exceeded ...however, this applies for Quake 1 MDLs only
280 if (!this->iGSFileVersion)
281 {
282 if (pcHeader->num_verts > AI_MDL_MAX_VERTS)
283 DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_VERTS vertices");
285 if (pcHeader->num_tris > AI_MDL_MAX_TRIANGLES)
286 DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_TRIANGLES triangles");
288 if (pcHeader->num_frames > AI_MDL_MAX_FRAMES)
289 DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_FRAMES frames");
291 // (this does not apply for 3DGS MDLs)
292 if (!this->iGSFileVersion && pcHeader->version != AI_MDL_VERSION)
293 DefaultLogger::get()->warn("Quake 1 MDL model has an unknown version: AI_MDL_VERSION (=6) is "
294 "the expected file format version");
295 if(pcHeader->num_skins && (!pcHeader->skinwidth || !pcHeader->skinheight))
296 DefaultLogger::get()->warn("Skin width or height are 0");
297 }
298 }
300 #ifdef AI_BUILD_BIG_ENDIAN
301 // ------------------------------------------------------------------------------------------------
302 void FlipQuakeHeader(BE_NCONST MDL::Header* pcHeader)
303 {
304 AI_SWAP4( pcHeader->ident);
305 AI_SWAP4( pcHeader->version);
306 AI_SWAP4( pcHeader->boundingradius);
307 AI_SWAP4( pcHeader->flags);
308 AI_SWAP4( pcHeader->num_frames);
309 AI_SWAP4( pcHeader->num_skins);
310 AI_SWAP4( pcHeader->num_tris);
311 AI_SWAP4( pcHeader->num_verts);
312 for (unsigned int i = 0; i < 3;++i)
313 {
314 AI_SWAP4( pcHeader->scale[i]);
315 AI_SWAP4( pcHeader->translate[i]);
316 }
317 AI_SWAP4( pcHeader->size);
318 AI_SWAP4( pcHeader->skinheight);
319 AI_SWAP4( pcHeader->skinwidth);
320 AI_SWAP4( pcHeader->synctype);
321 }
322 #endif
324 // ------------------------------------------------------------------------------------------------
325 // Read a Quake 1 file
326 void MDLImporter::InternReadFile_Quake1( )
327 {
328 ai_assert(NULL != pScene);
329 BE_NCONST MDL::Header *pcHeader = (BE_NCONST MDL::Header*)this->mBuffer;
331 #ifdef AI_BUILD_BIG_ENDIAN
332 FlipQuakeHeader(pcHeader);
333 #endif
335 ValidateHeader_Quake1(pcHeader);
337 // current cursor position in the file
338 const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
340 // need to read all textures
341 for (unsigned int i = 0; i < (unsigned int)pcHeader->num_skins;++i)
342 {
343 union{BE_NCONST MDL::Skin* pcSkin;BE_NCONST MDL::GroupSkin* pcGroupSkin;};
344 pcSkin = (BE_NCONST MDL::Skin*)szCurrent;
346 AI_SWAP4( pcSkin->group );
348 // Quake 1 groupskins
349 if (1 == pcSkin->group)
350 {
351 AI_SWAP4( pcGroupSkin->nb );
353 // need to skip multiple images
354 const unsigned int iNumImages = (unsigned int)pcGroupSkin->nb;
355 szCurrent += sizeof(uint32_t) * 2;
357 if (0 != iNumImages)
358 {
359 if (!i) {
360 // however, create only one output image (the first)
361 this->CreateTextureARGB8_3DGS_MDL3(szCurrent + iNumImages * sizeof(float));
362 }
363 // go to the end of the skin section / the beginning of the next skin
364 szCurrent += pcHeader->skinheight * pcHeader->skinwidth +
365 sizeof(float) * iNumImages;
366 }
367 }
368 // 3DGS has a few files that are using other 3DGS like texture formats here
369 else
370 {
371 szCurrent += sizeof(uint32_t);
372 unsigned int iSkip = i ? UINT_MAX : 0;
373 CreateTexture_3DGS_MDL4(szCurrent,pcSkin->group,&iSkip);
374 szCurrent += iSkip;
375 }
376 }
377 // get a pointer to the texture coordinates
378 BE_NCONST MDL::TexCoord* pcTexCoords = (BE_NCONST MDL::TexCoord*)szCurrent;
379 szCurrent += sizeof(MDL::TexCoord) * pcHeader->num_verts;
381 // get a pointer to the triangles
382 BE_NCONST MDL::Triangle* pcTriangles = (BE_NCONST MDL::Triangle*)szCurrent;
383 szCurrent += sizeof(MDL::Triangle) * pcHeader->num_tris;
384 VALIDATE_FILE_SIZE(szCurrent);
386 // now get a pointer to the first frame in the file
387 BE_NCONST MDL::Frame* pcFrames = (BE_NCONST MDL::Frame*)szCurrent;
388 BE_NCONST MDL::SimpleFrame* pcFirstFrame;
390 if (0 == pcFrames->type)
391 {
392 // get address of single frame
393 pcFirstFrame = &pcFrames->frame;
394 }
395 else
396 {
397 // get the first frame in the group
398 BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames;
399 pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type);
400 }
401 BE_NCONST MDL::Vertex* pcVertices = (BE_NCONST MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name));
402 VALIDATE_FILE_SIZE((const unsigned char*)(pcVertices + pcHeader->num_verts));
404 #ifdef AI_BUILD_BIG_ENDIAN
405 for (int i = 0; i<pcHeader->num_verts;++i)
406 {
407 AI_SWAP4( pcTexCoords[i].onseam );
408 AI_SWAP4( pcTexCoords[i].s );
409 AI_SWAP4( pcTexCoords[i].t );
410 }
412 for (int i = 0; i<pcHeader->num_tris;++i)
413 {
414 AI_SWAP4( pcTriangles[i].facesfront);
415 AI_SWAP4( pcTriangles[i].vertex[0]);
416 AI_SWAP4( pcTriangles[i].vertex[1]);
417 AI_SWAP4( pcTriangles[i].vertex[2]);
418 }
419 #endif
421 // setup materials
422 SetupMaterialProperties_3DGS_MDL5_Quake1();
424 // allocate enough storage to hold all vertices and triangles
425 aiMesh* pcMesh = new aiMesh();
427 pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
428 pcMesh->mNumVertices = pcHeader->num_tris * 3;
429 pcMesh->mNumFaces = pcHeader->num_tris;
430 pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
431 pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
432 pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
433 pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
434 pcMesh->mNumUVComponents[0] = 2;
436 // there won't be more than one mesh inside the file
437 pScene->mRootNode = new aiNode();
438 pScene->mRootNode->mNumMeshes = 1;
439 pScene->mRootNode->mMeshes = new unsigned int[1];
440 pScene->mRootNode->mMeshes[0] = 0;
441 pScene->mNumMeshes = 1;
442 pScene->mMeshes = new aiMesh*[1];
443 pScene->mMeshes[0] = pcMesh;
445 // now iterate through all triangles
446 unsigned int iCurrent = 0;
447 for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i)
448 {
449 pcMesh->mFaces[i].mIndices = new unsigned int[3];
450 pcMesh->mFaces[i].mNumIndices = 3;
452 unsigned int iTemp = iCurrent;
453 for (unsigned int c = 0; c < 3;++c,++iCurrent)
454 {
455 pcMesh->mFaces[i].mIndices[c] = iCurrent;
457 // read vertices
458 unsigned int iIndex = pcTriangles->vertex[c];
459 if (iIndex >= (unsigned int)pcHeader->num_verts)
460 {
461 iIndex = pcHeader->num_verts-1;
462 DefaultLogger::get()->warn("Index overflow in Q1-MDL vertex list.");
463 }
465 aiVector3D& vec = pcMesh->mVertices[iCurrent];
466 vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
467 vec.x += pcHeader->translate[0];
469 vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
470 vec.y += pcHeader->translate[1];
471 //vec.y *= -1.0f;
473 vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
474 vec.z += pcHeader->translate[2];
476 // read the normal vector from the precalculated normal table
477 MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
478 //pcMesh->mNormals[iCurrent].y *= -1.0f;
480 // read texture coordinates
481 float s = (float)pcTexCoords[iIndex].s;
482 float t = (float)pcTexCoords[iIndex].t;
484 // translate texture coordinates
485 if (0 == pcTriangles->facesfront && 0 != pcTexCoords[iIndex].onseam) {
486 s += pcHeader->skinwidth * 0.5f;
487 }
489 // Scale s and t to range from 0.0 to 1.0
490 pcMesh->mTextureCoords[0][iCurrent].x = (s + 0.5f) / pcHeader->skinwidth;
491 pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-(t + 0.5f) / pcHeader->skinheight;
493 }
494 pcMesh->mFaces[i].mIndices[0] = iTemp+2;
495 pcMesh->mFaces[i].mIndices[1] = iTemp+1;
496 pcMesh->mFaces[i].mIndices[2] = iTemp+0;
497 pcTriangles++;
498 }
499 return;
500 }
502 // ------------------------------------------------------------------------------------------------
503 // Setup material properties for Quake and older GameStudio files
504 void MDLImporter::SetupMaterialProperties_3DGS_MDL5_Quake1( )
505 {
506 const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
508 // allocate ONE material
509 pScene->mMaterials = new aiMaterial*[1];
510 pScene->mMaterials[0] = new aiMaterial();
511 pScene->mNumMaterials = 1;
513 // setup the material's properties
514 const int iMode = (int)aiShadingMode_Gouraud;
515 aiMaterial* const pcHelper = (aiMaterial*)pScene->mMaterials[0];
516 pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
518 aiColor4D clr;
519 if (0 != pcHeader->num_skins && pScene->mNumTextures) {
520 // can we replace the texture with a single color?
521 clr = this->ReplaceTextureWithColor(pScene->mTextures[0]);
522 if (is_not_qnan(clr.r)) {
523 delete pScene->mTextures[0];
524 delete[] pScene->mTextures;
526 pScene->mTextures = NULL;
527 pScene->mNumTextures = 0;
528 }
529 else {
530 clr.b = clr.a = clr.g = clr.r = 1.0f;
531 aiString szString;
532 ::memcpy(szString.data,AI_MAKE_EMBEDDED_TEXNAME(0),3);
533 szString.length = 2;
534 pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
535 }
536 }
538 pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
539 pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
541 clr.r *= 0.05f;clr.g *= 0.05f;
542 clr.b *= 0.05f;clr.a = 1.0f;
543 pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
544 }
546 // ------------------------------------------------------------------------------------------------
547 // Read a MDL 3,4,5 file
548 void MDLImporter::InternReadFile_3DGS_MDL345( )
549 {
550 ai_assert(NULL != pScene);
552 // the header of MDL 3/4/5 is nearly identical to the original Quake1 header
553 BE_NCONST MDL::Header *pcHeader = (BE_NCONST MDL::Header*)this->mBuffer;
554 #ifdef AI_BUILD_BIG_ENDIAN
555 FlipQuakeHeader(pcHeader);
556 #endif
557 ValidateHeader_Quake1(pcHeader);
559 // current cursor position in the file
560 const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
562 // need to read all textures
563 for (unsigned int i = 0; i < (unsigned int)pcHeader->num_skins;++i) {
564 BE_NCONST MDL::Skin* pcSkin;
565 pcSkin = (BE_NCONST MDL::Skin*)szCurrent;
566 AI_SWAP4( pcSkin->group);
567 // create one output image
568 unsigned int iSkip = i ? UINT_MAX : 0;
569 if (5 <= iGSFileVersion)
570 {
571 // MDL5 format could contain MIPmaps
572 CreateTexture_3DGS_MDL5((unsigned char*)pcSkin + sizeof(uint32_t),
573 pcSkin->group,&iSkip);
574 }
575 else {
576 CreateTexture_3DGS_MDL4((unsigned char*)pcSkin + sizeof(uint32_t),
577 pcSkin->group,&iSkip);
578 }
579 // need to skip one image
580 szCurrent += iSkip + sizeof(uint32_t);
582 }
583 // get a pointer to the texture coordinates
584 BE_NCONST MDL::TexCoord_MDL3* pcTexCoords = (BE_NCONST MDL::TexCoord_MDL3*)szCurrent;
585 szCurrent += sizeof(MDL::TexCoord_MDL3) * pcHeader->synctype;
587 // NOTE: for MDLn formats "synctype" corresponds to the number of UV coords
589 // get a pointer to the triangles
590 BE_NCONST MDL::Triangle_MDL3* pcTriangles = (BE_NCONST MDL::Triangle_MDL3*)szCurrent;
591 szCurrent += sizeof(MDL::Triangle_MDL3) * pcHeader->num_tris;
593 #ifdef AI_BUILD_BIG_ENDIAN
595 for (int i = 0; i<pcHeader->synctype;++i) {
596 AI_SWAP2( pcTexCoords[i].u );
597 AI_SWAP2( pcTexCoords[i].v );
598 }
600 for (int i = 0; i<pcHeader->num_tris;++i) {
601 AI_SWAP2( pcTriangles[i].index_xyz[0]);
602 AI_SWAP2( pcTriangles[i].index_xyz[1]);
603 AI_SWAP2( pcTriangles[i].index_xyz[2]);
604 AI_SWAP2( pcTriangles[i].index_uv[0]);
605 AI_SWAP2( pcTriangles[i].index_uv[1]);
606 AI_SWAP2( pcTriangles[i].index_uv[2]);
607 }
609 #endif
611 VALIDATE_FILE_SIZE(szCurrent);
613 // setup materials
614 SetupMaterialProperties_3DGS_MDL5_Quake1();
616 // allocate enough storage to hold all vertices and triangles
617 aiMesh* pcMesh = new aiMesh();
618 pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
620 pcMesh->mNumVertices = pcHeader->num_tris * 3;
621 pcMesh->mNumFaces = pcHeader->num_tris;
622 pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
624 // there won't be more than one mesh inside the file
625 pScene->mRootNode = new aiNode();
626 pScene->mRootNode->mNumMeshes = 1;
627 pScene->mRootNode->mMeshes = new unsigned int[1];
628 pScene->mRootNode->mMeshes[0] = 0;
629 pScene->mNumMeshes = 1;
630 pScene->mMeshes = new aiMesh*[1];
631 pScene->mMeshes[0] = pcMesh;
633 // allocate output storage
634 pcMesh->mNumVertices = (unsigned int)pcHeader->num_tris*3;
635 pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
636 pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
638 if (pcHeader->synctype) {
639 pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
640 pcMesh->mNumUVComponents[0] = 2;
641 }
643 // now get a pointer to the first frame in the file
644 BE_NCONST MDL::Frame* pcFrames = (BE_NCONST MDL::Frame*)szCurrent;
645 AI_SWAP4(pcFrames->type);
647 // byte packed vertices
648 // FIXME: these two snippets below are almost identical ... join them?
649 /////////////////////////////////////////////////////////////////////////////////////
650 if (0 == pcFrames->type || 3 >= this->iGSFileVersion) {
652 const MDL::SimpleFrame* pcFirstFrame = (const MDL::SimpleFrame*)(szCurrent + sizeof(uint32_t));
653 const MDL::Vertex* pcVertices = (const MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name));
655 VALIDATE_FILE_SIZE(pcVertices + pcHeader->num_verts);
657 // now iterate through all triangles
658 unsigned int iCurrent = 0;
659 for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i) {
660 pcMesh->mFaces[i].mIndices = new unsigned int[3];
661 pcMesh->mFaces[i].mNumIndices = 3;
663 unsigned int iTemp = iCurrent;
664 for (unsigned int c = 0; c < 3;++c,++iCurrent) {
665 // read vertices
666 unsigned int iIndex = pcTriangles->index_xyz[c];
667 if (iIndex >= (unsigned int)pcHeader->num_verts) {
668 iIndex = pcHeader->num_verts-1;
669 DefaultLogger::get()->warn("Index overflow in MDLn vertex list");
670 }
672 aiVector3D& vec = pcMesh->mVertices[iCurrent];
673 vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
674 vec.x += pcHeader->translate[0];
676 vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
677 vec.y += pcHeader->translate[1];
678 // vec.y *= -1.0f;
680 vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
681 vec.z += pcHeader->translate[2];
683 // read the normal vector from the precalculated normal table
684 MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
685 // pcMesh->mNormals[iCurrent].y *= -1.0f;
687 // read texture coordinates
688 if (pcHeader->synctype) {
689 ImportUVCoordinate_3DGS_MDL345(pcMesh->mTextureCoords[0][iCurrent],
690 pcTexCoords,pcTriangles->index_uv[c]);
691 }
692 }
693 pcMesh->mFaces[i].mIndices[0] = iTemp+2;
694 pcMesh->mFaces[i].mIndices[1] = iTemp+1;
695 pcMesh->mFaces[i].mIndices[2] = iTemp+0;
696 pcTriangles++;
697 }
699 }
700 // short packed vertices
701 /////////////////////////////////////////////////////////////////////////////////////
702 else {
703 // now get a pointer to the first frame in the file
704 const MDL::SimpleFrame_MDLn_SP* pcFirstFrame = (const MDL::SimpleFrame_MDLn_SP*) (szCurrent + sizeof(uint32_t));
706 // get a pointer to the vertices
707 const MDL::Vertex_MDL4* pcVertices = (const MDL::Vertex_MDL4*) ((pcFirstFrame->name) +
708 sizeof(pcFirstFrame->name));
710 VALIDATE_FILE_SIZE(pcVertices + pcHeader->num_verts);
712 // now iterate through all triangles
713 unsigned int iCurrent = 0;
714 for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i) {
715 pcMesh->mFaces[i].mIndices = new unsigned int[3];
716 pcMesh->mFaces[i].mNumIndices = 3;
718 unsigned int iTemp = iCurrent;
719 for (unsigned int c = 0; c < 3;++c,++iCurrent) {
720 // read vertices
721 unsigned int iIndex = pcTriangles->index_xyz[c];
722 if (iIndex >= (unsigned int)pcHeader->num_verts) {
723 iIndex = pcHeader->num_verts-1;
724 DefaultLogger::get()->warn("Index overflow in MDLn vertex list");
725 }
727 aiVector3D& vec = pcMesh->mVertices[iCurrent];
728 vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
729 vec.x += pcHeader->translate[0];
731 vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
732 vec.y += pcHeader->translate[1];
733 // vec.y *= -1.0f;
735 vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
736 vec.z += pcHeader->translate[2];
738 // read the normal vector from the precalculated normal table
739 MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
740 // pcMesh->mNormals[iCurrent].y *= -1.0f;
742 // read texture coordinates
743 if (pcHeader->synctype) {
744 ImportUVCoordinate_3DGS_MDL345(pcMesh->mTextureCoords[0][iCurrent],
745 pcTexCoords,pcTriangles->index_uv[c]);
746 }
747 }
748 pcMesh->mFaces[i].mIndices[0] = iTemp+2;
749 pcMesh->mFaces[i].mIndices[1] = iTemp+1;
750 pcMesh->mFaces[i].mIndices[2] = iTemp+0;
751 pcTriangles++;
752 }
753 }
755 // For MDL5 we will need to build valid texture coordinates
756 // basing upon the file loaded (only support one file as skin)
757 if (0x5 == iGSFileVersion)
758 CalculateUVCoordinates_MDL5();
759 return;
760 }
762 // ------------------------------------------------------------------------------------------------
763 // Get a single UV coordinate for Quake and older GameStudio files
764 void MDLImporter::ImportUVCoordinate_3DGS_MDL345(
765 aiVector3D& vOut,
766 const MDL::TexCoord_MDL3* pcSrc,
767 unsigned int iIndex)
768 {
769 ai_assert(NULL != pcSrc);
770 const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
772 // validate UV indices
773 if (iIndex >= (unsigned int) pcHeader->synctype) {
774 iIndex = pcHeader->synctype-1;
775 DefaultLogger::get()->warn("Index overflow in MDLn UV coord list");
776 }
778 float s = (float)pcSrc[iIndex].u;
779 float t = (float)pcSrc[iIndex].v;
781 // Scale s and t to range from 0.0 to 1.0
782 if (0x5 != iGSFileVersion) {
783 s = (s + 0.5f) / pcHeader->skinwidth;
784 t = 1.0f-(t + 0.5f) / pcHeader->skinheight;
785 }
787 vOut.x = s;
788 vOut.y = t;
789 vOut.z = 0.0f;
790 }
792 // ------------------------------------------------------------------------------------------------
793 // Compute UV coordinates for a MDL5 file
794 void MDLImporter::CalculateUVCoordinates_MDL5()
795 {
796 const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
797 if (pcHeader->num_skins && this->pScene->mNumTextures) {
798 const aiTexture* pcTex = this->pScene->mTextures[0];
800 // if the file is loaded in DDS format: get the size of the
801 // texture from the header of the DDS file
802 // skip three DWORDs and read first height, then the width
803 unsigned int iWidth, iHeight;
804 if (!pcTex->mHeight) {
805 const uint32_t* piPtr = (uint32_t*)pcTex->pcData;
807 piPtr += 3;
808 iHeight = (unsigned int)*piPtr++;
809 iWidth = (unsigned int)*piPtr;
810 if (!iHeight || !iWidth)
811 {
812 DefaultLogger::get()->warn("Either the width or the height of the "
813 "embedded DDS texture is zero. Unable to compute final texture "
814 "coordinates. The texture coordinates remain in their original "
815 "0-x/0-y (x,y = texture size) range.");
816 iWidth = 1;
817 iHeight = 1;
818 }
819 }
820 else {
821 iWidth = pcTex->mWidth;
822 iHeight = pcTex->mHeight;
823 }
825 if (1 != iWidth || 1 != iHeight) {
826 const float fWidth = (float)iWidth;
827 const float fHeight = (float)iHeight;
828 aiMesh* pcMesh = this->pScene->mMeshes[0];
829 for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
830 {
831 pcMesh->mTextureCoords[0][i].x /= fWidth;
832 pcMesh->mTextureCoords[0][i].y /= fHeight;
833 pcMesh->mTextureCoords[0][i].y = 1.0f - pcMesh->mTextureCoords[0][i].y; // DX to OGL
834 }
835 }
836 }
837 }
839 // ------------------------------------------------------------------------------------------------
840 // Validate the header of a MDL7 file
841 void MDLImporter::ValidateHeader_3DGS_MDL7(const MDL::Header_MDL7* pcHeader)
842 {
843 ai_assert(NULL != pcHeader);
845 // There are some fixed sizes ...
846 if (sizeof(MDL::ColorValue_MDL7) != pcHeader->colorvalue_stc_size) {
847 throw DeadlyImportError(
848 "[3DGS MDL7] sizeof(MDL::ColorValue_MDL7) != pcHeader->colorvalue_stc_size");
849 }
850 if (sizeof(MDL::TexCoord_MDL7) != pcHeader->skinpoint_stc_size) {
851 throw DeadlyImportError(
852 "[3DGS MDL7] sizeof(MDL::TexCoord_MDL7) != pcHeader->skinpoint_stc_size");
853 }
854 if (sizeof(MDL::Skin_MDL7) != pcHeader->skin_stc_size) {
855 throw DeadlyImportError(
856 "sizeof(MDL::Skin_MDL7) != pcHeader->skin_stc_size");
857 }
859 // if there are no groups ... how should we load such a file?
860 if(!pcHeader->groups_num) {
861 throw DeadlyImportError( "[3DGS MDL7] No frames found");
862 }
863 }
865 // ------------------------------------------------------------------------------------------------
866 // resolve bone animation matrices
867 void MDLImporter::CalcAbsBoneMatrices_3DGS_MDL7(MDL::IntBone_MDL7** apcOutBones)
868 {
869 const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
870 const MDL::Bone_MDL7* pcBones = (const MDL::Bone_MDL7*)(pcHeader+1);
871 ai_assert(NULL != apcOutBones);
873 // first find the bone that has NO parent, calculate the
874 // animation matrix for it, then go on and search for the next parent
875 // index (0) and so on until we can't find a new node.
876 uint16_t iParent = 0xffff;
877 uint32_t iIterations = 0;
878 while (iIterations++ < pcHeader->bones_num) {
879 for (uint32_t iBone = 0; iBone < pcHeader->bones_num;++iBone) {
880 BE_NCONST MDL::Bone_MDL7* pcBone = _AI_MDL7_ACCESS_PTR(pcBones,iBone,
881 pcHeader->bone_stc_size,MDL::Bone_MDL7);
883 AI_SWAP2(pcBone->parent_index);
884 AI_SWAP4(pcBone->x);
885 AI_SWAP4(pcBone->y);
886 AI_SWAP4(pcBone->z);
888 if (iParent == pcBone->parent_index) {
889 // MDL7 readme
890 ////////////////////////////////////////////////////////////////
891 /*
892 The animation matrix is then calculated the following way:
894 vector3 bPos = <absolute bone position>
895 matrix44 laM; // local animation matrix
896 sphrvector key_rotate = <bone rotation>
898 matrix44 m1,m2;
899 create_trans_matrix(m1, -bPos.x, -bPos.y, -bPos.z);
900 create_trans_matrix(m2, -bPos.x, -bPos.y, -bPos.z);
902 create_rotation_matrix(laM,key_rotate);
904 laM = sm1 * laM;
905 laM = laM * sm2;
906 */
907 /////////////////////////////////////////////////////////////////
909 MDL::IntBone_MDL7* const pcOutBone = apcOutBones[iBone];
911 // store the parent index of the bone
912 pcOutBone->iParent = pcBone->parent_index;
913 if (0xffff != iParent) {
914 const MDL::IntBone_MDL7* pcParentBone = apcOutBones[iParent];
915 pcOutBone->mOffsetMatrix.a4 = -pcParentBone->vPosition.x;
916 pcOutBone->mOffsetMatrix.b4 = -pcParentBone->vPosition.y;
917 pcOutBone->mOffsetMatrix.c4 = -pcParentBone->vPosition.z;
918 }
919 pcOutBone->vPosition.x = pcBone->x;
920 pcOutBone->vPosition.y = pcBone->y;
921 pcOutBone->vPosition.z = pcBone->z;
922 pcOutBone->mOffsetMatrix.a4 -= pcBone->x;
923 pcOutBone->mOffsetMatrix.b4 -= pcBone->y;
924 pcOutBone->mOffsetMatrix.c4 -= pcBone->z;
926 if (AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE == pcHeader->bone_stc_size) {
927 // no real name for our poor bone is specified :-(
928 pcOutBone->mName.length = ::sprintf(pcOutBone->mName.data,
929 "UnnamedBone_%i",iBone);
930 }
931 else {
932 // Make sure we won't run over the buffer's end if there is no
933 // terminal 0 character (however the documentation says there
934 // should be one)
935 uint32_t iMaxLen = pcHeader->bone_stc_size-16;
936 for (uint32_t qq = 0; qq < iMaxLen;++qq) {
937 if (!pcBone->name[qq]) {
938 iMaxLen = qq;
939 break;
940 }
941 }
943 // store the name of the bone
944 pcOutBone->mName.length = (size_t)iMaxLen;
945 ::memcpy(pcOutBone->mName.data,pcBone->name,pcOutBone->mName.length);
946 pcOutBone->mName.data[pcOutBone->mName.length] = '\0';
947 }
948 }
949 }
950 ++iParent;
951 }
952 }
954 // ------------------------------------------------------------------------------------------------
955 // read bones from a MDL7 file
956 MDL::IntBone_MDL7** MDLImporter::LoadBones_3DGS_MDL7()
957 {
958 const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
959 if (pcHeader->bones_num) {
960 // validate the size of the bone data structure in the file
961 if (AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS != pcHeader->bone_stc_size &&
962 AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_32_CHARS != pcHeader->bone_stc_size &&
963 AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE != pcHeader->bone_stc_size)
964 {
965 DefaultLogger::get()->warn("Unknown size of bone data structure");
966 return NULL;
967 }
969 MDL::IntBone_MDL7** apcBonesOut = new MDL::IntBone_MDL7*[pcHeader->bones_num];
970 for (uint32_t crank = 0; crank < pcHeader->bones_num;++crank)
971 apcBonesOut[crank] = new MDL::IntBone_MDL7();
973 // and calculate absolute bone offset matrices ...
974 CalcAbsBoneMatrices_3DGS_MDL7(apcBonesOut);
975 return apcBonesOut;
976 }
977 return NULL;
978 }
980 // ------------------------------------------------------------------------------------------------
981 // read faces from a MDL7 file
982 void MDLImporter::ReadFaces_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
983 MDL::IntGroupData_MDL7& groupData)
984 {
985 const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
986 MDL::Triangle_MDL7* pcGroupTris = groupInfo.pcGroupTris;
988 // iterate through all triangles and build valid display lists
989 unsigned int iOutIndex = 0;
990 for (unsigned int iTriangle = 0; iTriangle < (unsigned int)groupInfo.pcGroup->numtris; ++iTriangle) {
991 AI_SWAP2(pcGroupTris->v_index[0]);
992 AI_SWAP2(pcGroupTris->v_index[1]);
993 AI_SWAP2(pcGroupTris->v_index[2]);
995 // iterate through all indices of the current triangle
996 for (unsigned int c = 0; c < 3;++c,++iOutIndex) {
998 // validate the vertex index
999 unsigned int iIndex = pcGroupTris->v_index[c];
1000 if(iIndex > (unsigned int)groupInfo.pcGroup->numverts) {
1001 // (we might need to read this section a second time - to process frame vertices correctly)
1002 pcGroupTris->v_index[c] = iIndex = groupInfo.pcGroup->numverts-1;
1003 DefaultLogger::get()->warn("Index overflow in MDL7 vertex list");
1004 }
1006 // write the output face index
1007 groupData.pcFaces[iTriangle].mIndices[2-c] = iOutIndex;
1009 aiVector3D& vPosition = groupData.vPositions[ iOutIndex ];
1010 vPosition.x = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex, pcHeader->mainvertex_stc_size) .x;
1011 vPosition.y = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .y;
1012 vPosition.z = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .z;
1014 // if we have bones, save the index
1015 if (!groupData.aiBones.empty()) {
1016 groupData.aiBones[iOutIndex] = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,
1017 iIndex,pcHeader->mainvertex_stc_size).vertindex;
1018 }
1020 // now read the normal vector
1021 if (AI_MDL7_FRAMEVERTEX030305_STCSIZE <= pcHeader->mainvertex_stc_size) {
1022 // read the full normal vector
1023 aiVector3D& vNormal = groupData.vNormals[ iOutIndex ];
1024 vNormal.x = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[0];
1025 AI_SWAP4(vNormal.x);
1026 vNormal.y = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[1];
1027 AI_SWAP4(vNormal.y);
1028 vNormal.z = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[2];
1029 AI_SWAP4(vNormal.z);
1030 }
1031 else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size) {
1032 // read the normal vector from Quake2's smart table
1033 aiVector3D& vNormal = groupData.vNormals[ iOutIndex ];
1034 MD2::LookupNormalIndex(_AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,
1035 pcHeader->mainvertex_stc_size) .norm162index,vNormal);
1036 }
1037 // validate and process the first uv coordinate set
1038 if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV) {
1040 if (groupInfo.pcGroup->num_stpts) {
1041 AI_SWAP2(pcGroupTris->skinsets[0].st_index[0]);
1042 AI_SWAP2(pcGroupTris->skinsets[0].st_index[1]);
1043 AI_SWAP2(pcGroupTris->skinsets[0].st_index[2]);
1045 iIndex = pcGroupTris->skinsets[0].st_index[c];
1046 if(iIndex > (unsigned int)groupInfo.pcGroup->num_stpts) {
1047 iIndex = groupInfo.pcGroup->num_stpts-1;
1048 DefaultLogger::get()->warn("Index overflow in MDL7 UV coordinate list (#1)");
1049 }
1051 float u = groupInfo.pcGroupUVs[iIndex].u;
1052 float v = 1.0f-groupInfo.pcGroupUVs[iIndex].v; // DX to OGL
1054 groupData.vTextureCoords1[iOutIndex].x = u;
1055 groupData.vTextureCoords1[iOutIndex].y = v;
1056 }
1057 // assign the material index, but only if it is existing
1058 if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV_WITH_MATINDEX){
1059 AI_SWAP4(pcGroupTris->skinsets[0].material);
1060 groupData.pcFaces[iTriangle].iMatIndex[0] = pcGroupTris->skinsets[0].material;
1061 }
1062 }
1063 // validate and process the second uv coordinate set
1064 if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV) {
1066 if (groupInfo.pcGroup->num_stpts) {
1067 AI_SWAP2(pcGroupTris->skinsets[1].st_index[0]);
1068 AI_SWAP2(pcGroupTris->skinsets[1].st_index[1]);
1069 AI_SWAP2(pcGroupTris->skinsets[1].st_index[2]);
1070 AI_SWAP4(pcGroupTris->skinsets[1].material);
1072 iIndex = pcGroupTris->skinsets[1].st_index[c];
1073 if(iIndex > (unsigned int)groupInfo.pcGroup->num_stpts) {
1074 iIndex = groupInfo.pcGroup->num_stpts-1;
1075 DefaultLogger::get()->warn("Index overflow in MDL7 UV coordinate list (#2)");
1076 }
1078 float u = groupInfo.pcGroupUVs[ iIndex ].u;
1079 float v = 1.0f-groupInfo.pcGroupUVs[ iIndex ].v;
1081 groupData.vTextureCoords2[ iOutIndex ].x = u;
1082 groupData.vTextureCoords2[ iOutIndex ].y = v; // DX to OGL
1084 // check whether we do really need the second texture
1085 // coordinate set ... wastes memory and loading time
1086 if (0 != iIndex && (u != groupData.vTextureCoords1[ iOutIndex ].x ||
1087 v != groupData.vTextureCoords1[ iOutIndex ].y ) )
1088 groupData.bNeed2UV = true;
1090 // if the material differs, we need a second skin, too
1091 if (pcGroupTris->skinsets[ 1 ].material != pcGroupTris->skinsets[ 0 ].material)
1092 groupData.bNeed2UV = true;
1093 }
1094 // assign the material index
1095 groupData.pcFaces[ iTriangle ].iMatIndex[ 1 ] = pcGroupTris->skinsets[ 1 ].material;
1096 }
1097 }
1098 // get the next triangle in the list
1099 pcGroupTris = (MDL::Triangle_MDL7*)((const char*)pcGroupTris + pcHeader->triangle_stc_size);
1100 }
1101 }
1103 // ------------------------------------------------------------------------------------------------
1104 // handle frames in a MDL7 file
1105 bool MDLImporter::ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
1106 MDL::IntGroupData_MDL7& groupData,
1107 MDL::IntSharedData_MDL7& shared,
1108 const unsigned char* szCurrent,
1109 const unsigned char** szCurrentOut)
1110 {
1111 ai_assert(NULL != szCurrent && NULL != szCurrentOut);
1112 const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)mBuffer;
1114 // if we have no bones we can simply skip all frames,
1115 // otherwise we'll need to process them.
1116 // FIX: If we need another frame than the first we must apply frame vertex replacements ...
1117 for(unsigned int iFrame = 0; iFrame < (unsigned int)groupInfo.pcGroup->numframes;++iFrame) {
1118 MDL::IntFrameInfo_MDL7 frame ((BE_NCONST MDL::Frame_MDL7*)szCurrent,iFrame);
1120 AI_SWAP4(frame.pcFrame->vertices_count);
1121 AI_SWAP4(frame.pcFrame->transmatrix_count);
1123 const unsigned int iAdd = pcHeader->frame_stc_size +
1124 frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size +
1125 frame.pcFrame->transmatrix_count * pcHeader->bonetrans_stc_size;
1127 if (((const char*)szCurrent - (const char*)pcHeader) + iAdd > (unsigned int)pcHeader->data_size) {
1128 DefaultLogger::get()->warn("Index overflow in frame area. "
1129 "Ignoring all frames and all further mesh groups, too.");
1131 // don't parse more groups if we can't even read one
1132 // FIXME: sometimes this seems to occur even for valid files ...
1133 *szCurrentOut = szCurrent;
1134 return false;
1135 }
1136 // our output frame?
1137 if (configFrameID == iFrame) {
1138 BE_NCONST MDL::Vertex_MDL7* pcFrameVertices = (BE_NCONST MDL::Vertex_MDL7*)(szCurrent+pcHeader->frame_stc_size);
1140 for (unsigned int qq = 0; qq < frame.pcFrame->vertices_count;++qq) {
1141 // I assume this are simple replacements for normal vertices, the bone index serving
1142 // as the index of the vertex to be replaced.
1143 uint16_t iIndex = _AI_MDL7_ACCESS(pcFrameVertices,qq,pcHeader->framevertex_stc_size,MDL::Vertex_MDL7).vertindex;
1144 AI_SWAP2(iIndex);
1145 if (iIndex >= groupInfo.pcGroup->numverts) {
1146 DefaultLogger::get()->warn("Invalid vertex index in frame vertex section");
1147 continue;
1148 }
1150 aiVector3D vPosition,vNormal;
1152 vPosition.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .x;
1153 AI_SWAP4(vPosition.x);
1154 vPosition.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .y;
1155 AI_SWAP4(vPosition.y);
1156 vPosition.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .z;
1157 AI_SWAP4(vPosition.z);
1159 // now read the normal vector
1160 if (AI_MDL7_FRAMEVERTEX030305_STCSIZE <= pcHeader->mainvertex_stc_size) {
1161 // read the full normal vector
1162 vNormal.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[0];
1163 AI_SWAP4(vNormal.x);
1164 vNormal.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[1];
1165 AI_SWAP4(vNormal.y);
1166 vNormal.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[2];
1167 AI_SWAP4(vNormal.z);
1168 }
1169 else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size) {
1170 // read the normal vector from Quake2's smart table
1171 MD2::LookupNormalIndex(_AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,
1172 pcHeader->framevertex_stc_size) .norm162index,vNormal);
1173 }
1175 // FIXME: O(n^2) at the moment ...
1176 BE_NCONST MDL::Triangle_MDL7* pcGroupTris = groupInfo.pcGroupTris;
1177 unsigned int iOutIndex = 0;
1178 for (unsigned int iTriangle = 0; iTriangle < (unsigned int)groupInfo.pcGroup->numtris; ++iTriangle) {
1179 // iterate through all indices of the current triangle
1180 for (unsigned int c = 0; c < 3;++c,++iOutIndex) {
1181 // replace the vertex with the new data
1182 const unsigned int iCurIndex = pcGroupTris->v_index[c];
1183 if (iCurIndex == iIndex) {
1184 groupData.vPositions[iOutIndex] = vPosition;
1185 groupData.vNormals[iOutIndex] = vNormal;
1186 }
1187 }
1188 // get the next triangle in the list
1189 pcGroupTris = (BE_NCONST MDL::Triangle_MDL7*)((const char*)
1190 pcGroupTris + pcHeader->triangle_stc_size);
1191 }
1192 }
1193 }
1194 // parse bone trafo matrix keys (only if there are bones ...)
1195 if (shared.apcOutBones) {
1196 ParseBoneTrafoKeys_3DGS_MDL7(groupInfo,frame,shared);
1197 }
1198 szCurrent += iAdd;
1199 }
1200 *szCurrentOut = szCurrent;
1201 return true;
1202 }
1204 // ------------------------------------------------------------------------------------------------
1205 // Sort faces by material, handle multiple UVs correctly
1206 void MDLImporter::SortByMaterials_3DGS_MDL7(
1207 const MDL::IntGroupInfo_MDL7& groupInfo,
1208 MDL::IntGroupData_MDL7& groupData,
1209 MDL::IntSplitGroupData_MDL7& splitGroupData)
1210 {
1211 const unsigned int iNumMaterials = (unsigned int)splitGroupData.shared.pcMats.size();
1212 if (!groupData.bNeed2UV) {
1213 // if we don't need a second set of texture coordinates there is no reason to keep it in memory ...
1214 groupData.vTextureCoords2.clear();
1216 // allocate the array
1217 splitGroupData.aiSplit = new std::vector<unsigned int>*[iNumMaterials];
1219 for (unsigned int m = 0; m < iNumMaterials;++m)
1220 splitGroupData.aiSplit[m] = new std::vector<unsigned int>();
1222 // iterate through all faces and sort by material
1223 for (unsigned int iFace = 0; iFace < (unsigned int)groupInfo.pcGroup->numtris;++iFace) {
1224 // check range
1225 if (groupData.pcFaces[iFace].iMatIndex[0] >= iNumMaterials) {
1226 // use the last material instead
1227 splitGroupData.aiSplit[iNumMaterials-1]->push_back(iFace);
1229 // sometimes MED writes -1, but normally only if there is only
1230 // one skin assigned. No warning in this case
1231 if(0xFFFFFFFF != groupData.pcFaces[iFace].iMatIndex[0])
1232 DefaultLogger::get()->warn("Index overflow in MDL7 material list [#0]");
1233 }
1234 else splitGroupData.aiSplit[groupData.pcFaces[iFace].
1235 iMatIndex[0]]->push_back(iFace);
1236 }
1237 }
1238 else
1239 {
1240 // we need to build combined materials for each combination of
1241 std::vector<MDL::IntMaterial_MDL7> avMats;
1242 avMats.reserve(iNumMaterials*2);
1244 // fixme: why on the heap?
1245 std::vector<std::vector<unsigned int>* > aiTempSplit(iNumMaterials*2);
1246 for (unsigned int m = 0; m < iNumMaterials;++m)
1247 aiTempSplit[m] = new std::vector<unsigned int>();
1249 // iterate through all faces and sort by material
1250 for (unsigned int iFace = 0; iFace < (unsigned int)groupInfo.pcGroup->numtris;++iFace) {
1251 // check range
1252 unsigned int iMatIndex = groupData.pcFaces[iFace].iMatIndex[0];
1253 if (iMatIndex >= iNumMaterials) {
1254 // sometimes MED writes -1, but normally only if there is only
1255 // one skin assigned. No warning in this case
1256 if(UINT_MAX != iMatIndex)
1257 DefaultLogger::get()->warn("Index overflow in MDL7 material list [#1]");
1258 iMatIndex = iNumMaterials-1;
1259 }
1260 unsigned int iMatIndex2 = groupData.pcFaces[iFace].iMatIndex[1];
1262 unsigned int iNum = iMatIndex;
1263 if (UINT_MAX != iMatIndex2 && iMatIndex != iMatIndex2) {
1264 if (iMatIndex2 >= iNumMaterials) {
1265 // sometimes MED writes -1, but normally only if there is only
1266 // one skin assigned. No warning in this case
1267 DefaultLogger::get()->warn("Index overflow in MDL7 material list [#2]");
1268 iMatIndex2 = iNumMaterials-1;
1269 }
1271 // do a slow seach in the list ...
1272 iNum = 0;
1273 bool bFound = false;
1274 for (std::vector<MDL::IntMaterial_MDL7>::iterator i = avMats.begin();i != avMats.end();++i,++iNum){
1275 if ((*i).iOldMatIndices[0] == iMatIndex && (*i).iOldMatIndices[1] == iMatIndex2) {
1276 // reuse this material
1277 bFound = true;
1278 break;
1279 }
1280 }
1281 if (!bFound) {
1282 // build a new material ...
1283 MDL::IntMaterial_MDL7 sHelper;
1284 sHelper.pcMat = new aiMaterial();
1285 sHelper.iOldMatIndices[0] = iMatIndex;
1286 sHelper.iOldMatIndices[1] = iMatIndex2;
1287 JoinSkins_3DGS_MDL7(splitGroupData.shared.pcMats[iMatIndex],
1288 splitGroupData.shared.pcMats[iMatIndex2],sHelper.pcMat);
1290 // and add it to the list
1291 avMats.push_back(sHelper);
1292 iNum = (unsigned int)avMats.size()-1;
1293 }
1294 // adjust the size of the file array
1295 if (iNum == aiTempSplit.size()) {
1296 aiTempSplit.push_back(new std::vector<unsigned int>());
1297 }
1298 }
1299 aiTempSplit[iNum]->push_back(iFace);
1300 }
1302 // now add the newly created materials to the old list
1303 if (0 == groupInfo.iIndex) {
1304 splitGroupData.shared.pcMats.resize(avMats.size());
1305 for (unsigned int o = 0; o < avMats.size();++o)
1306 splitGroupData.shared.pcMats[o] = avMats[o].pcMat;
1307 }
1308 else {
1309 // This might result in redundant materials ...
1310 splitGroupData.shared.pcMats.resize(iNumMaterials + avMats.size());
1311 for (unsigned int o = iNumMaterials; o < avMats.size();++o)
1312 splitGroupData.shared.pcMats[o] = avMats[o].pcMat;
1313 }
1315 // and build the final face-to-material array
1316 splitGroupData.aiSplit = new std::vector<unsigned int>*[aiTempSplit.size()];
1317 for (unsigned int m = 0; m < iNumMaterials;++m)
1318 splitGroupData.aiSplit[m] = aiTempSplit[m];
1319 }
1320 }
1322 // ------------------------------------------------------------------------------------------------
1323 // Read a MDL7 file
1324 void MDLImporter::InternReadFile_3DGS_MDL7( )
1325 {
1326 ai_assert(NULL != pScene);
1328 MDL::IntSharedData_MDL7 sharedData;
1330 // current cursor position in the file
1331 BE_NCONST MDL::Header_MDL7 *pcHeader = (BE_NCONST MDL::Header_MDL7*)this->mBuffer;
1332 const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
1334 AI_SWAP4(pcHeader->version);
1335 AI_SWAP4(pcHeader->bones_num);
1336 AI_SWAP4(pcHeader->groups_num);
1337 AI_SWAP4(pcHeader->data_size);
1338 AI_SWAP4(pcHeader->entlump_size);
1339 AI_SWAP4(pcHeader->medlump_size);
1340 AI_SWAP2(pcHeader->bone_stc_size);
1341 AI_SWAP2(pcHeader->skin_stc_size);
1342 AI_SWAP2(pcHeader->colorvalue_stc_size);
1343 AI_SWAP2(pcHeader->material_stc_size);
1344 AI_SWAP2(pcHeader->skinpoint_stc_size);
1345 AI_SWAP2(pcHeader->triangle_stc_size);
1346 AI_SWAP2(pcHeader->mainvertex_stc_size);
1347 AI_SWAP2(pcHeader->framevertex_stc_size);
1348 AI_SWAP2(pcHeader->bonetrans_stc_size);
1349 AI_SWAP2(pcHeader->frame_stc_size);
1351 // validate the header of the file. There are some structure
1352 // sizes that are expected by the loader to be constant
1353 this->ValidateHeader_3DGS_MDL7(pcHeader);
1355 // load all bones (they are shared by all groups, so
1356 // we'll need to add them to all groups/meshes later)
1357 // apcBonesOut is a list of all bones or NULL if they could not been loaded
1358 szCurrent += pcHeader->bones_num * pcHeader->bone_stc_size;
1359 sharedData.apcOutBones = this->LoadBones_3DGS_MDL7();
1361 // vector to held all created meshes
1362 std::vector<aiMesh*>* avOutList;
1364 // 3 meshes per group - that should be OK for most models
1365 avOutList = new std::vector<aiMesh*>[pcHeader->groups_num];
1366 for (uint32_t i = 0; i < pcHeader->groups_num;++i)
1367 avOutList[i].reserve(3);
1369 // buffer to held the names of all groups in the file
1370 char* aszGroupNameBuffer = new char[AI_MDL7_MAX_GROUPNAMESIZE*pcHeader->groups_num];
1372 // read all groups
1373 for (unsigned int iGroup = 0; iGroup < (unsigned int)pcHeader->groups_num;++iGroup) {
1374 MDL::IntGroupInfo_MDL7 groupInfo((BE_NCONST MDL::Group_MDL7*)szCurrent,iGroup);
1375 szCurrent = (const unsigned char*)(groupInfo.pcGroup+1);
1377 VALIDATE_FILE_SIZE(szCurrent);
1379 AI_SWAP4(groupInfo.pcGroup->groupdata_size);
1380 AI_SWAP4(groupInfo.pcGroup->numskins);
1381 AI_SWAP4(groupInfo.pcGroup->num_stpts);
1382 AI_SWAP4(groupInfo.pcGroup->numtris);
1383 AI_SWAP4(groupInfo.pcGroup->numverts);
1384 AI_SWAP4(groupInfo.pcGroup->numframes);
1386 if (1 != groupInfo.pcGroup->typ) {
1387 // Not a triangle-based mesh
1388 DefaultLogger::get()->warn("[3DGS MDL7] Not a triangle mesh group. Continuing happily");
1389 }
1391 // store the name of the group
1392 const unsigned int ofs = iGroup*AI_MDL7_MAX_GROUPNAMESIZE;
1393 ::memcpy(&aszGroupNameBuffer[ofs],
1394 groupInfo.pcGroup->name,AI_MDL7_MAX_GROUPNAMESIZE);
1396 // make sure '\0' is at the end
1397 aszGroupNameBuffer[ofs+AI_MDL7_MAX_GROUPNAMESIZE-1] = '\0';
1399 // read all skins
1400 sharedData.pcMats.reserve(sharedData.pcMats.size() + groupInfo.pcGroup->numskins);
1401 sharedData.abNeedMaterials.resize(sharedData.abNeedMaterials.size() +
1402 groupInfo.pcGroup->numskins,false);
1404 for (unsigned int iSkin = 0; iSkin < (unsigned int)groupInfo.pcGroup->numskins;++iSkin) {
1405 ParseSkinLump_3DGS_MDL7(szCurrent,&szCurrent,sharedData.pcMats);
1406 }
1407 // if we have absolutely no skin loaded we need to generate a default material
1408 if (sharedData.pcMats.empty()) {
1409 const int iMode = (int)aiShadingMode_Gouraud;
1410 sharedData.pcMats.push_back(new aiMaterial());
1411 aiMaterial* pcHelper = (aiMaterial*)sharedData.pcMats[0];
1412 pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
1414 aiColor3D clr;
1415 clr.b = clr.g = clr.r = 0.6f;
1416 pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
1417 pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
1419 clr.b = clr.g = clr.r = 0.05f;
1420 pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
1422 aiString szName;
1423 szName.Set(AI_DEFAULT_MATERIAL_NAME);
1424 pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
1426 sharedData.abNeedMaterials.resize(1,false);
1427 }
1429 // now get a pointer to all texture coords in the group
1430 groupInfo.pcGroupUVs = (BE_NCONST MDL::TexCoord_MDL7*)szCurrent;
1431 for(int i = 0; i < groupInfo.pcGroup->num_stpts; ++i){
1432 AI_SWAP4(groupInfo.pcGroupUVs[i].u);
1433 AI_SWAP4(groupInfo.pcGroupUVs[i].v);
1434 }
1435 szCurrent += pcHeader->skinpoint_stc_size * groupInfo.pcGroup->num_stpts;
1437 // now get a pointer to all triangle in the group
1438 groupInfo.pcGroupTris = (Triangle_MDL7*)szCurrent;
1439 szCurrent += pcHeader->triangle_stc_size * groupInfo.pcGroup->numtris;
1441 // now get a pointer to all vertices in the group
1442 groupInfo.pcGroupVerts = (BE_NCONST MDL::Vertex_MDL7*)szCurrent;
1443 for(int i = 0; i < groupInfo.pcGroup->numverts; ++i){
1444 AI_SWAP4(groupInfo.pcGroupVerts[i].x);
1445 AI_SWAP4(groupInfo.pcGroupVerts[i].y);
1446 AI_SWAP4(groupInfo.pcGroupVerts[i].z);
1448 AI_SWAP2(groupInfo.pcGroupVerts[i].vertindex);
1449 //We can not swap the normal information now as we don't know which of the two kinds it is
1450 }
1451 szCurrent += pcHeader->mainvertex_stc_size * groupInfo.pcGroup->numverts;
1452 VALIDATE_FILE_SIZE(szCurrent);
1454 MDL::IntSplitGroupData_MDL7 splitGroupData(sharedData,avOutList[iGroup]);
1455 MDL::IntGroupData_MDL7 groupData;
1456 if (groupInfo.pcGroup->numtris && groupInfo.pcGroup->numverts)
1457 {
1458 // build output vectors
1459 const unsigned int iNumVertices = groupInfo.pcGroup->numtris*3;
1460 groupData.vPositions.resize(iNumVertices);
1461 groupData.vNormals.resize(iNumVertices);
1463 if (sharedData.apcOutBones)groupData.aiBones.resize(iNumVertices,UINT_MAX);
1465 // it is also possible that there are 0 UV coordinate sets
1466 if (groupInfo.pcGroup->num_stpts){
1467 groupData.vTextureCoords1.resize(iNumVertices,aiVector3D());
1469 // check whether the triangle data structure is large enough
1470 // to contain a second UV coodinate set
1471 if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV) {
1472 groupData.vTextureCoords2.resize(iNumVertices,aiVector3D());
1473 groupData.bNeed2UV = true;
1474 }
1475 }
1476 groupData.pcFaces = new MDL::IntFace_MDL7[groupInfo.pcGroup->numtris];
1478 // read all faces into the preallocated arrays
1479 ReadFaces_3DGS_MDL7(groupInfo, groupData);
1481 // sort by materials
1482 SortByMaterials_3DGS_MDL7(groupInfo, groupData,
1483 splitGroupData);
1485 for (unsigned int qq = 0; qq < sharedData.pcMats.size();++qq) {
1486 if (!splitGroupData.aiSplit[qq]->empty())
1487 sharedData.abNeedMaterials[qq] = true;
1488 }
1489 }
1490 else DefaultLogger::get()->warn("[3DGS MDL7] Mesh group consists of 0 "
1491 "vertices or faces. It will be skipped.");
1493 // process all frames and generate output meshes
1494 ProcessFrames_3DGS_MDL7(groupInfo,groupData, sharedData,szCurrent,&szCurrent);
1495 GenerateOutputMeshes_3DGS_MDL7(groupData,splitGroupData);
1496 }
1498 // generate a nodegraph and subnodes for each group
1499 pScene->mRootNode = new aiNode();
1501 // now we need to build a final mesh list
1502 for (uint32_t i = 0; i < pcHeader->groups_num;++i)
1503 pScene->mNumMeshes += (unsigned int)avOutList[i].size();
1505 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; {
1506 unsigned int p = 0,q = 0;
1507 for (uint32_t i = 0; i < pcHeader->groups_num;++i) {
1508 for (unsigned int a = 0; a < avOutList[i].size();++a) {
1509 pScene->mMeshes[p++] = avOutList[i][a];
1510 }
1511 if (!avOutList[i].empty())++pScene->mRootNode->mNumChildren;
1512 }
1513 // we will later need an extra node to serve as parent for all bones
1514 if (sharedData.apcOutBones)++pScene->mRootNode->mNumChildren;
1515 this->pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
1516 p = 0;
1517 for (uint32_t i = 0; i < pcHeader->groups_num;++i) {
1518 if (avOutList[i].empty())continue;
1520 aiNode* const pcNode = pScene->mRootNode->mChildren[p] = new aiNode();
1521 pcNode->mNumMeshes = (unsigned int)avOutList[i].size();
1522 pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
1523 pcNode->mParent = this->pScene->mRootNode;
1524 for (unsigned int a = 0; a < pcNode->mNumMeshes;++a)
1525 pcNode->mMeshes[a] = q + a;
1526 q += (unsigned int)avOutList[i].size();
1528 // setup the name of the node
1529 char* const szBuffer = &aszGroupNameBuffer[i*AI_MDL7_MAX_GROUPNAMESIZE];
1530 if ('\0' == *szBuffer)
1531 pcNode->mName.length = ::sprintf(szBuffer,"Group_%i",p);
1532 else pcNode->mName.length = ::strlen(szBuffer);
1533 ::strcpy(pcNode->mName.data,szBuffer);
1534 ++p;
1535 }
1536 }
1538 // if there is only one root node with a single child we can optimize it a bit ...
1539 if (1 == pScene->mRootNode->mNumChildren && !sharedData.apcOutBones) {
1540 aiNode* pcOldRoot = this->pScene->mRootNode;
1541 pScene->mRootNode = pcOldRoot->mChildren[0];
1542 pcOldRoot->mChildren[0] = NULL;
1543 delete pcOldRoot;
1544 pScene->mRootNode->mParent = NULL;
1545 }
1546 else pScene->mRootNode->mName.Set("<mesh_root>");
1548 delete[] avOutList;
1549 delete[] aszGroupNameBuffer;
1550 AI_DEBUG_INVALIDATE_PTR(avOutList);
1551 AI_DEBUG_INVALIDATE_PTR(aszGroupNameBuffer);
1553 // build a final material list.
1554 CopyMaterials_3DGS_MDL7(sharedData);
1555 HandleMaterialReferences_3DGS_MDL7();
1557 // generate output bone animations and add all bones to the scenegraph
1558 if (sharedData.apcOutBones) {
1559 // this step adds empty dummy bones to the nodegraph
1560 // insert another dummy node to avoid name conflicts
1561 aiNode* const pc = pScene->mRootNode->mChildren[pScene->mRootNode->mNumChildren-1] = new aiNode();
1563 pc->mName.Set("<skeleton_root>");
1565 // add bones to the nodegraph
1566 AddBonesToNodeGraph_3DGS_MDL7((const Assimp::MDL::IntBone_MDL7 **)
1567 sharedData.apcOutBones,pc,0xffff);
1569 // this steps build a valid output animation
1570 BuildOutputAnims_3DGS_MDL7((const Assimp::MDL::IntBone_MDL7 **)
1571 sharedData.apcOutBones);
1572 }
1573 }
1575 // ------------------------------------------------------------------------------------------------
1576 // Copy materials
1577 void MDLImporter::CopyMaterials_3DGS_MDL7(MDL::IntSharedData_MDL7 &shared)
1578 {
1579 pScene->mNumMaterials = (unsigned int)shared.pcMats.size();
1580 pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
1581 for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
1582 pScene->mMaterials[i] = shared.pcMats[i];
1583 }
1586 // ------------------------------------------------------------------------------------------------
1587 // Process material references
1588 void MDLImporter::HandleMaterialReferences_3DGS_MDL7()
1589 {
1590 // search for referrer materials
1591 for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
1592 int iIndex = 0;
1593 if (AI_SUCCESS == aiGetMaterialInteger(pScene->mMaterials[i],AI_MDL7_REFERRER_MATERIAL, &iIndex) ) {
1594 for (unsigned int a = 0; a < pScene->mNumMeshes;++a) {
1595 aiMesh* const pcMesh = pScene->mMeshes[a];
1596 if (i == pcMesh->mMaterialIndex) {
1597 pcMesh->mMaterialIndex = iIndex;
1598 }
1599 }
1600 // collapse the rest of the array
1601 delete pScene->mMaterials[i];
1602 for (unsigned int pp = i; pp < pScene->mNumMaterials-1;++pp) {
1604 pScene->mMaterials[pp] = pScene->mMaterials[pp+1];
1605 for (unsigned int a = 0; a < pScene->mNumMeshes;++a) {
1606 aiMesh* const pcMesh = pScene->mMeshes[a];
1607 if (pcMesh->mMaterialIndex > i)--pcMesh->mMaterialIndex;
1608 }
1609 }
1610 --pScene->mNumMaterials;
1611 }
1612 }
1613 }
1615 // ------------------------------------------------------------------------------------------------
1616 // Read bone transformation keys
1617 void MDLImporter::ParseBoneTrafoKeys_3DGS_MDL7(
1618 const MDL::IntGroupInfo_MDL7& groupInfo,
1619 IntFrameInfo_MDL7& frame,
1620 MDL::IntSharedData_MDL7& shared)
1621 {
1622 const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
1624 // only the first group contains bone animation keys
1625 if (frame.pcFrame->transmatrix_count) {
1626 if (!groupInfo.iIndex) {
1627 // skip all frames vertices. We can't support them
1628 const MDL::BoneTransform_MDL7* pcBoneTransforms = (const MDL::BoneTransform_MDL7*)
1629 (((const char*)frame.pcFrame) + pcHeader->frame_stc_size +
1630 frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size);
1632 // read all transformation matrices
1633 for (unsigned int iTrafo = 0; iTrafo < frame.pcFrame->transmatrix_count;++iTrafo) {
1634 if(pcBoneTransforms->bone_index >= pcHeader->bones_num) {
1635 DefaultLogger::get()->warn("Index overflow in frame area. "
1636 "Unable to parse this bone transformation");
1637 }
1638 else {
1639 AddAnimationBoneTrafoKey_3DGS_MDL7(frame.iIndex,
1640 pcBoneTransforms,shared.apcOutBones);
1641 }
1642 pcBoneTransforms = (const MDL::BoneTransform_MDL7*)(
1643 (const char*)pcBoneTransforms + pcHeader->bonetrans_stc_size);
1644 }
1645 }
1646 else {
1647 DefaultLogger::get()->warn("Ignoring animation keyframes in groups != 0");
1648 }
1649 }
1650 }
1652 // ------------------------------------------------------------------------------------------------
1653 // Attach bones to the output nodegraph
1654 void MDLImporter::AddBonesToNodeGraph_3DGS_MDL7(const MDL::IntBone_MDL7** apcBones,
1655 aiNode* pcParent,uint16_t iParentIndex)
1656 {
1657 ai_assert(NULL != apcBones && NULL != pcParent);
1659 // get a pointer to the header ...
1660 const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
1662 const MDL::IntBone_MDL7** apcBones2 = apcBones;
1663 for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
1665 const MDL::IntBone_MDL7* const pcBone = *apcBones2++;
1666 if (pcBone->iParent == iParentIndex) {
1667 ++pcParent->mNumChildren;
1668 }
1669 }
1670 pcParent->mChildren = new aiNode*[pcParent->mNumChildren];
1671 unsigned int qq = 0;
1672 for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
1674 const MDL::IntBone_MDL7* const pcBone = *apcBones++;
1675 if (pcBone->iParent != iParentIndex)continue;
1677 aiNode* pcNode = pcParent->mChildren[qq++] = new aiNode();
1678 pcNode->mName = aiString( pcBone->mName );
1680 AddBonesToNodeGraph_3DGS_MDL7(apcBones,pcNode,(uint16_t)i);
1681 }
1682 }
1684 // ------------------------------------------------------------------------------------------------
1685 // Build output animations
1686 void MDLImporter::BuildOutputAnims_3DGS_MDL7(
1687 const MDL::IntBone_MDL7** apcBonesOut)
1688 {
1689 ai_assert(NULL != apcBonesOut);
1690 const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)mBuffer;
1692 // one animation ...
1693 aiAnimation* pcAnim = new aiAnimation();
1694 for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
1695 if (!apcBonesOut[i]->pkeyPositions.empty()) {
1697 // get the last frame ... (needn't be equal to pcHeader->frames_num)
1698 for (size_t qq = 0; qq < apcBonesOut[i]->pkeyPositions.size();++qq) {
1699 pcAnim->mDuration = std::max(pcAnim->mDuration, (double)
1700 apcBonesOut[i]->pkeyPositions[qq].mTime);
1701 }
1702 ++pcAnim->mNumChannels;
1703 }
1704 }
1705 if (pcAnim->mDuration) {
1706 pcAnim->mChannels = new aiNodeAnim*[pcAnim->mNumChannels];
1708 unsigned int iCnt = 0;
1709 for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
1710 if (!apcBonesOut[i]->pkeyPositions.empty()) {
1711 const MDL::IntBone_MDL7* const intBone = apcBonesOut[i];
1713 aiNodeAnim* const pcNodeAnim = pcAnim->mChannels[iCnt++] = new aiNodeAnim();
1714 pcNodeAnim->mNodeName = aiString( intBone->mName );
1716 // allocate enough storage for all keys
1717 pcNodeAnim->mNumPositionKeys = (unsigned int)intBone->pkeyPositions.size();
1718 pcNodeAnim->mNumScalingKeys = (unsigned int)intBone->pkeyPositions.size();
1719 pcNodeAnim->mNumRotationKeys = (unsigned int)intBone->pkeyPositions.size();
1721 pcNodeAnim->mPositionKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys];
1722 pcNodeAnim->mScalingKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys];
1723 pcNodeAnim->mRotationKeys = new aiQuatKey[pcNodeAnim->mNumPositionKeys];
1725 // copy all keys
1726 for (unsigned int qq = 0; qq < pcNodeAnim->mNumPositionKeys;++qq) {
1727 pcNodeAnim->mPositionKeys[qq] = intBone->pkeyPositions[qq];
1728 pcNodeAnim->mScalingKeys[qq] = intBone->pkeyScalings[qq];
1729 pcNodeAnim->mRotationKeys[qq] = intBone->pkeyRotations[qq];
1730 }
1731 }
1732 }
1734 // store the output animation
1735 pScene->mNumAnimations = 1;
1736 pScene->mAnimations = new aiAnimation*[1];
1737 pScene->mAnimations[0] = pcAnim;
1738 }
1739 else delete pcAnim;
1740 }
1742 // ------------------------------------------------------------------------------------------------
1743 void MDLImporter::AddAnimationBoneTrafoKey_3DGS_MDL7(unsigned int iTrafo,
1744 const MDL::BoneTransform_MDL7* pcBoneTransforms,
1745 MDL::IntBone_MDL7** apcBonesOut)
1746 {
1747 ai_assert(NULL != pcBoneTransforms);
1748 ai_assert(NULL != apcBonesOut);
1750 // first .. get the transformation matrix
1751 aiMatrix4x4 mTransform;
1752 mTransform.a1 = pcBoneTransforms->m[0];
1753 mTransform.b1 = pcBoneTransforms->m[1];
1754 mTransform.c1 = pcBoneTransforms->m[2];
1755 mTransform.d1 = pcBoneTransforms->m[3];
1757 mTransform.a2 = pcBoneTransforms->m[4];
1758 mTransform.b2 = pcBoneTransforms->m[5];
1759 mTransform.c2 = pcBoneTransforms->m[6];
1760 mTransform.d2 = pcBoneTransforms->m[7];
1762 mTransform.a3 = pcBoneTransforms->m[8];
1763 mTransform.b3 = pcBoneTransforms->m[9];
1764 mTransform.c3 = pcBoneTransforms->m[10];
1765 mTransform.d3 = pcBoneTransforms->m[11];
1767 // now decompose the transformation matrix into separate
1768 // scaling, rotation and translation
1769 aiVectorKey vScaling,vPosition;
1770 aiQuatKey qRotation;
1772 // FIXME: Decompose will assert in debug builds if the matrix is invalid ...
1773 mTransform.Decompose(vScaling.mValue,qRotation.mValue,vPosition.mValue);
1775 // now generate keys
1776 vScaling.mTime = qRotation.mTime = vPosition.mTime = (double)iTrafo;
1778 // add the keys to the bone
1779 MDL::IntBone_MDL7* const pcBoneOut = apcBonesOut[pcBoneTransforms->bone_index];
1780 pcBoneOut->pkeyPositions.push_back ( vPosition );
1781 pcBoneOut->pkeyScalings.push_back ( vScaling );
1782 pcBoneOut->pkeyRotations.push_back ( qRotation );
1783 }
1785 // ------------------------------------------------------------------------------------------------
1786 // Construct output meshes
1787 void MDLImporter::GenerateOutputMeshes_3DGS_MDL7(
1788 MDL::IntGroupData_MDL7& groupData,
1789 MDL::IntSplitGroupData_MDL7& splitGroupData)
1790 {
1791 const MDL::IntSharedData_MDL7& shared = splitGroupData.shared;
1793 // get a pointer to the header ...
1794 const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
1795 const unsigned int iNumOutBones = pcHeader->bones_num;
1797 for (std::vector<aiMaterial*>::size_type i = 0; i < shared.pcMats.size();++i) {
1798 if (!splitGroupData.aiSplit[i]->empty()) {
1800 // allocate the output mesh
1801 aiMesh* pcMesh = new aiMesh();
1803 pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
1804 pcMesh->mMaterialIndex = (unsigned int)i;
1806 // allocate output storage
1807 pcMesh->mNumFaces = (unsigned int)splitGroupData.aiSplit[i]->size();
1808 pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
1810 pcMesh->mNumVertices = pcMesh->mNumFaces*3;
1811 pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
1812 pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
1814 if (!groupData.vTextureCoords1.empty()) {
1815 pcMesh->mNumUVComponents[0] = 2;
1816 pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
1817 if (!groupData.vTextureCoords2.empty()) {
1818 pcMesh->mNumUVComponents[1] = 2;
1819 pcMesh->mTextureCoords[1] = new aiVector3D[pcMesh->mNumVertices];
1820 }
1821 }
1823 // iterate through all faces and build an unique set of vertices
1824 unsigned int iCurrent = 0;
1825 for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace) {
1826 pcMesh->mFaces[iFace].mNumIndices = 3;
1827 pcMesh->mFaces[iFace].mIndices = new unsigned int[3];
1829 unsigned int iSrcFace = splitGroupData.aiSplit[i]->operator[](iFace);
1830 const MDL::IntFace_MDL7& oldFace = groupData.pcFaces[iSrcFace];
1832 // iterate through all face indices
1833 for (unsigned int c = 0; c < 3;++c) {
1834 const uint32_t iIndex = oldFace.mIndices[c];
1835 pcMesh->mVertices[iCurrent] = groupData.vPositions[iIndex];
1836 pcMesh->mNormals[iCurrent] = groupData.vNormals[iIndex];
1838 if (!groupData.vTextureCoords1.empty()) {
1840 pcMesh->mTextureCoords[0][iCurrent] = groupData.vTextureCoords1[iIndex];
1841 if (!groupData.vTextureCoords2.empty()) {
1842 pcMesh->mTextureCoords[1][iCurrent] = groupData.vTextureCoords2[iIndex];
1843 }
1844 }
1845 pcMesh->mFaces[iFace].mIndices[c] = iCurrent++;
1846 }
1847 }
1849 // if we have bones in the mesh we'll need to generate
1850 // proper vertex weights for them
1851 if (!groupData.aiBones.empty()) {
1852 std::vector<std::vector<unsigned int> > aaiVWeightList;
1853 aaiVWeightList.resize(iNumOutBones);
1855 int iCurrent = 0;
1856 for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace) {
1857 unsigned int iSrcFace = splitGroupData.aiSplit[i]->operator[](iFace);
1858 const MDL::IntFace_MDL7& oldFace = groupData.pcFaces[iSrcFace];
1860 // iterate through all face indices
1861 for (unsigned int c = 0; c < 3;++c) {
1862 unsigned int iBone = groupData.aiBones[ oldFace.mIndices[c] ];
1863 if (UINT_MAX != iBone) {
1864 if (iBone >= iNumOutBones) {
1865 DefaultLogger::get()->error("Bone index overflow. "
1866 "The bone index of a vertex exceeds the allowed range. ");
1867 iBone = iNumOutBones-1;
1868 }
1869 aaiVWeightList[ iBone ].push_back ( iCurrent );
1870 }
1871 ++iCurrent;
1872 }
1873 }
1874 // now check which bones are required ...
1875 for (std::vector<std::vector<unsigned int> >::const_iterator k = aaiVWeightList.begin();k != aaiVWeightList.end();++k) {
1876 if (!(*k).empty()) {
1877 ++pcMesh->mNumBones;
1878 }
1879 }
1880 pcMesh->mBones = new aiBone*[pcMesh->mNumBones];
1881 iCurrent = 0;
1882 for (std::vector<std::vector<unsigned int> >::const_iterator k = aaiVWeightList.begin();k!= aaiVWeightList.end();++k,++iCurrent)
1883 {
1884 if ((*k).empty())
1885 continue;
1887 // seems we'll need this node
1888 aiBone* pcBone = pcMesh->mBones[ iCurrent ] = new aiBone();
1889 pcBone->mName = aiString(shared.apcOutBones[ iCurrent ]->mName);
1890 pcBone->mOffsetMatrix = shared.apcOutBones[ iCurrent ]->mOffsetMatrix;
1892 // setup vertex weights
1893 pcBone->mNumWeights = (unsigned int)(*k).size();
1894 pcBone->mWeights = new aiVertexWeight[pcBone->mNumWeights];
1896 for (unsigned int weight = 0; weight < pcBone->mNumWeights;++weight) {
1897 pcBone->mWeights[weight].mVertexId = (*k)[weight];
1898 pcBone->mWeights[weight].mWeight = 1.0f;
1899 }
1900 }
1901 }
1902 // add the mesh to the list of output meshes
1903 splitGroupData.avOutList.push_back(pcMesh);
1904 }
1905 }
1906 }
1908 // ------------------------------------------------------------------------------------------------
1909 // Join to materials
1910 void MDLImporter::JoinSkins_3DGS_MDL7(
1911 aiMaterial* pcMat1,
1912 aiMaterial* pcMat2,
1913 aiMaterial* pcMatOut)
1914 {
1915 ai_assert(NULL != pcMat1 && NULL != pcMat2 && NULL != pcMatOut);
1917 // first create a full copy of the first skin property set
1918 // and assign it to the output material
1919 aiMaterial::CopyPropertyList(pcMatOut,pcMat1);
1921 int iVal = 0;
1922 pcMatOut->AddProperty<int>(&iVal,1,AI_MATKEY_UVWSRC_DIFFUSE(0));
1924 // then extract the diffuse texture from the second skin,
1925 // setup 1 as UV source and we have it
1926 aiString sString;
1927 if(AI_SUCCESS == aiGetMaterialString ( pcMat2, AI_MATKEY_TEXTURE_DIFFUSE(0),&sString )) {
1928 iVal = 1;
1929 pcMatOut->AddProperty<int>(&iVal,1,AI_MATKEY_UVWSRC_DIFFUSE(1));
1930 pcMatOut->AddProperty(&sString,AI_MATKEY_TEXTURE_DIFFUSE(1));
1931 }
1932 }
1934 // ------------------------------------------------------------------------------------------------
1935 // Read a half-life 2 MDL
1936 void MDLImporter::InternReadFile_HL2( )
1937 {
1938 //const MDL::Header_HL2* pcHeader = (const MDL::Header_HL2*)this->mBuffer;
1939 throw DeadlyImportError("HL2 MDLs are not implemented");
1940 }
1942 #endif // !! ASSIMP_BUILD_NO_MDL_IMPORTER