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