diff options
Diffstat (limited to 'src/mesh/assimp-master/code/AssetLib/MDL/MDLLoader.cpp')
-rw-r--r-- | src/mesh/assimp-master/code/AssetLib/MDL/MDLLoader.cpp | 1976 |
1 files changed, 0 insertions, 1976 deletions
diff --git a/src/mesh/assimp-master/code/AssetLib/MDL/MDLLoader.cpp b/src/mesh/assimp-master/code/AssetLib/MDL/MDLLoader.cpp deleted file mode 100644 index 1e90c8e..0000000 --- a/src/mesh/assimp-master/code/AssetLib/MDL/MDLLoader.cpp +++ /dev/null @@ -1,1976 +0,0 @@ -/* ---------------------------------------------------------------------------- -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 MDLLoader.cpp - * @brief Implementation of the main parts of the MDL importer class - * *TODO* Cleanup and further testing of some parts necessary - */ - -// internal headers - -#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER - -#include "AssetLib/MDL/MDLLoader.h" -#include "AssetLib/MD2/MD2FileData.h" -#include "AssetLib/MDL/HalfLife/HL1MDLLoader.h" -#include "AssetLib/MDL/MDLDefaultColorMap.h" - -#include <assimp/StringUtils.h> -#include <assimp/importerdesc.h> -#include <assimp/qnan.h> -#include <assimp/scene.h> -#include <assimp/DefaultLogger.hpp> -#include <assimp/IOSystem.hpp> -#include <assimp/Importer.hpp> - -#include <memory> - -using namespace Assimp; - -static const aiImporterDesc desc = { - "Quake Mesh / 3D GameStudio Mesh Importer", - "", - "", - "", - aiImporterFlags_SupportBinaryFlavour, - 0, - 0, - 7, - 0, - "mdl" -}; - -// ------------------------------------------------------------------------------------------------ -// Ugly stuff ... nevermind -#define _AI_MDL7_ACCESS(_data, _index, _limit, _type) \ - (*((const _type *)(((const char *)_data) + _index * _limit))) - -#define _AI_MDL7_ACCESS_PTR(_data, _index, _limit, _type) \ - ((BE_NCONST _type *)(((const char *)_data) + _index * _limit)) - -#define _AI_MDL7_ACCESS_VERT(_data, _index, _limit) \ - _AI_MDL7_ACCESS(_data, _index, _limit, MDL::Vertex_MDL7) - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -MDLImporter::MDLImporter() : - configFrameID(), mBuffer(), iGSFileVersion(), mIOHandler(nullptr), pScene(), iFileSize() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -MDLImporter::~MDLImporter() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. -bool MDLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { - static const uint32_t tokens[] = { - AI_MDL_MAGIC_NUMBER_LE_HL2a, - AI_MDL_MAGIC_NUMBER_LE_HL2b, - AI_MDL_MAGIC_NUMBER_LE_GS7, - AI_MDL_MAGIC_NUMBER_LE_GS5b, - AI_MDL_MAGIC_NUMBER_LE_GS5a, - AI_MDL_MAGIC_NUMBER_LE_GS4, - AI_MDL_MAGIC_NUMBER_LE_GS3, - AI_MDL_MAGIC_NUMBER_LE - }; - return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); -} - -// ------------------------------------------------------------------------------------------------ -// Setup configuration properties -void MDLImporter::SetupProperties(const Importer *pImp) { - configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MDL_KEYFRAME, -1); - - // The - // AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the - // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. - if (static_cast<unsigned int>(-1) == configFrameID) { - configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME, 0); - } - - // AI_CONFIG_IMPORT_MDL_COLORMAP - palette file - configPalette = pImp->GetPropertyString(AI_CONFIG_IMPORT_MDL_COLORMAP, "colormap.lmp"); - - // Read configuration specific to MDL (Half-Life 1). - mHL1ImportSettings.read_animations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_ANIMATIONS, true); - if (mHL1ImportSettings.read_animations) { - mHL1ImportSettings.read_animation_events = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_ANIMATION_EVENTS, true); - mHL1ImportSettings.read_blend_controllers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_BLEND_CONTROLLERS, true); - mHL1ImportSettings.read_sequence_transitions = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_SEQUENCE_TRANSITIONS, true); - } - mHL1ImportSettings.read_attachments = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_ATTACHMENTS, true); - mHL1ImportSettings.read_bone_controllers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_BONE_CONTROLLERS, true); - mHL1ImportSettings.read_hitboxes = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_HITBOXES, true); - mHL1ImportSettings.read_misc_global_info = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_MISC_GLOBAL_INFO, true); -} - -// ------------------------------------------------------------------------------------------------ -// Get a list of all supported extensions -const aiImporterDesc *MDLImporter::GetInfo() const { - return &desc; -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void MDLImporter::InternReadFile(const std::string &pFile, - aiScene *_pScene, IOSystem *pIOHandler) { - pScene = _pScene; - mIOHandler = 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 MDL file ", pFile, "."); - } - - // This should work for all other types of MDL files, too ... - // the HL1 sequence group header is one of the smallest, afaik - iFileSize = (unsigned int)file->FileSize(); - if (iFileSize < sizeof(MDL::HalfLife::SequenceHeader_HL1)) { - throw DeadlyImportError("MDL File is too small."); - } - - // delete the file buffer and cleanup. - auto DeleteBufferAndCleanup = [&]() { - if (mBuffer) { - delete[] mBuffer; - mBuffer = nullptr; - } - AI_DEBUG_INVALIDATE_PTR(mIOHandler); - AI_DEBUG_INVALIDATE_PTR(pScene); - }; - - try { - // Allocate storage and copy the contents of the file to a memory buffer - mBuffer = new unsigned char[iFileSize + 1]; - file->Read((void *)mBuffer, 1, iFileSize); - - // Append a binary zero to the end of the buffer. - // this is just for safety that string parsing routines - // find the end of the buffer ... - mBuffer[iFileSize] = '\0'; - const uint32_t iMagicWord = *((uint32_t *)mBuffer); - - // Determine the file subtype and call the appropriate member function - bool is_half_life = false; - - // Original Quake1 format - if (AI_MDL_MAGIC_NUMBER_BE == iMagicWord || AI_MDL_MAGIC_NUMBER_LE == iMagicWord) { - ASSIMP_LOG_DEBUG("MDL subtype: Quake 1, magic word is IDPO"); - iGSFileVersion = 0; - InternReadFile_Quake1(); - } - // GameStudio A<old> MDL2 format - used by some test models that come with 3DGS - else if (AI_MDL_MAGIC_NUMBER_BE_GS3 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS3 == iMagicWord) { - ASSIMP_LOG_DEBUG("MDL subtype: 3D GameStudio A2, magic word is MDL2"); - iGSFileVersion = 2; - InternReadFile_Quake1(); - } - // GameStudio A4 MDL3 format - else if (AI_MDL_MAGIC_NUMBER_BE_GS4 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS4 == iMagicWord) { - ASSIMP_LOG_DEBUG("MDL subtype: 3D GameStudio A4, magic word is MDL3"); - iGSFileVersion = 3; - InternReadFile_3DGS_MDL345(); - } - // GameStudio A5+ MDL4 format - else if (AI_MDL_MAGIC_NUMBER_BE_GS5a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS5a == iMagicWord) { - ASSIMP_LOG_DEBUG("MDL subtype: 3D GameStudio A4, magic word is MDL4"); - iGSFileVersion = 4; - InternReadFile_3DGS_MDL345(); - } - // GameStudio A5+ MDL5 format - else if (AI_MDL_MAGIC_NUMBER_BE_GS5b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS5b == iMagicWord) { - ASSIMP_LOG_DEBUG("MDL subtype: 3D GameStudio A5, magic word is MDL5"); - iGSFileVersion = 5; - InternReadFile_3DGS_MDL345(); - } - // GameStudio A7 MDL7 format - else if (AI_MDL_MAGIC_NUMBER_BE_GS7 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS7 == iMagicWord) { - ASSIMP_LOG_DEBUG("MDL subtype: 3D GameStudio A7, magic word is MDL7"); - iGSFileVersion = 7; - InternReadFile_3DGS_MDL7(); - } - // IDST/IDSQ Format (CS:S/HL^2, etc ...) - else if (AI_MDL_MAGIC_NUMBER_BE_HL2a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2a == iMagicWord || - AI_MDL_MAGIC_NUMBER_BE_HL2b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2b == iMagicWord) { - iGSFileVersion = 0; - is_half_life = true; - - HalfLife::HalfLifeMDLBaseHeader *pHeader = (HalfLife::HalfLifeMDLBaseHeader *)mBuffer; - if (pHeader->version == AI_MDL_HL1_VERSION) { - ASSIMP_LOG_DEBUG("MDL subtype: Half-Life 1/Goldsrc Engine, magic word is IDST/IDSQ"); - InternReadFile_HL1(pFile, iMagicWord); - } else { - ASSIMP_LOG_DEBUG("MDL subtype: Source(tm) Engine, magic word is IDST/IDSQ"); - InternReadFile_HL2(); - } - } else { - // print the magic word to the log file - throw DeadlyImportError("Unknown MDL subformat ", pFile, - ". Magic word (", ai_str_toprintable((const char *)&iMagicWord, sizeof(iMagicWord)), ") is not known"); - } - - if (is_half_life){ - // Now rotate the whole scene 90 degrees around the z and x axes to convert to internal coordinate system - pScene->mRootNode->mTransformation = aiMatrix4x4( - 0.f, -1.f, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, - -1.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 0.f, 1.f); - } - else { - // 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); - } - - DeleteBufferAndCleanup(); - } catch (...) { - DeleteBufferAndCleanup(); - throw; - } -} - -// ------------------------------------------------------------------------------------------------ -// Check whether we're still inside the valid file range -void MDLImporter::SizeCheck(const void *szPos) { - if (!szPos || (const unsigned char *)szPos > this->mBuffer + this->iFileSize) { - throw DeadlyImportError("Invalid MDL file. The file is too small " - "or contains invalid data."); - } -} - -// ------------------------------------------------------------------------------------------------ -// Just for debugging purposes -void MDLImporter::SizeCheck(const void *szPos, const char *szFile, unsigned int iLine) { - ai_assert(nullptr != szFile); - if (!szPos || (const unsigned char *)szPos > mBuffer + iFileSize) { - // remove a directory if there is one - const char *szFilePtr = ::strrchr(szFile, '\\'); - if (!szFilePtr) { - szFilePtr = ::strrchr(szFile, '/'); - if (nullptr == szFilePtr) { - szFilePtr = szFile; - } - } - if (szFilePtr) { - ++szFilePtr; - } - - char szBuffer[1024]; - ::sprintf(szBuffer, "Invalid MDL file. The file is too small " - "or contains invalid data (File: %s Line: %u)", - szFilePtr, iLine); - - throw DeadlyImportError(szBuffer); - } -} - -// ------------------------------------------------------------------------------------------------ -// Validate a quake file header -void MDLImporter::ValidateHeader_Quake1(const MDL::Header *pcHeader) { - // some values may not be nullptr - if (!pcHeader->num_frames) - throw DeadlyImportError("[Quake 1 MDL] There are no frames in the file"); - - if (!pcHeader->num_verts) - throw DeadlyImportError("[Quake 1 MDL] There are no vertices in the file"); - - if (!pcHeader->num_tris) - throw DeadlyImportError("[Quake 1 MDL] There are no triangles in the file"); - - // check whether the maxima are exceeded ...however, this applies for Quake 1 MDLs only - if (!this->iGSFileVersion) { - if (pcHeader->num_verts > AI_MDL_MAX_VERTS) - ASSIMP_LOG_WARN("Quake 1 MDL model has more than AI_MDL_MAX_VERTS vertices"); - - if (pcHeader->num_tris > AI_MDL_MAX_TRIANGLES) - ASSIMP_LOG_WARN("Quake 1 MDL model has more than AI_MDL_MAX_TRIANGLES triangles"); - - if (pcHeader->num_frames > AI_MDL_MAX_FRAMES) - ASSIMP_LOG_WARN("Quake 1 MDL model has more than AI_MDL_MAX_FRAMES frames"); - - // (this does not apply for 3DGS MDLs) - if (!this->iGSFileVersion && pcHeader->version != AI_MDL_VERSION) - ASSIMP_LOG_WARN("Quake 1 MDL model has an unknown version: AI_MDL_VERSION (=6) is " - "the expected file format version"); - if (pcHeader->num_skins && (!pcHeader->skinwidth || !pcHeader->skinheight)) - ASSIMP_LOG_WARN("Skin width or height are 0"); - } -} - -#ifdef AI_BUILD_BIG_ENDIAN -// ------------------------------------------------------------------------------------------------ -void FlipQuakeHeader(BE_NCONST MDL::Header *pcHeader) { - AI_SWAP4(pcHeader->ident); - AI_SWAP4(pcHeader->version); - AI_SWAP4(pcHeader->boundingradius); - AI_SWAP4(pcHeader->flags); - AI_SWAP4(pcHeader->num_frames); - AI_SWAP4(pcHeader->num_skins); - AI_SWAP4(pcHeader->num_tris); - AI_SWAP4(pcHeader->num_verts); - for (unsigned int i = 0; i < 3; ++i) { - AI_SWAP4(pcHeader->scale[i]); - AI_SWAP4(pcHeader->translate[i]); - } - AI_SWAP4(pcHeader->size); - AI_SWAP4(pcHeader->skinheight); - AI_SWAP4(pcHeader->skinwidth); - AI_SWAP4(pcHeader->synctype); -} -#endif - -// ------------------------------------------------------------------------------------------------ -// Read a Quake 1 file -void MDLImporter::InternReadFile_Quake1() { - ai_assert(nullptr != pScene); - - BE_NCONST MDL::Header *pcHeader = (BE_NCONST MDL::Header *)this->mBuffer; - -#ifdef AI_BUILD_BIG_ENDIAN - FlipQuakeHeader(pcHeader); -#endif - - ValidateHeader_Quake1(pcHeader); - - // current cursor position in the file - const unsigned char *szCurrent = (const unsigned char *)(pcHeader + 1); - - // need to read all textures - for (unsigned int i = 0; i < (unsigned int)pcHeader->num_skins; ++i) { - union { - BE_NCONST MDL::Skin *pcSkin; - BE_NCONST MDL::GroupSkin *pcGroupSkin; - }; - if (szCurrent + sizeof(MDL::Skin) > this->mBuffer + this->iFileSize) { - throw DeadlyImportError("[Quake 1 MDL] Unexpected EOF"); - } - pcSkin = (BE_NCONST MDL::Skin *)szCurrent; - - AI_SWAP4(pcSkin->group); - - // Quake 1 group-skins - if (1 == pcSkin->group) { - AI_SWAP4(pcGroupSkin->nb); - - // need to skip multiple images - const unsigned int iNumImages = (unsigned int)pcGroupSkin->nb; - szCurrent += sizeof(uint32_t) * 2; - - if (0 != iNumImages) { - if (!i) { - // however, create only one output image (the first) - this->CreateTextureARGB8_3DGS_MDL3(szCurrent + iNumImages * sizeof(float)); - } - // go to the end of the skin section / the beginning of the next skin - szCurrent += pcHeader->skinheight * pcHeader->skinwidth + - sizeof(float) * iNumImages; - } - } else { - szCurrent += sizeof(uint32_t); - unsigned int iSkip = i ? UINT_MAX : 0; - CreateTexture_3DGS_MDL4(szCurrent, pcSkin->group, &iSkip); - szCurrent += iSkip; - } - } - // get a pointer to the texture coordinates - BE_NCONST MDL::TexCoord *pcTexCoords = (BE_NCONST MDL::TexCoord *)szCurrent; - szCurrent += sizeof(MDL::TexCoord) * pcHeader->num_verts; - - // get a pointer to the triangles - BE_NCONST MDL::Triangle *pcTriangles = (BE_NCONST MDL::Triangle *)szCurrent; - szCurrent += sizeof(MDL::Triangle) * pcHeader->num_tris; - VALIDATE_FILE_SIZE(szCurrent); - - // now get a pointer to the first frame in the file - BE_NCONST MDL::Frame *pcFrames = (BE_NCONST MDL::Frame *)szCurrent; - MDL::SimpleFrame *pcFirstFrame; - - if (0 == pcFrames->type) { - // get address of single frame - pcFirstFrame = (MDL::SimpleFrame *)&pcFrames->frame; - } else { - // get the first frame in the group - BE_NCONST MDL::GroupFrame *pcFrames2 = (BE_NCONST MDL::GroupFrame *)szCurrent; - pcFirstFrame = (MDL::SimpleFrame *)( szCurrent + sizeof(MDL::GroupFrame::type) + sizeof(MDL::GroupFrame::numframes) - + sizeof(MDL::GroupFrame::min) + sizeof(MDL::GroupFrame::max) + sizeof(*MDL::GroupFrame::times) * pcFrames2->numframes ); - } - BE_NCONST MDL::Vertex *pcVertices = (BE_NCONST MDL::Vertex *)((pcFirstFrame->name) + sizeof(pcFirstFrame->name)); - VALIDATE_FILE_SIZE((const unsigned char *)(pcVertices + pcHeader->num_verts)); - -#ifdef AI_BUILD_BIG_ENDIAN - for (int i = 0; i < pcHeader->num_verts; ++i) { - AI_SWAP4(pcTexCoords[i].onseam); - AI_SWAP4(pcTexCoords[i].s); - AI_SWAP4(pcTexCoords[i].t); - } - - for (int i = 0; i < pcHeader->num_tris; ++i) { - AI_SWAP4(pcTriangles[i].facesfront); - AI_SWAP4(pcTriangles[i].vertex[0]); - AI_SWAP4(pcTriangles[i].vertex[1]); - AI_SWAP4(pcTriangles[i].vertex[2]); - } -#endif - - // setup materials - SetupMaterialProperties_3DGS_MDL5_Quake1(); - - // allocate enough storage to hold all vertices and triangles - aiMesh *pcMesh = new aiMesh(); - - pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - pcMesh->mNumVertices = pcHeader->num_tris * 3; - pcMesh->mNumFaces = pcHeader->num_tris; - pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; - pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; - pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; - pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; - pcMesh->mNumUVComponents[0] = 2; - - // there won't be more than one mesh inside the file - pScene->mRootNode = new aiNode(); - pScene->mRootNode->mNumMeshes = 1; - pScene->mRootNode->mMeshes = new unsigned int[1]; - pScene->mRootNode->mMeshes[0] = 0; - pScene->mNumMeshes = 1; - pScene->mMeshes = new aiMesh *[1]; - pScene->mMeshes[0] = pcMesh; - - // now iterate through all triangles - unsigned int iCurrent = 0; - for (unsigned int i = 0; i < (unsigned int)pcHeader->num_tris; ++i) { - pcMesh->mFaces[i].mIndices = new unsigned int[3]; - pcMesh->mFaces[i].mNumIndices = 3; - - unsigned int iTemp = iCurrent; - for (unsigned int c = 0; c < 3; ++c, ++iCurrent) { - pcMesh->mFaces[i].mIndices[c] = iCurrent; - - // read vertices - unsigned int iIndex = pcTriangles->vertex[c]; - if (iIndex >= (unsigned int)pcHeader->num_verts) { - iIndex = pcHeader->num_verts - 1; - ASSIMP_LOG_WARN("Index overflow in Q1-MDL vertex list."); - } - - aiVector3D &vec = pcMesh->mVertices[iCurrent]; - vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0]; - vec.x += pcHeader->translate[0]; - - vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1]; - vec.y += pcHeader->translate[1]; - //vec.y *= -1.0f; - - vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2]; - vec.z += pcHeader->translate[2]; - - // read the normal vector from the precalculated normal table - MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex, pcMesh->mNormals[iCurrent]); - //pcMesh->mNormals[iCurrent].y *= -1.0f; - - // read texture coordinates - float s = (float)pcTexCoords[iIndex].s; - float t = (float)pcTexCoords[iIndex].t; - - // translate texture coordinates - if (0 == pcTriangles->facesfront && 0 != pcTexCoords[iIndex].onseam) { - s += pcHeader->skinwidth * 0.5f; - } - - // Scale s and t to range from 0.0 to 1.0 - pcMesh->mTextureCoords[0][iCurrent].x = (s + 0.5f) / pcHeader->skinwidth; - pcMesh->mTextureCoords[0][iCurrent].y = 1.0f - (t + 0.5f) / pcHeader->skinheight; - } - pcMesh->mFaces[i].mIndices[0] = iTemp + 2; - pcMesh->mFaces[i].mIndices[1] = iTemp + 1; - pcMesh->mFaces[i].mIndices[2] = iTemp + 0; - pcTriangles++; - } - return; -} - -// ------------------------------------------------------------------------------------------------ -// Setup material properties for Quake and older GameStudio files -void MDLImporter::SetupMaterialProperties_3DGS_MDL5_Quake1() { - const MDL::Header *const pcHeader = (const MDL::Header *)this->mBuffer; - - // allocate ONE material - pScene->mMaterials = new aiMaterial *[1]; - pScene->mMaterials[0] = new aiMaterial(); - pScene->mNumMaterials = 1; - - // setup the material's properties - const int iMode = (int)aiShadingMode_Gouraud; - aiMaterial *const pcHelper = (aiMaterial *)pScene->mMaterials[0]; - pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); - - aiColor4D clr; - if (0 != pcHeader->num_skins && pScene->mNumTextures) { - // can we replace the texture with a single color? - clr = this->ReplaceTextureWithColor(pScene->mTextures[0]); - if (is_not_qnan(clr.r)) { - delete pScene->mTextures[0]; - delete[] pScene->mTextures; - - pScene->mTextures = nullptr; - pScene->mNumTextures = 0; - } else { - clr.b = clr.a = clr.g = clr.r = 1.0f; - aiString szString; - ::memcpy(szString.data, AI_MAKE_EMBEDDED_TEXNAME(0), 3); - szString.length = 2; - pcHelper->AddProperty(&szString, AI_MATKEY_TEXTURE_DIFFUSE(0)); - } - } - - pcHelper->AddProperty<aiColor4D>(&clr, 1, AI_MATKEY_COLOR_DIFFUSE); - pcHelper->AddProperty<aiColor4D>(&clr, 1, AI_MATKEY_COLOR_SPECULAR); - - clr.r *= 0.05f; - clr.g *= 0.05f; - clr.b *= 0.05f; - clr.a = 1.0f; - pcHelper->AddProperty<aiColor4D>(&clr, 1, AI_MATKEY_COLOR_AMBIENT); -} - -// ------------------------------------------------------------------------------------------------ -// Read a MDL 3,4,5 file -void MDLImporter::InternReadFile_3DGS_MDL345() { - ai_assert(nullptr != pScene); - - // the header of MDL 3/4/5 is nearly identical to the original Quake1 header - BE_NCONST MDL::Header *pcHeader = (BE_NCONST MDL::Header *)this->mBuffer; -#ifdef AI_BUILD_BIG_ENDIAN - FlipQuakeHeader(pcHeader); -#endif - ValidateHeader_Quake1(pcHeader); - - // current cursor position in the file - const unsigned char *szCurrent = (const unsigned char *)(pcHeader + 1); - const unsigned char *szEnd = mBuffer + iFileSize; - - // need to read all textures - for (unsigned int i = 0; i < (unsigned int)pcHeader->num_skins; ++i) { - if (szCurrent + sizeof(uint32_t) > szEnd) { - throw DeadlyImportError("Texture data past end of file."); - } - BE_NCONST MDL::Skin *pcSkin; - pcSkin = (BE_NCONST MDL::Skin *)szCurrent; - AI_SWAP4(pcSkin->group); - // create one output image - unsigned int iSkip = i ? UINT_MAX : 0; - if (5 <= iGSFileVersion) { - // MDL5 format could contain MIPmaps - CreateTexture_3DGS_MDL5((unsigned char *)pcSkin + sizeof(uint32_t), - pcSkin->group, &iSkip); - } else { - CreateTexture_3DGS_MDL4((unsigned char *)pcSkin + sizeof(uint32_t), - pcSkin->group, &iSkip); - } - // need to skip one image - szCurrent += iSkip + sizeof(uint32_t); - } - // get a pointer to the texture coordinates - BE_NCONST MDL::TexCoord_MDL3 *pcTexCoords = (BE_NCONST MDL::TexCoord_MDL3 *)szCurrent; - szCurrent += sizeof(MDL::TexCoord_MDL3) * pcHeader->synctype; - - // NOTE: for MDLn formats "synctype" corresponds to the number of UV coords - - // get a pointer to the triangles - BE_NCONST MDL::Triangle_MDL3 *pcTriangles = (BE_NCONST MDL::Triangle_MDL3 *)szCurrent; - szCurrent += sizeof(MDL::Triangle_MDL3) * pcHeader->num_tris; - -#ifdef AI_BUILD_BIG_ENDIAN - - for (int i = 0; i < pcHeader->synctype; ++i) { - AI_SWAP2(pcTexCoords[i].u); - AI_SWAP2(pcTexCoords[i].v); - } - - for (int i = 0; i < pcHeader->num_tris; ++i) { - AI_SWAP2(pcTriangles[i].index_xyz[0]); - AI_SWAP2(pcTriangles[i].index_xyz[1]); - AI_SWAP2(pcTriangles[i].index_xyz[2]); - AI_SWAP2(pcTriangles[i].index_uv[0]); - AI_SWAP2(pcTriangles[i].index_uv[1]); - AI_SWAP2(pcTriangles[i].index_uv[2]); - } - -#endif - - VALIDATE_FILE_SIZE(szCurrent); - - // setup materials - SetupMaterialProperties_3DGS_MDL5_Quake1(); - - // allocate enough storage to hold all vertices and triangles - aiMesh *pcMesh = new aiMesh(); - pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - pcMesh->mNumVertices = pcHeader->num_tris * 3; - pcMesh->mNumFaces = pcHeader->num_tris; - pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; - - // there won't be more than one mesh inside the file - pScene->mRootNode = new aiNode(); - pScene->mRootNode->mNumMeshes = 1; - pScene->mRootNode->mMeshes = new unsigned int[1]; - pScene->mRootNode->mMeshes[0] = 0; - pScene->mNumMeshes = 1; - pScene->mMeshes = new aiMesh *[1]; - pScene->mMeshes[0] = pcMesh; - - // allocate output storage - pcMesh->mNumVertices = (unsigned int)pcHeader->num_tris * 3; - pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; - pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; - - if (pcHeader->synctype) { - pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; - pcMesh->mNumUVComponents[0] = 2; - } - - // now get a pointer to the first frame in the file - BE_NCONST MDL::Frame *pcFrames = (BE_NCONST MDL::Frame *)szCurrent; - AI_SWAP4(pcFrames->type); - - // byte packed vertices - // FIXME: these two snippets below are almost identical ... join them? - ///////////////////////////////////////////////////////////////////////////////////// - if (0 == pcFrames->type || 3 >= this->iGSFileVersion) { - - const MDL::SimpleFrame *pcFirstFrame = (const MDL::SimpleFrame *)(szCurrent + sizeof(uint32_t)); - const MDL::Vertex *pcVertices = (const MDL::Vertex *)((pcFirstFrame->name) + sizeof(pcFirstFrame->name)); - - VALIDATE_FILE_SIZE(pcVertices + pcHeader->num_verts); - - // now iterate through all triangles - unsigned int iCurrent = 0; - for (unsigned int i = 0; i < (unsigned int)pcHeader->num_tris; ++i) { - pcMesh->mFaces[i].mIndices = new unsigned int[3]; - pcMesh->mFaces[i].mNumIndices = 3; - - unsigned int iTemp = iCurrent; - for (unsigned int c = 0; c < 3; ++c, ++iCurrent) { - // read vertices - unsigned int iIndex = pcTriangles->index_xyz[c]; - if (iIndex >= (unsigned int)pcHeader->num_verts) { - iIndex = pcHeader->num_verts - 1; - ASSIMP_LOG_WARN("Index overflow in MDLn vertex list"); - } - - aiVector3D &vec = pcMesh->mVertices[iCurrent]; - vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0]; - vec.x += pcHeader->translate[0]; - - vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1]; - vec.y += pcHeader->translate[1]; - // vec.y *= -1.0f; - - vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2]; - vec.z += pcHeader->translate[2]; - - // read the normal vector from the precalculated normal table - MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex, pcMesh->mNormals[iCurrent]); - // pcMesh->mNormals[iCurrent].y *= -1.0f; - - // read texture coordinates - if (pcHeader->synctype) { - ImportUVCoordinate_3DGS_MDL345(pcMesh->mTextureCoords[0][iCurrent], - pcTexCoords, pcTriangles->index_uv[c]); - } - } - pcMesh->mFaces[i].mIndices[0] = iTemp + 2; - pcMesh->mFaces[i].mIndices[1] = iTemp + 1; - pcMesh->mFaces[i].mIndices[2] = iTemp + 0; - pcTriangles++; - } - - } - // short packed vertices - ///////////////////////////////////////////////////////////////////////////////////// - else { - // now get a pointer to the first frame in the file - const MDL::SimpleFrame_MDLn_SP *pcFirstFrame = (const MDL::SimpleFrame_MDLn_SP *)(szCurrent + sizeof(uint32_t)); - - // get a pointer to the vertices - const MDL::Vertex_MDL4 *pcVertices = (const MDL::Vertex_MDL4 *)((pcFirstFrame->name) + - sizeof(pcFirstFrame->name)); - - VALIDATE_FILE_SIZE(pcVertices + pcHeader->num_verts); - - // now iterate through all triangles - unsigned int iCurrent = 0; - for (unsigned int i = 0; i < (unsigned int)pcHeader->num_tris; ++i) { - pcMesh->mFaces[i].mIndices = new unsigned int[3]; - pcMesh->mFaces[i].mNumIndices = 3; - - unsigned int iTemp = iCurrent; - for (unsigned int c = 0; c < 3; ++c, ++iCurrent) { - // read vertices - unsigned int iIndex = pcTriangles->index_xyz[c]; - if (iIndex >= (unsigned int)pcHeader->num_verts) { - iIndex = pcHeader->num_verts - 1; - ASSIMP_LOG_WARN("Index overflow in MDLn vertex list"); - } - - aiVector3D &vec = pcMesh->mVertices[iCurrent]; - vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0]; - vec.x += pcHeader->translate[0]; - - vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1]; - vec.y += pcHeader->translate[1]; - // vec.y *= -1.0f; - - vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2]; - vec.z += pcHeader->translate[2]; - - // read the normal vector from the precalculated normal table - MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex, pcMesh->mNormals[iCurrent]); - // pcMesh->mNormals[iCurrent].y *= -1.0f; - - // read texture coordinates - if (pcHeader->synctype) { - ImportUVCoordinate_3DGS_MDL345(pcMesh->mTextureCoords[0][iCurrent], - pcTexCoords, pcTriangles->index_uv[c]); - } - } - pcMesh->mFaces[i].mIndices[0] = iTemp + 2; - pcMesh->mFaces[i].mIndices[1] = iTemp + 1; - pcMesh->mFaces[i].mIndices[2] = iTemp + 0; - pcTriangles++; - } - } - - // For MDL5 we will need to build valid texture coordinates - // basing upon the file loaded (only support one file as skin) - if (0x5 == iGSFileVersion) - CalculateUVCoordinates_MDL5(); - return; -} - -// ------------------------------------------------------------------------------------------------ -// Get a single UV coordinate for Quake and older GameStudio files -void MDLImporter::ImportUVCoordinate_3DGS_MDL345( - aiVector3D &vOut, - const MDL::TexCoord_MDL3 *pcSrc, - unsigned int iIndex) { - ai_assert(nullptr != pcSrc); - const MDL::Header *const pcHeader = (const MDL::Header *)this->mBuffer; - - // validate UV indices - if (iIndex >= (unsigned int)pcHeader->synctype) { - iIndex = pcHeader->synctype - 1; - ASSIMP_LOG_WARN("Index overflow in MDLn UV coord list"); - } - - float s = (float)pcSrc[iIndex].u; - float t = (float)pcSrc[iIndex].v; - - // Scale s and t to range from 0.0 to 1.0 - if (0x5 != iGSFileVersion) { - s = (s + 0.5f) / pcHeader->skinwidth; - t = 1.0f - (t + 0.5f) / pcHeader->skinheight; - } - - vOut.x = s; - vOut.y = t; - vOut.z = 0.0f; -} - -// ------------------------------------------------------------------------------------------------ -// Compute UV coordinates for a MDL5 file -void MDLImporter::CalculateUVCoordinates_MDL5() { - const MDL::Header *const pcHeader = (const MDL::Header *)this->mBuffer; - if (pcHeader->num_skins && this->pScene->mNumTextures) { - const aiTexture *pcTex = this->pScene->mTextures[0]; - - // if the file is loaded in DDS format: get the size of the - // texture from the header of the DDS file - // skip three DWORDs and read first height, then the width - unsigned int iWidth, iHeight; - if (!pcTex->mHeight) { - const uint32_t *piPtr = (uint32_t *)pcTex->pcData; - - piPtr += 3; - iHeight = (unsigned int)*piPtr++; - iWidth = (unsigned int)*piPtr; - if (!iHeight || !iWidth) { - ASSIMP_LOG_WARN("Either the width or the height of the " - "embedded DDS texture is zero. Unable to compute final texture " - "coordinates. The texture coordinates remain in their original " - "0-x/0-y (x,y = texture size) range."); - iWidth = 1; - iHeight = 1; - } - } else { - iWidth = pcTex->mWidth; - iHeight = pcTex->mHeight; - } - - if (1 != iWidth || 1 != iHeight) { - const float fWidth = (float)iWidth; - const float fHeight = (float)iHeight; - aiMesh *pcMesh = this->pScene->mMeshes[0]; - for (unsigned int i = 0; i < pcMesh->mNumVertices; ++i) { - pcMesh->mTextureCoords[0][i].x /= fWidth; - pcMesh->mTextureCoords[0][i].y /= fHeight; - pcMesh->mTextureCoords[0][i].y = 1.0f - pcMesh->mTextureCoords[0][i].y; // DX to OGL - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Validate the header of a MDL7 file -void MDLImporter::ValidateHeader_3DGS_MDL7(const MDL::Header_MDL7 *pcHeader) { - ai_assert(nullptr != pcHeader); - - // There are some fixed sizes ... - if (sizeof(MDL::ColorValue_MDL7) != pcHeader->colorvalue_stc_size) { - throw DeadlyImportError( - "[3DGS MDL7] sizeof(MDL::ColorValue_MDL7) != pcHeader->colorvalue_stc_size"); - } - if (sizeof(MDL::TexCoord_MDL7) != pcHeader->skinpoint_stc_size) { - throw DeadlyImportError( - "[3DGS MDL7] sizeof(MDL::TexCoord_MDL7) != pcHeader->skinpoint_stc_size"); - } - if (sizeof(MDL::Skin_MDL7) != pcHeader->skin_stc_size) { - throw DeadlyImportError( - "sizeof(MDL::Skin_MDL7) != pcHeader->skin_stc_size"); - } - - // if there are no groups ... how should we load such a file? - if (!pcHeader->groups_num) { - throw DeadlyImportError("[3DGS MDL7] No frames found"); - } -} - -// ------------------------------------------------------------------------------------------------ -// resolve bone animation matrices -void MDLImporter::CalcAbsBoneMatrices_3DGS_MDL7(MDL::IntBone_MDL7 **apcOutBones) { - const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7 *)this->mBuffer; - const MDL::Bone_MDL7 *pcBones = (const MDL::Bone_MDL7 *)(pcHeader + 1); - ai_assert(nullptr != apcOutBones); - - // first find the bone that has NO parent, calculate the - // animation matrix for it, then go on and search for the next parent - // index (0) and so on until we can't find a new node. - uint16_t iParent = 0xffff; - uint32_t iIterations = 0; - while (iIterations++ < pcHeader->bones_num) { - for (uint32_t iBone = 0; iBone < pcHeader->bones_num; ++iBone) { - BE_NCONST MDL::Bone_MDL7 *pcBone = _AI_MDL7_ACCESS_PTR(pcBones, iBone, - pcHeader->bone_stc_size, MDL::Bone_MDL7); - - AI_SWAP2(pcBone->parent_index); - AI_SWAP4(pcBone->x); - AI_SWAP4(pcBone->y); - AI_SWAP4(pcBone->z); - - if (iParent == pcBone->parent_index) { - // MDL7 readme - //////////////////////////////////////////////////////////////// - /* - The animation matrix is then calculated the following way: - - vector3 bPos = <absolute bone position> - matrix44 laM; // local animation matrix - sphrvector key_rotate = <bone rotation> - - matrix44 m1,m2; - create_trans_matrix(m1, -bPos.x, -bPos.y, -bPos.z); - create_trans_matrix(m2, -bPos.x, -bPos.y, -bPos.z); - - create_rotation_matrix(laM,key_rotate); - - laM = sm1 * laM; - laM = laM * sm2; - */ - ///////////////////////////////////////////////////////////////// - - MDL::IntBone_MDL7 *const pcOutBone = apcOutBones[iBone]; - - // store the parent index of the bone - pcOutBone->iParent = pcBone->parent_index; - if (0xffff != iParent) { - const MDL::IntBone_MDL7 *pcParentBone = apcOutBones[iParent]; - pcOutBone->mOffsetMatrix.a4 = -pcParentBone->vPosition.x; - pcOutBone->mOffsetMatrix.b4 = -pcParentBone->vPosition.y; - pcOutBone->mOffsetMatrix.c4 = -pcParentBone->vPosition.z; - } - pcOutBone->vPosition.x = pcBone->x; - pcOutBone->vPosition.y = pcBone->y; - pcOutBone->vPosition.z = pcBone->z; - pcOutBone->mOffsetMatrix.a4 -= pcBone->x; - pcOutBone->mOffsetMatrix.b4 -= pcBone->y; - pcOutBone->mOffsetMatrix.c4 -= pcBone->z; - - if (AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE == pcHeader->bone_stc_size) { - // no real name for our poor bone is specified :-( - pcOutBone->mName.length = ai_snprintf(pcOutBone->mName.data, MAXLEN, - "UnnamedBone_%i", iBone); - } else { - // Make sure we won't run over the buffer's end if there is no - // terminal 0 character (however the documentation says there - // should be one) - uint32_t iMaxLen = pcHeader->bone_stc_size - 16; - for (uint32_t qq = 0; qq < iMaxLen; ++qq) { - if (!pcBone->name[qq]) { - iMaxLen = qq; - break; - } - } - - // store the name of the bone - pcOutBone->mName.length = (size_t)iMaxLen; - ::memcpy(pcOutBone->mName.data, pcBone->name, pcOutBone->mName.length); - pcOutBone->mName.data[pcOutBone->mName.length] = '\0'; - } - } - } - ++iParent; - } -} - -// ------------------------------------------------------------------------------------------------ -// read bones from a MDL7 file -MDL::IntBone_MDL7 **MDLImporter::LoadBones_3DGS_MDL7() { - const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7 *)this->mBuffer; - if (pcHeader->bones_num) { - // validate the size of the bone data structure in the file - if (AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS != pcHeader->bone_stc_size && - AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_32_CHARS != pcHeader->bone_stc_size && - AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE != pcHeader->bone_stc_size) { - ASSIMP_LOG_WARN("Unknown size of bone data structure"); - return nullptr; - } - - MDL::IntBone_MDL7 **apcBonesOut = new MDL::IntBone_MDL7 *[pcHeader->bones_num]; - for (uint32_t crank = 0; crank < pcHeader->bones_num; ++crank) - apcBonesOut[crank] = new MDL::IntBone_MDL7(); - - // and calculate absolute bone offset matrices ... - CalcAbsBoneMatrices_3DGS_MDL7(apcBonesOut); - return apcBonesOut; - } - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -// read faces from a MDL7 file -void MDLImporter::ReadFaces_3DGS_MDL7(const MDL::IntGroupInfo_MDL7 &groupInfo, - MDL::IntGroupData_MDL7 &groupData) { - const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7 *)this->mBuffer; - MDL::Triangle_MDL7 *pcGroupTris = groupInfo.pcGroupTris; - - // iterate through all triangles and build valid display lists - unsigned int iOutIndex = 0; - for (unsigned int iTriangle = 0; iTriangle < (unsigned int)groupInfo.pcGroup->numtris; ++iTriangle) { - AI_SWAP2(pcGroupTris->v_index[0]); - AI_SWAP2(pcGroupTris->v_index[1]); - AI_SWAP2(pcGroupTris->v_index[2]); - - // iterate through all indices of the current triangle - for (unsigned int c = 0; c < 3; ++c, ++iOutIndex) { - - // validate the vertex index - unsigned int iIndex = pcGroupTris->v_index[c]; - if (iIndex > (unsigned int)groupInfo.pcGroup->numverts) { - // (we might need to read this section a second time - to process frame vertices correctly) - pcGroupTris->v_index[c] = (uint16_t)(iIndex = groupInfo.pcGroup->numverts - 1); - ASSIMP_LOG_WARN("Index overflow in MDL7 vertex list"); - } - - // write the output face index - groupData.pcFaces[iTriangle].mIndices[2 - c] = iOutIndex; - - aiVector3D &vPosition = groupData.vPositions[iOutIndex]; - vPosition.x = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts, iIndex, pcHeader->mainvertex_stc_size).x; - vPosition.y = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts, iIndex, pcHeader->mainvertex_stc_size).y; - vPosition.z = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts, iIndex, pcHeader->mainvertex_stc_size).z; - - // if we have bones, save the index - if (!groupData.aiBones.empty()) { - groupData.aiBones[iOutIndex] = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts, - iIndex, pcHeader->mainvertex_stc_size) - .vertindex; - } - - // now read the normal vector - if (AI_MDL7_FRAMEVERTEX030305_STCSIZE <= pcHeader->mainvertex_stc_size) { - // read the full normal vector - aiVector3D &vNormal = groupData.vNormals[iOutIndex]; - vNormal.x = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts, iIndex, pcHeader->mainvertex_stc_size).norm[0]; - AI_SWAP4(vNormal.x); - vNormal.y = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts, iIndex, pcHeader->mainvertex_stc_size).norm[1]; - AI_SWAP4(vNormal.y); - vNormal.z = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts, iIndex, pcHeader->mainvertex_stc_size).norm[2]; - AI_SWAP4(vNormal.z); - } else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size) { - // read the normal vector from Quake2's smart table - aiVector3D &vNormal = groupData.vNormals[iOutIndex]; - MD2::LookupNormalIndex(_AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts, iIndex, - pcHeader->mainvertex_stc_size) - .norm162index, - vNormal); - } - // validate and process the first uv coordinate set - if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV) { - - if (groupInfo.pcGroup->num_stpts) { - AI_SWAP2(pcGroupTris->skinsets[0].st_index[0]); - AI_SWAP2(pcGroupTris->skinsets[0].st_index[1]); - AI_SWAP2(pcGroupTris->skinsets[0].st_index[2]); - - iIndex = pcGroupTris->skinsets[0].st_index[c]; - if (iIndex > (unsigned int)groupInfo.pcGroup->num_stpts) { - iIndex = groupInfo.pcGroup->num_stpts - 1; - ASSIMP_LOG_WARN("Index overflow in MDL7 UV coordinate list (#1)"); - } - - float u = groupInfo.pcGroupUVs[iIndex].u; - float v = 1.0f - groupInfo.pcGroupUVs[iIndex].v; // DX to OGL - - groupData.vTextureCoords1[iOutIndex].x = u; - groupData.vTextureCoords1[iOutIndex].y = v; - } - // assign the material index, but only if it is existing - if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV_WITH_MATINDEX) { - AI_SWAP4(pcGroupTris->skinsets[0].material); - groupData.pcFaces[iTriangle].iMatIndex[0] = pcGroupTris->skinsets[0].material; - } - } - // validate and process the second uv coordinate set - if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV) { - - if (groupInfo.pcGroup->num_stpts) { - AI_SWAP2(pcGroupTris->skinsets[1].st_index[0]); - AI_SWAP2(pcGroupTris->skinsets[1].st_index[1]); - AI_SWAP2(pcGroupTris->skinsets[1].st_index[2]); - AI_SWAP4(pcGroupTris->skinsets[1].material); - - iIndex = pcGroupTris->skinsets[1].st_index[c]; - if (iIndex > (unsigned int)groupInfo.pcGroup->num_stpts) { - iIndex = groupInfo.pcGroup->num_stpts - 1; - ASSIMP_LOG_WARN("Index overflow in MDL7 UV coordinate list (#2)"); - } - - float u = groupInfo.pcGroupUVs[iIndex].u; - float v = 1.0f - groupInfo.pcGroupUVs[iIndex].v; - - groupData.vTextureCoords2[iOutIndex].x = u; - groupData.vTextureCoords2[iOutIndex].y = v; // DX to OGL - - // check whether we do really need the second texture - // coordinate set ... wastes memory and loading time - if (0 != iIndex && (u != groupData.vTextureCoords1[iOutIndex].x || - v != groupData.vTextureCoords1[iOutIndex].y)) - groupData.bNeed2UV = true; - - // if the material differs, we need a second skin, too - if (pcGroupTris->skinsets[1].material != pcGroupTris->skinsets[0].material) - groupData.bNeed2UV = true; - } - // assign the material index - groupData.pcFaces[iTriangle].iMatIndex[1] = pcGroupTris->skinsets[1].material; - } - } - // get the next triangle in the list - pcGroupTris = (MDL::Triangle_MDL7 *)((const char *)pcGroupTris + pcHeader->triangle_stc_size); - } -} - -// ------------------------------------------------------------------------------------------------ -// handle frames in a MDL7 file -bool MDLImporter::ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7 &groupInfo, - MDL::IntGroupData_MDL7 &groupData, - MDL::IntSharedData_MDL7 &shared, - const unsigned char *szCurrent, - const unsigned char **szCurrentOut) { - ai_assert(nullptr != szCurrent); - ai_assert(nullptr != szCurrentOut); - - const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7 *)mBuffer; - - // if we have no bones we can simply skip all frames, - // otherwise we'll need to process them. - // FIX: If we need another frame than the first we must apply frame vertex replacements ... - for (unsigned int iFrame = 0; iFrame < (unsigned int)groupInfo.pcGroup->numframes; ++iFrame) { - MDL::IntFrameInfo_MDL7 frame((BE_NCONST MDL::Frame_MDL7 *)szCurrent, iFrame); - - AI_SWAP4(frame.pcFrame->vertices_count); - AI_SWAP4(frame.pcFrame->transmatrix_count); - - const unsigned int iAdd = pcHeader->frame_stc_size + - frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size + - frame.pcFrame->transmatrix_count * pcHeader->bonetrans_stc_size; - - if (((const char *)szCurrent - (const char *)pcHeader) + iAdd > (unsigned int)pcHeader->data_size) { - ASSIMP_LOG_WARN("Index overflow in frame area. " - "Ignoring all frames and all further mesh groups, too."); - - // don't parse more groups if we can't even read one - // FIXME: sometimes this seems to occur even for valid files ... - *szCurrentOut = szCurrent; - return false; - } - // our output frame? - if (configFrameID == iFrame) { - BE_NCONST MDL::Vertex_MDL7 *pcFrameVertices = (BE_NCONST MDL::Vertex_MDL7 *)(szCurrent + pcHeader->frame_stc_size); - - for (unsigned int qq = 0; qq < frame.pcFrame->vertices_count; ++qq) { - // I assume this are simple replacements for normal vertices, the bone index serving - // as the index of the vertex to be replaced. - uint16_t iIndex = _AI_MDL7_ACCESS(pcFrameVertices, qq, pcHeader->framevertex_stc_size, MDL::Vertex_MDL7).vertindex; - AI_SWAP2(iIndex); - if (iIndex >= groupInfo.pcGroup->numverts) { - ASSIMP_LOG_WARN("Invalid vertex index in frame vertex section"); - continue; - } - - aiVector3D vPosition, vNormal; - - vPosition.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices, qq, pcHeader->framevertex_stc_size).x; - AI_SWAP4(vPosition.x); - vPosition.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices, qq, pcHeader->framevertex_stc_size).y; - AI_SWAP4(vPosition.y); - vPosition.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices, qq, pcHeader->framevertex_stc_size).z; - AI_SWAP4(vPosition.z); - - // now read the normal vector - if (AI_MDL7_FRAMEVERTEX030305_STCSIZE <= pcHeader->mainvertex_stc_size) { - // read the full normal vector - vNormal.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices, qq, pcHeader->framevertex_stc_size).norm[0]; - AI_SWAP4(vNormal.x); - vNormal.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices, qq, pcHeader->framevertex_stc_size).norm[1]; - AI_SWAP4(vNormal.y); - vNormal.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices, qq, pcHeader->framevertex_stc_size).norm[2]; - AI_SWAP4(vNormal.z); - } else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size) { - // read the normal vector from Quake2's smart table - MD2::LookupNormalIndex(_AI_MDL7_ACCESS_VERT(pcFrameVertices, qq, - pcHeader->framevertex_stc_size) - .norm162index, - vNormal); - } - - // FIXME: O(n^2) at the moment ... - BE_NCONST MDL::Triangle_MDL7 *pcGroupTris = groupInfo.pcGroupTris; - unsigned int iOutIndex = 0; - for (unsigned int iTriangle = 0; iTriangle < (unsigned int)groupInfo.pcGroup->numtris; ++iTriangle) { - // iterate through all indices of the current triangle - for (unsigned int c = 0; c < 3; ++c, ++iOutIndex) { - // replace the vertex with the new data - const unsigned int iCurIndex = pcGroupTris->v_index[c]; - if (iCurIndex == iIndex) { - groupData.vPositions[iOutIndex] = vPosition; - groupData.vNormals[iOutIndex] = vNormal; - } - } - // get the next triangle in the list - pcGroupTris = (BE_NCONST MDL::Triangle_MDL7 *)((const char *) - pcGroupTris + - pcHeader->triangle_stc_size); - } - } - } - // parse bone trafo matrix keys (only if there are bones ...) - if (shared.apcOutBones) { - ParseBoneTrafoKeys_3DGS_MDL7(groupInfo, frame, shared); - } - szCurrent += iAdd; - } - *szCurrentOut = szCurrent; - return true; -} - -// ------------------------------------------------------------------------------------------------ -// Sort faces by material, handle multiple UVs correctly -void MDLImporter::SortByMaterials_3DGS_MDL7( - const MDL::IntGroupInfo_MDL7 &groupInfo, - MDL::IntGroupData_MDL7 &groupData, - MDL::IntSplitGroupData_MDL7 &splitGroupData) { - const unsigned int iNumMaterials = (unsigned int)splitGroupData.shared.pcMats.size(); - if (!groupData.bNeed2UV) { - // if we don't need a second set of texture coordinates there is no reason to keep it in memory ... - groupData.vTextureCoords2.clear(); - - // allocate the array - splitGroupData.aiSplit = new std::vector<unsigned int> *[iNumMaterials]; - - for (unsigned int m = 0; m < iNumMaterials; ++m) - splitGroupData.aiSplit[m] = new std::vector<unsigned int>(); - - // iterate through all faces and sort by material - for (unsigned int iFace = 0; iFace < (unsigned int)groupInfo.pcGroup->numtris; ++iFace) { - // check range - if (groupData.pcFaces[iFace].iMatIndex[0] >= iNumMaterials) { - // use the last material instead - splitGroupData.aiSplit[iNumMaterials - 1]->push_back(iFace); - - // sometimes MED writes -1, but normally only if there is only - // one skin assigned. No warning in this case - if (0xFFFFFFFF != groupData.pcFaces[iFace].iMatIndex[0]) - ASSIMP_LOG_WARN("Index overflow in MDL7 material list [#0]"); - } else - splitGroupData.aiSplit[groupData.pcFaces[iFace].iMatIndex[0]]->push_back(iFace); - } - } else { - // we need to build combined materials for each combination of - std::vector<MDL::IntMaterial_MDL7> avMats; - avMats.reserve(iNumMaterials * 2); - - // fixme: why on the heap? - std::vector<std::vector<unsigned int> *> aiTempSplit(iNumMaterials * 2); - for (unsigned int m = 0; m < iNumMaterials; ++m) - aiTempSplit[m] = new std::vector<unsigned int>(); - - // iterate through all faces and sort by material - for (unsigned int iFace = 0; iFace < (unsigned int)groupInfo.pcGroup->numtris; ++iFace) { - // check range - unsigned int iMatIndex = groupData.pcFaces[iFace].iMatIndex[0]; - if (iMatIndex >= iNumMaterials) { - // sometimes MED writes -1, but normally only if there is only - // one skin assigned. No warning in this case - if (UINT_MAX != iMatIndex) - ASSIMP_LOG_WARN("Index overflow in MDL7 material list [#1]"); - iMatIndex = iNumMaterials - 1; - } - unsigned int iMatIndex2 = groupData.pcFaces[iFace].iMatIndex[1]; - - unsigned int iNum = iMatIndex; - if (UINT_MAX != iMatIndex2 && iMatIndex != iMatIndex2) { - if (iMatIndex2 >= iNumMaterials) { - // sometimes MED writes -1, but normally only if there is only - // one skin assigned. No warning in this case - ASSIMP_LOG_WARN("Index overflow in MDL7 material list [#2]"); - iMatIndex2 = iNumMaterials - 1; - } - - // do a slow search in the list ... - iNum = 0; - bool bFound = false; - for (std::vector<MDL::IntMaterial_MDL7>::iterator i = avMats.begin(); i != avMats.end(); ++i, ++iNum) { - if ((*i).iOldMatIndices[0] == iMatIndex && (*i).iOldMatIndices[1] == iMatIndex2) { - // reuse this material - bFound = true; - break; - } - } - if (!bFound) { - // build a new material ... - MDL::IntMaterial_MDL7 sHelper; - sHelper.pcMat = new aiMaterial(); - sHelper.iOldMatIndices[0] = iMatIndex; - sHelper.iOldMatIndices[1] = iMatIndex2; - JoinSkins_3DGS_MDL7(splitGroupData.shared.pcMats[iMatIndex], - splitGroupData.shared.pcMats[iMatIndex2], sHelper.pcMat); - - // and add it to the list - avMats.push_back(sHelper); - iNum = (unsigned int)avMats.size() - 1; - } - // adjust the size of the file array - if (iNum == aiTempSplit.size()) { - aiTempSplit.push_back(new std::vector<unsigned int>()); - } - } - aiTempSplit[iNum]->push_back(iFace); - } - - // now add the newly created materials to the old list - if (0 == groupInfo.iIndex) { - splitGroupData.shared.pcMats.resize(avMats.size()); - for (unsigned int o = 0; o < avMats.size(); ++o) - splitGroupData.shared.pcMats[o] = avMats[o].pcMat; - } else { - // This might result in redundant materials ... - splitGroupData.shared.pcMats.resize(iNumMaterials + avMats.size()); - for (unsigned int o = iNumMaterials; o < avMats.size(); ++o) - splitGroupData.shared.pcMats[o] = avMats[o].pcMat; - } - - // and build the final face-to-material array - splitGroupData.aiSplit = new std::vector<unsigned int> *[aiTempSplit.size()]; - for (unsigned int m = 0; m < iNumMaterials; ++m) - splitGroupData.aiSplit[m] = aiTempSplit[m]; - } -} - -// ------------------------------------------------------------------------------------------------ -// Read a MDL7 file -void MDLImporter::InternReadFile_3DGS_MDL7() { - ai_assert(nullptr != pScene); - - MDL::IntSharedData_MDL7 sharedData; - - // current cursor position in the file - BE_NCONST MDL::Header_MDL7 *pcHeader = (BE_NCONST MDL::Header_MDL7 *)this->mBuffer; - const unsigned char *szCurrent = (const unsigned char *)(pcHeader + 1); - - AI_SWAP4(pcHeader->version); - AI_SWAP4(pcHeader->bones_num); - AI_SWAP4(pcHeader->groups_num); - AI_SWAP4(pcHeader->data_size); - AI_SWAP4(pcHeader->entlump_size); - AI_SWAP4(pcHeader->medlump_size); - AI_SWAP2(pcHeader->bone_stc_size); - AI_SWAP2(pcHeader->skin_stc_size); - AI_SWAP2(pcHeader->colorvalue_stc_size); - AI_SWAP2(pcHeader->material_stc_size); - AI_SWAP2(pcHeader->skinpoint_stc_size); - AI_SWAP2(pcHeader->triangle_stc_size); - AI_SWAP2(pcHeader->mainvertex_stc_size); - AI_SWAP2(pcHeader->framevertex_stc_size); - AI_SWAP2(pcHeader->bonetrans_stc_size); - AI_SWAP2(pcHeader->frame_stc_size); - - // validate the header of the file. There are some structure - // sizes that are expected by the loader to be constant - this->ValidateHeader_3DGS_MDL7(pcHeader); - - // load all bones (they are shared by all groups, so - // we'll need to add them to all groups/meshes later) - // apcBonesOut is a list of all bones or nullptr if they could not been loaded - szCurrent += pcHeader->bones_num * pcHeader->bone_stc_size; - sharedData.apcOutBones = this->LoadBones_3DGS_MDL7(); - - // vector to held all created meshes - std::vector<aiMesh *> *avOutList; - - // 3 meshes per group - that should be OK for most models - avOutList = new std::vector<aiMesh *>[pcHeader->groups_num]; - for (uint32_t i = 0; i < pcHeader->groups_num; ++i) - avOutList[i].reserve(3); - - // buffer to held the names of all groups in the file - const size_t buffersize(AI_MDL7_MAX_GROUPNAMESIZE * pcHeader->groups_num); - char *aszGroupNameBuffer = new char[buffersize]; - - // read all groups - for (unsigned int iGroup = 0; iGroup < (unsigned int)pcHeader->groups_num; ++iGroup) { - MDL::IntGroupInfo_MDL7 groupInfo((BE_NCONST MDL::Group_MDL7 *)szCurrent, iGroup); - szCurrent = (const unsigned char *)(groupInfo.pcGroup + 1); - - VALIDATE_FILE_SIZE(szCurrent); - - AI_SWAP4(groupInfo.pcGroup->groupdata_size); - AI_SWAP4(groupInfo.pcGroup->numskins); - AI_SWAP4(groupInfo.pcGroup->num_stpts); - AI_SWAP4(groupInfo.pcGroup->numtris); - AI_SWAP4(groupInfo.pcGroup->numverts); - AI_SWAP4(groupInfo.pcGroup->numframes); - - if (1 != groupInfo.pcGroup->typ) { - // Not a triangle-based mesh - ASSIMP_LOG_WARN("[3DGS MDL7] Not a triangle mesh group. Continuing happily"); - } - - // store the name of the group - const unsigned int ofs = iGroup * AI_MDL7_MAX_GROUPNAMESIZE; - ::memcpy(&aszGroupNameBuffer[ofs], - groupInfo.pcGroup->name, AI_MDL7_MAX_GROUPNAMESIZE); - - // make sure '\0' is at the end - aszGroupNameBuffer[ofs + AI_MDL7_MAX_GROUPNAMESIZE - 1] = '\0'; - - // read all skins - sharedData.pcMats.reserve(sharedData.pcMats.size() + groupInfo.pcGroup->numskins); - sharedData.abNeedMaterials.resize(sharedData.abNeedMaterials.size() + - groupInfo.pcGroup->numskins, - false); - - for (unsigned int iSkin = 0; iSkin < (unsigned int)groupInfo.pcGroup->numskins; ++iSkin) { - ParseSkinLump_3DGS_MDL7(szCurrent, &szCurrent, sharedData.pcMats); - } - // if we have absolutely no skin loaded we need to generate a default material - if (sharedData.pcMats.empty()) { - const int iMode = (int)aiShadingMode_Gouraud; - sharedData.pcMats.push_back(new aiMaterial()); - aiMaterial *pcHelper = (aiMaterial *)sharedData.pcMats[0]; - pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); - - aiColor3D clr; - clr.b = clr.g = clr.r = 0.6f; - pcHelper->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_DIFFUSE); - pcHelper->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_SPECULAR); - - clr.b = clr.g = clr.r = 0.05f; - pcHelper->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_AMBIENT); - - aiString szName; - szName.Set(AI_DEFAULT_MATERIAL_NAME); - pcHelper->AddProperty(&szName, AI_MATKEY_NAME); - - sharedData.abNeedMaterials.resize(1, false); - } - - // now get a pointer to all texture coords in the group - groupInfo.pcGroupUVs = (BE_NCONST MDL::TexCoord_MDL7 *)szCurrent; - for (int i = 0; i < groupInfo.pcGroup->num_stpts; ++i) { - AI_SWAP4(groupInfo.pcGroupUVs[i].u); - AI_SWAP4(groupInfo.pcGroupUVs[i].v); - } - szCurrent += pcHeader->skinpoint_stc_size * groupInfo.pcGroup->num_stpts; - - // now get a pointer to all triangle in the group - groupInfo.pcGroupTris = (Triangle_MDL7 *)szCurrent; - szCurrent += pcHeader->triangle_stc_size * groupInfo.pcGroup->numtris; - - // now get a pointer to all vertices in the group - groupInfo.pcGroupVerts = (BE_NCONST MDL::Vertex_MDL7 *)szCurrent; - for (int i = 0; i < groupInfo.pcGroup->numverts; ++i) { - AI_SWAP4(groupInfo.pcGroupVerts[i].x); - AI_SWAP4(groupInfo.pcGroupVerts[i].y); - AI_SWAP4(groupInfo.pcGroupVerts[i].z); - - AI_SWAP2(groupInfo.pcGroupVerts[i].vertindex); - //We can not swap the normal information now as we don't know which of the two kinds it is - } - szCurrent += pcHeader->mainvertex_stc_size * groupInfo.pcGroup->numverts; - VALIDATE_FILE_SIZE(szCurrent); - - MDL::IntSplitGroupData_MDL7 splitGroupData(sharedData, avOutList[iGroup]); - MDL::IntGroupData_MDL7 groupData; - if (groupInfo.pcGroup->numtris && groupInfo.pcGroup->numverts) { - // build output vectors - const unsigned int iNumVertices = groupInfo.pcGroup->numtris * 3; - groupData.vPositions.resize(iNumVertices); - groupData.vNormals.resize(iNumVertices); - - if (sharedData.apcOutBones) groupData.aiBones.resize(iNumVertices, UINT_MAX); - - // it is also possible that there are 0 UV coordinate sets - if (groupInfo.pcGroup->num_stpts) { - groupData.vTextureCoords1.resize(iNumVertices, aiVector3D()); - - // check whether the triangle data structure is large enough - // to contain a second UV coordinate set - if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV) { - groupData.vTextureCoords2.resize(iNumVertices, aiVector3D()); - groupData.bNeed2UV = true; - } - } - groupData.pcFaces.resize(groupInfo.pcGroup->numtris); - - // read all faces into the preallocated arrays - ReadFaces_3DGS_MDL7(groupInfo, groupData); - - // sort by materials - SortByMaterials_3DGS_MDL7(groupInfo, groupData, - splitGroupData); - - for (unsigned int qq = 0; qq < sharedData.pcMats.size(); ++qq) { - if (!splitGroupData.aiSplit[qq]->empty()) - sharedData.abNeedMaterials[qq] = true; - } - } else - ASSIMP_LOG_WARN("[3DGS MDL7] Mesh group consists of 0 " - "vertices or faces. It will be skipped."); - - // process all frames and generate output meshes - ProcessFrames_3DGS_MDL7(groupInfo, groupData, sharedData, szCurrent, &szCurrent); - GenerateOutputMeshes_3DGS_MDL7(groupData, splitGroupData); - } - - // generate a nodegraph and subnodes for each group - pScene->mRootNode = new aiNode(); - - // now we need to build a final mesh list - for (uint32_t i = 0; i < pcHeader->groups_num; ++i) - pScene->mNumMeshes += (unsigned int)avOutList[i].size(); - - pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; - { - unsigned int p = 0, q = 0; - for (uint32_t i = 0; i < pcHeader->groups_num; ++i) { - for (unsigned int a = 0; a < avOutList[i].size(); ++a) { - pScene->mMeshes[p++] = avOutList[i][a]; - } - if (!avOutList[i].empty()) ++pScene->mRootNode->mNumChildren; - } - // we will later need an extra node to serve as parent for all bones - if (sharedData.apcOutBones) ++pScene->mRootNode->mNumChildren; - this->pScene->mRootNode->mChildren = new aiNode *[pScene->mRootNode->mNumChildren]; - p = 0; - for (uint32_t i = 0; i < pcHeader->groups_num; ++i) { - if (avOutList[i].empty()) continue; - - aiNode *const pcNode = pScene->mRootNode->mChildren[p] = new aiNode(); - pcNode->mNumMeshes = (unsigned int)avOutList[i].size(); - pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; - pcNode->mParent = this->pScene->mRootNode; - for (unsigned int a = 0; a < pcNode->mNumMeshes; ++a) - pcNode->mMeshes[a] = q + a; - q += (unsigned int)avOutList[i].size(); - - // setup the name of the node - char *const szBuffer = &aszGroupNameBuffer[i * AI_MDL7_MAX_GROUPNAMESIZE]; - if ('\0' == *szBuffer) { - const size_t maxSize(buffersize - (i * AI_MDL7_MAX_GROUPNAMESIZE)); - pcNode->mName.length = ai_snprintf(szBuffer, maxSize, "Group_%u", p); - } else { - pcNode->mName.length = (ai_uint32)::strlen(szBuffer); - } - ::strncpy(pcNode->mName.data, szBuffer, MAXLEN - 1); - ++p; - } - } - - // if there is only one root node with a single child we can optimize it a bit ... - if (1 == pScene->mRootNode->mNumChildren && !sharedData.apcOutBones) { - aiNode *pcOldRoot = this->pScene->mRootNode; - pScene->mRootNode = pcOldRoot->mChildren[0]; - pcOldRoot->mChildren[0] = nullptr; - delete pcOldRoot; - pScene->mRootNode->mParent = nullptr; - } else - pScene->mRootNode->mName.Set("<mesh_root>"); - - delete[] avOutList; - delete[] aszGroupNameBuffer; - AI_DEBUG_INVALIDATE_PTR(avOutList); - AI_DEBUG_INVALIDATE_PTR(aszGroupNameBuffer); - - // build a final material list. - CopyMaterials_3DGS_MDL7(sharedData); - HandleMaterialReferences_3DGS_MDL7(); - - // generate output bone animations and add all bones to the scenegraph - if (sharedData.apcOutBones) { - // this step adds empty dummy bones to the nodegraph - // insert another dummy node to avoid name conflicts - aiNode *const pc = pScene->mRootNode->mChildren[pScene->mRootNode->mNumChildren - 1] = new aiNode(); - - pc->mName.Set("<skeleton_root>"); - - // add bones to the nodegraph - AddBonesToNodeGraph_3DGS_MDL7((const Assimp::MDL::IntBone_MDL7 **) - sharedData.apcOutBones, - pc, 0xffff); - - // this steps build a valid output animation - BuildOutputAnims_3DGS_MDL7((const Assimp::MDL::IntBone_MDL7 **) - sharedData.apcOutBones); - } -} - -// ------------------------------------------------------------------------------------------------ -// Copy materials -void MDLImporter::CopyMaterials_3DGS_MDL7(MDL::IntSharedData_MDL7 &shared) { - pScene->mNumMaterials = (unsigned int)shared.pcMats.size(); - pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; - for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) - pScene->mMaterials[i] = shared.pcMats[i]; -} - -// ------------------------------------------------------------------------------------------------ -// Process material references -void MDLImporter::HandleMaterialReferences_3DGS_MDL7() { - // search for referrer materials - for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) { - int iIndex = 0; - if (AI_SUCCESS == aiGetMaterialInteger(pScene->mMaterials[i], AI_MDL7_REFERRER_MATERIAL, &iIndex)) { - for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { - aiMesh *const pcMesh = pScene->mMeshes[a]; - if (i == pcMesh->mMaterialIndex) { - pcMesh->mMaterialIndex = iIndex; - } - } - // collapse the rest of the array - delete pScene->mMaterials[i]; - for (unsigned int pp = i; pp < pScene->mNumMaterials - 1; ++pp) { - - pScene->mMaterials[pp] = pScene->mMaterials[pp + 1]; - for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { - aiMesh *const pcMesh = pScene->mMeshes[a]; - if (pcMesh->mMaterialIndex > i) --pcMesh->mMaterialIndex; - } - } - --pScene->mNumMaterials; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Read bone transformation keys -void MDLImporter::ParseBoneTrafoKeys_3DGS_MDL7( - const MDL::IntGroupInfo_MDL7 &groupInfo, - IntFrameInfo_MDL7 &frame, - MDL::IntSharedData_MDL7 &shared) { - const MDL::Header_MDL7 *const pcHeader = (const MDL::Header_MDL7 *)this->mBuffer; - - // only the first group contains bone animation keys - if (frame.pcFrame->transmatrix_count) { - if (!groupInfo.iIndex) { - // skip all frames vertices. We can't support them - const MDL::BoneTransform_MDL7 *pcBoneTransforms = (const MDL::BoneTransform_MDL7 *)(((const char *)frame.pcFrame) + pcHeader->frame_stc_size + - frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size); - - // read all transformation matrices - for (unsigned int iTrafo = 0; iTrafo < frame.pcFrame->transmatrix_count; ++iTrafo) { - if (pcBoneTransforms->bone_index >= pcHeader->bones_num) { - ASSIMP_LOG_WARN("Index overflow in frame area. " - "Unable to parse this bone transformation"); - } else { - AddAnimationBoneTrafoKey_3DGS_MDL7(frame.iIndex, - pcBoneTransforms, shared.apcOutBones); - } - pcBoneTransforms = (const MDL::BoneTransform_MDL7 *)((const char *)pcBoneTransforms + pcHeader->bonetrans_stc_size); - } - } else { - ASSIMP_LOG_WARN("Ignoring animation keyframes in groups != 0"); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Attach bones to the output nodegraph -void MDLImporter::AddBonesToNodeGraph_3DGS_MDL7(const MDL::IntBone_MDL7 **apcBones, - aiNode *pcParent, uint16_t iParentIndex) { - ai_assert(nullptr != apcBones); - ai_assert(nullptr != pcParent); - - // get a pointer to the header ... - const MDL::Header_MDL7 *const pcHeader = (const MDL::Header_MDL7 *)this->mBuffer; - - const MDL::IntBone_MDL7 **apcBones2 = apcBones; - for (uint32_t i = 0; i < pcHeader->bones_num; ++i) { - - const MDL::IntBone_MDL7 *const pcBone = *apcBones2++; - if (pcBone->iParent == iParentIndex) { - ++pcParent->mNumChildren; - } - } - pcParent->mChildren = new aiNode *[pcParent->mNumChildren]; - unsigned int qq = 0; - for (uint32_t i = 0; i < pcHeader->bones_num; ++i) { - - const MDL::IntBone_MDL7 *const pcBone = *apcBones++; - if (pcBone->iParent != iParentIndex) continue; - - aiNode *pcNode = pcParent->mChildren[qq++] = new aiNode(); - pcNode->mName = aiString(pcBone->mName); - - AddBonesToNodeGraph_3DGS_MDL7(apcBones, pcNode, (uint16_t)i); - } -} - -// ------------------------------------------------------------------------------------------------ -// Build output animations -void MDLImporter::BuildOutputAnims_3DGS_MDL7( - const MDL::IntBone_MDL7 **apcBonesOut) { - ai_assert(nullptr != apcBonesOut); - const MDL::Header_MDL7 *const pcHeader = (const MDL::Header_MDL7 *)mBuffer; - - // one animation ... - aiAnimation *pcAnim = new aiAnimation(); - for (uint32_t i = 0; i < pcHeader->bones_num; ++i) { - if (!apcBonesOut[i]->pkeyPositions.empty()) { - - // get the last frame ... (needn't be equal to pcHeader->frames_num) - for (size_t qq = 0; qq < apcBonesOut[i]->pkeyPositions.size(); ++qq) { - pcAnim->mDuration = std::max(pcAnim->mDuration, (double) - apcBonesOut[i] - ->pkeyPositions[qq] - .mTime); - } - ++pcAnim->mNumChannels; - } - } - if (pcAnim->mDuration) { - pcAnim->mChannels = new aiNodeAnim *[pcAnim->mNumChannels]; - - unsigned int iCnt = 0; - for (uint32_t i = 0; i < pcHeader->bones_num; ++i) { - if (!apcBonesOut[i]->pkeyPositions.empty()) { - const MDL::IntBone_MDL7 *const intBone = apcBonesOut[i]; - - aiNodeAnim *const pcNodeAnim = pcAnim->mChannels[iCnt++] = new aiNodeAnim(); - pcNodeAnim->mNodeName = aiString(intBone->mName); - - // allocate enough storage for all keys - pcNodeAnim->mNumPositionKeys = (unsigned int)intBone->pkeyPositions.size(); - pcNodeAnim->mNumScalingKeys = (unsigned int)intBone->pkeyPositions.size(); - pcNodeAnim->mNumRotationKeys = (unsigned int)intBone->pkeyPositions.size(); - - pcNodeAnim->mPositionKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys]; - pcNodeAnim->mScalingKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys]; - pcNodeAnim->mRotationKeys = new aiQuatKey[pcNodeAnim->mNumPositionKeys]; - - // copy all keys - for (unsigned int qq = 0; qq < pcNodeAnim->mNumPositionKeys; ++qq) { - pcNodeAnim->mPositionKeys[qq] = intBone->pkeyPositions[qq]; - pcNodeAnim->mScalingKeys[qq] = intBone->pkeyScalings[qq]; - pcNodeAnim->mRotationKeys[qq] = intBone->pkeyRotations[qq]; - } - } - } - - // store the output animation - pScene->mNumAnimations = 1; - pScene->mAnimations = new aiAnimation *[1]; - pScene->mAnimations[0] = pcAnim; - } else - delete pcAnim; -} - -// ------------------------------------------------------------------------------------------------ -void MDLImporter::AddAnimationBoneTrafoKey_3DGS_MDL7(unsigned int iTrafo, - const MDL::BoneTransform_MDL7 *pcBoneTransforms, - MDL::IntBone_MDL7 **apcBonesOut) { - ai_assert(nullptr != pcBoneTransforms); - ai_assert(nullptr != apcBonesOut); - - // first .. get the transformation matrix - aiMatrix4x4 mTransform; - mTransform.a1 = pcBoneTransforms->m[0]; - mTransform.b1 = pcBoneTransforms->m[1]; - mTransform.c1 = pcBoneTransforms->m[2]; - mTransform.d1 = pcBoneTransforms->m[3]; - - mTransform.a2 = pcBoneTransforms->m[4]; - mTransform.b2 = pcBoneTransforms->m[5]; - mTransform.c2 = pcBoneTransforms->m[6]; - mTransform.d2 = pcBoneTransforms->m[7]; - - mTransform.a3 = pcBoneTransforms->m[8]; - mTransform.b3 = pcBoneTransforms->m[9]; - mTransform.c3 = pcBoneTransforms->m[10]; - mTransform.d3 = pcBoneTransforms->m[11]; - - // now decompose the transformation matrix into separate - // scaling, rotation and translation - aiVectorKey vScaling, vPosition; - aiQuatKey qRotation; - - // FIXME: Decompose will assert in debug builds if the matrix is invalid ... - mTransform.Decompose(vScaling.mValue, qRotation.mValue, vPosition.mValue); - - // now generate keys - vScaling.mTime = qRotation.mTime = vPosition.mTime = (double)iTrafo; - - // add the keys to the bone - MDL::IntBone_MDL7 *const pcBoneOut = apcBonesOut[pcBoneTransforms->bone_index]; - pcBoneOut->pkeyPositions.push_back(vPosition); - pcBoneOut->pkeyScalings.push_back(vScaling); - pcBoneOut->pkeyRotations.push_back(qRotation); -} - -// ------------------------------------------------------------------------------------------------ -// Construct output meshes -void MDLImporter::GenerateOutputMeshes_3DGS_MDL7( - MDL::IntGroupData_MDL7 &groupData, - MDL::IntSplitGroupData_MDL7 &splitGroupData) { - const MDL::IntSharedData_MDL7 &shared = splitGroupData.shared; - - // get a pointer to the header ... - const MDL::Header_MDL7 *const pcHeader = (const MDL::Header_MDL7 *)this->mBuffer; - const unsigned int iNumOutBones = pcHeader->bones_num; - - for (std::vector<aiMaterial *>::size_type i = 0; i < shared.pcMats.size(); ++i) { - if (!splitGroupData.aiSplit[i]->empty()) { - - // allocate the output mesh - aiMesh *pcMesh = new aiMesh(); - - pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - pcMesh->mMaterialIndex = (unsigned int)i; - - // allocate output storage - pcMesh->mNumFaces = (unsigned int)splitGroupData.aiSplit[i]->size(); - pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; - - pcMesh->mNumVertices = pcMesh->mNumFaces * 3; - pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; - pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; - - if (!groupData.vTextureCoords1.empty()) { - pcMesh->mNumUVComponents[0] = 2; - pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; - if (!groupData.vTextureCoords2.empty()) { - pcMesh->mNumUVComponents[1] = 2; - pcMesh->mTextureCoords[1] = new aiVector3D[pcMesh->mNumVertices]; - } - } - - // iterate through all faces and build an unique set of vertices - unsigned int iCurrent = 0; - for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces; ++iFace) { - pcMesh->mFaces[iFace].mNumIndices = 3; - pcMesh->mFaces[iFace].mIndices = new unsigned int[3]; - - unsigned int iSrcFace = splitGroupData.aiSplit[i]->operator[](iFace); - const MDL::IntFace_MDL7 &oldFace = groupData.pcFaces[iSrcFace]; - - // iterate through all face indices - for (unsigned int c = 0; c < 3; ++c) { - const uint32_t iIndex = oldFace.mIndices[c]; - pcMesh->mVertices[iCurrent] = groupData.vPositions[iIndex]; - pcMesh->mNormals[iCurrent] = groupData.vNormals[iIndex]; - - if (!groupData.vTextureCoords1.empty()) { - - pcMesh->mTextureCoords[0][iCurrent] = groupData.vTextureCoords1[iIndex]; - if (!groupData.vTextureCoords2.empty()) { - pcMesh->mTextureCoords[1][iCurrent] = groupData.vTextureCoords2[iIndex]; - } - } - pcMesh->mFaces[iFace].mIndices[c] = iCurrent++; - } - } - - // if we have bones in the mesh we'll need to generate - // proper vertex weights for them - if (!groupData.aiBones.empty()) { - std::vector<std::vector<unsigned int>> aaiVWeightList; - aaiVWeightList.resize(iNumOutBones); - - int iCurrentWeight = 0; - for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces; ++iFace) { - unsigned int iSrcFace = splitGroupData.aiSplit[i]->operator[](iFace); - const MDL::IntFace_MDL7 &oldFace = groupData.pcFaces[iSrcFace]; - - // iterate through all face indices - for (unsigned int c = 0; c < 3; ++c) { - unsigned int iBone = groupData.aiBones[oldFace.mIndices[c]]; - if (UINT_MAX != iBone) { - if (iBone >= iNumOutBones) { - ASSIMP_LOG_ERROR("Bone index overflow. " - "The bone index of a vertex exceeds the allowed range. "); - iBone = iNumOutBones - 1; - } - aaiVWeightList[iBone].push_back(iCurrentWeight); - } - ++iCurrentWeight; - } - } - // now check which bones are required ... - for (std::vector<std::vector<unsigned int>>::const_iterator k = aaiVWeightList.begin(); k != aaiVWeightList.end(); ++k) { - if (!(*k).empty()) { - ++pcMesh->mNumBones; - } - } - pcMesh->mBones = new aiBone *[pcMesh->mNumBones]; - iCurrent = 0; - for (std::vector<std::vector<unsigned int>>::const_iterator k = aaiVWeightList.begin(); k != aaiVWeightList.end(); ++k, ++iCurrent) { - if ((*k).empty()) - continue; - - // seems we'll need this node - aiBone *pcBone = pcMesh->mBones[iCurrent] = new aiBone(); - pcBone->mName = aiString(shared.apcOutBones[iCurrent]->mName); - pcBone->mOffsetMatrix = shared.apcOutBones[iCurrent]->mOffsetMatrix; - - // setup vertex weights - pcBone->mNumWeights = (unsigned int)(*k).size(); - pcBone->mWeights = new aiVertexWeight[pcBone->mNumWeights]; - - for (unsigned int weight = 0; weight < pcBone->mNumWeights; ++weight) { - pcBone->mWeights[weight].mVertexId = (*k)[weight]; - pcBone->mWeights[weight].mWeight = 1.0f; - } - } - } - // add the mesh to the list of output meshes - splitGroupData.avOutList.push_back(pcMesh); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Join to materials -void MDLImporter::JoinSkins_3DGS_MDL7( - aiMaterial *pcMat1, - aiMaterial *pcMat2, - aiMaterial *pcMatOut) { - ai_assert(nullptr != pcMat1); - ai_assert(nullptr != pcMat2); - ai_assert(nullptr != pcMatOut); - - // first create a full copy of the first skin property set - // and assign it to the output material - aiMaterial::CopyPropertyList(pcMatOut, pcMat1); - - int iVal = 0; - pcMatOut->AddProperty<int>(&iVal, 1, AI_MATKEY_UVWSRC_DIFFUSE(0)); - - // then extract the diffuse texture from the second skin, - // setup 1 as UV source and we have it - aiString sString; - if (AI_SUCCESS == aiGetMaterialString(pcMat2, AI_MATKEY_TEXTURE_DIFFUSE(0), &sString)) { - iVal = 1; - pcMatOut->AddProperty<int>(&iVal, 1, AI_MATKEY_UVWSRC_DIFFUSE(1)); - pcMatOut->AddProperty(&sString, AI_MATKEY_TEXTURE_DIFFUSE(1)); - } -} - -// ------------------------------------------------------------------------------------------------ -// Read a Half-life 1 MDL -void MDLImporter::InternReadFile_HL1(const std::string &pFile, const uint32_t iMagicWord) { - // We can't correctly load an MDL from a MDL "sequence" file. - if (iMagicWord == AI_MDL_MAGIC_NUMBER_BE_HL2b || iMagicWord == AI_MDL_MAGIC_NUMBER_LE_HL2b) - throw DeadlyImportError("Impossible to properly load a model from an MDL sequence file."); - - // Read the MDL file. - HalfLife::HL1MDLLoader loader( - pScene, - mIOHandler, - mBuffer, - pFile, - mHL1ImportSettings); -} - -// ------------------------------------------------------------------------------------------------ -// Read a half-life 2 MDL -void MDLImporter::InternReadFile_HL2() { - //const MDL::Header_HL2* pcHeader = (const MDL::Header_HL2*)this->mBuffer; - throw DeadlyImportError("HL2 MDLs are not implemented"); -} - -#endif // !! ASSIMP_BUILD_NO_MDL_IMPORTER |