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-9d2fd5bffc1fpull/1/head
commit
b76f999cb7
|
@ -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).
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
}
|
||||||
|
// ---------------------------------------------------------------------------
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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 }
|
||||||
|
};
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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];
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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++;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
@ -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?>
|
|
@ -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_ ]
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
@ -0,0 +1 @@
|
||||||
|
All textures are from cgtextures.com and are free for commercial use
|
|
@ -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
Loading…
Reference in New Issue