682 lines
24 KiB
C++
682 lines
24 KiB
C++
/*
|
|
Open Asset Import Library (assimp)
|
|
---------------------------------------------------------------------------------------------------
|
|
|
|
Copyright (c) 2006-2022, assimp team
|
|
|
|
|
|
All rights reserved.
|
|
|
|
Redistribution and use of this software in source and binary forms,
|
|
with or without modification, are permitted provided that the
|
|
following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above
|
|
copyright notice, this list of conditions and the
|
|
following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above
|
|
copyright notice, this list of conditions and the
|
|
following disclaimer in the documentation and/or other
|
|
materials provided with the distribution.
|
|
|
|
* Neither the name of the assimp team, nor the names of its
|
|
contributors may be used to endorse or promote products
|
|
derived from this software without specific prior
|
|
written permission of the assimp team.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
---------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
|
|
|
|
#include "Q3BSPFileImporter.h"
|
|
#include "Q3BSPFileData.h"
|
|
#include "Q3BSPFileParser.h"
|
|
|
|
#include <assimp/DefaultLogger.hpp>
|
|
|
|
#include "zlib.h"
|
|
|
|
#include <assimp/DefaultIOSystem.h>
|
|
#include <assimp/StringComparison.h>
|
|
#include <assimp/ZipArchiveIOSystem.h>
|
|
#include <assimp/ai_assert.h>
|
|
#include <assimp/importerdesc.h>
|
|
#include <assimp/mesh.h>
|
|
#include <assimp/scene.h>
|
|
#include <assimp/types.h>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
static constexpr aiImporterDesc desc = {
|
|
"Quake III BSP Importer",
|
|
"",
|
|
"",
|
|
"",
|
|
aiImporterFlags_SupportBinaryFlavour,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
"bsp pk3"
|
|
};
|
|
|
|
namespace Assimp {
|
|
|
|
using namespace Q3BSP;
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Local function to create a material key name.
|
|
static void createKey(int id1, int id2, std::string &key) {
|
|
std::ostringstream str;
|
|
str << id1 << "." << id2;
|
|
key = str.str();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Local function to extract the texture ids from a material key-name.
|
|
static void extractIds(const std::string &key, int &id1, int &id2) {
|
|
id1 = -1;
|
|
id2 = -1;
|
|
if (key.empty()) {
|
|
return;
|
|
}
|
|
|
|
const std::string::size_type pos = key.find('.');
|
|
if (std::string::npos == pos) {
|
|
return;
|
|
}
|
|
|
|
std::string tmp1 = key.substr(0, pos);
|
|
std::string tmp2 = key.substr(pos + 1, key.size() - pos - 1);
|
|
id1 = atoi(tmp1.c_str());
|
|
id2 = atoi(tmp2.c_str());
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Local helper function to normalize filenames.
|
|
static void normalizePathName(const std::string &rPath, std::string &normalizedPath) {
|
|
normalizedPath = std::string();
|
|
if (rPath.empty()) {
|
|
return;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
std::string sep = "\\";
|
|
#else
|
|
std::string sep = "/";
|
|
#endif
|
|
|
|
static const unsigned int numDelimiters = 2;
|
|
const char delimiters[numDelimiters] = { '/', '\\' };
|
|
normalizedPath = rPath;
|
|
for (const char delimiter : delimiters) {
|
|
for (size_t j = 0; j < normalizedPath.size(); ++j) {
|
|
if (normalizedPath[j] == delimiter) {
|
|
normalizedPath[j] = sep[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Constructor.
|
|
Q3BSPFileImporter::Q3BSPFileImporter() :
|
|
m_pCurrentMesh(nullptr), m_pCurrentFace(nullptr) {
|
|
// empty
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Destructor.
|
|
Q3BSPFileImporter::~Q3BSPFileImporter() {
|
|
clear();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Q3BSPFileImporter::clear() {
|
|
for (FaceMap::iterator it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end(); ++it) {
|
|
const std::string &matName = it->first;
|
|
if (!matName.empty()) {
|
|
delete it->second;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Returns true if the loader can read this.
|
|
bool Q3BSPFileImporter::CanRead(const std::string &filename, IOSystem * /*pIOHandler*/, bool checkSig) const {
|
|
if (!checkSig) {
|
|
return SimpleExtensionCheck(filename, "pk3", "bsp");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Adds extensions.
|
|
const aiImporterDesc *Q3BSPFileImporter::GetInfo() const {
|
|
return &desc;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Import method.
|
|
void Q3BSPFileImporter::InternReadFile(const std::string &rFile, aiScene *scene, IOSystem *ioHandler) {
|
|
clear();
|
|
ZipArchiveIOSystem Archive(ioHandler, rFile);
|
|
if (!Archive.isOpen()) {
|
|
throw DeadlyImportError("Failed to open file ", rFile, ".");
|
|
}
|
|
|
|
std::string archiveName, mapName;
|
|
separateMapName(rFile, archiveName, mapName);
|
|
|
|
if (mapName.empty()) {
|
|
if (!findFirstMapInArchive(Archive, mapName)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
Q3BSPFileParser fileParser(mapName, &Archive);
|
|
Q3BSPModel *pBSPModel = fileParser.getModel();
|
|
if (nullptr != pBSPModel) {
|
|
CreateDataFromImport(pBSPModel, scene, &Archive);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Separates the map name from the import name.
|
|
void Q3BSPFileImporter::separateMapName(const std::string &importName, std::string &archiveName, std::string &mapName) {
|
|
archiveName = std::string();
|
|
mapName = std::string();
|
|
if (importName.empty()) {
|
|
return;
|
|
}
|
|
|
|
const std::string::size_type pos = importName.rfind(',');
|
|
if (std::string::npos == pos) {
|
|
archiveName = importName;
|
|
return;
|
|
}
|
|
|
|
archiveName = importName.substr(0, pos);
|
|
mapName = importName.substr(pos, importName.size() - pos - 1);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Returns the first map in the map archive.
|
|
bool Q3BSPFileImporter::findFirstMapInArchive(ZipArchiveIOSystem &bspArchive, std::string &mapName) {
|
|
mapName = std::string();
|
|
std::vector<std::string> fileList;
|
|
bspArchive.getFileListExtension(fileList, "bsp");
|
|
if (fileList.empty()) {
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string>::iterator it(fileList.begin());
|
|
for (; it != fileList.end(); ++it) {
|
|
const std::string::size_type pos = (*it).find("maps/");
|
|
if (std::string::npos != pos) {
|
|
std::string::size_type extPos = (*it).find(".bsp");
|
|
if (std::string::npos != extPos) {
|
|
mapName = *it;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Creates the assimp specific data.
|
|
void Q3BSPFileImporter::CreateDataFromImport(const Q3BSP::Q3BSPModel *pModel, aiScene *pScene,
|
|
ZipArchiveIOSystem *pArchive) {
|
|
if (nullptr == pModel || nullptr == pScene) {
|
|
return;
|
|
}
|
|
|
|
pScene->mRootNode = new aiNode;
|
|
if (!pModel->m_ModelName.empty()) {
|
|
pScene->mRootNode->mName.Set(pModel->m_ModelName);
|
|
}
|
|
|
|
// Create the face to material relation map
|
|
createMaterialMap(pModel);
|
|
|
|
// Create all nodes
|
|
CreateNodes(pModel, pScene, pScene->mRootNode);
|
|
|
|
// Create the assigned materials
|
|
createMaterials(pModel, pScene, pArchive);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Creates all assimp nodes.
|
|
void Q3BSPFileImporter::CreateNodes(const Q3BSP::Q3BSPModel *pModel, aiScene *pScene,
|
|
aiNode *pParent) {
|
|
if (nullptr == pModel) {
|
|
return;
|
|
}
|
|
|
|
unsigned int matIdx(0);
|
|
std::vector<aiMesh *> MeshArray;
|
|
std::vector<aiNode *> NodeArray;
|
|
for (FaceMapIt it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end(); ++it) {
|
|
std::vector<Q3BSP::sQ3BSPFace *> *pArray = (*it).second;
|
|
size_t numVerts = countData(*pArray);
|
|
if (0 != numVerts) {
|
|
aiMesh *pMesh(nullptr);
|
|
aiNode *pNode = CreateTopology(pModel, matIdx, *pArray, &pMesh);
|
|
if (nullptr != pNode) {
|
|
NodeArray.push_back(pNode);
|
|
MeshArray.push_back(pMesh);
|
|
}
|
|
}
|
|
matIdx++;
|
|
}
|
|
|
|
pScene->mNumMeshes = static_cast<unsigned int>(MeshArray.size());
|
|
if (pScene->mNumMeshes > 0) {
|
|
pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
|
|
for (size_t i = 0; i < MeshArray.size(); i++) {
|
|
aiMesh *pMesh = MeshArray[i];
|
|
if (nullptr != pMesh) {
|
|
pScene->mMeshes[i] = pMesh;
|
|
}
|
|
}
|
|
}
|
|
|
|
pParent->mNumChildren = static_cast<unsigned int>(MeshArray.size());
|
|
pParent->mChildren = new aiNode *[pScene->mRootNode->mNumChildren];
|
|
for (size_t i = 0; i < NodeArray.size(); i++) {
|
|
aiNode *pNode = NodeArray[i];
|
|
pNode->mParent = pParent;
|
|
pParent->mChildren[i] = pNode;
|
|
pParent->mChildren[i]->mMeshes[0] = static_cast<unsigned int>(i);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Creates the topology.
|
|
aiNode *Q3BSPFileImporter::CreateTopology(const Q3BSP::Q3BSPModel *pModel, unsigned int materialIdx,
|
|
std::vector<sQ3BSPFace *> &rArray, aiMesh **pMesh) {
|
|
size_t numVerts = countData(rArray);
|
|
if (0 == numVerts) {
|
|
return nullptr;
|
|
}
|
|
|
|
size_t numFaces = countFaces(rArray);
|
|
if (0 == numFaces) {
|
|
return nullptr;
|
|
}
|
|
|
|
aiMesh *mesh = new aiMesh;
|
|
size_t numTriangles = countTriangles(rArray);
|
|
mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
|
|
|
|
mesh->mFaces = new aiFace[numTriangles];
|
|
mesh->mNumFaces = static_cast<unsigned int>(numTriangles);
|
|
|
|
mesh->mNumVertices = static_cast<unsigned int>(numVerts);
|
|
mesh->mVertices = new aiVector3D[numVerts];
|
|
mesh->mNormals = new aiVector3D[numVerts];
|
|
mesh->mTextureCoords[0] = new aiVector3D[numVerts];
|
|
mesh->mTextureCoords[1] = new aiVector3D[numVerts];
|
|
mesh->mMaterialIndex = materialIdx;
|
|
|
|
unsigned int faceIdx = 0;
|
|
unsigned int vertIdx = 0;
|
|
mesh->mNumUVComponents[0] = 2;
|
|
mesh->mNumUVComponents[1] = 2;
|
|
for (std::vector<sQ3BSPFace *>::const_iterator it = rArray.begin(); it != rArray.end(); ++it) {
|
|
Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
|
|
ai_assert(nullptr != pQ3BSPFace);
|
|
if (nullptr == pQ3BSPFace) {
|
|
continue;
|
|
}
|
|
|
|
if (pQ3BSPFace->iNumOfFaceVerts > 0) {
|
|
if (pQ3BSPFace->iType == Polygon || pQ3BSPFace->iType == TriangleMesh) {
|
|
createTriangleTopology(pModel, pQ3BSPFace, mesh, faceIdx, vertIdx);
|
|
}
|
|
}
|
|
}
|
|
|
|
aiNode *pNode = new aiNode;
|
|
pNode->mNumMeshes = 1;
|
|
pNode->mMeshes = new unsigned int[1];
|
|
*pMesh = mesh;
|
|
|
|
return pNode;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Creates the triangle topology from a face array.
|
|
void Q3BSPFileImporter::createTriangleTopology(const Q3BSP::Q3BSPModel *pModel, sQ3BSPFace *pQ3BSPFace,
|
|
aiMesh *pMesh, unsigned int &faceIdx, unsigned int &vertIdx) {
|
|
ai_assert(faceIdx < pMesh->mNumFaces);
|
|
|
|
m_pCurrentFace = getNextFace(pMesh, faceIdx);
|
|
if (nullptr == m_pCurrentFace) {
|
|
return;
|
|
}
|
|
|
|
m_pCurrentFace->mNumIndices = 3;
|
|
m_pCurrentFace->mIndices = new unsigned int[m_pCurrentFace->mNumIndices];
|
|
|
|
size_t idx(0);
|
|
for (size_t i = 0; i < (size_t)pQ3BSPFace->iNumOfFaceVerts; ++i) {
|
|
const size_t index = pQ3BSPFace->iVertexIndex + pModel->m_Indices[pQ3BSPFace->iFaceVertexIndex + i];
|
|
if (index >= pModel->m_Vertices.size()) {
|
|
continue;
|
|
}
|
|
|
|
sQ3BSPVertex *pVertex = pModel->m_Vertices[index];
|
|
if (nullptr == pVertex) {
|
|
continue;
|
|
}
|
|
if (idx > 2) {
|
|
idx = 0;
|
|
m_pCurrentFace = getNextFace(pMesh, faceIdx);
|
|
if (nullptr != m_pCurrentFace) {
|
|
m_pCurrentFace->mNumIndices = 3;
|
|
m_pCurrentFace->mIndices = new unsigned int[3];
|
|
m_pCurrentFace->mIndices[idx] = vertIdx;
|
|
}
|
|
}
|
|
|
|
pMesh->mVertices[vertIdx].Set(pVertex->vPosition.x, pVertex->vPosition.y, pVertex->vPosition.z);
|
|
pMesh->mNormals[vertIdx].Set(pVertex->vNormal.x, pVertex->vNormal.y, pVertex->vNormal.z);
|
|
|
|
pMesh->mTextureCoords[0][vertIdx].Set(pVertex->vTexCoord.x, pVertex->vTexCoord.y, 0.0f);
|
|
pMesh->mTextureCoords[1][vertIdx].Set(pVertex->vLightmap.x, pVertex->vLightmap.y, 0.0f);
|
|
|
|
vertIdx++;
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Creates all referenced materials.
|
|
void Q3BSPFileImporter::createMaterials(const Q3BSP::Q3BSPModel *pModel, aiScene *pScene,
|
|
ZipArchiveIOSystem *pArchive) {
|
|
if (m_MaterialLookupMap.empty()) {
|
|
return;
|
|
}
|
|
|
|
pScene->mMaterials = new aiMaterial *[m_MaterialLookupMap.size()];
|
|
aiString aiMatName;
|
|
int textureId(-1), lightmapId(-1);
|
|
for (FaceMapIt it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end();
|
|
++it) {
|
|
const std::string matName(it->first);
|
|
if (matName.empty()) {
|
|
continue;
|
|
}
|
|
|
|
aiMatName.Set(matName);
|
|
aiMaterial *pMatHelper = new aiMaterial;
|
|
pMatHelper->AddProperty(&aiMatName, AI_MATKEY_NAME);
|
|
|
|
extractIds(matName, textureId, lightmapId);
|
|
|
|
// Adding the texture
|
|
if (-1 != textureId) {
|
|
sQ3BSPTexture *pTexture = pModel->m_Textures[textureId];
|
|
if (nullptr != pTexture) {
|
|
std::string tmp("*"), texName;
|
|
tmp += pTexture->strName;
|
|
tmp += ".jpg";
|
|
normalizePathName(tmp, texName);
|
|
|
|
if (!importTextureFromArchive(pModel, pArchive, pScene, pMatHelper, textureId)) {
|
|
ASSIMP_LOG_ERROR("Cannot import texture from archive ", texName);
|
|
}
|
|
}
|
|
}
|
|
if (-1 != lightmapId) {
|
|
importLightmap(pModel, pScene, pMatHelper, lightmapId);
|
|
}
|
|
pScene->mMaterials[pScene->mNumMaterials] = pMatHelper;
|
|
pScene->mNumMaterials++;
|
|
}
|
|
pScene->mNumTextures = static_cast<unsigned int>(mTextures.size());
|
|
pScene->mTextures = new aiTexture *[pScene->mNumTextures];
|
|
std::copy(mTextures.begin(), mTextures.end(), pScene->mTextures);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Counts the number of referenced vertices.
|
|
size_t Q3BSPFileImporter::countData(const std::vector<sQ3BSPFace *> &faceArray) const {
|
|
size_t numVerts(0);
|
|
for (std::vector<sQ3BSPFace *>::const_iterator it = faceArray.begin(); it != faceArray.end();
|
|
++it) {
|
|
sQ3BSPFace *pQ3BSPFace = *it;
|
|
if (pQ3BSPFace->iType == Polygon || pQ3BSPFace->iType == TriangleMesh) {
|
|
Q3BSP::sQ3BSPFace *face = *it;
|
|
if (nullptr != face) {
|
|
numVerts += face->iNumOfFaceVerts;
|
|
}
|
|
}
|
|
}
|
|
|
|
return numVerts;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Counts the faces with vertices.
|
|
size_t Q3BSPFileImporter::countFaces(const std::vector<Q3BSP::sQ3BSPFace *> &rArray) const {
|
|
size_t numFaces = 0;
|
|
for (std::vector<sQ3BSPFace *>::const_iterator it = rArray.begin(); it != rArray.end();
|
|
++it) {
|
|
Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
|
|
if (pQ3BSPFace->iNumOfFaceVerts > 0) {
|
|
numFaces++;
|
|
}
|
|
}
|
|
|
|
return numFaces;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Counts the number of triangles in a Q3-face-array.
|
|
size_t Q3BSPFileImporter::countTriangles(const std::vector<Q3BSP::sQ3BSPFace *> &rArray) const {
|
|
size_t numTriangles = 0;
|
|
for (std::vector<Q3BSP::sQ3BSPFace *>::const_iterator it = rArray.begin(); it != rArray.end();
|
|
++it) {
|
|
const Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
|
|
if (nullptr != pQ3BSPFace) {
|
|
numTriangles += pQ3BSPFace->iNumOfFaceVerts / 3;
|
|
}
|
|
}
|
|
|
|
return numTriangles;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Creates the faces-to-material map.
|
|
void Q3BSPFileImporter::createMaterialMap(const Q3BSP::Q3BSPModel *pModel) {
|
|
std::string key;
|
|
std::vector<sQ3BSPFace *> *pCurFaceArray = nullptr;
|
|
for (size_t idx = 0; idx < pModel->m_Faces.size(); idx++) {
|
|
Q3BSP::sQ3BSPFace *pQ3BSPFace = pModel->m_Faces[idx];
|
|
const int texId = pQ3BSPFace->iTextureID;
|
|
const int lightMapId = pQ3BSPFace->iLightmapID;
|
|
createKey(texId, lightMapId, key);
|
|
FaceMapIt it = m_MaterialLookupMap.find(key);
|
|
if (m_MaterialLookupMap.end() == it) {
|
|
pCurFaceArray = new std::vector<Q3BSP::sQ3BSPFace *>;
|
|
m_MaterialLookupMap[key] = pCurFaceArray;
|
|
} else {
|
|
pCurFaceArray = (*it).second;
|
|
}
|
|
ai_assert(nullptr != pCurFaceArray);
|
|
if (nullptr != pCurFaceArray) {
|
|
pCurFaceArray->push_back(pQ3BSPFace);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Returns the next face.
|
|
aiFace *Q3BSPFileImporter::getNextFace(aiMesh *mesh, unsigned int &faceIdx) {
|
|
aiFace *face(nullptr);
|
|
if (faceIdx < mesh->mNumFaces) {
|
|
face = &mesh->mFaces[faceIdx];
|
|
++faceIdx;
|
|
}
|
|
|
|
return face;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Imports a texture file.
|
|
bool Q3BSPFileImporter::importTextureFromArchive(const Q3BSP::Q3BSPModel *model,
|
|
ZipArchiveIOSystem *archive, aiScene *,
|
|
aiMaterial *pMatHelper, int textureId) {
|
|
if (nullptr == archive || nullptr == pMatHelper) {
|
|
return false;
|
|
}
|
|
|
|
if (textureId < 0 || textureId >= static_cast<int>(model->m_Textures.size())) {
|
|
return false;
|
|
}
|
|
|
|
bool res = true;
|
|
sQ3BSPTexture *pTexture = model->m_Textures[textureId];
|
|
if (!pTexture) {
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string> supportedExtensions;
|
|
supportedExtensions.emplace_back(".jpg");
|
|
supportedExtensions.emplace_back(".png");
|
|
supportedExtensions.emplace_back(".tga");
|
|
std::string textureName, ext;
|
|
if (expandFile(archive, pTexture->strName, supportedExtensions, textureName, ext)) {
|
|
IOStream *pTextureStream = archive->Open(textureName.c_str());
|
|
if (pTextureStream) {
|
|
size_t texSize = pTextureStream->FileSize();
|
|
aiTexture *curTexture = new aiTexture;
|
|
curTexture->mHeight = 0;
|
|
curTexture->mWidth = static_cast<unsigned int>(texSize);
|
|
unsigned char *pData = new unsigned char[curTexture->mWidth];
|
|
size_t readSize = pTextureStream->Read(pData, sizeof(unsigned char), curTexture->mWidth);
|
|
(void)readSize;
|
|
ai_assert(readSize == curTexture->mWidth);
|
|
curTexture->pcData = reinterpret_cast<aiTexel *>(pData);
|
|
curTexture->achFormatHint[0] = ext[1];
|
|
curTexture->achFormatHint[1] = ext[2];
|
|
curTexture->achFormatHint[2] = ext[3];
|
|
curTexture->achFormatHint[3] = '\0';
|
|
res = true;
|
|
|
|
aiString name;
|
|
name.data[0] = '*';
|
|
name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast<unsigned int>(MAXLEN - 1), static_cast<int32_t>(mTextures.size()));
|
|
|
|
archive->Close(pTextureStream);
|
|
|
|
pMatHelper->AddProperty(&name, AI_MATKEY_TEXTURE_DIFFUSE(0));
|
|
mTextures.push_back(curTexture);
|
|
} else {
|
|
// If it doesn't exist in the archive, it is probably just a reference to an external file.
|
|
// We'll leave it up to the user to figure out which extension the file has.
|
|
aiString name;
|
|
strncpy(name.data, pTexture->strName, sizeof name.data);
|
|
name.length = static_cast<ai_uint32>(strlen(name.data));
|
|
pMatHelper->AddProperty(&name, AI_MATKEY_TEXTURE_DIFFUSE(0));
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Imports a light map file.
|
|
bool Q3BSPFileImporter::importLightmap(const Q3BSP::Q3BSPModel *pModel, aiScene *pScene,
|
|
aiMaterial *pMatHelper, int lightmapId) {
|
|
if (nullptr == pModel || nullptr == pScene || nullptr == pMatHelper) {
|
|
return false;
|
|
}
|
|
|
|
if (lightmapId < 0 || lightmapId >= static_cast<int>(pModel->m_Lightmaps.size())) {
|
|
return false;
|
|
}
|
|
|
|
sQ3BSPLightmap *pLightMap = pModel->m_Lightmaps[lightmapId];
|
|
if (nullptr == pLightMap) {
|
|
return false;
|
|
}
|
|
|
|
aiTexture *pTexture = new aiTexture;
|
|
|
|
pTexture->mWidth = CE_BSP_LIGHTMAPWIDTH;
|
|
pTexture->mHeight = CE_BSP_LIGHTMAPHEIGHT;
|
|
pTexture->pcData = new aiTexel[CE_BSP_LIGHTMAPWIDTH * CE_BSP_LIGHTMAPHEIGHT];
|
|
|
|
::memcpy(pTexture->pcData, pLightMap->bLMapData, pTexture->mWidth);
|
|
size_t p = 0;
|
|
for (size_t i = 0; i < CE_BSP_LIGHTMAPWIDTH * CE_BSP_LIGHTMAPHEIGHT; ++i) {
|
|
pTexture->pcData[i].r = pLightMap->bLMapData[p++];
|
|
pTexture->pcData[i].g = pLightMap->bLMapData[p++];
|
|
pTexture->pcData[i].b = pLightMap->bLMapData[p++];
|
|
pTexture->pcData[i].a = 0xFF;
|
|
}
|
|
|
|
aiString name;
|
|
name.data[0] = '*';
|
|
name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast<unsigned int>(MAXLEN - 1), static_cast<int32_t>(mTextures.size()));
|
|
|
|
pMatHelper->AddProperty(&name, AI_MATKEY_TEXTURE_LIGHTMAP(1));
|
|
mTextures.push_back(pTexture);
|
|
|
|
return true;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Will search for a supported extension.
|
|
bool Q3BSPFileImporter::expandFile(ZipArchiveIOSystem *pArchive, const std::string &rFilename,
|
|
const std::vector<std::string> &rExtList, std::string &rFile,
|
|
std::string &rExt) {
|
|
ai_assert(nullptr != pArchive);
|
|
ai_assert(!rFilename.empty());
|
|
|
|
if (rExtList.empty()) {
|
|
rFile = rFilename;
|
|
rExt = std::string();
|
|
return true;
|
|
}
|
|
|
|
bool found = false;
|
|
for (std::vector<std::string>::const_iterator it = rExtList.begin(); it != rExtList.end(); ++it) {
|
|
const std::string textureName = rFilename + *it;
|
|
if (pArchive->Exists(textureName.c_str())) {
|
|
rExt = *it;
|
|
rFile = textureName;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
} // Namespace Assimp
|
|
|
|
#endif // ASSIMP_BUILD_NO_Q3BSP_IMPORTER
|