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