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