Coverage Report

Created: 2025-06-22 07:30

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