Initiual commit: AssetImporter source moved from ZFXCE repository to its own repository. PLease do not use the ZFXCE repo any more.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
kimmi 2008-05-05 12:36:31 +00:00
commit b76f999cb7
131 changed files with 24989 additions and 0 deletions

0
CREDITS 100644
View File

0
INSTALL 100644
View File

5
README 100644
View File

@ -0,0 +1,5 @@
AssetImporter
---------------
To use the asset importer lbrary just compile it with visual-c++ 8.0 (at this
moment no other build enviroments are supported).

View File

@ -0,0 +1,827 @@
/** @file Implementation of the 3ds importer class */
#include "3DSLoader.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;
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ReplaceDefaultMaterial()
{
// try to find an existing material that matches the
// typical default material setting:
// - no textures
// - diffuse color (in grey!)
// NOTE: This is here to workaround the fact that some
// exporters are writing a default material, too.
unsigned int iIndex = 0xcdcdcdcd;
for (unsigned int i = 0; i < this->mScene->mMaterials.size();++i)
{
if (std::string::npos == this->mScene->mMaterials[i].mName.find("default") &&
std::string::npos == this->mScene->mMaterials[i].mName.find("DEFAULT"))continue;
if (this->mScene->mMaterials[i].mDiffuse.r !=
this->mScene->mMaterials[i].mDiffuse.g ||
this->mScene->mMaterials[i].mDiffuse.r !=
this->mScene->mMaterials[i].mDiffuse.b)continue;
if (this->mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 ||
this->mScene->mMaterials[i].sTexBump.mMapName.length()!= 0 ||
this->mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 ||
this->mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 ||
this->mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 ||
this->mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 )continue;
iIndex = i;
}
if (0xcdcdcdcd == iIndex)iIndex = this->mScene->mMaterials.size();
// now iterate through all meshes and through all faces and
// find all faces that are using the default material
unsigned int iCnt = 0;
for (std::vector<Dot3DS::Mesh>::iterator
i = this->mScene->mMeshes.begin();
i != this->mScene->mMeshes.end();++i)
{
for (std::vector<unsigned int>::iterator
a = (*i).mFaceMaterials.begin();
a != (*i).mFaceMaterials.end();++a)
{
// NOTE: The additional check seems to be necessary,
// some exporters seem to generate invalid data here
if (0xcdcdcdcd == (*a) || (*a) >= this->mScene->mMaterials.size())
{
(*a) = iIndex;
++iCnt;
}
}
}
if (0 != iCnt && iIndex == this->mScene->mMaterials.size())
{
// we need to create our own default material
Dot3DS::Material sMat;
sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f);
sMat.mName = "%%%DEFAULT";
this->mScene->mMaterials.push_back(sMat);
}
return;
}
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::CheckIndices(Dot3DS::Mesh* sMesh)
{
for (std::vector< Dot3DS::Face >::iterator
i = sMesh->mFaces.begin();
i != sMesh->mFaces.end();++i)
{
// check whether all indices are in range
if ((*i).i1 >= sMesh->mPositions.size())
{
(*i).i1 = sMesh->mPositions.size()-1;
}
if ((*i).i2 >= sMesh->mPositions.size())
{
(*i).i2 = sMesh->mPositions.size()-1;
}
if ((*i).i3 >= sMesh->mPositions.size())
{
(*i).i3 = sMesh->mPositions.size()-1;
}
}
return;
}
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::MakeUnique(Dot3DS::Mesh* sMesh)
{
std::vector<aiVector3D> vNew;
vNew.resize(sMesh->mFaces.size() * 3);
std::vector<aiVector2D> vNew2;
// TODO: Remove this step. By maintaining a small LUT it
// would be possible to do this directly in the parsing step
unsigned int iBase = 0;
if (0 != sMesh->mTexCoords.size())
{
vNew2.resize(sMesh->mFaces.size() * 3);
for (unsigned int i = 0; i < sMesh->mFaces.size();++i)
{
// position and texture coordinates
vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i1];
vNew2[iBase] = sMesh->mTexCoords[sMesh->mFaces[i].i1];
sMesh->mFaces[i].i1 = iBase++;
vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i2];
vNew2[iBase] = sMesh->mTexCoords[sMesh->mFaces[i].i2];
sMesh->mFaces[i].i2 = iBase++;
vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i3];
vNew2[iBase] = sMesh->mTexCoords[sMesh->mFaces[i].i3];
sMesh->mFaces[i].i3 = iBase++;
}
}
else
{
for (unsigned int i = 0; i < sMesh->mFaces.size();++i)
{
// position only
vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i1];
sMesh->mFaces[i].i1 = iBase++;
vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i2];
sMesh->mFaces[i].i2 = iBase++;
vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i3];
sMesh->mFaces[i].i3 = iBase++;
}
}
sMesh->mPositions = vNew;
sMesh->mTexCoords = vNew2;
return;
}
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
MaterialHelper& mat)
{
// NOTE: Pass the background image to the viewer by bypassing the
// material system. This is an evil hack, never do it again!
if (0 != this->mBackgroundImage.length() && this->bHasBG)
{
aiString tex;
tex.Set( this->mBackgroundImage);
mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
// be sure this is only done for the first material
this->mBackgroundImage = std::string("");
}
// At first add the base ambient color of the
// scene to the material
oldMat.mAmbient.r += this->mClrAmbient.r;
oldMat.mAmbient.g += this->mClrAmbient.g;
oldMat.mAmbient.b += this->mClrAmbient.b;
aiString name;
name.Set( oldMat.mName);
mat.AddProperty( &name, AI_MATKEY_NAME);
// material colors
mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
// opacity
mat.AddProperty<float>( &oldMat.mTransparency,1,AI_MATKEY_OPACITY);
// bump height scaling
mat.AddProperty<float>( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING);
// shading mode
aiShadingMode eShading = aiShadingMode_NoShading;
switch (oldMat.mShading)
{
case Dot3DS::Dot3DSFile::Flat:
eShading = aiShadingMode_Flat; break;
case Dot3DS::Dot3DSFile::Phong :
eShading = aiShadingMode_Phong; break;
// I don't know what "Wire" shading should be,
// assume it is simple lambertian diffuse (L dot N) shading
case Dot3DS::Dot3DSFile::Wire:
case Dot3DS::Dot3DSFile::Gouraud:
eShading = aiShadingMode_Gouraud; break;
// assume cook-torrance shading for metals.
// NOTE: I assume the real shader inside 3ds max is an anisotropic
// Phong-Blinn shader, but this is a good approximation too
case Dot3DS::Dot3DSFile::Metal :
eShading = aiShadingMode_CookTorrance; break;
}
mat.AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
// texture, if there is one
if( oldMat.sTexDiffuse.mMapName.length() > 0)
{
aiString tex;
tex.Set( oldMat.sTexDiffuse.mMapName);
mat.AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0));
mat.AddProperty<float>( &oldMat.sTexDiffuse.mTextureBlend, 1, AI_MATKEY_TEXBLEND_DIFFUSE(0));
}
if( oldMat.sTexSpecular.mMapName.length() > 0)
{
aiString tex;
tex.Set( oldMat.sTexSpecular.mMapName);
mat.AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR(0));
mat.AddProperty<float>( &oldMat.sTexSpecular.mTextureBlend, 1, AI_MATKEY_TEXBLEND_SPECULAR(0));
}
if( oldMat.sTexOpacity.mMapName.length() > 0)
{
aiString tex;
tex.Set( oldMat.sTexOpacity.mMapName);
mat.AddProperty( &tex, AI_MATKEY_TEXTURE_OPACITY(0));
mat.AddProperty<float>( &oldMat.sTexOpacity.mTextureBlend, 1,AI_MATKEY_TEXBLEND_OPACITY(0));
}
if( oldMat.sTexEmissive.mMapName.length() > 0)
{
aiString tex;
tex.Set( oldMat.sTexEmissive.mMapName);
mat.AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE(0));
mat.AddProperty<float>( &oldMat.sTexEmissive.mTextureBlend, 1, AI_MATKEY_TEXBLEND_EMISSIVE(0));
}
if( oldMat.sTexBump.mMapName.length() > 0)
{
aiString tex;
tex.Set( oldMat.sTexBump.mMapName);
mat.AddProperty( &tex, AI_MATKEY_TEXTURE_BUMP(0));
mat.AddProperty<float>( &oldMat.sTexBump.mTextureBlend, 1, AI_MATKEY_TEXBLEND_BUMP(0));
}
if( oldMat.sTexShininess.mMapName.length() > 0)
{
aiString tex;
tex.Set( oldMat.sTexShininess.mMapName);
mat.AddProperty( &tex, AI_MATKEY_TEXTURE_SHININESS(0));
mat.AddProperty<float>( &oldMat.sTexBump.mTextureBlend, 1, AI_MATKEY_TEXBLEND_SHININESS(0));
}
// store the name of the material itself, too
if( oldMat.mName.length() > 0)
{
aiString tex;
tex.Set( oldMat.mName);
mat.AddProperty( &tex, AI_MATKEY_NAME);
}
return;
}
// ------------------------------------------------------------------------------------------------
void SetupMatUVSrc (aiMaterial* pcMat, const Dot3DS::Material* pcMatIn)
{
MaterialHelper* pcHelper = (MaterialHelper*)pcMat;
pcHelper->AddProperty<int>(&pcMatIn->sTexDiffuse.iUVSrc,1,AI_MATKEY_UVWSRC_DIFFUSE(0));
pcHelper->AddProperty<int>(&pcMatIn->sTexSpecular.iUVSrc,1,AI_MATKEY_UVWSRC_SPECULAR(0));
pcHelper->AddProperty<int>(&pcMatIn->sTexEmissive.iUVSrc,1,AI_MATKEY_UVWSRC_EMISSIVE(0));
pcHelper->AddProperty<int>(&pcMatIn->sTexBump.iUVSrc,1,AI_MATKEY_UVWSRC_BUMP(0));
pcHelper->AddProperty<int>(&pcMatIn->sTexShininess.iUVSrc,1,AI_MATKEY_UVWSRC_SHININESS(0));
pcHelper->AddProperty<int>(&pcMatIn->sTexOpacity.iUVSrc,1,AI_MATKEY_UVWSRC_OPACITY(0));
}
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
{
std::vector<aiMesh*> avOutMeshes;
avOutMeshes.reserve(this->mScene->mMeshes.size() * 2);
unsigned int iFaceCnt = 0;
// we need to split all meshes by their materials
for (std::vector<Dot3DS::Mesh>::iterator
i = this->mScene->mMeshes.begin();
i != this->mScene->mMeshes.end();++i)
{
std::vector<unsigned int>* aiSplit = new std::vector<unsigned int>[
this->mScene->mMaterials.size()];
unsigned int iNum = 0;
for (std::vector<unsigned int>::const_iterator
a = (*i).mFaceMaterials.begin();
a != (*i).mFaceMaterials.end();++a,++iNum)
{
// check range
if ((*a) >= this->mScene->mMaterials.size())
{
// use the last material instead
aiSplit[this->mScene->mMaterials.size()-1].push_back(iNum);
}
else aiSplit[*a].push_back(iNum);
}
// now generate submeshes
#if 0
bool bFirst = true;
#endif
for (unsigned int p = 0; p < this->mScene->mMaterials.size();++p)
{
if (aiSplit[p].size() != 0)
{
aiMesh* p_pcOut = new aiMesh();
// be sure to setup the correct material index
p_pcOut->mMaterialIndex = p;
// use the color data as temporary storage
p_pcOut->mColors[0] = (aiColor4D*)new std::string((*i).mName);
avOutMeshes.push_back(p_pcOut);
#if 0
if (bFirst)
{
p_pcOut->mColors[1] = (aiColor4D*)new aiMatrix4x4();
*((aiMatrix4x4*)p_pcOut->mColors[1]) = (*i).mMat;
bFirst = false;
}
#endif
// convert vertices
p_pcOut->mNumVertices = aiSplit[p].size()*3;
p_pcOut->mNumFaces = aiSplit[p].size();
iFaceCnt += p_pcOut->mNumFaces;
if (p_pcOut->mNumVertices != 0)
{
p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices];
p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices];
unsigned int iBase = 0;
p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces];
for (unsigned int q = 0; q < aiSplit[p].size();++q)
{
unsigned int iIndex = aiSplit[p][q];
p_pcOut->mFaces[q].mIndices = new unsigned int[3];
p_pcOut->mFaces[q].mNumIndices = 3;
p_pcOut->mFaces[q].mIndices[0] = iBase;
p_pcOut->mVertices[iBase] = (*i).mPositions[(*i).mFaces[iIndex].i1];
p_pcOut->mNormals[iBase++] = (*i).mNormals[(*i).mFaces[iIndex].i1];
p_pcOut->mFaces[q].mIndices[1] = iBase;
p_pcOut->mVertices[iBase] = (*i).mPositions[(*i).mFaces[iIndex].i2];
p_pcOut->mNormals[iBase++] = (*i).mNormals[(*i).mFaces[iIndex].i2];
p_pcOut->mFaces[q].mIndices[2] = iBase;
p_pcOut->mVertices[iBase] = (*i).mPositions[(*i).mFaces[iIndex].i3];
p_pcOut->mNormals[iBase++] = (*i).mNormals[(*i).mFaces[iIndex].i3];
}
}
// convert texture coordinates
if ((*i).mTexCoords.size() != 0)
{
p_pcOut->mTextureCoords[0] = new aiVector3D[p_pcOut->mNumVertices];
unsigned int iBase = 0;
for (unsigned int q = 0; q < aiSplit[p].size();++q)
{
unsigned int iIndex2 = aiSplit[p][q];
unsigned int iIndex = (*i).mFaces[iIndex2].i1;
aiVector2D& pc = (*i).mTexCoords[iIndex];
p_pcOut->mTextureCoords[0][iBase++] = aiVector3D(pc.x,pc.y,0.0f);
iIndex = (*i).mFaces[iIndex2].i2;
pc = (*i).mTexCoords[iIndex];
p_pcOut->mTextureCoords[0][iBase++] = aiVector3D(pc.x,pc.y,0.0f);
iIndex = (*i).mFaces[iIndex2].i3;
pc = (*i).mTexCoords[iIndex];
p_pcOut->mTextureCoords[0][iBase++] = aiVector3D(pc.x,pc.y,0.0f);
}
// apply texture coordinate scalings
this->BakeScaleNOffset ( p_pcOut, &this->mScene->mMaterials[
p_pcOut->mMaterialIndex] );
// setup bitflags to indicate which texture coordinate
// channels are used
p_pcOut->mNumUVComponents[0] = 2;
if (p_pcOut->HasTextureCoords(1))
p_pcOut->mNumUVComponents[1] = 2;
if (p_pcOut->HasTextureCoords(2))
p_pcOut->mNumUVComponents[2] = 2;
if (p_pcOut->HasTextureCoords(3))
p_pcOut->mNumUVComponents[3] = 2;
}
}
}
delete[] aiSplit;
}
pcOut->mNumMeshes = avOutMeshes.size();
pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
for (unsigned int a = 0; a < pcOut->mNumMeshes;++a)
{
pcOut->mMeshes[a] = avOutMeshes[a];
}
if (0 == iFaceCnt)
{
throw new ImportErrorException("No faces loaded. The mesh is empty");
}
// for each material in the scene we need to setup the UV source
// set for each texture
for (unsigned int a = 0; a < pcOut->mNumMaterials;++a)
{
SetupMatUVSrc( pcOut->mMaterials[a], &this->mScene->mMaterials[a] );
}
return;
}
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pcIn)
{
// find the corresponding mesh indices
std::vector<unsigned int> iArray;
if (pcIn->mName != "$$$DUMMY")
{
for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
{
if (0 == ASSIMP_stricmp(pcIn->mName.c_str(),
((std::string*)pcSOut->mMeshes[a]->mColors[0])->c_str()))
{
iArray.push_back(a);
}
}
}
pcOut->mName.Set(pcIn->mName);
pcOut->mNumMeshes = iArray.size();
pcOut->mMeshes = new unsigned int[iArray.size()];
for (unsigned int i = 0;i < iArray.size();++i)
{
const unsigned int iIndex = iArray[i];
#if 0
if (NULL != pcSOut->mMeshes[iIndex]->mColors[1])
{
pcOut->mTransformation = *((aiMatrix4x4*)
(pcSOut->mMeshes[iIndex]->mColors[1]));
delete (aiMatrix4x4*)pcSOut->mMeshes[iIndex]->mColors[1];
pcSOut->mMeshes[iIndex]->mColors[1] = NULL;
}
#endif
pcOut->mMeshes[i] = iIndex;
}
// NOTE: Not necessary. We can use the given transformation matrix.
// However, we'd need it if we wanted to implement keyframe animation
#if 0
// build the scaling matrix. Toggle y and z axis
aiMatrix4x4 mS;
mS.a1 = pcIn->vScaling.x;
mS.b2 = pcIn->vScaling.z;
mS.c3 = pcIn->vScaling.y;
// build the translation matrix. Toggle y and z axis
aiMatrix4x4 mT;
mT.a4 = pcIn->vPosition.x;
mT.b4 = pcIn->vPosition.z;
mT.c4 = pcIn->vPosition.y;
// build the pivot matrix. Toggle y and z axis
aiMatrix4x4 mP;
mP.a4 = pcIn->vPivot.x;
mP.b4 = pcIn->vPivot.z;
mP.c4 = pcIn->vPivot.y;
#endif
pcOut->mTransformation = aiMatrix4x4(); // mT * pcIn->mRotation * mS * mP * pcOut->mTransformation.Inverse();
pcOut->mNumChildren = pcIn->mChildren.size();
pcOut->mChildren = new aiNode*[pcIn->mChildren.size()];
for (unsigned int i = 0; i < pcIn->mChildren.size();++i)
{
pcOut->mChildren[i] = new aiNode();
pcOut->mChildren[i]->mParent = pcOut;
AddNodeToGraph(pcSOut,pcOut->mChildren[i],
pcIn->mChildren[i]);
}
return;
}
// ------------------------------------------------------------------------------------------------
inline bool HasUVTransform(const Dot3DS::Texture& rcIn)
{
return (0.0f != rcIn.mOffsetU ||
0.0f != rcIn.mOffsetV ||
1.0f != rcIn.mScaleU ||
1.0f != rcIn.mScaleV ||
0.0f != rcIn.mRotation);
}
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ApplyScaleNOffset()
{
unsigned int iNum = 0;
for (std::vector<Dot3DS::Material>::iterator
i = this->mScene->mMaterials.begin();
i != this->mScene->mMaterials.end();++i,++iNum)
{
unsigned int iCnt = 0;
Dot3DS::Texture* pcTexture = NULL;
if (HasUVTransform((*i).sTexDiffuse))
{
(*i).sTexDiffuse.bPrivate = true;
pcTexture = &(*i).sTexDiffuse;
++iCnt;
}
if (HasUVTransform((*i).sTexSpecular))
{
(*i).sTexSpecular.bPrivate = true;
pcTexture = &(*i).sTexSpecular;
++iCnt;
}
if (HasUVTransform((*i).sTexOpacity))
{
(*i).sTexOpacity.bPrivate = true;
pcTexture = &(*i).sTexOpacity;
++iCnt;
}
if (HasUVTransform((*i).sTexEmissive))
{
(*i).sTexEmissive.bPrivate = true;
pcTexture = &(*i).sTexEmissive;
++iCnt;
}
if (HasUVTransform((*i).sTexBump))
{
(*i).sTexBump.bPrivate = true;
pcTexture = &(*i).sTexBump;
++iCnt;
}
if (HasUVTransform((*i).sTexShininess))
{
(*i).sTexShininess.bPrivate = true;
pcTexture = &(*i).sTexShininess;
++iCnt;
}
if (0 != iCnt)
{
// if only one texture needs scaling/offset operations
// we can apply them directly to the first texture
// coordinate sets of all meshes referencing *this* material
// However, we can't do it now. We need to wait until
// everything is sorted by materials.
if (1 == iCnt)
{
(*i).iBakeUVTransform = 1;
(*i).pcSingleTexture = pcTexture;
}
// we will need to generate a separate new texture channel
// for each texture.
// However, we can't do it now. We need to wait until
// everything is sorted by materials.
else (*i).iBakeUVTransform = 2;
}
}
}
// ------------------------------------------------------------------------------------------------
struct STransformVecInfo
{
float fScaleU;
float fScaleV;
float fOffsetU;
float fOffsetV;
float fRotation;
std::vector<Dot3DS::Texture*> pcTextures;
};
// ------------------------------------------------------------------------------------------------
void AddToList(std::vector<STransformVecInfo>& rasVec,Dot3DS::Texture* pcTex)
{
if (0 == pcTex->mMapName.length())return;
for (std::vector<STransformVecInfo>::iterator
i = rasVec.begin();
i != rasVec.end();++i)
{
if ((*i).fOffsetU == pcTex->mOffsetU &&
(*i).fOffsetV == pcTex->mOffsetV &&
(*i).fScaleU == pcTex->mScaleU &&
(*i).fScaleV == pcTex->mScaleV &&
(*i).fRotation == pcTex->mRotation)
{
(*i).pcTextures.push_back(pcTex);
return;
}
}
STransformVecInfo sInfo;
sInfo.fScaleU = pcTex->mScaleU;
sInfo.fScaleV = pcTex->mScaleV;
sInfo.fOffsetU = pcTex->mOffsetU;
sInfo.fOffsetV = pcTex->mOffsetV;
sInfo.fRotation = pcTex->mRotation;
sInfo.pcTextures.push_back(pcTex);
rasVec.push_back(sInfo);
}
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::BakeScaleNOffset(
aiMesh* pcMesh, Dot3DS::Material* pcSrc)
{
if (!pcMesh->mTextureCoords[0])return;
if (1 == pcSrc->iBakeUVTransform)
{
if (0.0f == pcSrc->pcSingleTexture->mRotation)
{
for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
{
pcMesh->mTextureCoords[0][i].x *= pcSrc->pcSingleTexture->mScaleU;
pcMesh->mTextureCoords[0][i].y *= pcSrc->pcSingleTexture->mScaleV;
pcMesh->mTextureCoords[0][i].x += pcSrc->pcSingleTexture->mOffsetU;
pcMesh->mTextureCoords[0][i].y += pcSrc->pcSingleTexture->mOffsetV;
}
}
else
{
const float fSin = sinf(pcSrc->pcSingleTexture->mRotation);
const float fCos = cosf(pcSrc->pcSingleTexture->mRotation);
for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
{
pcMesh->mTextureCoords[0][i].x *= pcSrc->pcSingleTexture->mScaleU;
pcMesh->mTextureCoords[0][i].y *= pcSrc->pcSingleTexture->mScaleV;
pcMesh->mTextureCoords[0][i].x *= fCos;
pcMesh->mTextureCoords[0][i].y *= fSin;
pcMesh->mTextureCoords[0][i].x += pcSrc->pcSingleTexture->mOffsetU;
pcMesh->mTextureCoords[0][i].y += pcSrc->pcSingleTexture->mOffsetV;
}
}
}
else if (2 == pcSrc->iBakeUVTransform)
{
// now we need to find all textures in the material
// which require scaling/offset operations
std::vector<STransformVecInfo> sOps;
AddToList(sOps,&pcSrc->sTexDiffuse);
AddToList(sOps,&pcSrc->sTexSpecular);
AddToList(sOps,&pcSrc->sTexEmissive);
AddToList(sOps,&pcSrc->sTexOpacity);
AddToList(sOps,&pcSrc->sTexBump);
AddToList(sOps,&pcSrc->sTexShininess);
const aiVector3D* _pvBase;
if (0.0f == sOps[0].fOffsetU && 0.0f == sOps[0].fOffsetV &&
1.0f == sOps[0].fScaleU && 1.0f == sOps[0].fScaleV &&
0.0f == sOps[0].fRotation)
{
// we'll have an unmodified set, so we can use *this* one
_pvBase = pcMesh->mTextureCoords[0];
}
else
{
_pvBase = new aiVector3D[pcMesh->mNumVertices];
memcpy(const_cast<aiVector3D*>(_pvBase),pcMesh->mTextureCoords[0],
pcMesh->mNumVertices * sizeof(aiVector3D));
}
unsigned int iCnt = 0;
for (std::vector<STransformVecInfo>::iterator
i = sOps.begin();
i != sOps.end();++i,++iCnt)
{
if (!pcMesh->mTextureCoords[iCnt])
{
pcMesh->mTextureCoords[iCnt] = new aiVector3D[pcMesh->mNumVertices];
}
// more than 4 UV texture channels are not available
if (iCnt > 3)
{
for (std::vector<Dot3DS::Texture*>::iterator
a = (*i).pcTextures.begin();
a != (*i).pcTextures.end();++a)
{
(*a)->iUVSrc = 0;
}
continue;
}
const aiVector3D* pvBase = _pvBase;
if (0.0f == (*i).fRotation)
{
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)
{
pcMesh->mTextureCoords[iCnt][n].x = pvBase->x * (*i).fScaleU;
pcMesh->mTextureCoords[iCnt][n].y = pvBase->y * (*i).fScaleV;
pcMesh->mTextureCoords[iCnt][n].x += (*i).fOffsetU;
pcMesh->mTextureCoords[iCnt][n].y += (*i).fOffsetV;
pvBase++;
}
}
else
{
const float fSin = sinf((*i).fRotation);
const float fCos = cosf((*i).fRotation);
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)
{
pcMesh->mTextureCoords[iCnt][n].x = pvBase->x * (*i).fScaleU;
pcMesh->mTextureCoords[iCnt][n].y = pvBase->y * (*i).fScaleV;
pcMesh->mTextureCoords[iCnt][n].x *= fCos;
pcMesh->mTextureCoords[iCnt][n].y *= fSin;
pcMesh->mTextureCoords[iCnt][n].x += (*i).fOffsetU;
pcMesh->mTextureCoords[iCnt][n].y += (*i).fOffsetV;
pvBase++;
}
}
// setup UV source
for (std::vector<Dot3DS::Texture*>::iterator
a = (*i).pcTextures.begin();
a != (*i).pcTextures.end();++a)
{
(*a)->iUVSrc = iCnt;
}
}
// release temporary storage
if (_pvBase != pcMesh->mTextureCoords[0])
delete[] _pvBase;
}
}
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::GenerateNodeGraph(aiScene* pcOut)
{
pcOut->mRootNode = new aiNode();
if (0 == this->mRootNode->mChildren.size())
{
// seems the file has not even a hierarchy.
// generate a flat hiearachy which looks like this:
//
// ROOT_NODE
// |
// ----------------------------------------
// | | | |
// MESH_0 MESH_1 MESH_2 ... MESH_N
//
unsigned int iCnt = 0;
pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes;
pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mNumMeshes ];
for (unsigned int i = 0; i < pcOut->mNumMeshes;++i)
{
aiNode* pcNode = new aiNode();
pcNode->mParent = pcOut->mRootNode;
pcNode->mNumChildren = 0;
pcNode->mChildren = 0;
pcNode->mMeshes = new unsigned int[1];
pcNode->mMeshes[0] = i;
pcNode->mNumMeshes = 1;
pcNode->mName.Set("UNNAMED");
// add the new child to the parent node
pcOut->mRootNode->mChildren[i] = pcNode;
}
}
else this->AddNodeToGraph(pcOut, pcOut->mRootNode, this->mRootNode);
for (unsigned int a = 0; a < pcOut->mNumMeshes;++a)
{
delete (std::string*)pcOut->mMeshes[a]->mColors[0];
pcOut->mMeshes[a]->mColors[0] = NULL;
// may be NULL
delete (aiMatrix4x4*)pcOut->mMeshes[a]->mColors[1];
pcOut->mMeshes[a]->mColors[1] = NULL;
}
}
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ConvertScene(aiScene* pcOut)
{
pcOut->mNumMaterials = this->mScene->mMaterials.size();
pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials];
for (unsigned int i = 0; i < pcOut->mNumMaterials;++i)
{
MaterialHelper* pcNew = new MaterialHelper();
this->ConvertMaterial(this->mScene->mMaterials[i],*pcNew);
pcOut->mMaterials[i] = pcNew;
}
this->ConvertMeshes(pcOut);
return;
}
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::GenTexCoord (Dot3DS::Texture* pcTexture,
const std::vector<aiVector2D>& p_vIn,
std::vector<aiVector2D>& p_vOut)
{
p_vOut.resize(p_vIn.size());
std::vector<aiVector2D>::const_iterator i = p_vIn.begin();
std::vector<aiVector2D>::iterator a = p_vOut.begin();
for(;i != p_vOut.end();++i,++a)
{
// TODO: Find out in which order 3ds max is performing
// scaling and translation. However it seems reasonable to
// scale first.
//
// TODO: http://www.jalix.org/ressources/graphics/3DS/_specifications/3ds-0.1.htm
// says it is not u and v scale but 1/u and 1/v scale. Other sources
// tell different things. Believe this one, the author seems to be funny
// or drunken or both ;-)
(*a) = (*i);
(*a).x /= pcTexture->mScaleU;
(*a).y /= pcTexture->mScaleV;
(*a).x += pcTexture->mOffsetU;
(*a).y += pcTexture->mOffsetV;
}
return;
}

View File

@ -0,0 +1,134 @@
/** @file Implementation of the 3ds importer class */
#include "3DSLoader.h"
#include "MaterialSystem.h"
#include <algorithm>
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
#include "../include/aiAssert.h"
#include "3DSSpatialSort.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
void Dot3DSImporter::GenNormals(Dot3DS::Mesh* sMesh)
{
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// First generate face normals
sMesh->mNormals.resize(sMesh->mPositions.size(),aiVector3D());
for( unsigned int a = 0; a < sMesh->mFaces.size(); a++)
{
const Dot3DS::Face& face = sMesh->mFaces[a];
// assume it is a triangle
aiVector3D* pV1 = &sMesh->mPositions[face.i1];
aiVector3D* pV2 = &sMesh->mPositions[face.i2];
aiVector3D* pV3 = &sMesh->mPositions[face.i3];
aiVector3D pDelta1 = *pV2 - *pV1;
aiVector3D pDelta2 = *pV3 - *pV1;
aiVector3D vNor = pDelta1 ^ pDelta2;
//float fLength = vNor.Length();
//if (0.0f != fLength)vNor /= fLength;
sMesh->mNormals[face.i1] = vNor;
sMesh->mNormals[face.i2] = vNor;
sMesh->mNormals[face.i3] = vNor;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// calculate the position bounds so we have a reliable epsilon to
// check position differences against
// @Schrompf: This is the 6th time this snippet is repeated!
aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
for( unsigned int a = 0; a < sMesh->mPositions.size(); a++)
{
minVec.x = std::min( minVec.x, sMesh->mPositions[a].x);
minVec.y = std::min( minVec.y, sMesh->mPositions[a].y);
minVec.z = std::min( minVec.z, sMesh->mPositions[a].z);
maxVec.x = std::max( maxVec.x, sMesh->mPositions[a].x);
maxVec.y = std::max( maxVec.y, sMesh->mPositions[a].y);
maxVec.z = std::max( maxVec.z, sMesh->mPositions[a].z);
}
const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
std::vector<aiVector3D> avNormals;
avNormals.resize(sMesh->mNormals.size());
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// now generate the spatial sort tree
D3DSSpatialSorter sSort(sMesh);
for( std::vector<Dot3DS::Face>::iterator
i = sMesh->mFaces.begin();
i != sMesh->mFaces.end();++i)
{
std::vector<unsigned int> poResult;
// need to repeat the code for all three vertices of the triangle
// vertex 1
sSort.FindPositions(sMesh->mPositions[(*i).i1],(*i).iSmoothGroup,
posEpsilon,poResult);
aiVector3D vNormals;
float fDiv = 0.0f;
for (std::vector<unsigned int>::const_iterator
a = poResult.begin();
a != poResult.end();++a)
{
vNormals += sMesh->mNormals[(*a)];
fDiv += 1.0f;
}
vNormals.x /= fDiv;
vNormals.y /= fDiv;
vNormals.z /= fDiv;
vNormals.Normalize();
avNormals[(*i).i1] = vNormals;
poResult.clear();
// vertex 2
sSort.FindPositions(sMesh->mPositions[(*i).i2],(*i).iSmoothGroup,
posEpsilon,poResult);
vNormals = aiVector3D();
fDiv = 0.0f;
for (std::vector<unsigned int>::const_iterator
a = poResult.begin();
a != poResult.end();++a)
{
vNormals += sMesh->mNormals[(*a)];
fDiv += 1.0f;
}
vNormals.x /= fDiv;
vNormals.y /= fDiv;
vNormals.z /= fDiv;
vNormals.Normalize();
avNormals[(*i).i2] = vNormals;
poResult.clear();
// vertex 3
sSort.FindPositions(sMesh->mPositions[(*i).i3],(*i).iSmoothGroup,
posEpsilon ,poResult);
vNormals = aiVector3D();
fDiv = 0.0f;
for (std::vector<unsigned int>::const_iterator
a = poResult.begin();
a != poResult.end();++a)
{
vNormals += sMesh->mNormals[(*a)];
fDiv += 1.0f;
}
vNormals.x /= fDiv;
vNormals.y /= fDiv;
vNormals.z /= fDiv;
vNormals.Normalize();
avNormals[(*i).i3] = vNormals;
}
sMesh->mNormals = avNormals;
return;
}

472
code/3DSHelper.h 100644
View File

@ -0,0 +1,472 @@
/** @file Defines the helper data structures for importing XFiles */
#ifndef AI_3DSFILEHELPER_H_INC
#define AI_3DSFILEHELPER_H_INC
#include <string>
#include <vector>
#include <sstream>
#include "../include/aiTypes.h"
#include "../include/aiQuaternion.h"
#include "../include/aiMesh.h"
#include "../include/aiAnim.h"
#include "SpatialSort.h"
namespace Assimp
{
namespace Dot3DS
{
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack(push,2)
# define PACK_STRUCT
#elif defined( __GNUC__ )
# define PACK_STRUCT __attribute__((packed))
#else
# error Compiler not supported
#endif
// ---------------------------------------------------------------------------
/** Dot3DSFile class: Helper class for loading 3ds files. Defines chunks
* and data structures.
*/
class Dot3DSFile
{
public:
inline Dot3DSFile() {}
// data structure for a single chunk in a .3ds file
struct Chunk
{
unsigned short Flag;
long Size;
} PACK_STRUCT;
// source for this used own structures,
// replaced it with out standard math helpers
typedef aiMatrix3x3 MatTransform;
typedef aiVector3D MatTranslate;
// Used for shading field in material3ds structure
// From AutoDesk 3ds SDK
typedef enum
{
Wire = 0,
Flat = 1,
Gouraud = 2,
Phong = 3,
Metal = 4
} shadetype3ds;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// enum for all chunks in 3ds files. Unused
// ones are commented, list is not complete since
// there are many undocumented chunks.
//
// Links: http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
enum
{
// **************************************************************
// Base chunks which can be found everywhere in the file
CHUNK_VERSION = 0x0002,
CHUNK_RGBF = 0x0010, // float4 R; float4 G; float4 B
CHUNK_RGBB = 0x0011, // int1 R; int1 G; int B
// Linear color values (gamma = 2.2?)
CHUNK_LINRGBF = 0x0013, // float4 R; float4 G; float4 B
CHUNK_LINRGBB = 0x0012, // int1 R; int1 G; int B
CHUNK_PERCENTW = 0x0030, // int2 percentage
CHUNK_PERCENTF = 0x0031, // float4 percentage
// **************************************************************
// Unknown and ignored
CHUNK_PRJ = 0xC23D,
// Unknown. Possibly a reference to an external .mli file?
CHUNK_MLI = 0x3DAA,
// Primary main chunk of the .3ds file
CHUNK_MAIN = 0x4D4D,
// Mesh main chunk
CHUNK_OBJMESH = 0x3D3D,
// Specifies the background color of the .3ds file
// This is passed through the material system for
// viewing purposes.
CHUNK_BKGCOLOR = 0x1200,
// Specifies the ambient base color of the scene.
// This is added to all materials in the file
CHUNK_AMBCOLOR = 0x2100,
// Specifies the background image for the whole scene
// This value is passed through the material system
// to the viewer
CHUNK_BIT_MAP = 0x1100,
CHUNK_BIT_MAP_EXISTS = 0x1101,
// **************************************************************
// Viewport related stuff. Ignored
CHUNK_DEFAULT_VIEW = 0x3000,
CHUNK_VIEW_TOP = 0x3010,
CHUNK_VIEW_BOTTOM = 0x3020,
CHUNK_VIEW_LEFT = 0x3030,
CHUNK_VIEW_RIGHT = 0x3040,
CHUNK_VIEW_FRONT = 0x3050,
CHUNK_VIEW_BACK = 0x3060,
CHUNK_VIEW_USER = 0x3070,
CHUNK_VIEW_CAMERA = 0x3080,
// **************************************************************
// Mesh chunks
CHUNK_OBJBLOCK = 0x4000,
CHUNK_TRIMESH = 0x4100,
CHUNK_VERTLIST = 0x4110,
CHUNK_VERTFLAGS = 0x4111,
CHUNK_FACELIST = 0x4120,
CHUNK_FACEMAT = 0x4130,
CHUNK_MAPLIST = 0x4140,
CHUNK_SMOOLIST = 0x4150,
CHUNK_TRMATRIX = 0x4160,
CHUNK_MESHCOLOR = 0x4165,
CHUNK_TXTINFO = 0x4170,
CHUNK_LIGHT = 0x4600,
CHUNK_SPOTLIGHT = 0x4610,
CHUNK_CAMERA = 0x4700,
CHUNK_HIERARCHY = 0x4F00,
// Specifies the global scaling factor. This is applied
// to the root node's transformation matrix
CHUNK_MASTER_SCALE = 0x0100,
// **************************************************************
// Material chunks
CHUNK_MAT_MATERIAL = 0xAFFF,
// asciiz containing the name of the material
CHUNK_MAT_MATNAME = 0xA000,
CHUNK_MAT_AMBIENT = 0xA010, // followed by color chunk
CHUNK_MAT_DIFFUSE = 0xA020, // followed by color chunk
CHUNK_MAT_SPECULAR = 0xA030, // followed by color chunk
// Specifies the shininess of the material
// followed by percentage chunk
CHUNK_MAT_SHININESS = 0xA040,
// Specifies the shading mode to be used
// followed by a short
CHUNK_MAT_SHADING = 0xA100,
// NOTE: Emissive color (self illumination) seems not
// to be a color but a single value, type is unknown.
// Make the parser accept both of them.
// followed by percentage chunk (?)
CHUNK_MAT_SELF_ILLUM = 0xA080,
// Always followed by percentage chunk (?)
CHUNK_MAT_SELF_ILPCT = 0xA084,
// Always followed by percentage chunk
CHUNK_MAT_TRANSPARENCY = 0xA050,
// Diffuse texture channel 0
CHUNK_MAT_TEXTURE = 0xA200,
// Contains opacity information for each texel
CHUNK_MAT_OPACMAP = 0xA210,
// Contains a reflection map to be used to reflect
// the environment. This is partially supported.
CHUNK_MAT_REFLMAP = 0xA220,
// Self Illumination map (emissive colors)
CHUNK_MAT_SELFIMAP = 0xA33d,
// Bumpmap. Not specified whether it is a heightmap
// or a normal map. Assme it is a heightmap since
// artist normally prefer this format.
CHUNK_MAT_BUMPMAP = 0xA230,
// Specular map. Seems to influence the specular color
CHUNK_MAT_SPECMAP = 0xA204,
// Holds shininess data. I assume the specular exponent is
// calculated like this:
//
// s[x,y] = stex[x,y] * base_shininess;
// I also assume the data in the texture must be renormalized
// (normally by dividing / 255) after loading.
CHUNK_MAT_MAT_SHINMAP = 0xA33C,
// Scaling in U/V direction.
// (need to gen separate UV coordinate set
// and do this by hand)
CHUNK_MAT_MAP_USCALE = 0xA354,
CHUNK_MAT_MAP_VSCALE = 0xA356,
// Translation in U/V direction.
// (need to gen separate UV coordinate set
// and do this by hand)
CHUNK_MAT_MAP_UOFFSET = 0xA358,
CHUNK_MAT_MAP_VOFFSET = 0xA35a,
// UV-coordinates rotation around the z-axis
// Assumed to be in radians.
CHUNK_MAT_MAP_ANG = 0xA35C,
// Specifies the file name of a texture
CHUNK_MAPFILE = 0xA300,
// **************************************************************
// Main keyframer chunk. Contains translation/rotation/scaling data
CHUNK_KEYFRAMER = 0xB000,
// Supported sub chunks
CHUNK_TRACKINFO = 0xB002,
CHUNK_TRACKOBJNAME = 0xB010,
CHUNK_TRACKPIVOT = 0xB013,
CHUNK_TRACKPOS = 0xB020,
CHUNK_TRACKROTATE = 0xB021,
CHUNK_TRACKSCALE = 0xB022,
// **************************************************************
// Keyframes for various other stuff in the file
// Ignored
CHUNK_AMBIENTKEY = 0xB001,
CHUNK_TRACKMORPH = 0xB026,
CHUNK_TRACKHIDE = 0xB029,
CHUNK_OBJNUMBER = 0xB030,
CHUNK_TRACKCAMERA = 0xB003,
CHUNK_TRACKFOV = 0xB023,
CHUNK_TRACKROLL = 0xB024,
CHUNK_TRACKCAMTGT = 0xB004,
CHUNK_TRACKLIGHT = 0xB005,
CHUNK_TRACKLIGTGT = 0xB006,
CHUNK_TRACKSPOTL = 0xB007,
CHUNK_FRAMES = 0xB008
// **************************************************************
};
};
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack( pop )
#endif
#undef PACK_STRUCT
// ---------------------------------------------------------------------------
/** Helper structure representing a 3ds mesh face */
struct Face
{
Face() : iSmoothGroup(0), bDirection(true)
{
// let the rest uninitialized for performance
}
// Indices. .3ds is using uint16. However, after
// an unique vrtex set has been geneerated it might
// be an index becomes > 2^16
uint32_t i1;
uint32_t i2;
uint32_t i3;
// specifies to which smoothing group the face belongs to
uint32_t iSmoothGroup;
// Direction the normal vector of the face
// will be pointing to
bool bDirection;
};
// ---------------------------------------------------------------------------
/** Helper structure representing a texture */
struct Texture
{
Texture()
:
mScaleU(1.0f),
mScaleV(1.0f),
mOffsetU(0.0f),
mOffsetV(0.0f),
mRotation(0.0f),
iUVSrc(0)
{
mTextureBlend = std::numeric_limits<float>::quiet_NaN();
}
// Specifies the blending factor for the texture
float mTextureBlend;
// Specifies the filename of the texture
std::string mMapName;
// Specifies texture coordinate offsets/scaling/rotations
float mScaleU;
float mScaleV;
float mOffsetU;
float mOffsetV;
float mRotation;
// Used internally
bool bPrivate;
int iUVSrc;
};
// ---------------------------------------------------------------------------
/** Helper structure representing a 3ds material */
struct Material
{
Material()
:
mSpecularExponent (0.0f),
mShading(Dot3DSFile::Gouraud),
mTransparency (1.0f),
mBumpHeight (1.0f),
iBakeUVTransform (0),
pcSingleTexture (NULL)
{
static int iCnt = 0;
std::stringstream ss(mName);
ss << "%%_UNNAMED_" << iCnt++ << "_%%";
}
// Name of the material
std::string mName;
// Diffuse color of the material
aiColor3D mDiffuse;
// Specular exponent
float mSpecularExponent;
// Specular color of the material
aiColor3D mSpecular;
// Ambient color of the material
aiColor3D mAmbient;
// Shading type to be used
Dot3DSFile::shadetype3ds mShading;
// Opacity of the material
float mTransparency;
// Different texture channels
Texture sTexDiffuse;
Texture sTexOpacity;
Texture sTexSpecular;
Texture sTexBump;
Texture sTexEmissive;
Texture sTexShininess;
/*
float mReflectionTextureBlend;
std::string mReflectionTexture;
*/
float mBumpHeight;
// Emissive color
aiColor3D mEmissive;
// Used internally
unsigned int iBakeUVTransform;
Texture* pcSingleTexture;
};
// ---------------------------------------------------------------------------
/** Helper structure to represent a 3ds file mesh */
struct Mesh
{
Mesh()
{
static int iCnt = 0;
std::stringstream ss(mName);
ss << "%%_UNNAMED_" << iCnt++ << "_%%";
#if 0
for (unsigned int i = 0; i < 32;++i)
bSmoothGroupRequired[i] = false;
#endif
}
std::string mName;
std::vector<aiVector3D> mPositions;
std::vector<Face> mFaces;
std::vector<aiVector2D> mTexCoords;
std::vector<unsigned int> mFaceMaterials;
std::vector<aiVector3D> mNormals;
#if 0
bool bSmoothGroupRequired[32];
#endif
aiMatrix4x4 mMat;
};
// ---------------------------------------------------------------------------
/** Helper structure to represent a 3ds file node */
struct Node
{
Node()
#if 0
: vScaling(1.0f,1.0f,1.0f)
#endif
{
static int iCnt = 0;
std::stringstream ss(mName);
ss << "%%_UNNAMED_" << iCnt++ << "_%%";
mHierarchyPos = 0;
mHierarchyIndex = 0;
}
Node* mParent;
std::vector<Node*> mChildren;
std::string mName;
int16_t mHierarchyPos;
int16_t mHierarchyIndex;
#if 0
aiVector3D vPivot;
aiVector3D vScaling;
aiMatrix4x4 mRotation;
aiVector3D vPosition;
#endif
inline Node& push_back(Node* pc)
{
mChildren.push_back(pc);
pc->mParent = this;
pc->mHierarchyPos = this->mHierarchyPos+1;
return *this;
}
};
// ---------------------------------------------------------------------------
/** Helper structure analogue to aiScene */
struct Scene
{
// NOTE: 3ds references materials globally
std::vector<Material> mMaterials;
std::vector<Mesh> mMeshes;
Node* pcRootNode;
};
// ---------------------------------------------------------------------------
inline bool is_qnan(float p_fIn)
{
// NOTE: Comparison against qnan is generally problematic
// because qnan == qnan is false AFAIK
union FTOINT
{
float fFloat;
int32_t iInt;
} one, two;
one.fFloat = std::numeric_limits<float>::quiet_NaN();
two.fFloat = p_fIn;
return (one.iInt == two.iInt);
}
// ---------------------------------------------------------------------------
inline bool is_not_qnan(float p_fIn)
{
return !is_qnan(p_fIn);
}
} // end of namespace Dot3DS
} // end of namespace Assimp
#endif // AI_XFILEHELPER_H_INC

1310
code/3DSLoader.cpp 100644

File diff suppressed because it is too large Load Diff

235
code/3DSLoader.h 100644
View File

@ -0,0 +1,235 @@
/** @file Definition of the .3ds importer class. */
#ifndef AI_3DSIMPORTER_H_INC
#define AI_3DSIMPORTER_H_INC
#include <map>
#include "BaseImporter.h"
#include "../include/aiTypes.h"
struct aiNode;
#include "3DSHelper.h"
namespace Assimp
{
class MaterialHelper;
using namespace Dot3DS;
// ---------------------------------------------------------------------------
/** The Dot3DSImporter is a worker class capable of importing a scene from a
* 3ds Max 4/5 File (.3ds)
*/
class Dot3DSImporter : public BaseImporter
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
Dot3DSImporter();
/** Destructor, private as well */
~Dot3DSImporter();
public:
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details. */
bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
protected:
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
// -------------------------------------------------------------------
/** Converts a temporary material to the outer representation
*/
void ConvertMaterial(Dot3DS::Material& p_cMat,
MaterialHelper& p_pcOut);
// -------------------------------------------------------------------
/** Read a chunk, get a pointer to it
* The mCurrent pointer will be increased by sizeof(Dot3DSFile::Chunk),
* thus pointing directly to the data of the chunk
*/
void ReadChunk(const Dot3DSFile::Chunk** p_ppcOut);
// -------------------------------------------------------------------
/** Parse a percentage chunk. mCurrent will point to the next
* chunk behind afterwards. If no percentage chunk is found
* QNAN is returned.
*/
float ParsePercentageChunk();
// -------------------------------------------------------------------
/** Parse a color chunk. mCurrent will point to the next
* chunk behind afterwards. If no color chunk is found
* QNAN is returned in all members.
*/
void ParseColorChunk(aiColor3D* p_pcOut,
bool p_bAcceptPercent = true);
// -------------------------------------------------------------------
/** Skip a chunk in the file
*/
void SkipChunk();
// -------------------------------------------------------------------
/** Generate the nodegraph
*/
void GenerateNodeGraph(aiScene* pcOut);
// -------------------------------------------------------------------
/** Parse a main top-level chunk in the file
*/
void ParseMainChunk(int* piRemaining);
// -------------------------------------------------------------------
/** Parse a top-level chunk in the file
*/
void ParseChunk(int* piRemaining);
// -------------------------------------------------------------------
/** Parse a top-level editor chunk in the file
*/
void ParseEditorChunk(int* piRemaining);
// -------------------------------------------------------------------
/** Parse a top-level object chunk in the file
*/
void ParseObjectChunk(int* piRemaining);
// -------------------------------------------------------------------
/** Parse a material chunk in the file
*/
void ParseMaterialChunk(int* piRemaining);
// -------------------------------------------------------------------
/** Apply texture coordinate offsets
*/
void ApplyScaleNOffset();
void BakeScaleNOffset(aiMesh* pcMesh, Dot3DS::Material* pcSrc);
// -------------------------------------------------------------------
/** Parse a mesh chunk in the file
*/
void ParseMeshChunk(int* piRemaining);
// -------------------------------------------------------------------
/** Parse a face list chunk in the file
*/
void ParseFaceChunk(int* piRemaining);
// -------------------------------------------------------------------
/** Parse a keyframe chunk in the file
*/
void ParseKeyframeChunk(int* piRemaining);
// -------------------------------------------------------------------
/** Parse a hierarchy chunk in the file
*/
void ParseHierarchyChunk(int* piRemaining);
// -------------------------------------------------------------------
/** Parse a texture chunk in the file
*/
void ParseTextureChunk(int* piRemaining,Dot3DS::Texture* pcOut);
// -------------------------------------------------------------------
/** Convert the meshes in the file
*/
void ConvertMeshes(aiScene* pcOut);
// -------------------------------------------------------------------
/** Replace the default material in the scene
*/
void ReplaceDefaultMaterial();
// -------------------------------------------------------------------
/** Convert the whole scene
*/
void ConvertScene(aiScene* pcOut);
// -------------------------------------------------------------------
/** U/V Scaling/Offset handling
*/
void GenTexCoord (Dot3DS::Texture* pcTexture,
const std::vector<aiVector2D>& p_vIn,
std::vector<aiVector2D>& p_vOut);
// -------------------------------------------------------------------
/** generate normal vectors for a given mesh
*/
void GenNormals(Dot3DS::Mesh* sMesh);
// -------------------------------------------------------------------
/** generate unique vertices for a mesh
*/
void MakeUnique(Dot3DS::Mesh* sMesh);
// -------------------------------------------------------------------
/** Add a node to the node graph
*/
void AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pcIn);
// -------------------------------------------------------------------
/** Search for a node in the graph.
* Called recursively
*/
void InverseNodeSearch(Dot3DS::Node* pcNode,Dot3DS::Node* pcCurrent);
// -------------------------------------------------------------------
/** Apply the master scaling factor to the mesh
*/
void ApplyMasterScale(aiScene* pScene);
// -------------------------------------------------------------------
/** Clamp all indices in the file to a valid range
*/
void Dot3DSImporter::CheckIndices(Dot3DS::Mesh* sMesh);
protected:
/** Buffer to hold the loaded file */
unsigned char* mBuffer;
/** Pointer to the current read position */
const unsigned char* mCurrent;
/** Used to store old chunk addresses to jump back in the file*/
const unsigned char* mLast;
/** Last touched node index */
short mLastNodeIndex;
/** Current node, root node */
Dot3DS::Node* mCurrentNode, *mRootNode;
/** Scene under construction */
Dot3DS::Scene* mScene;
/** Ambient base color of the scene */
aiColor3D mClrAmbient;
/** Master scaling factor of the scene */
float mMasterScale;
/** Path to the background image of the scene */
std::string mBackgroundImage;
bool bHasBG;
};
} // end of namespace Assimp
#endif // AI_3DSIMPORTER_H_INC

View File

@ -0,0 +1,148 @@
/** @file Implementation of the helper class to quickly find vertices close to a given position */
#include <algorithm>
#include "3DSSpatialSort.h"
using namespace Assimp;
using namespace Assimp::Dot3DS;
// ------------------------------------------------------------------------------------------------
// Constructs a spatially sorted representation from the given position array.
D3DSSpatialSorter::D3DSSpatialSorter( const aiVector3D* pPositions,
unsigned int pNumPositions, unsigned int pElementOffset)
{
// define the reference plane. We choose some arbitrary vector away from all basic axises
// in the hope that no model spreads all its vertices along this plane.
mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
mPlaneNormal.Normalize();
// store references to all given positions along with their distance to the reference plane
mPositions.reserve( pNumPositions);
for( unsigned int a = 0; a < pNumPositions; a++)
{
const char* tempPointer = reinterpret_cast<const char*> (pPositions);
const aiVector3D* vec = reinterpret_cast<const aiVector3D*> (tempPointer + a * pElementOffset);
// store position by index and distance
float distance = *vec * mPlaneNormal;
mPositions.push_back( Entry( a, *vec, distance,0));
}
// now sort the array ascending by distance.
std::sort( mPositions.begin(), mPositions.end());
}
// ------------------------------------------------------------------------------------------------
D3DSSpatialSorter::D3DSSpatialSorter( const Dot3DS::Mesh* p_pcMesh)
{
// define the reference plane. We choose some arbitrary vector away from all basic axises
// in the hope that no model spreads all its vertices along this plane.
mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
mPlaneNormal.Normalize();
// store references to all given positions along with their distance to the reference plane
mPositions.reserve( p_pcMesh->mPositions.size());
for( std::vector<Dot3DS::Face>::const_iterator
i = p_pcMesh->mFaces.begin();
i != p_pcMesh->mFaces.end();++i)
{
// store position by index and distance
float distance = p_pcMesh->mPositions[(*i).i1] * mPlaneNormal;
mPositions.push_back( Entry( (*i).i1, p_pcMesh->mPositions[(*i).i1],
distance, (*i).iSmoothGroup));
// triangle vertex 2
distance = p_pcMesh->mPositions[(*i).i2] * mPlaneNormal;
mPositions.push_back( Entry( (*i).i2, p_pcMesh->mPositions[(*i).i2],
distance, (*i).iSmoothGroup));
// triangle vertex 3
distance = p_pcMesh->mPositions[(*i).i3] * mPlaneNormal;
mPositions.push_back( Entry( (*i).i3, p_pcMesh->mPositions[(*i).i3],
distance, (*i).iSmoothGroup));
}
// now sort the array ascending by distance.
std::sort( this->mPositions.begin(), this->mPositions.end());
}
// ------------------------------------------------------------------------------------------------
// Destructor
D3DSSpatialSorter::~D3DSSpatialSorter()
{
// nothing to do here, everything destructs automatically
}
// ------------------------------------------------------------------------------------------------
// Returns an iterator for all positions close to the given position.
void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition,
uint32_t pSG,float pRadius, std::vector<unsigned int>& poResults) const
{
float dist = pPosition * mPlaneNormal;
float minDist = dist - pRadius, maxDist = dist + pRadius;
// clear the array in this strange fashion because a simple clear() would also deallocate
// the array which we want to avoid
poResults.erase( poResults.begin(), poResults.end());
// quick check for positions outside the range
if( mPositions.size() == 0)
return;
if( maxDist < mPositions.front().mDistance)
return;
if( minDist > mPositions.back().mDistance)
return;
// do a binary search for the minimal distance to start the iteration there
unsigned int index = mPositions.size() / 2;
unsigned int binaryStepSize = mPositions.size() / 4;
while( binaryStepSize > 1)
{
if( mPositions[index].mDistance < minDist)
index += binaryStepSize;
else
index -= binaryStepSize;
binaryStepSize /= 2;
}
// depending on the direction of the last step we need to single step a bit back or forth
// to find the actual beginning element of the range
while( index > 0 && mPositions[index].mDistance > minDist)
index--;
while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
index++;
// Mow start iterating from there until the first position lays outside of the distance range.
// Add all positions inside the distance range within the given radius to the result aray
if (0 == pSG)
{
std::vector<Entry>::const_iterator it = mPositions.begin() + index;
float squareEpsilon = pRadius * pRadius;
while( it->mDistance < maxDist)
{
if((it->mPosition - pPosition).SquareLength() < squareEpsilon)
{
poResults.push_back( it->mIndex);
}
++it;
if( it == mPositions.end())
break;
}
}
else
{
std::vector<Entry>::const_iterator it = mPositions.begin() + index;
float squareEpsilon = pRadius * pRadius;
while( it->mDistance < maxDist)
{
if((it->mPosition - pPosition).SquareLength() < squareEpsilon &&
(it->mSmoothGroups & pSG || 0 == it->mSmoothGroups))
{
poResults.push_back( it->mIndex);
}
++it;
if( it == mPositions.end())
break;
}
}
}

View File

@ -0,0 +1,83 @@
/** Small helper classes to optimise finding vertizes close to a given location */
#ifndef AI_D3DSSPATIALSORT_H_INC
#define AI_D3DSSPATIALSORT_H_INC
#include <vector>
#include "../include/aiVector3D.h"
#include "3DSHelper.h"
namespace Assimp
{
using namespace Dot3DS;
// ------------------------------------------------------------------------------------------------
/** Specialized version of SpatialSort to support smoothing groups
* This is used in the .3ds loader
*/
class D3DSSpatialSorter
{
public:
D3DSSpatialSorter() {/* This is unintialized. This is evil. This is OK. */}
/** Constructs a spatially sorted representation from the given position array.
* Supply the positions in its layout in memory, the class will only refer to them
* by index.
* @param pPositions Pointer to the first position vector of the array.
* @param pNumPositions Number of vectors to expect in that array.
* @param pElementOffset Offset in bytes from the beginning of one vector in memory to the beginning of the next vector.
* @note Smoothing groups are ignored
*/
D3DSSpatialSorter( const aiVector3D* pPositions,
unsigned int pNumPositions, unsigned int pElementOffset);
/** Construction from a given face array, handling smoothing groups properly
* @param p_pcMesh Input mesh.
*/
D3DSSpatialSorter( const Dot3DS::Mesh* p_pcMesh);
/** Destructor */
~D3DSSpatialSorter();
/** Returns an iterator for all positions close to the given position.
* @param pPosition The position to look for vertices.
* @param pSG Only included vertices with at least one shared smooth group
* @param pRadius Maximal distance from the position a vertex may have to be counted in.
* @param poResults The container to store the indices of the found positions. Will be emptied
* by the call so it may contain anything.
* @return An iterator to iterate over all vertices in the given area.
*/
void FindPositions( const aiVector3D& pPosition, uint32_t pSG,
float pRadius, std::vector<unsigned int>& poResults) const;
protected:
/** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */
aiVector3D mPlaneNormal;
/** An entry in a spatially sorted position array. Consists of a vertex index,
* its position and its precalculated distance from the reference plane */
struct Entry
{
unsigned int mIndex; ///< The vertex referred by this entry
aiVector3D mPosition; ///< Position
uint32_t mSmoothGroups;
float mDistance; ///< Distance of this vertex to the sorting plane
Entry() { /** intentionally not initialized.*/ }
Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance,uint32_t pSG)
:
mPosition( pPosition), mIndex( pIndex), mDistance( pDistance),mSmoothGroups (pSG)
{ }
bool operator < (const Entry& e) const { return mDistance < e.mDistance; }
};
// all positions, sorted by distance to the sorting plane
std::vector<Entry> mPositions;
};
} // end of namespace Assimp
#endif // AI_SPATIALSORT_H_INC

61
code/Assimp.cpp 100644
View File

@ -0,0 +1,61 @@
/** @file Implementation of the Plain-C API */
#include <map>
#include "../include/assimp.h"
#include "../include/assimp.hpp"
/** Stores the importer objects for all active import processes */
typedef std::map< const aiScene*, Assimp::Importer* > ImporterMap;
/** Local storage of all active import processes */
static ImporterMap gActiveImports;
/** Error message of the last failed import process */
static std::string gLastErrorString;
// ------------------------------------------------------------------------------------------------
// Reads the given file and returns its content.
const aiScene* aiImportFile( const char* pFile, unsigned int pFlags)
{
// create an Importer for this file
Assimp::Importer* imp = new Assimp::Importer;
// and have it read the file
const aiScene* scene = imp->ReadFile( pFile, pFlags);
// if succeeded, place it in the collection of active processes
if( scene)
{
gActiveImports[scene] = imp;
}
else
{
// if failed, extract error code and destroy the import
gLastErrorString = imp->GetErrorString();
delete imp;
}
// return imported data. If the import failed the pointer is NULL anyways
return scene;
}
// ------------------------------------------------------------------------------------------------
// Releases all resources associated with the given import process.
void aiReleaseImport( const aiScene* pScene)
{
// find the importer associated with this data
ImporterMap::iterator it = gActiveImports.find( pScene);
// it should be there... else the user is playing fools with us
if( it == gActiveImports.end())
return;
// kill the importer, the data dies with it
delete it->second;
gActiveImports.erase( it);
}
// ------------------------------------------------------------------------------------------------
// Returns the error text of the last failed import process.
const char* aiGetErrorString()
{
return gLastErrorString.c_str();
}

View File

@ -0,0 +1,45 @@
/** @file Implementation of the few default functions of the base importer class */
#include "BaseImporter.h"
#include "../include/aiScene.h"
#include "aiAssert.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
BaseImporter::BaseImporter()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
BaseImporter::~BaseImporter()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Imports the given file and returns the imported data.
aiScene* BaseImporter::ReadFile( const std::string& pFile, IOSystem* pIOHandler)
{
// create a scene object to hold the data
aiScene* scene = new aiScene;
// dispatch importing
try
{
InternReadFile( pFile, scene, pIOHandler);
} catch( ImportErrorException* exception)
{
// extract error description
mErrorText = exception->GetErrorText();
delete exception;
// and kill the partially imported data
delete scene;
scene = NULL;
}
// return what we gathered from the import.
return scene;
}

105
code/BaseImporter.h 100644
View File

@ -0,0 +1,105 @@
/** @file Definition of the base class for all importer worker classes. */
#ifndef AI_BASEIMPORTER_H_INC
#define AI_BASEIMPORTER_H_INC
#include <string>
struct aiScene;
namespace Assimp
{
class IOSystem;
// ---------------------------------------------------------------------------
/** Simple exception class to be thrown if an error occurs while importing. */
class ImportErrorException
{
public:
/** Constructor with arguments */
ImportErrorException( const std::string& pErrorText)
{
mErrorText = pErrorText;
}
/** Returns the error text provided when throwing the exception */
const std::string& GetErrorText() const { return mErrorText; }
private:
std::string mErrorText;
};
// ---------------------------------------------------------------------------
/** The BaseImporter defines a common interface for all importer worker
* classes.
*
* The interface defines two functions: CanRead() is used to check if the
* importer can handle the format of the given file. If an implementation of
* this function returns true, the importer then calls ReadFile() which
* imports the given file. ReadFile is not overridable, it just calls InternReadFile()
* and catches any ImportErrorException that might occur.
*/
class BaseImporter
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
BaseImporter();
/** Destructor, private as well */
virtual ~BaseImporter();
public:
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* @param pFile Path and file name of the file to be examined.
* @param pIOHandler The IO handler to use for accessing any file.
* @return true if the class can read this file, false if not.
*/
virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const = 0;
// -------------------------------------------------------------------
/** Imports the given file and returns the imported data.
* If the import succeeds, ownership of the data is transferred to the caller.
* If the import failes, NULL is returned. The function takes care that any
* partially constructed data is destroyed beforehand.
*
* @param pFile Path of the file to be imported.
* @param pIOHandler IO-Handler used to open this and possible other files.
* @return The imported data or NULL if failed. If it failed a human-readable
* error description can be retrieved by calling GetErrorText()
*
* @note This function is not intended to be overridden. Implement InternReadFile()
* to do the import. If an exception is thrown somewhere in InternReadFile(),
* this function will catch it and transform it into a suitable response to the caller.
*/
aiScene* ReadFile( const std::string& pFile, IOSystem* pIOHandler);
// -------------------------------------------------------------------
/** Returns the error description of the last error that occured.
* @return A description of the last error that occured. An empty string if no error.
*/
const std::string& GetErrorText() const { return mErrorText; }
protected:
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure. The function is
* expected to throw an ImportErrorException if there is an error. If it
* terminates normally, the data in aiScene is expected to be correct.
* Override this function to implement the actual importing.
*
* @param pFile Path of the file to be imported.
* @param pScene The scene object to hold the imported data.
* @param pIOHandler The IO handler to use for any file access.
*/
virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) = 0;
protected:
/** Error description in case there was one. */
std::string mErrorText;
};
} // end of namespace Assimp
#endif // AI_BASEIMPORTER_H_INC

59
code/BaseProcess.h 100644
View File

@ -0,0 +1,59 @@
/** @file Base class of all import post processing steps */
#ifndef AI_BASEPROCESS_H_INC
#define AI_BASEPROCESS_H_INC
struct aiScene;
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The BaseProcess defines a common interface for all post processing steps.
* A post processing step is run after a successful import if the caller
* specified the corresponding flag when calling ReadFile(). Enum #aiPostProcessSteps
* defines which flags are available.
* After a successful import the Importer iterates over its internal array of processes
* and calls IsActive() on each process to evaluate if the step should be executed.
* If the function returns true, the class' Execute() function is called subsequently.
*/
class BaseProcess
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
BaseProcess();
/** Destructor, private as well */
virtual ~BaseProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
virtual bool IsActive( unsigned int pFlags) const = 0;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
virtual void Execute( aiScene* pScene) = 0;
};
/** Constructor, dummy implementation to keep the compiler from complaining */
inline BaseProcess::BaseProcess()
{
}
/** Destructor */
inline BaseProcess::~BaseProcess()
{
}
} // end of namespace Assimp
#endif // AI_BASEPROCESS_H_INC

View File

@ -0,0 +1,186 @@
/** @file Implementation of the post processing step to calculate tangents and bitangents
* for all imported meshes
*/
#include <vector>
#include <assert.h>
#include "CalcTangentsProcess.h"
#include "SpatialSort.h"
#include "../include/aiPostProcess.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
CalcTangentsProcess::CalcTangentsProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
CalcTangentsProcess::~CalcTangentsProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool CalcTangentsProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_CalcTangentSpace) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void CalcTangentsProcess::Execute( aiScene* pScene)
{
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
ProcessMesh( pScene->mMeshes[a]);
}
// ------------------------------------------------------------------------------------------------
// Calculates tangents and bitangents for the given mesh
void CalcTangentsProcess::ProcessMesh( aiMesh* pMesh)
{
// we assume that the mesh is still in the verbose vertex format where each face has its own set
// of vertices and no vertices are shared between faces. Sadly I don't know any quick test to
// assert() it here.
//assert( must be verbose, dammit);
// what we can check, though, is if the mesh has normals and texture coord. That's a requirement
if( pMesh->mNormals == NULL || pMesh->mTextureCoords[0] == NULL)
return;
// calculate the position bounds so we have a reliable epsilon to check position differences against
aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
{
minVec.x = std::min( minVec.x, pMesh->mVertices[a].x);
minVec.y = std::min( minVec.y, pMesh->mVertices[a].y);
minVec.z = std::min( minVec.z, pMesh->mVertices[a].z);
maxVec.x = std::max( maxVec.x, pMesh->mVertices[a].x);
maxVec.y = std::max( maxVec.y, pMesh->mVertices[a].y);
maxVec.z = std::max( maxVec.z, pMesh->mVertices[a].z);
}
// calculate epsilons border
const float epsilon = 1e-5f;
const float posEpsilon = (maxVec - minVec).Length() * epsilon;
const float angleEpsilon = 0.9999f;
// create space for the tangents and bitangents
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
const aiVector3D* meshPos = pMesh->mVertices;
const aiVector3D* meshNorm = pMesh->mNormals;
const aiVector3D* meshTex = pMesh->mTextureCoords[0];
aiVector3D* meshTang = pMesh->mTangents;
aiVector3D* meshBitang = pMesh->mBitangents;
// calculate the tangent and bitangent for every face
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
const aiFace& face = pMesh->mFaces[a];
// triangle or polygon... we always use only the first three indices. A polygon
// is supposed to be planar anyways....
// FIXME: (thom) create correct calculation for multi-vertex polygons maybe?
const unsigned int p0 = face.mIndices[0], p1 = face.mIndices[1], p2 = face.mIndices[2];
// position differences p1->p2 and p1->p3
aiVector3D v = meshPos[p1] - meshPos[p0], w = meshPos[p2] - meshPos[p0];
// texture offset p1->p2 and p1->p3
float sx = meshTex[p1].x - meshTex[p0].x, sy = meshTex[p1].y - meshTex[p0].y;
float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y;
float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f;
// tangent points in the direction where to positive X axis of the texture coords would point in model space
// bitangents points along the positive Y axis of the texture coords, respectively
aiVector3D tangent, bitangent;
tangent.x = (w.x * sy - v.x * ty) * dirCorrection;
tangent.y = (w.y * sy - v.y * ty) * dirCorrection;
tangent.z = (w.z * sy - v.z * ty) * dirCorrection;
bitangent.x = (w.x * sx - v.x * tx) * dirCorrection;
bitangent.y = (w.y * sx - v.y * tx) * dirCorrection;
bitangent.z = (w.z * sx - v.z * tx) * dirCorrection;
// store for every vertex of that face
for( unsigned int b = 0; b < face.mNumIndices; b++)
{
unsigned int p = face.mIndices[b];
// project tangent and bitangent into the plane formed by the vertex' normal
aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
localTangent.Normalize(); localBitangent.Normalize();
// and write it into the mesh.
meshTang[p] = localTangent;
meshBitang[p] = localBitangent;
}
}
// create a helper to quickly find locally close vertices among the vertex array
SpatialSort vertexFinder( meshPos, pMesh->mNumVertices, sizeof( aiVector3D));
std::vector<unsigned int> verticesFound;
// in the second pass we now smooth out all tangents and bitangents at the same local position
// if they are not too far off.
std::vector<bool> vertexDone( pMesh->mNumVertices, false);
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
{
if( vertexDone[a])
continue;
const aiVector3D& origPos = pMesh->mVertices[a];
const aiVector3D& origNorm = pMesh->mNormals[a];
const aiVector3D& origTang = pMesh->mTangents[a];
const aiVector3D& origBitang = pMesh->mBitangents[a];
std::vector<unsigned int> closeVertices;
closeVertices.push_back( a);
// find all vertices close to that position
vertexFinder.FindPositions( origPos, posEpsilon, verticesFound);
// look among them for other vertices sharing the same normal and a close-enough tangent/bitangent
static const float MAX_DIFF_ANGLE = 0.701f;
for( unsigned int b = 0; b < verticesFound.size(); b++)
{
unsigned int idx = verticesFound[b];
if( vertexDone[idx])
continue;
if( meshNorm[idx] * origNorm < angleEpsilon)
continue;
if( meshTang[idx] * origTang < MAX_DIFF_ANGLE)
continue;
if( meshBitang[idx] * origBitang < MAX_DIFF_ANGLE)
continue;
// it's similar enough -> add it to the smoothing group
closeVertices.push_back( idx);
vertexDone[idx] = true;
}
// smooth the tangents and bitangents of all vertices that were found to be close enough
aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0);
for( unsigned int b = 0; b < closeVertices.size(); b++)
{
smoothTangent += meshTang[ closeVertices[b] ];
smoothBitangent += meshBitang[ closeVertices[b] ];
}
smoothTangent.Normalize();
smoothBitangent.Normalize();
// and write it back into all affected tangents
for( unsigned int b = 0; b < closeVertices.size(); b++)
{
meshTang[ closeVertices[b] ] = smoothTangent;
meshBitang[ closeVertices[b] ] = smoothBitangent;
}
}
}

View File

@ -0,0 +1,55 @@
/** @file Defines a post processing step to calculate tangents and bitangents on all imported meshes.*/
#ifndef AI_CALCTANGENTSPROCESS_H_INC
#define AI_CALCTANGENTSPROCESS_H_INC
#include "BaseProcess.h"
struct aiMesh;
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The CalcTangentsProcess calculates the tangent and bitangent for any vertex
* of all meshes. It is expected to be run before the JoinVerticesProcess runs
* because the joining of vertices also considers tangents and bitangents for uniqueness.
*/
class CalcTangentsProcess : public BaseProcess
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
CalcTangentsProcess();
/** Destructor, private as well */
~CalcTangentsProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
protected:
// -------------------------------------------------------------------
/** Calculates tangents and bitangents for the given mesh
* @param pMesh The mesh to process.
*/
void ProcessMesh( aiMesh* pMesh);
};
} // end of namespace Assimp
#endif // AI_CALCTANGENTSPROCESS_H_INC

View File

@ -0,0 +1,155 @@
/** @file Implementation of the post processing step to convert all imported data
* to a left-handed coordinate system.
*/
#include "ConvertToLHProcess.h"
#include "../include/aiPostProcess.h"
#include "../include/aiMesh.h"
#include "../include/aiAnim.h"
#include "../include/aiScene.h"
using namespace Assimp;
// The transformation matrix to convert from DirectX coordinates to OpenGL coordinates.
const aiMatrix3x3 Assimp::ConvertToLHProcess::sToOGLTransform(
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f
);
// The transformation matrix to convert from OpenGL coordinates to DirectX coordinates.
const aiMatrix3x3 Assimp::ConvertToLHProcess::sToDXTransform(
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f
);
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
ConvertToLHProcess::ConvertToLHProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
ConvertToLHProcess::~ConvertToLHProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool ConvertToLHProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_ConvertToLeftHanded) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void ConvertToLHProcess::Execute( aiScene* pScene)
{
// Check for an existent root node to proceed
if (NULL == pScene->mRootNode)
return;
// transform the root node of the scene, the other nodes will follow then
ConvertToDX( pScene->mRootNode->mTransformation);
// transform all meshes accordingly
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
ProcessMesh( pScene->mMeshes[a]);
// transform all animation channels affecting the root node as well
for( unsigned int a = 0; a < pScene->mNumAnimations; a++)
{
aiAnimation* anim = pScene->mAnimations[a];
for( unsigned int b = 0; b < anim->mNumBones; b++)
{
aiBoneAnim* boneAnim = anim->mBones[b];
if( strcmp( boneAnim->mBoneName.data, pScene->mRootNode->mName.data) == 0)
ProcessAnimation( boneAnim);
}
}
}
// ------------------------------------------------------------------------------------------------
// Converts a single mesh to left handed coordinates.
void ConvertToLHProcess::ProcessMesh( aiMesh* pMesh)
{
// invert the order of all faces in this mesh
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
aiFace& face = pMesh->mFaces[a];
for( unsigned int b = 0; b < face.mNumIndices / 2; b++)
std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]);
}
// mirror texture y coordinate
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
{
if( pMesh->HasTextureCoords( a))
{
for( unsigned int b = 0; b < pMesh->mNumVertices; b++)
pMesh->mTextureCoords[a][b].y = 1.0f - pMesh->mTextureCoords[a][b].y;
}
}
// mirror bitangents as well as they're derived from the texture coords
if( pMesh->HasTangentsAndBitangents())
{
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mBitangents[a] = -pMesh->mBitangents[a];
}
}
// ------------------------------------------------------------------------------------------------
// Converts the given animation to LH coordinates.
void ConvertToLHProcess::ProcessAnimation( aiBoneAnim* pAnim)
{
// position keys
for( unsigned int a = 0; a < pAnim->mNumPositionKeys; a++)
ConvertToDX( pAnim->mPositionKeys[a].mValue);
// rotation keys
for( unsigned int a = 0; a < pAnim->mNumRotationKeys; a++)
{
aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix();
ConvertToDX( rotmat);
pAnim->mRotationKeys[a].mValue = aiQuaternion( rotmat);
}
}
// ------------------------------------------------------------------------------------------------
// Static helper function to convert a vector/matrix from DX to OGL coords
void ConvertToLHProcess::ConvertToOGL( aiVector3D& poVector)
{
poVector = sToOGLTransform * poVector;
}
// ------------------------------------------------------------------------------------------------
void ConvertToLHProcess::ConvertToOGL( aiMatrix3x3& poMatrix)
{
poMatrix *= sToOGLTransform;
}
// ------------------------------------------------------------------------------------------------
void ConvertToLHProcess::ConvertToOGL( aiMatrix4x4& poMatrix)
{
poMatrix *= aiMatrix4x4( sToOGLTransform);
}
// ------------------------------------------------------------------------------------------------
// Static helper function to convert a vector/matrix from OGL back to DX coords
void ConvertToLHProcess::ConvertToDX( aiVector3D& poVector)
{
poVector = sToDXTransform * poVector;
}
// ------------------------------------------------------------------------------------------------
void ConvertToLHProcess::ConvertToDX( aiMatrix3x3& poMatrix)
{
poMatrix *= sToDXTransform;
}
// ------------------------------------------------------------------------------------------------
void ConvertToLHProcess::ConvertToDX( aiMatrix4x4& poMatrix)
{
aiMatrix4x4 temp(sToDXTransform);
poMatrix *= temp;
}

View File

@ -0,0 +1,88 @@
/** @file Defines a post processing step to convert all data to a left-handed coordinate system.*/
#ifndef AI_CONVERTTOLHPROCESS_H_INC
#define AI_CONVERTTOLHPROCESS_H_INC
#include "../include/aiTypes.h"
#include "BaseProcess.h"
struct aiMesh;
struct aiBoneAnim;
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The ConvertToLHProcess converts all imported data to a left-handed coordinate
* system. This implies inverting the Z axis for all transformation matrices
* invert the orientation of all faces, and adapting skinning and animation
* data in a similar way.
*/
class ConvertToLHProcess : public BaseProcess
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
ConvertToLHProcess();
/** Destructor, private as well */
~ConvertToLHProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
// -------------------------------------------------------------------
/** Static helper function to convert a vector/matrix from DX coords to OGL coords.
* @param poMatrix The matrix to convert.
*/
static void ConvertToOGL( aiVector3D& poVector);
static void ConvertToOGL( aiMatrix3x3& poMatrix);
static void ConvertToOGL( aiMatrix4x4& poMatrix);
// -------------------------------------------------------------------
/** Static helper function to convert a vector/matrix from OGL coords back to DX coords.
* @param poMatrix The matrix to convert.
*/
static void ConvertToDX( aiVector3D& poVector);
static void ConvertToDX( aiMatrix3x3& poMatrix);
static void ConvertToDX( aiMatrix4x4& poMatrix);
protected:
// -------------------------------------------------------------------
/** Converts a single mesh to left handed coordinates.
* This simply means the order of all faces is inverted.
* @param pMesh The mesh to convert.
*/
void ProcessMesh( aiMesh* pMesh);
// -------------------------------------------------------------------
/** Converts the given animation to LH coordinates.
* The rotation and translation keys are transformed, the scale keys
* work in local space and can therefore be left untouched.
* @param pAnim The bone animation to transform
*/
void ProcessAnimation( aiBoneAnim* pAnim);
public:
/** The transformation matrix to convert from DirectX coordinates to OpenGL coordinates. */
static const aiMatrix3x3 sToOGLTransform;
/** The transformation matrix to convert from OpenGL coordinates to DirectX coordinates. */
static const aiMatrix3x3 sToDXTransform;
};
} // end of namespace Assimp
#endif // AI_CONVERTTOLHPROCESS_H_INC

View File

@ -0,0 +1,92 @@
/** @file Default File I/O implementation for #Importer */
#include "DefaultIOStream.h"
#include "aiAssert.h"
#include <sys/types.h>
#include <sys/stat.h>
using namespace Assimp;
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
DefaultIOStream::~DefaultIOStream()
{
if (this->mFile)
{
fclose(this->mFile);
}
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
size_t DefaultIOStream::Read(void* pvBuffer,
size_t pSize,
size_t pCount)
{
if (!this->mFile)
return 0;
return fread(pvBuffer, pSize, pCount, this->mFile);
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
size_t DefaultIOStream::Write(const void* pvBuffer,
size_t pSize,
size_t pCount)
{
if (!this->mFile)return 0;
fseek(mFile, 0, SEEK_SET);
return fwrite(pvBuffer, pSize, pCount, this->mFile);
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
aiReturn DefaultIOStream::Seek(size_t pOffset,
aiOrigin pOrigin)
{
if (!this->mFile)return AI_FAILURE;
return (0 == fseek(this->mFile, pOffset,
(aiOrigin_CUR == pOrigin ? SEEK_CUR :
(aiOrigin_END == pOrigin ? SEEK_END : SEEK_SET)))
? AI_SUCCESS : AI_FAILURE);
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
size_t DefaultIOStream::Tell() const
{
if (!this->mFile)return 0;
return ftell(this->mFile);
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
size_t DefaultIOStream::FileSize() const
{
ai_assert (!mFilename.empty());
if (NULL == mFile)
return 0;
#if defined _WIN32 && !defined __GNUC__
struct __stat64 fileStat;
int err = _stat64( mFilename.c_str(), &fileStat );
if (0 != err)
return 0;
return (size_t) (fileStat.st_size);
#else
struct stat fileStat;
int err = stat(mFilename.c_str(), &fileStat );
if (0 != err)
return 0;
return (size_t) (fileStat.st_size);
#endif
}
// ---------------------------------------------------------------------------

View File

@ -0,0 +1,82 @@
/** @file Default file I/O using fXXX()-family of functions */
#ifndef AI_DEFAULTIOSTREAM_H_INC
#define AI_DEFAULTIOSTREAM_H_INC
#include <string>
#include <stdio.h>
#include "IOStream.h"
namespace Assimp
{
// ---------------------------------------------------------------------------
//! \class DefaultIOStream
//! \brief Default IO implementation, use standard IO operations
// ---------------------------------------------------------------------------
class DefaultIOStream : public IOStream
{
friend class DefaultIOSystem;
protected:
DefaultIOStream ();
DefaultIOStream (FILE* pFile, const std::string &strFilename);
public:
/** Destructor public to allow simple deletion to close the file. */
~DefaultIOStream ();
// -------------------------------------------------------------------
// -------------------------------------------------------------------
size_t Read(void* pvBuffer,
size_t pSize,
size_t pCount);
// -------------------------------------------------------------------
// -------------------------------------------------------------------
size_t Write(const void* pvBuffer,
size_t pSize,
size_t pCount);
// -------------------------------------------------------------------
// -------------------------------------------------------------------
aiReturn Seek(size_t pOffset,
aiOrigin pOrigin);
// -------------------------------------------------------------------
// -------------------------------------------------------------------
size_t Tell() const;
//! Returns filesize
size_t FileSize() const;
private:
//! File datastructure, using clib
FILE* mFile;
//! Filename
std::string mFilename;
};
// ---------------------------------------------------------------------------
inline DefaultIOStream::DefaultIOStream () :
mFile(NULL),
mFilename("")
{
// empty
}
// ---------------------------------------------------------------------------
inline DefaultIOStream::DefaultIOStream (FILE* pFile,
const std::string &strFilename) :
mFile(pFile),
mFilename(strFilename)
{
// empty
}
// ---------------------------------------------------------------------------
} // ns assimp
#endif //!!AI_DEFAULTIOSTREAM_H_INC

View File

@ -0,0 +1,64 @@
/** @file Default implementation of IOSystem using the standard C file functions */
#include <stdlib.h>
#include <string>
#include "DefaultIOSystem.h"
#include "DefaultIOStream.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor.
DefaultIOSystem::DefaultIOSystem()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor.
DefaultIOSystem::~DefaultIOSystem()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Tests for the existence of a file at the given path.
bool DefaultIOSystem::Exists( const std::string& pFile) const
{
FILE* file = fopen( pFile.c_str(), "rb");
if( !file)
return false;
fclose( file);
return true;
}
// ------------------------------------------------------------------------------------------------
// Open a new file with a given path.
IOStream* DefaultIOSystem::Open( const std::string& strFile, const std::string& strMode)
{
FILE* file = fopen( strFile.c_str(), strMode.c_str());
if( NULL == file)
return NULL;
return new DefaultIOStream( file, strFile);
}
// ------------------------------------------------------------------------------------------------
// Closes the given file and releases all resources associated with it.
void DefaultIOSystem::Close( IOStream* pFile)
{
delete pFile;
}
// ------------------------------------------------------------------------------------------------
// Returns the operation specific directory separator
std::string DefaultIOSystem::getOsSeparator() const
{
#ifndef _WIN32
std::string sep = "/";
#else
std::string sep = "\\";
#endif
return sep;
}

View File

@ -0,0 +1,40 @@
/** @file Default implementation of IOSystem using the standard C file functions */
#ifndef AI_DEFAULTIOSYSTEM_H_INC
#define AI_DEFAULTIOSYSTEM_H_INC
#include "IOSystem.h"
namespace Assimp
{
// ---------------------------------------------------------------------------
/** Default implementation of IOSystem using the standard C file functions */
class DefaultIOSystem : public IOSystem
{
public:
/** Constructor. */
DefaultIOSystem();
/** Destructor. */
~DefaultIOSystem();
// -------------------------------------------------------------------
/** Tests for the existence of a file at the given path. */
bool Exists( const std::string& pFile) const;
// -------------------------------------------------------------------
/** Returns the directory separator. */
std::string getOsSeparator() const;
// -------------------------------------------------------------------
/** Open a new file with a given path. */
IOStream* Open( const std::string& pFile, const std::string& pMode = std::string("rb"));
// -------------------------------------------------------------------
/** Closes the given file and releases all resources associated with it. */
void Close( IOStream* pFile);
};
} //!ns Assimp
#endif //AI_DEFAULTIOSYSTEM_H_INC

View File

@ -0,0 +1,67 @@
/** @file Implementation of the post processing step to generate face
* normals for all imported faces.
*/
#include "GenFaceNormalsProcess.h"
#include "../include/aiPostProcess.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
using namespace Assimp;
// Constructor to be privately used by Importer
GenFaceNormalsProcess::GenFaceNormalsProcess()
{
}
// Destructor, private as well
GenFaceNormalsProcess::~GenFaceNormalsProcess()
{
// nothing to do here
}
// -------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_GenNormals) != 0;
}
// -------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenFaceNormalsProcess::Execute( aiScene* pScene)
{
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
this->GenMeshFaceNormals( pScene->mMeshes[a]);
}
// -------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
{
if (NULL != pMesh->mNormals)return;
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
const aiFace& face = pMesh->mFaces[a];
// assume it is a triangle
aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[2]];
aiVector3D pDelta1 = *pV2 - *pV1;
aiVector3D pDelta2 = *pV3 - *pV1;
aiVector3D vNor = pDelta1 ^ pDelta2;
// NOTE: Never normalize here. Causes problems ...
//float fLength = vNor.Length();
//if (0.0f != fLength)vNor /= fLength;
pMesh->mNormals[face.mIndices[0]] = vNor;
pMesh->mNormals[face.mIndices[1]] = vNor;
pMesh->mNormals[face.mIndices[2]] = vNor;
}
return;
}

View File

@ -0,0 +1,48 @@
/** @file Defines a post processing step to compute face normals for all loaded faces*/
#ifndef AI_GENFACENORMALPROCESS_H_INC
#define AI_GENFACENORMALPROCESS_H_INC
#include "BaseProcess.h"
#include "../include/aiMesh.h"
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The GenFaceNormalsProcess computes face normals for all faces of all meshes
*/
class GenFaceNormalsProcess : public BaseProcess
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
GenFaceNormalsProcess();
/** Destructor, private as well */
~GenFaceNormalsProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
private:
void GenMeshFaceNormals (aiMesh* pcMesh);
};
} // end of namespace Assimp
#endif // !!AI_GENFACENORMALPROCESS_H_INC

View File

@ -0,0 +1,104 @@
/** @file Implementation of the post processing step to generate face
* normals for all imported faces.
*/
#include "GenVertexNormalsProcess.h"
#include "SpatialSort.h"
#include "../include/aiPostProcess.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
using namespace Assimp;
// Constructor to be privately used by Importer
GenVertexNormalsProcess::GenVertexNormalsProcess()
{
}
// Destructor, private as well
GenVertexNormalsProcess::~GenVertexNormalsProcess()
{
// nothing to do here
}
// -------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_GenSmoothNormals) != 0;
}
// -------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenVertexNormalsProcess::Execute( aiScene* pScene)
{
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
this->GenMeshVertexNormals( pScene->mMeshes[a]);
}
// -------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
{
if (NULL != pMesh->mNormals)return;
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
const aiFace& face = pMesh->mFaces[a];
// assume it is a triangle
aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[2]];
aiVector3D pDelta1 = *pV2 - *pV1;
aiVector3D pDelta2 = *pV3 - *pV1;
aiVector3D vNor = pDelta1 ^ pDelta2;
float fLength = vNor.Length();
if (0.0f != fLength)vNor /= fLength;
pMesh->mNormals[face.mIndices[0]] = vNor;
pMesh->mNormals[face.mIndices[1]] = vNor;
pMesh->mNormals[face.mIndices[2]] = vNor;
}
// calculate the position bounds so we have a reliable epsilon to
// check position differences against
aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
{
minVec.x = std::min( minVec.x, pMesh->mVertices[a].x);
minVec.y = std::min( minVec.y, pMesh->mVertices[a].y);
minVec.z = std::min( minVec.z, pMesh->mVertices[a].z);
maxVec.x = std::max( maxVec.x, pMesh->mVertices[a].x);
maxVec.y = std::max( maxVec.y, pMesh->mVertices[a].y);
maxVec.z = std::max( maxVec.z, pMesh->mVertices[a].z);
}
const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
// set up a SpatialSort to quickly find all vertices close to a given position
SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
std::vector<unsigned int> verticesFound;
aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices];
for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
{
const aiVector3D& posThis = pMesh->mVertices[i];
// get all vertices that share this one ...
vertexFinder.FindPositions( posThis, posEpsilon, verticesFound);
aiVector3D pcNor;
for (unsigned int a = 0; a < verticesFound.size(); ++a)
{
unsigned int vidx = verticesFound[a];
pcNor += pMesh->mNormals[vidx];
}
pcNor /= (float) verticesFound.size();
pcNew[i] = pcNor;
}
delete pMesh->mNormals;
pMesh->mNormals = pcNew;
return;
}

View File

@ -0,0 +1,47 @@
/** @file Defines a post processing step to compute vertex normals for all loaded vertizes */
#ifndef AI_GENVERTEXNORMALPROCESS_H_INC
#define AI_GENVERTEXNORMALPROCESS_H_INC
#include "BaseProcess.h"
#include "../include/aiMesh.h"
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The GenFaceNormalsProcess computes vertex normals for all vertizes of all meshes
*/
class GenVertexNormalsProcess : public BaseProcess
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
GenVertexNormalsProcess();
/** Destructor, private as well */
~GenVertexNormalsProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
private:
void GenMeshVertexNormals (aiMesh* pcMesh);
};
} // end of namespace Assimp
#endif // !!AI_GENVERTEXNORMALPROCESS_H_INC

136
code/Importer.cpp 100644
View File

@ -0,0 +1,136 @@
/** @file Implementation of the CPP-API class #Importer */
#include <fstream>
#include <string>
#include "../include/assimp.hpp"
#include "../include/aiScene.h"
#include "BaseImporter.h"
#include "BaseProcess.h"
#include "DefaultIOStream.h"
#include "DefaultIOSystem.h"
#include "XFileImporter.h"
#include "3DSLoader.h"
#include "MD3Loader.h"
#include "MD2Loader.h"
#include "PlyLoader.h"
#include "ObjFileImporter.h"
#include "CalcTangentsProcess.h"
#include "JoinVerticesProcess.h"
#include "ConvertToLHProcess.h"
#include "TriangulateProcess.h"
#include "GenFaceNormalsProcess.h"
#include "GenVertexNormalsProcess.h"
#include "KillNormalsProcess.h"
#include "SplitLargeMeshes.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor.
Importer::Importer() :
mIOHandler(NULL),
mScene(NULL),
mErrorString("")
{
// default IO handler
mIOHandler = new DefaultIOSystem;
// add an instance of each worker class here
mImporter.push_back( new XFileImporter());
mImporter.push_back( new ObjFileImporter());
mImporter.push_back( new Dot3DSImporter());
mImporter.push_back( new MD3Importer());
mImporter.push_back( new MD2Importer());
mImporter.push_back( new PLYImporter());
// add an instance of each post processing step here in the order of sequence it is executed
mPostProcessingSteps.push_back( new TriangulateProcess());
mPostProcessingSteps.push_back( new SplitLargeMeshesProcess());
mPostProcessingSteps.push_back( new KillNormalsProcess());
mPostProcessingSteps.push_back( new GenFaceNormalsProcess());
mPostProcessingSteps.push_back( new GenVertexNormalsProcess());
mPostProcessingSteps.push_back( new CalcTangentsProcess());
mPostProcessingSteps.push_back( new JoinVerticesProcess());
mPostProcessingSteps.push_back( new ConvertToLHProcess());
}
// ------------------------------------------------------------------------------------------------
// Destructor.
Importer::~Importer()
{
for( unsigned int a = 0; a < mImporter.size(); a++)
delete mImporter[a];
for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++)
delete mPostProcessingSteps[a];
delete mIOHandler;
// kill imported scene. Destructors should do that recursivly
delete mScene;
}
// ------------------------------------------------------------------------------------------------
// Supplies a custom IO handler to the importer to open and access files.
void Importer::SetIOHandler( IOSystem* pIOHandler)
{
delete mIOHandler;
mIOHandler = pIOHandler;
}
// ------------------------------------------------------------------------------------------------
// Reads the given file and returns its contents if successful.
const aiScene* Importer::ReadFile( const std::string& pFile, unsigned int pFlags)
{
// first check if the file is accessable at all
if( !mIOHandler->Exists( pFile))
{
mErrorString = "Unable to open file \"" + pFile + "\".";
return NULL;
}
// find an worker class which can handle the file
BaseImporter* imp = NULL;
for( unsigned int a = 0; a < mImporter.size(); a++)
{
if( mImporter[a]->CanRead( pFile, mIOHandler))
{
imp = mImporter[a];
break;
}
}
// put a proper error message if no suitable importer was found
if( !imp)
{
mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
return NULL;
}
// dispatch the reading to the worker class for this format
mScene = imp->ReadFile( pFile, mIOHandler);
// if failed, extract the error string
if( !mScene)
mErrorString = imp->GetErrorText();
// if successful, apply all active post processing steps to the imported data
if( mScene)
{
for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++)
{
BaseProcess* process = mPostProcessingSteps[a];
if( process->IsActive( pFlags))
process->Execute( mScene);
}
}
// either successful or failure - the pointer expresses it anyways
return mScene;
}
// ------------------------------------------------------------------------------------------------
// Empty and rpivate copy constructor
Importer::Importer(const Importer &other)
{
// empty
}

View File

@ -0,0 +1,262 @@
/** @file Implementation of the post processing step to join identical vertices
* for all imported meshes
*/
#include <vector>
#include <assert.h>
#include "JoinVerticesProcess.h"
#include "SpatialSort.h"
#include "../include/aiPostProcess.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
JoinVerticesProcess::JoinVerticesProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
JoinVerticesProcess::~JoinVerticesProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool JoinVerticesProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void JoinVerticesProcess::Execute( aiScene* pScene)
{
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
ProcessMesh( pScene->mMeshes[a]);
}
// ------------------------------------------------------------------------------------------------
// Unites identical vertices in the given mesh
void JoinVerticesProcess::ProcessMesh( aiMesh* pMesh)
{
// helper structure to hold all the data a single vertex can possibly have
typedef struct Vertex vertex;
struct Vertex
{
aiVector3D mPosition;
aiVector3D mNormal;
aiVector3D mTangent, mBitangent;
aiColor4D mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
aiVector3D mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
};
std::vector<Vertex> uniqueVertices;
uniqueVertices.reserve( pMesh->mNumVertices);
// For each vertex the index of the vertex it was replaced by.
std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
// for each vertex whether it was replaced by an existing unique vertex (true) or a new vertex was created for it (false)
std::vector<bool> isVertexUnique( pMesh->mNumVertices, false);
// calculate the position bounds so we have a reliable epsilon to check position differences against
aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
{
minVec.x = std::min( minVec.x, pMesh->mVertices[a].x);
minVec.y = std::min( minVec.y, pMesh->mVertices[a].y);
minVec.z = std::min( minVec.z, pMesh->mVertices[a].z);
maxVec.x = std::max( maxVec.x, pMesh->mVertices[a].x);
maxVec.y = std::max( maxVec.y, pMesh->mVertices[a].y);
maxVec.z = std::max( maxVec.z, pMesh->mVertices[a].z);
}
// squared because we check against squared length of the vector difference
const float epsilon = 1e-5f;
const float posEpsilon = (maxVec - minVec).Length() * epsilon;
const float squareEpsilon = epsilon * epsilon;
// a little helper to find locally close vertices faster
SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
std::vector<unsigned int> verticesFound;
// now check each vertex if it brings something new to the table
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
{
// collect the vertex data
Vertex v;
v.mPosition = pMesh->mVertices[a];
v.mNormal = (pMesh->mNormals != NULL) ? pMesh->mNormals[a] : aiVector3D( 0, 0, 0);
v.mTangent = (pMesh->mTangents != NULL) ? pMesh->mTangents[a] : aiVector3D( 0, 0, 0);
v.mBitangent = (pMesh->mBitangents != NULL) ? pMesh->mBitangents[a] : aiVector3D( 0, 0, 0);
for( unsigned int b = 0; b < AI_MAX_NUMBER_OF_COLOR_SETS; b++)
v.mColors[b] = (pMesh->mColors[b] != NULL) ? pMesh->mColors[b][a] : aiColor4D( 0, 0, 0, 0);
for( unsigned int b = 0; b < AI_MAX_NUMBER_OF_TEXTURECOORDS; b++)
v.mTexCoords[b] = (pMesh->mTextureCoords[b] != NULL) ? pMesh->mTextureCoords[b][a] : aiVector3D( 0, 0, 0);
// collect all vertices that are close enough to the given position
vertexFinder.FindPositions( v.mPosition, posEpsilon, verticesFound);
unsigned int matchIndex = 0xffffffff;
// check all unique vertices close to the position if this vertex is already present among them
for( unsigned int b = 0; b < verticesFound.size(); b++)
{
unsigned int vidx = verticesFound[b];
unsigned int uidx = replaceIndex[ vidx];
if( uidx == 0xffffffff || !isVertexUnique[ vidx])
continue;
const Vertex& uv = uniqueVertices[ uidx];
// Position mismatch is impossible - the vertex finder already discarded all non-matching positions
// We just test the other attributes even if they're not present in the mesh.
// In this case they're initialized to 0 so the comparision succeeds.
// By this method the non-present attributes are effectively ignored in the comparision.
if( (uv.mNormal - v.mNormal).SquareLength() > squareEpsilon)
continue;
if( (uv.mTangent - v.mTangent).SquareLength() > squareEpsilon)
continue;
if( (uv.mBitangent - v.mBitangent).SquareLength() > squareEpsilon)
continue;
// manually unrolled because continue wouldn't work as desired in an inner loop
assert( AI_MAX_NUMBER_OF_COLOR_SETS == 4);
if( GetColorDifference( uv.mColors[0], v.mColors[0]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[1], v.mColors[1]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[2], v.mColors[2]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[3], v.mColors[3]) > squareEpsilon)
continue;
// texture coord matching manually unrolled as well
assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 4);
if( (uv.mTexCoords[0] - v.mTexCoords[0]).SquareLength() > squareEpsilon)
continue;
if( (uv.mTexCoords[1] - v.mTexCoords[1]).SquareLength() > squareEpsilon)
continue;
if( (uv.mTexCoords[2] - v.mTexCoords[2]).SquareLength() > squareEpsilon)
continue;
if( (uv.mTexCoords[3] - v.mTexCoords[3]).SquareLength() > squareEpsilon)
continue;
// we're still here -> this vertex perfectly matches our given vertex
matchIndex = uidx;
break;
}
// found a replacement vertex among the uniques?
if( matchIndex != 0xffffffff)
{
// store where to found the matching unique vertex
replaceIndex[a] = matchIndex;
isVertexUnique[a] = false;
} else
{
// no unique vertex matches it upto now -> so add it
replaceIndex[a] = uniqueVertices.size();
uniqueVertices.push_back( v);
isVertexUnique[a] = true;
}
}
// replace vertex data with the unique data sets
pMesh->mNumVertices = uniqueVertices.size();
// Position
delete [] pMesh->mVertices;
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mVertices[a] = uniqueVertices[a].mPosition;
// Normals, if present
if( pMesh->mNormals)
{
delete [] pMesh->mNormals;
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mNormals[a] = uniqueVertices[a].mNormal;
}
// Tangents, if present
if( pMesh->mTangents)
{
delete [] pMesh->mTangents;
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mTangents[a] = uniqueVertices[a].mTangent;
}
// Bitangents as well
if( pMesh->mBitangents)
{
delete [] pMesh->mBitangents;
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mBitangents[a] = uniqueVertices[a].mBitangent;
}
// Vertex colors
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
{
if( !pMesh->mColors[a])
continue;
delete [] pMesh->mColors[a];
pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
for( unsigned int b = 0; b < pMesh->mNumVertices; b++)
pMesh->mColors[a][b] = uniqueVertices[b].mColors[a];
}
// Texture coords
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
{
if( !pMesh->mTextureCoords[a])
continue;
delete [] pMesh->mTextureCoords[a];
pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
for( unsigned int b = 0; b < pMesh->mNumVertices; b++)
pMesh->mTextureCoords[a][b] = uniqueVertices[b].mTexCoords[a];
}
// adjust the indices in all faces
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
aiFace& face = pMesh->mFaces[a];
for( unsigned int b = 0; b < face.mNumIndices; b++)
{
const size_t index = face.mIndices[b];
face.mIndices[b] = replaceIndex[face.mIndices[b]];
}
}
// adjust bone vertex weights.
for( unsigned int a = 0; a < pMesh->mNumBones; a++)
{
aiBone* bone = pMesh->mBones[a];
std::vector<aiVertexWeight> newWeights;
newWeights.reserve( bone->mNumWeights);
for( unsigned int b = 0; b < bone->mNumWeights; b++)
{
const aiVertexWeight& ow = bone->mWeights[b];
// if the vertex is a unique one, translate it
if( isVertexUnique[ow.mVertexId])
{
aiVertexWeight nw;
nw.mVertexId = replaceIndex[ow.mVertexId];
nw.mWeight = ow.mWeight;
newWeights.push_back( nw);
}
}
// there should be some. At least I think there should be some
assert( newWeights.size() > 0);
// kill the old and replace them with the translated weights
delete [] bone->mWeights;
bone->mNumWeights = newWeights.size();
bone->mWeights = new aiVertexWeight[bone->mNumWeights];
memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight));
}
}

View File

@ -0,0 +1,65 @@
/** @file Defines a post processing step to join identical vertices on all imported meshes.*/
#ifndef AI_JOINVERTICESPROCESS_H_INC
#define AI_CALCTANGENTSPROCESS_H_INC
#include "BaseProcess.h"
#include "../include/aiTypes.h"
struct aiMesh;
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The JoinVerticesProcess unites identical vertices in all imported meshes.
* By default the importer returns meshes where each face addressed its own
* set of vertices even if that means that identical vertices are stored multiple
* times. The JoinVerticesProcess finds these identical vertices and
* erases all but one of the copies. This usually reduces the number of vertices
* in a mesh by a serious amount and is the standard form to render a mesh.
*/
class JoinVerticesProcess : public BaseProcess
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
JoinVerticesProcess();
/** Destructor, private as well */
~JoinVerticesProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
protected:
// -------------------------------------------------------------------
/** Unites identical vertices in the given mesh.
* @param pMesh The mesh to process.
*/
void ProcessMesh( aiMesh* pMesh);
/** Little helper function to calculate the quadratic difference of two colours. */
float GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pColor2) const
{
aiColor4D c( pColor1.r - pColor2.r, pColor1.g - pColor2.g, pColor1.b - pColor2.b, pColor1.a - pColor2.a);
return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a;
}
};
} // end of namespace Assimp
#endif // AI_CALCTANGENTSPROCESS_H_INC

View File

@ -0,0 +1,43 @@
/** @file Implementation of the post processing step tokill mesh normals
*/
#include "KillNormalsProcess.h"
#include "../include/aiPostProcess.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
using namespace Assimp;
// Constructor to be privately used by Importer
KillNormalsProcess::KillNormalsProcess()
{
}
// Destructor, private as well
KillNormalsProcess::~KillNormalsProcess()
{
// nothing to do here
}
// -------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool KillNormalsProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_KillNormals) != 0;
}
// -------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void KillNormalsProcess::Execute( aiScene* pScene)
{
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
this->KillMeshNormals( pScene->mMeshes[a]);
}
// -------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void KillNormalsProcess::KillMeshNormals(aiMesh* pMesh)
{
delete[] pMesh->mNormals;
pMesh->mNormals = NULL;
return;
}

View File

@ -0,0 +1,47 @@
/** @file Defines a post processing step to kill all loaded normals */
#ifndef AI_KILLNORMALPROCESS_H_INC
#define AI_KILLNORMALPROCESS_H_INC
#include "BaseProcess.h"
#include "../include/aiMesh.h"
namespace Assimp
{
// ---------------------------------------------------------------------------
/** KillNormalsProcess: Class to kill all normals loaded
*/
class KillNormalsProcess : public BaseProcess
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
KillNormalsProcess();
/** Destructor, private as well */
~KillNormalsProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
private:
void KillMeshNormals (aiMesh* pcMesh);
};
} // end of namespace Assimp
#endif // !!AI_KILLNORMALPROCESS_H_INC

137
code/MD2FileData.h 100644
View File

@ -0,0 +1,137 @@
/** @file Defines the helper data structures for importing MD2 files */
#ifndef AI_MD2FILEHELPER_H_INC
#define AI_MD2FILEHELPER_H_INC
#include <string>
#include <vector>
#include <sstream>
#include "../include/aiTypes.h"
#include "../include/aiMesh.h"
#include "../include/aiAnim.h"
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack(push,1)
# define PACK_STRUCT
#elif defined( __GNUC__ )
# define PACK_STRUCT __attribute__((packed))
#else
# error Compiler not supported
#endif
namespace Assimp
{
//http://linux.ucla.edu/~phaethon/q3/formats/md2-schoenblum.html
namespace MD2
{
#define AI_MD2_MAGIC_NUMBER_BE 'IDP2'
#define AI_MD2_MAGIC_NUMBER_LE '2PDI'
// common limitations
#define AI_MD2_VERSION 15
#define AI_MD2_MAXQPATH 64
#define AI_MD2_MAX_FRAMES 512
#define AI_MD2_MAX_SKINS 32
#define AI_MD2_MAX_VERTS 2048
#define AI_MD2_MAX_TRIANGLES 4096
// ---------------------------------------------------------------------------
/** \brief Data structure for the MD2 main header
*/
// ---------------------------------------------------------------------------
struct Header
{
int32_t magic;
int32_t version;
int32_t skinWidth;
int32_t skinHeight;
int32_t frameSize;
int32_t numSkins;
int32_t numVertices;
int32_t numTexCoords;
int32_t numTriangles;
int32_t numGlCommands;
int32_t numFrames;
int32_t offsetSkins;
int32_t offsetTexCoords;
int32_t offsetTriangles;
int32_t offsetFrames;
int32_t offsetGlCommands;
int32_t offsetEnd;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for a MD2 OpenGl draw command
*/
// ---------------------------------------------------------------------------
struct GLCommand
{
float s, t;
uint32_t vertexIndex;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for a MD2 triangle
*/
// ---------------------------------------------------------------------------
struct Triangle
{
uint16_t vertexIndices[3];
uint16_t textureIndices[3];
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for a MD2 vertex
*/
// ---------------------------------------------------------------------------
struct Vertex
{
uint8_t vertex[3];
uint8_t lightNormalIndex;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for a MD2 frame
*/
// ---------------------------------------------------------------------------
struct Frame
{
float scale[3];
float translate[3];
char name[16];
Vertex vertices[1];
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for a MD2 texture coordinate
*/
// ---------------------------------------------------------------------------
struct TexCoord
{
int16_t s;
int16_t t;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for a MD2 skin
*/
// ---------------------------------------------------------------------------
struct Skin
{
char name[AI_MD2_MAXQPATH]; /* texture file name */
} PACK_STRUCT;
// reset packing to the original value
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack( pop )
#endif
#undef PACK_STRUCT
};
};
#endif // !! include guard

310
code/MD2Loader.cpp 100644
View File

@ -0,0 +1,310 @@
/** @file Implementation of the MD2 importer class */
#include "MD2Loader.h"
#include "MaterialSystem.h"
#include "MD2NormalTable.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;
// ------------------------------------------------------------------------------------------------
inline bool is_qnan(float p_fIn)
{
// NOTE: Comparison against qnan is generally problematic
// because qnan == qnan is false AFAIK
union FTOINT
{
float fFloat;
int32_t iInt;
} one, two;
one.fFloat = std::numeric_limits<float>::quiet_NaN();
two.fFloat = p_fIn;
return (one.iInt == two.iInt);
}
// ------------------------------------------------------------------------------------------------
inline bool is_not_qnan(float p_fIn)
{
return !is_qnan(p_fIn);
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
MD2Importer::MD2Importer()
{
}
// ------------------------------------------------------------------------------------------------
// 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) 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 == ".md2" || extension == ".MD2" ||
extension == ".mD2" || extension == ".Md2")
return true;
return false;
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void MD2Importer::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 md2 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(MD2::Header))
{
throw new ImportErrorException( ".md2 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 MD2::Header*)this->mBuffer;
// check magic number
if (this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE &&
this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE)
{
throw new ImportErrorException( "Invalid md2 file: Magic bytes not found");
}
// check file format version
if (this->m_pcHeader->version != 8)
{
throw new ImportErrorException( "Unsupported md3 file version");
}
// check some values whether they are valid
if (0 == this->m_pcHeader->numFrames)
{
throw new ImportErrorException( "Invalid md2 file: NUM_FRAMES is 0");
}
if (this->m_pcHeader->offsetEnd > (int32_t)fileSize)
{
throw new ImportErrorException( "Invalid md2 file: File is too small");
}
// 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 MaterialHelper();
pScene->mNumMeshes = 1;
pScene->mMeshes = new aiMesh*[1];
pScene->mMeshes[0] = new aiMesh();
// navigate to the begin of the frame data
const MD2::Frame* pcFrame = (const MD2::Frame*) ((unsigned char*)this->m_pcHeader +
this->m_pcHeader->offsetFrames);
// navigate to the begin of the triangle data
MD2::Triangle* pcTriangles = (MD2::Triangle*) ((unsigned char*)this->m_pcHeader +
this->m_pcHeader->offsetTriangles);
// navigate to the begin of the tex coords data
const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) ((unsigned char*)this->m_pcHeader +
this->m_pcHeader->offsetTexCoords);
// navigate to the begin of the vertex data
const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices);
pScene->mMeshes[0]->mNumFaces = this->m_pcHeader->numTriangles;
pScene->mMeshes[0]->mFaces = new aiFace[this->m_pcHeader->numTriangles];
// temporary vectors for position/texture coordinates/normals
std::vector<aiVector3D> vPositions;
std::vector<aiVector3D> vTexCoords;
std::vector<aiVector3D> vNormals;
vPositions.resize(this->m_pcHeader->numVertices,aiVector3D());
vTexCoords.resize(this->m_pcHeader->numVertices,aiVector3D(
std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),0.0f));
vNormals.resize(this->m_pcHeader->numVertices,aiVector3D());
// not sure whether there are MD2 files without texture coordinates
if (0 != this->m_pcHeader->numTexCoords && 0 != this->m_pcHeader->numSkins)
{
// navigate to the first texture associated with the mesh
const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)this->m_pcHeader +
this->m_pcHeader->offsetSkins);
const int iMode = (int)aiShadingMode_Gouraud;
MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
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);
aiString szString;
const size_t iLen = strlen(pcSkins->name);
memcpy(szString.data,pcSkins->name,iLen+1);
szString.length = iLen-1;
pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
}
else
{
// apply a default material
const int iMode = (int)aiShadingMode_Gouraud;
MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
aiColor3D clr;
clr.b = clr.g = clr.r = 0.6f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
}
// now read all vertices of the frame
for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numVertices;++i)
{
// read x,y, and z component of the vertex
aiVector3D& vec = vPositions[i];
vec.x = (float)pcVerts[i].vertex[0] * pcFrame->scale[0];
vec.x += pcFrame->translate[0];
// (flip z and y component)
vec.z = (float)pcVerts[i].vertex[1] * pcFrame->scale[1];
vec.z += pcFrame->translate[1];
vec.y = (float)pcVerts[i].vertex[2] * pcFrame->scale[2];
vec.y += pcFrame->translate[2];
// read the normal vector from the precalculated normal table
vNormals[i] = *((const aiVector3D*)(&g_avNormals[std::min(
int(pcVerts[i].lightNormalIndex),
int(sizeof(g_avNormals) / sizeof(g_avNormals[0]))-1)]));
std::swap ( vNormals[i].y,vNormals[i].z );
}
// now read all triangles of the first frame, apply scaling and translation
if (0 != this->m_pcHeader->numTexCoords)
{
for (unsigned int i = 0; i < (unsigned int)this->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)
{
// validate vertex indices
if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices)
pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1;
// copy face indices
pScene->mMeshes[0]->mFaces[i].mIndices[c] = (unsigned int)pcTriangles[i].vertexIndices[c];
// validate texture coordinates
if (pcTriangles[i].textureIndices[c] >= this->m_pcHeader->numTexCoords)
pcTriangles[i].textureIndices[c] = this->m_pcHeader->numTexCoords-1;
aiVector3D* pcOut = &vTexCoords[pScene->mMeshes[0]->mFaces[i].mIndices[c]];
float u,v;
u = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].s / this->m_pcHeader->skinWidth;
v = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].t / this->m_pcHeader->skinHeight;
if ( is_not_qnan ( pcOut->x ) && (pcOut->x != u || pcOut->y != v))
{
// generate a separate vertex/index set
vTexCoords.push_back(aiVector3D(u,v,0.0f));
vPositions.push_back(vPositions[pcTriangles[i].vertexIndices[c]]);
vNormals.push_back(vPositions[pcTriangles[i].vertexIndices[c]]);
unsigned int iPos = vTexCoords.size()-1;
pScene->mMeshes[0]->mFaces[i].mIndices[c] = iPos;
}
else
{
pcOut->x = u;
pcOut->y = v;
}
}
}
}
else
{
for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numTriangles;++i)
{
// allocate the face
pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3];
pScene->mMeshes[0]->mFaces[i].mNumIndices = 3;
// validate vertex indices
if (pcTriangles[i].vertexIndices[0] >= this->m_pcHeader->numVertices)
pcTriangles[i].vertexIndices[0] = this->m_pcHeader->numVertices-1;
if (pcTriangles[i].vertexIndices[1] >= this->m_pcHeader->numVertices)
pcTriangles[i].vertexIndices[1] = this->m_pcHeader->numVertices-1;
if (pcTriangles[i].vertexIndices[2] >= this->m_pcHeader->numVertices)
pcTriangles[i].vertexIndices[2] = this->m_pcHeader->numVertices-1;
// copy face indices
pScene->mMeshes[0]->mFaces[i].mIndices[0] = (unsigned int)pcTriangles[i].vertexIndices[0];
pScene->mMeshes[0]->mFaces[i].mIndices[1] = (unsigned int)pcTriangles[i].vertexIndices[1];
pScene->mMeshes[0]->mFaces[i].mIndices[2] = (unsigned int)pcTriangles[i].vertexIndices[2];
}
}
// allocate output storage
pScene->mMeshes[0]->mNumVertices = vPositions.size();
pScene->mMeshes[0]->mVertices = new aiVector3D[vPositions.size()];
pScene->mMeshes[0]->mNormals = new aiVector3D[vPositions.size()];
pScene->mMeshes[0]->mTextureCoords[0] = new aiVector3D[vPositions.size()];
// memcpy() the data to the c-syle arrays
memcpy(pScene->mMeshes[0]->mVertices, &vPositions[0], vPositions.size() * sizeof(aiVector3D));
memcpy(pScene->mMeshes[0]->mNormals, &vNormals[0], vPositions.size() * sizeof(aiVector3D));
memcpy(pScene->mMeshes[0]->mTextureCoords[0], &vTexCoords[0], vPositions.size() * sizeof(aiVector3D));
return;
}

57
code/MD2Loader.h 100644
View File

@ -0,0 +1,57 @@
/** @file Definition of the .MD2 importer class. */
#ifndef AI_MD2LOADER_H_INCLUDED
#define AI_MD2LOADER_H_INCLUDED
#include "BaseImporter.h"
#include "../include/aiTypes.h"
struct aiNode;
#include "MD2FileData.h"
namespace Assimp
{
class MaterialHelper;
using namespace MD2;
// ---------------------------------------------------------------------------
/** Used to load MD2 files
*/
class MD2Importer : public BaseImporter
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
MD2Importer();
/** Destructor, private as well */
~MD2Importer();
public:
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details. */
bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
protected:
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
protected:
/** Header of the MD2 file */
const MD2::Header* m_pcHeader;
/** Buffer to hold the loaded file */
const unsigned char* mBuffer;
};
} // end of namespace Assimp
#endif // AI_3DSIMPORTER_H_INC

View File

@ -0,0 +1,171 @@
/*
* Slightly modified version of the anorms.h header file released by
* ID software with the Quake 2 source code.
*
* Table of normals used by MD2 models
*/
float g_avNormals[][3] = {
{ -0.525731f, 0.000000f, 0.850651f },
{ -0.442863f, 0.238856f, 0.864188f },
{ -0.295242f, 0.000000f, 0.955423f },
{ -0.309017f, 0.500000f, 0.809017f },
{ -0.162460f, 0.262866f, 0.951056f },
{ 0.000000f, 0.000000f, 1.000000f },
{ 0.000000f, 0.850651f, 0.525731f },
{ -0.147621f, 0.716567f, 0.681718f },
{ 0.147621f, 0.716567f, 0.681718f },
{ 0.000000f, 0.525731f, 0.850651f },
{ 0.309017f, 0.500000f, 0.809017f },
{ 0.525731f, 0.000000f, 0.850651f },
{ 0.295242f, 0.000000f, 0.955423f },
{ 0.442863f, 0.238856f, 0.864188f },
{ 0.162460f, 0.262866f, 0.951056f },
{ -0.681718f, 0.147621f, 0.716567f },
{ -0.809017f, 0.309017f, 0.500000f },
{ -0.587785f, 0.425325f, 0.688191f },
{ -0.850651f, 0.525731f, 0.000000f },
{ -0.864188f, 0.442863f, 0.238856f },
{ -0.716567f, 0.681718f, 0.147621f },
{ -0.688191f, 0.587785f, 0.425325f },
{ -0.500000f, 0.809017f, 0.309017f },
{ -0.238856f, 0.864188f, 0.442863f },
{ -0.425325f, 0.688191f, 0.587785f },
{ -0.716567f, 0.681718f, -0.147621f },
{ -0.500000f, 0.809017f, -0.309017f },
{ -0.525731f, 0.850651f, 0.000000f },
{ 0.000000f, 0.850651f, -0.525731f },
{ -0.238856f, 0.864188f, -0.442863f },
{ 0.000000f, 0.955423f, -0.295242f },
{ -0.262866f, 0.951056f, -0.162460f },
{ 0.000000f, 1.000000f, 0.000000f },
{ 0.000000f, 0.955423f, 0.295242f },
{ -0.262866f, 0.951056f, 0.162460f },
{ 0.238856f, 0.864188f, 0.442863f },
{ 0.262866f, 0.951056f, 0.162460f },
{ 0.500000f, 0.809017f, 0.309017f },
{ 0.238856f, 0.864188f, -0.442863f },
{ 0.262866f, 0.951056f, -0.162460f },
{ 0.500000f, 0.809017f, -0.309017f },
{ 0.850651f, 0.525731f, 0.000000f },
{ 0.716567f, 0.681718f, 0.147621f },
{ 0.716567f, 0.681718f, -0.147621f },
{ 0.525731f, 0.850651f, 0.000000f },
{ 0.425325f, 0.688191f, 0.587785f },
{ 0.864188f, 0.442863f, 0.238856f },
{ 0.688191f, 0.587785f, 0.425325f },
{ 0.809017f, 0.309017f, 0.500000f },
{ 0.681718f, 0.147621f, 0.716567f },
{ 0.587785f, 0.425325f, 0.688191f },
{ 0.955423f, 0.295242f, 0.000000f },
{ 1.000000f, 0.000000f, 0.000000f },
{ 0.951056f, 0.162460f, 0.262866f },
{ 0.850651f, -0.525731f, 0.000000f },
{ 0.955423f, -0.295242f, 0.000000f },
{ 0.864188f, -0.442863f, 0.238856f },
{ 0.951056f, -0.162460f, 0.262866f },
{ 0.809017f, -0.309017f, 0.500000f },
{ 0.681718f, -0.147621f, 0.716567f },
{ 0.850651f, 0.000000f, 0.525731f },
{ 0.864188f, 0.442863f, -0.238856f },
{ 0.809017f, 0.309017f, -0.500000f },
{ 0.951056f, 0.162460f, -0.262866f },
{ 0.525731f, 0.000000f, -0.850651f },
{ 0.681718f, 0.147621f, -0.716567f },
{ 0.681718f, -0.147621f, -0.716567f },
{ 0.850651f, 0.000000f, -0.525731f },
{ 0.809017f, -0.309017f, -0.500000f },
{ 0.864188f, -0.442863f, -0.238856f },
{ 0.951056f, -0.162460f, -0.262866f },
{ 0.147621f, 0.716567f, -0.681718f },
{ 0.309017f, 0.500000f, -0.809017f },
{ 0.425325f, 0.688191f, -0.587785f },
{ 0.442863f, 0.238856f, -0.864188f },
{ 0.587785f, 0.425325f, -0.688191f },
{ 0.688191f, 0.587785f, -0.425325f },
{ -0.147621f, 0.716567f, -0.681718f },
{ -0.309017f, 0.500000f, -0.809017f },
{ 0.000000f, 0.525731f, -0.850651f },
{ -0.525731f, 0.000000f, -0.850651f },
{ -0.442863f, 0.238856f, -0.864188f },
{ -0.295242f, 0.000000f, -0.955423f },
{ -0.162460f, 0.262866f, -0.951056f },
{ 0.000000f, 0.000000f, -1.000000f },
{ 0.295242f, 0.000000f, -0.955423f },
{ 0.162460f, 0.262866f, -0.951056f },
{ -0.442863f, -0.238856f, -0.864188f },
{ -0.309017f, -0.500000f, -0.809017f },
{ -0.162460f, -0.262866f, -0.951056f },
{ 0.000000f, -0.850651f, -0.525731f },
{ -0.147621f, -0.716567f, -0.681718f },
{ 0.147621f, -0.716567f, -0.681718f },
{ 0.000000f, -0.525731f, -0.850651f },
{ 0.309017f, -0.500000f, -0.809017f },
{ 0.442863f, -0.238856f, -0.864188f },
{ 0.162460f, -0.262866f, -0.951056f },
{ 0.238856f, -0.864188f, -0.442863f },
{ 0.500000f, -0.809017f, -0.309017f },
{ 0.425325f, -0.688191f, -0.587785f },
{ 0.716567f, -0.681718f, -0.147621f },
{ 0.688191f, -0.587785f, -0.425325f },
{ 0.587785f, -0.425325f, -0.688191f },
{ 0.000000f, -0.955423f, -0.295242f },
{ 0.000000f, -1.000000f, 0.000000f },
{ 0.262866f, -0.951056f, -0.162460f },
{ 0.000000f, -0.850651f, 0.525731f },
{ 0.000000f, -0.955423f, 0.295242f },
{ 0.238856f, -0.864188f, 0.442863f },
{ 0.262866f, -0.951056f, 0.162460f },
{ 0.500000f, -0.809017f, 0.309017f },
{ 0.716567f, -0.681718f, 0.147621f },
{ 0.525731f, -0.850651f, 0.000000f },
{ -0.238856f, -0.864188f, -0.442863f },
{ -0.500000f, -0.809017f, -0.309017f },
{ -0.262866f, -0.951056f, -0.162460f },
{ -0.850651f, -0.525731f, 0.000000f },
{ -0.716567f, -0.681718f, -0.147621f },
{ -0.716567f, -0.681718f, 0.147621f },
{ -0.525731f, -0.850651f, 0.000000f },
{ -0.500000f, -0.809017f, 0.309017f },
{ -0.238856f, -0.864188f, 0.442863f },
{ -0.262866f, -0.951056f, 0.162460f },
{ -0.864188f, -0.442863f, 0.238856f },
{ -0.809017f, -0.309017f, 0.500000f },
{ -0.688191f, -0.587785f, 0.425325f },
{ -0.681718f, -0.147621f, 0.716567f },
{ -0.442863f, -0.238856f, 0.864188f },
{ -0.587785f, -0.425325f, 0.688191f },
{ -0.309017f, -0.500000f, 0.809017f },
{ -0.147621f, -0.716567f, 0.681718f },
{ -0.425325f, -0.688191f, 0.587785f },
{ -0.162460f, -0.262866f, 0.951056f },
{ 0.442863f, -0.238856f, 0.864188f },
{ 0.162460f, -0.262866f, 0.951056f },
{ 0.309017f, -0.500000f, 0.809017f },
{ 0.147621f, -0.716567f, 0.681718f },
{ 0.000000f, -0.525731f, 0.850651f },
{ 0.425325f, -0.688191f, 0.587785f },
{ 0.587785f, -0.425325f, 0.688191f },
{ 0.688191f, -0.587785f, 0.425325f },
{ -0.955423f, 0.295242f, 0.000000f },
{ -0.951056f, 0.162460f, 0.262866f },
{ -1.000000f, 0.000000f, 0.000000f },
{ -0.850651f, 0.000000f, 0.525731f },
{ -0.955423f, -0.295242f, 0.000000f },
{ -0.951056f, -0.162460f, 0.262866f },
{ -0.864188f, 0.442863f, -0.238856f },
{ -0.951056f, 0.162460f, -0.262866f },
{ -0.809017f, 0.309017f, -0.500000f },
{ -0.864188f, -0.442863f, -0.238856f },
{ -0.951056f, -0.162460f, -0.262866f },
{ -0.809017f, -0.309017f, -0.500000f },
{ -0.681718f, 0.147621f, -0.716567f },
{ -0.681718f, -0.147621f, -0.716567f },
{ -0.850651f, 0.000000f, -0.525731f },
{ -0.688191f, 0.587785f, -0.425325f },
{ -0.587785f, 0.425325f, -0.688191f },
{ -0.425325f, 0.688191f, -0.587785f },
{ -0.425325f, -0.688191f, -0.587785f },
{ -0.587785f, -0.425325f, -0.688191f },
{ -0.688191f, -0.587785f, -0.425325f }
};

273
code/MD3FileData.h 100644
View File

@ -0,0 +1,273 @@
/** @file Defines the helper data structures for importing MD3 files */
#ifndef AI_MD3FILEHELPER_H_INC
#define AI_MD3FILEHELPER_H_INC
#include <string>
#include <vector>
#include <sstream>
#include "../include/aiTypes.h"
#include "../include/aiMesh.h"
#include "../include/aiAnim.h"
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack(push,1)
# define PACK_STRUCT
#elif defined( __GNUC__ )
# define PACK_STRUCT __attribute__((packed))
#else
# error Compiler not supported
#endif
namespace Assimp
{
// http://linux.ucla.edu/~phaethon/q3/formats/md3format.html
namespace MD3
{
#define AI_MD3_MAGIC_NUMBER_BE 'IDP3'
#define AI_MD3_MAGIC_NUMBER_LE '3PDI'
// common limitations
#define AI_MD3_VERSION 15
#define AI_MD3_MAXQPATH 64
#define AI_MD3_MAX_FRAMES 1024
#define AI_MD3_MAX_TAGS 16
#define AI_MD3_MAX_SURFACES 32
#define AI_MD3_MAX_SHADERS 256
#define AI_MD3_MAX_VERTS 4096
#define AI_MD3_MAX_TRIANGLES 8192
// master scale factor for all vertices in a MD3 model
#define AI_MD3_XYZ_SCALE (1.0f/64.0f)
// ---------------------------------------------------------------------------
/** \brief Data structure for the MD3 main header
*/
// ---------------------------------------------------------------------------
struct Header
{
// magic number
int32_t IDENT;
// file format version
int32_t VERSION;
// original name in .pak archive
unsigned char NAME[ AI_MD3_MAXQPATH ];
// unknown
int32_t FLAGS;
// number of frames in the file
int32_t NUM_FRAMES;
// number of tags in the file
int32_t NUM_TAGS;
// number of surfaces in the file
int32_t NUM_SURFACES;
// number of skins in the file
int32_t NUM_SKINS;
// offset of the first frame
int32_t OFS_FRAMES;
// offset of the first tag
int32_t OFS_TAGS;
// offset of the first surface
int32_t OFS_SURFACES;
// end of file
int32_t OFS_EOF;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for the frame header
*/
// ---------------------------------------------------------------------------
struct Frame
{
// no need to define this, we won't need
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for the tag header
*/
// ---------------------------------------------------------------------------
struct Tag
{
// no need to define this, we won't need
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for the surface header
*/
// ---------------------------------------------------------------------------
struct Surface
{
// magic number
int32_t IDENT;
// original name of the surface
unsigned char NAME[ AI_MD3_MAXQPATH ];
// unknown
int32_t FLAGS;
// number of frames in the surface
int32_t NUM_FRAMES;
// number of shaders in the surface
int32_t NUM_SHADER;
// number of vertices in the surface
int32_t NUM_VERTICES;
// number of triangles in the surface
int32_t NUM_TRIANGLES;
// offset to the triangle data
int32_t OFS_TRIANGLES;
// offset to the shader data
int32_t OFS_SHADERS;
// offset to the texture coordinate data
int32_t OFS_ST;
// offset to the vertex/normal data
int32_t OFS_XYZNORMAL;
// offset to the end of the Surface object
int32_t OFS_END;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for a shader
*/
// ---------------------------------------------------------------------------
struct Shader
{
// filename of the shader
unsigned char NAME[ AI_MD3_MAXQPATH ];
// index of the shader
int32_t SHADER_INDEX;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for a triangle
*/
// ---------------------------------------------------------------------------
struct Triangle
{
// triangle indices
int32_t INDEXES[3];
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for an UV coord
*/
// ---------------------------------------------------------------------------
struct TexCoord
{
// UV coordinates
float U,V;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** \brief Data structure for a vertex
*/
// ---------------------------------------------------------------------------
struct Vertex
{
// X/Y/Z coordinates
int16_t X,Y,Z;
// encoded normal vector
int16_t NORMAL;
} PACK_STRUCT;
// reset packing to the original value
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack( pop )
#endif
#undef PACK_STRUCT
// ---------------------------------------------------------------------------
/** \brief Unpack a Q3 16 bit vector to its full float3 representation
*
* \param p_iNormal Input normal vector in latitude/longitude form
* \param p_afOut Pointer to an array of three floats to receive the result
*
* \note This has been taken from q3 source (misc_model.c)
*/
// ---------------------------------------------------------------------------
inline void LatLngNormalToVec3(uint16_t p_iNormal, float* p_afOut)
{
float lat = (float)(( p_iNormal >> 8 ) & 0xff);
float lng = (float)(( p_iNormal & 0xff ));
lat *= 3.141926f/128.0f;
lng *= 3.141926f/128.0f;
p_afOut[0] = cosf(lat) * sinf(lng);
p_afOut[1] = sinf(lat) * sinf(lng);
p_afOut[2] = cosf(lng);
return;
}
// ---------------------------------------------------------------------------
/** \brief Pack a Q3 normal into 16bit latitute/longitude representation
* \param p_vIn Input vector
* \param p_iOut Output normal
*
* \note This has been taken from q3 source (mathlib.c)
*/
// ---------------------------------------------------------------------------
inline void Vec3NormalToLatLng( const aiVector3D& p_vIn, uint16_t& p_iOut )
{
// check for singularities
if ( 0.0f == p_vIn[0] && 0.0f == p_vIn[1] )
{
if ( p_vIn[2] > 0.0f )
{
((unsigned char*)&p_iOut)[0] = 0;
((unsigned char*)&p_iOut)[1] = 0; // lat = 0, long = 0
}
else
{
((unsigned char*)&p_iOut)[0] = 128;
((unsigned char*)&p_iOut)[1] = 0; // lat = 0, long = 128
}
}
else
{
int a, b;
a = int(57.2957795f * ( atan2f( p_vIn[1], p_vIn[0] ) ) * (255.0f / 360.0f ));
a &= 0xff;
b = int(57.2957795f * ( acosf( p_vIn[2] ) ) * ( 255.0f / 360.0f ));
b &= 0xff;
((unsigned char*)&p_iOut)[0] = b; // longitude
((unsigned char*)&p_iOut)[1] = a; // lattitude
}
}
};
};
#endif // !! AI_MD3FILEHELPER_H_INC

328
code/MD3Loader.cpp 100644
View File

@ -0,0 +1,328 @@
/** @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;
}

60
code/MD3Loader.h 100644
View File

@ -0,0 +1,60 @@
/** @file Definition of the .MD3 importer class. */
#ifndef AI_MD3LOADER_H_INCLUDED
#define AI_MD3LOADER_H_INCLUDED
#include <map>
#include "BaseImporter.h"
#include "../include/aiTypes.h"
struct aiNode;
#include "MD3FileData.h"
namespace Assimp
{
class MaterialHelper;
using namespace MD3;
// ---------------------------------------------------------------------------
/** Used to load MD3 files
*/
class MD3Importer : public BaseImporter
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
MD3Importer();
/** Destructor, private as well */
~MD3Importer();
public:
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details. */
bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
protected:
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
protected:
/** Header of the MD3 file */
const MD3::Header* m_pcHeader;
/** Buffer to hold the loaded file */
const unsigned char* mBuffer;
};
} // end of namespace Assimp
#endif // AI_3DSIMPORTER_H_INC

View File

57
code/MD4Loader.h 100644
View File

@ -0,0 +1,57 @@
/** @file Definition of the .MD4 importer class. */
#ifndef AI_MD4LOADER_H_INCLUDED
#define AI_MD4LOADER_H_INCLUDED
#include "BaseImporter.h"
#include "../include/aiTypes.h"
struct aiNode;
#include "MD4FileData.h"
namespace Assimp
{
class MaterialHelper;
using namespace MD4;
// ---------------------------------------------------------------------------
/** Used to load MD4 files
*/
class MD4Importer : public BaseImporter
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
MD4Importer();
/** Destructor, private as well */
~MD4Importer();
public:
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details. */
bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
protected:
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
protected:
/** Header of the MD4 file */
const MD4::Header* m_pcHeader;
/** Buffer to hold the loaded file */
const unsigned char* mBuffer;
};
} // end of namespace Assimp
#endif // AI_3DSIMPORTER_H_INC

57
code/MD5Loader.h 100644
View File

@ -0,0 +1,57 @@
/** @file Definition of the .MD5 importer class. */
#ifndef AI_MD5LOADER_H_INCLUDED
#define AI_MD5LOADER_H_INCLUDED
#include "BaseImporter.h"
#include "../include/aiTypes.h"
struct aiNode;
#include "MD5FileData.h"
namespace Assimp
{
class MaterialHelper;
using namespace MD5;
// ---------------------------------------------------------------------------
/** Used to load MD5 files
*/
class MD5Importer : public BaseImporter
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
MD5Importer();
/** Destructor, private as well */
~MD5Importer();
public:
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details. */
bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
protected:
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
protected:
/** Header of the MD5 file */
const MD5::Header* m_pcHeader;
/** Buffer to hold the loaded file */
const unsigned char* mBuffer;
};
} // end of namespace Assimp
#endif // AI_3DSIMPORTER_H_INC

View File

@ -0,0 +1,285 @@
#include "assimp.h"
#include "aiMaterial.h"
#include "assimp.hpp"
#include "MaterialSystem.h"
#include "../include/aiAssert.h"
using namespace Assimp;
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
aiReturn aiGetMaterialProperty(const aiMaterial* pMat,
const char* pKey,
const aiMaterialProperty** pPropOut)
{
#if (defined DEBUG)
ai_assert (pMat != NULL);
ai_assert (pKey != NULL);
ai_assert (pPropOut != NULL);
#endif // ASSIMP_DEBUG
for (unsigned int i = 0; i < pMat->mNumProperties;++i)
{
if (NULL != pMat->mProperties[i])
{
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
{
*pPropOut = pMat->mProperties[i];
return AI_SUCCESS;
}
}
}
*pPropOut = NULL;
return AI_FAILURE;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat,
const char* pKey,
float* pOut,
unsigned int* pMax)
{
#if (defined DEBUG)
ai_assert (pMat != NULL);
ai_assert (pKey != NULL);
ai_assert (pOut != NULL);
#endif // ASSIMP_DEBUG
for (unsigned int i = 0; i < pMat->mNumProperties;++i)
{
if (NULL != pMat->mProperties[i])
{
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
{
// data is given in floats, simply copy it
if( aiPTI_Float == pMat->mProperties[i]->mType ||
aiPTI_Buffer == pMat->mProperties[i]->mType)
{
unsigned int iWrite = pMat->mProperties[i]->
mDataLength / sizeof(float);
if (NULL != pMax)
iWrite = *pMax < iWrite ? *pMax : iWrite;
memcpy (pOut, pMat->mProperties[i]->mData, iWrite * sizeof (float));
if (NULL != pMax)
*pMax = iWrite;
}
// data is given in ints, convert to float
else if( aiPTI_Integer == pMat->mProperties[i]->mType)
{
unsigned int iWrite = pMat->mProperties[i]->
mDataLength / sizeof(int);
if (NULL != pMax)
iWrite = *pMax < iWrite ? *pMax : iWrite;
for (unsigned int a = 0; a < iWrite;++a)
{
pOut[a] = (float) ((int*)pMat->mProperties[i]->mData)[a];
}
if (NULL != pMax)
*pMax = iWrite;
}
// it is a string ... no way to read something out of this
else
{
if (NULL != pMax)
*pMax = 0;
return AI_FAILURE;
}
return AI_SUCCESS;
}
}
}
return AI_FAILURE;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat,
const char* pKey,
int* pOut,
unsigned int* pMax)
{
#if (defined DEBUG)
ai_assert (pMat != NULL);
ai_assert (pKey != NULL);
ai_assert (pOut != NULL);
#endif // ASSIMP_DEBUG
for (unsigned int i = 0; i < pMat->mNumProperties;++i)
{
if (NULL != pMat->mProperties[i])
{
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
{
// data is given in ints, simply copy it
if( aiPTI_Integer == pMat->mProperties[i]->mType ||
aiPTI_Buffer == pMat->mProperties[i]->mType)
{
unsigned int iWrite = pMat->mProperties[i]->
mDataLength / sizeof(int);
if (NULL != pMax)
iWrite = *pMax < iWrite ? *pMax : iWrite;
memcpy (pOut, pMat->mProperties[i]->mData, iWrite * sizeof (int));
if (NULL != pMax)
*pMax = iWrite;
}
// data is given in floats convert to int (lossy!)
else if( aiPTI_Float == pMat->mProperties[i]->mType)
{
unsigned int iWrite = pMat->mProperties[i]->
mDataLength / sizeof(float);
if (NULL != pMax)
iWrite = *pMax < iWrite ? *pMax : iWrite;
for (unsigned int a = 0; a < iWrite;++a)
{
pOut[a] = (int) ((float*)pMat->mProperties[i]->mData)[a];
}
if (NULL != pMax)
*pMax = iWrite;
}
// it is a string ... no way to read something out of this
else
{
if (NULL != pMax)
*pMax = 0;
return AI_FAILURE;
}
return AI_SUCCESS;
}
}
}
return AI_FAILURE;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
aiReturn aiGetMaterialColor(const aiMaterial* pMat,
const char* pKey,
aiColor4D* pOut)
{
unsigned int iMax = 4;
aiReturn eRet = aiGetMaterialFloatArray(pMat,pKey,(float*)pOut,&iMax);
// if no alpha channel is provided set it to 1.0 by default
if (3 == iMax)pOut->a = 1.0f;
return eRet;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
aiReturn aiGetMaterialString(const aiMaterial* pMat,
const char* pKey,
aiString* pOut)
{
#if (defined DEBUG)
ai_assert (pMat != NULL);
ai_assert (pKey != NULL);
ai_assert (pOut != NULL);
#endif // ASSIMP_DEBUG
for (unsigned int i = 0; i < pMat->mNumProperties;++i)
{
if (NULL != pMat->mProperties[i])
{
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
{
if( aiPTI_String == pMat->mProperties[i]->mType)
{
memcpy (pOut, pMat->mProperties[i]->mData,
sizeof(aiString));
}
// wrong type
else return AI_FAILURE;
return AI_SUCCESS;
}
}
}
return AI_FAILURE;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
aiReturn MaterialHelper::AddBinaryProperty (const void* pInput,
const unsigned int pSizeInBytes,
const char* pKey,
aiPropertyTypeInfo pType)
{
#if (defined DEBUG)
ai_assert (pInput != NULL);
ai_assert (pKey != NULL);
ai_assert (0 != pSizeInBytes);
#endif // ASSIMP_DEBUG
aiMaterialProperty* pcNew = new aiMaterialProperty();
// fill this
pcNew->mKey = new aiString();
pcNew->mType = pType;
pcNew->mDataLength = pSizeInBytes;
pcNew->mData = new char[pSizeInBytes];
memcpy (pcNew->mData,pInput,pSizeInBytes);
pcNew->mKey->length = strlen(pKey);
ai_assert ( MAXLEN > pcNew->mKey->length);
strcpy( pcNew->mKey->data, pKey );
// resize the array ... allocate
// storage for 5 other properties
if (this->mNumProperties == this->mNumAllocated)
{
unsigned int iOld = this->mNumAllocated;
this->mNumAllocated += 5;
aiMaterialProperty** ppTemp = new aiMaterialProperty*[this->mNumAllocated];
if (NULL == ppTemp)return AI_OUTOFMEMORY;
memcpy (ppTemp,this->mProperties,iOld * sizeof(void*));
delete[] this->mProperties;
this->mProperties = ppTemp;
}
// push back ...
this->mProperties[this->mNumProperties++] = pcNew;
return AI_SUCCESS;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
aiReturn MaterialHelper::AddProperty (const aiString* pInput,
const char* pKey)
{
return this->AddBinaryProperty(pInput,
sizeof(aiString),pKey,aiPTI_String);
}

View File

@ -0,0 +1,187 @@
/** @file Definition of the base class for all importer worker classes. */
#ifndef AI_MATERIALSYSTEM_H_INC
#define AI_MATERIALSYSTEM_H_INC
#include "../include/aiMaterial.h"
namespace Assimp
{
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
inline int ASSIMP_stricmp(const char *s1, const char *s2)
{
const char *a1, *a2;
a1 = s1;
a2 = s2;
while (true)
{
char c1 = (char)tolower(*a1);
char c2 = (char)tolower(*a2);
if ((0 == c1) && (0 == c2)) return 0;
if (c1 < c2) return-1;
if (c1 > c2) return 1;
++a1;
++a2;
}
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n)
{
const char *a1, *a2;
a1 = s1;
a2 = s2;
unsigned int p = 0;
while (true)
{
if (p >= n)return 0;
char c1 = (char)tolower(*a1);
char c2 = (char)tolower(*a2);
if ((0 == c1) && (0 == c2)) return 0;
if (c1 < c2) return-1;
if (c1 > c2) return 1;
++a1;
++a2;
++p;
}
}
// ---------------------------------------------------------------------------
/** Internal material helper class. Can be used to fill an aiMaterial
structure easily. */
class MaterialHelper : public ::aiMaterial
{
public:
inline MaterialHelper();
inline ~MaterialHelper();
// -------------------------------------------------------------------
/** Add a property with a given key and type info to the material
structure */
aiReturn AddBinaryProperty (const void* pInput,
const unsigned int pSizeInBytes,
const char* pKey,
aiPropertyTypeInfo pType);
// -------------------------------------------------------------------
/** Add a string property with a given key and type info to the
material structure */
aiReturn AddProperty (const aiString* pInput,
const char* pKey);
// -------------------------------------------------------------------
/** Add a property with a given key to the material structure */
template<class TYPE>
aiReturn AddProperty (const TYPE* pInput,
const unsigned int pNumValues,
const char* pKey);
};
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
inline MaterialHelper::MaterialHelper()
{
// allocate 5 entries by default
this->mNumProperties = 0;
this->mNumAllocated = 5;
this->mProperties = new aiMaterialProperty*[5];
return;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
inline MaterialHelper::~MaterialHelper()
{
for (unsigned int i = 0; i < this->mNumProperties;++i)
{
// be careful ...
if(NULL != this->mProperties[i])
{
delete[] this->mProperties[i]->mKey;
delete[] this->mProperties[i]->mData;
delete this->mProperties[i];
}
}
return;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
template<class TYPE>
aiReturn MaterialHelper::AddProperty (const TYPE* pInput,
const unsigned int pNumValues,
const char* pKey)
{
return this->AddBinaryProperty((const void*)pInput,
pNumValues * sizeof(TYPE),
pKey,aiPTI_Buffer);
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
template<>
inline aiReturn MaterialHelper::AddProperty<float> (const float* pInput,
const unsigned int pNumValues,
const char* pKey)
{
return this->AddBinaryProperty((const void*)pInput,
pNumValues * sizeof(float),
pKey,aiPTI_Float);
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
template<>
inline aiReturn MaterialHelper::AddProperty<aiColor4D> (const aiColor4D* pInput,
const unsigned int pNumValues,
const char* pKey)
{
return this->AddBinaryProperty((const void*)pInput,
pNumValues * sizeof(aiColor4D),
pKey,aiPTI_Float);
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
template<>
inline aiReturn MaterialHelper::AddProperty<aiColor3D> (const aiColor3D* pInput,
const unsigned int pNumValues,
const char* pKey)
{
return this->AddBinaryProperty((const void*)pInput,
pNumValues * sizeof(aiColor3D),
pKey,aiPTI_Float);
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
template<>
inline aiReturn MaterialHelper::AddProperty<int> (const int* pInput,
const unsigned int pNumValues,
const char* pKey)
{
return this->AddBinaryProperty((const void*)pInput,
pNumValues * sizeof(int),
pKey,aiPTI_Integer);
}
}
#endif //!! AI_MATERIALSYSTEM_H_INC

170
code/ObjFileData.h 100644
View File

@ -0,0 +1,170 @@
#ifndef OBJ_FILEDATA_H_INC
#define OBJ_FILEDATA_H_INC
#include <vector>
#include <map>
#include "aiTypes.h"
namespace Assimp
{
namespace ObjFile
{
struct Object;
struct Face;
struct Material;
// ------------------------------------------------------------------------------------------------
//! \struct Face
//! \brief Datastructure for a simple obj-face, descripes discredisation and materials
struct Face
{
typedef std::vector<unsigned int> IndexArray;
//! Primitive type
int m_PrimitiveType;
//! Vertex indices
IndexArray *m_pVertices;
//! Normal indices
IndexArray *m_pNormals;
//! Texture coordinates indices
IndexArray *m_pTexturCoords;
//! Pointer to assigned material
Material *m_pMaterial;
//! \brief Default constructor
//! \param pVertices Pointer to assigned vertex indexbuffer
//! \param pNormals Pointer to assigned normals indexbuffer
//! \param pTexCoords Pointer to assigned texture indexbuffer
Face(std::vector<unsigned int> *pVertices,
std::vector<unsigned int> *pNormals,
std::vector<unsigned int> *pTexCoords) :
m_PrimitiveType(2),
m_pVertices(pVertices),
m_pNormals(pNormals),
m_pTexturCoords(pTexCoords),
m_pMaterial(0L)
{
// empty
}
//! \brief Destructor
~Face()
{
// empty
}
};
// ------------------------------------------------------------------------------------------------
//! \struct Object
//! \brief Stores all objects of an objfile object definition
struct Object
{
//! Obejct name
std::string m_strObjName;
//! Assigend face instances
std::vector<Face*> m_Faces;
//! Transformation matrix, stored in OpenGL format
aiMatrix4x4 m_Transformation;
//! All subobjects references by this object
std::vector<Object*> m_SubObjects;
//! \brief Default constructor
Object() :
m_strObjName("")
{
// empty
}
//! \brief Destructor
~Object()
{
for (std::vector<Object*>::iterator it = m_SubObjects.begin();
it != m_SubObjects.end(); ++it)
{
delete *it;
}
m_SubObjects.clear();
}
};
// ------------------------------------------------------------------------------------------------
//! \struct Material
//! \brief Data structure to store all material specific data
struct Material
{
aiString MaterialName;
aiString texture;
aiColor3D ambient;
aiColor3D diffuse;
aiColor3D specular;
float alpha;
float shineness;
int illumination_model;
};
// ------------------------------------------------------------------------------------------------
//! \struct Model
//! \brief Data structure to store all obj-specific model datas
struct Model
{
typedef std::map<std::string*, std::vector<unsigned int>* > GroupMap;
typedef std::map<std::string*, std::vector<unsigned int>* >::iterator GroupMapIt;
typedef std::map<std::string*, std::vector<unsigned int>* >::const_iterator ConstGroupMapIt;
//! Model name
std::string m_ModelName;
//! List ob assigned objects
std::vector<Object*> m_Objects;
//! Pointer to current object
ObjFile::Object *m_pCurrent;
//! Pointer to current material
ObjFile::Material *m_pCurrentMaterial;
//! Pointer to default material
ObjFile::Material *m_pDefaultMaterial;
//! Vector with all generated materials
std::vector<std::string> m_MaterialLib;
//! Vector with all generated group
std::vector<std::string> m_GroupLib;
//! Vector with all generated vertices
std::vector<aiVector3D_t*> m_Vertices;
//! vector with all generated normals
std::vector<aiVector3D_t*> m_Normals;
//! Groupmap
GroupMap m_Groups;
std::vector<unsigned int> *m_pGroupFaceIDs;
//! Active group
std::string m_strActiveGroup;
//! Vector with generated texture coordinates
std::vector<aiVector2D_t*> m_TextureCoord;
//! Material map
std::map<std::string, Material*> m_MaterialMap;
//! \brief Default constructor
Model() :
m_ModelName(""),
m_pCurrent(NULL),
m_pCurrentMaterial(NULL),
m_pDefaultMaterial(NULL),
m_strActiveGroup("")
{
// empty
}
//! \brief DEstructor
~Model()
{
for (std::vector<Object*>::iterator it = m_Objects.begin();
it != m_Objects.end(); ++it)
{
delete *it;
}
m_Objects.clear();
}
};
} // Namespace ObjFile
} // Namespace Assimp
#endif

View File

@ -0,0 +1,354 @@
#include "ObjFileImporter.h"
#include "ObjFileParser.h"
#include "ObjFileData.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
#include "aiAssert.h"
#include "MaterialSystem.h"
#include <boost/scoped_ptr.hpp>
#include <boost/format.hpp>
namespace Assimp
{
// ------------------------------------------------------------------------------------------------
using namespace std;
//! Obj-file-format extention
const string ObjFileImporter::OBJ_EXT = "obj";
// ------------------------------------------------------------------------------------------------
// Default constructor
ObjFileImporter::ObjFileImporter() :
m_pRootObject(NULL),
m_strAbsPath("\\")
{
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor
ObjFileImporter::~ObjFileImporter()
{
// Release root object instance
if (NULL != m_pRootObject)
{
delete m_pRootObject;
m_pRootObject = NULL;
}
}
// ------------------------------------------------------------------------------------------------
// Returns true, fi file is an obj file
bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
{
if (pFile.empty())
return false;
string::size_type pos = pFile.find_last_of(".");
if (string::npos == pos)
return false;
const string ext = pFile.substr(pos+1, pFile.size() - pos - 1);
if (ext == OBJ_EXT)
return true;
return false;
}
// ------------------------------------------------------------------------------------------------
// Obj-file import implementation
void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{
// Read file into memory
const std::string mode = "rb";
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, mode));
if (NULL == file.get())
throw new ImportErrorException( "Failed to open file " + pFile + ".");
// Get the filesize and vaslidate it, throwing an exception when failes
size_t fileSize = file->FileSize();
if( fileSize < 16)
throw new ImportErrorException( "OBJ-file is too small.");
// Allocate buffer and read file into it
m_Buffer.resize( fileSize );
const size_t readsize = file->Read(&m_Buffer.front(), sizeof(char), fileSize);
assert (readsize == fileSize);
//
std::string strDirectory("\\"), strModelName;
std::string::size_type pos = pFile.find_last_of("\\");
if (pos != std::string::npos)
{
strDirectory = pFile.substr(0, pos);
strModelName = pFile.substr(pos+1, pFile.size() - pos - 1);
}
else
{
strModelName = pFile;
}
// parse the file into a temporary representation
ObjFileParser parser(m_Buffer, strDirectory, strModelName);
// And create the proper return structures out of it
CreateDataFromImport(parser.GetModel(), pScene);
}
// ------------------------------------------------------------------------------------------------
// Create the data from parsed obj-file
void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene)
{
if (0L == pModel)
return;
// Create the root node of the scene
pScene->mRootNode = new aiNode();
if (!pModel->m_ModelName.empty())
{
// Set the name of the scene
pScene->mRootNode->mName.Set(pModel->m_ModelName);
}
else
{
// This is an error, so break down the application
assert (false);
}
// Create nodes for the whole scene
std::vector<aiMesh*> MeshArray;
for (size_t index = 0; index < pModel->m_Objects.size(); index++)
{
createNodes(pModel, pModel->m_Objects[ index ], pScene->mRootNode, pScene, MeshArray);
}
// Create mesh pointer buffer for this scene
if (pScene->mNumMeshes > 0)
{
pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ];
for (size_t index =0; index < pScene->mNumMeshes; index++)
{
pScene->mMeshes [ index ] = MeshArray[ index ];
}
}
// Create all materials
for (size_t index = 0; index < pModel->m_Objects.size(); index++)
{
createMaterial(pModel, pModel->m_Objects[ index ], pScene);
}
}
// ------------------------------------------------------------------------------------------------
// Creates all nodes of the model
aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pData,
aiNode *pParent, aiScene* pScene,
std::vector<aiMesh*> &MeshArray)
{
if (NULL == pData)
return NULL;
// Store older mesh size to be able to computate mesh offsets for new mesh instances
size_t oldMeshSize = MeshArray.size();
aiNode *pNode = new aiNode();
if (pParent != NULL)
this->appendChildToParentNode(pParent, pNode);
aiMesh *pMesh = new aiMesh();
MeshArray.push_back(pMesh);
createTopology(pModel, pData, pMesh);
// Create all nodes from the subobjects stored in the current object
if (!pData->m_SubObjects.empty())
{
pNode->mNumChildren = pData->m_SubObjects.size();
pNode->mChildren = new aiNode*[pData->m_SubObjects.size()];
pNode->mNumMeshes = 1;
pNode->mMeshes = new unsigned int[1];
// Loop over all child objects
for (size_t index = 0; index < pData->m_SubObjects.size(); index++)
{
// Create all child nodes
pNode->mChildren[index] = createNodes(pModel, pData, pNode, pScene, MeshArray);
// Create meshes of this object
pMesh = new aiMesh();
MeshArray.push_back(pMesh);
createTopology(pModel, pData->m_SubObjects[ index ], pMesh);
// Create material of this object
createMaterial(pModel, pData->m_SubObjects[ index ], pScene);
}
}
// Set mesh instances into scene- and node-instances
const size_t meshSizeDiff = MeshArray.size()- oldMeshSize;
if (meshSizeDiff > 0 )
{
pNode->mMeshes = new unsigned int[ meshSizeDiff ];
pNode->mNumMeshes++;
size_t index = 0;
for (size_t i = oldMeshSize; i < MeshArray.size(); i++)
{
pNode->mMeshes[ index ] = pScene->mNumMeshes;
pScene->mNumMeshes++;
index++;
}
}
return pNode;
}
// ------------------------------------------------------------------------------------------------
// Create topology data
void ObjFileImporter::createTopology(const ObjFile::Model* pModel, const ObjFile::Object* pData,
aiMesh* pMesh)
{
if (NULL == pData)
return;
// Create mesh vertices
createVertexArray(pModel, pData, pMesh);
// Create faces
pMesh->mNumFaces = pData->m_Faces.size();
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
for (size_t index = 0; index < pMesh->mNumFaces; index++)
{
aiFace *pFace = &pMesh->mFaces[ index ];
pFace->mNumIndices = pData->m_Faces[index]->m_pVertices->size();
if (pFace->mNumIndices > 0)
{
pFace->mIndices = new unsigned int[pMesh->mFaces[index].mNumIndices];
ObjFile::Face::IndexArray *pArray = pData->m_Faces[index]->m_pVertices;
// TODO: Should be implement much better
//memcpy(pFace->mIndices, &pData->m_Faces[index]->m_pVertices[0], pFace->mNumIndices * sizeof(unsigned int));
if (pArray != NULL)
{
for (size_t a=0; a<pFace->mNumIndices; a++)
{
pFace->mIndices[a] = pArray->at( a );
}
}
else
{
ai_assert (false);
}
}
}
}
// ------------------------------------------------------------------------------------------------
void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
const ObjFile::Object* pCurrentObject,
aiMesh* pMesh)
{
// Break, if no faces are stored in object
if (pCurrentObject->m_Faces.empty())
return;
// Copy all stored vertices, normals and so on
pMesh->mNumVertices = pModel->m_Vertices.size();
pMesh->mVertices = new aiVector3D_t[pMesh->mNumVertices];
for (size_t index=0; index < pModel->m_Vertices.size(); index++)
{
pMesh->mVertices[ index ] = *pModel->m_Vertices[ index ];
}
if (!pModel->m_Normals.empty())
{
pMesh->mNormals = new aiVector3D_t[pModel->m_Normals.size()];
for (size_t index = 0; index < pModel->m_Normals.size(); index++)
{
pMesh->mNormals[ index ] = *pModel->m_Normals[ index ];
}
}
if (!pModel->m_TextureCoord.empty())
{
// TODO: Implement texture coodinates
}
}
// ------------------------------------------------------------------------------------------------
void ObjFileImporter::countObjects(const std::vector<ObjFile::Object*> &rObjects, int &iNumMeshes)
{
iNumMeshes = 0;
if (rObjects.empty())
return;
iNumMeshes += rObjects.size();
for (std::vector<ObjFile::Object*>::const_iterator it = rObjects.begin();
it != rObjects.end();
++it)
{
if (!(*it)->m_SubObjects.empty())
{
countObjects((*it)->m_SubObjects, iNumMeshes);
}
}
}
// ------------------------------------------------------------------------------------------------
void ObjFileImporter::createMaterial(const ObjFile::Model* pModel, const ObjFile::Object* pData,
aiScene* pScene)
{
ai_assert (NULL != pScene);
if (NULL == pData)
return;
// Create only a dumy material to enshure a running viewer
pScene->mNumMaterials = 1;
Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
// Create a new material
pScene->mMaterials = new aiMaterial*[1];
pScene->mMaterials[0] = mat;
}
// ------------------------------------------------------------------------------------------------
// Appends this node to the parent node
void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild)
{
// Checking preconditions
ai_assert (NULL != pParent);
ai_assert (NULL != pChild);
// Assign parent to child
pChild->mParent = pParent;
size_t sNumChildren = 0;
// If already children was assigned to the parent node, store them in a
std::vector<aiNode*> temp;
if (pParent->mChildren != NULL)
{
sNumChildren = pParent->mNumChildren;
ai_assert (0 != sNumChildren);
for (size_t index = 0; index < pParent->mNumChildren; index++)
{
temp.push_back(pParent->mChildren [ index ] );
}
delete [] pParent->mChildren;
}
// Copy node instances into parent node
pParent->mNumChildren++;
pParent->mChildren = new aiNode*[ pParent->mNumChildren ];
for (size_t index = 0; index < pParent->mNumChildren-1; index++)
{
pParent->mChildren[ index ] = temp [ index ];
}
pParent->mChildren[ pParent->mNumChildren-1 ] = pChild;
}
// ------------------------------------------------------------------------------------------------
} // Namespace Assimp

View File

@ -0,0 +1,78 @@
#ifndef OBJ_FILE_IMPORTER_H_INC
#define OBJ_FILE_IMPORTER_H_INC
#include "BaseImporter.h"
#include <vector>
struct aiMesh;
struct aiNode;
namespace Assimp
{
namespace ObjFile
{
struct Object;
struct Model;
}
/// \class ObjFileImporter
/// \brief IMports a waveform obj file
class ObjFileImporter :
BaseImporter
{
friend class Importer;
//! OB file extention
static const std::string OBJ_EXT;
protected:
/// \brief Default constructor
ObjFileImporter();
/// \brief Destructor
~ObjFileImporter();
public:
/// \brief Returns whether the class can handle the format of the given file.
/// \remark See BaseImporter::CanRead() for details.
bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
private:
//! \brief
void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
//! \brief
void CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene);
//! \brief
aiNode *createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pData,
aiNode *pParent, aiScene* pScene, std::vector<aiMesh*> &MeshArray);
//! \brief
void createTopology(const ObjFile::Model* pModel, const ObjFile::Object* pData,
aiMesh* pMesh);
//! \brief
void createVertexArray(const ObjFile::Model* pModel,
const ObjFile::Object* pCurrentObject, aiMesh* pMesh);
//! \brief
void countObjects(const std::vector<ObjFile::Object*> &rObjects, int &iNumMeshes);
//! \brief
void createMaterial(const ObjFile::Model* pModel, const ObjFile::Object* pData,
aiScene* pScene);
//! \brief
void appendChildToParentNode(aiNode *pParent, aiNode *pChild);
private:
std::vector<char> m_Buffer;
ObjFile::Object *m_pRootObject;
std::string m_strAbsPath;
};
} // Namespace Assimp
#endif

View File

@ -0,0 +1,56 @@
#include "ObjFileMtlImporter.h"
namespace Assimp
{
// -------------------------------------------------------------------
ObjFileMtlImporter::ObjFileMtlImporter()
{
// TODO: Inplement this
}
// -------------------------------------------------------------------
ObjFileMtlImporter::~ObjFileMtlImporter()
{
// empty
}
// -------------------------------------------------------------------
ObjFileMtlImporter::ObjFileMtlImporter(const ObjFileMtlImporter &rOther)
{
// empty
}
// -------------------------------------------------------------------
ObjFileMtlImporter &ObjFileMtlImporter::operator = (const ObjFileMtlImporter &rOther)
{
return *this;
}
// -------------------------------------------------------------------
void ObjFileMtlImporter::getColorRGBA()
{
}
// -------------------------------------------------------------------
void ObjFileMtlImporter::getIlluminationModel()
{
}
// -------------------------------------------------------------------
void ObjFileMtlImporter::getFloatValue()
{
}
// -------------------------------------------------------------------
void ObjFileMtlImporter::createMaterial()
{
}
// -------------------------------------------------------------------
void ObjFileMtlImporter::getTexture()
{
}
// -------------------------------------------------------------------
} // Namespace Assimp

View File

@ -0,0 +1,36 @@
#ifndef OBJFILEMTLIMPORTER_H_INC
#define OBJFILEMTLIMPORTER_H_INC
namespace Assimp
{
/**
* @class ObjFileMtlImporter
* @brief Loads the material description from a mtl file.
*/
class ObjFileMtlImporter
{
public:
//! \brief Default constructor
ObjFileMtlImporter();
//! \brief DEstructor
~ObjFileMtlImporter();
private:
//! \brief Copy constructor, empty.
ObjFileMtlImporter(const ObjFileMtlImporter &rOther);
//! \brief Assignment operator, returns only a reference of this instance.
ObjFileMtlImporter &operator = (const ObjFileMtlImporter &rOther);
void getColorRGBA();
void getIlluminationModel();
void getFloatValue();
void createMaterial();
void getTexture();
};
} // Namespace Assimp
#endif

View File

@ -0,0 +1,551 @@
#include "ObjFileParser.h"
#include "ObjTools.h"
#include "ObjFileData.h"
#include "DefaultIOSystem.h"
#include "IOStream.h"
#include "aiTypes.h"
#include "aiVector3D.h"
#include "aiAssert.h"
#include "fast_atof.h"
#include <iostream>
#include <vector>
#include <cassert>
namespace Assimp
{
// -------------------------------------------------------------------
ObjFileParser::ObjFileParser(std::vector<char> &Data, const std::string &strAbsPath, const std::string &strModelName) :
m_strAbsPath(strAbsPath),
m_DataIt(Data.begin()),
m_DataItEnd(Data.end()),
m_pModel(NULL),
m_uiLine(0)
{
// Create the model instance to store all the data
m_pModel = new ObjFile::Model();
m_pModel->m_ModelName = strModelName;
// Start parsing the file
parseFile();
}
// -------------------------------------------------------------------
ObjFileParser::~ObjFileParser()
{
// empty
}
// -------------------------------------------------------------------
ObjFile::Model *ObjFileParser::GetModel() const
{
return m_pModel;
}
// -------------------------------------------------------------------
void ObjFileParser::parseFile()
{
if (m_DataIt == m_DataItEnd)
return;
while (m_DataIt != m_DataItEnd)
{
switch (*m_DataIt)
{
case 'v': // Parse a vertex texture coordinate
{
++m_DataIt;
if (*m_DataIt == ' ')
{
// Read in vertex definition
getVector3(m_pModel->m_Vertices);
}
else if (*m_DataIt == 't')
{
// Read in texture coordinate (2D)
getVector2(m_pModel->m_TextureCoord);
}
else if (*m_DataIt == 'n')
{
// Read in normal vector definition
getVector3(m_pModel->m_Normals);
}
}
break;
case 'f': // Parse a face
{
getFace();
}
break;
case '#': // Parse a comment
{
getComment();
}
break;
case 'u': // Parse a material desc. setter
{
getMaterialDesc();
}
break;
case 'm': // Parse a material library
{
getMaterialLib();
}
break;
case 'g': // Parse group name
{
getGroupName();
}
break;
case 's': // Parse group number
{
getGroupNumber();
}
break;
case 'o': // Parse object name
{
getObjectName();
}
break;
default:
{
skipLine();
}
break;
}
}
}
// -------------------------------------------------------------------
// Copy the next word in a temporary buffer
void ObjFileParser::copyNextWord(char *pBuffer, size_t length)
{
size_t index = 0;
m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
while (!isSpace(*m_DataIt) && m_DataIt != m_DataItEnd)
{
pBuffer[index] = *m_DataIt;
index++;
if (index == length-1)
break;
++m_DataIt;
}
pBuffer[index] = '\0';
}
// -------------------------------------------------------------------
// Copy the next line into a temporary buffer
void ObjFileParser::copyNextLine(char *pBuffer, size_t length)
{
size_t index = 0;
while (m_DataIt != m_DataItEnd)
{
if (*m_DataIt == '\n' || *m_DataIt == '\r')
break;
assert (index+1 <= length);
pBuffer[ index ] = *m_DataIt;
++index;
++m_DataIt;
}
pBuffer[ index ] = '\0';
}
// -------------------------------------------------------------------
// Get values for a new 3D vector instance
void ObjFileParser::getVector3(std::vector<aiVector3D_t*> &point3d_array)
{
float x, y, z;
copyNextWord(m_buffer, BUFFERSIZE);
x = (float) fast_atof(m_buffer);
copyNextWord(m_buffer, BUFFERSIZE);
y = (float) fast_atof(m_buffer);
copyNextWord(m_buffer, BUFFERSIZE);
z = (float) fast_atof(m_buffer);
point3d_array.push_back(new aiVector3D(x,y,z));
skipLine();
}
// -------------------------------------------------------------------
// Get values for a new 2D vector instance
void ObjFileParser::getVector2(std::vector<aiVector2D_t*> &point2d_array)
{
float x, y;
copyNextWord(m_buffer, BUFFERSIZE);
x = (float) fast_atof(m_buffer);
copyNextWord(m_buffer, BUFFERSIZE);
y = (float) fast_atof(m_buffer);
point2d_array.push_back(new aiVector2D(x, y));
skipLine();
}
// -------------------------------------------------------------------
// Skips a line
void ObjFileParser::skipLine()
{
while (m_DataIt != m_DataItEnd && *m_DataIt != '\n')
++m_DataIt;
if (m_DataIt != m_DataItEnd)
{
++m_DataIt;
++m_uiLine;
}
}
// -------------------------------------------------------------------
// Get values for a new face instance
void ObjFileParser::getFace()
{
copyNextLine(m_buffer, BUFFERSIZE);
if (m_DataIt == m_DataItEnd)
return;
char *pPtr = m_buffer;
char *pEnd = &pPtr[BUFFERSIZE];
pPtr = getNextToken<char*>(pPtr, pEnd);
if (pPtr == '\0')
return;
std::vector<unsigned int> *pIndices = new std::vector<unsigned int>;
std::vector<unsigned int> *pTexID = new std::vector<unsigned int>;
std::vector<unsigned int> *pNormalID = new std::vector<unsigned int>;
bool vt = (!m_pModel->m_TextureCoord.empty());
bool vn = (!m_pModel->m_Normals.empty());
int iStep = 0, iPos = 0;
while (pPtr != pEnd)
{
iStep = 1;
if (*pPtr == '\0')
break;
if (*pPtr=='\r')
break;
if (*pPtr=='/' )
{
if (iPos == 0)
{
//if there are no texturecoordinates in the obj file but normals
if (!vt && vn)
iPos = 1;
}
iPos++;
}
else if (isSpace(*pPtr))
{
iPos = 0;
}
else
{
//OBJ USES 1 Base ARRAYS!!!!
const int iVal = atoi(pPtr);
int tmp = iVal;
while ((tmp = tmp / 10)!=0)
++iStep;
if (0 != iVal)
{
// Store parsed index
if (0 == iPos)
{
pIndices->push_back(iVal-1);
}
else if (1 == iPos)
{
pTexID->push_back(iVal-1);
}
else if (2 == iPos)
{
pNormalID->push_back(iVal-1);
}
else
{
reportErrorTokenInFace();
}
}
}
for (int i=0; i<iStep; i++)
++pPtr;
}
ObjFile::Face *face = new ObjFile::Face(pIndices, pNormalID, pTexID);
// Set active material, if one set
if (NULL != m_pModel->m_pCurrentMaterial)
face->m_pMaterial = m_pModel->m_pCurrentMaterial;
else
face->m_pMaterial = m_pModel->m_pDefaultMaterial;
// Create a default object, if nothing there
if (NULL == m_pModel->m_pCurrent)
createObject("defaultobject");
// Store the new instance
m_pModel->m_pCurrent->m_Faces.push_back(face);
// Skip the rest of the line
skipLine();
}
// -------------------------------------------------------------------
// Get values for a new material description
void ObjFileParser::getMaterialDesc()
{
// Get next data for material data
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
if (m_DataIt == m_DataItEnd)
return;
char *pStart = &(*m_DataIt);
while (!isSpace(*m_DataIt) && m_DataIt != m_DataItEnd)
m_DataIt++;
// Get name
std::string strName(pStart, &(*m_DataIt));
if (strName.empty())
return;
// Search for material
std::string strFile;
std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find(strName);
if (it == m_pModel->m_MaterialMap.end())
{
m_pModel->m_pCurrentMaterial = new ObjFile::Material();
m_pModel->m_MaterialMap[strName] = m_pModel->m_pCurrentMaterial;
}
skipLine();
}
// -------------------------------------------------------------------
// Get a comment, values will be skipped
void ObjFileParser::getComment()
{
while (true)
{
if ('\n' == (*m_DataIt) || m_DataIt == m_DataItEnd)
{
++m_DataIt;
break;
}
else
{
++m_DataIt;
}
}
}
// -------------------------------------------------------------------
//
void ObjFileParser::getMaterialLib()
{
// Translate tuple
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
if (m_DataIt == m_DataItEnd)
return;
char *pStart = &(*m_DataIt);
while (!isSpace(*m_DataIt))
m_DataIt++;
// Check for existence
DefaultIOSystem IOSystem;
std::string strMatName(pStart, &(*m_DataIt));
std::string absName = m_strAbsPath + IOSystem.getOsSeparator() + strMatName;
if (!IOSystem.Exists(absName))
{
skipLine();
return;
}
std::string strExt("");
extractExtension(strMatName, strExt);
std::string mat = "mtl";
DefaultIOSystem FileSystem;
IOStream *pFile = FileSystem.Open(absName);
if (0L != pFile)
{
size_t size = pFile->FileSize();
char *pBuffer = new char[size];
size_t read_size = pFile->Read(pBuffer, sizeof(char), size);
FileSystem.Close(pFile);
// TODO: Load mtl file
delete [] pBuffer;
}
// Load material library (all materials will be created)
m_pModel->m_MaterialLib.push_back(strMatName);
skipLine();
}
// -------------------------------------------------------------------
//
void ObjFileParser::getNewMaterial()
{
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
char *pStart = &(*m_DataIt);
std::string strMat(pStart, *m_DataIt);
while (isSpace(*m_DataIt))
m_DataIt++;
std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find(strMat);
if (it == m_pModel->m_MaterialMap.end())
{
// Show a warning, if material was not found
std::string strWarn ("Unsupported material requested: ");
strWarn += strMat;
std::cerr << "Warning : " << strWarn << std::endl;
m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
}
else
{
// Set new material
m_pModel->m_pCurrentMaterial = (*it).second;
}
skipLine();
}
// -------------------------------------------------------------------
//
void ObjFileParser::getGroupName()
{
// Get next word from data buffer
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
// Store groupname in group library
char *pStart = &(*m_DataIt);
while (!isSpace(*m_DataIt))
m_DataIt++;
std::string strGroupName(pStart, &(*m_DataIt));
// Change active group, if necessary
if (m_pModel->m_strActiveGroup != strGroupName)
{
// Search for already existing entry
ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(&strGroupName);
// New group name, creating a new entry
ObjFile::Object *pObject = m_pModel->m_pCurrent;
if (it == m_pModel->m_Groups.end())
{
std::vector<unsigned int> *pFaceIDArray = new std::vector<unsigned int>;
m_pModel->m_Groups[ &strGroupName ] = pFaceIDArray;
m_pModel->m_pGroupFaceIDs = (pFaceIDArray);
}
else
{
m_pModel->m_pGroupFaceIDs = (*it).second;
}
m_pModel->m_strActiveGroup = strGroupName;
}
skipLine();
}
// -------------------------------------------------------------------
// Not supported
void ObjFileParser::getGroupNumber()
{
// TODO: Implement this
skipLine();
}
// -------------------------------------------------------------------
// Stores values for a new object instance, name will be used to
// identify it.
void ObjFileParser::getObjectName()
{
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
if (m_DataIt == m_DataItEnd)
return;
char *pStart = &(*m_DataIt);
while (!isSpace(*m_DataIt))
m_DataIt++;
std::string strObjectName(pStart, &(*m_DataIt));
if (!strObjectName.empty())
{
// Reset current object
m_pModel->m_pCurrent = NULL;
// Search for actual object
for (std::vector<ObjFile::Object*>::const_iterator it = m_pModel->m_Objects.begin();
it != m_pModel->m_Objects.end();
++it)
{
if ((*it)->m_strObjName == strObjectName)
{
m_pModel->m_pCurrent = *it;
break;
}
}
// Allocate a new object, if current one wasn´t found before
if (m_pModel->m_pCurrent == NULL)
{
createObject(strObjectName);
/*m_pModel->m_pCurrent = new ObjFile::Object();
m_pModel->m_pCurrent->m_strObjName = strObjectName;
m_pModel->m_Objects.push_back(m_pModel->m_pCurrent);*/
}
}
skipLine();
}
// -------------------------------------------------------------------
// Creates a new object instance
void ObjFileParser::createObject(const std::string &strObjectName)
{
ai_assert (NULL != m_pModel);
ai_assert (!strObjectName.empty());
m_pModel->m_pCurrent = new ObjFile::Object();
m_pModel->m_pCurrent->m_strObjName = strObjectName;
m_pModel->m_Objects.push_back(m_pModel->m_pCurrent);
}
// -------------------------------------------------------------------
// Shows an error in parsing process.
void ObjFileParser::reportErrorTokenInFace()
{
std::string strErr("");
skipLine();
std::cerr << "Not supported token in face desc. detected : " << strErr << std::endl;
}
// -------------------------------------------------------------------
// Extracts the extention from a filename
void ObjFileParser::extractExtension(const std::string strFile,
std::string &strExt)
{
strExt = "";
if (strFile.empty())
return;
std::string::size_type pos = strFile.find_last_of(".");
if (pos == std::string::npos)
return;
strExt = strFile.substr(pos, strFile.size() - pos);
}
// -------------------------------------------------------------------
} // Namespace Assimp

56
code/ObjTools.h 100644
View File

@ -0,0 +1,56 @@
/** @file ObjTools.h
* @brief Some helpful templates for text parsing
*/
#ifndef OBJ_TOOLS_H_INC
#define OBJ_TOOLS_H_INC
namespace Assimp
{
/** @brief Returns true, if token is a space on any supported platform
* @param token Token to search in
* @return true, if token is a space
*/
inline bool isSpace(char token)
{
return (token == ' ' || token == '\n' || token == '\f' || token == '\r' ||
token == '\t');
}
/** @brief Returns next word separated by a space
* @param pBuffer Pointer to data buffer
* @param pEnd Pointer to end of buffer
* @return Pointer to next space
*/
template<class Char_T>
inline Char_T getNextWord(Char_T pBuffer, Char_T pEnd)
{
while (pBuffer != pEnd)
{
if (!isSpace(*pBuffer))
break;
pBuffer++;
}
return pBuffer;
}
/** @brief Returns ponter a next token
* @param pBuffer Pointer to data buffer
* @param pEnd Pointer to end of buffer
* @return Pointer to next token
*/
template<class Char_T>
inline Char_T getNextToken(Char_T pBuffer, Char_T pEnd)
{
while (pBuffer != pEnd)
{
if (isSpace(*pBuffer))
break;
pBuffer++;
}
return getNextWord(pBuffer, pEnd);
}
} // Namespace Assimp
#endif

990
code/PlyLoader.cpp 100644
View File

@ -0,0 +1,990 @@
/** @file Implementation of the PLY importer class */
#include "PLYLoader.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
PLYImporter::PLYImporter()
{
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
PLYImporter::~PLYImporter()
{
}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool PLYImporter::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);
if (extension.length() < 4)return false;
if (extension[0] != '.')return false;
if (extension[1] != 'p' && extension[1] != 'P')return false;
if (extension[2] != 'l' && extension[2] != 'L')return false;
if (extension[3] != 'y' && extension[3] != 'Y')return false;
return true;
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void PLYImporter::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 PLY file " + pFile + ".");
}
// check whether the ply file is large enough to contain
// at least the file header
size_t fileSize = file->FileSize();
if( fileSize < 10)
{
throw new ImportErrorException( ".ply File is too small.");
}
// allocate storage and copy the contents of the file to a memory buffer
// (terminate it with zero)
this->mBuffer = new unsigned char[fileSize+1];
file->Read( (void*)mBuffer, 1, fileSize);
this->mBuffer[fileSize] = '\0';
// the beginning of the file must be PLY
if (this->mBuffer[0] != 'P' && this->mBuffer[0] != 'p' ||
this->mBuffer[1] != 'L' && this->mBuffer[1] != 'l' ||
this->mBuffer[2] != 'Y' && this->mBuffer[2] != 'y')
{
throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there");
}
char* szMe = (char*)&this->mBuffer[3];
SkipSpacesAndLineEnd(szMe,(const char**)&szMe);
// determine the format of the file data
PLY::DOM sPlyDom;
if (0 == ASSIMP_strincmp(szMe,"format",6) && IsSpace(*(szMe+6)))
{
szMe += 7;
if (0 == ASSIMP_strincmp(szMe,"ascii",5) && IsSpace(*(szMe+5)))
{
szMe += 6;
SkipLine(szMe,(const char**)&szMe);
if(!PLY::DOM::ParseInstance(szMe,&sPlyDom))
{
throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#1)");
}
}
else if (0 == ASSIMP_strincmp(szMe,"binary_",7))
{
bool bIsBE = false;
// binary_little_endian
// binary_big_endian
szMe += 7;
#if (defined AI_BUILD_BIG_ENDIAN)
if ('l' == *szMe || 'L' == *szMe)bIsBE = true;
#else
if ('b' == *szMe || 'B' == *szMe)bIsBE = true;
#endif // ! AI_BUILD_BIG_ENDIAN
// skip the line, parse the rest of the header and build the DOM
SkipLine(szMe,(const char**)&szMe);
if(!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE))
{
throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#2)");
}
}
else
{
throw new ImportErrorException( "Invalid .ply file: Unknown file format");
}
}
else
{
throw new ImportErrorException( "Invalid .ply file: Missing format specification");
}
this->pcDOM = &sPlyDom;
// now load a list of vertices. This must be sucessfull in order to procede
std::vector<aiVector3D> avPositions;
this->LoadVertices(&avPositions,false);
if (avPositions.empty())
{
throw new ImportErrorException( "Invalid .ply file: No vertices found");
}
// now load a list of normals.
std::vector<aiVector3D> avNormals;
this->LoadVertices(&avNormals,true);
// load the face list
std::vector<PLY::Face> avFaces;
this->LoadFaces(&avFaces);
// if no face list is existing we assume that the vertex
// list is containing a list of triangles
if (avFaces.empty())
{
if (avPositions.size() < 3)
{
throw new ImportErrorException( "Invalid .ply file: Not enough vertices to build "
"a face list. ");
}
unsigned int iNum = avPositions.size() / 3;
for (unsigned int i = 0; i< iNum;++i)
{
PLY::Face sFace;
sFace.mIndices.push_back((iNum*3));
sFace.mIndices.push_back((iNum*3)+1);
sFace.mIndices.push_back((iNum*3)+2);
avFaces.push_back(sFace);
}
}
// now load a list of all materials
std::vector<MaterialHelper*> avMaterials;
this->LoadMaterial(&avMaterials);
// now load a list of all vertex color channels
std::vector<aiColor4D> avColors;
this->LoadVertexColor(&avColors);
// now replace the default material in all faces and validate all material indices
this->ReplaceDefaultMaterial(&avFaces,&avMaterials);
// now convert this to a list of aiMesh instances
std::vector<aiMesh*> avMeshes;
this->ConvertMeshes(&avFaces,&avPositions,&avNormals,
&avColors,&avMaterials,&avMeshes);
if (avMeshes.empty())
{
throw new ImportErrorException( "Invalid .ply file: Unable to extract mesh data ");
}
// now generate the output scene object. Fill the material list
pScene->mNumMaterials = avMaterials.size();
pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
pScene->mMaterials[i] = avMaterials[i];
// fill the mesh list
pScene->mNumMeshes = avMeshes.size();
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pScene->mMeshes[i] = avMeshes[i];
// generate a simple node structure
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes;++i)
pScene->mRootNode->mMeshes[i] = i;
// delete the file buffer
delete[] this->mBuffer;
// DOM is lying on the stack, will be deconstructed automatically
return;
}
// ------------------------------------------------------------------------------------------------
void PLYImporter::ConvertMeshes(std::vector<PLY::Face>* avFaces,
const std::vector<aiVector3D>* avPositions,
const std::vector<aiVector3D>* avNormals,
const std::vector<aiColor4D>* avColors,
const std::vector<MaterialHelper*>* avMaterials,
std::vector<aiMesh*>* avOut)
{
ai_assert(NULL != avFaces);
ai_assert(NULL != avPositions);
ai_assert(NULL != avMaterials);
// split by materials
std::vector<unsigned int>* aiSplit = new std::vector<unsigned int>[
avMaterials->size()];
unsigned int iNum = 0;
for (std::vector<PLY::Face>::const_iterator
i = avFaces->begin();
i != avFaces->end();++i,++iNum)
{
// index has already been checked
aiSplit[(*i).iMaterialIndex].push_back(iNum);
}
// now generate submeshes
for (unsigned int p = 0; p < avMaterials->size();++p)
{
if (aiSplit[p].size() != 0)
{
// allocate the mesh object
aiMesh* p_pcOut = new aiMesh();
p_pcOut->mMaterialIndex = p;
p_pcOut->mNumFaces = aiSplit[p].size();
p_pcOut->mFaces = new aiFace[aiSplit[p].size()];
// at first we need to determine the size of the output vector array
unsigned int iNum = 0;
for (unsigned int i = 0; i < aiSplit[p].size();++i)
{
iNum += (*avFaces)[aiSplit[p][i]].mIndices.size();
}
p_pcOut->mNumVertices = iNum;
p_pcOut->mVertices = new aiVector3D[iNum];
if (!avColors->empty())
p_pcOut->mColors[0] = new aiColor4D[iNum];
if (!avNormals->empty())
p_pcOut->mNormals = new aiVector3D[iNum];
// add all faces
iNum = 0;
unsigned int iVertex = 0;
for (std::vector<unsigned int>::const_iterator
i = aiSplit[p].begin();
i != aiSplit[p].end();++i,++iNum)
{
p_pcOut->mFaces[iNum].mNumIndices = (*avFaces)[*i].mIndices.size();
p_pcOut->mFaces[iNum].mIndices = new unsigned int[p_pcOut->mFaces[iNum].mNumIndices];
// build an unique set of vertices/colors for this face
// hardcode all combinations to speedup this piece of code
if (!avColors->empty())
{
if (!avNormals->empty())
{
for (unsigned int q = 0; q < p_pcOut->mFaces[iNum].mNumIndices;++q)
{
p_pcOut->mFaces[iNum].mIndices[q] = iVertex;
p_pcOut->mVertices[iVertex] = (*avPositions)[(*avFaces)[*i].mIndices[q]];
p_pcOut->mColors[0][iVertex] = (*avColors)[(*avFaces)[*i].mIndices[q]];
p_pcOut->mNormals[iVertex] = (*avNormals)[(*avFaces)[*i].mIndices[q]];
iVertex++;
}
}
else
{
for (unsigned int q = 0; q < p_pcOut->mFaces[iNum].mNumIndices;++q)
{
p_pcOut->mFaces[iNum].mIndices[q] = iVertex;
p_pcOut->mVertices[iVertex] = (*avPositions)[(*avFaces)[*i].mIndices[q]];
p_pcOut->mColors[0][iVertex] = (*avColors)[(*avFaces)[*i].mIndices[q]];
iVertex++;
}
}
}
else
{
if (!avNormals->empty())
{
for (unsigned int q = 0; q < p_pcOut->mFaces[iNum].mNumIndices;++q)
{
p_pcOut->mFaces[iNum].mIndices[q] = iVertex;
p_pcOut->mVertices[iVertex] = (*avPositions)[(*avFaces)[*i].mIndices[q]];
p_pcOut->mNormals[iVertex] = (*avNormals)[(*avFaces)[*i].mIndices[q]];
iVertex++;
}
}
else
{
for (unsigned int q = 0; q < p_pcOut->mFaces[iNum].mNumIndices;++q)
{
p_pcOut->mFaces[iNum].mIndices[q] = iVertex;
p_pcOut->mVertices[iVertex] = (*avPositions)[(*avFaces)[*i].mIndices[q]];
iVertex++;
}
}
}
}
// add the mesh to the output list
avOut->push_back(p_pcOut);
}
}
delete[] aiSplit;
return;
}
// ------------------------------------------------------------------------------------------------
void PLYImporter::ReplaceDefaultMaterial(std::vector<PLY::Face>* avFaces,
std::vector<MaterialHelper*>* avMaterials)
{
bool bNeedDefaultMat = false;
for (std::vector<PLY::Face>::iterator
i = avFaces->begin();i != avFaces->end();++i)
{
if (0xFFFFFFFF == (*i).iMaterialIndex)
{
bNeedDefaultMat = true;
(*i).iMaterialIndex = avMaterials->size();
}
else if ((*i).iMaterialIndex >= avMaterials->size() )
{
// clamp the index
(*i).iMaterialIndex = avMaterials->size()-1;
}
}
if (bNeedDefaultMat)
{
// generate a default material
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);
avMaterials->push_back(pcHelper);
}
return;
}
// ------------------------------------------------------------------------------------------------
void PLYImporter::LoadVertices(std::vector<aiVector3D>* pvOut, bool p_bNormals)
{
unsigned int aiPositions[3] = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF};
PLY::EDataType aiTypes[3];
PLY::ElementInstanceList* pcList = NULL;
unsigned int cnt = 0;
// serach in the DOM for a vertex entry
unsigned int _i = 0;
for (std::vector<PLY::Element>::const_iterator
i = this->pcDOM->alElements.begin();
i != this->pcDOM->alElements.end();++i,++_i)
{
if (PLY::EEST_Vertex == (*i).eSemantic)
{
pcList = &this->pcDOM->alElementData[_i];
// load normal vectors?
if (p_bNormals)
{
// now check whether which normal components are available
unsigned int _a = 0;
for (std::vector<PLY::Property>::const_iterator
a = (*i).alProperties.begin();
a != (*i).alProperties.end();++a,++_a)
{
if ((*a).bIsList)continue;
if (PLY::EST_XNormal == (*a).Semantic)
{
cnt++;
aiPositions[0] = _a;
aiTypes[0] = (*a).eType;
}
else if (PLY::EST_YNormal == (*a).Semantic)
{
cnt++;
aiPositions[1] = _a;
aiTypes[1] = (*a).eType;
}
else if (PLY::EST_ZNormal == (*a).Semantic)
{
cnt++;
aiPositions[2] = _a;
aiTypes[2] = (*a).eType;
}
if (3 == cnt)break;
}
}
// load vertex coordinates
else
{
// now check whether which coordinate sets are available
unsigned int _a = 0;
for (std::vector<PLY::Property>::const_iterator
a = (*i).alProperties.begin();
a != (*i).alProperties.end();++a,++_a)
{
if ((*a).bIsList)continue;
if (PLY::EST_XCoord == (*a).Semantic)
{
cnt++;
aiPositions[0] = _a;
aiTypes[0] = (*a).eType;
}
else if (PLY::EST_YCoord == (*a).Semantic)
{
cnt++;
aiPositions[1] = _a;
aiTypes[1] = (*a).eType;
}
else if (PLY::EST_ZCoord == (*a).Semantic)
{
cnt++;
aiPositions[2] = _a;
aiTypes[2] = (*a).eType;
}
if (3 == cnt)break;
}
}
break;
}
}
// check whether we have a valid source for the vertex data
if (NULL != pcList && 0 != cnt)
{
pvOut->reserve(pcList->alInstances.size());
for (std::vector<ElementInstance>::const_iterator
i = pcList->alInstances.begin();
i != pcList->alInstances.end();++i)
{
// convert the vertices to sp floats
aiVector3D vOut;
if (0xFFFFFFFF == aiPositions[0])vOut.x = 0.0f;
else
{
vOut.x = PLY::PropertyInstance::ConvertTo<float>(
(*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]);
}
if (0xFFFFFFFF == aiPositions[1])vOut.y = 0.0f;
else
{
vOut.y = PLY::PropertyInstance::ConvertTo<float>(
(*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]);
}
if (0xFFFFFFFF == aiPositions[2])vOut.z = 0.0f;
else
{
vOut.z = PLY::PropertyInstance::ConvertTo<float>(
(*i).alProperties[aiPositions[2]].avList.front(),aiTypes[2]);
}
// and add them to our nice list
pvOut->push_back(vOut);
}
}
return;
}
// ------------------------------------------------------------------------------------------------
float NormalizeColorValue (PLY::PropertyInstance::ValueUnion val,PLY::EDataType eType)
{
switch (eType)
{
case EDT_Float:
return val.fFloat;
case EDT_Double:
return (float)val.fDouble;
case EDT_UChar:
return (float)val.iUInt / (float)0xFF;
case EDT_Char:
return (float)(val.iInt+(0xFF/2)) / (float)0xFF;
case EDT_UShort:
return (float)val.iUInt / (float)0xFFFF;
case EDT_Short:
return (float)(val.iInt+(0xFFFF/2)) / (float)0xFFFF;
case EDT_UInt:
return (float)val.iUInt / (float)0xFFFF;
case EDT_Int:
return ((float)val.iInt / (float)0xFF) + 0.5f;
};
return 0.0f;
}
// ------------------------------------------------------------------------------------------------
void PLYImporter::LoadVertexColor(std::vector<aiColor4D>* pvOut)
{
unsigned int aiPositions[4] = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF};
PLY::EDataType aiTypes[4];
unsigned int cnt = 0;
PLY::ElementInstanceList* pcList = NULL;
// serach in the DOM for a vertex entry
unsigned int _i = 0;
for (std::vector<PLY::Element>::const_iterator
i = this->pcDOM->alElements.begin();
i != this->pcDOM->alElements.end();++i,++_i)
{
if (PLY::EEST_Vertex == (*i).eSemantic)
{
pcList = &this->pcDOM->alElementData[_i];
// now check whether which coordinate sets are available
unsigned int _a = 0;
for (std::vector<PLY::Property>::const_iterator
a = (*i).alProperties.begin();
a != (*i).alProperties.end();++a,++_a)
{
if ((*a).bIsList)continue;
if (PLY::EST_Red == (*a).Semantic)
{
cnt++;
aiPositions[0] = _a;
aiTypes[0] = (*a).eType;
}
else if (PLY::EST_Green == (*a).Semantic)
{
cnt++;
aiPositions[1] = _a;
aiTypes[1] = (*a).eType;
}
else if (PLY::EST_Blue == (*a).Semantic)
{
cnt++;
aiPositions[2] = _a;
aiTypes[2] = (*a).eType;
}
else if (PLY::EST_Alpha == (*a).Semantic)
{
cnt++;
aiPositions[3] = _a;
aiTypes[3] = (*a).eType;
}
if (4 == cnt)break;
}
break;
}
}
// check whether we have a valid source for the vertex data
if (NULL != pcList && 0 != cnt)
{
pvOut->reserve(pcList->alInstances.size());
for (std::vector<ElementInstance>::const_iterator
i = pcList->alInstances.begin();
i != pcList->alInstances.end();++i)
{
// convert the vertices to sp floats
aiColor4D vOut;
if (0xFFFFFFFF == aiPositions[0])vOut.r = 0.0f;
else
{
vOut.r = NormalizeColorValue((*i).alProperties[
aiPositions[0]].avList.front(),aiTypes[0]);
}
if (0xFFFFFFFF == aiPositions[1])vOut.g = 0.0f;
else
{
vOut.g = NormalizeColorValue((*i).alProperties[
aiPositions[1]].avList.front(),aiTypes[1]);
}
if (0xFFFFFFFF == aiPositions[2])vOut.b = 0.0f;
else
{
vOut.b = NormalizeColorValue((*i).alProperties[
aiPositions[2]].avList.front(),aiTypes[2]);
}
// assume 1.0 for the alpha channel ifit is not set
if (0xFFFFFFFF == aiPositions[3])vOut.a = 1.0f;
else
{
vOut.a = NormalizeColorValue((*i).alProperties[
aiPositions[3]].avList.front(),aiTypes[3]);
}
// and add them to our nice list
pvOut->push_back(vOut);
}
}
return;
}
// ------------------------------------------------------------------------------------------------
void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
{
PLY::ElementInstanceList* pcList = NULL;
bool bOne = false;
// index of the vertex index list
unsigned int iProperty = 0xFFFFFFFF;
PLY::EDataType eType;
bool bIsTristrip = false;
// index of the material index property
unsigned int iMaterialIndex = 0xFFFFFFFF;
PLY::EDataType eType2;
// serach in the DOM for a face entry
unsigned int _i = 0;
for (std::vector<PLY::Element>::const_iterator
i = this->pcDOM->alElements.begin();
i != this->pcDOM->alElements.end();++i,++_i)
{
// face = unique number of vertex indices
if (PLY::EEST_Face == (*i).eSemantic)
{
pcList = &this->pcDOM->alElementData[_i];
unsigned int _a = 0;
for (std::vector<PLY::Property>::const_iterator
a = (*i).alProperties.begin();
a != (*i).alProperties.end();++a,++_a)
{
if (PLY::EST_VertexIndex == (*a).Semantic)
{
// must be a dynamic list!
if (!(*a).bIsList)continue;
iProperty = _a;
bOne = true;
eType = (*a).eType;
}
else if (PLY::EST_MaterialIndex == (*a).Semantic)
{
if ((*a).bIsList)continue;
iMaterialIndex = _a;
bOne = true;
eType2 = (*a).eType;
}
}
break;
}
// triangle strip
// TODO: triangle strip and material index support???
else if (PLY::EEST_TriStrip == (*i).eSemantic)
{
// find a list property in this ...
pcList = &this->pcDOM->alElementData[_i];
unsigned int _a = 0;
for (std::vector<PLY::Property>::const_iterator
a = (*i).alProperties.begin();
a != (*i).alProperties.end();++a,++_a)
{
// must be a dynamic list!
if (!(*a).bIsList)continue;
iProperty = _a;
bOne = true;
bIsTristrip = true;
eType = (*a).eType;
break;
}
break;
}
}
// check whether we have at least one per-face information set
if (pcList && bOne)
{
if (!bIsTristrip)
{
pvOut->reserve(pcList->alInstances.size());
for (std::vector<ElementInstance>::const_iterator
i = pcList->alInstances.begin();
i != pcList->alInstances.end();++i)
{
PLY::Face sFace;
// parse the list of vertex indices
if (0xFFFFFFFF != iProperty)
{
const unsigned int iNum = (*i).alProperties[iProperty].avList.size();
sFace.mIndices.resize(iNum);
std::list<PLY::PropertyInstance::ValueUnion>::const_iterator p =
(*i).alProperties[iProperty].avList.begin();
for (unsigned int a = 0; a < iNum;++a,++p)
{
sFace.mIndices[a] = PLY::PropertyInstance::ConvertTo<unsigned int>(*p,eType);
}
}
// parse the material index
if (0xFFFFFFFF != iMaterialIndex)
{
sFace.iMaterialIndex = PLY::PropertyInstance::ConvertTo<unsigned int>(
(*i).alProperties[iMaterialIndex].avList.front(),eType2);
}
pvOut->push_back(sFace);
}
}
else // triangle strips
{
// normally we have only one triangle strip instance where
// a value of -1 indicates a restart of the strip
for (std::vector<ElementInstance>::const_iterator
i = pcList->alInstances.begin();
i != pcList->alInstances.end();++i)
{
int aiTable[2] = {-1,-1};
for (std::list<PLY::PropertyInstance::ValueUnion>::const_iterator
a = (*i).alProperties[iProperty].avList.begin();
a != (*i).alProperties[iProperty].avList.end();++a)
{
int p = PLY::PropertyInstance::ConvertTo<int>(*a,eType);
if (-1 == p)
{
// restart the strip ...
aiTable[0] = aiTable[1] = -1;
continue;
}
if (-1 == aiTable[0])
{
aiTable[0] = p;
continue;
}
if (-1 == aiTable[1])
{
aiTable[1] = p;
continue;
}
PLY::Face sFace;
sFace.mIndices.push_back((unsigned int)aiTable[0]);
sFace.mIndices.push_back((unsigned int)aiTable[1]);
sFace.mIndices.push_back((unsigned int)p);
pvOut->push_back(sFace);
aiTable[0] = aiTable[1];
aiTable[1] = p;
}
}
}
}
return;
}
// ------------------------------------------------------------------------------------------------
void GetMaterialColor(const std::vector<PLY::PropertyInstance>& avList,
unsigned int aiPositions[4],
PLY::EDataType aiTypes[4],
aiColor4D* clrOut)
{
ai_assert(NULL != clrOut);
if (0xFFFFFFFF == aiPositions[0])clrOut->r = 0.0f;
else
{
clrOut->r = NormalizeColorValue(avList[
aiPositions[0]].avList.front(),aiTypes[0]);
}
if (0xFFFFFFFF == aiPositions[1])clrOut->g = 0.0f;
else
{
clrOut->g = NormalizeColorValue(avList[
aiPositions[1]].avList.front(),aiTypes[1]);
}
if (0xFFFFFFFF == aiPositions[2])clrOut->b = 0.0f;
else
{
clrOut->b = NormalizeColorValue(avList[
aiPositions[2]].avList.front(),aiTypes[2]);
}
// assume 1.0 for the alpha channel ifit is not set
if (0xFFFFFFFF == aiPositions[3])clrOut->a = 1.0f;
else
{
clrOut->a = NormalizeColorValue(avList[
aiPositions[3]].avList.front(),aiTypes[3]);
}
return;
}
// ------------------------------------------------------------------------------------------------
void PLYImporter::LoadMaterial(std::vector<MaterialHelper*>* pvOut)
{
// diffuse[4], specular[4], ambient[4]
// rgba order
unsigned int aaiPositions[3][4] = {
{0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
{0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
{0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
};
// dto.
PLY::EDataType aaiTypes[3][4];
PLY::ElementInstanceList* pcList = NULL;
unsigned int iPhong = 0xFFFFFFFF;
PLY::EDataType ePhong;
unsigned int iOpacity = 0xFFFFFFFF;
PLY::EDataType eOpacity;
// serach in the DOM for a vertex entry
unsigned int _i = 0;
for (std::vector<PLY::Element>::const_iterator
i = this->pcDOM->alElements.begin();
i != this->pcDOM->alElements.end();++i,++_i)
{
if (PLY::EEST_Material == (*i).eSemantic)
{
pcList = &this->pcDOM->alElementData[_i];
// now check whether which coordinate sets are available
unsigned int _a = 0;
for (std::vector<PLY::Property>::const_iterator
a = (*i).alProperties.begin();
a != (*i).alProperties.end();++a,++_a)
{
if ((*a).bIsList)continue;
// pohng specularity -----------------------------------
if (PLY::EST_PhongPower == (*a).Semantic)
{
iPhong = _a;
ePhong = (*a).eType;
}
// general opacity -----------------------------------
if (PLY::EST_Opacity == (*a).Semantic)
{
iOpacity = _a;
eOpacity = (*a).eType;
}
// diffuse color channels -----------------------------------
if (PLY::EST_DiffuseRed == (*a).Semantic)
{
aaiPositions[0][0] = _a;
aaiTypes[0][0] = (*a).eType;
}
else if (PLY::EST_DiffuseGreen == (*a).Semantic)
{
aaiPositions[0][1] = _a;
aaiTypes[0][1] = (*a).eType;
}
else if (PLY::EST_DiffuseBlue == (*a).Semantic)
{
aaiPositions[0][2] = _a;
aaiTypes[0][2] = (*a).eType;
}
else if (PLY::EST_DiffuseAlpha == (*a).Semantic)
{
aaiPositions[0][3] = _a;
aaiTypes[0][3] = (*a).eType;
}
// specular color channels -----------------------------------
else if (PLY::EST_SpecularRed == (*a).Semantic)
{
aaiPositions[1][0] = _a;
aaiTypes[1][0] = (*a).eType;
}
else if (PLY::EST_SpecularGreen == (*a).Semantic)
{
aaiPositions[1][1] = _a;
aaiTypes[1][1] = (*a).eType;
}
else if (PLY::EST_SpecularBlue == (*a).Semantic)
{
aaiPositions[1][2] = _a;
aaiTypes[1][2] = (*a).eType;
}
else if (PLY::EST_SpecularAlpha == (*a).Semantic)
{
aaiPositions[1][3] = _a;
aaiTypes[1][3] = (*a).eType;
}
// ambient color channels -----------------------------------
else if (PLY::EST_AmbientRed == (*a).Semantic)
{
aaiPositions[2][0] = _a;
aaiTypes[2][0] = (*a).eType;
}
else if (PLY::EST_AmbientGreen == (*a).Semantic)
{
aaiPositions[2][1] = _a;
aaiTypes[2][1] = (*a).eType;
}
else if (PLY::EST_AmbientBlue == (*a).Semantic)
{
aaiPositions[22][2] = _a;
aaiTypes[2][2] = (*a).eType;
}
else if (PLY::EST_AmbientAlpha == (*a).Semantic)
{
aaiPositions[2][3] = _a;
aaiTypes[2][3] = (*a).eType;
}
}
break;
}
}
// check whether we have a valid source for the material data
if (NULL != pcList)
{
for (std::vector<ElementInstance>::const_iterator
i = pcList->alInstances.begin();
i != pcList->alInstances.end();++i)
{
aiColor4D clrOut;
MaterialHelper* pcHelper = new MaterialHelper();
// build the diffuse material color
GetMaterialColor((*i).alProperties,aaiPositions[0],aaiTypes[0],&clrOut);
pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_DIFFUSE);
// build the specular material color
GetMaterialColor((*i).alProperties,aaiPositions[1],aaiTypes[1],&clrOut);
pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_SPECULAR);
// build the ambient material color
GetMaterialColor((*i).alProperties,aaiPositions[2],aaiTypes[2],&clrOut);
pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_AMBIENT);
// handle phong power and shading mode
int iMode;
if (0xFFFFFFFF != iPhong)
{
float fSpec = PLY::PropertyInstance::ConvertTo<float>(
(*i).alProperties[iPhong].avList.front(),ePhong);
// if shininess is 0 (and the pow() calculation would therefore always
// become 1, not depending on the angle) use gouraud lighting
if (0.0f != fSpec)
{
// scale this with 15 ... hopefully this is correct
fSpec += 15;
pcHelper->AddProperty<float>(&fSpec, 1, AI_MATKEY_SHININESS);
iMode = (int)aiShadingMode_Phong;
}
else iMode = (int)aiShadingMode_Gouraud;
}
else iMode = (int)aiShadingMode_Gouraud;
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
// handle opacity
if (0xFFFFFFFF != iOpacity)
{
float fOpacity = PLY::PropertyInstance::ConvertTo<float>(
(*i).alProperties[iPhong].avList.front(),eOpacity);
pcHelper->AddProperty<float>(&fOpacity, 1, AI_MATKEY_OPACITY);
}
// add the newly created material instance to the list
pvOut->push_back(pcHelper);
}
}
return;
}

99
code/PlyLoader.h 100644
View File

@ -0,0 +1,99 @@
/** @file Definition of the .ply importer class. */
#ifndef AI_PLYLOADER_H_INCLUDED
#define AI_PLYLOADER_H_INCLUDED
#include "BaseImporter.h"
#include "../include/aiTypes.h"
struct aiNode;
#include "PlyParser.h"
namespace Assimp
{
class MaterialHelper;
using namespace PLY;
// ---------------------------------------------------------------------------
/** Used to load PLY files
*/
class PLYImporter : public BaseImporter
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
PLYImporter();
/** Destructor, private as well */
~PLYImporter();
public:
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details. */
bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
protected:
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
protected:
// -------------------------------------------------------------------
/** Extract vertices from the DOM
*/
void LoadVertices(std::vector<aiVector3D>* pvOut,bool p_bNormals = false);
// -------------------------------------------------------------------
/** Extract vertex color channels
*/
void LoadVertexColor(std::vector<aiColor4D>* pvOut);
// -------------------------------------------------------------------
/** Extract a face list from the DOM
*/
void LoadFaces(std::vector<PLY::Face>* pvOut);
// -------------------------------------------------------------------
/** Extract a material list from the DOM
*/
void LoadMaterial(std::vector<MaterialHelper*>* pvOut);
// -------------------------------------------------------------------
/** Validate material indices, replace default material identifiers
*/
void ReplaceDefaultMaterial(std::vector<PLY::Face>* avFaces,
std::vector<MaterialHelper*>* avMaterials);
// -------------------------------------------------------------------
/** Convert all meshes into our ourer representation
*/
void ConvertMeshes(std::vector<PLY::Face>* avFaces,
const std::vector<aiVector3D>* avPositions,
const std::vector<aiVector3D>* avNormals,
const std::vector<aiColor4D>* avColors,
const std::vector<MaterialHelper*>* avMaterials,
std::vector<aiMesh*>* avOut);
/** Buffer to hold the loaded file */
unsigned char* mBuffer;
/** Document object model representation extracted from the file */
PLY::DOM* pcDOM;
};
} // end of namespace Assimp
#endif // AI_3DSIMPORTER_H_INC

874
code/PlyParser.cpp 100644
View File

@ -0,0 +1,874 @@
/** @file Implementation of the PLY parser class */
#include "PLYLoader.h"
#include "MaterialSystem.h"
#include "fast_atof.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;
// ------------------------------------------------------------------------------------------------
PLY::EDataType PLY::Property::ParseDataType(const char* p_szIn,const char** p_szOut)
{
PLY::EDataType eOut = PLY::EDT_INVALID;
if (0 == ASSIMP_strincmp(p_szIn,"char",4) ||
0 == ASSIMP_strincmp(p_szIn,"int8",4))
{
p_szIn+=4;
eOut = PLY::EDT_Char;
}
else if (0 == ASSIMP_strincmp(p_szIn,"uchar",5) ||
0 == ASSIMP_strincmp(p_szIn,"uint8",5))
{
p_szIn+=5;
eOut = PLY::EDT_UChar;
}
else if (0 == ASSIMP_strincmp(p_szIn,"short",5) ||
0 == ASSIMP_strincmp(p_szIn,"int16",5))
{
p_szIn+=5;
eOut = PLY::EDT_Short;
}
else if (0 == ASSIMP_strincmp(p_szIn,"ushort",6) ||
0 == ASSIMP_strincmp(p_szIn,"uint16",6))
{
p_szIn+=6;
eOut = PLY::EDT_UShort;
}
else if (0 == ASSIMP_strincmp(p_szIn,"int32",5))
{
p_szIn+=5;
eOut = PLY::EDT_Int;
}
else if (0 == ASSIMP_strincmp(p_szIn,"uint32",6))
{
p_szIn+=6;
eOut = PLY::EDT_UInt;
}
else if (0 == ASSIMP_strincmp(p_szIn,"int",3))
{
p_szIn+=3;
eOut = PLY::EDT_Int;
}
else if (0 == ASSIMP_strincmp(p_szIn,"uint",4))
{
p_szIn+=4;
eOut = PLY::EDT_UInt;
}
else if (0 == ASSIMP_strincmp(p_szIn,"float32",7))
{
p_szIn+=7;
eOut = PLY::EDT_Float;
}
else if (0 == ASSIMP_strincmp(p_szIn,"float",5))
{
p_szIn+=5;
eOut = PLY::EDT_Float;
}
else if (0 == ASSIMP_strincmp(p_szIn,"float64",7))
{
p_szIn+=7;
eOut = PLY::EDT_Double;
}
else if (0 == ASSIMP_strincmp(p_szIn,"double64",8))
{
p_szIn+=8;
eOut = PLY::EDT_Double;
}
else if (0 == ASSIMP_strincmp(p_szIn,"double",6))
{
p_szIn+=6;
eOut = PLY::EDT_Double;
}
// either end of line or space, but no other characters allowed
if (!(IsSpace(*p_szIn) || IsLineEnd(*p_szIn)))
{
eOut = PLY::EDT_INVALID;
}
*p_szOut = p_szIn;
return eOut;
}
// ------------------------------------------------------------------------------------------------
PLY::ESemantic PLY::Property::ParseSemantic(const char* p_szIn,const char** p_szOut)
{
PLY::ESemantic eOut = PLY::EST_INVALID;
if (0 == ASSIMP_strincmp(p_szIn,"x",1))
{
p_szIn++;
eOut = PLY::EST_XCoord;
}
else if (0 == ASSIMP_strincmp(p_szIn,"y",1))
{
p_szIn++;
eOut = PLY::EST_YCoord;
}
else if (0 == ASSIMP_strincmp(p_szIn,"z",1))
{
p_szIn++;
eOut = PLY::EST_ZCoord;
}
else if (0 == ASSIMP_strincmp(p_szIn,"red",3))
{
p_szIn+=3;
eOut = PLY::EST_Red;
}
else if (0 == ASSIMP_strincmp(p_szIn,"green",4))
{
p_szIn+=5;
eOut = PLY::EST_Green;
}
else if (0 == ASSIMP_strincmp(p_szIn,"blue",4))
{
p_szIn+=4;
eOut = PLY::EST_Blue;
}
else if (0 == ASSIMP_strincmp(p_szIn,"alpha",5))
{
p_szIn+=5;
eOut = PLY::EST_Alpha;
}
else if (0 == ASSIMP_strincmp(p_szIn,"vertex_index",12))
{
p_szIn+=12;
eOut = PLY::EST_VertexIndex;
}
else if (0 == ASSIMP_strincmp(p_szIn,"vertex_indices",14))
{
p_szIn+=14;
eOut = PLY::EST_VertexIndex;
}
else if (0 == ASSIMP_strincmp(p_szIn,"material_index",14))
{
p_szIn+=14;
eOut = PLY::EST_MaterialIndex;
}
else if (0 == ASSIMP_strincmp(p_szIn,"ambient_red",11))
{
p_szIn+=11;
eOut = PLY::EST_AmbientRed;
}
else if (0 == ASSIMP_strincmp(p_szIn,"ambient_green",13))
{
p_szIn+=13;
eOut = PLY::EST_AmbientGreen;
}
else if (0 == ASSIMP_strincmp(p_szIn,"ambient_blue",12))
{
p_szIn+=12;
eOut = PLY::EST_AmbientBlue;
}
else if (0 == ASSIMP_strincmp(p_szIn,"ambient_alpha",13))
{
p_szIn+=13;
eOut = PLY::EST_AmbientAlpha;
}
else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_red",11))
{
p_szIn+=11;
eOut = PLY::EST_DiffuseRed;
}
else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_green",13))
{
p_szIn+=13;
eOut = PLY::EST_DiffuseGreen;
}
else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_blue",12))
{
p_szIn+=12;
eOut = PLY::EST_DiffuseBlue;
}
else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_alpha",13))
{
p_szIn+=13;
eOut = PLY::EST_DiffuseAlpha;
}
else if (0 == ASSIMP_strincmp(p_szIn,"specular_red",12))
{
p_szIn+=12;
eOut = PLY::EST_SpecularRed;
}
else if (0 == ASSIMP_strincmp(p_szIn,"specular_green",14))
{
p_szIn+=14;
eOut = PLY::EST_SpecularGreen;
}
else if (0 == ASSIMP_strincmp(p_szIn,"specular_blue",13))
{
p_szIn+=13;
eOut = PLY::EST_SpecularBlue;
}
else if (0 == ASSIMP_strincmp(p_szIn,"specular_alpha",14))
{
p_szIn+=14;
eOut = PLY::EST_SpecularAlpha;
}
else if (0 == ASSIMP_strincmp(p_szIn,"opacity",7))
{
p_szIn+=7;
eOut = PLY::EST_Opacity;
}
else if (0 == ASSIMP_strincmp(p_szIn,"specular_power",6))
{
p_szIn+=7;
eOut = PLY::EST_PhongPower;
}
else if (0 == ASSIMP_strincmp(p_szIn,"r",1))
{
p_szIn++;
eOut = PLY::EST_Red;
}
else if (0 == ASSIMP_strincmp(p_szIn,"g",1))
{
p_szIn++;
eOut = PLY::EST_Green;
}
else if (0 == ASSIMP_strincmp(p_szIn,"b",1))
{
p_szIn++;
eOut = PLY::EST_Blue;
}
else
{
// ... find the next space or new line
while (*p_szIn != ' ' && *p_szIn != '\t' &&
*p_szIn != '\r' && *p_szIn != '\0' && *p_szIn != '\n')p_szIn++;
}
// either end of line or space, but no other characters allowed
if (!(IsSpace(*p_szIn) || IsLineEnd(*p_szIn)))
{
eOut = PLY::EST_INVALID;
}
*p_szOut = p_szIn;
return eOut;
}
// ------------------------------------------------------------------------------------------------
bool PLY::Property::ParseProperty (const char* p_szIn, const char** p_szOut, PLY::Property* pOut)
{
// Forms supported:
// "property float x"
// "property list uchar int vertex_index"
*p_szOut = p_szIn;
// skip leading spaces
if (!SkipSpaces(p_szIn,&p_szIn))return false;
// skip the "property" string at the beginning
if (0 != ASSIMP_strincmp(p_szIn,"property",8) || !IsSpace(*(p_szIn+8)))
{
// seems not to be a valid property entry
return false;
}
// get next word
p_szIn += 9;
if (!SkipSpaces(p_szIn,&p_szIn))return false;
if (0 == ASSIMP_strincmp(p_szIn,"list",4) && IsSpace(*(p_szIn+4)))
{
pOut->bIsList = true;
// seems to be a list.
p_szIn += 5;
if(EDT_INVALID == (pOut->eFirstType = PLY::Property::ParseDataType(p_szIn, &p_szIn)))
{
// unable to parse list size data type
SkipLine(p_szIn,&p_szIn);
*p_szOut = p_szIn;
return false;
}
if (!SkipSpaces(p_szIn,&p_szIn))return false;
if(EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(p_szIn, &p_szIn)))
{
// unable to parse list data type
SkipLine(p_szIn,&p_szIn);
*p_szOut = p_szIn;
return false;
}
}
else
{
if(EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(p_szIn, &p_szIn)))
{
// unable to parse data type. Skip the property
SkipLine(p_szIn,&p_szIn);
*p_szOut = p_szIn;
return false;
}
}
if (!SkipSpaces(p_szIn,&p_szIn))return false;
const char* szCur = p_szIn;
pOut->Semantic = PLY::Property::ParseSemantic(p_szIn, &p_szIn);
if (PLY::EST_INVALID == pOut->Semantic)
{
// store the name of the semantic
uintptr_t iDiff = (uintptr_t)p_szIn - (uintptr_t)szCur;
pOut->szName = std::string(szCur,iDiff);
}
SkipSpacesAndLineEnd(p_szIn,&p_szIn);
*p_szOut = p_szIn;
return true;
}
// ------------------------------------------------------------------------------------------------
PLY::EElementSemantic PLY::Element::ParseSemantic(const char* p_szIn,const char** p_szOut)
{
PLY::EElementSemantic eOut = PLY::EEST_INVALID;
if (0 == ASSIMP_strincmp(p_szIn,"vertex",6))
{
p_szIn+=6;
eOut = PLY::EEST_Vertex;
}
else if (0 == ASSIMP_strincmp(p_szIn,"face",4))
{
p_szIn+=4;
eOut = PLY::EEST_Face;
}
#if 0
else if (0 == ASSIMP_strincmp(p_szIn,"range_grid",10))
{
p_szIn+=10;
eOut = PLY::EEST_Face;
}
#endif
else if (0 == ASSIMP_strincmp(p_szIn,"tristrips",9))
{
p_szIn+=9;
eOut = PLY::EEST_TriStrip;
}
else if (0 == ASSIMP_strincmp(p_szIn,"edge",4))
{
p_szIn+=4;
eOut = PLY::EEST_Edge;
}
else if (0 == ASSIMP_strincmp(p_szIn,"material",8))
{
p_szIn+=8;
eOut = PLY::EEST_Material;
}
// either end of line or space, but no other characters allowed
if (!(IsSpace(*p_szIn) || IsLineEnd(*p_szIn)))
{
eOut = PLY::EEST_INVALID;
}
*p_szOut = p_szIn;
return eOut;
}
// ------------------------------------------------------------------------------------------------
bool PLY::Element::ParseElement (const char* p_szIn, const char** p_szOut,
PLY::Element* pOut)
{
// Example format: "element vertex 8"
*p_szOut = p_szIn;
// skip leading spaces
if (!SkipSpaces(p_szIn,&p_szIn))return false;
// skip the "element" string at the beginning
if (0 != ASSIMP_strincmp(p_szIn,"element",7) || !IsSpace(*(p_szIn+7)))
{
// seems not to be a valid property entry
return false;
}
// get next word
p_szIn += 8;
if (!SkipSpaces(p_szIn,&p_szIn))return false;
// parse the semantic of the element
const char* szCur = p_szIn;
pOut->eSemantic = PLY::Element::ParseSemantic(p_szIn,&p_szIn);
if (PLY::EEST_INVALID == pOut->eSemantic)
{
// store the name of the semantic
uintptr_t iDiff = (uintptr_t)p_szIn - (uintptr_t)szCur;
pOut->szName = std::string(szCur,iDiff);
}
if (!SkipSpaces(p_szIn,&p_szIn))return false;
//parse the number of occurences of this element
pOut->NumOccur = strtol10(p_szIn,&p_szIn);
// go to the next line
SkipSpacesAndLineEnd(p_szIn,&p_szIn);
// now parse all properties of the element
while(true)
{
// skip all comments
PLY::DOM::SkipComments(p_szIn,&p_szIn);
Property prop;
if(!PLY::Property::ParseProperty(p_szIn,&p_szIn,&prop))break;
// add the property to the property list
pOut->alProperties.push_back(prop);
}
*p_szOut = p_szIn;
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::DOM::SkipComments (const char* p_szIn,const char** p_szOut)
{
*p_szOut = p_szIn;
// skip spaces
if (!SkipSpaces(p_szIn,&p_szIn))return false;
if (0 == ASSIMP_strincmp(p_szIn,"comment",7))
{
p_szIn += 7;
SkipLine(p_szIn,&p_szIn);
SkipComments(p_szIn,&p_szIn);
*p_szOut = p_szIn;
return true;
}
return false;
}
// ------------------------------------------------------------------------------------------------
bool PLY::DOM::ParseHeader (const char* p_szIn,const char** p_szOut)
{
// after ply and format line
*p_szOut = p_szIn;
// parse all elements
while (true)
{
// skip all comments
PLY::DOM::SkipComments(p_szIn,&p_szIn);
Element out;
if(PLY::Element::ParseElement(p_szIn,&p_szIn,&out))
{
// add the element to the list of elements
this->alElements.push_back(out);
}
else if (0 == ASSIMP_strincmp(p_szIn,"end_header",10) && IsSpaceOrNewLine(*(p_szIn+10)))
{
// we have reached the end of the header
p_szIn += 11;
break;
}
// ignore unknown header elements
}
SkipSpacesAndLineEnd(p_szIn,&p_szIn);
*p_szOut = p_szIn;
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::DOM::ParseElementInstanceLists (const char* p_szIn,const char** p_szOut)
{
this->alElementData.resize(this->alElements.size());
std::vector<PLY::Element>::const_iterator i = this->alElements.begin();
std::vector<PLY::ElementInstanceList>::iterator a = this->alElementData.begin();
// parse all element instances
for (;i != this->alElements.end();++i,++a)
{
*a = PLY::ElementInstanceList(&(*i)); // reserve enough storage
PLY::ElementInstanceList::ParseInstanceList(p_szIn,&p_szIn,&(*i),&(*a));
}
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::DOM::ParseElementInstanceListsBinary (const char* p_szIn,const char** p_szOut,bool p_bBE)
{
this->alElementData.resize(this->alElements.size());
std::vector<PLY::Element>::const_iterator i = this->alElements.begin();
std::vector<PLY::ElementInstanceList>::iterator a = this->alElementData.begin();
// parse all element instances
for (;i != this->alElements.end();++i,++a)
{
*a = PLY::ElementInstanceList(&(*i)); // reserve enough storage
PLY::ElementInstanceList::ParseInstanceListBinary(p_szIn,&p_szIn,&(*i),&(*a),p_bBE);
}
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::DOM::ParseInstanceBinary (const char* p_szIn,DOM* p_pcOut,bool p_bBE)
{
if(!p_pcOut->ParseHeader(p_szIn,&p_szIn))
{
return false;
}
if(!p_pcOut->ParseElementInstanceListsBinary(p_szIn,&p_szIn,p_bBE))
{
return false;
}
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::DOM::ParseInstance (const char* p_szIn,DOM* p_pcOut)
{
if(!p_pcOut->ParseHeader(p_szIn,&p_szIn))
{
return false;
}
if(!p_pcOut->ParseElementInstanceLists(p_szIn,&p_szIn))
{
return false;
}
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::ElementInstanceList::ParseInstanceList (const char* p_szIn,const char** p_szOut,
const PLY::Element* pcElement, PLY::ElementInstanceList* p_pcOut)
{
if (EEST_INVALID == pcElement->eSemantic)
{
// if the element has an unknown semantic we can skip all lines
// However, there could be comments
for (unsigned int i = 0; i < pcElement->NumOccur;++i)
{
PLY::DOM::SkipComments(p_szIn,&p_szIn);
SkipLine(p_szIn,&p_szIn);
}
}
else
{
// be sure to have enough storage
p_pcOut->alInstances.resize(pcElement->NumOccur);
for (unsigned int i = 0; i < pcElement->NumOccur;++i)
{
PLY::DOM::SkipComments(p_szIn,&p_szIn);
PLY::ElementInstance out;
PLY::ElementInstance::ParseInstance(p_szIn, &p_szIn,pcElement, &out);
// add it to the list
p_pcOut->alInstances[i] = out;
}
}
*p_szOut = p_szIn;
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::ElementInstanceList::ParseInstanceListBinary (const char* p_szIn,const char** p_szOut,
const PLY::Element* pcElement, PLY::ElementInstanceList* p_pcOut,bool p_bBE)
{
// we can add special handling code for unknown element semantics since
// we can't skip it as a whole block (we don't know its exact size
// due to the fact that lists could be contained in the property list
// of the unknown element)
for (unsigned int i = 0; i < pcElement->NumOccur;++i)
{
PLY::ElementInstance out;
PLY::ElementInstance::ParseInstanceBinary(p_szIn, &p_szIn,pcElement, &out, p_bBE);
// add it to the list
p_pcOut->alInstances[i] = out;
}
*p_szOut = p_szIn;
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::ElementInstance::ParseInstance (const char* p_szIn,const char** p_szOut,
const PLY::Element* pcElement, PLY::ElementInstance* p_pcOut)
{
if (!SkipSpaces(p_szIn, &p_szIn))return false;
p_pcOut->alProperties.resize(pcElement->alProperties.size());
*p_szOut = p_szIn;
std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
for (;i != p_pcOut->alProperties.end();++i,++a)
{
if(!(PLY::PropertyInstance::ParseInstance(p_szIn, &p_szIn,&(*a),&(*i))))
{
// skip the rest of the instance
SkipLine(p_szIn, &p_szIn);
PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType);
(*i).avList.push_back(v);
}
}
*p_szOut = p_szIn;
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::ElementInstance::ParseInstanceBinary (const char* p_szIn,const char** p_szOut,
const PLY::Element* pcElement, PLY::ElementInstance* p_pcOut, bool p_bBE)
{
p_pcOut->alProperties.resize(pcElement->alProperties.size());
*p_szOut = p_szIn;
std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
for (;i != p_pcOut->alProperties.end();++i,++a)
{
if(!(PLY::PropertyInstance::ParseInstance(p_szIn, &p_szIn,&(*a),&(*i))))
{
PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType);
(*i).avList.push_back(v);
}
}
*p_szOut = p_szIn;
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::PropertyInstance::ParseInstance (const char* p_szIn,const char** p_szOut,
const PLY::Property* prop, PLY::PropertyInstance* p_pcOut)
{
*p_szOut = p_szIn;
// skip spaces at the beginning
if (!SkipSpaces(p_szIn, &p_szIn))return false;
if (prop->bIsList)
{
// parse the number of elements in the list
PLY::PropertyInstance::ValueUnion v;
PLY::PropertyInstance::ParseValue(p_szIn, &p_szIn,prop->eFirstType,&v);
// convert to unsigned int
unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v,prop->eFirstType);
// parse all list elements
for (unsigned int i = 0; i < iNum;++i)
{
if (!SkipSpaces(p_szIn, &p_szIn))return false;
PLY::PropertyInstance::ParseValue(p_szIn, &p_szIn,prop->eType,&v);
p_pcOut->avList.push_back(v);
}
}
else
{
// parse the property
PLY::PropertyInstance::ValueUnion v;
PLY::PropertyInstance::ParseValue(p_szIn, &p_szIn,prop->eType,&v);
p_pcOut->avList.push_back(v);
}
SkipSpacesAndLineEnd(p_szIn, &p_szIn);
*p_szOut = p_szIn;
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::PropertyInstance::ParseInstanceBinary (const char* p_szIn,const char** p_szOut,
const PLY::Property* prop, PLY::PropertyInstance* p_pcOut,bool p_bBE)
{
*p_szOut = p_szIn;
if (prop->bIsList)
{
// parse the number of elements in the list
PLY::PropertyInstance::ValueUnion v;
PLY::PropertyInstance::ParseValueBinary(p_szIn, &p_szIn,prop->eFirstType,&v,p_bBE);
// convert to unsigned int
unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v,prop->eFirstType);
// parse all list elements
for (unsigned int i = 0; i < iNum;++i)
{
PLY::PropertyInstance::ParseValueBinary(p_szIn, &p_szIn,prop->eType,&v,p_bBE);
p_pcOut->avList.push_back(v);
}
}
else
{
// parse the property
PLY::PropertyInstance::ValueUnion v;
PLY::PropertyInstance::ParseValueBinary(p_szIn, &p_szIn,prop->eType,&v,p_bBE);
p_pcOut->avList.push_back(v);
}
*p_szOut = p_szIn;
return true;
}
// ------------------------------------------------------------------------------------------------
PLY::PropertyInstance::ValueUnion PLY::PropertyInstance::DefaultValue(
PLY::EDataType eType)
{
PLY::PropertyInstance::ValueUnion out;
switch (eType)
{
case EDT_Float:
out.fFloat = 0.0f;
return out;
case EDT_Double:
out.fDouble = 0.0;
return out;
};
out.iUInt = 0;
return out;
}
// ------------------------------------------------------------------------------------------------
bool PLY::PropertyInstance::ParseValue(const char* p_szIn,const char** p_szOut,
PLY::EDataType eType,PLY::PropertyInstance::ValueUnion* out)
{
*p_szOut = p_szIn;
switch (eType)
{
case EDT_UInt:
case EDT_UShort:
case EDT_UChar:
// simply parse in a full uint
out->iUInt = (uint32_t)strtol10(p_szIn, &p_szIn);
break;
case EDT_Int:
case EDT_Short:
case EDT_Char:
{
// simply parse in a full int
// Take care of the sign at the beginning
bool bMinus = false;
if (*p_szIn == '-')
{
p_szIn++;
bMinus = true;
}
out->iInt = (int32_t)strtol10(p_szIn, &p_szIn);
if (bMinus)out->iInt *= -1;
break;
}
case EDT_Float:
// parse a simple float
p_szIn = fast_atof_move(p_szIn,out->fFloat);
break;
case EDT_Double:
// Parse a double float. .. TODO: support this
float f;
p_szIn = fast_atof_move(p_szIn,f);
out->fDouble = (double)f;
default:
return false;
}
*p_szOut = p_szIn;
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::PropertyInstance::ParseValueBinary(const char* p_szIn,const char** p_szOut,
PLY::EDataType eType,PLY::PropertyInstance::ValueUnion* out, bool p_bBE)
{
*p_szOut = p_szIn;
switch (eType)
{
case EDT_UInt:
out->iUInt = (uint32_t)*((uint32_t*)p_szIn);
p_szIn += 4;
if (p_bBE)
{
std::swap(((unsigned char*)(&out->iUInt))[0],((unsigned char*)(&out->iUInt))[3]);
std::swap(((unsigned char*)(&out->iUInt))[1],((unsigned char*)(&out->iUInt))[2]);
}
break;
case EDT_UShort:
{
uint16_t i = *((uint16_t*)p_szIn);
if (p_bBE)
{
std::swap(((unsigned char*)(&i))[0],((unsigned char*)(&i))[1]);
}
out->iUInt = (uint32_t)i;
p_szIn += 2;
break;
}
case EDT_UChar:
{
uint8_t i = *((uint8_t*)p_szIn);
out->iUInt = (uint32_t)i;
p_szIn += 2;
break;
}
case EDT_Int:
out->iInt = *((int32_t*)p_szIn);
p_szIn += 4;
if (p_bBE)
{
std::swap(((unsigned char*)(&out->iInt))[0],((unsigned char*)(&out->iInt))[3]);
std::swap(((unsigned char*)(&out->iInt))[1],((unsigned char*)(&out->iInt))[2]);
}
break;
case EDT_Short:
{
int16_t i = *((int16_t*)p_szIn);
if (p_bBE)
{
std::swap(((unsigned char*)(&i))[0],((unsigned char*)(&i))[1]);
}
out->iInt = (int32_t)i;
p_szIn += 2;
break;
}
case EDT_Char:
out->iInt = (int32_t)*((int8_t*)p_szIn);
p_szIn += 1;
break;
case EDT_Float:
if (p_bBE)
{
union {char szArray[4]; float fValue; } _X;
_X.szArray[0] = ((unsigned char*)p_szIn)[3];
_X.szArray[1] = ((unsigned char*)p_szIn)[2];
_X.szArray[2] = ((unsigned char*)p_szIn)[1];
_X.szArray[3] = ((unsigned char*)p_szIn)[0];
out->fFloat = _X.fValue;
}
else out->fFloat = *((float*)p_szIn);
p_szIn += 4;
break;
case EDT_Double:
if (p_bBE)
{
union {char szArray[8]; double fValue; } _X;
_X.szArray[0] = ((unsigned char*)p_szIn)[7];
_X.szArray[1] = ((unsigned char*)p_szIn)[6];
_X.szArray[2] = ((unsigned char*)p_szIn)[5];
_X.szArray[3] = ((unsigned char*)p_szIn)[4];
_X.szArray[4] = ((unsigned char*)p_szIn)[3];
_X.szArray[5] = ((unsigned char*)p_szIn)[2];
_X.szArray[6] = ((unsigned char*)p_szIn)[1];
_X.szArray[7] = ((unsigned char*)p_szIn)[0];
out->fDouble = _X.fValue;
}
else out->fDouble = *((double*)p_szIn);
p_szIn += 8;
break;
default:
return false;
}
*p_szOut = p_szIn;
return true;
}

483
code/PlyParser.h 100644
View File

@ -0,0 +1,483 @@
/** @file Defines the helper data structures for importing PLY files */
#ifndef AI_PLYFILEHELPER_H_INC
#define AI_PLYFILEHELPER_H_INC
#include <string>
#include <vector>
#include <list>
#include <sstream>
#include "../include/aiTypes.h"
#include "../include/aiMesh.h"
#include "../include/aiAnim.h"
namespace Assimp
{
// http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/
// http://w3.impa.br/~lvelho/outgoing/sossai/old/ViHAP_D4.4.2_PLY_format_v1.1.pdf
// http://www.okino.com/conv/exp_ply.htm
namespace PLY
{
// ---------------------------------------------------------------------------------
/*
name type number of bytes
---------------------------------------
char character 1
uchar unsigned character 1
short short integer 2
ushort unsigned short integer 2
int integer 4
uint unsigned integer 4
float single-precision float 4
double double-precision float 8
int8
int16
uint8 ... forms are also used
*/
enum EDataType
{
EDT_Char = 0x0u,
EDT_UChar,
EDT_Short,
EDT_UShort,
EDT_Int,
EDT_UInt,
EDT_Float,
EDT_Double,
// Marks invalid entries
EDT_INVALID
};
// ---------------------------------------------------------------------------------
/** \brief Specifies semantics for PLY element properties
*
* Semantics define the usage of a property, e.g. x coordinate
*/
enum ESemantic
{
//! vertex position x coordinate
EST_XCoord = 0x0u,
//! vertex position x coordinate
EST_YCoord,
//! vertex position x coordinate
EST_ZCoord,
//! vertex normal x coordinate
EST_XNormal,
//! vertex normal y coordinate
EST_YNormal,
//! vertex normal z coordinate
EST_ZNormal,
//! vertex colors, red channel
EST_Red,
//! vertex colors, green channel
EST_Green,
//! vertex colors, blue channel
EST_Blue,
//! vertex colors, alpha channel
EST_Alpha,
//! vertex index list
EST_VertexIndex,
//! texture index
EST_TextureIndex,
//! texture coordinates (stored as element of a face)
EST_TextureCoordinates,
//! material index
EST_MaterialIndex,
//! ambient color, red channel
EST_AmbientRed,
//! ambient color, green channel
EST_AmbientGreen,
//! ambient color, blue channel
EST_AmbientBlue,
//! ambient color, alpha channel
EST_AmbientAlpha,
//! diffuse color, red channel
EST_DiffuseRed,
//! diffuse color, green channel
EST_DiffuseGreen,
//! diffuse color, blue channel
EST_DiffuseBlue,
//! diffuse color, alpha channel
EST_DiffuseAlpha,
//! specular color, red channel
EST_SpecularRed,
//! specular color, green channel
EST_SpecularGreen,
//! specular color, blue channel
EST_SpecularBlue,
//! specular color, alpha channel
EST_SpecularAlpha,
//! specular power for phong shading
EST_PhongPower,
//! opacity between 0 and 1
EST_Opacity,
//! Marks invalid entries
EST_INVALID
};
// ---------------------------------------------------------------------------------
/** \brief Specifies semantics for PLY elements
*
* Semantics define the usage of an element, e.g. vertex or material
*/
enum EElementSemantic
{
//! The element is a vertex
EEST_Vertex = 0x0u,
//! The element is a face description (index table)
EEST_Face,
//! The element is a tristrip description (index table)
EEST_TriStrip,
//! The element is an edge description (ignored)
EEST_Edge,
//! The element is a material description
EEST_Material,
//! Marks invalid entries
EEST_INVALID
};
// ---------------------------------------------------------------------------------
/** \brief Helper class for a property in a PLY file.
*
* This can e.g. be a part of the vertex declaration
*/
class Property
{
public:
//! Default constructor
Property()
: eType (EDT_Int), bIsList(false), eFirstType(EDT_UChar)
{}
//! Data type of the property
EDataType eType;
//! Semantical meaning of the property
ESemantic Semantic;
//! Of the semantic of the property could not be parsed:
//! Contains the semantic specified in the file
std::string szName;
//! Specifies whether the data type is a list where
//! the first element specifies the size of the list
bool bIsList;
EDataType eFirstType;
//! Parse a property from a string. The end of the
//! string is either '\n', '\r' or '\0'. Return valie is false
//! if the input string is NOT a valid property (E.g. does
//! not start with the "property" keyword)
static bool ParseProperty (const char* p_szIn, const char** p_szOut,
Property* pOut);
//! Parse a data type from a string
static EDataType ParseDataType(const char* p_szIn,const char** p_szOut);
//! Parse a semantic from a string
static ESemantic ParseSemantic(const char* p_szIn,const char** p_szOut);
};
// ---------------------------------------------------------------------------------
/** \brief Helper class for an element in a PLY file.
*
* This can e.g. be the vertex declaration. Elements contain a
* well-defined number of properties.
*/
class Element
{
public:
//! Default constructor
Element()
: NumOccur(0), eSemantic (EEST_INVALID)
{}
//! List of properties assigned to the element
//! std::vector to support operator[]
std::vector<Property> alProperties;
//! Semantic of the element
EElementSemantic eSemantic;
//! Of the semantic of the element could not be parsed:
//! Contains the semantic specified in the file
std::string szName;
//! How many times will the element occur?
unsigned int NumOccur;
//! Parse an element from a string.
//! The function will parse all properties contained in the
//! element, too.
static bool ParseElement (const char* p_szIn, const char** p_szOut,
Element* pOut);
//! Parse a semantic from a string
static EElementSemantic ParseSemantic(const char* p_szIn,
const char** p_szOut);
};
// ---------------------------------------------------------------------------------
/** \brief Instance of a property in a PLY file
*/
class PropertyInstance
{
public:
//! Default constructor
PropertyInstance ()
{}
union ValueUnion
{
//! uInt32 representation of the property. All
// uint types are automatically converted to uint32
uint32_t iUInt;
//! Int32 representation of the property. All
// int types are automatically converted to int32
int32_t iInt;
//! Float32 representation of the property
float fFloat;
//! Float64 representation of the property
double fDouble;
};
//! List of all values parsed. Contains only one value
// for non-list propertys
std::list<ValueUnion> avList;
//! Parse a property instance
static bool ParseInstance (const char* p_szIn,const char** p_szOut,
const Property* prop, PropertyInstance* p_pcOut);
//! Parse a property instance in binary format
static bool ParseInstanceBinary (const char* p_szIn,const char** p_szOut,
const Property* prop, PropertyInstance* p_pcOut,bool p_bBE);
//! Get the default value for a given data type
static ValueUnion DefaultValue(EDataType eType);
//! Parse a value
static bool ParseValue(const char* p_szIn,const char** p_szOut,
EDataType eType,ValueUnion* out);
//! Parse a binary value
static bool ParseValueBinary(const char* p_szIn,const char** p_szOut,
EDataType eType,ValueUnion* out,bool p_bBE);
//! Convert a property value to a given type TYPE
template <typename TYPE>
static TYPE ConvertTo(ValueUnion v, EDataType eType);
};
// ---------------------------------------------------------------------------------
/** \brief Class for an element instance in a PLY file
*/
class ElementInstance
{
public:
//! Default constructor
ElementInstance ()
{}
//! List of all parsed properties
std::vector< PropertyInstance > alProperties;
//! Parse an element instance
static bool ParseInstance (const char* p_szIn,const char** p_szOut,
const Element* pcElement, ElementInstance* p_pcOut);
//! Parse a binary element instance
static bool ParseInstanceBinary (const char* p_szIn,const char** p_szOut,
const Element* pcElement, ElementInstance* p_pcOut,bool p_bBE);
};
// ---------------------------------------------------------------------------------
/** \brief Class for an element instance list in a PLY file
*/
class ElementInstanceList
{
public:
//! Default constructor
ElementInstanceList ()
{}
ElementInstanceList (const Element* pc)
{
alInstances.reserve(pc->NumOccur);
}
//! List of all element instances
std::vector< ElementInstance > alInstances;
//! Parse an element instance list
static bool ParseInstanceList (const char* p_szIn,const char** p_szOut,
const Element* pcElement, ElementInstanceList* p_pcOut);
//! Parse a binary element instance list
static bool ParseInstanceListBinary (const char* p_szIn,const char** p_szOut,
const Element* pcElement, ElementInstanceList* p_pcOut,bool p_bBE);
};
// ---------------------------------------------------------------------------------
/** \brief Class to represent the document object model of an ASCII or binary
* (both little and big-endian) PLY file
*/
class DOM
{
public:
//! Default constructor
DOM()
{}
std::vector<Element> alElements;
std::vector<ElementInstanceList> alElementData;
//! Parse the DOM for a PLY file. The input string is assumed
//! to be terminated with zero
static bool ParseInstance (const char* p_szIn,DOM* p_pcOut);
static bool ParseInstanceBinary (const char* p_szIn,
DOM* p_pcOut,bool p_bBE);
//! Skip all comment lines after this
static bool SkipComments (const char* p_szIn,const char** p_szOut);
private:
//! Handle the file header and read all element descriptions
bool ParseHeader (const char* p_szIn,const char** p_szOut);
//! Read in all element instance lists
bool ParseElementInstanceLists (const char* p_szIn,const char** p_szOut);
//! Read in all element instance lists for a binary file format
bool ParseElementInstanceListsBinary (const char* p_szIn,
const char** p_szOut,bool p_bBE);
};
// ---------------------------------------------------------------------------------
/** \brief Helper class to represent a loaded face
*/
class Face
{
public:
Face()
: iMaterialIndex(0xFFFFFFFF)
{
// set all indices to zero by default
mIndices.resize(3,0);
}
public:
//! List of vertex indices
std::vector<unsigned int> mIndices;
//! Material index
unsigned int iMaterialIndex;
};
// ---------------------------------------------------------------------------------
template <typename TYPE>
TYPE PLY::PropertyInstance::ConvertTo(
PLY::PropertyInstance::ValueUnion v, PLY::EDataType eType)
{
switch (eType)
{
case EDT_Float:
return (TYPE)v.fFloat;
case EDT_Double:
return (TYPE)v.fDouble;
case EDT_UInt:
case EDT_UShort:
case EDT_UChar:
return (TYPE)v.iUInt;
case EDT_Int:
case EDT_Short:
case EDT_Char:
return (TYPE)v.iInt;
};
return (TYPE)0;
}
// ---------------------------------------------------------------------------------
inline bool IsSpace( const char in)
{
return (in == ' ' || in == '\t');
}
// ---------------------------------------------------------------------------------
inline bool IsLineEnd( const char in)
{
return (in == '\r' || in == '\n' || in == '\0');
}
// ---------------------------------------------------------------------------------
inline bool IsSpaceOrNewLine( const char in)
{
return IsSpace(in) || IsLineEnd(in);
}
// ---------------------------------------------------------------------------------
inline bool SkipSpaces( const char* in, const char** out)
{
while (*in == ' ' || *in == '\t')in++;
*out = in;
return !IsLineEnd(*in);
}
// ---------------------------------------------------------------------------------
inline bool SkipLine( const char* in, const char** out)
{
while (*in != '\r' && *in != '\n' && *in != '\0')in++;
if (*in == '\0')
{
*out = in;
return false;
}
in++;
*out = in;
return true;
}
// ---------------------------------------------------------------------------------
inline void SkipSpacesAndLineEnd( const char* in, const char** out)
{
while (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n')in++;
*out = in;
}
};
};
#endif // !! include guard

View File

@ -0,0 +1,93 @@
/** @file Implementation of the helper class to quickly find vertices close to a given position */
#include <algorithm>
#include "SpatialSort.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructs a spatially sorted representation from the given position array.
SpatialSort::SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions, unsigned int pElementOffset)
{
// define the reference plane. We choose some arbitrary vector away from all basic axises
// in the hope that no model spreads all its vertices along this plane.
mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
mPlaneNormal.Normalize();
// store references to all given positions along with their distance to the reference plane
mPositions.reserve( pNumPositions);
for( unsigned int a = 0; a < pNumPositions; a++)
{
const char* tempPointer = reinterpret_cast<const char*> (pPositions);
const aiVector3D* vec = reinterpret_cast<const aiVector3D*> (tempPointer + a * pElementOffset);
// store position by index and distance
float distance = *vec * mPlaneNormal;
mPositions.push_back( Entry( a, *vec, distance));
}
// now sort the array ascending by distance.
std::sort( mPositions.begin(), mPositions.end());
}
// ------------------------------------------------------------------------------------------------
// Destructor
SpatialSort::~SpatialSort()
{
// nothing to do here, everything destructs automatically
}
// ------------------------------------------------------------------------------------------------
// Returns an iterator for all positions close to the given position.
void SpatialSort::FindPositions( const aiVector3D& pPosition, float pRadius, std::vector<unsigned int>& poResults) const
{
float dist = pPosition * mPlaneNormal;
float minDist = dist - pRadius, maxDist = dist + pRadius;
// clear the array in this strange fashion because a simple clear() would also deallocate
// the array which we want to avoid
poResults.erase( poResults.begin(), poResults.end());
// quick check for positions outside the range
if( mPositions.size() == 0)
return;
if( maxDist < mPositions.front().mDistance)
return;
if( minDist > mPositions.back().mDistance)
return;
// do a binary search for the minimal distance to start the iteration there
unsigned int index = mPositions.size() / 2;
unsigned int binaryStepSize = mPositions.size() / 4;
while( binaryStepSize > 1)
{
if( mPositions[index].mDistance < minDist)
index += binaryStepSize;
else
index -= binaryStepSize;
binaryStepSize /= 2;
}
// depending on the direction of the last step we need to single step a bit back or forth
// to find the actual beginning element of the range
while( index > 0 && mPositions[index].mDistance > minDist)
index--;
while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
index++;
// Mow start iterating from there until the first position lays outside of the distance range.
// Add all positions inside the distance range within the given radius to the result aray
std::vector<Entry>::const_iterator it = mPositions.begin() + index;
float squareEpsilon = pRadius * pRadius;
while( it->mDistance < maxDist)
{
if( (it->mPosition - pPosition).SquareLength() < squareEpsilon)
poResults.push_back( it->mIndex);
++it;
if( it == mPositions.end())
break;
}
// that's it
}

72
code/SpatialSort.h 100644
View File

@ -0,0 +1,72 @@
/** Small helper classes to optimise finding vertizes close to a given location */
#ifndef AI_SPATIALSORT_H_INC
#define AI_SPATIALSORT_H_INC
#include <vector>
#include "../include/aiVector3D.h"
namespace Assimp
{
// ------------------------------------------------------------------------------------------------
/** A little helper class to quickly find all vertices in the epsilon environment of a given
* position. Construct an instance with an array of positions. The class stores the given positions
* by their indices and sorts them by their distance to an arbitrary chosen plane.
* You can then query the instance for all vertices close to a given position in an average O(log n)
* time, with O(n) worst case complexity when all vertices lay on the plane. The plane is chosen
* so that it avoids common planes in usual data sets.
*/
class SpatialSort
{
public:
SpatialSort() {/* This is unintialized. This is evil. This is OK. */}
/** Constructs a spatially sorted representation from the given position array.
* Supply the positions in its layout in memory, the class will only refer to them
* by index.
* @param pPositions Pointer to the first position vector of the array.
* @param pNumPositions Number of vectors to expect in that array.
* @param pElementOffset Offset in bytes from the beginning of one vector in memory to the beginning of the next vector.
*/
SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions, unsigned int pElementOffset);
/** Destructor */
~SpatialSort();
/** Returns an iterator for all positions close to the given position.
* @param pPosition The position to look for vertices.
* @param pRadius Maximal distance from the position a vertex may have to be counted in.
* @param poResults The container to store the indices of the found positions. Will be emptied
* by the call so it may contain anything.
* @return An iterator to iterate over all vertices in the given area.
*/
void FindPositions( const aiVector3D& pPosition, float pRadius, std::vector<unsigned int>& poResults) const;
protected:
/** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */
aiVector3D mPlaneNormal;
/** An entry in a spatially sorted position array. Consists of a vertex index,
* its position and its precalculated distance from the reference plane */
struct Entry
{
unsigned int mIndex; ///< The vertex referred by this entry
aiVector3D mPosition; ///< Position
float mDistance; ///< Distance of this vertex to the sorting plane
Entry() { /** intentionally not initialized.*/ }
Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance)
: mPosition( pPosition), mIndex( pIndex), mDistance( pDistance)
{ }
bool operator < (const Entry& e) const { return mDistance < e.mDistance; }
};
// all positions, sorted by distance to the sorting plane
std::vector<Entry> mPositions;
};
} // end of namespace Assimp
#endif // AI_SPATIALSORT_H_INC

View File

@ -0,0 +1,227 @@
/** @file Implementation of the SplitLargeMeshes postprocessing step
*/
#include "SplitLargeMeshes.h"
#include "../include/aiPostProcess.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
using namespace Assimp;
// Constructor to be privately used by Importer
SplitLargeMeshesProcess::SplitLargeMeshesProcess()
{
}
// Destructor, private as well
SplitLargeMeshesProcess::~SplitLargeMeshesProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool SplitLargeMeshesProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_SplitLargeMeshes) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void SplitLargeMeshesProcess::Execute( aiScene* pScene)
{
std::vector<std::pair<aiMesh*, unsigned int> > avList;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
this->SplitMesh(a, pScene->mMeshes[a],avList);
if (avList.size() != pScene->mNumMeshes)
{
// it seems something has been splitted. rebuild the mesh list
delete[] pScene->mMeshes;
pScene->mNumMeshes = avList.size();
pScene->mMeshes = new aiMesh*[avList.size()];
for (unsigned int i = 0; i < avList.size();++i)
pScene->mMeshes[i] = avList[i].first;
// now we need to update all nodes
this->UpdateNode(pScene->mRootNode,avList);
}
return;
}
// ------------------------------------------------------------------------------------------------
// Update a node after some meshes have been split
void SplitLargeMeshesProcess::UpdateNode(aiNode* pcNode,
const std::vector<std::pair<aiMesh*, unsigned int> >& avList)
{
// for every index in out list build a new entry
// TODO: Currently O(n^2)
std::vector<unsigned int> aiEntries;
aiEntries.reserve(pcNode->mNumMeshes + 1);
for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
{
for (unsigned int a = 0; a < avList.size();++a)
{
if (avList[a].second == pcNode->mMeshes[i])
{
aiEntries.push_back(a);
}
}
}
// now build the new list
delete pcNode->mMeshes;
pcNode->mNumMeshes = aiEntries.size();
pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
for (unsigned int b = 0; b < pcNode->mNumMeshes;++b)
pcNode->mMeshes[b] = aiEntries[b];
// recusively update all other nodes
for (unsigned int i = 0; i < pcNode->mNumChildren;++i)
{
this->UpdateNode ( pcNode->mChildren[i], avList );
}
return;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void SplitLargeMeshesProcess::SplitMesh(
unsigned int a,
aiMesh* pMesh,
std::vector<std::pair<aiMesh*, unsigned int> >& avList)
{
// TODO: Mesh splitting is currently not supported for meshes
// containing bones
if (pMesh->mNumVertices > AI_SLM_MAX_VERTICES && 0 == pMesh->mNumBones)
{
// we need to split this mesh into sub meshes
// determine the size of a submesh
const unsigned int iSubMeshes = (pMesh->mNumVertices / AI_SLM_MAX_VERTICES) + 1;
const unsigned int iOutFaceNum = pMesh->mNumFaces / iSubMeshes;
const unsigned int iOutVertexNum = iOutFaceNum * 3;
// now generate all submeshes
for (unsigned int i = 0; i < iSubMeshes;++i)
{
aiMesh* pcMesh = new aiMesh;
pcMesh->mNumFaces = iOutFaceNum;
pcMesh->mMaterialIndex = pMesh->mMaterialIndex;
if (i == iSubMeshes-1)
{
pcMesh->mNumFaces = iOutFaceNum + (
pMesh->mNumFaces - iOutFaceNum * iSubMeshes);
}
// copy the list of faces
pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
const unsigned int iBase = iOutFaceNum * i;
// get the total number of indices
unsigned int iCnt = 0;
for (unsigned int p = iBase; p < pcMesh->mNumFaces + iBase;++p)
{
iCnt += pMesh->mFaces[p].mNumIndices;
}
pcMesh->mNumVertices = iCnt;
// allocate storage
if (pMesh->mVertices != NULL)
pcMesh->mVertices = new aiVector3D[iCnt];
if (pMesh->HasNormals())
pcMesh->mNormals = new aiVector3D[iCnt];
if (pMesh->HasTangentsAndBitangents())
{
pcMesh->mTangents = new aiVector3D[iCnt];
pcMesh->mBitangents = new aiVector3D[iCnt];
}
// texture coordinates
for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
{
pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c];
if (pMesh->HasTextureCoords( c))
{
pcMesh->mTextureCoords[c] = new aiVector3D[iCnt];
}
}
// vertex colors
for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c)
{
if (pMesh->HasVertexColors( c))
{
pcMesh->mColors[c] = new aiColor4D[iCnt];
}
}
// (we will also need to copy the array of indices)
for (unsigned int p = 0; p < pcMesh->mNumFaces;++p)
{
pcMesh->mFaces[p].mNumIndices = 3;
// allocate a new array
unsigned int* pi = pMesh->mFaces[p + iBase].mIndices;
pcMesh->mFaces[p].mIndices = new unsigned int[3];
// and copy the contents of the old array, offset by current base
for (unsigned int v = 0; v < 3;++v)
{
unsigned int iIndex = pMesh->mFaces[p+iBase].mIndices[v];
unsigned int iIndexOut = p*3 + v;
pcMesh->mFaces[p].mIndices[v] = iIndexOut;
// copy positions
if (pMesh->mVertices != NULL)
{
pcMesh->mVertices[iIndexOut] = pMesh->mVertices[iIndex];
}
// copy normals
if (pMesh->HasNormals())
{
pcMesh->mNormals[iIndexOut] = pMesh->mNormals[iIndex];
}
// copy tangents/bitangents
if (pMesh->HasTangentsAndBitangents())
{
pcMesh->mTangents[iIndexOut] = pMesh->mTangents[iIndex];
pcMesh->mBitangents[iIndexOut] = pMesh->mBitangents[iIndex];
}
// texture coordinates
for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
{
if (pMesh->HasTextureCoords( c))
{
pcMesh->mTextureCoords[c][iIndexOut] = pMesh->mTextureCoords[c][iIndex];
}
}
// vertex colors
for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c)
{
if (pMesh->HasVertexColors( c))
{
pcMesh->mColors[c][iIndexOut] = pMesh->mColors[c][iIndex];
}
}
}
}
// add the newly created mesh to the list
avList.push_back(std::pair<aiMesh*, unsigned int>(pcMesh,a));
}
// now delete the old mesh data
delete pMesh;
}
else avList.push_back(std::pair<aiMesh*, unsigned int>(pMesh,a));
return;
}

View File

@ -0,0 +1,60 @@
/** @file Defines a post processing step to split large meshes into submeshes*/
#ifndef AI_SPLITLARGEMESHES_H_INC
#define AI_SPLITLARGEMESHES_H_INC
#include <vector>
#include "BaseProcess.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
namespace Assimp
{
#define AI_SLM_MAX_VERTICES 1000000
// ---------------------------------------------------------------------------
/** Postprocessing filter to split large meshes into submeshes
*/
class SplitLargeMeshesProcess : public BaseProcess
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
SplitLargeMeshesProcess();
/** Destructor, private as well */
~SplitLargeMeshesProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
private:
//! Apply the algorithm to a given mesh
void SplitMesh (unsigned int a, aiMesh* pcMesh,
std::vector<std::pair<aiMesh*, unsigned int> >& avList);
//! Update a node in the asset after a few of its meshes
//! have been split
void UpdateNode(aiNode* pcNode,
const std::vector<std::pair<aiMesh*, unsigned int> >& avList);
};
} // end of namespace Assimp
#endif // !!AI_SPLITLARGEMESHES_H_INC

View File

@ -0,0 +1,83 @@
/** @file Implementation of the post processing step to split up
* all faces with more than three indices into triangles.
*/
#include <vector>
#include <assert.h>
#include "TriangulateProcess.h"
#include "../include/aiPostProcess.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
TriangulateProcess::TriangulateProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
TriangulateProcess::~TriangulateProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool TriangulateProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_Triangulate) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void TriangulateProcess::Execute( aiScene* pScene)
{
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
TriangulateMesh( pScene->mMeshes[a]);
}
// ------------------------------------------------------------------------------------------------
// Triangulates the given mesh.
void TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
{
std::vector<aiFace> newFaces;
newFaces.reserve( pMesh->mNumFaces*2);
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
const aiFace& face = pMesh->mFaces[a];
// if it's a simple primitive, just copy it
if( face.mNumIndices == 3)
{
newFaces.push_back( aiFace());
aiFace& nface = newFaces.back();
nface.mNumIndices = face.mNumIndices;
nface.mIndices = new unsigned int[nface.mNumIndices];
memcpy( nface.mIndices, face.mIndices, nface.mNumIndices * sizeof( unsigned int));
} else
{
assert( face.mNumIndices > 3);
for( unsigned int b = 0; b < face.mNumIndices - 2; b++)
{
newFaces.push_back( aiFace());
aiFace& nface = newFaces.back();
nface.mNumIndices = 3;
nface.mIndices = new unsigned int[3];
nface.mIndices[0] = face.mIndices[0];
nface.mIndices[1] = face.mIndices[b+1];
nface.mIndices[2] = face.mIndices[b+2];
}
}
}
// kill the old faces
delete [] pMesh->mFaces;
// and insert our newly generated faces
pMesh->mNumFaces = newFaces.size();
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
for( unsigned int a = 0; a < newFaces.size(); a++)
pMesh->mFaces[a] = newFaces[a];
}

View File

@ -0,0 +1,54 @@
/** @file Defines a post processing step to triangulate all faces with more than three vertices.*/
#ifndef AI_TRIANGULATEPROCESS_H_INC
#define AI_TRIANGULATEPROCESS_H_INC
#include "BaseProcess.h"
struct aiMesh;
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The TriangulateProcess splits up all faces with more than three indices
* into triangles. You usually want this to happen because the graphics cards
* need their data as triangles.
*/
class TriangulateProcess : public BaseProcess
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
TriangulateProcess();
/** Destructor, private as well */
~TriangulateProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
protected:
// -------------------------------------------------------------------
/** Triangulates the given mesh.
* @param pMesh The mesh to triangulate.
*/
void TriangulateMesh( aiMesh* pMesh);
};
} // end of namespace Assimp
#endif // AI_TRIANGULATEPROCESS_H_INC

147
code/XFileHelper.h 100644
View File

@ -0,0 +1,147 @@
/** @file Defines the helper data structures for importing XFiles */
#ifndef AI_XFILEHELPER_H_INC
#define AI_XFILEHELPER_H_INC
#include <string>
#include <vector>
#include "../include/aiTypes.h"
#include "../include/aiQuaternion.h"
#include "../include/aiMesh.h"
#include "../include/aiAnim.h"
namespace Assimp
{
namespace XFile
{
/** Helper structure representing a XFile mesh face */
struct Face
{
std::vector<unsigned int> mIndices;
};
/** Helper structure representing a XFile material */
struct Material
{
std::string mName;
bool mIsReference; // if true, mName holds a name by which the actual material can be found in the material list
aiColor4D mDiffuse;
float mSpecularExponent;
aiColor3D mSpecular;
aiColor3D mEmissive;
std::vector<std::string> mTextures;
Material() { mIsReference = false; }
};
/** Helper structure to represent a bone weight */
struct BoneWeight
{
unsigned int mVertex;
float mWeight;
};
/** Helper structure to represent a bone in a mesh */
struct Bone
{
std::string mName;
std::vector<BoneWeight> mWeights;
aiMatrix4x4 mOffsetMatrix;
};
/** Helper structure to represent an XFile mesh */
struct Mesh
{
std::vector<aiVector3D> mPositions;
std::vector<Face> mPosFaces;
std::vector<aiVector3D> mNormals;
std::vector<Face> mNormFaces;
unsigned int mNumTextures;
std::vector<aiVector2D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
unsigned int mNumColorSets;
std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
std::vector<unsigned int> mFaceMaterials;
std::vector<Material> mMaterials;
std::vector<Bone> mBones;
Mesh() { mNumTextures = 0; mNumColorSets = 0; }
};
/** Helper structure to represent a XFile frame */
struct Node
{
std::string mName;
aiMatrix4x4 mTrafoMatrix;
Node* mParent;
std::vector<Node*> mChildren;
std::vector<Mesh*> mMeshes;
Node() { mParent = NULL; }
Node( Node* pParent) { mParent = pParent; }
~Node()
{
for( unsigned int a = 0; a < mChildren.size(); a++)
delete mChildren[a];
for( unsigned int a = 0; a < mMeshes.size(); a++)
delete mMeshes[a];
}
};
struct MatrixKey
{
double mTime;
aiMatrix4x4 mMatrix;
};
/** Helper structure representing a single animated bone in a XFile */
struct AnimBone
{
std::string mBoneName;
std::vector<aiVectorKey> mPosKeys; // either three separate key sequences for position, rotation, scaling
std::vector<aiQuatKey> mRotKeys;
std::vector<aiVectorKey> mScaleKeys;
std::vector<MatrixKey> mTrafoKeys; // or a combined key sequence of transformation matrices.
};
/** Helper structure to represent an animation set in a XFile */
struct Animation
{
std::string mName;
std::vector<AnimBone*> mAnims;
~Animation()
{
for( unsigned int a = 0; a < mAnims.size(); a++)
delete mAnims[a];
}
};
/** Helper structure analogue to aiScene */
struct Scene
{
Node* mRootNode;
std::vector<Mesh*> mGlobalMeshes; // global meshes found outside of any frames
std::vector<Material> mGlobalMaterials; // global materials found outside of any meshes.
std::vector<Animation*> mAnims;
unsigned int mAnimTicksPerSecond;
Scene() { mRootNode = NULL; mAnimTicksPerSecond = 0; }
~Scene()
{
delete mRootNode;
for( unsigned int a = 0; a < mGlobalMeshes.size(); a++)
delete mGlobalMeshes[a];
for( unsigned int a = 0; a < mAnims.size(); a++)
delete mAnims[a];
}
};
} // end of namespace XFile
} // end of namespace Assimp
#endif // AI_XFILEHELPER_H_INC

View File

@ -0,0 +1,703 @@
/** @file Implementation of the XFile importer class */
#include "XFileImporter.h"
#include "XFileParser.h"
#include "MaterialSystem.h"
#include "ConvertToLHProcess.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
#include <boost/scoped_ptr.hpp>
#include <boost/format.hpp>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
XFileImporter::XFileImporter()
{
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
XFileImporter::~XFileImporter()
{
}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool XFileImporter::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);
if( extension == ".x" || extension == ".X")
return true;
return false;
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{
// read file into memory
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
if( file.get() == NULL)
throw new ImportErrorException( "Failed to open file " + pFile + ".");
size_t fileSize = file->FileSize();
if( fileSize < 16)
throw new ImportErrorException( "XFile is too small.");
mBuffer.resize( fileSize);
file->Read( &mBuffer.front(), 1, fileSize);
// parse the file into a temporary representation
XFileParser parser( mBuffer);
// and create the proper return structures out of it
CreateDataRepresentationFromImport( pScene, parser.GetImportedData());
}
// ------------------------------------------------------------------------------------------------
// Constructs the return data structure out of the imported data.
void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, const XFile::Scene* pData)
{
// Read the global materials first so that meshes referring to them can find them later
ConvertMaterials( pScene, pData->mGlobalMaterials);
// copy nodes, extracting meshes and materials on the way
pScene->mRootNode = CreateNodes( pScene, NULL, pData->mRootNode);
// extract animations
CreateAnimations( pScene, pData);
// read the global meshes that were stored outside of any node
if( pData->mGlobalMeshes.size() > 0)
{
// create a root node to hold them if there isn't any, yet
if( pScene->mRootNode == NULL)
{
pScene->mRootNode = new aiNode;
pScene->mRootNode->mName.Set( "$dummy_node");
}
// convert all global meshes and store them in the root node.
// If there was one before, the global meshes now suddenly have its transformation matrix...
// Don't know what to do there, I don't want to insert another node under the present root node
// just to avoid this.
CreateMeshes( pScene, pScene->mRootNode, pData->mGlobalMeshes);
}
// convert the root node's transformation to OGL coords
ConvertToLHProcess::ConvertToOGL( pScene->mRootNode->mTransformation);
// finally: create a dummy material if not material was imported
if( pScene->mNumMaterials == 0)
{
pScene->mNumMaterials = 1;
// create the Material
Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
int shadeMode = (int) aiShadingMode_Gouraud;
mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
// material colours
int specExp = 1;
mat->AddProperty( &aiColor3D( 0, 0, 0), 1, AI_MATKEY_COLOR_EMISSIVE);
mat->AddProperty( &aiColor3D( 0.5f, 0.5f, 0.5f), 1, AI_MATKEY_COLOR_DIFFUSE);
mat->AddProperty( &aiColor3D( 0, 0, 0), 1, AI_MATKEY_COLOR_SPECULAR);
mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS);
pScene->mMaterials = new aiMaterial*[1];
pScene->mMaterials[0] = mat;
}
}
// ------------------------------------------------------------------------------------------------
// Recursively creates scene nodes from the imported hierarchy.
aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode)
{
if( !pNode)
return NULL;
// create node
aiNode* node = new aiNode;
node->mName.length = pNode->mName.length();
node->mParent = pParent;
memcpy( node->mName.data, pNode->mName.c_str(), pNode->mName.length());
node->mName.data[node->mName.length] = 0;
node->mTransformation = pNode->mTrafoMatrix;
// convert meshes from the source node
CreateMeshes( pScene, node, pNode->mMeshes);
// handle childs
if( pNode->mChildren.size() > 0)
{
node->mNumChildren = pNode->mChildren.size();
node->mChildren = new aiNode* [node->mNumChildren];
for( unsigned int a = 0; a < pNode->mChildren.size(); a++)
node->mChildren[a] = CreateNodes( pScene, node, pNode->mChildren[a]);
}
return node;
}
// ------------------------------------------------------------------------------------------------
// Creates the meshes for the given node.
void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector<XFile::Mesh*>& pMeshes)
{
if( pMeshes.size() == 0)
return;
// create a mesh for each mesh-material combination in the source node
std::vector<aiMesh*> meshes;
for( unsigned int a = 0; a < pMeshes.size(); a++)
{
const XFile::Mesh* sourceMesh = pMeshes[a];
// first convert its materials so that we can find them when searching by name afterwards
ConvertMaterials( pScene, sourceMesh->mMaterials);
unsigned int numMaterials = std::max( sourceMesh->mMaterials.size(), 1u);
for( unsigned int b = 0; b < numMaterials; b++)
{
// collect the faces belonging to this material
std::vector<unsigned int> faces;
unsigned int numVertices = 0;
if( sourceMesh->mFaceMaterials.size() > 0)
{
// if there is a per-face material defined, select the faces with the corresponding material
for( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); c++)
{
if( sourceMesh->mFaceMaterials[c] == b)
{
faces.push_back( c);
numVertices += sourceMesh->mPosFaces[c].mIndices.size();
}
}
} else
{
// if there is no per-face material, place everything into one mesh
for( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); c++)
{
faces.push_back( c);
numVertices += sourceMesh->mPosFaces[c].mIndices.size();
}
}
// no faces/vertices using this material? strange...
if( numVertices == 0)
continue;
// create a submesh using this material
aiMesh* mesh = new aiMesh;
meshes.push_back( mesh);
// find the material by name in the scene's material list. Either own material
// or referenced material, it should already be found there
if( sourceMesh->mFaceMaterials.size() > 0)
{
std::map<std::string, unsigned int>::const_iterator matIt = mImportedMats.find( sourceMesh->mMaterials[b].mName);
if( matIt == mImportedMats.end())
mesh->mMaterialIndex = 0;
else
mesh->mMaterialIndex = matIt->second;
} else
{
mesh->mMaterialIndex = 0;
}
// Create properly sized data arrays in the mesh. We store unique vertices per face,
// as specified
mesh->mNumVertices = numVertices;
mesh->mVertices = new aiVector3D[numVertices];
mesh->mNumFaces = faces.size();
mesh->mFaces = new aiFace[mesh->mNumFaces];
// normals?
if( sourceMesh->mNormals.size() > 0)
mesh->mNormals = new aiVector3D[numVertices];
// texture coords
for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; c++)
{
if( sourceMesh->mTexCoords[c].size() > 0)
mesh->mTextureCoords[c] = new aiVector3D[numVertices];
}
// vertex colors
for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; c++)
{
if( sourceMesh->mColors[c].size() > 0)
mesh->mColors[c] = new aiColor4D[numVertices];
}
// now collect the vertex data of all data streams present in the imported mesh
unsigned int newIndex = 0;
std::vector<unsigned int> orgPoints; // from which original point each new vertex stems
orgPoints.resize( numVertices, 0);
for( unsigned int c = 0; c < faces.size(); c++)
{
unsigned int f = faces[c]; // index of the source face
const XFile::Face& pf = sourceMesh->mPosFaces[f]; // position source face
// create face. either triangle or triangle fan depending on the index count
aiFace& df = mesh->mFaces[c]; // destination face
df.mNumIndices = pf.mIndices.size();
df.mIndices = new unsigned int[ df.mNumIndices];
// collect vertex data for indices of this face
for( unsigned int d = 0; d < df.mNumIndices; d++)
{
df.mIndices[df.mNumIndices - 1 - d] = newIndex; // inverted face orientation for OGL
orgPoints[newIndex] = pf.mIndices[d];
// Position
mesh->mVertices[newIndex] = sourceMesh->mPositions[pf.mIndices[d]];
// Normal, if present
if( mesh->HasNormals())
mesh->mNormals[newIndex] = sourceMesh->mNormals[sourceMesh->mNormFaces[f].mIndices[d]];
// texture coord sets
for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; e++)
{
if( mesh->HasTextureCoords( e))
{
aiVector2D tex = sourceMesh->mTexCoords[e][pf.mIndices[d]];
mesh->mTextureCoords[e][newIndex] = aiVector3D( tex.x, 1.0f - tex.y, 0.0f);
}
}
// vertex color sets
for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; e++)
if( mesh->HasVertexColors( e))
mesh->mColors[e][newIndex] = sourceMesh->mColors[e][pf.mIndices[d]];
newIndex++;
}
}
// there should be as much new vertices as we calculated before
assert( newIndex == numVertices);
// convert all bones of the source mesh which influence vertices in this newly created mesh
const std::vector<XFile::Bone>& bones = sourceMesh->mBones;
std::vector<aiBone*> newBones;
for( unsigned int c = 0; c < bones.size(); c++)
{
const XFile::Bone& obone = bones[c];
// set up a vertex-linear array of the weights for quick searching if a bone influences a vertex
std::vector<float> oldWeights( sourceMesh->mPositions.size(), 0.0f);
for( unsigned int d = 0; d < obone.mWeights.size(); d++)
oldWeights[obone.mWeights[d].mVertex] = obone.mWeights[d].mWeight;
// collect all vertex weights that influence a vertex in the new mesh
std::vector<aiVertexWeight> newWeights;
newWeights.reserve( numVertices);
for( unsigned int d = 0; d < orgPoints.size(); d++)
{
// does the new vertex stem from an old vertex which was influenced by this bone?
float w = oldWeights[orgPoints[d]];
if( w > 0.0f)
newWeights.push_back( aiVertexWeight( d, w));
}
// if the bone has no weights in the newly created mesh, ignore it
if( newWeights.size() == 0)
continue;
// create
aiBone* nbone = new aiBone;
newBones.push_back( nbone);
// copy name and matrix
nbone->mName.Set( obone.mName);
nbone->mOffsetMatrix = obone.mOffsetMatrix;
nbone->mNumWeights = newWeights.size();
nbone->mWeights = new aiVertexWeight[nbone->mNumWeights];
for( unsigned int d = 0; d < newWeights.size(); d++)
nbone->mWeights[d] = newWeights[d];
}
// store the bones in the mesh
mesh->mNumBones = newBones.size();
mesh->mBones = new aiBone*[mesh->mNumBones];
for( unsigned int c = 0; c < newBones.size(); c++)
mesh->mBones[c] = newBones[c];
}
}
// reallocate scene mesh array to be large enough
aiMesh** prevArray = pScene->mMeshes;
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes + meshes.size()];
if( prevArray)
{
memcpy( pScene->mMeshes, prevArray, pScene->mNumMeshes * sizeof( aiMesh*));
delete [] prevArray;
}
// allocate mesh index array in the node
pNode->mNumMeshes = meshes.size();
pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
// store all meshes in the mesh library of the scene and store their indices in the node
for( unsigned int a = 0; a < meshes.size(); a++)
{
pScene->mMeshes[pScene->mNumMeshes] = meshes[a];
pNode->mMeshes[a] = pScene->mNumMeshes;
pScene->mNumMeshes++;
}
}
// ------------------------------------------------------------------------------------------------
// Converts the animations from the given imported data and creates them in the scene.
void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData)
{
std::vector<aiAnimation*> newAnims;
for( unsigned int a = 0; a < pData->mAnims.size(); a++)
{
const XFile::Animation* anim = pData->mAnims[a];
// create a new animation to hold the data
aiAnimation* nanim = new aiAnimation;
newAnims.push_back( nanim);
nanim->mName.Set( anim->mName);
// duration will be determined by the maximum length
nanim->mDuration = 0;
nanim->mTicksPerSecond = pData->mAnimTicksPerSecond;
nanim->mNumBones = anim->mAnims.size();
nanim->mBones = new aiBoneAnim*[nanim->mNumBones];
for( unsigned int b = 0; b < anim->mAnims.size(); b++)
{
const XFile::AnimBone* bone = anim->mAnims[b];
aiBoneAnim* nbone = new aiBoneAnim;
nbone->mBoneName.Set( bone->mBoneName);
nanim->mBones[b] = nbone;
// apply the LH->RH conversion if the animation affects the root bone
bool isRootAnim = (bone->mBoneName == pScene->mRootNode->mName.data);
// keyframes are given as combined transformation matrix keys
if( bone->mTrafoKeys.size() > 0)
{
nbone->mNumPositionKeys = bone->mTrafoKeys.size();
nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys];
nbone->mNumRotationKeys = bone->mTrafoKeys.size();
nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys];
nbone->mNumScalingKeys = bone->mTrafoKeys.size();
nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys];
for( unsigned int c = 0; c < bone->mTrafoKeys.size(); c++)
{
// deconstruct each matrix into separate position, rotation and scaling
double time = bone->mTrafoKeys[c].mTime;
aiMatrix4x4 trafo = bone->mTrafoKeys[c].mMatrix;
// extract position
aiVector3D pos( trafo.a4, trafo.b4, trafo.c4);
if( isRootAnim)
ConvertToLHProcess::ConvertToOGL( pos);
nbone->mPositionKeys[c].mTime = time;
nbone->mPositionKeys[c].mValue = pos;
// extract scaling
aiVector3D scale;
scale.x = aiVector3D( trafo.a1, trafo.b1, trafo.c1).Length();
scale.y = aiVector3D( trafo.a2, trafo.b2, trafo.c2).Length();
scale.z = aiVector3D( trafo.a3, trafo.b3, trafo.c3).Length();
nbone->mScalingKeys[c].mTime = time;
nbone->mScalingKeys[c].mValue = scale;
// reconstruct rotation matrix without scaling
aiMatrix3x3 rotmat(
trafo.a1 / scale.x, trafo.a2 / scale.y, trafo.a3 / scale.z,
trafo.b1 / scale.x, trafo.b2 / scale.y, trafo.b3 / scale.z,
trafo.c1 / scale.x, trafo.c2 / scale.y, trafo.c3 / scale.z);
if( isRootAnim)
ConvertToLHProcess::ConvertToOGL( rotmat);
// and convert it into a quaternion
nbone->mRotationKeys[c].mTime = time;
nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat);
}
// longest lasting key sequence determines duration
nanim->mDuration = std::max( nanim->mDuration, bone->mTrafoKeys.back().mTime);
} else
{
// separate key sequences for position, rotation, scaling
nbone->mNumPositionKeys = bone->mPosKeys.size();
nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys];
for( unsigned int c = 0; c < nbone->mNumPositionKeys; c++)
{
aiVector3D pos = bone->mPosKeys[c].mValue;
if( isRootAnim)
ConvertToLHProcess::ConvertToOGL( pos);
nbone->mPositionKeys[c].mTime = bone->mPosKeys[c].mTime;
nbone->mPositionKeys[c].mValue = pos;
}
// rotation
nbone->mNumRotationKeys = bone->mRotKeys.size();
nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys];
for( unsigned int c = 0; c < nbone->mNumRotationKeys; c++)
{
aiMatrix3x3 rotmat = bone->mRotKeys[c].mValue.GetMatrix();
if( isRootAnim)
ConvertToLHProcess::ConvertToOGL( rotmat);
nbone->mRotationKeys[c].mTime = bone->mRotKeys[c].mTime;
nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat);
}
// scaling
nbone->mNumScalingKeys = bone->mScaleKeys.size();
nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys];
for( unsigned int c = 0; c < nbone->mNumScalingKeys; c++)
nbone->mScalingKeys[c] = bone->mScaleKeys[c];
// longest lasting key sequence determines duration
if( bone->mPosKeys.size() > 0)
nanim->mDuration = std::max( nanim->mDuration, bone->mPosKeys.back().mTime);
if( bone->mRotKeys.size() > 0)
nanim->mDuration = std::max( nanim->mDuration, bone->mRotKeys.back().mTime);
if( bone->mScaleKeys.size() > 0)
nanim->mDuration = std::max( nanim->mDuration, bone->mScaleKeys.back().mTime);
}
}
}
// store all converted animations in the scene
if( newAnims.size() > 0)
{
pScene->mNumAnimations = newAnims.size();
pScene->mAnimations = new aiAnimation* [pScene->mNumAnimations];
for( unsigned int a = 0; a < newAnims.size(); a++)
pScene->mAnimations[a] = newAnims[a];
}
}
// ------------------------------------------------------------------------------------------------
// Converts all materials in the given array and stores them in the scene's material list.
void XFileImporter::ConvertMaterials( aiScene* pScene, const std::vector<XFile::Material>& pMaterials)
{
// count the non-referrer materials in the array
unsigned int numMaterials = 0;
for( unsigned int a = 0; a < pMaterials.size(); a++)
if( !pMaterials[a].mIsReference)
numMaterials++;
if( numMaterials == 0)
return;
// resize the scene's material list to offer enough space for the new materials
aiMaterial** prevMats = pScene->mMaterials;
pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numMaterials];
if( prevMats)
{
memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*));
delete [] prevMats;
}
// convert all the materials given in the array
for( unsigned int a = 0; a < pMaterials.size(); a++)
{
const XFile::Material& oldMat = pMaterials[a];
if( oldMat.mIsReference)
continue;
Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
aiString name;
name.Set( oldMat.mName);
mat->AddProperty( &name, AI_MATKEY_NAME);
// Shading model: hardcoded to PHONG, there is no such information in an XFile
int shadeMode = (int) aiShadingMode_Phong;
mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
// material colours
mat->AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
mat->AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
mat->AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
mat->AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
// texture, if there is one
if (1 == oldMat.mTextures.size())
{
// if there is only one texture, assume it contains the
// diffuse color
aiString tex;
tex.Set( oldMat.mTextures[0]);
mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0));
}
else
{
// Otherwise ... try to search for typical strings in the
// texture's file name like 'bump' or 'diffuse'
unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0;
for( unsigned int b = 0; b < oldMat.mTextures.size(); b++)
{
std::string sz = oldMat.mTextures[b];
char key[256];
// find the file name
const size_t iLen = sz.length();
std::string::size_type s = sz.rfind('\\',iLen-1);
if (std::string::npos == s)
{
s = sz.rfind('/',iLen-1);
if (std::string::npos == s)s = 0;
}
// cut off the file extension
std::string::size_type sExt = sz.rfind('.',iLen-1);
if (std::string::npos != sExt)
{
sz[sExt] = '\0';
}
// bump map
std::string::size_type s2 = sz.find("bump",s);
if (std::string::npos == s2)
{
s2 = sz.find("BUMP",s);
if (std::string::npos == s2)
{
s2 = sz.find("Bump",s);
if (std::string::npos == s2)
{
s2 = sz.find("height",s);
if (std::string::npos == s2)
{
s2 = sz.find("HEIGHT",s);
if (std::string::npos == s2)
{
s2 = sz.find("Height",s);
}
}
}
}
}
if (std::string::npos != s2)
{
sprintf(key,AI_MATKEY_TEXTURE_BUMP_ "[%i]",iHM++);
}
else
{
// Normal map
std::string::size_type s2 = sz.find("normal",s);
if (std::string::npos == s2)
{
s2 = sz.find("NORMAL",s);
if (std::string::npos == s2)
{
s2 = sz.find("nm",s); // not really unique
if (std::string::npos == s2)
{
s2 = sz.find("Normal",s);
if (std::string::npos == s2)
{
s2 = sz.find("NM",s);
}
}
}
}
if (std::string::npos != s2)
{
sprintf(key,AI_MATKEY_TEXTURE_NORMALS_ "[%i]",iNM++);
}
else
{
// specular color texture (not unique, too. Could
// also be the material's shininess)
std::string::size_type s2 = sz.find("spec",s);
if (std::string::npos == s2)
{
s2 = sz.find("Spec",s);
if (std::string::npos == s2)
{
s2 = sz.find("SPEC",s);
if (std::string::npos == s2)
{
s2 = sz.find("Glanz",s);
if (std::string::npos == s2)
{
s2 = sz.find("glanz",s);
}
}
}
}
if (std::string::npos != s2)
{
sprintf(key,AI_MATKEY_TEXTURE_SPECULAR_ "[%i]",iSM++);
}
else
{
// ambient color texture
std::string::size_type s2 = sz.find("ambi",s);
if (std::string::npos == s2)
{
s2 = sz.find("AMBI",s);
if (std::string::npos == s2)
{
s2 = sz.find("umgebungsfarbe",s);
if (std::string::npos == s2)
{
s2 = sz.find("Ambi",s);
}
}
}
if (std::string::npos != s2)
{
sprintf(key,AI_MATKEY_TEXTURE_AMBIENT_ "[%i]",iAM++);
}
else
{
// emissive color texture
std::string::size_type s2 = sz.find("emissive",s);
if (std::string::npos == s2)
{
s2 = sz.find("EMISSIVE",s);
if (std::string::npos == s2)
{
// self illumination
s2 = sz.find("self",s);
if (std::string::npos == s2)
{
s2 = sz.find("Emissive",s);
}
}
}
if (std::string::npos != s2)
{
sprintf(key,AI_MATKEY_TEXTURE_EMISSIVE_ "[%i]",iEM++);
}
else
{
// assume it is a diffuse texture
sprintf(key,AI_MATKEY_TEXTURE_DIFFUSE_ "[%i]",iDM++);
}
}
}
}
}
aiString tex;
tex.Set( oldMat.mTextures[b] );
mat->AddProperty( &tex, key);
}
}
pScene->mMaterials[pScene->mNumMaterials] = mat;
mImportedMats[oldMat.mName] = pScene->mNumMaterials;
pScene->mNumMaterials++;
}
}

View File

@ -0,0 +1,97 @@
/** @file Definition of the XFile importer class. */
#ifndef AI_XFILEIMPORTER_H_INC
#define AI_XFILEIMPORTER_H_INC
#include <map>
#include "XFileHelper.h"
#include "BaseImporter.h"
#include "../include/aiTypes.h"
struct aiNode;
namespace Assimp
{
struct XFile::Scene;
struct XFile::Node;
// ---------------------------------------------------------------------------
/** The XFileImporter is a worker class capable of importing a scene from a
* DirectX file .x
*/
class XFileImporter : public BaseImporter
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
XFileImporter();
/** Destructor, private as well */
~XFileImporter();
public:
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details. */
bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
protected:
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
// -------------------------------------------------------------------
/** Constructs the return data structure out of the imported data.
* @param pScene The scene to construct the return data in.
* @param pData The imported data in the internal temporary representation.
*/
void CreateDataRepresentationFromImport( aiScene* pScene, const XFile::Scene* pData);
// -------------------------------------------------------------------
/** Recursively creates scene nodes from the imported hierarchy. The meshes and materials
* of the nodes will be extracted on the way.
* @param pScene The scene to construct the return data in.
* @param pParent The parent node where to create new child nodes
* @param pNode The temporary node to copy.
* @return The created node
*/
aiNode* CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode);
// -------------------------------------------------------------------
/** Converts all meshes in the given mesh array. Each mesh is splitted up per material,
* the indices of the generated meshes are stored in the node structure.
* @param pScene The scene to construct the return data in.
* @param pNode The target node structure that references the constructed meshes.
* @param pMeshes The array of meshes to convert
*/
void CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector<XFile::Mesh*>& pMeshes);
// -------------------------------------------------------------------
/** Converts the animations from the given imported data and creates them in the scene.
* @param pScene The scene to hold to converted animations
* @param pData The data to read the animations from
*/
void CreateAnimations( aiScene* pScene, const XFile::Scene* pData);
// -------------------------------------------------------------------
/** Converts all materials in the given array and stores them in the scene's material list.
* @param pScene The scene to hold the converted materials.
* @param pMaterials The material array to convert.
*/
void ConvertMaterials( aiScene* pScene, const std::vector<XFile::Material>& pMaterials);
protected:
/** Buffer to hold the loaded file */
std::vector<char> mBuffer;
/** Imported materials: index in the scene's material list by name */
std::map<std::string, unsigned int> mImportedMats;
};
} // end of namespace Assimp
#endif // AI_BASEIMPORTER_H_INC

1182
code/XFileParser.cpp 100644

File diff suppressed because it is too large Load Diff

119
code/XFileParser.h 100644
View File

@ -0,0 +1,119 @@
/** @file Helper class to parse a XFile into a temporary structure */
#ifndef AI_XFILEPARSER_H_INC
#define AI_XFILEPARSER_H_INC
#include <string>
#include <vector>
#include "../include/aiTypes.h"
namespace Assimp
{
namespace XFile
{
struct Node;
struct Mesh;
struct Scene;
struct Material;
struct Animation;
struct AnimBone;
}
/** The XFileParser reads a XFile either in text or binary form and builds a temporary
* data structure out of it.
*/
class XFileParser
{
public:
/** Constructor. Creates a data structure out of the XFile given in the memory block.
* @param pBuffer Memory buffer containing the XFile
*/
XFileParser( const std::vector<char>& pBuffer);
/** Destructor. Destroys all imported data along with it */
~XFileParser();
/** Returns the temporary representation of the imported data */
const XFile::Scene* GetImportedData() const { return mScene; }
protected:
void ParseFile();
void ParseDataObjectTemplate();
void ParseDataObjectFrame( XFile::Node *pParent);
void ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix);
void ParseDataObjectMesh( XFile::Mesh* pMesh);
void ParseDataObjectSkinWeights( XFile::Mesh* pMesh);
void ParseDataObjectSkinMeshHeader( XFile::Mesh* pMesh);
void ParseDataObjectMeshNormals( XFile::Mesh* pMesh);
void ParseDataObjectMeshTextureCoords( XFile::Mesh* pMesh);
void ParseDataObjectMeshVertexColors( XFile::Mesh* pMesh);
void ParseDataObjectMeshMaterialList( XFile::Mesh* pMesh);
void ParseDataObjectMaterial( XFile::Material* pMaterial);
void ParseDataObjectAnimTicksPerSecond();
void ParseDataObjectAnimationSet();
void ParseDataObjectAnimation( XFile::Animation* pAnim);
void ParseDataObjectAnimationKey( XFile::AnimBone *pAnimBone);
void ParseDataObjectTextureFilename( std::string& pName);
void ParseUnknownDataObject();
//! places pointer to next begin of a token, and ignores comments
void FindNextNoneWhiteSpace();
//! returns next parseable token. Returns empty string if no token there
std::string GetNextToken();
//! reads header of dataobject including the opening brace.
//! returns false if error happened, and writes name of object
//! if there is one
void readHeadOfDataObject( std::string* poName = NULL);
//! checks for closing curly brace, throws exception if not there
void CheckForClosingBrace();
//! checks for one following semicolon, throws exception if not there
void CheckForSemicolon();
//! checks for a separator char, either a ',' or a ';'
void CheckForSeparator();
//! reads a x file style string
void GetNextTokenAsString( std::string& poString);
void ReadUntilEndOfLine();
unsigned short ReadBinWord();
unsigned int ReadBinDWord();
unsigned int ReadInt();
float ReadFloat();
aiVector2D ReadVector2();
aiVector3D ReadVector3();
aiColor3D ReadRGB();
aiColor4D ReadRGBA();
/** Throws an exception with a line number and the given text. */
void ThrowException( const std::string& pText);
/** Filters the imported hierarchy for some degenerated cases that some exporters produce.
* @param pData The sub-hierarchy to filter
*/
void FilterHierarchy( XFile::Node* pNode);
protected:
unsigned int mMajorVersion, mMinorVersion; ///< version numbers
bool mIsBinaryFormat; ///< true if the file is in binary, false if it's in text form
unsigned int mBinaryFloatSize; ///< float size, either 32 or 64 bits
// counter for number arrays in binary format
unsigned int mBinaryNumCount;
const char* P;
const char* End;
/// Line number when reading in text format
unsigned int mLineNumber;
/// Imported data
XFile::Scene* mScene;
};
}
#endif // AI_XFILEPARSER_H_INC

26
code/aiAssert.cpp 100644
View File

@ -0,0 +1,26 @@
#include <iostream>
#include "aiAssert.h"
#ifdef _WIN32
#ifndef __GNUC__
# include "crtdbg.h"
#endif //ndef gcc
#endif
// Set a breakpoint using win32, else line, file and message will be returned and progam ends with
// errrocode = 1
void Assimp::aiAssert (bool expression, const std::string &message, unsigned int uiLine, const std::string &file)
{
if (!expression)
{
std::cerr << "File :" << file << ", line " << uiLine << " : " << message << std::endl;
#ifdef _WIN32
#ifndef __GNUC__
// Set breakpoint
__debugbreak();
#endif //ndef gcc
#else
exit (1);
#endif
}
}

114
code/fast_atof.h 100644
View File

@ -0,0 +1,114 @@
// Copyright (C) 2002-2007 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine" and the "irrXML" project.
// For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
// Adapted to the ASSIMP library because the builtin atof indeed takes AGES to parse a
// float inside a large string. Before parsing, it does a strlen on the given point.
#ifndef __FAST_A_TO_F_H_INCLUDED__
#define __FAST_A_TO_F_H_INCLUDED__
#include <math.h>
namespace Assimp
{
const float fast_atof_table[16] = { // we write [16] here instead of [] to work around a swig bug
0.f,
0.1f,
0.01f,
0.001f,
0.0001f,
0.00001f,
0.000001f,
0.0000001f,
0.00000001f,
0.000000001f,
0.0000000001f,
0.00000000001f,
0.000000000001f,
0.0000000000001f,
0.00000000000001f,
0.000000000000001f
};
inline unsigned int strtol10( const char* in, const char** out=0)
{
unsigned int value = 0;
while ( 1 )
{
if ( *in < '0' || *in > '9' )
break;
value = ( value * 10 ) + ( *in - '0' );
++in;
}
if (out)
*out = in;
return value;
}
//! Provides a fast function for converting a string into a float,
//! about 6 times faster than atof in win32.
// If you find any bugs, please send them to me, niko (at) irrlicht3d.org.
inline const char* fast_atof_move( const char* c, float& out)
{
bool inv = false;
const char *t;
float f;
if (*c=='-')
{
++c;
inv = true;
}
//f = (float)strtol(c, &t, 10);
f = (float) strtol10 ( c, &c );
if (*c == '.')
{
++c;
//float pl = (float)strtol(c, &t, 10);
float pl = (float) strtol10 ( c, &t );
pl *= fast_atof_table[t-c];
f += pl;
c = t;
if (*c == 'e')
{
++c;
//float exp = (float)strtol(c, &t, 10);
bool einv = (*c=='-');
if (einv)
++c;
float exp = (float)strtol10(c, &c);
if (einv)
exp *= -1.0f;
f *= pow(10.0f, exp);
}
}
if (inv)
f *= -1.0f;
out = f;
return c;
}
inline float fast_atof(const char* c)
{
float ret;
fast_atof_move(c, ret);
return ret;
}
} // end of namespace Assimp
#endif

Binary file not shown.

View File

@ -0,0 +1,83 @@
<importedScene alias data alias Model?>
<version 1.2 /> <smiley code=":-)" />
<frame alias object>
<name/>
<transformation matrix4x4 />
<mesh id="1" />
<mesh ... />
<childframe alias childobject as type frame alias object />
<childframe... />
</frame>
<meshes>
<mesh id="1">
<meshheader with MatID, Vertexcount, Facecount, a.s.o. />
<vertice>
<vertex xyzw />
<vertex ... />
</vertice>
<texturecoords>
<texturecoord uv />
<texturecoord ... />
</texturcoords>
<vertexcolors>
<color rgba />
<color ... />
</vertexcolors>
<normals>
<normal xyz />
<normal ... />
</normals>
<faces>
<face p1, p2, p3 />
<face ... />
</faces>
<bones>
<bone>
<name />
<weights>
<weight v, w />
<weight... />
</weights>
</bone>
<bone ... />
</bones>
</mesh>
<mesh id="x" ... />
</meshes>
<anims>
<anim name="rudern" duration="4500" ticksPerSecond="50">
<animbone name="bla">
<rotation>
<rotkey time="0" rot="xyzw" />
<rotkey time="100" rot="xyzw" />
...
</rotation>
<translation>
<transkey time="0" trans="xyz" />
<transkey time="100" trans="xyz" />
...
</translation>
<scaling>
<scalekey time="0" scale="xyz" />
<scalekey time="100" scale="xyz" />
...
<scaling>
</animbone>
<animbone name="blubb">
...
</animbone>
</anim>
<anim ... />
</anims>
<materials>
<material>
<param>
<name/>
<value/>
</param>
</material>
<material ... />
</materials>
</importedScene alias data alias model?>

View File

@ -0,0 +1,222 @@
// ISO C9x compliant stdint.h for Microsoft Visual Studio
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
//
// Copyright (c) 2006 Alexander Chemeris
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. The name of the author may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _MSC_VER // [
#error "Use this header only with Microsoft Visual C++ compilers!"
#endif // _MSC_VER ]
#ifndef _MSC_STDINT_H_ // [
#define _MSC_STDINT_H_
#if _MSC_VER > 1000
#pragma once
#endif
#include <limits.h>
// For Visual Studio 6 in C++ mode wrap <wchar.h> include with 'extern "C++" {}'
// or compiler give many errors like this:
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
#if (_MSC_VER < 1300) && defined(__cplusplus)
extern "C++" {
#endif
# include <wchar.h>
#if (_MSC_VER < 1300) && defined(__cplusplus)
}
#endif
// 7.18.1 Integer types
// 7.18.1.1 Exact-width integer types
typedef __int8 int8_t;
typedef __int16 int16_t;
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
// 7.18.1.2 Minimum-width integer types
typedef int8_t int_least8_t;
typedef int16_t int_least16_t;
typedef int32_t int_least32_t;
typedef int64_t int_least64_t;
typedef uint8_t uint_least8_t;
typedef uint16_t uint_least16_t;
typedef uint32_t uint_least32_t;
typedef uint64_t uint_least64_t;
// 7.18.1.3 Fastest minimum-width integer types
typedef int8_t int_fast8_t;
typedef int16_t int_fast16_t;
typedef int32_t int_fast32_t;
typedef int64_t int_fast64_t;
typedef uint8_t uint_fast8_t;
typedef uint16_t uint_fast16_t;
typedef uint32_t uint_fast32_t;
typedef uint64_t uint_fast64_t;
// 7.18.1.4 Integer types capable of holding object pointers
#ifdef _WIN64 // [
typedef __int64 intptr_t;
typedef unsigned __int64 uintptr_t;
#else // _WIN64 ][
typedef int intptr_t;
typedef unsigned int uintptr_t;
#endif // _WIN64 ]
// 7.18.1.5 Greatest-width integer types
typedef int64_t intmax_t;
typedef uint64_t uintmax_t;
// 7.18.2 Limits of specified-width integer types
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
// 7.18.2.1 Limits of exact-width integer types
#define INT8_MIN ((int8_t)_I8_MIN)
#define INT8_MAX _I8_MAX
#define INT16_MIN ((int16_t)_I16_MIN)
#define INT16_MAX _I16_MAX
#define INT32_MIN ((int32_t)_I32_MIN)
#define INT32_MAX _I32_MAX
#define INT64_MIN ((int64_t)_I64_MIN)
#define INT64_MAX _I64_MAX
#define UINT8_MAX _UI8_MAX
#define UINT16_MAX _UI16_MAX
#define UINT32_MAX _UI32_MAX
#define UINT64_MAX _UI64_MAX
// 7.18.2.2 Limits of minimum-width integer types
#define INT_LEAST8_MIN INT8_MIN
#define INT_LEAST8_MAX INT8_MAX
#define INT_LEAST16_MIN INT16_MIN
#define INT_LEAST16_MAX INT16_MAX
#define INT_LEAST32_MIN INT32_MIN
#define INT_LEAST32_MAX INT32_MAX
#define INT_LEAST64_MIN INT64_MIN
#define INT_LEAST64_MAX INT64_MAX
#define UINT_LEAST8_MAX UINT8_MAX
#define UINT_LEAST16_MAX UINT16_MAX
#define UINT_LEAST32_MAX UINT32_MAX
#define UINT_LEAST64_MAX UINT64_MAX
// 7.18.2.3 Limits of fastest minimum-width integer types
#define INT_FAST8_MIN INT8_MIN
#define INT_FAST8_MAX INT8_MAX
#define INT_FAST16_MIN INT16_MIN
#define INT_FAST16_MAX INT16_MAX
#define INT_FAST32_MIN INT32_MIN
#define INT_FAST32_MAX INT32_MAX
#define INT_FAST64_MIN INT64_MIN
#define INT_FAST64_MAX INT64_MAX
#define UINT_FAST8_MAX UINT8_MAX
#define UINT_FAST16_MAX UINT16_MAX
#define UINT_FAST32_MAX UINT32_MAX
#define UINT_FAST64_MAX UINT64_MAX
// 7.18.2.4 Limits of integer types capable of holding object pointers
#ifdef _WIN64 // [
# define INTPTR_MIN INT64_MIN
# define INTPTR_MAX INT64_MAX
# define UINTPTR_MAX UINT64_MAX
#else // _WIN64 ][
# define INTPTR_MIN INT32_MIN
# define INTPTR_MAX INT32_MAX
# define UINTPTR_MAX UINT32_MAX
#endif // _WIN64 ]
// 7.18.2.5 Limits of greatest-width integer types
#define INTMAX_MIN INT64_MIN
#define INTMAX_MAX INT64_MAX
#define UINTMAX_MAX UINT64_MAX
// 7.18.3 Limits of other integer types
#ifdef _WIN64 // [
# define PTRDIFF_MIN _I64_MIN
# define PTRDIFF_MAX _I64_MAX
#else // _WIN64 ][
# define PTRDIFF_MIN _I32_MIN
# define PTRDIFF_MAX _I32_MAX
#endif // _WIN64 ]
#define SIG_ATOMIC_MIN INT_MIN
#define SIG_ATOMIC_MAX INT_MAX
#ifndef SIZE_MAX // [
# ifdef _WIN64 // [
# define SIZE_MAX _UI64_MAX
# else // _WIN64 ][
# define SIZE_MAX _UI32_MAX
# endif // _WIN64 ]
#endif // SIZE_MAX ]
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
#ifndef WCHAR_MIN // [
# define WCHAR_MIN 0
#endif // WCHAR_MIN ]
#ifndef WCHAR_MAX // [
# define WCHAR_MAX _UI16_MAX
#endif // WCHAR_MAX ]
#define WINT_MIN 0
#define WINT_MAX _UI16_MAX
#endif // __STDC_LIMIT_MACROS ]
// 7.18.4 Limits of other integer types
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
// 7.18.4.1 Macros for minimum-width integer constants
#define INT8_C(val) val##i8
#define INT16_C(val) val##i16
#define INT32_C(val) val##i32
#define INT64_C(val) val##i64
#define UINT8_C(val) val##ui8
#define UINT16_C(val) val##ui16
#define UINT32_C(val) val##ui32
#define UINT64_C(val) val##ui64
// 7.18.4.2 Macros for greatest-width integer constants
#define INTMAX_C INT64_C
#define UINTMAX_C UINT64_C
#endif // __STDC_CONSTANT_MACROS ]
#endif // _MSC_STDINT_H_ ]

111
include/IOStream.h 100644
View File

@ -0,0 +1,111 @@
/** @file File I/O wrappers for C++. Use interfaces instead of function
* pointers to be sure even the silliest men on earth can work with this
*/
#ifndef AI_IOSTREAM_H_INC
#define AI_IOSTREAM_H_INC
#include <string>
#include <stddef.h>
#include "aiTypes.h"
#include "aiFileIO.h"
#ifndef __cplusplus
#error This header requires C++ to be used.
#endif
namespace Assimp
{
// ---------------------------------------------------------------------------
/** Class to handle file I/O for C++
*
* Derive an own implementation from this interface to provide custom IO handling
* to the Importer. If you implement this interface, be sure to also provide an
* implementation for IOSystem that creates instances of your custom IO class.
*/
// ---------------------------------------------------------------------------
class IOStream
{
protected:
/** Constructor protected, use IOSystem::Open() to create an instance. */
IOStream(void);
public:
// -------------------------------------------------------------------
/** Destructor. Deleting the object closes the underlying file,
* alternatively you may use IOSystem::Close() to release the file.
*/
virtual ~IOStream(void);
// -------------------------------------------------------------------
/** Read from the file
*
* See fread() for more details
* This fails for write-only files
*/
// -------------------------------------------------------------------
virtual size_t Read(
void* pvBuffer,
size_t pSize,
size_t pCount) = 0;
// -------------------------------------------------------------------
/** Write to the file
*
* See fwrite() for more details
* This fails for read-only files
*/
// -------------------------------------------------------------------
virtual size_t Write(
const void* pvBuffer,
size_t pSize,
size_t pCount) = 0;
// -------------------------------------------------------------------
/** Set the read/write cursor of the file
*
* See fseek() for more details
*/
// -------------------------------------------------------------------
virtual aiReturn Seek(
size_t pOffset,
aiOrigin pOrigin) = 0;
// -------------------------------------------------------------------
/** Get the current position of the read/write cursor
*
* See ftell() for more details
*/
// -------------------------------------------------------------------
virtual size_t Tell(void) const = 0;
// -------------------------------------------------------------------
/** Returns filesize
*
* Returns the filesize
*/
// -------------------------------------------------------------------
virtual size_t FileSize() const = 0;
};
// ----------------------------------------------------------------------------
inline IOStream::IOStream()
{
// empty
}
// ----------------------------------------------------------------------------
inline IOStream::~IOStream()
{
// empty
}
// ----------------------------------------------------------------------------
} //!ns Assimp
#endif //!!AI_IOSTREAM_H_INC

90
include/IOSystem.h 100644
View File

@ -0,0 +1,90 @@
/** @file Filesystem wrapper for C++. Inherit this class to supply custom file handling
* logic to the Import library.
*/
#ifndef AI_IOSYSTEM_H_INC
#define AI_IOSYSTEM_H_INC
#ifndef __cplusplus
#error This header requires C++ to be used.
#endif
#include <string>
namespace Assimp
{
class IOStream;
// ---------------------------------------------------------------------------
/** Interface to the file system.
*
* Derive an own implementation from this interface to supply custom file handling
* to the importer library. If you implement this interface, you also want to
* supply a custom implementation for IOStream.
*/
class IOSystem
{
public:
/** Constructor. Create an instance of your derived class and assign it to
* the #Importer instance by calling Importer::SetIOHandler().
*/
IOSystem();
/** Destructor. */
virtual ~IOSystem();
// -------------------------------------------------------------------
/** Tests for the existence of a file at the given path.
*
* @param pFile Path to the file
* @return true if there is a file with this path, else false.
*/
virtual bool Exists( const std::string& pFile) const = 0;
// -------------------------------------------------------------------
/** Returns the system specific directory separator
* @return System specific directory separator
*/
virtual std::string getOsSeparator() const = 0;
// -------------------------------------------------------------------
/** Open a new file with a given path. When the access to the file is finished,
* call Close() to release all associated resources.
*
* @param pFile Path to the file
* @param pMode Desired file I/O mode. Required are: "wb", "w", "wt",
* "rb", "r", "rt".
*
* @return New IOStream interface allowing the lib to access
* the underlying file.
* @note When implementing this class to provide custom IO handling, you propably
* have to supply an own implementation of IOStream as well.
*/
virtual IOStream* Open(
const std::string& pFile,
const std::string& pMode = std::string("rb")) = 0;
// -------------------------------------------------------------------
/** Closes the given file and releases all resources associated with it.
* @param pFile The file instance previously created by Open().
*/
virtual void Close( IOStream* pFile) = 0;
};
// ----------------------------------------------------------------------------
inline IOSystem::IOSystem()
{
// empty
}
// ----------------------------------------------------------------------------
inline IOSystem::~IOSystem()
{
// empty
}
// ----------------------------------------------------------------------------
} //!ns Assimp
#endif //AI_IOSYSTEM_H_INC

View File

@ -0,0 +1,68 @@
#ifndef OBJ_FILEPARSER_H_INC
#define OBJ_FILEPARSER_H_INC
#include <vector>
#include <string>
#include "aiTypes.h"
/*struct aiVector2D_t;
struct aiVector3D_t;*/
namespace Assimp
{
namespace ObjFile
{
struct Model;
struct Object;
struct Material;
struct Point3;
struct Point2;
}
class ObjFileImporter;
class ObjFileParser
{
public:
static const size_t BUFFERSIZE = 1024;
typedef std::vector<char> DataArray;
typedef std::vector<char>::iterator DataArrayIt;
typedef std::vector<char>::const_iterator ConstDataArrayIt;
public:
ObjFileParser(std::vector<char> &Data, const std::string &strAbsPath, const std::string &strModelName);
~ObjFileParser();
ObjFile::Model *GetModel() const;
private:
void parseFile();
void copyNextWord(char *pBuffer, size_t length);
void copyNextLine(char *pBuffer, size_t length);
void getVector3(std::vector<aiVector3D_t*> &point3d_array);
void getVector2(std::vector<aiVector2D_t*> &point2d_array);
void skipLine();
void getFace();
void getMaterialDesc();
void getComment();
void getMaterialLib();
void getNewMaterial();
void getGroupName();
void getGroupNumber();
void getObjectName();
void createObject(const std::string &strObjectName);
void reportErrorTokenInFace();
void extractExtension(const std::string strFile, std::string &strExt);
private:
std::string m_strAbsPath;
DataArrayIt m_DataIt;
DataArrayIt m_DataItEnd;
ObjFile::Model *m_pModel;
unsigned int m_uiLine;
char m_buffer[BUFFERSIZE];
};
} // Namespace Assimp
#endif

118
include/aiAnim.h 100644
View File

@ -0,0 +1,118 @@
/** @file Defines the data structures in which the imported animations are returned. */
#ifndef AI_ANIM_H_INC
#define AI_ANIM_H_INC
#include "aiTypes.h"
#include "aiQuaternion.h"
#ifdef __cplusplus
extern "C" {
#endif
/** A time-value pair specifying a certain 3D vector for the given time. */
struct aiVectorKey
{
double mTime; ///< The time of this key
aiVector3D_t mValue; ///< The value of this key
};
/** A time-value pair specifying a rotation for the given time. For joint animations
* the rotation is usually expressed using a quaternion.
*/
struct aiQuatKey
{
double mTime; ///< The time of this key
aiQuaternion_t mValue; ///< The value of this key
};
/** Describes the animation of a single bone. The name specifies the bone which is affected by this
* animation channel. The keyframes are given in three separate series of values, one each for
* position, rotation and scaling.
*/
struct aiBoneAnim
{
/** The name of the bone affected by this animation. */
aiString mBoneName;
/** The number of position keys */
unsigned int mNumPositionKeys;
/** The position keys of this animation channel. Positions are specified as 3D vector.
* The array is mNumPositionKeys in size.
*/
aiVectorKey* mPositionKeys;
/** The number of rotation keys */
unsigned int mNumRotationKeys;
/** The rotation keys of this animation channel. Rotations are given as quaternions,
* which are 4D vectors. The array is mNumRotationKeys in size.
*/
aiQuatKey* mRotationKeys;
/** The number of scaling keys */
unsigned int mNumScalingKeys;
/** The scaling keys of this animation channel. Scalings are specified as 3D vector.
* The array is mNumScalingKeys in size.
*/
aiVectorKey* mScalingKeys;
#ifdef __cplusplus
aiBoneAnim()
{
mNumPositionKeys = 0; mPositionKeys = NULL;
mNumRotationKeys= 0; mRotationKeys = NULL;
mNumScalingKeys = 0; mScalingKeys = NULL;
}
~aiBoneAnim()
{
delete [] mPositionKeys;
delete [] mRotationKeys;
delete [] mScalingKeys;
}
#endif // __cplusplus
};
/** An animation consists of keyframe data for a number of bones. For each bone affected by the animation
* a separate series of data is given.
*/
struct aiAnimation
{
/** The name of the animation. If the modelling package this data was exported from does support
* only a single animation channel, this name is usually empty (length is zero).
*/
aiString mName;
/** Duration of the animation in ticks. */
double mDuration;
/** Ticks per second. 0 if not specified in the imported file */
double mTicksPerSecond;
/** The number of bone animation channels. Each channel affects a single bone. */
unsigned int mNumBones;
/** The bone animation channels. Each channel affects a single bone. The array
* is mNumBones in size.
*/
aiBoneAnim** mBones;
#ifdef __cplusplus
aiAnimation()
{
mDuration = 0;
mTicksPerSecond = 0;
mNumBones = 0; mBones = NULL;
}
~aiAnimation()
{
for( unsigned int a = 0; a < mNumBones; a++)
delete mBones[a];
delete [] mBones;
}
#endif // __cplusplus
};
#ifdef __cplusplus
}
#endif
#endif // AI_ANIM_H_INC

28
include/aiAssert.h 100644
View File

@ -0,0 +1,28 @@
#ifndef AI_DEBUG_H_INC
#define AI_DEBUG_H_INC
#include <string>
#ifndef __cplusplus
#error This header requires C++ to be used.
#endif
namespace Assimp {
//! \brief ASSIMP specific assertion test, just works in debug mode
//! \param uiLine Line in file
//! \param file Source file
void aiAssert (bool expression, const std::string &message, unsigned int uiLine, const std::string &file);
//! \def ai_assert
//! \brief ASSIM specific assertion test
#ifdef DEBUG
# define ai_assert(expression) aiAssert (expression, #expression, __LINE__, __FILE__);
#else
# define ai_assert(expression)
#endif
} // Namespace Assimp
#endif

23
include/aiDefs.h 100644
View File

@ -0,0 +1,23 @@
/** @file Helper macros for the library
*/
#ifndef AI_DEF_H_INC
#define AI_DEF_H_INC
/** Namespace helper macros for c++ compilers
*/
#ifdef __cplusplus
#define AI_NAMESPACE_START namespace Assimp {
#define AI_NAMESPACE_END };
#else
#define AI_NAMESPACE_START
#define AI_NAMESPACE_END
#endif
#ifdef _DEBUG
# define ASSIMP_DEBUG
#endif
#endif //!!AI_DEF_H_INC

63
include/aiFileIO.h 100644
View File

@ -0,0 +1,63 @@
/** @file Defines generic routines to access memory-mapped files
*
*/
#ifndef AI_FILEIO_H_INC
#define AI_FILEIO_H_INC
#include "aiTypes.h"
#ifdef __cplusplus
extern "C" {
#endif
struct aiFileIO;
//enum aiOrigin;
typedef aiFileIO (*aiFileOpenProc)(aiFileIO*, const char*, const char*);
typedef aiReturn (*aiFileCloseProc)(aiFileIO*);
typedef unsigned long (*aiFileReadWriteProc)(aiFileIO*, char*, unsigned int, unsigned int);
typedef unsigned long (*aiFileTellProc)(aiFileIO*);
// ---------------------------------------------------------------------------
/** Define seek origins in fseek()-style.
*/
// ---------------------------------------------------------------------------
enum aiOrigin
{
aiOrigin_SET = 0x0, //!< Set position
aiOrigin_CUR = 0x1, //!< Current position
aiOrigin_END = 0x2 //!< End of file
};
typedef aiReturn (*aiFileSeek)(aiFileIO*, unsigned long, aiOrigin);
typedef char* aiUserData;
// ---------------------------------------------------------------------------
/** Data structure to wrap a set of fXXXX (e.g fopen) replacement functions
*
* The functions behave the same way as their appropriate fXXXX
* counterparts in the CRT.
*/
// ---------------------------------------------------------------------------
struct aiFileIO
{
aiUserData UserData;
aiFileOpenProc OpenFunc;
aiFileCloseProc CloseFunc;
aiFileReadWriteProc ReadFunc;
aiFileReadWriteProc WriteFunc;
aiFileTellProc TellProc;
aiFileSeek SeekProc;
};
#ifdef __cplusplus
}
#endif
#endif // AI_FILEIO_H_INC

View File

@ -0,0 +1,476 @@
/** @file Defines the material system of the library
*
*/
#ifndef AI_MATERIAL_H_INC
#define AI_MATERIAL_H_INC
#include "aiTypes.h"
#ifdef __cplusplus
extern "C" {
#endif
// ---------------------------------------------------------------------------
/** Defines type identifiers for use within the material system.
*
*/
// ---------------------------------------------------------------------------
enum aiPropertyTypeInfo
{
/** Array of single-precision floats
*/
aiPTI_Float = 0x1,
/** aiString data structure
*/
aiPTI_String = 0x3,
/** Array of Integers
*/
aiPTI_Integer = 0x4,
/** Simple binary buffer
*/
aiPTI_Buffer = 0x5,
};
// ---------------------------------------------------------------------------
/** Defines algorithms for generating UVW-coords (for texture sampling)
* procedurally.
*/
// ---------------------------------------------------------------------------
enum aiTexUVWGen
{
/** The view vector will be reflected to a pixel's normal.
*
* The result is used as UVW-coordinate for
* accessing a cubemap
*/
aiTexUVWGen_VIEWREFLEFT = 0x800001,
/** The view vector will be used as UVW-src
*
* The view vector is used as UVW-coordinate for
* accessing a cubemap
*/
aiTexUVWGen_VIEW = 0x800002,
/** The view vector will be refracted to the pixel's normal.
*
* If this is used, the refraction index to be applied should
* also be contained in the material description.
* The result is used as UVW-coordinate for
* accessing a cubemap.
*/
aiTexUVWGen_VIEWREFRACT = 0x800003
};
// ---------------------------------------------------------------------------
/** Defines all shading models supported by the library
*
* @note The list of shading modes has been taken from Blender3D.
* See Blender3D documentation for more information. The API does
* not distinguish between "specular" and "diffuse" shaders (thus the
* specular term for diffuse shading models like Oren-Nayar remains
* undefined)
*/
// ---------------------------------------------------------------------------
enum aiShadingMode
{
/** Flat shading. Shading is done on per-face base,
* diffuse only.
*/
aiShadingMode_Flat = 0x1,
/** Diffuse gouraud shading. Shading on per-vertex base
*/
aiShadingMode_Gouraud = 0x2,
/** Diffuse/Specular Phong-Shading
*
* Shading is applied on per-pixel base. This is the
* slowest algorithm, but generates the best results.
*/
aiShadingMode_Phong = 0x3,
/** Diffuse/Specular Phong-Blinn-Shading
*
* Shading is applied on per-pixel base. This is a little
* bit faster than phong and in some cases even
* more realistic
*/
aiShadingMode_Blinn = 0x4,
/** Toon-Shading per pixel
*
* Shading is applied on per-pixel base. The output looks
* like a comic. Often combined with edge detection.
*/
aiShadingMode_Toon = 0x5,
/** OrenNayar-Shading per pixel
*
* Extension to standard lambertian shading, taking the
* roughness of the material into account
*
*/
aiShadingMode_OrenNayar = 0x6,
/** Minnaert-Shading per pixel
*
* Extension to standard lambertian shading, taking the
* "darkness" of the material into account
*/
aiShadingMode_Minnaert = 0x7,
/** CookTorrance-Shading per pixel
*/
aiShadingMode_CookTorrance = 0x8,
/** No shading at all
*/
aiShadingMode_NoShading = 0x8
};
// ---------------------------------------------------------------------------
/** Data structure for a single property inside a material
*
* @see aiMaterial
*/
// ---------------------------------------------------------------------------
struct aiMaterialProperty
{
/** Specifies the name of the property (key)
*
* Keys are case insensitive.
*/
aiString* mKey;
/** Size of the buffer mData is pointing to, in bytes
*/
unsigned int mDataLength;
/** Type information for the property.
*
* Defines the data layout inside the
* data buffer. This is used by the library
* internally to perform debug checks.
*/
aiPropertyTypeInfo mType;
/** Binary buffer to hold the property's value
*
* The buffer has no terminal character. However,
* if a string is stored inside it may use 0 as terminal,
* but it would be contained in mDataLength.
*/
char* mData;
};
// ---------------------------------------------------------------------------
/** Data structure for a material
*
* Material data is stored using a key-value structure, called property
* (to guarant that the system is maximally flexible).
* The library defines a set of standard keys, which should be enough
* for nearly all purposes.
*/
// ---------------------------------------------------------------------------
#ifdef __cplusplus
class aiMaterial
{
protected:
aiMaterial() {}
public:
#else
struct aiMaterial
{
#endif // __cplusplus
/** List of all material properties loaded.
*/
aiMaterialProperty** mProperties;
/** Number of properties loaded
*/
unsigned int mNumProperties;
unsigned int mNumAllocated;
};
// ---------------------------------------------------------------------------
/** @def AI_MATKEY_NAME
* Defines the name of the material (aiString)
*/
#define AI_MATKEY_NAME "$mat.name"
/** @def AI_MATKEY_SHADING_MODE
* Defines the shading model to use (aiShadingMode)
*/
#define AI_MATKEY_SHADING_MODEL "$mat.shadingm"
/** @def AI_MATKEY_OPACITY
* Defines the base opacity of the material
*/
#define AI_MATKEY_OPACITY "$mat.opacity"
/** @def AI_MATKEY_BUMPSCALING
* Defines the height scaling of a bump map (for stuff like Parallax
* Occlusion Mapping)
*/
#define AI_MATKEY_BUMPSCALING "$mat.bumpscaling"
/** @def AI_MATKEY_SHININESS
* Defines the base shininess of the material
*/
#define AI_MATKEY_SHININESS "$mat.shininess"
/** @def AI_MATKEY_COLOR_DIFFUSE
* Defines the diffuse base color of the material
*/
#define AI_MATKEY_COLOR_DIFFUSE "$clr.diffuse"
/** @def AI_MATKEY_COLOR_AMBIENT
* Defines the ambient base color of the material
*/
#define AI_MATKEY_COLOR_AMBIENT "$clr.ambient"
/** @def AI_MATKEY_COLOR_SPECULAR
* Defines the specular base color of the material
*/
#define AI_MATKEY_COLOR_SPECULAR "$clr.specular"
/** @def AI_MATKEY_COLOR_EMISSIVE
* Defines the emissive base color of the material
*/
#define AI_MATKEY_COLOR_EMISSIVE "$clr.emissive"
/** @def AI_MATKEY_TEXTURE_DIFFUSE
* Defines a specified diffuse texture channel of the material
*/
#define AI_MATKEY_TEXTURE_DIFFUSE(N) "$tex.file.diffuse["#N"]"
#define AI_MATKEY_TEXTURE_DIFFUSE_ "$tex.file.diffuse"
/** @def AI_MATKEY_TEXTURE_AMBIENT
* Defines a specified ambient texture channel of the material
*/
#define AI_MATKEY_TEXTURE_AMBIENT(N) "$tex.file.ambient["#N"]"
#define AI_MATKEY_TEXTURE_AMBIENT_ "$tex.file.ambient"
/** @def AI_MATKEY_TEXTURE_SPECULAR
* Defines a specified specular texture channel of the material
*/
#define AI_MATKEY_TEXTURE_SPECULAR(N) "$tex.file.specular["#N"]"
#define AI_MATKEY_TEXTURE_SPECULAR_ "$tex.file.specular"
/** @def AI_MATKEY_TEXTURE_EMISSIVE
* Defines a specified emissive texture channel of the material
*/
#define AI_MATKEY_TEXTURE_EMISSIVE(N) "$tex.file.emissive["#N"]"
#define AI_MATKEY_TEXTURE_EMISSIVE_ "$tex.file.emissive"
/** @def AI_MATKEY_TEXTURE_NORMALS
* Defines a specified normal texture channel of the material
*/
#define AI_MATKEY_TEXTURE_NORMALS(N) "$tex.file.normals["#N"]"
#define AI_MATKEY_TEXTURE_NORMALS_ "$tex.file.normals"
/** @def AI_MATKEY_TEXTURE_BUMP
* Defines a specified bumpmap texture (=heightmap) channel of the material
* This is very similar to #AI_MATKEY_TEXTURE_NORMALS. It is provided
* to allow applications to determine whether the input data for
* normal mapping is already a normal map or needs to be converted to
* a heightmap.
*/
#define AI_MATKEY_TEXTURE_BUMP(N) "$tex.file.bump["#N"]"
#define AI_MATKEY_TEXTURE_BUMP_ "$tex.file.bump"
/** @def AI_MATKEY_TEXTURE_SHININESS
* Defines a specified shininess texture channel of the material
*/
#define AI_MATKEY_TEXTURE_SHININESS(N) "$tex.file.shininess["#N"]"
#define AI_MATKEY_TEXTURE_SHININESS_ "$tex.file.shininess"
/** @def AI_MATKEY_TEXTURE_OPACITY
* Defines a specified opacity texture channel of the material
*/
#define AI_MATKEY_TEXTURE_OPACITY(N) "$tex.file.opacity["#N"]"
#define AI_MATKEY_TEXTURE_OPACITY_ "$tex.file.opacity"
#define AI_MATKEY_TEXOP_DIFFUSE(N) "$tex.op.diffuse["#N"]"
#define AI_MATKEY_TEXOP_AMBIENT(N) "$tex.op.ambient["#N"]"
#define AI_MATKEY_TEXOP_SPECULAR(N) "$tex.op.specular["#N"]"
#define AI_MATKEY_TEXOP_EMISSIVE(N) "$tex.op.emissive["#N"]"
#define AI_MATKEY_TEXOP_NORMALS(N) "$tex.op.normals["#N"]"
#define AI_MATKEY_TEXOP_BUMP(N) "$tex.op.bump["#N"]"
#define AI_MATKEY_TEXOP_SHININESS(N) "$tex.op.shininess["#N"]"
#define AI_MATKEY_TEXOP_OPACITY(N) "$tex.op.opacity["#N"]"
#define AI_MATKEY_UVWSRC_DIFFUSE(N) "$tex.uvw.diffuse["#N"]"
#define AI_MATKEY_UVWSRC_AMBIENT(N) "$tex.uvw.ambient["#N"]"
#define AI_MATKEY_UVWSRC_SPECULAR(N) "$tex.uvw.specular["#N"]"
#define AI_MATKEY_UVWSRC_EMISSIVE(N) "$tex.uvw.emissive["#N"]"
#define AI_MATKEY_UVWSRC_NORMALS(N) "$tex.uvw.normals["#N"]"
#define AI_MATKEY_UVWSRC_BUMP(N) "$tex.uvw.bump["#N"]"
#define AI_MATKEY_UVWSRC_SHININESS(N) "$tex.uvw.shininess["#N"]"
#define AI_MATKEY_UVWSRC_OPACITY(N) "$tex.uvw.opacity["#N"]"
#define AI_MATKEY_REFRACTI_DIFFUSE(N) "$tex.refracti.diffuse["#N"]"
#define AI_MATKEY_REFRACTI_AMBIENT(N) "$tex.refracti.ambient["#N"]"
#define AI_MATKEY_REFRACTI_SPECULAR(N) "$tex.refracti.specular["#N"]"
#define AI_MATKEY_REFRACTI_EMISSIVE(N) "$tex.refracti.emissive["#N"]"
#define AI_MATKEY_REFRACTI_NORMALS(N) "$tex.refracti.normals["#N"]"
#define AI_MATKEY_REFRACTI_BUMP(N) "$tex.refracti.bump["#N"]"
#define AI_MATKEY_REFRACTI_SHININESS(N) "$tex.refracti.shininess["#N"]"
#define AI_MATKEY_REFRACTI_OPACITY(N) "$tex.refracti.opacity["#N"]"
#define AI_MATKEY_TEXBLEND_DIFFUSE(N) "$tex.blend.diffuse["#N"]"
#define AI_MATKEY_TEXBLEND_AMBIENT(N) "$tex.blend.ambient["#N"]"
#define AI_MATKEY_TEXBLEND_SPECULAR(N) "$tex.blend.specular["#N"]"
#define AI_MATKEY_TEXBLEND_EMISSIVE(N) "$tex.blend.emissive["#N"]"
#define AI_MATKEY_TEXBLEND_NORMALS(N) "$tex.blend.normals["#N"]"
#define AI_MATKEY_TEXBLEND_BUMP(N) "$tex.blend.bump["#N"]"
#define AI_MATKEY_TEXBLEND_SHININESS(N) "$tex.blend.shininess["#N"]"
#define AI_MATKEY_TEXBLEND_OPACITY(N) "$tex.blend.opacity["#N"]"
#define AI_MATKEY_ORENNAYAR_ROUGHNESS "$shading.orennayar.roughness"
#define AI_MATKEY_MINNAERT_DARKNESS "$shading.minnaert.darkness"
#define AI_MATKEY_COOK_TORRANCE_REFRACTI "$shading.cookt.refracti"
#define AI_MATKEY_COOK_TORRANCE_PARAM "$shading.cookt.param"
/** @def AI_MATKEY_GLOBAL_BACKGROUND_IMAGE
* Global property defined by some loaders. Contains the path to
* the image file to be used as background image.
*/
#define AI_MATKEY_GLOBAL_BACKGROUND_IMAGE "$global.bg.image2d"
// ---------------------------------------------------------------------------
/** Retrieve a material property with a specific key from the material
*
* @param pMat Pointer to the input material. May not be NULL
* @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
* @param pPropOut Pointer to receive a pointer to a valid aiMaterialProperty
* structure or NULL if the key has not been found.
*/
// ---------------------------------------------------------------------------
aiReturn aiGetMaterialProperty(const aiMaterial* pMat,
const char* pKey,
const aiMaterialProperty** pPropOut);
// ---------------------------------------------------------------------------
/** Retrieve an array of float values with a specific key
* from the material
*
* @param pMat Pointer to the input material. May not be NULL
* @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
* @param pOut Pointer to a buffer to receive the result.
* @param pMax Specifies the size of the given buffer, in float's.
* Receives the number of values (not bytes!) read.
*/
// ---------------------------------------------------------------------------
aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat,
const char* pKey,
float* pOut,
unsigned int* pMax);
#ifdef __cplusplus
// inline it
inline aiReturn aiGetMaterialFloat(const aiMaterial* pMat,
const char* pKey,
float* pOut)
{return aiGetMaterialFloatArray(pMat,pKey,pOut,(unsigned int*)0x0);}
#else
// use our friend, the C preprocessor
#define aiGetMaterialFloat (pMat, pKey, pOut) \
aiGetMaterialFloatArray(pMat, pKey, pOut, NULL)
#endif //!__cplusplus
// ---------------------------------------------------------------------------
/** Retrieve an array of integer values with a specific key
* from the material
*
* @param pMat Pointer to the input material. May not be NULL
* @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
* @param pOut Pointer to a buffer to receive the result.
* @param pMax Specifies the size of the given buffer, in int's.
* Receives the number of values (not bytes!) read.
*/
// ---------------------------------------------------------------------------
aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat,
const char* pKey,
int* pOut,
unsigned int* pMax);
#ifdef __cplusplus
// inline it
inline aiReturn aiGetMaterialInteger(const aiMaterial* pMat,
const char* pKey,
int* pOut)
{return aiGetMaterialIntegerArray(pMat,pKey,pOut,(unsigned int*)0x0);}
#else
// use our friend, the C preprocessor
#define aiGetMaterialInteger (pMat, pKey, pOut) \
aiGetMaterialIntegerArray(pMat, pKey, pOut, NULL)
#endif //!__cplusplus
// ---------------------------------------------------------------------------
/** Retrieve a color value from the material property table
*
* @param pMat Pointer to the input material. May not be NULL
* @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
* @param pOut Pointer to a buffer to receive the result.
*/
// ---------------------------------------------------------------------------
aiReturn aiGetMaterialColor(const aiMaterial* pMat,
const char* pKey,
aiColor4D* pOut);
// ---------------------------------------------------------------------------
/** Retrieve a string from the material property table
*
* @param pMat Pointer to the input material. May not be NULL
* @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
* @param pOut Pointer to a buffer to receive the result.
*/
// ---------------------------------------------------------------------------
aiReturn aiGetMaterialString(const aiMaterial* pMat,
const char* pKey,
aiString* pOut);
#ifdef __cplusplus
}
#endif //!__cplusplus
#endif //!!AI_MATERIAL_H_INC

View File

@ -0,0 +1,50 @@
/** @file Definition of a 3x3 matrix, including operators when compiling in C++ */
#ifndef AI_MATRIX3x3_H_INC
#define AI_MATRIX3x3_H_INC
#ifdef __cplusplus
extern "C" {
#endif
struct aiMatrix4x4;
// ---------------------------------------------------------------------------
/** Represents a column-major 3x3 matrix
*/
// ---------------------------------------------------------------------------
typedef struct aiMatrix3x3
{
#ifdef __cplusplus
aiMatrix3x3 () :
a1(1.0f), a2(0.0f), a3(0.0f),
b1(0.0f), b2(1.0f), b3(0.0f),
c1(0.0f), c2(0.0f), c3(1.0f) {}
aiMatrix3x3 ( float _a1, float _a2, float _a3,
float _b1, float _b2, float _b3,
float _c1, float _c2, float _c3) :
a1(_a1), a2(_a2), a3(_a3),
b1(_b1), b2(_b2), b3(_b3),
c1(_c1), c2(_c2), c3(_c3)
{}
/** Construction from a 4x4 matrix. The remaining parts of the matrix are ignored. */
explicit aiMatrix3x3( const aiMatrix4x4& pMatrix);
aiMatrix3x3& operator *= (const aiMatrix3x3& m);
aiMatrix3x3 operator* (const aiMatrix3x3& m) const;
aiMatrix3x3& Transpose();
#endif // __cplusplus
float a1, a2, a3;
float b1, b2, b3;
float c1, c2, c3;
} aiMatrix3x3_t;
#ifdef __cplusplus
} // end of extern C
#endif
#endif // AI_MATRIX3x3_H_INC

View File

@ -0,0 +1,54 @@
/** @file Inline implementation of the 3x3 matrix operators */
#ifndef AI_MATRIX3x3_INL_INC
#define AI_MATRIX3x3_INL_INC
#include "aiMatrix3x3.h"
#ifdef __cplusplus
#include "aiMatrix4x4.h"
#include <algorithm>
// ------------------------------------------------------------------------------------------------
// Construction from a 4x4 matrix. The remaining parts of the matrix are ignored.
inline aiMatrix3x3::aiMatrix3x3( const aiMatrix4x4& pMatrix)
{
a1 = pMatrix.a1; a2 = pMatrix.a2; a3 = pMatrix.a3;
b1 = pMatrix.b1; b2 = pMatrix.b2; b3 = pMatrix.b3;
c1 = pMatrix.c1; c2 = pMatrix.c2; c3 = pMatrix.c3;
}
// ------------------------------------------------------------------------------------------------
inline aiMatrix3x3& aiMatrix3x3::operator *= (const aiMatrix3x3& m)
{
*this = aiMatrix3x3(
m.a1 * a1 + m.b1 * a2 + m.c1 * a3,
m.a2 * a1 + m.b2 * a2 + m.c2 * a3,
m.a3 * a1 + m.b3 * a2 + m.c3 * a3,
m.a1 * b1 + m.b1 * b2 + m.c1 * b3,
m.a2 * b1 + m.b2 * b2 + m.c2 * b3,
m.a3 * b1 + m.b3 * b2 + m.c3 * b3,
m.a1 * c1 + m.b1 * c2 + m.c1 * c3,
m.a2 * c1 + m.b2 * c2 + m.c2 * c3,
m.a3 * c1 + m.b3 * c2 + m.c3 * c3);
return *this;
}
// ------------------------------------------------------------------------------------------------
inline aiMatrix3x3 aiMatrix3x3::operator* (const aiMatrix3x3& m) const
{
aiMatrix3x3 temp( *this);
temp *= m;
return temp;
}
// ------------------------------------------------------------------------------------------------
inline aiMatrix3x3& aiMatrix3x3::Transpose()
{
std::swap( a2, b1);
std::swap( a3, c1);
std::swap( b3, c2);
}
#endif // __cplusplus
#endif // AI_MATRIX3x3_INL_INC

View File

@ -0,0 +1,77 @@
/** @file 4x4 matrix structure, including operators when compiling in C++ */
#ifndef AI_MATRIX4X4_H_INC
#define AI_MATRIX4X4_H_INC
#ifdef __cplusplus
extern "C" {
#endif
struct aiMatrix3x3;
// Set packing to 4
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
#pragma pack(push,4)
#define PACK_STRUCT
#elif defined( __GNUC__ )
#define PACK_STRUCT __attribute__((packed))
#else
#error Compiler not supported
#endif
// ---------------------------------------------------------------------------
/** Represents a column-major 4x4 matrix,
* use this for homogenious coordinates
*/
// ---------------------------------------------------------------------------
typedef struct aiMatrix4x4
{
#ifdef __cplusplus
aiMatrix4x4 () :
a1(1.0f), a2(0.0f), a3(0.0f), a4(0.0f),
b1(0.0f), b2(1.0f), b3(0.0f), b4(0.0f),
c1(0.0f), c2(0.0f), c3(1.0f), c4(0.0f),
d1(0.0f), d2(0.0f), d3(0.0f), d4(1.0f){}
aiMatrix4x4 ( float _a1, float _a2, float _a3, float _a4,
float _b1, float _b2, float _b3, float _b4,
float _c1, float _c2, float _c3, float _c4,
float _d1, float _d2, float _d3, float _d4) :
a1(_a1), a2(_a2), a3(_a3), a4(_a4),
b1(_b1), b2(_b2), b3(_b3), b4(_b4),
c1(_c1), c2(_c2), c3(_c3), c4(_c4),
d1(_d1), d2(_d2), d3(_d3), d4(_d4)
{}
/** Constructor from 3x3 matrix. The remaining elements are set to identity. */
explicit aiMatrix4x4( const aiMatrix3x3& m);
aiMatrix4x4& operator *= (const aiMatrix4x4& m);
aiMatrix4x4 operator* (const aiMatrix4x4& m) const;
aiMatrix4x4& Transpose();
aiMatrix4x4& Inverse();
float Determinant() const;
float* operator[](unsigned int p_iIndex);
const float* operator[](unsigned int p_iIndex) const;
#endif // __cplusplus
float a1, a2, a3, a4;
float b1, b2, b3, b4;
float c1, c2, c3, c4;
float d1, d2, d3, d4;
} PACK_STRUCT aiMatrix4x4_t;
// Reset packing
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
#pragma pack( pop )
#endif
#undef PACK_STRUCT
#ifdef __cplusplus
} // end extern "C"
#endif // __cplusplus
#endif // AI_MATRIX4X4_H_INC

View File

@ -0,0 +1,136 @@
/** @file Inline implementation of the 4x4 matrix operators */
#ifndef AI_MATRIX4x4_INL_INC
#define AI_MATRIX4x4_INL_INC
#include "aiMatrix4x4.h"
#ifdef __cplusplus
#include "aiMatrix3x3.h"
#include <algorithm>
#include <limits>
#include <math.h>
// ---------------------------------------------------------------------------
inline aiMatrix4x4::aiMatrix4x4( const aiMatrix3x3& m)
{
a1 = m.a1; a2 = m.a2; a3 = m.a3; a4 = 0.0f;
b1 = m.b1; b2 = m.b2; b3 = m.b3; b4 = 0.0f;
c1 = m.c1; c2 = m.c2; c3 = m.c3; c4 = 0.0f;
d1 = 0.0f; d2 = 0.0f; d3 = 0.0f; d4 = 1.0f;
}
// ---------------------------------------------------------------------------
inline aiMatrix4x4& aiMatrix4x4::operator *= (const aiMatrix4x4& m)
{
*this = aiMatrix4x4(
m.a1 * a1 + m.b1 * a2 + m.c1 * a3 + m.d1 * a4,
m.a2 * a1 + m.b2 * a2 + m.c2 * a3 + m.d2 * a4,
m.a3 * a1 + m.b3 * a2 + m.c3 * a3 + m.d3 * a4,
m.a4 * a1 + m.b4 * a2 + m.c4 * a3 + m.d4 * a4,
m.a1 * b1 + m.b1 * b2 + m.c1 * b3 + m.d1 * b4,
m.a2 * b1 + m.b2 * b2 + m.c2 * b3 + m.d2 * b4,
m.a3 * b1 + m.b3 * b2 + m.c3 * b3 + m.d3 * b4,
m.a4 * b1 + m.b4 * b2 + m.c4 * b3 + m.d4 * b4,
m.a1 * c1 + m.b1 * c2 + m.c1 * c3 + m.d1 * c4,
m.a2 * c1 + m.b2 * c2 + m.c2 * c3 + m.d2 * c4,
m.a3 * c1 + m.b3 * c2 + m.c3 * c3 + m.d3 * c4,
m.a4 * c1 + m.b4 * c2 + m.c4 * c3 + m.d4 * c4,
m.a1 * d1 + m.b1 * d2 + m.c1 * d3 + m.d1 * d4,
m.a2 * d1 + m.b2 * d2 + m.c2 * d3 + m.d2 * d4,
m.a3 * d1 + m.b3 * d2 + m.c3 * d3 + m.d3 * d4,
m.a4 * d1 + m.b4 * d2 + m.c4 * d3 + m.d4 * d4);
return *this;
}
// ---------------------------------------------------------------------------
inline aiMatrix4x4 aiMatrix4x4::operator* (const aiMatrix4x4& m) const
{
aiMatrix4x4 temp( *this);
temp *= m;
return temp;
}
// ---------------------------------------------------------------------------
inline aiMatrix4x4& aiMatrix4x4::Transpose()
{
std::swap( (float&)b1, (float&)a2);
std::swap( (float&)c1, (float&)a3);
std::swap( (float&)c2, (float&)b3);
std::swap( (float&)d1, (float&)a4);
std::swap( (float&)d2, (float&)b4);
std::swap( (float&)d3, (float&)c4);
return *this;
}
// ---------------------------------------------------------------------------
inline float aiMatrix4x4::Determinant() const
{
return a1*b2*c3*d4 - a1*b2*c4*d3 + a1*b3*c4*d2 - a1*b3*c2*d4
+ a1*b4*c2*d3 - a1*b4*c3*d2 - a2*b3*c4*d1 + a2*b3*c1*d4
- a2*b4*c1*d3 + a2*b4*c3*d1 - a2*b1*c3*d4 + a2*b1*c4*d3
+ a3*b4*c1*d2 - a3*b4*c2*d1 + a3*b1*c2*d4 - a3*b1*c4*d2
+ a3*b2*c4*d1 - a3*b2*c1*d4 - a4*b1*c2*d3 + a4*b1*c3*d2
- a4*b2*c3*d1 + a4*b2*c1*d3 - a4*b3*c1*d2 + a4*b3*c2*d1;
}
// ---------------------------------------------------------------------------
inline aiMatrix4x4& aiMatrix4x4::Inverse()
{
// Compute the reciprocal determinant
float det = Determinant();
if(det == 0.0f)
{
*this = aiMatrix4x4(
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN());
return *this;
}
float invdet = 1.0f / det;
aiMatrix4x4 res;
res.a1 = invdet * (b2 * (c3 * d4 - c4 * d3) + b3 * (c4 * d2 - c2 * d4) + b4 * (c2 * d3 - c3 * d2));
res.a2 = -invdet * (a2 * (c3 * d4 - c4 * d3) + a3 * (c4 * d2 - c2 * d4) + a4 * (c2 * d3 - c3 * d2));
res.a3 = invdet * (a2 * (b3 * d4 - b4 * d3) + a3 * (b4 * d2 - b2 * d4) + a4 * (b2 * d3 - b3 * d2));
res.a4 = -invdet * (a2 * (b3 * c4 - b4 * c3) + a3 * (b4 * c2 - b2 * c4) + a4 * (b2 * c3 - b3 * c2));
res.b1 = -invdet * (b1 * (c3 * d4 - c4 * d3) + b3 * (c4 * d1 - c1 * d4) + b4 * (c1 * d3 - c3 * d1));
res.b2 = invdet * (a1 * (c3 * d4 - c4 * d3) + a3 * (c4 * d1 - c1 * d4) + a4 * (c1 * d3 - c3 * d1));
res.b3 = -invdet * (a1 * (b3 * d4 - b4 * d3) + a3 * (b4 * d1 - b1 * d4) + a4 * (b1 * d3 - b3 * d1));
res.b4 = invdet * (a1 * (b3 * c4 - b4 * c3) + a3 * (b4 * c1 - b1 * c4) + a4 * (b1 * c3 - b3 * c1));
res.c1 = invdet * (b1 * (c2 * d4 - c4 * d2) + b2 * (c4 * d1 - c1 * d4) + b4 * (c1 * d2 - c2 * d1));
res.c2 = -invdet * (a1 * (c2 * d4 - c4 * d2) + a2 * (c4 * d1 - c1 * d4) + a4 * (c1 * d2 - c2 * d1));
res.c3 = invdet * (a1 * (b2 * d4 - b4 * d2) + a2 * (b4 * d1 - b1 * d4) + a4 * (b1 * d2 - b2 * d1));
res.c4 = -invdet * (a1 * (b2 * c4 - b4 * c2) + a2 * (b4 * c1 - b1 * c4) + a4 * (b1 * c2 - b2 * c1));
res.d1 = -invdet * (b1 * (c2 * d3 - c3 * d2) + b2 * (c3 * d1 - c1 * d3) + b3 * (c1 * d2 - c2 * d1));
res.d2 = invdet * (a1 * (c2 * d3 - c3 * d2) + a2 * (c3 * d1 - c1 * d3) + a3 * (c1 * d2 - c2 * d1));
res.d3 = -invdet * (a1 * (b2 * d3 - b3 * d2) + a2 * (b3 * d1 - b1 * d3) + a3 * (b1 * d2 - b2 * d1));
res.d4 = invdet * (a1 * (b2 * c3 - b3 * c2) + a2 * (b3 * c1 - b1 * c3) + a3 * (b1 * c2 - b2 * c1));
*this = res;
return *this;
}
// ---------------------------------------------------------------------------
inline float* aiMatrix4x4::operator[](unsigned int p_iIndex)
{
return &this->a1 + p_iIndex * 4;
}
// ---------------------------------------------------------------------------
inline const float* aiMatrix4x4::operator[](unsigned int p_iIndex) const
{
return &this->a1 + p_iIndex * 4;
}
#endif // __cplusplus
#endif // AI_MATRIX4x4_INL_INC

268
include/aiMesh.h 100644
View File

@ -0,0 +1,268 @@
/** @file Defines the data structures in which the imported geometry is returned. */
#ifndef AI_MESH_H_INC
#define AI_MESH_H_INC
#include "aiTypes.h"
#ifdef __cplusplus
extern "C" {
#endif
// ---------------------------------------------------------------------------
/** A single face in a mesh, referring to multiple vertices.
* If mNumIndices is 3, the face is a triangle, for mNumIndices > 3 it's a polygon.
*/
// ---------------------------------------------------------------------------
struct aiFace
{
unsigned int mNumIndices; ///< Number of indices defining this face. 3 for a triangle, >3 for polygon
unsigned int* mIndices; ///< Pointer to the indices array. Size of the array is given in numIndices.
#ifdef __cplusplus
aiFace()
{
mNumIndices = 0; mIndices = NULL;
}
~aiFace()
{
delete [] mIndices;
}
aiFace( const aiFace& o)
{
mIndices = NULL;
*this = o;
}
const aiFace& operator = ( const aiFace& o)
{
if (&o == this)
return *this;
delete mIndices;
mNumIndices = o.mNumIndices;
mIndices = new unsigned int[mNumIndices];
memcpy( mIndices, o.mIndices, mNumIndices * sizeof( unsigned int));
return *this;
}
#endif // __cplusplus
};
// ---------------------------------------------------------------------------
/** A single influence of a bone on a vertex. */
// ---------------------------------------------------------------------------
struct aiVertexWeight
{
unsigned int mVertexId; ///< Index of the vertex which is influenced by the bone.
float mWeight; ///< The strength of the influence in the range (0...1). The influence from all bones at one vertex amounts to 1.
#ifdef __cplusplus
aiVertexWeight() { }
aiVertexWeight( unsigned int pID, float pWeight) : mVertexId( pID), mWeight( pWeight) { }
#endif // __cplusplus
};
// ---------------------------------------------------------------------------
/** A single bone of a mesh. A bone has a name by which it can be found
* in the frame hierarchy and by which it can be addressed by animations.
* In addition it has a number of influences on vertices.
*/
// ---------------------------------------------------------------------------
struct aiBone
{
aiString mName; ///< The name of the bone.
unsigned int mNumWeights; ///< The number of vertices affected by this bone
aiVertexWeight* mWeights; ///< The vertices affected by this bone
aiMatrix4x4 mOffsetMatrix; ///< Matrix that transforms from mesh space to bone space in bind pose
#ifdef __cplusplus
aiBone()
{
mNumWeights = 0; mWeights = NULL;
}
~aiBone()
{
delete [] mWeights;
}
#endif // __cplusplus
};
/** Maximum number of vertex color sets per mesh.
*
* Diffuse, specular, ambient and emissive
*/
#define AI_MAX_NUMBER_OF_COLOR_SETS 0x4
/** Maximum number of texture coord sets (UV channels) per mesh
*/
#define AI_MAX_NUMBER_OF_TEXTURECOORDS 0x4
// ---------------------------------------------------------------------------
/** A mesh represents a geometry or model with a single material.
*
* It usually consists of a number of vertices and a series of primitives/faces
* referencing the vertices. In addition there might be a series of bones, each
* of them addressing a number of vertices with a certain weight. Vertex data is
* presented in channels with each channel containing a single per-vertex
* information such as a set of texture coords or a normal vector.
* If a data pointer is non-null, the corresponding data stream is present.
* From C++-programs you can also use the comfort functions Has*() to
* test for the presence of various data streams.
*
* A Mesh uses only a single material which is referenced by a material ID.
*/
struct aiMesh
{
/** The number of vertices in this mesh.
* This is also the size of all of the per-vertex data arrays
*/
unsigned int mNumVertices;
/** The number of primitives (triangles, polygones, lines) in this mesh.
* This is also the size of the mFaces array
*/
unsigned int mNumFaces;
/** Vertex positions.
* This array is always present in a mesh. The array is
* mNumVertices in size.
*/
aiVector3D_t* mVertices;
/** Vertex normals.
* The array contains normalized vectors, NULL if not present.
* The array is mNumVertices in size.
*/
aiVector3D_t* mNormals;
/** Vertex tangents.
* The tangent of a vertex points in the direction of the positive
* X texture axis. The array contains normalized vectors, NULL if
* not present. The array is mNumVertices in size.
* @note If the mesh contains tangents, it automatically also
* contains bitangents.
*/
aiVector3D_t* mTangents;
/** Vertex bitangents.
* The bitangent of a vertex points in the direction of the positive
* Y texture axis. The array contains normalized vectors, NULL if not
* present. The array is mNumVertices in size.
* @note If the mesh contains tangents, it automatically also contains
* bitangents.
*/
aiVector3D_t* mBitangents;
/** Vertex color sets.
* A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex
* colors per vertex. NULL if not present. Each array is
* mNumVertices in size if present.
*/
aiColor4D_t* mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
/** Vertex texture coords, also known as UV channels.
* A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per
* vertex. NULL if not present. The array is mNumVertices in size.
*/
aiVector3D_t* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
/** Specifies the number of components for a given UV channel.
* Up to three channels are supported (UVW, for accessing volume
* or cube maps). If the value is 2 for a given channel n, the
* component p.z of mTextureCoords[n][p] is set to 0.0f.
* If the value is 1 for a given channel, p.y is set to 0.0f, too.
* @note 4D coords are not supported
*/
unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS];
/** The faces the mesh is contstructed from.
* Each face referres to a number of vertices by their indices.
* This array is always present in a mesh, its size is given
* in mNumFaces.
*/
aiFace* mFaces;
/** The number of bones this mesh contains.
* Can be 0, in which case the mBones array is NULL.
*/
unsigned int mNumBones;
/** The bones of this mesh.
* A bone consists of a name by which it can be found in the
* frame hierarchy and a set of vertex weights.
*/
aiBone** mBones;
/** The material used by this mesh.
* A mesh does use only a single material. If an imported model uses multiple materials,
* the import splits up the mesh. Use this value as index into the scene's material list.
*/
unsigned int mMaterialIndex;
#ifdef __cplusplus
aiMesh()
{
mNumVertices = 0; mNumFaces = 0;
mVertices = NULL; mFaces = NULL;
mNormals = NULL; mTangents = NULL;
mBitangents = NULL;
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
{
mNumUVComponents[a] = 0;
mTextureCoords[a] = NULL;
}
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
mColors[a] = NULL;
mNumBones = 0; mBones = NULL;
mMaterialIndex = 0;
}
~aiMesh()
{
delete [] mVertices;
delete [] mFaces;
delete [] mNormals;
delete [] mTangents;
delete [] mBitangents;
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
delete [] mTextureCoords[a];
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
delete [] mColors[a];
for( unsigned int a = 0; a < mNumBones; a++)
delete mBones[a];
delete [] mBones;
}
bool HasNormals() const { return mNormals != NULL; }
bool HasTangentsAndBitangents() const { return mTangents != NULL && mBitangents != NULL; }
bool HasVertexColors( unsigned int pIndex)
{
if( pIndex >= AI_MAX_NUMBER_OF_COLOR_SETS)
return false;
else
return mColors[pIndex] != NULL;
}
bool HasTextureCoords( unsigned int pIndex)
{
if( pIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS)
return false;
else
return mTextureCoords[pIndex] != NULL;
}
bool HasBones() const { return mBones != NULL; }
#endif // __cplusplus
};
#ifdef __cplusplus
}
#endif
#endif // AI_MESH_H_INC

View File

@ -0,0 +1,78 @@
/** @file Definitions for import post processing steps */
#ifndef AI_POSTPROCESS_H_INC
#define AI_POSTPROCESS_H_INC
#ifdef __cplusplus
extern "C" {
#endif
/** Defines the flags for all possible post processing steps. */
enum aiPostProcessSteps
{
/** Calculates the binormals and tangents for the imported meshes. Does nothing
* if a mesh does not have normals. You might want this post processing step to be
* executed if you plan to use tangent space calculations such as normal mapping
* applied to the meshes.
*/
aiProcess_CalcTangentSpace = 1,
/** Identifies and joins identical vertex data sets within all imported meshes.
* After this step is run each mesh does contain only unique vertices anymore,
* so a vertex is possibly used by multiple faces. You propably always want
* to use this post processing step.*/
aiProcess_JoinIdenticalVertices = 2,
/** Converts all the imported data to a left-handed coordinate space such as
* the DirectX coordinate system. By default the data is returned in a right-handed
* coordinate space which for example OpenGL preferres. In this space, +X points to the
* right, +Y points upwards and +Z points to the viewer. In the DirectX coordinate space
* +X points to the right, +Y points upwards and +Z points away from the viewer
* into the screen.
*/
aiProcess_ConvertToLeftHanded = 4,
/** Triangulates all faces of all meshes. By default the imported mesh data might
* contain faces with more than 3 indices. For rendering a mesh you usually need
* all faces to be triangles. This post processing step splits up all higher faces
* to triangles.
*/
aiProcess_Triangulate = 8,
/** Omits all normals found in the file. This can be used together
* with either the aiProcess_GenNormals or the aiProcess_GenSmoothNormals
* flag to force the recomputation of the normals.
*/
aiProcess_KillNormals = 0x10,
/** Generates normals for all faces of all meshes. The normals are shared
* between the three vertices of a face. This is ignored
* if normals are already existing. This flag may not be specified together
* with aiProcess_GenSmoothNormals
*/
aiProcess_GenNormals = 0x20,
/** Generates smooth normals for all vertices in the mesh. This is ignored
* if normals are already existing. This flag may not be specified together
* with aiProcess_GenNormals
*/
aiProcess_GenSmoothNormals = 0x40,
/** Splits large meshes into submeshes
* This is quite useful for realtime rendering where the number of vertices
* is usually limited by the video driver.
*
* A mesh is split if it consists of more than 1 * 10^6 vertices. This is defined
* in the internal SplitLargeMeshes.h header as AI_SLM_MAX_VERTICES.
*/
aiProcess_SplitLargeMeshes = 0x80
};
#ifdef __cplusplus
} // end of extern "C"
#endif
#endif // AI_POSTPROCESS_H_INC

View File

@ -0,0 +1,96 @@
/** @file Quaternion structure, including operators when compiling in C++ */
#ifndef AI_QUATERNION_H_INC
#define AI_QUATERNION_H_INC
#include <math.h>
#include "aiTypes.h"
#ifdef __cplusplus
extern "C" {
#endif
// ---------------------------------------------------------------------------
/** Represents a quaternion in a 4D vector. */
typedef struct aiQuaternion
{
#ifdef __cplusplus
aiQuaternion() : w(0.0f), x(0.0f), y(0.0f), z(0.0f) {}
aiQuaternion(float _w, float _x, float _y, float _z) : w(_w), x(_x), y(_y), z(_z) {}
/** Construct from rotation matrix. Result is undefined if the matrix is not orthonormal. */
aiQuaternion( const aiMatrix3x3& pRotMatrix);
/** Returns a matrix representation of the quaternion */
aiMatrix3x3 GetMatrix() const;
#endif // __cplusplus
float w, x, y, z;
} aiQuaternion_t;
#ifdef __cplusplus
// ---------------------------------------------------------------------------
// Constructs a quaternion from a rotation matrix
inline aiQuaternion::aiQuaternion( const aiMatrix3x3 &pRotMatrix)
{
float t = 1 + pRotMatrix.a1 + pRotMatrix.b2 + pRotMatrix.c3;
// large enough
if( t > 0.00001f)
{
float s = sqrt( t) * 2.0f;
x = (pRotMatrix.b3 - pRotMatrix.c2) / s;
y = (pRotMatrix.c1 - pRotMatrix.a3) / s;
z = (pRotMatrix.a2 - pRotMatrix.b1) / s;
w = 0.25f * s;
} // else we have to check several cases
else if( pRotMatrix.a1 > pRotMatrix.b2 && pRotMatrix.a1 > pRotMatrix.c3 )
{
// Column 0:
float s = sqrt( 1.0f + pRotMatrix.a1 - pRotMatrix.b2 - pRotMatrix.c3) * 2.0f;
x = 0.25f * s;
y = (pRotMatrix.a2 + pRotMatrix.b1) / s;
z = (pRotMatrix.c1 + pRotMatrix.a3) / s;
w = (pRotMatrix.b3 - pRotMatrix.c2) / s;
} else
if( pRotMatrix.b2 > pRotMatrix.c3)
{
// Column 1:
float s = sqrt( 1.0f + pRotMatrix.b2 - pRotMatrix.a1 - pRotMatrix.c3) * 2.0f;
x = (pRotMatrix.a2 + pRotMatrix.b1) / s;
y = 0.25f * s;
z = (pRotMatrix.b3 + pRotMatrix.c2) / s;
w = (pRotMatrix.c1 - pRotMatrix.a3) / s;
} else
{
// Column 2:
float s = sqrt( 1.0f + pRotMatrix.c3 - pRotMatrix.a1 - pRotMatrix.b2) * 2.0f;
x = (pRotMatrix.c1 + pRotMatrix.a3) / s;
y = (pRotMatrix.b3 + pRotMatrix.c2) / s;
z = 0.25f * s;
w = (pRotMatrix.a2 - pRotMatrix.b1) / s;
}
}
// ---------------------------------------------------------------------------
// Returns a matrix representation of the quaternion
inline aiMatrix3x3 aiQuaternion::GetMatrix() const
{
aiMatrix3x3 resMatrix;
resMatrix.a1 = 1.0f - 2.0f * (y * y + z * z);
resMatrix.a2 = 2.0f * (x * y + z * w);
resMatrix.a3 = 2.0f * (x * z - y * w);
resMatrix.b1 = 2.0f * (x * y - z * w);
resMatrix.b2 = 1.0f - 2.0f * (x * x + z * z);
resMatrix.b3 = 2.0f * (y * z + x * w);
resMatrix.c1 = 2.0f * (x * z + y * w);
resMatrix.c2 = 2.0f * (y * z - x * w);
resMatrix.c3 = 1.0f - 2.0f * (x * x + y * y);
return resMatrix;
}
} // end extern "C"
#endif // __cplusplus
#endif // AI_QUATERNION_H_INC

147
include/aiScene.h 100644
View File

@ -0,0 +1,147 @@
/** @file Defines the data structures in which the imported scene is returned. */
#ifndef AI_SCENE_H_INC
#define AI_SCENE_H_INC
#include "aiTypes.h"
#include "aiMesh.h"
#include "aiMaterial.h"
#include "aiAnim.h"
#ifdef __cplusplus
extern "C" {
#endif
// ---------------------------------------------------------------------------
/** A node in the imported hierarchy.
*
* Each node has name, a parent node (except for the root node),
* a transformation relative to its parent and possibly several child nodes.
* Simple file formats don't support hierarchical structures, for these formats
* the imported scene does consist of only a single root node with no childs.
*/
// ---------------------------------------------------------------------------
struct aiNode
{
/** The name of the node.
*
* The name might be empty (length of zero) but all nodes which
* need to be accessed afterwards by bones or anims are usually named.
*/
aiString mName;
/** The transformation relative to the node's parent. */
aiMatrix4x4 mTransformation;
/** Parent node. NULL if this node is the root node. */
aiNode* mParent;
/** The number of child nodes of this node. */
unsigned int mNumChildren;
/** The child nodes of this node. NULL if mNumChildren is 0. */
aiNode** mChildren;
/** The number of meshes of this node. */
unsigned int mNumMeshes;
/** The meshes of this node. Each entry is an index into the mesh */
unsigned int* mMeshes;
#ifdef __cplusplus
/** Constructor */
aiNode()
{
mParent = NULL;
mNumChildren = 0; mChildren = NULL;
mNumMeshes = 0; mMeshes = NULL;
}
/** Destructor */
~aiNode()
{
for( unsigned int a = 0; a < mNumChildren; a++)
delete mChildren[a];
delete [] mChildren;
delete [] mMeshes;
}
#endif // __cplusplus
};
// ---------------------------------------------------------------------------
/** The root structure of the imported data.
*
* Everything that was imported from the given file can be accessed from here.
*/
// ---------------------------------------------------------------------------
struct aiScene
{
/** The root node of the hierarchy.
*
* There will always be at least the root node if the import
* was successful. Presence of further nodes depends on the
* format and content of the imported file.
*/
aiNode* mRootNode;
/** The number of meshes in the scene. */
unsigned int mNumMeshes;
/** The array of meshes.
*
* Use the indices given in the aiNode structure to access
* this array. The array is mNumMeshes in size.
*/
aiMesh** mMeshes;
/** The number of materials in the scene. */
unsigned int mNumMaterials;
/** The array of materials.
*
* Use the index given in each aiMesh structure to access this
* array. The array is mNumMaterials in size.
*/
aiMaterial** mMaterials;
/** The number of animations in the scene. */
unsigned int mNumAnimations;
/** The array of animations.
*
* All animations imported from the given file are listed here.
* The array is mNumAnimations in size.
*/
aiAnimation** mAnimations;
#ifdef __cplusplus
aiScene()
{
mRootNode = NULL;
mNumMeshes = 0; mMeshes = NULL;
mNumMaterials = 0; mMaterials = NULL;
mNumAnimations = 0; mAnimations = NULL;
}
~aiScene()
{
delete mRootNode;
for( unsigned int a = 0; a < mNumMeshes; a++)
delete mMeshes[a];
delete [] mMeshes;
for( unsigned int a = 0; a < mNumMaterials; a++)
delete mMaterials[a];
delete [] mMaterials;
for( unsigned int a = 0; a < mNumAnimations; a++)
delete mAnimations[a];
delete [] mAnimations;
}
#endif // __cplusplus
};
#ifdef __cplusplus
}
#endif
#endif // AI_SCENE_H_INC

140
include/aiTypes.h 100644
View File

@ -0,0 +1,140 @@
#ifndef AI_TYPES_H_INC
#define AI_TYPES_H_INC
#include <sys/types.h>
#include <memory.h>
#if (defined _MSC_VER)
# include "Compiler/VisualStudio/stdint.h"
#endif // (defined _MSC_VER)
#include "aiVector3D.h"
#include "aiMatrix3x3.h"
#include "aiMatrix4x4.h"
#include "aiVector3D.inl"
#include "aiMatrix3x3.inl"
#include "aiMatrix4x4.inl"
#ifdef __cplusplus
#include <string>
extern "C" {
#endif
/** Maximum dimension for strings, ASSIMP strings are zero terminated */
const size_t MAXLEN = 1024;
// ---------------------------------------------------------------------------
/** Represents a two-dimensional vector.
*/
// ---------------------------------------------------------------------------
typedef struct aiVector2D
{
#ifdef __cplusplus
aiVector2D () : x(0.0f), y(0.0f) {}
aiVector2D (float _x, float _y) : x(_x), y(_y) {}
aiVector2D (const aiVector2D& o) : x(o.x), y(o.y) {}
#endif // __cplusplus
float x, y;
} aiVector2D_t;
// aiVector3D type moved to separate header due to size of operators
// aiQuaternion type moved to separate header due to size of operators
// aiMatrix4x4 type moved to separate header due to size of operators
// ---------------------------------------------------------------------------
/** Represents a color in Red-Green-Blue space.
*/
// ---------------------------------------------------------------------------
typedef struct aiColor3D
{
#ifdef __cplusplus
aiColor3D () : r(0.0f), g(0.0f), b(0.0f) {}
aiColor3D (float _r, float _g, float _b) : r(_r), g(_g), b(_b) {}
aiColor3D (const aiColor3D& o) : r(o.r), g(o.g), b(o.b) {}
#endif // __cplusplus
float r, g, b;
} aiColor3D_t;
// ---------------------------------------------------------------------------
/** Represents a color in Red-Green-Blue space including an
* alpha component.
*/
// ---------------------------------------------------------------------------
typedef struct aiColor4D
{
#ifdef __cplusplus
aiColor4D () : r(0.0f), g(0.0f), b(0.0f), a(0.0f) {}
aiColor4D (float _r, float _g, float _b, float _a)
: r(_r), g(_g), b(_b), a(_a) {}
aiColor4D (const aiColor4D& o)
: r(o.r), g(o.g), b(o.b), a(o.a) {}
#endif // __cplusplus
float r, g, b, a;
} aiColor4D_t;
// ---------------------------------------------------------------------------
/** Represents a string, zero byte terminated
*/
// ---------------------------------------------------------------------------
typedef struct aiString
{
#ifdef __cplusplus
inline aiString() :
length(0)
{
// empty
}
inline aiString(const aiString& rOther) :
length(rOther.length)
{
memcpy( data, rOther.data, rOther.length);
this->data[this->length] = '\0';
}
void Set( const std::string& pString)
{
if( pString.length() > MAXLEN - 1)
return;
length = pString.length();
memcpy( data, pString.c_str(), length);
data[length] = 0;
}
#endif // __cplusplus
size_t length;
char data[MAXLEN];
} aiString_t;
// ---------------------------------------------------------------------------
/** Standard return type for all library functions.
*
* To check whether a function failed or not check against
* AI_SUCCESS.
*/
// ---------------------------------------------------------------------------
enum aiReturn
{
AI_SUCCESS = 0x0,
AI_FAILURE = -0x1,
AI_INVALIDFILE = -0x2,
AI_OUTOFMEMORY = -0x3,
AI_INVALIDARG = -0x4
};
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,99 @@
/** @file 3D vector structure, including operators when compiling in C++ */
#ifndef AI_VECTOR3D_H_INC
#define AI_VECTOR3D_H_INC
#include <math.h>
#include "aiAssert.h"
#ifdef __cplusplus
extern "C" {
#endif
// ---------------------------------------------------------------------------
/** Represents a three-dimensional vector. */
typedef struct aiVector3D
{
#ifdef __cplusplus
aiVector3D () : x(0.0f), y(0.0f), z(0.0f) {}
aiVector3D (float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
aiVector3D (const aiVector3D& o) : x(o.x), y(o.y), z(o.z) {}
void Set( float pX, float pY, float pZ) { x = pX; y = pY; z = pZ; }
float SquareLength() const { return x*x + y*y + z*z; }
float Length() const { return sqrt( SquareLength()); }
aiVector3D& Normalize() { *this /= Length(); return *this; }
const aiVector3D& operator += (const aiVector3D& o) { x += o.x; y += o.y; z += o.z; return *this; }
const aiVector3D& operator -= (const aiVector3D& o) { x -= o.x; y -= o.y; z -= o.z; return *this; }
const aiVector3D& operator *= (float f) { x *= f; y *= f; z *= f; return *this; }
const aiVector3D& operator /= (float f) { x /= f; y /= f; z /= f; return *this; }
inline float operator[](unsigned int i) const {return *(&x + i);}
inline float& operator[](unsigned int i) {return *(&x + i);}
#endif // __cplusplus
float x, y, z;
} aiVector3D_t;
#ifdef __cplusplus
} // end extern "C"
// symmetric addition
inline aiVector3D operator + (const aiVector3D& v1, const aiVector3D& v2)
{
return aiVector3D( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
}
// symmetric subtraction
inline aiVector3D operator - (const aiVector3D& v1, const aiVector3D& v2)
{
return aiVector3D( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
}
// scalar product
inline float operator * (const aiVector3D& v1, const aiVector3D& v2)
{
return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
}
// scalar multiplication
inline aiVector3D operator * ( float f, const aiVector3D& v)
{
return aiVector3D( f*v.x, f*v.y, f*v.z);
}
// and the other way around
inline aiVector3D operator * ( const aiVector3D& v, float f)
{
return aiVector3D( f*v.x, f*v.y, f*v.z);
}
// scalar division
inline aiVector3D operator / ( const aiVector3D& v, float f)
{
//ai_assert(0.0f != f);
return v * (1/f);
}
// vector division
inline aiVector3D operator / ( const aiVector3D& v, const aiVector3D& v2)
{
//ai_assert(0.0f != v2.x && 0.0f != v2.y && 0.0f != v2.z);
return aiVector3D(v.x / v2.x,v.y / v2.y,v.z / v2.z);
}
// cross product
inline aiVector3D operator ^ ( const aiVector3D& v1, const aiVector3D& v2)
{
return aiVector3D( v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x);
}
// vector inversion
inline aiVector3D operator - ( const aiVector3D& v)
{
return aiVector3D( -v.x, -v.y, -v.z);
}
#endif // __cplusplus
#endif // AI_VECTOR3D_H_INC

View File

@ -0,0 +1,33 @@
/** @file Inline implementation of vector3D operators */
#ifndef AI_VECTOR3D_INL_INC
#define AI_VECTOR3D_INL_INC
#include "aiVector3D.h"
#ifdef __cplusplus
#include "aiMatrix3x3.h"
#include "aiMatrix4x4.h"
/** Transformation of a vector by a 3x3 matrix */
inline aiVector3D operator * (const aiMatrix3x3& pMatrix, const aiVector3D& pVector)
{
aiVector3D res;
res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z;
res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z;
res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z;
return res;
}
/** Transformation of a vector by a 4x4 matrix */
inline aiVector3D operator * (const aiMatrix4x4& pMatrix, const aiVector3D& pVector)
{
aiVector3D res;
res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z + pMatrix.a4;
res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z + pMatrix.b4;
res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z + pMatrix.c4;
return res;
}
#endif // __cplusplus
#endif // AI_VECTOR3D_INL_INC

77
include/assimp.h 100644
View File

@ -0,0 +1,77 @@
/** @file Defines the C-API to the Asset Import Library. */
#ifndef AI_ASSIMP_H_INC
#define AI_ASSIMP_H_INC
#ifdef __cplusplus
extern "C" {
#endif
struct aiScene;
struct aiFileIO;
//enum aiOrigin;
// ---------------------------------------------------------------------------
/** Reads the given file and returns its content.
*
* If the call succeeds, the imported data is returned in an aiScene structure.
* The data is intended to be read-only, it stays property of the ASSIMP
* library and will be stable until aiReleaseImport() is called. After you're
* done with it, call aiReleaseImport() to free the resources associated with
* this file. If the import fails, NULL is returned instead. Call
* aiGetErrorString() to retrieve a human-readable error text.
* @param pFile Path and filename of the file to be imported,
* expected to be a null-terminated c-string.
* @param pFlags Optional post processing steps to be executed after
* a successful import. Provide a bitwise combination of the #aiPostProcessSteps
* flags.
* @return Pointer to the imported data or NULL if the import failed.
*/
// ---------------------------------------------------------------------------
const aiScene* aiImportFile( const char* pFile, unsigned int pFlags);
// ---------------------------------------------------------------------------
/** Reads the given file using user-defined I/O functions and returns
* its content.
*
* If the call succeeds, the imported data is returned in an aiScene structure.
* The data is intended to be read-only, it stays property of the ASSIMP
* library and will be stable until aiReleaseImport() is called. After you're
* done with it, call aiReleaseImport() to free the resources associated with
* this file. If the import fails, NULL is returned instead. Call
* aiGetErrorString() to retrieve a human-readable error text.
* @param pFile aiFileIO structure. All functions pointers must be
* initialized. aiFileIO::OpenFunc() and aiFileIO::CloseFunc()
* will be used to open other files in the fs if the asset to be
* loaded depends on them.
* @return Pointer to the imported data or NULL if the import failed.
*/
// ---------------------------------------------------------------------------
const aiScene* aiImportFileEx( const aiFileIO* pFile);
// ---------------------------------------------------------------------------
/** Releases all resources associated with the given import process.
*
* Call this function after you're done with the imported data.
* @param pScene The imported data to release.
*/
// ---------------------------------------------------------------------------
void aiReleaseImport( const aiScene* pScene);
// ---------------------------------------------------------------------------
/** Returns the error text of the last failed import process.
*
* @return A textual description of the error that occured at the last
* import process. NULL if there was no error.
*/
// ---------------------------------------------------------------------------
const char* aiGetErrorString();
#ifdef __cplusplus
}
#endif
#endif // AI_ASSIMP_H_INC

123
include/assimp.hpp 100644
View File

@ -0,0 +1,123 @@
/** @file Defines the CPP-API to the Asset Import Library. */
#ifndef AI_ASSIMP_HPP_INC
#define AI_ASSIMP_HPP_INC
#ifndef __cplusplus
#error This header requires C++ to be used.
#endif
#include <string>
#include <vector>
struct aiScene;
namespace Assimp
{
class BaseImporter;
class BaseProcess;
class IOStream;
class IOSystem;
// ---------------------------------------------------------------------------
/** The Importer class forms an C++ interface to the functionality of the
* Asset Import library.
*
* Create an object of this class and call ReadFile() to import a file.
* If the import succeeds, the function returns a pointer to the imported data.
* The data remains property of the object, it is intended to be accessed
* read-only. The imported data will be destroyed along with the Importer
* object. If the import failes, ReadFile() returns a NULL pointer. In this
* case you can retrieve a human-readable error description be calling
* GetErrorString().
*
* If you need the Importer to do custom file handling to access the files,
* implement IOSystem and IOStream and supply an instance of your custom IOSystem
* implementation by calling SetIOHandler() before calling ReadFile(). If you
* do not assign a custion IO handler, a default handler using the standard C++
* IO logic will be used.
*/
class Importer
{
public:
// -------------------------------------------------------------------
/** Constructor. Creates an empty importer object.
*
* Call ReadFile() to start the import process.
*/
Importer();
// -------------------------------------------------------------------
/** Destructor. The object kept ownership of the imported data,
* which now will be destroyed along with the object.
*/
~Importer();
// -------------------------------------------------------------------
/** Supplies a custom IO handler to the importer to open and access files.
* If you need the importer to use custion IO logic to access the files,
* you need to provide a custom implementation of IOSystem and IOFile
* to the importer. Then create an instance of your custion IOSystem
* implementation and supply it by this function.
*
* The Importer takes ownership of the object and will destroy it afterwards.
* The previously assigned handler will be deleted.
*
* @param pIOHandler The IO handler to be used in all file accesses of the Importer.
*/
void SetIOHandler( IOSystem* pIOHandler);
// -------------------------------------------------------------------
/** Reads the given file and returns its contents if successful.
*
* If the call succeeds, the contents of the file are returned as a
* pointer to an aiScene object. The returned data is intended to be
* read-only, the importer object keeps ownership of the data and will
* destroy it upon destruction. If the import failes, NULL is returned.
* A human-readable error description can be retrieved by calling
* GetErrorString().
* @param pFile Path and filename to the file to be imported.
* @param pFlags Optional post processing steps to be executed after
* a successful import. Provide a bitwise combination of the #aiPostProcessSteps
* flags.
* @return A pointer to the imported data, NULL if the import failed.
*/
const aiScene* ReadFile( const std::string& pFile, unsigned int pFlags);
// -------------------------------------------------------------------
/** Returns an error description of an error that occured in ReadFile().
*
* Returns an empty string if no error occured.
* @return A description of the last error, an empty string if no
* error occured.
*/
inline const std::string& GetErrorString() const
{ return mErrorString; }
private:
/** Empty copy constructor. */
Importer(const Importer &other);
protected:
/** IO handler to use for all file accesses. */
IOSystem* mIOHandler;
/** Format-specific importer worker objects -
* one for each format we can read. */
std::vector<BaseImporter*> mImporter;
/** Post processing steps we can apply at the imported data. */
std::vector<BaseProcess*> mPostProcessingSteps;
/** The imported data, if ReadFile() was successful,
* NULL otherwise. */
aiScene* mScene;
/** The error description, if there was one. */
std::string mErrorString;
};
} // End of namespace Assimp
#endif // AI_ASSIMP_HPP_INC

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

View File

@ -0,0 +1 @@
All textures are from cgtextures.com and are free for commercial use

View File

@ -0,0 +1,135 @@
//-------------------------------------------------------------------------------
/**
* This program is distributed under the terms of the GNU Lesser General
* Public License (LGPL).
*
* ASSIMP Viewer Utility
*
*/
//-------------------------------------------------------------------------------
#if (!defined AV_ASSET_HELPER_H_INCLUDED)
#define AV_ASSET_HELPER_H_INCLUDED
//-------------------------------------------------------------------------------
/** \brief Class to wrap ASSIMP's asset output structures
*/
//-------------------------------------------------------------------------------
class AssetHelper
{
public:
//---------------------------------------------------------------
// default vertex data structure
// (even if tangents, bitangents or normals aren't
// required by the shader they will be committed to the GPU)
//---------------------------------------------------------------
struct Vertex
{
aiVector3D vPosition;
aiVector3D vNormal;
D3DCOLOR dColorDiffuse;
aiVector3D vTangent;
aiVector3D vBitangent;
aiVector2D vTextureUV;
// retrieves the FVF code of the vertex type
static DWORD GetFVF()
{
return D3DFVF_DIFFUSE | D3DFVF_XYZ | D3DFVF_NORMAL |
D3DFVF_TEX1 | D3DFVF_TEX2 | D3DFVF_TEX3 |
D3DFVF_TEXCOORDSIZE3(0) | D3DFVF_TEXCOORDSIZE3(1);
}
};
//---------------------------------------------------------------
// FVF vertex structure used for normals
//---------------------------------------------------------------
struct LineVertex
{
aiVector3D vPosition;
DWORD dColorDiffuse;
// retrieves the FVF code of the vertex type
static DWORD GetFVF()
{
return D3DFVF_DIFFUSE | D3DFVF_XYZ;
}
};
//---------------------------------------------------------------
// Helper class to store GPU related resources created for
// a given aiMesh
//---------------------------------------------------------------
class MeshHelper
{
public:
MeshHelper ()
:
piVB (NULL),
piIB (NULL),
piVBNormals (NULL),
piDiffuseTexture (NULL),
piSpecularTexture (NULL),
piAmbientTexture (NULL),
piNormalTexture (NULL),
piEmissiveTexture (NULL),
piOpacityTexture (NULL),
bSharedFX(false) {}
~MeshHelper ()
{
// NOTE: This is done in DeleteAssetData()
// TODO: Make this a proper d'tor
}
// shading mode to use. Either Lambert or otherwise phong
// will be used in every case
aiShadingMode eShadingMode;
// vertex buffer
IDirect3DVertexBuffer9* piVB;
// index buffer
IDirect3DIndexBuffer9* piIB;
// vertex buffer to be used to draw vertex normals
// (vertex normals are generated in every case)
IDirect3DVertexBuffer9* piVBNormals;
// shader to be used
ID3DXEffect* piEffect;
bool bSharedFX;
// material textures
IDirect3DTexture9* piDiffuseTexture;
IDirect3DTexture9* piSpecularTexture;
IDirect3DTexture9* piAmbientTexture;
IDirect3DTexture9* piEmissiveTexture;
IDirect3DTexture9* piNormalTexture;
IDirect3DTexture9* piOpacityTexture;
// material colors
D3DXVECTOR4 vDiffuseColor;
D3DXVECTOR4 vSpecularColor;
D3DXVECTOR4 vAmbientColor;
D3DXVECTOR4 vEmissiveColor;
// opacity for the material
float fOpacity;
// shininess for the material
float fShininess;
};
// One instance per aiMesh in the globally loaded asset
MeshHelper** apcMeshes;
// Scene wrapper instance
const aiScene* pcScene;
};
#endif // !! IG

Some files were not shown because too many files have changed in this diff Show More