123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- /** @file Implementation of the MD3 importer class */
- #include "MD3Loader.h"
- #include "MaterialSystem.h"
- #include "../include/IOStream.h"
- #include "../include/IOSystem.h"
- #include "../include/aiMesh.h"
- #include "../include/aiScene.h"
- #include "../include/aiAssert.h"
- #include <boost/scoped_ptr.hpp>
- using namespace Assimp;
- // ------------------------------------------------------------------------------------------------
- // Constructor to be privately used by Importer
- MD3Importer::MD3Importer()
- {
- }
- // ------------------------------------------------------------------------------------------------
- // Destructor, private as well
- MD3Importer::~MD3Importer()
- {
- }
- // ------------------------------------------------------------------------------------------------
- // Returns whether the class can handle the format of the given file.
- bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
- {
- // simple check of file extension is enough for the moment
- std::string::size_type pos = pFile.find_last_of('.');
- // no file extension - can't read
- if( pos == std::string::npos)
- return false;
- std::string extension = pFile.substr( pos);
- // not brilliant but working ;-)
- if( extension == ".md3" || extension == ".MD3" ||
- extension == ".mD3" || extension == ".Md3")
- return true;
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- // Imports the given file into the given scene structure.
- void MD3Importer::InternReadFile(
- const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
- {
- boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
- // Check whether we can read from the file
- if( file.get() == NULL)
- {
- throw new ImportErrorException( "Failed to open md3 file " + pFile + ".");
- }
- // check whether the md3 file is large enough to contain
- // at least the file header
- size_t fileSize = file->FileSize();
- if( fileSize < sizeof(MD3::Header))
- {
- throw new ImportErrorException( ".md3 File is too small.");
- }
- // allocate storage and copy the contents of the file to a memory buffer
- this->mBuffer = new unsigned char[fileSize];
- file->Read( (void*)mBuffer, 1, fileSize);
- this->m_pcHeader = (const MD3::Header*)this->mBuffer;
- // check magic number
- if (this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE &&
- this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
- {
- throw new ImportErrorException( "Invalid md3 file: Magic bytes not found");
- }
- // check file format version
- if (this->m_pcHeader->VERSION > 15)
- {
- throw new ImportErrorException( "Unsupported md3 file version");
- }
- // check some values whether they are valid
- if (0 == this->m_pcHeader->NUM_FRAMES)
- {
- throw new ImportErrorException( "Invalid md3 file: NUM_FRAMES is 0");
- }
- if (0 == this->m_pcHeader->NUM_SURFACES)
- {
- throw new ImportErrorException( "Invalid md3 file: NUM_SURFACES is 0");
- }
- if (this->m_pcHeader->OFS_EOF > (int32_t)fileSize)
- {
- throw new ImportErrorException( "Invalid md3 file: File is too small");
- }
- // now navigate to the list of surfaces
- const MD3::Surface* pcSurfaces = (const MD3::Surface*)
- (this->mBuffer + this->m_pcHeader->OFS_SURFACES);
- // allocate output storage
- pScene->mNumMeshes = this->m_pcHeader->NUM_SURFACES;
- pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
- pScene->mNumMaterials = this->m_pcHeader->NUM_SURFACES;
- pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
- unsigned int iNum = this->m_pcHeader->NUM_SURFACES;
- unsigned int iNumMaterials = 0;
- unsigned int iDefaultMatIndex = 0xFFFFFFFF;
- while (iNum-- > 0)
- {
- // navigate to the vertex list of the surface
- const MD3::Vertex* pcVertices = (const MD3::Vertex*)
- (((unsigned char*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL);
- // navigate to the triangle list of the surface
- const MD3::Triangle* pcTriangles = (const MD3::Triangle*)
- (((unsigned char*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES);
- // navigate to the texture coordinate list of the surface
- const MD3::TexCoord* pcUVs = (const MD3::TexCoord*)
- (((unsigned char*)pcSurfaces) + pcSurfaces->OFS_ST);
- // navigate to the shader list of the surface
- const MD3::Shader* pcShaders = (const MD3::Shader*)
- (((unsigned char*)pcSurfaces) + pcSurfaces->OFS_SHADERS);
- // if the submesh is empty ignore it
- if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES)
- {
- pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
- pScene->mNumMeshes--;
- continue;
- }
- // allocate the output mesh
- pScene->mMeshes[iNum] = new aiMesh();
- aiMesh* pcMesh = pScene->mMeshes[iNum];
- pcMesh->mNumVertices = pcSurfaces->NUM_VERTICES;
- pcMesh->mNumBones = 0;
- pcMesh->mColors[0] = pcMesh->mColors[1] = pcMesh->mColors[2] = pcMesh->mColors[3] = NULL;
- pcMesh->mNumFaces = pcSurfaces->NUM_TRIANGLES;
- pcMesh->mFaces = new aiFace[pcSurfaces->NUM_TRIANGLES];
- pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
- pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
- pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
- pcMesh->mTextureCoords[1] = pcMesh->mTextureCoords[2] = pcMesh->mTextureCoords[3] = NULL;
- pcMesh->mNumUVComponents[0] = 2;
- // fill in all vertices and normals
- // fill in all texture coordinates
- for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_VERTICES;++i)
- {
- pcMesh->mVertices[i].x = pcVertices->X;
- pcMesh->mVertices[i].y = pcVertices->Y;
- pcMesh->mVertices[i].z = -1.0f*pcVertices->Z;
- // convert the normal vector to uncompressed float3 format
- LatLngNormalToVec3(pcVertices->NORMAL,(float*)&pcMesh->mNormals[i]);
- // read texture coordinates
- pcMesh->mTextureCoords[0][i].x = pcUVs->U;
- pcMesh->mTextureCoords[0][i].y = 1.0f - pcUVs->V;
- pcVertices++;
- pcUVs++;
- }
- // fill in all triangles
- for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i)
- {
- pcMesh->mFaces[i].mIndices = new unsigned int[3];
- pcMesh->mFaces[i].mNumIndices = 3;
- pcMesh->mFaces[i].mIndices[0] = pcTriangles->INDEXES[0];
- pcMesh->mFaces[i].mIndices[1] = pcTriangles->INDEXES[1];
- pcMesh->mFaces[i].mIndices[2] = pcTriangles->INDEXES[2];
- pcTriangles++;
- }
- // get the first shader (= texture?) assigned to the surface
- if (0 != pcSurfaces->NUM_SHADER)
- {
- // make a relative path.
- // if the MD3's internal path itself and the given path are using
- // the same directory remove it
- const char* szEndDir1 = strrchr((const char*)this->m_pcHeader->NAME,'\\');
- if (!szEndDir1)szEndDir1 = strrchr((const char*)this->m_pcHeader->NAME,'/');
- const char* szEndDir2 = strrchr((const char*)pcShaders->NAME,'\\');
- if (!szEndDir2)szEndDir2 = strrchr((const char*)pcShaders->NAME,'/');
- if (szEndDir1 && szEndDir2)
- {
- // both of them are valid
- const unsigned int iLen1 = (unsigned int)(szEndDir1 - (const char*)this->m_pcHeader->NAME);
- const unsigned int iLen2 = std::min (iLen1, (unsigned int)(szEndDir2 - (const char*)pcShaders->NAME) );
- bool bSuccess = true;
- for (unsigned int a = 0; a < iLen2;++a)
- {
- char sz = tolower ( pcShaders->NAME[a] );
- char sz2 = tolower ( this->m_pcHeader->NAME[a] );
- if (sz != sz2)
- {
- bSuccess = false;
- break;
- }
- }
- if (bSuccess)
- {
- // use the file name only
- szEndDir2++;
- }
- else
- {
- // use the full path
- szEndDir2 = (const char*)pcShaders->NAME;
- }
- }
- // now try to find out whether we have this shader already
- bool bHave = false;
- for (unsigned int p = 0; p < iNumMaterials;++p)
- {
- if (iDefaultMatIndex == p)continue;
- aiString szOut;
- if(AI_SUCCESS == aiGetMaterialString ( (aiMaterial*)pScene->mMaterials[p],
- AI_MATKEY_TEXBLEND_DIFFUSE(0),&szOut))
- {
- if (0 == ASSIMP_stricmp(szOut.data,szEndDir2))
- {
- // equal. reuse this material (texture)
- bHave = true;
- pcMesh->mMaterialIndex = p;
- break;
- }
- }
- }
- if (!bHave)
- {
- MaterialHelper* pcHelper = new MaterialHelper();
- aiString szString;
- const size_t iLen = strlen(szEndDir2);
- memcpy(szString.data,szEndDir2,iLen+1);
- szString.length = iLen-1;
- pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
- int iMode = (int)aiShadingMode_Gouraud;
- pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
- 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);
- pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
- pcMesh->mMaterialIndex = iNumMaterials++;
- }
- }
- else
- {
- if (0xFFFFFFFF != iDefaultMatIndex)
- {
- pcMesh->mMaterialIndex = iDefaultMatIndex;
- }
- else
- {
- MaterialHelper* pcHelper = new MaterialHelper();
- // fill in a default material
- int iMode = (int)aiShadingMode_Gouraud;
- pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
- aiColor3D clr;
- clr.b = clr.g = clr.r = 0.6f;
- pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
- pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
- clr.b = clr.g = clr.r = 0.05f;
- pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
- pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
- pcMesh->mMaterialIndex = iNumMaterials++;
- }
- }
- pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
- }
- if (0 == pScene->mNumMeshes)
- {
- // cleanup before returning
- delete pScene;
- throw new ImportErrorException( "Invalid md3 file: File contains no valid mesh");
- }
- pScene->mNumMaterials = iNumMaterials;
- // now we need to generate an empty node graph
- pScene->mRootNode = new aiNode();
- pScene->mRootNode->mNumChildren = pScene->mNumMeshes;
- pScene->mRootNode->mChildren = new aiNode*[pScene->mNumMeshes];
- for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
- {
- pScene->mRootNode->mChildren[i] = new aiNode();
- pScene->mRootNode->mChildren[i]->mParent = pScene->mRootNode;
- pScene->mRootNode->mChildren[i]->mNumMeshes = 1;
- pScene->mRootNode->mChildren[i]->mMeshes = new unsigned int[1];
- pScene->mRootNode->mChildren[i]->mMeshes[0] = i;
- }
- delete[] this->mBuffer;
- return;
- }
|