diff options
Diffstat (limited to 'NvCloth/samples/external/assimp-4.1.0/code/MD2Loader.cpp')
| -rw-r--r-- | NvCloth/samples/external/assimp-4.1.0/code/MD2Loader.cpp | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/NvCloth/samples/external/assimp-4.1.0/code/MD2Loader.cpp b/NvCloth/samples/external/assimp-4.1.0/code/MD2Loader.cpp new file mode 100644 index 0000000..f0a5432 --- /dev/null +++ b/NvCloth/samples/external/assimp-4.1.0/code/MD2Loader.cpp @@ -0,0 +1,458 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + + +#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER + +/** @file Implementation of the MD2 importer class */ +#include "MD2Loader.h" +#include "ByteSwapper.h" +#include "MD2NormalTable.h" // shouldn't be included by other units +#include <assimp/DefaultLogger.hpp> +#include <assimp/Importer.hpp> +#include <assimp/IOSystem.hpp> +#include <assimp/scene.h> +#include <assimp/importerdesc.h> + +#include <memory> + +using namespace Assimp; +using namespace Assimp::MD2; + +// helper macro to determine the size of an array +#if (!defined ARRAYSIZE) +# define ARRAYSIZE(_array) (int(sizeof(_array) / sizeof(_array[0]))) +#endif + +static const aiImporterDesc desc = { + "Quake II Mesh Importer", + "", + "", + "", + aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "md2" +}; + +// ------------------------------------------------------------------------------------------------ +// Helper function to lookup a normal in Quake 2's precalculated table +void MD2::LookupNormalIndex(uint8_t iNormalIndex,aiVector3D& vOut) +{ + // make sure the normal index has a valid value + if (iNormalIndex >= ARRAYSIZE(g_avNormals)) { + DefaultLogger::get()->warn("Index overflow in Quake II normal vector list"); + iNormalIndex = ARRAYSIZE(g_avNormals) - 1; + } + vOut = *((const aiVector3D*)(&g_avNormals[iNormalIndex])); +} + + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +MD2Importer::MD2Importer() + : configFrameID(), + m_pcHeader(), + mBuffer(), + fileSize() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +MD2Importer::~MD2Importer() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +{ + const std::string extension = GetExtension(pFile); + if (extension == "md2") + return true; + + // if check for extension is not enough, check for the magic tokens + if (!extension.length() || checkSig) { + uint32_t tokens[1]; + tokens[0] = AI_MD2_MAGIC_NUMBER_LE; + return CheckMagicToken(pIOHandler,pFile,tokens,1); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Get a list of all extensions supported by this loader +const aiImporterDesc* MD2Importer::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration properties +void MD2Importer::SetupProperties(const Importer* pImp) +{ + // The + // AI_CONFIG_IMPORT_MD2_KEYFRAME option overrides the + // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. + configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD2_KEYFRAME,-1); + if(static_cast<unsigned int>(-1) == configFrameID){ + configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); + } +} +// ------------------------------------------------------------------------------------------------ +// Validate the file header +void MD2Importer::ValidateHeader( ) +{ + // check magic number + if (m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE && + m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE) + { + char szBuffer[5]; + szBuffer[0] = ((char*)&m_pcHeader->magic)[0]; + szBuffer[1] = ((char*)&m_pcHeader->magic)[1]; + szBuffer[2] = ((char*)&m_pcHeader->magic)[2]; + szBuffer[3] = ((char*)&m_pcHeader->magic)[3]; + szBuffer[4] = '\0'; + + throw DeadlyImportError("Invalid MD2 magic word: should be IDP2, the " + "magic word found is " + std::string(szBuffer)); + } + + // check file format version + if (m_pcHeader->version != 8) + DefaultLogger::get()->warn( "Unsupported md2 file version. Continuing happily ..."); + + // check some values whether they are valid + if (0 == m_pcHeader->numFrames) + throw DeadlyImportError( "Invalid md2 file: NUM_FRAMES is 0"); + + if (m_pcHeader->offsetEnd > (uint32_t)fileSize) + throw DeadlyImportError( "Invalid md2 file: File is too small"); + + if (m_pcHeader->numSkins > AI_MAX_ALLOC(MD2::Skin)) { + throw DeadlyImportError("Invalid MD2 header: too many skins, would overflow"); + } + + if (m_pcHeader->numVertices > AI_MAX_ALLOC(MD2::Vertex)) { + throw DeadlyImportError("Invalid MD2 header: too many vertices, would overflow"); + } + + if (m_pcHeader->numTexCoords > AI_MAX_ALLOC(MD2::TexCoord)) { + throw DeadlyImportError("Invalid MD2 header: too many texcoords, would overflow"); + } + + if (m_pcHeader->numTriangles > AI_MAX_ALLOC(MD2::Triangle)) { + throw DeadlyImportError("Invalid MD2 header: too many triangles, would overflow"); + } + + if (m_pcHeader->numFrames > AI_MAX_ALLOC(MD2::Frame)) { + throw DeadlyImportError("Invalid MD2 header: too many frames, would overflow"); + } + + // -1 because Frame already contains one + unsigned int frameSize = sizeof (MD2::Frame) + (m_pcHeader->numVertices - 1) * sizeof(MD2::Vertex); + + if (m_pcHeader->offsetSkins + m_pcHeader->numSkins * sizeof (MD2::Skin) >= fileSize || + m_pcHeader->offsetTexCoords + m_pcHeader->numTexCoords * sizeof (MD2::TexCoord) >= fileSize || + m_pcHeader->offsetTriangles + m_pcHeader->numTriangles * sizeof (MD2::Triangle) >= fileSize || + m_pcHeader->offsetFrames + m_pcHeader->numFrames * frameSize >= fileSize || + m_pcHeader->offsetEnd > fileSize) + { + throw DeadlyImportError("Invalid MD2 header: some offsets are outside the file"); + } + + if (m_pcHeader->numSkins > AI_MD2_MAX_SKINS) + DefaultLogger::get()->warn("The model contains more skins than Quake 2 supports"); + if ( m_pcHeader->numFrames > AI_MD2_MAX_FRAMES) + DefaultLogger::get()->warn("The model contains more frames than Quake 2 supports"); + if (m_pcHeader->numVertices > AI_MD2_MAX_VERTS) + DefaultLogger::get()->warn("The model contains more vertices than Quake 2 supports"); + + if (m_pcHeader->numFrames <= configFrameID ) + throw DeadlyImportError("The requested frame is not existing the file"); +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void MD2Importer::InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) +{ + std::unique_ptr<IOStream> file( pIOHandler->Open( pFile)); + + // Check whether we can read from the file + if( file.get() == NULL) + throw DeadlyImportError( "Failed to open MD2 file " + pFile + ""); + + // check whether the md3 file is large enough to contain + // at least the file header + fileSize = (unsigned int)file->FileSize(); + if( fileSize < sizeof(MD2::Header)) + throw DeadlyImportError( "MD2 File is too small"); + + std::vector<uint8_t> mBuffer2(fileSize); + file->Read(&mBuffer2[0], 1, fileSize); + mBuffer = &mBuffer2[0]; + + + m_pcHeader = (BE_NCONST MD2::Header*)mBuffer; + +#ifdef AI_BUILD_BIG_ENDIAN + + ByteSwap::Swap4(&m_pcHeader->frameSize); + ByteSwap::Swap4(&m_pcHeader->magic); + ByteSwap::Swap4(&m_pcHeader->numFrames); + ByteSwap::Swap4(&m_pcHeader->numGlCommands); + ByteSwap::Swap4(&m_pcHeader->numSkins); + ByteSwap::Swap4(&m_pcHeader->numTexCoords); + ByteSwap::Swap4(&m_pcHeader->numTriangles); + ByteSwap::Swap4(&m_pcHeader->numVertices); + ByteSwap::Swap4(&m_pcHeader->offsetEnd); + ByteSwap::Swap4(&m_pcHeader->offsetFrames); + ByteSwap::Swap4(&m_pcHeader->offsetGlCommands); + ByteSwap::Swap4(&m_pcHeader->offsetSkins); + ByteSwap::Swap4(&m_pcHeader->offsetTexCoords); + ByteSwap::Swap4(&m_pcHeader->offsetTriangles); + ByteSwap::Swap4(&m_pcHeader->skinHeight); + ByteSwap::Swap4(&m_pcHeader->skinWidth); + ByteSwap::Swap4(&m_pcHeader->version); + +#endif + + ValidateHeader(); + + // there won't be more than one mesh inside the file + pScene->mNumMaterials = 1; + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mNumMeshes = 1; + pScene->mRootNode->mMeshes = new unsigned int[1]; + pScene->mRootNode->mMeshes[0] = 0; + pScene->mMaterials = new aiMaterial*[1]; + pScene->mMaterials[0] = new aiMaterial(); + pScene->mNumMeshes = 1; + pScene->mMeshes = new aiMesh*[1]; + + aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh(); + pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + // navigate to the begin of the current frame data + BE_NCONST MD2::Frame* pcFrame = (BE_NCONST MD2::Frame*) ((uint8_t*) + m_pcHeader + m_pcHeader->offsetFrames + (m_pcHeader->frameSize * configFrameID)); + + // navigate to the begin of the triangle data + MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*) + m_pcHeader + m_pcHeader->offsetTriangles); + + // navigate to the begin of the tex coords data + BE_NCONST MD2::TexCoord* pcTexCoords = (BE_NCONST MD2::TexCoord*) ((uint8_t*) + m_pcHeader + m_pcHeader->offsetTexCoords); + + // navigate to the begin of the vertex data + BE_NCONST MD2::Vertex* pcVerts = (BE_NCONST MD2::Vertex*) (pcFrame->vertices); + +#ifdef AI_BUILD_BIG_ENDIAN + for (uint32_t i = 0; i< m_pcHeader->numTriangles; ++i) + { + for (unsigned int p = 0; p < 3;++p) + { + ByteSwap::Swap2(& pcTriangles[i].textureIndices[p]); + ByteSwap::Swap2(& pcTriangles[i].vertexIndices[p]); + } + } + for (uint32_t i = 0; i < m_pcHeader->offsetTexCoords;++i) + { + ByteSwap::Swap2(& pcTexCoords[i].s); + ByteSwap::Swap2(& pcTexCoords[i].t); + } + ByteSwap::Swap4( & pcFrame->scale[0] ); + ByteSwap::Swap4( & pcFrame->scale[1] ); + ByteSwap::Swap4( & pcFrame->scale[2] ); + ByteSwap::Swap4( & pcFrame->translate[0] ); + ByteSwap::Swap4( & pcFrame->translate[1] ); + ByteSwap::Swap4( & pcFrame->translate[2] ); +#endif + + pcMesh->mNumFaces = m_pcHeader->numTriangles; + pcMesh->mFaces = new aiFace[m_pcHeader->numTriangles]; + + // allocate output storage + pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3; + pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; + pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; + + // Not sure whether there are MD2 files without texture coordinates + // NOTE: texture coordinates can be there without a texture, + // but a texture can't be there without a valid UV channel + aiMaterial* pcHelper = (aiMaterial*)pScene->mMaterials[0]; + const int iMode = (int)aiShadingMode_Gouraud; + pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + if (m_pcHeader->numTexCoords && m_pcHeader->numSkins) + { + // navigate to the first texture associated with the mesh + const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)m_pcHeader + + m_pcHeader->offsetSkins); + + aiColor3D clr; + clr.b = clr.g = clr.r = 1.0f; + 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); + + if (pcSkins->name[0]) + { + aiString szString; + const size_t iLen = ::strlen(pcSkins->name); + ::memcpy(szString.data,pcSkins->name,iLen); + szString.data[iLen] = '\0'; + szString.length = iLen; + + pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0)); + } + else{ + DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped."); + } + } + else { + // apply a default material + 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); + + aiString sz; + + // TODO: Try to guess the name of the texture file from the model file name + + sz.Set("$texture_dummy.bmp"); + pcHelper->AddProperty(&sz,AI_MATKEY_TEXTURE_DIFFUSE(0)); + } + + + // now read all triangles of the first frame, apply scaling and translation + unsigned int iCurrent = 0; + + float fDivisorU = 1.0f,fDivisorV = 1.0f; + if (m_pcHeader->numTexCoords) { + // allocate storage for texture coordinates, too + pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; + pcMesh->mNumUVComponents[0] = 2; + + // check whether the skin width or height are zero (this would + // cause a division through zero) + if (!m_pcHeader->skinWidth) { + DefaultLogger::get()->error("MD2: No valid skin width given"); + } + else fDivisorU = (float)m_pcHeader->skinWidth; + if (!m_pcHeader->skinHeight){ + DefaultLogger::get()->error("MD2: No valid skin height given"); + } + else fDivisorV = (float)m_pcHeader->skinHeight; + } + + for (unsigned int i = 0; i < (unsigned int)m_pcHeader->numTriangles;++i) { + // Allocate the face + pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3]; + pScene->mMeshes[0]->mFaces[i].mNumIndices = 3; + + // copy texture coordinates + // check whether they are different from the previous value at this index. + // In this case, create a full separate set of vertices/normals/texcoords + for (unsigned int c = 0; c < 3;++c,++iCurrent) { + + // validate vertex indices + unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c]; + if (iIndex >= m_pcHeader->numVertices) { + DefaultLogger::get()->error("MD2: Vertex index is outside the allowed range"); + iIndex = m_pcHeader->numVertices-1; + } + + // read x,y, and z component of the vertex + aiVector3D& vec = pcMesh->mVertices[iCurrent]; + + vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0]; + vec.x += pcFrame->translate[0]; + + vec.y = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1]; + vec.y += pcFrame->translate[1]; + + vec.z = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2]; + vec.z += pcFrame->translate[2]; + + // read the normal vector from the precalculated normal table + aiVector3D& vNormal = pcMesh->mNormals[iCurrent]; + LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal); + + // flip z and y to become right-handed + std::swap((float&)vNormal.z,(float&)vNormal.y); + std::swap((float&)vec.z,(float&)vec.y); + + if (m_pcHeader->numTexCoords) { + // validate texture coordinates + iIndex = pcTriangles[i].textureIndices[c]; + if (iIndex >= m_pcHeader->numTexCoords) { + DefaultLogger::get()->error("MD2: UV index is outside the allowed range"); + iIndex = m_pcHeader->numTexCoords-1; + } + + aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent]; + + // the texture coordinates are absolute values but we + // need relative values between 0 and 1 + pcOut.x = pcTexCoords[iIndex].s / fDivisorU; + pcOut.y = 1.f-pcTexCoords[iIndex].t / fDivisorV; + } + pScene->mMeshes[0]->mFaces[i].mIndices[c] = iCurrent; + } + } +} + +#endif // !! ASSIMP_BUILD_NO_MD2_IMPORTER |