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