/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 |