diff options
author | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
commit | db81b925d776103326128bf629cbdda576a223e7 (patch) | |
tree | 58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/AssetLib/MDC/MDCLoader.cpp | |
parent | 55860037b14fb3893ba21cf2654c83d349cc1082 (diff) |
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/MDC/MDCLoader.cpp')
-rw-r--r-- | libs/assimp/code/AssetLib/MDC/MDCLoader.cpp | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/MDC/MDCLoader.cpp b/libs/assimp/code/AssetLib/MDC/MDCLoader.cpp new file mode 100644 index 0000000..7810800 --- /dev/null +++ b/libs/assimp/code/AssetLib/MDC/MDCLoader.cpp @@ -0,0 +1,460 @@ +/* +--------------------------------------------------------------------------- +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. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the MDC importer class */ + +#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER + +// internal headers +#include "AssetLib/MDC/MDCLoader.h" +#include "AssetLib/MD3/MD3FileData.h" +#include "AssetLib/MDC/MDCNormalTable.h" // shouldn't be included by other units + +#include <assimp/importerdesc.h> +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/IOSystem.hpp> +#include <assimp/Importer.hpp> +#include <assimp/StringUtils.h> + +#include <memory> + +using namespace Assimp; +using namespace Assimp::MDC; + +static const aiImporterDesc desc = { + "Return To Castle Wolfenstein Mesh Importer", + "", + "", + "", + aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "mdc" +}; + +// ------------------------------------------------------------------------------------------------ +void MDC::BuildVertex(const Frame &frame, + const BaseVertex &bvert, + const CompressedVertex &cvert, + aiVector3D &vXYZOut, + aiVector3D &vNorOut) { + // compute the position + const float xd = (cvert.xd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING; + const float yd = (cvert.yd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING; + const float zd = (cvert.zd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING; + vXYZOut.x = frame.localOrigin.x + AI_MDC_BASE_SCALING * (bvert.x + xd); + vXYZOut.y = frame.localOrigin.y + AI_MDC_BASE_SCALING * (bvert.y + yd); + vXYZOut.z = frame.localOrigin.z + AI_MDC_BASE_SCALING * (bvert.z + zd); + + // compute the normal vector .. ehm ... lookup it in the table :-) + vNorOut.x = mdcNormals[cvert.nd][0]; + vNorOut.y = mdcNormals[cvert.nd][1]; + vNorOut.z = mdcNormals[cvert.nd][2]; +} + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +MDCImporter::MDCImporter() : + configFrameID(), + pcHeader(), + mBuffer(), + fileSize() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +MDCImporter::~MDCImporter() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool MDCImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const uint32_t tokens[] = { AI_MDC_MAGIC_NUMBER_LE }; + return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); +} + +// ------------------------------------------------------------------------------------------------ +const aiImporterDesc *MDCImporter::GetInfo() const { + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Validate the header of the given MDC file +void MDCImporter::ValidateHeader() { + AI_SWAP4(this->pcHeader->ulVersion); + AI_SWAP4(this->pcHeader->ulFlags); + AI_SWAP4(this->pcHeader->ulNumFrames); + AI_SWAP4(this->pcHeader->ulNumTags); + AI_SWAP4(this->pcHeader->ulNumSurfaces); + AI_SWAP4(this->pcHeader->ulNumSkins); + AI_SWAP4(this->pcHeader->ulOffsetBorderFrames); + + if (pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_BE && + pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_LE) { + throw DeadlyImportError("Invalid MDC magic word: expected IDPC, found ", + ai_str_toprintable((char *)&pcHeader->ulIdent, 4)); + } + + if (pcHeader->ulVersion != AI_MDC_VERSION) { + ASSIMP_LOG_WARN("Unsupported MDC file version (2 (AI_MDC_VERSION) was expected)"); + } + + if (pcHeader->ulOffsetBorderFrames + pcHeader->ulNumFrames * sizeof(MDC::Frame) > this->fileSize || + pcHeader->ulOffsetSurfaces + pcHeader->ulNumSurfaces * sizeof(MDC::Surface) > this->fileSize) { + throw DeadlyImportError("Some of the offset values in the MDC header are invalid " + "and point to something behind the file."); + } + + if (this->configFrameID >= this->pcHeader->ulNumFrames) { + throw DeadlyImportError("The requested frame is not available"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Validate the header of a given MDC file surface +void MDCImporter::ValidateSurfaceHeader(BE_NCONST MDC::Surface *pcSurf) { + AI_SWAP4(pcSurf->ulFlags); + AI_SWAP4(pcSurf->ulNumCompFrames); + AI_SWAP4(pcSurf->ulNumBaseFrames); + AI_SWAP4(pcSurf->ulNumShaders); + AI_SWAP4(pcSurf->ulNumVertices); + AI_SWAP4(pcSurf->ulNumTriangles); + AI_SWAP4(pcSurf->ulOffsetTriangles); + AI_SWAP4(pcSurf->ulOffsetTexCoords); + AI_SWAP4(pcSurf->ulOffsetBaseVerts); + AI_SWAP4(pcSurf->ulOffsetCompVerts); + AI_SWAP4(pcSurf->ulOffsetFrameBaseFrames); + AI_SWAP4(pcSurf->ulOffsetFrameCompFrames); + AI_SWAP4(pcSurf->ulOffsetEnd); + + const unsigned int iMax = this->fileSize - (unsigned int)((int8_t *)pcSurf - (int8_t *)pcHeader); + + if (pcSurf->ulOffsetBaseVerts + pcSurf->ulNumVertices * sizeof(MDC::BaseVertex) > iMax || + (pcSurf->ulNumCompFrames && pcSurf->ulOffsetCompVerts + pcSurf->ulNumVertices * sizeof(MDC::CompressedVertex) > iMax) || + pcSurf->ulOffsetTriangles + pcSurf->ulNumTriangles * sizeof(MDC::Triangle) > iMax || + pcSurf->ulOffsetTexCoords + pcSurf->ulNumVertices * sizeof(MDC::TexturCoord) > iMax || + pcSurf->ulOffsetShaders + pcSurf->ulNumShaders * sizeof(MDC::Shader) > iMax || + pcSurf->ulOffsetFrameBaseFrames + pcSurf->ulNumBaseFrames * 2 > iMax || + (pcSurf->ulNumCompFrames && pcSurf->ulOffsetFrameCompFrames + pcSurf->ulNumCompFrames * 2 > iMax)) { + throw DeadlyImportError("Some of the offset values in the MDC surface header " + "are invalid and point somewhere behind the file."); + } +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration properties +void MDCImporter::SetupProperties(const Importer *pImp) { + // The AI_CONFIG_IMPORT_MDC_KEYFRAME option overrides the + // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. + if (static_cast<unsigned int>(-1) == (configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MDC_KEYFRAME, -1))) { + configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME, 0); + } +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void MDCImporter::InternReadFile( + const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { + std::unique_ptr<IOStream> file(pIOHandler->Open(pFile)); + + // Check whether we can read from the file + if (file.get() == nullptr) { + throw DeadlyImportError("Failed to open MDC file ", pFile, "."); + } + + // check whether the mdc file is large enough to contain the file header + fileSize = static_cast<unsigned int>(file->FileSize()); + if (fileSize < sizeof(MDC::Header)) { + throw DeadlyImportError("MDC File is too small."); + } + + std::vector<unsigned char> mBuffer2(fileSize); + file->Read(&mBuffer2[0], 1, fileSize); + mBuffer = &mBuffer2[0]; + + // validate the file header + this->pcHeader = (BE_NCONST MDC::Header *)this->mBuffer; + this->ValidateHeader(); + + std::vector<std::string> aszShaders; + + // get a pointer to the frame we want to read + BE_NCONST MDC::Frame *pcFrame = (BE_NCONST MDC::Frame *)(this->mBuffer + + this->pcHeader->ulOffsetBorderFrames); + + // no need to swap the other members, we won't need them + pcFrame += configFrameID; + AI_SWAP4(pcFrame->localOrigin[0]); + AI_SWAP4(pcFrame->localOrigin[1]); + AI_SWAP4(pcFrame->localOrigin[2]); + + // get the number of valid surfaces + BE_NCONST MDC::Surface *pcSurface, *pcSurface2; + pcSurface = pcSurface2 = reinterpret_cast<BE_NCONST MDC::Surface *>(mBuffer + pcHeader->ulOffsetSurfaces); + unsigned int iNumShaders = 0; + for (unsigned int i = 0; i < pcHeader->ulNumSurfaces; ++i) { + // validate the surface header + this->ValidateSurfaceHeader(pcSurface2); + + if (pcSurface2->ulNumVertices && pcSurface2->ulNumTriangles) { + ++pScene->mNumMeshes; + } + iNumShaders += pcSurface2->ulNumShaders; + pcSurface2 = reinterpret_cast<BE_NCONST MDC::Surface *>((BE_NCONST int8_t *)pcSurface2 + pcSurface2->ulOffsetEnd); + } + aszShaders.reserve(iNumShaders); + pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; + + // necessary that we don't crash if an exception occurs + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + pScene->mMeshes[i] = nullptr; + } + + // now read all surfaces + unsigned int iDefaultMatIndex = UINT_MAX; + for (unsigned int i = 0, iNum = 0; i < pcHeader->ulNumSurfaces; ++i) { + if (!pcSurface->ulNumVertices || !pcSurface->ulNumTriangles) continue; + aiMesh *pcMesh = pScene->mMeshes[iNum++] = new aiMesh(); + + pcMesh->mNumFaces = pcSurface->ulNumTriangles; + pcMesh->mNumVertices = pcMesh->mNumFaces * 3; + + // store the name of the surface for use as node name. + pcMesh->mName.Set(std::string(pcSurface->ucName, strnlen(pcSurface->ucName, AI_MDC_MAXQPATH - 1))); + + // go to the first shader in the file. ignore the others. + if (pcSurface->ulNumShaders) { + const MDC::Shader *pcShader = (const MDC::Shader *)((int8_t *)pcSurface + pcSurface->ulOffsetShaders); + pcMesh->mMaterialIndex = (unsigned int)aszShaders.size(); + + // create a new shader + aszShaders.push_back(std::string(pcShader->ucName, + ::strnlen(pcShader->ucName, sizeof(pcShader->ucName)))); + } + // need to create a default material + else if (UINT_MAX == iDefaultMatIndex) { + pcMesh->mMaterialIndex = iDefaultMatIndex = (unsigned int)aszShaders.size(); + aszShaders.push_back(std::string()); + } + // otherwise assign a reference to the default material + else + pcMesh->mMaterialIndex = iDefaultMatIndex; + + // allocate output storage for the mesh + aiVector3D *pcVertCur = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; + aiVector3D *pcNorCur = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; + aiVector3D *pcUVCur = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; + aiFace *pcFaceCur = pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; + + // create all vertices/faces + BE_NCONST MDC::Triangle *pcTriangle = (BE_NCONST MDC::Triangle *)((int8_t *)pcSurface + pcSurface->ulOffsetTriangles); + + BE_NCONST MDC::TexturCoord *const pcUVs = (BE_NCONST MDC::TexturCoord *)((int8_t *)pcSurface + pcSurface->ulOffsetTexCoords); + + // get a pointer to the uncompressed vertices + int16_t iOfs = *((int16_t *)((int8_t *)pcSurface + + pcSurface->ulOffsetFrameBaseFrames) + + this->configFrameID); + + AI_SWAP2(iOfs); + + BE_NCONST MDC::BaseVertex *const pcVerts = (BE_NCONST MDC::BaseVertex *)((int8_t *)pcSurface + pcSurface->ulOffsetBaseVerts) + + ((int)iOfs * pcSurface->ulNumVertices * 4); + + // do the main swapping stuff ... +#if (defined AI_BUILD_BIG_ENDIAN) + + // swap all triangles + for (unsigned int i = 0; i < pcSurface->ulNumTriangles; ++i) { + AI_SWAP4(pcTriangle[i].aiIndices[0]); + AI_SWAP4(pcTriangle[i].aiIndices[1]); + AI_SWAP4(pcTriangle[i].aiIndices[2]); + } + + // swap all vertices + for (unsigned int i = 0; i < pcSurface->ulNumVertices * pcSurface->ulNumBaseFrames; ++i) { + AI_SWAP2(pcVerts->normal); + AI_SWAP2(pcVerts->x); + AI_SWAP2(pcVerts->y); + AI_SWAP2(pcVerts->z); + } + + // swap all texture coordinates + for (unsigned int i = 0; i < pcSurface->ulNumVertices; ++i) { + AI_SWAP4(pcUVs->u); + AI_SWAP4(pcUVs->v); + } + +#endif + + const MDC::CompressedVertex *pcCVerts = nullptr; + int16_t *mdcCompVert = nullptr; + + // access compressed frames for large frame numbers, but never for the first + if (this->configFrameID && pcSurface->ulNumCompFrames > 0) { + mdcCompVert = (int16_t *)((int8_t *)pcSurface + pcSurface->ulOffsetFrameCompFrames) + this->configFrameID; + AI_SWAP2P(mdcCompVert); + if (*mdcCompVert >= 0) { + pcCVerts = (const MDC::CompressedVertex *)((int8_t *)pcSurface + + pcSurface->ulOffsetCompVerts) + + *mdcCompVert * pcSurface->ulNumVertices; + } else + mdcCompVert = nullptr; + } + + // copy all faces + for (unsigned int iFace = 0; iFace < pcSurface->ulNumTriangles; ++iFace, + ++pcTriangle, ++pcFaceCur) { + const unsigned int iOutIndex = iFace * 3; + pcFaceCur->mNumIndices = 3; + pcFaceCur->mIndices = new unsigned int[3]; + + for (unsigned int iIndex = 0; iIndex < 3; ++iIndex, + ++pcVertCur, ++pcUVCur, ++pcNorCur) { + uint32_t quak = pcTriangle->aiIndices[iIndex]; + if (quak >= pcSurface->ulNumVertices) { + ASSIMP_LOG_ERROR("MDC vertex index is out of range"); + quak = pcSurface->ulNumVertices - 1; + } + + // compressed vertices? + if (mdcCompVert) { + MDC::BuildVertex(*pcFrame, pcVerts[quak], pcCVerts[quak], + *pcVertCur, *pcNorCur); + } else { + // copy position + pcVertCur->x = pcVerts[quak].x * AI_MDC_BASE_SCALING; + pcVertCur->y = pcVerts[quak].y * AI_MDC_BASE_SCALING; + pcVertCur->z = pcVerts[quak].z * AI_MDC_BASE_SCALING; + + // copy normals + MD3::LatLngNormalToVec3(pcVerts[quak].normal, &pcNorCur->x); + + // copy texture coordinates + pcUVCur->x = pcUVs[quak].u; + pcUVCur->y = ai_real(1.0) - pcUVs[quak].v; // DX to OGL + } + pcVertCur->x += pcFrame->localOrigin[0]; + pcVertCur->y += pcFrame->localOrigin[1]; + pcVertCur->z += pcFrame->localOrigin[2]; + } + + // swap the face order - DX to OGL + pcFaceCur->mIndices[0] = iOutIndex + 2; + pcFaceCur->mIndices[1] = iOutIndex + 1; + pcFaceCur->mIndices[2] = iOutIndex + 0; + } + + pcSurface = reinterpret_cast<BE_NCONST MDC::Surface *>((BE_NCONST int8_t *)pcSurface + pcSurface->ulOffsetEnd); + } + + // create a flat node graph with a root node and one child for each surface + if (!pScene->mNumMeshes) + throw DeadlyImportError("Invalid MDC file: File contains no valid mesh"); + else if (1 == pScene->mNumMeshes) { + pScene->mRootNode = new aiNode(); + if (nullptr != pScene->mMeshes[0]) { + pScene->mRootNode->mName = pScene->mMeshes[0]->mName; + pScene->mRootNode->mNumMeshes = 1; + pScene->mRootNode->mMeshes = new unsigned int[1]; + pScene->mRootNode->mMeshes[0] = 0; + } + } else { + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mNumChildren = pScene->mNumMeshes; + pScene->mRootNode->mChildren = new aiNode *[pScene->mNumMeshes]; + pScene->mRootNode->mName.Set("<root>"); + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + aiNode *pcNode = pScene->mRootNode->mChildren[i] = new aiNode(); + pcNode->mParent = pScene->mRootNode; + pcNode->mName = pScene->mMeshes[i]->mName; + pcNode->mNumMeshes = 1; + pcNode->mMeshes = new unsigned int[1]; + pcNode->mMeshes[0] = i; + } + } + + // create materials + pScene->mNumMaterials = (unsigned int)aszShaders.size(); + pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; + for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) { + aiMaterial *pcMat = new aiMaterial(); + pScene->mMaterials[i] = pcMat; + + const std::string &name = aszShaders[i]; + + int iMode = (int)aiShadingMode_Gouraud; + pcMat->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + // add a small ambient color value - RtCW seems to have one + aiColor3D clr; + clr.b = clr.g = clr.r = 0.05f; + pcMat->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_AMBIENT); + + if (name.length()) + clr.b = clr.g = clr.r = 1.0f; + else + clr.b = clr.g = clr.r = 0.6f; + + pcMat->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_DIFFUSE); + pcMat->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_SPECULAR); + + if (name.length()) { + aiString path; + path.Set(name); + pcMat->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0)); + } + } + + // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system + pScene->mRootNode->mTransformation = aiMatrix4x4( + 1.f, 0.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, -1.f, 0.f, 0.f, + 0.f, 0.f, 0.f, 1.f); +} + +#endif // !! ASSIMP_BUILD_NO_MDC_IMPORTER |