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