1/*
2Open Asset Import Library (assimp)
3---------------------------------------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39---------------------------------------------------------------------------------------------------
40*/
41
42#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
43
44#include "Q3BSPFileImporter.h"
45#include "Q3BSPZipArchive.h"
46#include "Q3BSPFileParser.h"
47#include "Q3BSPFileData.h"
48
49#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
50# include <zlib.h>
51#else
52# include "../contrib/zlib/zlib.h"
53#endif
54
55#include <assimp/types.h>
56#include <assimp/mesh.h>
57#include <assimp/scene.h>
58#include <assimp/ai_assert.h>
59#include <assimp/DefaultIOSystem.h>
60#include <assimp/importerdesc.h>
61#include <vector>
62#include <sstream>
63#include "StringComparison.h"
64
65static const aiImporterDesc desc = {
66 "Quake III BSP Importer",
67 "",
68 "",
69 "",
70 aiImporterFlags_SupportBinaryFlavour,
71 0,
72 0,
73 0,
74 0,
75 "pk3"
76};
77
78namespace Assimp {
79
80using namespace Q3BSP;
81
82// ------------------------------------------------------------------------------------------------
83// Local function to create a material key name.
84static void createKey( int id1, int id2, std::string &rKey )
85{
86 std::ostringstream str;
87 str << id1 << "." << id2;
88 rKey = str.str();
89}
90
91// ------------------------------------------------------------------------------------------------
92// Local function to extract the texture ids from a material key-name.
93static void extractIds( const std::string &rKey, int &rId1, int &rId2 )
94{
95 rId1 = -1;
96 rId2 = -1;
97 if ( rKey.empty() )
98 return;
99
100 std::string::size_type pos = rKey.find( "." );
101 if ( std::string::npos == pos )
102 return;
103
104 std::string tmp1 = rKey.substr( 0, pos );
105 std::string tmp2 = rKey.substr( pos + 1, rKey.size() - pos - 1 );
106 rId1 = atoi( tmp1.c_str() );
107 rId2 = atoi( tmp2.c_str() );
108}
109
110// ------------------------------------------------------------------------------------------------
111// Local helper function to normalize filenames.
112static void normalizePathName( const std::string &rPath, std::string &rNormalizedPath )
113{
114 rNormalizedPath = "";
115 if ( rPath.empty() )
116 return;
117
118#ifdef _WIN32
119 std::string sep = "\\";
120#else
121 std::string sep = "/";
122#endif
123
124 static const unsigned int numDelimiters = 2;
125 const char delimiters[ numDelimiters ] = { '/', '\\' };
126 rNormalizedPath = rPath;
127 for (const char delimiter : delimiters)
128 {
129 for ( size_t j=0; j<rNormalizedPath.size(); j++ )
130 {
131 if ( rNormalizedPath[j] == delimiter )
132 {
133 rNormalizedPath[ j ] = sep[ 0 ];
134 }
135 }
136 }
137}
138
139// ------------------------------------------------------------------------------------------------
140// Constructor.
141Q3BSPFileImporter::Q3BSPFileImporter() :
142 m_pCurrentMesh( NULL ),
143 m_pCurrentFace( NULL ),
144 m_MaterialLookupMap(),
145 mTextures()
146{
147 // empty
148}
149
150// ------------------------------------------------------------------------------------------------
151// Destructor.
152Q3BSPFileImporter::~Q3BSPFileImporter() {
153 m_pCurrentMesh = NULL;
154 m_pCurrentFace = NULL;
155
156 // Clear face-to-material map
157 for ( FaceMap::iterator it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end(); ++it ) {
158 const std::string &matName = it->first;
159 if ( !matName.empty() ) {
160 delete it->second;
161 }
162 }
163 m_MaterialLookupMap.clear();
164}
165
166// ------------------------------------------------------------------------------------------------
167// Returns true, if the loader can read this.
168bool Q3BSPFileImporter::CanRead( const std::string& rFile, IOSystem* /*pIOHandler*/, bool checkSig ) const
169{
170 if(!checkSig) {
171 return SimpleExtensionCheck( rFile, "pk3", "bsp" );
172 }
173 // TODO perhaps add keyword based detection
174 return false;
175}
176
177// ------------------------------------------------------------------------------------------------
178// Adds extensions.
179const aiImporterDesc* Q3BSPFileImporter::GetInfo () const
180{
181 return &desc;
182}
183
184// ------------------------------------------------------------------------------------------------
185// Import method.
186void Q3BSPFileImporter::InternReadFile(const std::string &rFile, aiScene* pScene, IOSystem* pIOHandler)
187{
188 Q3BSPZipArchive Archive( pIOHandler, rFile );
189 if ( !Archive.isOpen() )
190 {
191 throw DeadlyImportError( "Failed to open file " + rFile + "." );
192 }
193
194 std::string archiveName( "" ), mapName( "" );
195 separateMapName( rFile, archiveName, mapName );
196
197 if ( mapName.empty() )
198 {
199 if ( !findFirstMapInArchive( Archive, mapName ) )
200 {
201 return;
202 }
203 }
204
205 Q3BSPFileParser fileParser( mapName, &Archive );
206 Q3BSPModel *pBSPModel = fileParser.getModel();
207 if ( NULL != pBSPModel )
208 {
209 CreateDataFromImport( pBSPModel, pScene, &Archive );
210 }
211}
212
213// ------------------------------------------------------------------------------------------------
214// Separates the map name from the import name.
215void Q3BSPFileImporter::separateMapName( const std::string &rImportName, std::string &rArchiveName,
216 std::string &rMapName )
217{
218 rArchiveName = "";
219 rMapName = "";
220 if ( rImportName.empty() )
221 return;
222
223 std::string::size_type pos = rImportName.rfind( "," );
224 if ( std::string::npos == pos )
225 {
226 rArchiveName = rImportName;
227 return;
228 }
229
230 rArchiveName = rImportName.substr( 0, pos );
231 rMapName = rImportName.substr( pos, rImportName.size() - pos - 1 );
232}
233
234// ------------------------------------------------------------------------------------------------
235// Returns the first map in the map archive.
236bool Q3BSPFileImporter::findFirstMapInArchive( Q3BSPZipArchive &rArchive, std::string &rMapName )
237{
238 rMapName = "";
239 std::vector<std::string> fileList;
240 rArchive.getFileList( fileList );
241 if ( fileList.empty() )
242 return false;
243
244 for ( std::vector<std::string>::iterator it = fileList.begin(); it != fileList.end();
245 ++it )
246 {
247 std::string::size_type pos = (*it).find( "maps/" );
248 if ( std::string::npos != pos )
249 {
250 std::string::size_type extPos = (*it).find( ".bsp" );
251 if ( std::string::npos != extPos )
252 {
253 rMapName = *it;
254 return true;
255 }
256 }
257 }
258
259 return false;
260}
261
262// ------------------------------------------------------------------------------------------------
263// Creates the assimp specific data.
264void Q3BSPFileImporter::CreateDataFromImport( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
265 Q3BSPZipArchive *pArchive )
266{
267 if ( NULL == pModel || NULL == pScene )
268 return;
269
270 pScene->mRootNode = new aiNode;
271 if ( !pModel->m_ModelName.empty() )
272 {
273 pScene->mRootNode->mName.Set( pModel->m_ModelName );
274 }
275
276 // Create the face to material relation map
277 createMaterialMap( pModel );
278
279 // Create all nodes
280 CreateNodes( pModel, pScene, pScene->mRootNode );
281
282 // Create the assigned materials
283 createMaterials( pModel, pScene, pArchive );
284}
285
286// ------------------------------------------------------------------------------------------------
287// Creates all assimp nodes.
288void Q3BSPFileImporter::CreateNodes( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
289 aiNode *pParent )
290{
291 ai_assert( NULL != pModel );
292 if ( NULL == pModel )
293 {
294 return;
295 }
296
297 unsigned int matIdx = 0;
298 std::vector<aiMesh*> MeshArray;
299 std::vector<aiNode*> NodeArray;
300 for ( FaceMapIt it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end(); ++it )
301 {
302 std::vector<Q3BSP::sQ3BSPFace*> *pArray = (*it).second;
303 size_t numVerts = countData( *pArray );
304 if ( 0 != numVerts )
305 {
306 aiMesh* pMesh = new aiMesh;
307 aiNode *pNode = CreateTopology( pModel, matIdx, *pArray, pMesh );
308 if ( NULL != pNode )
309 {
310 NodeArray.push_back( pNode );
311 MeshArray.push_back( pMesh );
312 }
313 else
314 {
315 delete pMesh;
316 }
317 }
318 matIdx++;
319 }
320
321 pScene->mNumMeshes = static_cast<unsigned int>( MeshArray.size() );
322 if ( pScene->mNumMeshes > 0 )
323 {
324 pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ];
325 for ( size_t i = 0; i < MeshArray.size(); i++ )
326 {
327 aiMesh *pMesh = MeshArray[ i ];
328 if ( NULL != pMesh )
329 {
330 pScene->mMeshes[ i ] = pMesh;
331 }
332 }
333 }
334
335 pParent->mNumChildren = static_cast<unsigned int>(MeshArray.size());
336 pParent->mChildren = new aiNode*[ pScene->mRootNode->mNumChildren ];
337 for ( size_t i=0; i<NodeArray.size(); i++ )
338 {
339 aiNode *pNode = NodeArray[ i ];
340 pNode->mParent = pParent;
341 pParent->mChildren[ i ] = pNode;
342 pParent->mChildren[ i ]->mMeshes[ 0 ] = static_cast<unsigned int>(i);
343 }
344}
345
346// ------------------------------------------------------------------------------------------------
347// Creates the topology.
348aiNode *Q3BSPFileImporter::CreateTopology( const Q3BSP::Q3BSPModel *pModel,
349 unsigned int materialIdx,
350 std::vector<sQ3BSPFace*> &rArray,
351 aiMesh* pMesh )
352{
353 size_t numVerts = countData( rArray );
354 if ( 0 == numVerts )
355 {
356 return NULL;
357 }
358
359 size_t numFaces = countFaces( rArray );
360 if ( 0 == numFaces )
361 {
362 return NULL;
363 }
364
365 size_t numTriangles = countTriangles( rArray );
366 pMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
367
368 pMesh->mFaces = new aiFace[ numTriangles ];
369 pMesh->mNumFaces = static_cast<unsigned int>(numTriangles);
370
371 pMesh->mNumVertices = static_cast<unsigned int>(numVerts);
372 pMesh->mVertices = new aiVector3D[ numVerts ];
373 pMesh->mNormals = new aiVector3D[ numVerts ];
374 pMesh->mTextureCoords[ 0 ] = new aiVector3D[ numVerts ];
375 pMesh->mTextureCoords[ 1 ] = new aiVector3D[ numVerts ];
376 pMesh->mMaterialIndex = materialIdx;
377
378 unsigned int faceIdx = 0;
379 unsigned int vertIdx = 0;
380 pMesh->mNumUVComponents[ 0 ] = 2;
381 pMesh->mNumUVComponents[ 1 ] = 2;
382 for ( std::vector<sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end(); ++it )
383 {
384 Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
385 ai_assert( NULL != pQ3BSPFace );
386 if ( NULL == pQ3BSPFace )
387 {
388 continue;
389 }
390
391 if ( pQ3BSPFace->iNumOfFaceVerts > 0 )
392 {
393 if ( pQ3BSPFace->iType == Polygon || pQ3BSPFace->iType == TriangleMesh )
394 {
395 createTriangleTopology( pModel, pQ3BSPFace, pMesh, faceIdx, vertIdx );
396 }
397 }
398 }
399
400 aiNode *pNode = new aiNode;
401 pNode->mNumMeshes = 1;
402 pNode->mMeshes = new unsigned int[ 1 ];
403
404 return pNode;
405}
406
407// ------------------------------------------------------------------------------------------------
408// Creates the triangle topology from a face array.
409void Q3BSPFileImporter::createTriangleTopology( const Q3BSP::Q3BSPModel *pModel,
410 Q3BSP::sQ3BSPFace *pQ3BSPFace,
411 aiMesh* pMesh,
412 unsigned int &rFaceIdx,
413 unsigned int &rVertIdx )
414{
415 ai_assert( rFaceIdx < pMesh->mNumFaces );
416
417 m_pCurrentFace = getNextFace( pMesh, rFaceIdx );
418 ai_assert( NULL != m_pCurrentFace );
419 if ( NULL == m_pCurrentFace )
420 {
421 return;
422 }
423
424 m_pCurrentFace->mNumIndices = 3;
425 m_pCurrentFace->mIndices = new unsigned int[ m_pCurrentFace->mNumIndices ];
426
427 size_t idx = 0;
428 for ( size_t i = 0; i < (size_t) pQ3BSPFace->iNumOfFaceVerts; i++ )
429 {
430 const size_t index = pQ3BSPFace->iVertexIndex + pModel->m_Indices[ pQ3BSPFace->iFaceVertexIndex + i ];
431 ai_assert( index < pModel->m_Vertices.size() );
432 if ( index >= pModel->m_Vertices.size() )
433 {
434 continue;
435 }
436
437 sQ3BSPVertex *pVertex = pModel->m_Vertices[ index ];
438 ai_assert( NULL != pVertex );
439 if ( NULL == pVertex )
440 {
441 continue;
442 }
443
444 pMesh->mVertices[ rVertIdx ].Set( pVertex->vPosition.x, pVertex->vPosition.y, pVertex->vPosition.z );
445 pMesh->mNormals[ rVertIdx ].Set( pVertex->vNormal.x, pVertex->vNormal.y, pVertex->vNormal.z );
446
447 pMesh->mTextureCoords[ 0 ][ rVertIdx ].Set( pVertex->vTexCoord.x, pVertex->vTexCoord.y, 0.0f );
448 pMesh->mTextureCoords[ 1 ][ rVertIdx ].Set( pVertex->vLightmap.x, pVertex->vLightmap.y, 0.0f );
449
450 m_pCurrentFace->mIndices[ idx ] = rVertIdx;
451 rVertIdx++;
452
453 idx++;
454 if ( idx > 2 )
455 {
456 idx = 0;
457 m_pCurrentFace = getNextFace( pMesh, rFaceIdx );
458 if ( NULL != m_pCurrentFace )
459 {
460 m_pCurrentFace->mNumIndices = 3;
461 m_pCurrentFace->mIndices = new unsigned int[ 3 ];
462 }
463 }
464 }
465 rFaceIdx--;
466}
467
468// ------------------------------------------------------------------------------------------------
469// Creates all referenced materials.
470void Q3BSPFileImporter::createMaterials( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
471 Q3BSPZipArchive *pArchive )
472{
473 if ( m_MaterialLookupMap.empty() )
474 {
475 return;
476 }
477
478 pScene->mMaterials = new aiMaterial*[ m_MaterialLookupMap.size() ];
479 aiString aiMatName;
480 int textureId( -1 ), lightmapId( -1 );
481 for ( FaceMapIt it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end();
482 ++it )
483 {
484 const std::string matName = (*it).first;
485 if ( matName.empty() )
486 {
487 continue;
488 }
489
490 aiMatName.Set( matName );
491 aiMaterial *pMatHelper = new aiMaterial;
492 pMatHelper->AddProperty( &aiMatName, AI_MATKEY_NAME );
493
494 extractIds( matName, textureId, lightmapId );
495
496 // Adding the texture
497 if ( -1 != textureId )
498 {
499 sQ3BSPTexture *pTexture = pModel->m_Textures[ textureId ];
500 if ( NULL != pTexture )
501 {
502 std::string tmp( "*" ), texName( "" );
503 tmp += pTexture->strName;
504 tmp += ".jpg";
505 normalizePathName( tmp, texName );
506
507 if ( !importTextureFromArchive( pModel, pArchive, pScene, pMatHelper, textureId ) )
508 {
509 }
510 }
511
512 }
513 if ( -1 != lightmapId )
514 {
515 importLightmap( pModel, pScene, pMatHelper, lightmapId );
516 }
517 pScene->mMaterials[ pScene->mNumMaterials ] = pMatHelper;
518 pScene->mNumMaterials++;
519 }
520 pScene->mNumTextures = static_cast<unsigned int>(mTextures.size());
521 pScene->mTextures = new aiTexture*[ pScene->mNumTextures ];
522 std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures );
523}
524
525// ------------------------------------------------------------------------------------------------
526// Counts the number of referenced vertices.
527size_t Q3BSPFileImporter::countData( const std::vector<sQ3BSPFace*> &rArray ) const
528{
529 size_t numVerts = 0;
530 for ( std::vector<sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end();
531 ++it )
532 {
533 sQ3BSPFace *pQ3BSPFace = *it;
534 if ( pQ3BSPFace->iType == Polygon || pQ3BSPFace->iType == TriangleMesh )
535 {
536 Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
537 ai_assert( NULL != pQ3BSPFace );
538 numVerts += pQ3BSPFace->iNumOfFaceVerts;
539 }
540 }
541
542 return numVerts;
543}
544
545// ------------------------------------------------------------------------------------------------
546// Counts the faces with vertices.
547size_t Q3BSPFileImporter::countFaces( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const
548{
549 size_t numFaces = 0;
550 for ( std::vector<sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end();
551 ++it )
552 {
553 Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
554 if ( pQ3BSPFace->iNumOfFaceVerts > 0 )
555 {
556 numFaces++;
557 }
558 }
559
560 return numFaces;
561}
562
563// ------------------------------------------------------------------------------------------------
564// Counts the number of triangles in a Q3-face-array.
565size_t Q3BSPFileImporter::countTriangles( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const
566{
567 size_t numTriangles = 0;
568 for ( std::vector<Q3BSP::sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end();
569 ++it )
570 {
571 const Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
572 if ( NULL != pQ3BSPFace )
573 {
574 numTriangles += pQ3BSPFace->iNumOfFaceVerts / 3;
575 }
576 }
577
578 return numTriangles;
579}
580
581// ------------------------------------------------------------------------------------------------
582// Creates the faces-to-material map.
583void Q3BSPFileImporter::createMaterialMap( const Q3BSP::Q3BSPModel *pModel )
584{
585 std::string key( "" );
586 std::vector<sQ3BSPFace*> *pCurFaceArray = NULL;
587 for ( size_t idx = 0; idx < pModel->m_Faces.size(); idx++ )
588 {
589 Q3BSP::sQ3BSPFace *pQ3BSPFace = pModel->m_Faces[ idx ];
590 const int texId = pQ3BSPFace->iTextureID;
591 const int lightMapId = pQ3BSPFace->iLightmapID;
592 createKey( texId, lightMapId, key );
593 FaceMapIt it = m_MaterialLookupMap.find( key );
594 if ( m_MaterialLookupMap.end() == it )
595 {
596 pCurFaceArray = new std::vector<Q3BSP::sQ3BSPFace*>;
597 m_MaterialLookupMap[ key ] = pCurFaceArray;
598 }
599 else
600 {
601 pCurFaceArray = (*it).second;
602 }
603 ai_assert( NULL != pCurFaceArray );
604 if ( NULL != pCurFaceArray )
605 {
606 pCurFaceArray->push_back( pQ3BSPFace );
607 }
608 }
609}
610
611// ------------------------------------------------------------------------------------------------
612// Returns the next face.
613aiFace *Q3BSPFileImporter::getNextFace( aiMesh *pMesh, unsigned int &rFaceIdx )
614{
615 aiFace *pFace( NULL );
616 if ( rFaceIdx < pMesh->mNumFaces ) {
617 pFace = &pMesh->mFaces[ rFaceIdx ];
618 rFaceIdx++;
619 }
620
621 return pFace;
622}
623
624// ------------------------------------------------------------------------------------------------
625// Imports a texture file.
626bool Q3BSPFileImporter::importTextureFromArchive( const Q3BSP::Q3BSPModel *pModel,
627 Q3BSP::Q3BSPZipArchive *pArchive, aiScene*,
628 aiMaterial *pMatHelper, int textureId ) {
629 if ( NULL == pArchive || NULL == pMatHelper ) {
630 return false;
631 }
632
633 if ( textureId < 0 || textureId >= static_cast<int>( pModel->m_Textures.size() ) ) {
634 return false;
635 }
636
637 bool res = true;
638 sQ3BSPTexture *pTexture = pModel->m_Textures[ textureId ];
639 if ( !pTexture ) {
640 return false;
641 }
642
643 std::vector<std::string> supportedExtensions;
644 supportedExtensions.push_back( ".jpg" );
645 supportedExtensions.push_back( ".png" );
646 supportedExtensions.push_back( ".tga" );
647 std::string textureName, ext;
648 if ( expandFile( pArchive, pTexture->strName, supportedExtensions, textureName, ext ) ) {
649 IOStream *pTextureStream = pArchive->Open( textureName.c_str() );
650 if ( pTextureStream ) {
651 size_t texSize = pTextureStream->FileSize();
652 aiTexture *pTexture = new aiTexture;
653 pTexture->mHeight = 0;
654 pTexture->mWidth = static_cast<unsigned int>(texSize);
655 unsigned char *pData = new unsigned char[ pTexture->mWidth ];
656 size_t readSize = pTextureStream->Read( pData, sizeof( unsigned char ), pTexture->mWidth );
657 (void)readSize;
658 ai_assert( readSize == pTexture->mWidth );
659 pTexture->pcData = reinterpret_cast<aiTexel*>( pData );
660 pTexture->achFormatHint[ 0 ] = ext[ 1 ];
661 pTexture->achFormatHint[ 1 ] = ext[ 2 ];
662 pTexture->achFormatHint[ 2 ] = ext[ 3 ];
663 pTexture->achFormatHint[ 3 ] = '\0';
664 res = true;
665
666 aiString name;
667 name.data[ 0 ] = '*';
668 name.length = 1 + ASSIMP_itoa10( name.data + 1, static_cast<unsigned int>(MAXLEN-1), static_cast<int32_t>(mTextures.size()) );
669
670 pArchive->Close( pTextureStream );
671
672 pMatHelper->AddProperty( &name, AI_MATKEY_TEXTURE_DIFFUSE( 0 ) );
673 mTextures.push_back( pTexture );
674 } else {
675 // If it doesn't exist in the archive, it is probably just a reference to an external file.
676 // We'll leave it up to the user to figure out which extension the file has.
677 aiString name;
678 strncpy( name.data, pTexture->strName, sizeof name.data );
679 name.length = strlen( name.data );
680 pMatHelper->AddProperty( &name, AI_MATKEY_TEXTURE_DIFFUSE( 0 ) );
681 }
682 }
683
684 return res;
685}
686
687// ------------------------------------------------------------------------------------------------
688// Imports a light map file.
689bool Q3BSPFileImporter::importLightmap( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
690 aiMaterial *pMatHelper, int lightmapId )
691{
692 if ( NULL == pModel || NULL == pScene || NULL == pMatHelper )
693 {
694 return false;
695 }
696
697 if ( lightmapId < 0 || lightmapId >= static_cast<int>( pModel->m_Lightmaps.size() ) )
698 {
699 return false;
700 }
701
702 sQ3BSPLightmap *pLightMap = pModel->m_Lightmaps[ lightmapId ];
703 if ( NULL == pLightMap )
704 {
705 return false;
706 }
707
708 aiTexture *pTexture = new aiTexture;
709
710 pTexture->mWidth = CE_BSP_LIGHTMAPWIDTH;
711 pTexture->mHeight = CE_BSP_LIGHTMAPHEIGHT;
712 pTexture->pcData = new aiTexel[CE_BSP_LIGHTMAPWIDTH * CE_BSP_LIGHTMAPHEIGHT];
713
714 ::memcpy( pTexture->pcData, pLightMap->bLMapData, pTexture->mWidth );
715 size_t p = 0;
716 for ( size_t i = 0; i < CE_BSP_LIGHTMAPWIDTH * CE_BSP_LIGHTMAPHEIGHT; ++i )
717 {
718 pTexture->pcData[ i ].r = pLightMap->bLMapData[ p++ ];
719 pTexture->pcData[ i ].g = pLightMap->bLMapData[ p++ ];
720 pTexture->pcData[ i ].b = pLightMap->bLMapData[ p++ ];
721 pTexture->pcData[ i ].a = 0xFF;
722 }
723
724 aiString name;
725 name.data[ 0 ] = '*';
726 name.length = 1 + ASSIMP_itoa10( name.data + 1, static_cast<unsigned int>(MAXLEN-1), static_cast<int32_t>(mTextures.size()) );
727
728 pMatHelper->AddProperty( &name,AI_MATKEY_TEXTURE_LIGHTMAP( 1 ) );
729 mTextures.push_back( pTexture );
730
731 return true;
732}
733
734
735// ------------------------------------------------------------------------------------------------
736// Will search for a supported extension.
737bool Q3BSPFileImporter::expandFile( Q3BSP::Q3BSPZipArchive *pArchive, const std::string &rFilename,
738 const std::vector<std::string> &rExtList, std::string &rFile,
739 std::string &rExt )
740{
741 ai_assert( NULL != pArchive );
742 ai_assert( !rFilename.empty() );
743
744 if ( rExtList.empty() )
745 {
746 rFile = rFilename;
747 rExt = "";
748 return true;
749 }
750
751 bool found = false;
752 for ( std::vector<std::string>::const_iterator it = rExtList.begin(); it != rExtList.end(); ++it )
753 {
754 const std::string textureName = rFilename + *it;
755 if ( pArchive->Exists( textureName.c_str() ) )
756 {
757 rExt = *it;
758 rFile = textureName;
759 found = true;
760 break;
761 }
762 }
763
764 return found;
765}
766
767// ------------------------------------------------------------------------------------------------
768
769} // Namespace Assimp
770
771#endif // ASSIMP_BUILD_NO_Q3BSP_IMPORTER
772