MD5 bugfix.

WIP version of the OptimizeGraph-Step.
Added hashes to some string routines (speedup).
Improved property system for float and strings.
Refactoring code for computing normals from smoothinggroups.
Implemented C-ASSIMP.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@116 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2008-08-28 17:35:36 +00:00
parent 51bfc46c1d
commit 38d04b6796
62 changed files with 3399 additions and 1226 deletions

View File

@ -59,10 +59,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
#ifdef _MSC_VER
# define sprintf sprintf_s
#endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ReplaceDefaultMaterial() void Dot3DSImporter::ReplaceDefaultMaterial()
{ {
@ -130,70 +126,66 @@ void Dot3DSImporter::ReplaceDefaultMaterial()
} }
return; return;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::CheckIndices(Dot3DS::Mesh* sMesh) void Dot3DSImporter::CheckIndices(Dot3DS::Mesh& sMesh)
{ {
for (std::vector< Dot3DS::Face >::iterator for (std::vector< Dot3DS::Face >::iterator
i = sMesh->mFaces.begin(); i = sMesh.mFaces.begin();
i != sMesh->mFaces.end();++i) i != sMesh.mFaces.end();++i)
{ {
// check whether all indices are in range // check whether all indices are in range
if ((*i).mIndices[0] >= sMesh->mPositions.size()) for (unsigned int a = 0; a < 3;++a)
{ {
DefaultLogger::get()->warn("Face index overflow in 3DS file (#1)"); if ((*i).mIndices[a] >= sMesh.mPositions.size())
(*i).mIndices[0] = (uint32_t)sMesh->mPositions.size()-1; {
} DefaultLogger::get()->warn("3DS: Face index overflow)");
if ((*i).mIndices[1] >= sMesh->mPositions.size()) (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1;
{ }
DefaultLogger::get()->warn("Face index overflow in 3DS file (#2)");
(*i).mIndices[1] = (uint32_t)sMesh->mPositions.size()-1;
}
if ((*i).mIndices[2] >= sMesh->mPositions.size())
{
DefaultLogger::get()->warn("Face index overflow in 3DS file (#3)");
(*i).mIndices[2] = (uint32_t)sMesh->mPositions.size()-1;
} }
} }
return; return;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::MakeUnique(Dot3DS::Mesh* sMesh) void Dot3DSImporter::MakeUnique(Dot3DS::Mesh& sMesh)
{ {
unsigned int iBase = 0; unsigned int iBase = 0;
std::vector<aiVector3D> vNew; std::vector<aiVector3D> vNew;
std::vector<aiVector2D> vNew2; std::vector<aiVector2D> vNew2;
vNew.resize(sMesh->mFaces.size() * 3); vNew.resize(sMesh.mFaces.size() * 3);
if (sMesh->mTexCoords.size())vNew2.resize(sMesh->mFaces.size() * 3); if (sMesh.mTexCoords.size())vNew2.resize(sMesh.mFaces.size() * 3);
for (unsigned int i = 0; i < sMesh->mFaces.size();++i) for (unsigned int i = 0; i < sMesh.mFaces.size();++i)
{ {
uint32_t iTemp1,iTemp2; uint32_t iTemp1,iTemp2;
// positions // positions
vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].mIndices[2]]; vNew[iBase] = sMesh.mPositions[sMesh.mFaces[i].mIndices[2]];
iTemp1 = iBase++; iTemp1 = iBase++;
vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].mIndices[1]]; vNew[iBase] = sMesh.mPositions[sMesh.mFaces[i].mIndices[1]];
iTemp2 = iBase++; iTemp2 = iBase++;
vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].mIndices[0]]; vNew[iBase] = sMesh.mPositions[sMesh.mFaces[i].mIndices[0]];
// texture coordinates // texture coordinates
if (sMesh->mTexCoords.size()) if (sMesh.mTexCoords.size())
{ {
vNew2[iTemp1] = sMesh->mTexCoords[sMesh->mFaces[i].mIndices[2]]; vNew2[iTemp1] = sMesh.mTexCoords[sMesh.mFaces[i].mIndices[2]];
vNew2[iTemp2] = sMesh->mTexCoords[sMesh->mFaces[i].mIndices[1]]; vNew2[iTemp2] = sMesh.mTexCoords[sMesh.mFaces[i].mIndices[1]];
vNew2[iBase] = sMesh->mTexCoords[sMesh->mFaces[i].mIndices[0]]; vNew2[iBase] = sMesh.mTexCoords[sMesh.mFaces[i].mIndices[0]];
} }
sMesh->mFaces[i].mIndices[2] = iBase++; sMesh.mFaces[i].mIndices[2] = iBase++;
sMesh->mFaces[i].mIndices[0] = iTemp1; sMesh.mFaces[i].mIndices[0] = iTemp1;
sMesh->mFaces[i].mIndices[1] = iTemp2; sMesh.mFaces[i].mIndices[1] = iTemp2;
} }
sMesh->mPositions = vNew; sMesh.mPositions = vNew;
sMesh->mTexCoords = vNew2; sMesh.mTexCoords = vNew2;
return; return;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat, void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
MaterialHelper& mat) MaterialHelper& mat)
@ -201,14 +193,14 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
// NOTE: Pass the background image to the viewer by bypassing the // NOTE: Pass the background image to the viewer by bypassing the
// material system. This is an evil hack, never do it again! // material system. This is an evil hack, never do it again!
if (0 != this->mBackgroundImage.length() && this->bHasBG) if (0 != this->mBackgroundImage.length() && this->bHasBG)
{ {
aiString tex; aiString tex;
tex.Set( this->mBackgroundImage); tex.Set( this->mBackgroundImage);
mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE); mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
// be sure this is only done for the first material // be sure this is only done for the first material
this->mBackgroundImage = std::string(""); this->mBackgroundImage = std::string("");
} }
// At first add the base ambient color of the // At first add the base ambient color of the
// scene to the material // scene to the material
@ -250,7 +242,7 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
// two sided rendering? // two sided rendering?
if (oldMat.mTwoSided) if (oldMat.mTwoSided)
{ {
int i = 0; int i = 1;
mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED); mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
} }
@ -268,8 +260,6 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
eShading = aiShadingMode_Gouraud; break; eShading = aiShadingMode_Gouraud; break;
// assume cook-torrance shading for metals. // 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::Phong : case Dot3DS::Dot3DSFile::Phong :
eShading = aiShadingMode_Phong; break; eShading = aiShadingMode_Phong; break;
@ -409,20 +399,7 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
a = (*i).mFaceMaterials.begin(); a = (*i).mFaceMaterials.begin();
a != (*i).mFaceMaterials.end();++a,++iNum) a != (*i).mFaceMaterials.end();++a,++iNum)
{ {
// check range aiSplit[*a].push_back(iNum);
// FIX: shouldn't be necessary anymore, has been moved to ReplaceDefaultMaterial()
#if 0
if ((*a) >= this->mScene->mMaterials.size())
{
DefaultLogger::get()->error("3DS face material index is out of range");
// use the last material instead
// TODO: assign the default material index
aiSplit[this->mScene->mMaterials.size()-1].push_back(iNum);
}
else
#endif
aiSplit[*a].push_back(iNum);
} }
// now generate submeshes // now generate submeshes
bool bFirst = true; bool bFirst = true;
@ -506,12 +483,9 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
pcOut->mNumMeshes = (unsigned int)avOutMeshes.size(); pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes](); pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) for (unsigned int a = 0; a < pcOut->mNumMeshes;++a)
{
pcOut->mMeshes[a] = avOutMeshes[a]; pcOut->mMeshes[a] = avOutMeshes[a];
}
if (!iFaceCnt) if (!iFaceCnt)throw new ImportErrorException("No faces loaded. The mesh is empty");
throw new ImportErrorException("No faces loaded. The mesh is empty");
// for each material in the scene we need to setup the UV source // for each material in the scene we need to setup the UV source
// set for each texture // set for each texture
@ -519,6 +493,7 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
TextureTransform::SetupMatUVSrc( pcOut->mMaterials[a], &this->mScene->mMaterials[a] ); TextureTransform::SetupMatUVSrc( pcOut->mMaterials[a], &this->mScene->mMaterials[a] );
return; return;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pcIn) void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pcIn)
{ {
@ -533,11 +508,8 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node*
ai_assert(NULL != pcMesh); ai_assert(NULL != pcMesh);
// do case independent comparisons here, just for safety // do case independent comparisons here, just for safety
if (pcIn->mName.length() == pcMesh->mName.length() && if (!ASSIMP_stricmp(pcIn->mName,pcMesh->mName))
!ASSIMP_stricmp(pcIn->mName.c_str(),pcMesh->mName.c_str()))
{
iArray.push_back(a); iArray.push_back(a);
}
} }
if (!iArray.empty()) if (!iArray.empty())
{ {
@ -568,10 +540,11 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node*
pvCurrent->y -= pivot.y; pvCurrent->y -= pivot.y;
pvCurrent->z -= pivot.z; pvCurrent->z -= pivot.z;
*pvCurrent = mTrafo * (*pvCurrent); *pvCurrent = mTrafo * (*pvCurrent);
std::swap( pvCurrent->y, pvCurrent->z ); //std::swap( pvCurrent->y, pvCurrent->z );
++pvCurrent; ++pvCurrent;
} }
} }
#if 0
else else
{ {
while (pvCurrent != pvEnd) while (pvCurrent != pvEnd)
@ -580,6 +553,7 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node*
++pvCurrent; ++pvCurrent;
} }
} }
#endif
pcOut->mMeshes[i] = iIndex; pcOut->mMeshes[i] = iIndex;
} }
} }
@ -658,9 +632,7 @@ void Dot3DSImporter::GenerateNodeGraph(aiScene* pcOut)
// if the root node is a default node setup a name for it // if the root node is a default node setup a name for it
if (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') if (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$')
{
pcOut->mRootNode->mName.Set("<root>"); pcOut->mRootNode->mName.Set("<root>");
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ConvertScene(aiScene* pcOut) void Dot3DSImporter::ConvertScene(aiScene* pcOut)

View File

@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------- ----------------------------------------------------------------------
*/ */
/** @file Defines the helper data structures for importing 3DS files. /** @file Defines helper data structures for the import of 3DS files.
http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */ http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */
#ifndef AI_3DSFILEHELPER_H_INC #ifndef AI_3DSFILEHELPER_H_INC
@ -55,20 +55,18 @@ http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */
#include "../include/aiMaterial.h" #include "../include/aiMaterial.h"
#include "SpatialSort.h" #include "SpatialSort.h"
#include "SmoothingGroups.h"
namespace Assimp { namespace Assimp {
namespace Dot3DS { namespace Dot3DS {
#include "./Compiler/pushpack1.h" #include "./Compiler/pushpack1.h"
#ifdef _MSC_VER
# define sprintf sprintf_s
#endif
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** Dot3DSFile class: Helper class for loading 3ds files. Defines chunks /** Dot3DSFile class: Helper class for loading 3ds files. Defines chunks
* and data structures. * and data structures.
*/ */
// ---------------------------------------------------------------------------
class Dot3DSFile class Dot3DSFile
{ {
public: public:
@ -91,32 +89,27 @@ public:
typedef enum typedef enum
{ {
// translated to gouraud shading with wireframe active // translated to gouraud shading with wireframe active
Wire = 0, Wire = 0x0,
// if this material is set, no vertex normals will // if this material is set, no vertex normals will
// be calculated for the model. Face normals + gouraud // be calculated for the model. Face normals + gouraud
Flat = 1, Flat = 0x1,
// standard gouraud shading // standard gouraud shading
Gouraud = 2, Gouraud = 0x2,
// phong shading // phong shading
Phong = 3, Phong = 0x3,
// cooktorrance or anistropic phong shading ... // cooktorrance or anistropic phong shading ...
// the exact meaning is unknown, if you know it // the exact meaning is unknown, if you know it
// feel free to tell me ;-) // feel free to tell me ;-)
Metal = 4, Metal = 0x4,
// required by the ASE loader // required by the ASE loader
Blinn = 5 Blinn = 0x5
} shadetype3ds; } shadetype3ds;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// enum for all chunks in 3ds files. Unused
// ones are commented, list is not complete since
// there are many undocumented chunks
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
enum enum
{ {
@ -316,26 +309,10 @@ public:
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** Helper structure representing a 3ds mesh face */ /** Helper structure representing a 3ds mesh face */
struct Face struct Face : public FaceWithSmoothingGroup
{ {
Face() : iSmoothGroup(0), bFlipped(false) //! Specifies that the face normal must be flipped.
{ //! todo: do we really need this?
// let the rest uninitialized for performance
this->mIndices[0] = 0;
this->mIndices[1] = 0;
this->mIndices[2] = 0;
}
//! Indices. .3ds is using uint16. However, after
//! an unique vrtex set has been geneerated it might
//! be an index becomes > 2^16
uint32_t mIndices[3];
//! specifies to which smoothing group the face belongs to
uint32_t iSmoothGroup;
//! Specifies that the face normal must be flipped
bool bFlipped; bool bFlipped;
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -442,7 +419,7 @@ struct Material
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** Helper structure to represent a 3ds file mesh */ /** Helper structure to represent a 3ds file mesh */
struct Mesh struct Mesh : public MeshWithSmoothingGroups<Dot3DS::Face>
{ {
//! Default constructor //! Default constructor
Mesh() Mesh()
@ -457,21 +434,12 @@ struct Mesh
//! Name of the mesh //! Name of the mesh
std::string mName; std::string mName;
//! Vertex positions
std::vector<aiVector3D> mPositions;
//! Face lists
std::vector<Face> mFaces;
//! Texture coordinates //! Texture coordinates
std::vector<aiVector2D> mTexCoords; std::vector<aiVector2D> mTexCoords;
//! Face materials //! Face materials
std::vector<unsigned int> mFaceMaterials; std::vector<unsigned int> mFaceMaterials;
//! Normal vectors
std::vector<aiVector3D> mNormals;
//! Local transformation matrix //! Local transformation matrix
aiMatrix4x4 mMat; aiMatrix4x4 mMat;
}; };

View File

@ -50,23 +50,48 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// public ASSIMP headers // public ASSIMP headers
#include "../include/DefaultLogger.h" #include "../include/DefaultLogger.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h" #include "../include/aiScene.h"
#include "../include/aiAssert.h" #include "../include/aiAssert.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/assimp.hpp" #include "../include/assimp.hpp"
// boost headers // boost headers
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
using namespace Assimp; using namespace Assimp;
#if (!defined ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG) // begin a chunk: parse it, validate its length, get a pointer to its end
# define ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG \ #define ASSIMP_3DS_BEGIN_CHUNK() \
"WARNING: Size of chunk data plus size of " \ const Dot3DSFile::Chunk* psChunk; \
"subordinate chunks is larger than the size " \ this->ReadChunk(&psChunk); \
"specified in the top-level chunk header." const unsigned char* pcCur = this->mCurrent; \
const unsigned char* pcCurNext = pcCur + (psChunk->Size \
- sizeof(Dot3DSFile::Chunk));
// process the end of a chunk and return if the end of the file is reached
#define ASSIMP_3DS_END_CHUNK() \
this->mCurrent = pcCurNext; \
piRemaining -= psChunk->Size; \
if (0 >= piRemaining)return;
// check whether the size of all subordinate chunks of a chunks is
// not larger than the size of the chunk itself
#ifdef _DEBUG
# define ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG \
"Size of chunk data plus size of subordinate chunks is " \
"larger than the size specified in the top-level chunk header."
# define ASSIMP_3DS_VALIDATE_CHUNK_SIZE() \
if (pcCurNext < this->mCurrent) \
{ \
DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); \
pcCurNext = this->mCurrent; \
}
#else
# define ASSIMP_3DS_VALIDATE_CHUNK_SIZE()
#endif #endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -92,7 +117,7 @@ bool Dot3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) co
return false; return false;
std::string extension = pFile.substr( pos); std::string extension = pFile.substr( pos);
// not brilliant but working ;-) // not brillant but working ;-)
if( extension == ".3ds" || extension == ".3DS" || if( extension == ".3ds" || extension == ".3DS" ||
extension == ".3Ds" || extension == ".3dS") extension == ".3Ds" || extension == ".3dS")
return true; return true;
@ -103,7 +128,7 @@ bool Dot3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) co
// Setup configuration properties // Setup configuration properties
void Dot3DSImporter::SetupProperties(const Importer* pImp) void Dot3DSImporter::SetupProperties(const Importer* pImp)
{ {
this->configSkipPivot = pImp->GetProperty(AI_CONFIG_IMPORT_3DS_IGNORE_PIVOT,0) ? true : false; this->configSkipPivot = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_3DS_IGNORE_PIVOT,0) ? true : false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure. // Imports the given file into the given scene structure.
@ -151,11 +176,11 @@ void Dot3DSImporter::InternReadFile(
i != this->mScene->mMeshes.end();++i) i != this->mScene->mMeshes.end();++i)
{ {
// TODO: see function body // TODO: see function body
this->CheckIndices(&(*i)); this->CheckIndices(*i);
this->MakeUnique(&(*i)); this->MakeUnique(*i);
// first generate normals for the mesh // first generate normals for the mesh
this->GenNormals(&(*i)); ComputeNormalsWithSmoothingsGroups<Dot3DS::Face>(*i);
} }
// Apply scaling and offsets to all texture coordinates // Apply scaling and offsets to all texture coordinates
@ -179,11 +204,7 @@ void Dot3DSImporter::InternReadFile(
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ApplyMasterScale(aiScene* pScene) void Dot3DSImporter::ApplyMasterScale(aiScene* pScene)
{ {
// NOTE: Some invalid files have masterscale set to 0.0 if (!this->mMasterScale)this->mMasterScale = 1.0f;
if (0.0f == this->mMasterScale)
{
this->mMasterScale = 1.0f;
}
else this->mMasterScale = 1.0f / this->mMasterScale; else this->mMasterScale = 1.0f / this->mMasterScale;
// construct an uniform scaling matrix and multiply with it // construct an uniform scaling matrix and multiply with it
@ -200,9 +221,8 @@ void Dot3DSImporter::ReadChunk(const Dot3DSFile::Chunk** p_ppcOut)
// read chunk // read chunk
if (this->mCurrent >= this->mLast) if (this->mCurrent >= this->mLast)
{
throw new ImportErrorException("Unexpected end of file, can't read chunk header"); throw new ImportErrorException("Unexpected end of file, can't read chunk header");
}
const uintptr_t iDiff = this->mLast - this->mCurrent; const uintptr_t iDiff = this->mLast - this->mCurrent;
if (iDiff < sizeof(Dot3DSFile::Chunk)) if (iDiff < sizeof(Dot3DSFile::Chunk))
{ {
@ -211,23 +231,15 @@ void Dot3DSImporter::ReadChunk(const Dot3DSFile::Chunk** p_ppcOut)
} }
*p_ppcOut = (const Dot3DSFile::Chunk*) this->mCurrent; *p_ppcOut = (const Dot3DSFile::Chunk*) this->mCurrent;
if ((**p_ppcOut).Size + this->mCurrent > this->mLast) if ((**p_ppcOut).Size + this->mCurrent > this->mLast)
{
throw new ImportErrorException("Unexpected end of file, can't read chunk footer"); throw new ImportErrorException("Unexpected end of file, can't read chunk footer");
}
this->mCurrent += sizeof(Dot3DSFile::Chunk); this->mCurrent += sizeof(Dot3DSFile::Chunk);
return; return;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ParseMainChunk(int& piRemaining) void Dot3DSImporter::ParseMainChunk(int& piRemaining)
{ {
const Dot3DSFile::Chunk* psChunk; ASSIMP_3DS_BEGIN_CHUNK();
this->ReadChunk(&psChunk);
const unsigned char* pcCur = this->mCurrent;
const unsigned char* pcCurNext = pcCur + (psChunk->Size
- sizeof(Dot3DSFile::Chunk));
// get chunk type // get chunk type
int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@ -237,27 +249,14 @@ void Dot3DSImporter::ParseMainChunk(int& piRemaining)
this->ParseEditorChunk(iRemaining); this->ParseEditorChunk(iRemaining);
break; break;
}; };
if (pcCurNext < this->mCurrent) ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
{ ASSIMP_3DS_END_CHUNK();
// place an error message. If we crash the programmer
// will be able to find it
DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
pcCurNext = this->mCurrent;
}
// Go to the starting position of the next top-level chunk
this->mCurrent = pcCurNext;
piRemaining -= psChunk->Size;
if (0 >= piRemaining)return;
return this->ParseMainChunk(piRemaining); return this->ParseMainChunk(piRemaining);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ParseEditorChunk(int& piRemaining) void Dot3DSImporter::ParseEditorChunk(int& piRemaining)
{ {
const Dot3DSFile::Chunk* psChunk; ASSIMP_3DS_BEGIN_CHUNK();
this->ReadChunk(&psChunk);
const unsigned char* pcCur = this->mCurrent;
const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
// get chunk type // get chunk type
int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@ -291,27 +290,14 @@ void Dot3DSImporter::ParseEditorChunk(int& piRemaining)
} }
break; break;
}; };
if (pcCurNext < this->mCurrent) ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
{ ASSIMP_3DS_END_CHUNK();
// place an error message. If we crash the programmer
// will be able to find it
DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
pcCurNext = this->mCurrent;
}
// Go to the starting position of the next top-level chunk
this->mCurrent = pcCurNext;
piRemaining -= psChunk->Size;
if (0 >= piRemaining)return;
return this->ParseEditorChunk(piRemaining); return this->ParseEditorChunk(piRemaining);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ParseObjectChunk(int& piRemaining) void Dot3DSImporter::ParseObjectChunk(int& piRemaining)
{ {
const Dot3DSFile::Chunk* psChunk; ASSIMP_3DS_BEGIN_CHUNK();
this->ReadChunk(&psChunk);
const unsigned char* pcCur = this->mCurrent;
const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
const unsigned char* sz = this->mCurrent; const unsigned char* sz = this->mCurrent;
unsigned int iCnt = 0; unsigned int iCnt = 0;
@ -384,17 +370,8 @@ void Dot3DSImporter::ParseObjectChunk(int& piRemaining)
break; break;
}; };
if (pcCurNext < this->mCurrent) ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
{ ASSIMP_3DS_END_CHUNK();
// place an error message. If we crash the programmer
// will be able to find it
DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
pcCurNext = this->mCurrent;
}
// Go to the starting position of the next top-level chunk
this->mCurrent = pcCurNext;
piRemaining -= psChunk->Size;
if (0 >= piRemaining)return;
return this->ParseObjectChunk(piRemaining); return this->ParseObjectChunk(piRemaining);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -409,11 +386,7 @@ void Dot3DSImporter::SkipChunk()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ParseChunk(int& piRemaining) void Dot3DSImporter::ParseChunk(int& piRemaining)
{ {
const Dot3DSFile::Chunk* psChunk; ASSIMP_3DS_BEGIN_CHUNK();
this->ReadChunk(&psChunk);
const unsigned char* pcCur = this->mCurrent;
const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
// get chunk type // get chunk type
int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@ -424,28 +397,14 @@ void Dot3DSImporter::ParseChunk(int& piRemaining)
this->ParseMeshChunk(iRemaining); this->ParseMeshChunk(iRemaining);
break; break;
}; };
if (pcCurNext < this->mCurrent) ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
{ ASSIMP_3DS_END_CHUNK();
// place an error message. If we crash the programmer
// will be able to find it
DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
pcCurNext = this->mCurrent;
}
// Go to the starting position of the next top-level chunk
this->mCurrent = pcCurNext;
piRemaining -= psChunk->Size;
if (0 >= piRemaining)return;
return this->ParseChunk(piRemaining); return this->ParseChunk(piRemaining);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ParseKeyframeChunk(int& piRemaining) void Dot3DSImporter::ParseKeyframeChunk(int& piRemaining)
{ {
const Dot3DSFile::Chunk* psChunk; ASSIMP_3DS_BEGIN_CHUNK();
this->ReadChunk(&psChunk);
const unsigned char* pcCur = this->mCurrent;
const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
// get chunk type // get chunk type
int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@ -456,18 +415,8 @@ void Dot3DSImporter::ParseKeyframeChunk(int& piRemaining)
this->ParseHierarchyChunk(iRemaining); this->ParseHierarchyChunk(iRemaining);
break; break;
}; };
if (pcCurNext < this->mCurrent) ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
{ ASSIMP_3DS_END_CHUNK();
// place an error message. If we crash the programmer
// will be able to find it
DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
pcCurNext = this->mCurrent;
}
// Go to the starting position of the next top-level chunk
this->mCurrent = pcCurNext;
piRemaining -= psChunk->Size;
if (0 >= piRemaining)return;
return this->ParseKeyframeChunk(piRemaining); return this->ParseKeyframeChunk(piRemaining);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -489,14 +438,7 @@ void Dot3DSImporter::InverseNodeSearch(Dot3DS::Node* pcNode,Dot3DS::Node* pcCurr
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining) void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
{ {
const Dot3DSFile::Chunk* psChunk; ASSIMP_3DS_BEGIN_CHUNK();
this->ReadChunk(&psChunk);
const unsigned char* pcCur = this->mCurrent;
const unsigned char* pcCurNext = pcCur + (psChunk->Size
- sizeof(Dot3DSFile::Chunk));
// get chunk type // get chunk type
const unsigned char* sz = (unsigned char*)this->mCurrent; const unsigned char* sz = (unsigned char*)this->mCurrent;
@ -548,6 +490,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
// pivot = origin of rotation and scaling // pivot = origin of rotation and scaling
this->mCurrentNode->vPivot = *((const aiVector3D*)this->mCurrent); this->mCurrentNode->vPivot = *((const aiVector3D*)this->mCurrent);
std::swap(this->mCurrentNode->vPivot.y,this->mCurrentNode->vPivot.z);
this->mCurrent += sizeof(aiVector3D); this->mCurrent += sizeof(aiVector3D);
break; break;
@ -576,8 +519,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
uint16_t sNum = *((const uint16_t*)mCurrent); uint16_t sNum = *((const uint16_t*)mCurrent);
this->mCurrent += sizeof(uint16_t); this->mCurrent += sizeof(uint16_t);
aiVectorKey v; aiVectorKey v;v.mTime = (double)sNum;
v.mTime = (double)sNum;
this->mCurrent += sizeof(uint32_t); this->mCurrent += sizeof(uint32_t);
v.mValue = *((const aiVector3D*)this->mCurrent); v.mValue = *((const aiVector3D*)this->mCurrent);
@ -591,7 +533,8 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;} if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;}
} }
// add the new keyframe // add the new keyframe
if (v.mTime != -10e10f)this->mCurrentNode->aPositionKeys.push_back(v); if (v.mTime != -10e10f)
this->mCurrentNode->aPositionKeys.push_back(v);
} }
break; break;
@ -618,9 +561,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
uint16_t sNum = *((const uint16_t*)mCurrent); uint16_t sNum = *((const uint16_t*)mCurrent);
this->mCurrent += sizeof(uint16_t); this->mCurrent += sizeof(uint16_t);
aiQuatKey v; aiQuatKey v;v.mTime = (double)sNum;
v.mTime = (double)sNum;
this->mCurrent += sizeof(uint32_t); this->mCurrent += sizeof(uint32_t);
float fRadians = *((const float*)this->mCurrent); float fRadians = *((const float*)this->mCurrent);
@ -640,7 +581,8 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;} if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;}
} }
// add the new keyframe // add the new keyframe
if (v.mTime != -10e10f)this->mCurrentNode->aRotationKeys.push_back(v); if (v.mTime != -10e10f)
this->mCurrentNode->aRotationKeys.push_back(v);
} }
break; break;
@ -698,31 +640,16 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
break; break;
#endif #endif
}; };
if (pcCurNext < this->mCurrent) ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
{ ASSIMP_3DS_END_CHUNK();
// place an error message. If we crash the programmer
// will be able to find it
DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
pcCurNext = this->mCurrent;
}
// Go to the starting position of the next top-level chunk
this->mCurrent = pcCurNext;
piRemaining -= psChunk->Size;
if (0 >= piRemaining)return;
return this->ParseHierarchyChunk(piRemaining); return this->ParseHierarchyChunk(piRemaining);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ParseFaceChunk(int& piRemaining) void Dot3DSImporter::ParseFaceChunk(int& piRemaining)
{ {
const Dot3DSFile::Chunk* psChunk; ASSIMP_3DS_BEGIN_CHUNK();
Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back(); Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back();
this->ReadChunk(&psChunk);
const unsigned char* pcCur = this->mCurrent;
const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
// get chunk type // get chunk type
const unsigned char* sz = this->mCurrent; const unsigned char* sz = this->mCurrent;
uint32_t iCnt = 0,iTemp; uint32_t iCnt = 0,iTemp;
@ -796,31 +723,16 @@ void Dot3DSImporter::ParseFaceChunk(int& piRemaining)
break; break;
}; };
if (pcCurNext < this->mCurrent) ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
{ ASSIMP_3DS_END_CHUNK();
// place an error message. If we crash the programmer
// will be able to find it
DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
pcCurNext = this->mCurrent;
}
// Go to the starting position of the next chunk on this level
this->mCurrent = pcCurNext;
piRemaining -= psChunk->Size;
if (0 >= piRemaining)return;
return ParseFaceChunk(piRemaining); return ParseFaceChunk(piRemaining);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ParseMeshChunk(int& piRemaining) void Dot3DSImporter::ParseMeshChunk(int& piRemaining)
{ {
const Dot3DSFile::Chunk* psChunk; ASSIMP_3DS_BEGIN_CHUNK();
Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back(); Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back();
this->ReadChunk(&psChunk);
const unsigned char* pcCur = this->mCurrent;
const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
// get chunk type // get chunk type
const unsigned char* sz = this->mCurrent; const unsigned char* sz = this->mCurrent;
unsigned int iCnt = 0; unsigned int iCnt = 0;
@ -837,7 +749,7 @@ void Dot3DSImporter::ParseMeshChunk(int& piRemaining)
{ {
mMesh.mPositions.push_back(*((aiVector3D*)this->mCurrent)); mMesh.mPositions.push_back(*((aiVector3D*)this->mCurrent));
aiVector3D& v = mMesh.mPositions.back(); aiVector3D& v = mMesh.mPositions.back();
//std::swap( v.y, v.z); std::swap( v.y, v.z);
//v.y *= -1.0f; //v.y *= -1.0f;
this->mCurrent += sizeof(aiVector3D); this->mCurrent += sizeof(aiVector3D);
} }
@ -924,28 +836,14 @@ void Dot3DSImporter::ParseMeshChunk(int& piRemaining)
break; break;
}; };
if (pcCurNext < this->mCurrent) ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
{ ASSIMP_3DS_END_CHUNK();
// place an error message. If we crash the programmer
// will be able to find it
DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
pcCurNext = this->mCurrent;
}
// Go to the starting position of the next chunk on this level
this->mCurrent = pcCurNext;
piRemaining -= psChunk->Size;
if (0 >= piRemaining)return;
return ParseMeshChunk(piRemaining); return ParseMeshChunk(piRemaining);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ParseMaterialChunk(int& piRemaining) void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
{ {
const Dot3DSFile::Chunk* psChunk; ASSIMP_3DS_BEGIN_CHUNK();
this->ReadChunk(&psChunk);
const unsigned char* pcCur = this->mCurrent;
const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
// get chunk type // get chunk type
const unsigned char* sz = this->mCurrent; const unsigned char* sz = this->mCurrent;
@ -958,9 +856,8 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
case Dot3DSFile::CHUNK_MAT_MATNAME: case Dot3DSFile::CHUNK_MAT_MATNAME:
// string in file is zero-terminated, // string in file is zero-terminated,
// this should be no problem. However, validate whether // this should be no problem. However, validate whether it overlaps
// it overlaps the end of the chunk, if yes we should // the end of the chunk, if yes we should truncate it.
// truncate it.
while (*sz++ != '\0') while (*sz++ != '\0')
{ {
if (sz > pcCurNext-1) if (sz > pcCurNext-1)
@ -1017,8 +914,7 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
pcf = &this->mScene->mMaterials.back().mTransparency; pcf = &this->mScene->mMaterials.back().mTransparency;
*pcf = this->ParsePercentageChunk(); *pcf = this->ParsePercentageChunk();
// NOTE: transparency, not opacity // NOTE: transparency, not opacity
if (is_qnan(*pcf)) if (is_qnan(*pcf))*pcf = 1.0f;
*pcf = 1.0f;
else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f; else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f;
break; break;
@ -1037,16 +933,14 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
case Dot3DSFile::CHUNK_MAT_SHININESS: case Dot3DSFile::CHUNK_MAT_SHININESS:
pcf = &this->mScene->mMaterials.back().mSpecularExponent; pcf = &this->mScene->mMaterials.back().mSpecularExponent;
*pcf = this->ParsePercentageChunk(); *pcf = this->ParsePercentageChunk();
if (is_qnan(*pcf)) if (is_qnan(*pcf))*pcf = 0.0f;
*pcf = 0.0f;
else *pcf *= (float)0xFFFF; else *pcf *= (float)0xFFFF;
break; break;
case Dot3DSFile::CHUNK_MAT_SHININESS_PERCENT: case Dot3DSFile::CHUNK_MAT_SHININESS_PERCENT:
pcf = &this->mScene->mMaterials.back().mShininessStrength; pcf = &this->mScene->mMaterials.back().mShininessStrength;
*pcf = this->ParsePercentageChunk(); *pcf = this->ParsePercentageChunk();
if (is_qnan(*pcf)) if (is_qnan(*pcf))*pcf = 0.0f;
*pcf = 0.0f;
else *pcf *= (float)0xffff / 100.0f; else *pcf *= (float)0xffff / 100.0f;
break; break;
@ -1054,8 +948,7 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
// TODO: need to multiply with emissive base color? // TODO: need to multiply with emissive base color?
pcf = &this->mScene->mMaterials.back().sTexEmissive.mTextureBlend; pcf = &this->mScene->mMaterials.back().sTexEmissive.mTextureBlend;
*pcf = this->ParsePercentageChunk(); *pcf = this->ParsePercentageChunk();
if (is_qnan(*pcf)) if (is_qnan(*pcf))*pcf = 0.0f;
*pcf = 0.0f;
else *pcf = *pcf * (float)0xFFFF / 100.0f; else *pcf = *pcf * (float)0xFFFF / 100.0f;
break; break;
@ -1085,31 +978,14 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
this->ParseTextureChunk(iRemaining,&this->mScene->mMaterials.back().sTexEmissive); this->ParseTextureChunk(iRemaining,&this->mScene->mMaterials.back().sTexEmissive);
break; break;
}; };
if (pcCurNext < this->mCurrent) ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
{ ASSIMP_3DS_END_CHUNK();
// place an error message. If we crash the programmer
// will be able to find it
DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
pcCurNext = this->mCurrent;
}
// Go to the starting position of the next chunk on this level
this->mCurrent = pcCurNext;
piRemaining -= psChunk->Size;
if (0 >= piRemaining)return;
return ParseMaterialChunk(piRemaining); return ParseMaterialChunk(piRemaining);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::ParseTextureChunk(int& piRemaining,Dot3DS::Texture* pcOut) void Dot3DSImporter::ParseTextureChunk(int& piRemaining,Dot3DS::Texture* pcOut)
{ {
const Dot3DSFile::Chunk* psChunk; ASSIMP_3DS_BEGIN_CHUNK();
this->ReadChunk(&psChunk);
const unsigned char* pcCur = this->mCurrent;
const unsigned char* pcCurNext = pcCur + (psChunk->Size
- sizeof(Dot3DSFile::Chunk));
// get chunk type // get chunk type
const unsigned char* sz = this->mCurrent; const unsigned char* sz = this->mCurrent;
@ -1184,11 +1060,8 @@ void Dot3DSImporter::ParseTextureChunk(int& piRemaining,Dot3DS::Texture* pcOut)
break; break;
}; };
// Go to the starting position of the next chunk on this level ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
this->mCurrent = pcCurNext; ASSIMP_3DS_END_CHUNK();
piRemaining -= psChunk->Size;
if (0 >= piRemaining)return;
return ParseTextureChunk(piRemaining,pcOut); return ParseTextureChunk(piRemaining,pcOut);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -1226,20 +1099,22 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
const Dot3DSFile::Chunk* psChunk; const Dot3DSFile::Chunk* psChunk;
this->ReadChunk(&psChunk); this->ReadChunk(&psChunk);
if (NULL == psChunk) if (!psChunk)
{ {
*p_pcOut = clrError; *p_pcOut = clrError;
return; return;
} }
const unsigned int diff = psChunk->Size - sizeof(Dot3DSFile::Chunk);
const unsigned char* pcCur = this->mCurrent; const unsigned char* pcCur = this->mCurrent;
this->mCurrent += psChunk->Size - sizeof(Dot3DSFile::Chunk); this->mCurrent += diff;
bool bGamma = false; bool bGamma = false;
switch(psChunk->Flag) switch(psChunk->Flag)
{ {
case Dot3DSFile::CHUNK_LINRGBF: case Dot3DSFile::CHUNK_LINRGBF:
bGamma = true; bGamma = true;
case Dot3DSFile::CHUNK_RGBF: case Dot3DSFile::CHUNK_RGBF:
if (sizeof(float) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk)) if (sizeof(float) * 3 > diff)
{ {
*p_pcOut = clrError; *p_pcOut = clrError;
return; return;
@ -1252,7 +1127,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
case Dot3DSFile::CHUNK_LINRGBB: case Dot3DSFile::CHUNK_LINRGBB:
bGamma = true; bGamma = true;
case Dot3DSFile::CHUNK_RGBB: case Dot3DSFile::CHUNK_RGBB:
if (sizeof(char) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk)) if (sizeof(char) * 3 > diff)
{ {
*p_pcOut = clrError; *p_pcOut = clrError;
return; return;
@ -1265,7 +1140,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
// percentage chunks: accepted to be compatible with various // percentage chunks: accepted to be compatible with various
// .3ds files with very curious content // .3ds files with very curious content
case Dot3DSFile::CHUNK_PERCENTF: case Dot3DSFile::CHUNK_PERCENTF:
if (p_bAcceptPercent && 4 <= psChunk->Size - sizeof(Dot3DSFile::Chunk)) if (p_bAcceptPercent && 4 <= diff)
{ {
p_pcOut->r = *((float*)pcCur); p_pcOut->r = *((float*)pcCur);
p_pcOut->g = *((float*)pcCur); p_pcOut->g = *((float*)pcCur);
@ -1275,7 +1150,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
*p_pcOut = clrError; *p_pcOut = clrError;
return; return;
case Dot3DSFile::CHUNK_PERCENTW: case Dot3DSFile::CHUNK_PERCENTW:
if (p_bAcceptPercent && 1 <= psChunk->Size - sizeof(Dot3DSFile::Chunk)) if (p_bAcceptPercent && 1 <= diff)
{ {
p_pcOut->r = (float)pcCur[0] / 255.0f; p_pcOut->r = (float)pcCur[0] / 255.0f;
p_pcOut->g = (float)pcCur[0] / 255.0f; p_pcOut->g = (float)pcCur[0] / 255.0f;
@ -1289,9 +1164,6 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
// skip unknown chunks, hope this won't cause any problems. // skip unknown chunks, hope this won't cause any problems.
return this->ParseColorChunk(p_pcOut,p_bAcceptPercent); return this->ParseColorChunk(p_pcOut,p_bAcceptPercent);
}; };
// assume input gamma = 1.0, output gamma = 2.2
// Not sure whether this is correct, too tired to
// think about it ;-)
if (bGamma) if (bGamma)
{ {
p_pcOut->r = powf(p_pcOut->r, 1.0f / 2.2f); p_pcOut->r = powf(p_pcOut->r, 1.0f / 2.2f);

View File

@ -206,15 +206,10 @@ protected:
*/ */
void ConvertScene(aiScene* pcOut); void ConvertScene(aiScene* pcOut);
// -------------------------------------------------------------------
/** generate normal vectors for a given mesh
*/
void GenNormals(Dot3DS::Mesh* sMesh);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** generate unique vertices for a mesh /** generate unique vertices for a mesh
*/ */
void MakeUnique(Dot3DS::Mesh* sMesh); void MakeUnique(Dot3DS::Mesh& sMesh);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Add a node to the node graph /** Add a node to the node graph
@ -235,7 +230,7 @@ protected:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Clamp all indices in the file to a valid range /** Clamp all indices in the file to a valid range
*/ */
void CheckIndices(Dot3DS::Mesh* sMesh); void CheckIndices(Dot3DS::Mesh& sMesh);
protected: protected:

View File

@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// internal headers // internal headers
#include "ASELoader.h" #include "ASELoader.h"
#include "3DSSpatialSort.h"
#include "MaterialSystem.h" #include "MaterialSystem.h"
#include "StringComparison.h" #include "StringComparison.h"
#include "TextureTransform.h" #include "TextureTransform.h"
@ -142,6 +141,7 @@ void ASEImporter::InternReadFile(
if ((*i).bSkip)continue; if ((*i).bSkip)continue;
this->TransformVertices(*i); this->TransformVertices(*i);
// now we need to create proper meshes from the import we need to // now we need to create proper meshes from the import we need to
// split them by materials, build valid vertex/face lists ... // split them by materials, build valid vertex/face lists ...
this->BuildUniqueRepresentation(*i); this->BuildUniqueRepresentation(*i);
@ -1135,9 +1135,7 @@ void ASEImporter::BuildMaterialIndices()
} }
// prepare for the next step // prepare for the next step
for (unsigned int hans = 0; hans < this->mParser->m_vMaterials.size();++hans) for (unsigned int hans = 0; hans < this->mParser->m_vMaterials.size();++hans)
{
TextureTransform::ApplyScaleNOffset(this->mParser->m_vMaterials[hans]); TextureTransform::ApplyScaleNOffset(this->mParser->m_vMaterials[hans]);
}
// now we need to iterate through all meshes, // now we need to iterate through all meshes,
// generating correct texture coordinates and material uv indices // generating correct texture coordinates and material uv indices
@ -1182,75 +1180,6 @@ void ASEImporter::GenerateNormals(ASE::Mesh& mesh)
} }
} }
if (mesh.mNormals.empty()) if (mesh.mNormals.empty())
{ ComputeNormalsWithSmoothingsGroups<ASE::Face>(mesh);
// need to calculate normals ...
// TODO: Find a way to merge this with the code in 3DSGenNormals.cpp
mesh.mNormals.resize(mesh.mPositions.size(),aiVector3D());
for( unsigned int a = 0; a < mesh.mFaces.size(); a++)
{
const ASE::Face& face = mesh.mFaces[a];
// assume it is a triangle
aiVector3D* pV1 = &mesh.mPositions[face.mIndices[2]];
aiVector3D* pV2 = &mesh.mPositions[face.mIndices[1]];
aiVector3D* pV3 = &mesh.mPositions[face.mIndices[0]];
aiVector3D pDelta1 = *pV2 - *pV1;
aiVector3D pDelta2 = *pV3 - *pV1;
aiVector3D vNor = pDelta1 ^ pDelta2;
for (unsigned int i = 0; i < 3;++i)
mesh.mNormals[face.mIndices[i]] = vNor;
}
// calculate the position bounds so we have a reliable epsilon to
// check position differences against
// @Schrompf: This is the 7th time this snippet is repeated!
aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
for( unsigned int a = 0; a < mesh.mPositions.size(); a++)
{
minVec.x = std::min( minVec.x, mesh.mPositions[a].x);
minVec.y = std::min( minVec.y, mesh.mPositions[a].y);
minVec.z = std::min( minVec.z, mesh.mPositions[a].z);
maxVec.x = std::max( maxVec.x, mesh.mPositions[a].x);
maxVec.y = std::max( maxVec.y, mesh.mPositions[a].y);
maxVec.z = std::max( maxVec.z, mesh.mPositions[a].z);
}
const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
std::vector<aiVector3D> avNormals;
avNormals.resize(mesh.mNormals.size());
// now generate the spatial sort tree
D3DSSpatialSorter sSort;
for( std::vector<ASE::Face>::iterator
i = mesh.mFaces.begin();
i != mesh.mFaces.end();++i){sSort.AddFace(&(*i),mesh.mPositions);}
sSort.Prepare();
for( std::vector<ASE::Face>::iterator
i = mesh.mFaces.begin();
i != mesh.mFaces.end();++i)
{
std::vector<unsigned int> poResult;
for (unsigned int c = 0; c < 3;++c)
{
sSort.FindPositions(mesh.mPositions[(*i).mIndices[c]],(*i).iSmoothGroup,
posEpsilon,poResult);
aiVector3D vNormals;
for (std::vector<unsigned int>::const_iterator
a = poResult.begin();
a != poResult.end();++a)
{
vNormals += mesh.mNormals[(*a)];
}
vNormals.Normalize();
avNormals[(*i).mIndices[c]] = vNormals;
poResult.clear();
}
}
mesh.mNormals = avNormals;
}
return; return;
} }

View File

@ -64,7 +64,7 @@ using namespace Assimp::ASE;
#define BLUBB(_message_) \ #define BLUBB(_message_) \
{this->LogError(_message_);return;} {this->LogError(_message_);return;}
// ------------------------------------------------------------------------------------------------
#define AI_ASE_HANDLE_TOP_LEVEL_SECTION(iDepth) \ #define AI_ASE_HANDLE_TOP_LEVEL_SECTION(iDepth) \
else if ('{' == *this->m_szFile)iDepth++; \ else if ('{' == *this->m_szFile)iDepth++; \
else if ('}' == *this->m_szFile) \ else if ('}' == *this->m_szFile) \
@ -87,6 +87,7 @@ using namespace Assimp::ASE;
} else bLastWasEndLine = false; \ } else bLastWasEndLine = false; \
++this->m_szFile; ++this->m_szFile;
// ------------------------------------------------------------------------------------------------
#define AI_ASE_HANDLE_SECTION(iDepth, level, msg) \ #define AI_ASE_HANDLE_SECTION(iDepth, level, msg) \
else if ('{' == *this->m_szFile)iDepth++; \ else if ('{' == *this->m_szFile)iDepth++; \
else if ('}' == *this->m_szFile) \ else if ('}' == *this->m_szFile) \
@ -1723,62 +1724,29 @@ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut)
ai_assert(NULL != apOut); ai_assert(NULL != apOut);
for (unsigned int i = 0; i < 3;++i) for (unsigned int i = 0; i < 3;++i)
{ ParseLV4MeshLong(apOut[i]);
// skip spaces and tabs
if(!SkipSpaces(this->m_szFile,&this->m_szFile))
{
// LOG
this->LogWarning("Unable to parse indexable long triple: unexpected EOL [#1]");
++this->iLineNumber;
apOut[0] = apOut[1] = apOut[2] = 0;
return;
}
apOut[i] = strtol10(this->m_szFile,&this->m_szFile);
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut) void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut)
{ {
ai_assert(NULL != apOut); ai_assert(NULL != apOut);
// skip spaces and tabs
if(!SkipSpaces(this->m_szFile,&this->m_szFile))
{
// LOG
this->LogWarning("Unable to parse indexed long triple: unexpected EOL [#4]");
rIndexOut = 0;
apOut[0] = apOut[1] = apOut[2] = 0;
++this->iLineNumber;
return;
}
// parse the index // parse the index
rIndexOut = strtol10(this->m_szFile,&this->m_szFile); ParseLV4MeshLong(rIndexOut);
// parse the three others // parse the three others
this->ParseLV4MeshLongTriple(apOut); this->ParseLV4MeshLongTriple(apOut);
return;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut) void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut)
{ {
ai_assert(NULL != apOut); ai_assert(NULL != apOut);
// skip spaces and tabs
if(!SkipSpaces(this->m_szFile,&this->m_szFile))
{
// LOG
this->LogWarning("Unable to parse indexed float triple: unexpected EOL [#1]");
rIndexOut = 0;
apOut[0] = apOut[1] = apOut[2] = 0.0f;
++this->iLineNumber;
return;
}
// parse the index // parse the index
rIndexOut = strtol10(this->m_szFile,&this->m_szFile); ParseLV4MeshLong(rIndexOut);
// parse the three others // parse the three others
this->ParseLV4MeshFloatTriple(apOut); this->ParseLV4MeshFloatTriple(apOut);
return;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Parser::ParseLV4MeshFloatTriple(float* apOut) void Parser::ParseLV4MeshFloatTriple(float* apOut)
@ -1786,20 +1754,7 @@ void Parser::ParseLV4MeshFloatTriple(float* apOut)
ai_assert(NULL != apOut); ai_assert(NULL != apOut);
for (unsigned int i = 0; i < 3;++i) for (unsigned int i = 0; i < 3;++i)
{ ParseLV4MeshFloat(apOut[i]);
// skip spaces and tabs
if(!SkipSpaces(this->m_szFile,&this->m_szFile))
{
// LOG
this->LogWarning("Unable to parse float triple: unexpected EOL [#5]");
apOut[0] = apOut[1] = apOut[2] = 0.0f;
++this->iLineNumber;
return;
}
// parse the float
this->m_szFile = fast_atof_move(this->m_szFile,apOut[i]);
}
return;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Parser::ParseLV4MeshFloat(float& fOut) void Parser::ParseLV4MeshFloat(float& fOut)
@ -1815,9 +1770,6 @@ void Parser::ParseLV4MeshFloat(float& fOut)
} }
// parse the first float // parse the first float
this->m_szFile = fast_atof_move(this->m_szFile,fOut); this->m_szFile = fast_atof_move(this->m_szFile,fOut);
// go to the next valid sequence
//this->SkipToNextToken();
return;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Parser::ParseLV4MeshLong(unsigned int& iOut) void Parser::ParseLV4MeshLong(unsigned int& iOut)
@ -1833,7 +1785,4 @@ void Parser::ParseLV4MeshLong(unsigned int& iOut)
} }
// parse the value // parse the value
iOut = strtol10(this->m_szFile,&this->m_szFile); iOut = strtol10(this->m_szFile,&this->m_szFile);
// go to the next valid sequence
//this->SkipToNextToken();
return;
} }

View File

@ -54,7 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../include/aiAnim.h" #include "../include/aiAnim.h"
// for some helper routines like IsSpace() // for some helper routines like IsSpace()
#include "PlyParser.h" #include "ParsingUtils.h"
#include "qnan.h" #include "qnan.h"
// ASE is quite similar to 3ds. We can reuse some structures // ASE is quite similar to 3ds. We can reuse some structures
@ -87,7 +87,7 @@ struct Material : public Dot3DS::Material
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** Helper structure to represent an ASE file face */ /** Helper structure to represent an ASE file face */
struct Face : public Dot3DS::Face struct Face : public FaceWithSmoothingGroup
{ {
//! Default constructor. Initializes everything with 0 //! Default constructor. Initializes everything with 0
Face() Face()
@ -224,13 +224,13 @@ struct DecompTransform
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** Helper structure to represent an ASE file mesh */ /** Helper structure to represent an ASE file mesh */
struct Mesh struct Mesh : public MeshWithSmoothingGroups<ASE::Face>
{ {
//! Constructor. Creates a default name for the mesh //! Constructor. Creates a default name for the mesh
Mesh() : bSkip(false) Mesh() : bSkip(false)
{ {
static int iCnt = 0; static int iCnt = 0;
char szTemp[128]; char szTemp[128]; // should be sufficiently large
::sprintf(szTemp,"UNNAMED_%i",iCnt++); ::sprintf(szTemp,"UNNAMED_%i",iCnt++);
mName = szTemp; mName = szTemp;
@ -249,21 +249,12 @@ struct Mesh
//! "" if there is no parent ... //! "" if there is no parent ...
std::string mParent; std::string mParent;
//! vertex positions
std::vector<aiVector3D> mPositions;
//! List of all faces loaded
std::vector<ASE::Face> mFaces;
//! List of all texture coordinate sets //! List of all texture coordinate sets
std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
//! List of all vertex color sets. //! List of all vertex color sets.
std::vector<aiColor4D> mVertexColors; std::vector<aiColor4D> mVertexColors;
//! List of normal vectors
std::vector<aiVector3D> mNormals;
//! List of all bone vertices //! List of all bone vertices
std::vector<BoneVertex> mBoneVertices; std::vector<BoneVertex> mBoneVertices;

View File

@ -40,14 +40,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** @file Implementation of the Plain-C API */ /** @file Implementation of the Plain-C API */
// CRT headers
#include <map>
// public ASSIMP headers // public ASSIMP headers
#include "../include/assimp.h" #include "../include/assimp.h"
#include "../include/aiFileIO.h"
#include "../include/assimp.hpp" #include "../include/assimp.hpp"
#include "../include/DefaultLogger.h" #include "../include/DefaultLogger.h"
#include "../include/aiAssert.h" #include "../include/aiAssert.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "GenericProperty.h"
// boost headers // boost headers
#define AI_C_THREADSAFE #define AI_C_THREADSAFE
@ -67,19 +70,158 @@ static ImporterMap gActiveImports;
/** Error message of the last failed import process */ /** Error message of the last failed import process */
static std::string gLastErrorString; static std::string gLastErrorString;
/** Configuration properties */
static Importer::IntPropertyMap gIntProperties;
static Importer::FloatPropertyMap gFloatProperties;
static Importer::StringPropertyMap gStringProperties;
#if (defined AI_C_THREADSAFE) #if (defined AI_C_THREADSAFE)
/** Global mutex to manage the access to the importer map */ /** Global mutex to manage the access to the importer map */
static boost::mutex gMutex; static boost::mutex gMutex;
#endif #endif
class CIOSystemWrapper;
class CIOStreamWrapper;
// ------------------------------------------------------------------------------------------------
// Custom IOStream implementation for the C-API
class CIOStreamWrapper : public IOStream
{
friend class CIOSystemWrapper;
public:
CIOStreamWrapper(aiFile* pFile)
: mFile(pFile)
{}
// -------------------------------------------------------------------
size_t Read(void* pvBuffer,
size_t pSize,
size_t pCount)
{
// need to typecast here as C has no void*
return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount);
}
// -------------------------------------------------------------------
size_t Write(const void* pvBuffer,
size_t pSize,
size_t pCount)
{
// need to typecast here as C has no void*
return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount);
}
// -------------------------------------------------------------------
aiReturn Seek(size_t pOffset,
aiOrigin pOrigin)
{
return mFile->SeekProc(mFile,pOffset,pOrigin);
}
// -------------------------------------------------------------------
size_t Tell(void) const
{
return mFile->TellProc(mFile);
}
// -------------------------------------------------------------------
size_t FileSize() const
{
return mFile->FileSizeProc(mFile);
}
private:
aiFile* mFile;
};
// ------------------------------------------------------------------------------------------------
// Custom IOStream implementation for the C-API
class CIOSystemWrapper : public IOSystem
{
public:
CIOSystemWrapper(aiFileIO* pFile)
: mFileSystem(pFile)
{}
// -------------------------------------------------------------------
bool Exists( const std::string& pFile) const
{
CIOSystemWrapper* pip = const_cast<CIOSystemWrapper*>(this);
IOStream* p = pip->Open(pFile);
if (p){pip->Close(p);return true;}
return false;
}
// -------------------------------------------------------------------
std::string getOsSeparator() const
{
return "/";
}
// -------------------------------------------------------------------
IOStream* Open(const std::string& pFile,
const std::string& pMode = std::string("rb"))
{
aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile.c_str(),pMode.c_str());
if (!p)return NULL;
return new CIOStreamWrapper(p);
}
// -------------------------------------------------------------------
void Close( IOStream* pFile)
{
if (!pFile)return;
mFileSystem->CloseProc(mFileSystem,((CIOStreamWrapper*) pFile)->mFile);
delete pFile;
}
private:
aiFileIO* mFileSystem;
};
// ------------------------------------------------------------------------------------------------
void ReportSceneNotFoundError()
{
DefaultLogger::get()->error("Unable to find the Importer instance for this scene. "
"Are you sure it has been created by aiImportFile(ex)(...)?");
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reads the given file and returns its content. // Reads the given file and returns its content.
const aiScene* aiImportFile( const char* pFile, unsigned int pFlags) const aiScene* aiImportFile( const char* pFile, unsigned int pFlags)
{
return aiImportFileEx(pFile,pFlags,NULL);
}
// ------------------------------------------------------------------------------------------------
const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags,
aiFileIO* pFS)
{ {
ai_assert(NULL != pFile); ai_assert(NULL != pFile);
// create an Importer for this file // create an Importer for this file
Assimp::Importer* imp = new Assimp::Importer; Assimp::Importer* imp = new Assimp::Importer;
// copy the global property lists to the Importer instance
// (we are a friend of Importer)
imp->mIntProperties = gIntProperties;
imp->mFloatProperties = gFloatProperties;
imp->mStringProperties = gStringProperties;
// setup a custom IO system if necessary
if (pFS)
{
imp->SetIOHandler( new CIOSystemWrapper (pFS) );
}
// and have it read the file // and have it read the file
const aiScene* scene = imp->ReadFile( pFile, pFlags); const aiScene* scene = imp->ReadFile( pFile, pFlags);
@ -118,8 +260,7 @@ void aiReleaseImport( const aiScene* pScene)
// it should be there... else the user is playing fools with us // it should be there... else the user is playing fools with us
if( it == gActiveImports.end()) if( it == gActiveImports.end())
{ {
DefaultLogger::get()->error("Unable to find the Importer instance for this scene. " ReportSceneNotFoundError();
"Are you sure it has been created by aiImportFile(ex)(...)?");
return; return;
} }
@ -196,11 +337,31 @@ void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn,
// it should be there... else the user is playing fools with us // it should be there... else the user is playing fools with us
if( it == gActiveImports.end()) if( it == gActiveImports.end())
{ {
DefaultLogger::get()->error("Unable to find the Importer instance for this scene. " ReportSceneNotFoundError();
"Are you sure it has been created by aiImportFile(ex)(...)?");
return; return;
} }
// get memory statistics // get memory statistics
it->second->GetMemoryRequirements(*in); it->second->GetMemoryRequirements(*in);
} }
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiSetImportPropertyInteger(const char* szName, int value)
{
SetGenericProperty<int>(gIntProperties,szName,value,NULL);
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiSetImportPropertyFloat(const char* szName, float value)
{
SetGenericProperty<float>(gFloatProperties,szName,value,NULL);
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiSetImportPropertyString(const char* szName,
const C_STRUCT aiString* st)
{
if (!st)return;
SetGenericProperty<std::string>(gStringProperties,szName,
std::string( st->data ),NULL);
}

View File

@ -86,7 +86,7 @@ bool CalcTangentsProcess::IsActive( unsigned int pFlags) const
void CalcTangentsProcess::SetupProperties(const Importer* pImp) void CalcTangentsProcess::SetupProperties(const Importer* pImp)
{ {
// get the current value of the property // get the current value of the property
this->configMaxAngle = pImp->GetProperty(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45000) / 1000.0f; this->configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f);
this->configMaxAngle = std::max(std::min(this->configMaxAngle,180.0f),0.0f); this->configMaxAngle = std::max(std::min(this->configMaxAngle,180.0f),0.0f);
this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle); this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle);
} }

100
code/DXFLoader.cpp 100644
View File

@ -0,0 +1,100 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (ASSIMP)
---------------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the DXF importer class */
#include "DXFLoader.h"
// public ASSIMP headers
#include "../include/aiScene.h"
#include "../include/aiAssert.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/DefaultLogger.h"
// boost headers
#include <boost/scoped_ptr.hpp>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
DXFImporter::DXFImporter()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
DXFImporter::~DXFImporter()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool DXFImporter::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);
return !(extension.length() != 4 || extension[0] != '.' ||
extension[1] != 'd' && extension[1] != 'D' ||
extension[2] != 'x' && extension[2] != 'X' ||
extension[3] != 'f' && extension[3] != 'F');
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void DXFImporter::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 DXF file " + pFile + "");
throw new ImportErrorException("DXF: not yet implemented");
}

93
code/DXFLoader.h 100644
View File

@ -0,0 +1,93 @@
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the .dxf importer class. */
#ifndef AI_DXFLOADER_H_INCLUDED
#define AI_DXFLOADER_H_INCLUDED
#include "BaseImporter.h"
#include "../include/aiTypes.h"
namespace Assimp {
// ---------------------------------------------------------------------------
/** DXF importer class
*/
class DXFImporter : public BaseImporter
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
DXFImporter();
/** Destructor, private as well */
~DXFImporter();
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:
// -------------------------------------------------------------------
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.dxf");
}
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene,
IOSystem* pIOHandler);
};
} // end of namespace Assimp
#endif // AI_3DSIMPORTER_H_INC

View File

@ -76,7 +76,7 @@ bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const
void GenVertexNormalsProcess::SetupProperties(const Importer* pImp) void GenVertexNormalsProcess::SetupProperties(const Importer* pImp)
{ {
// get the current value of the property // get the current value of the property
this->configMaxAngle = pImp->GetProperty(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,180000) / 1000.0f; this->configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,180.f);
this->configMaxAngle = std::max(std::min(this->configMaxAngle,180.0f),0.0f); this->configMaxAngle = std::max(std::min(this->configMaxAngle,180.0f),0.0f);
this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle); this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle);
} }
@ -145,8 +145,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
std::vector<unsigned int> verticesFound; std::vector<unsigned int> verticesFound;
const float fLimit = (AI_MESH_SMOOTHING_ANGLE_NOT_SET == pMesh->mMaxSmoothingAngle const float fLimit = this->configMaxAngle;
? this->configMaxAngle : pMesh->mMaxSmoothingAngle);
aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices]; aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices];
for (unsigned int i = 0; i < pMesh->mNumVertices;++i) for (unsigned int i = 0; i < pMesh->mNumVertices;++i)

View File

@ -0,0 +1,88 @@
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#ifndef AI_GENERIC_PROPERTY_H_INCLUDED
#define AI_GENERIC_PROPERTY_H_INCLUDED
#include "./../include/assimp.hpp"
#include "Hash.h"
// ------------------------------------------------------------------------------------------------
template <class T>
inline void SetGenericProperty(std::map< uint32_t, T >& list,
const char* szName, const T& value, bool* bWasExisting)
{
ai_assert(NULL != szName);
typedef std::map< uint32_t, T > GenericPropertyMap;
typedef std::pair< uint32_t, T > GenericPair;
uint32_t hash = SuperFastHash(szName);
GenericPropertyMap::iterator it = list.find(hash);
if (it == list.end())
{
if (bWasExisting)*bWasExisting = false;
list.insert(GenericPair( hash, value ));
return;
}
(*it).second = value;
if (bWasExisting)*bWasExisting = true;
}
// ------------------------------------------------------------------------------------------------
template <class T>
inline T GetGenericProperty(const std::map< uint32_t, T >& list,
const char* szName, const T& errorReturn)
{
ai_assert(NULL != szName);
typedef std::map< uint32_t, T > GenericPropertyMap;
typedef std::pair< uint32_t, T > GenericPair;
uint32_t hash = SuperFastHash(szName);
GenericPropertyMap::const_iterator it = list.find(hash);
if (it == list.end())return errorReturn;
return (*it).second;
}
#endif // !! AI_GENERIC_PROPERTY_H_INCLUDED

View File

@ -1,3 +1,42 @@
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#ifndef AI_HASH_H_INCLUDED #ifndef AI_HASH_H_INCLUDED
#define AI_HASH_H_INCLUDED #define AI_HASH_H_INCLUDED
@ -19,11 +58,12 @@
#endif #endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
inline uint32_t SuperFastHash (const char * data, int len, uint32_t hash = 0) { inline uint32_t SuperFastHash (const char * data, unsigned int len = 0, uint32_t hash = 0) {
uint32_t tmp; uint32_t tmp;
int rem; int rem;
if (len <= 0 || data == NULL) return 0; if (!data) return 0;
if (!len)len = (unsigned int)::strlen(data);
rem = len & 3; rem = len & 3;
len >>= 2; len >>= 2;

View File

@ -57,6 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "BaseProcess.h" #include "BaseProcess.h"
#include "DefaultIOStream.h" #include "DefaultIOStream.h"
#include "DefaultIOSystem.h" #include "DefaultIOSystem.h"
#include "GenericProperty.h"
// Importers // Importers
#if (!defined AI_BUILD_NO_X_IMPORTER) #if (!defined AI_BUILD_NO_X_IMPORTER)
@ -151,6 +152,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS) #if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
# include "RemoveRedundantMaterials.h" # include "RemoveRedundantMaterials.h"
#endif #endif
#if (!defined AI_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
# include "OptimizeGraphProcess.h"
#endif
using namespace Assimp; using namespace Assimp;
@ -168,6 +172,9 @@ Importer::Importer() :
bExtraVerbose = false; // disable extra verbose mode by default bExtraVerbose = false; // disable extra verbose mode by default
// add an instance of each worker class here // add an instance of each worker class here
// the order doesn't really care, however file formats that are
// used more frequently than others should be at the beginning.
#if (!defined AI_BUILD_NO_X_IMPORTER) #if (!defined AI_BUILD_NO_X_IMPORTER)
mImporter.push_back( new XFileImporter()); mImporter.push_back( new XFileImporter());
#endif #endif
@ -217,13 +224,18 @@ Importer::Importer() :
#endif #endif
// add an instance of each post processing step here in the order // add an instance of each post processing step here in the order
// of sequence it is executed // of sequence it is executed. steps that are added here are not validated -
// as RegisterPPStep() does - all dependencies must be there.
#if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS) #if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS)
mPostProcessingSteps.push_back( new ValidateDSProcess()); // must be first mPostProcessingSteps.push_back( new ValidateDSProcess()); // must be first
#endif #endif
#if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS) #if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
mPostProcessingSteps.push_back( new RemoveRedundantMatsProcess()); mPostProcessingSteps.push_back( new RemoveRedundantMatsProcess());
#endif #endif
#if (!defined AI_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
mPostProcessingSteps.push_back( new OptimizeGraphProcess());
#endif
#if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS) #if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS)
mPostProcessingSteps.push_back( new TriangulateProcess()); mPostProcessingSteps.push_back( new TriangulateProcess());
#endif #endif
@ -379,8 +391,16 @@ bool ValidateFlags(unsigned int pFlags)
if (pFlags & aiProcess_GenSmoothNormals && if (pFlags & aiProcess_GenSmoothNormals &&
pFlags & aiProcess_GenNormals) pFlags & aiProcess_GenNormals)
{ {
DefaultLogger::get()->error("aiProcess_GenSmoothNormals and aiProcess_GenNormals " DefaultLogger::get()->error("aiProcess_GenSmoothNormals and "
"may not be specified together"); "aiProcess_GenNormals may not be specified together");
return false;
}
if (pFlags & aiProcess_PreTransformVertices &&
pFlags & aiProcess_OptimizeGraph)
{
DefaultLogger::get()->error("aiProcess_PreTransformVertives and "
"aiProcess_OptimizeGraph may not be specified together");
return false; return false;
} }
@ -488,15 +508,21 @@ const aiScene* Importer::ReadFile( const std::string& pFile, unsigned int pFlags
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Helper function to check whether an extension is supported by ASSIMP // Helper function to check whether an extension is supported by ASSIMP
bool Importer::IsExtensionSupported(const std::string& szExtension) bool Importer::IsExtensionSupported(const std::string& szExtension)
{
return NULL != FindLoader(szExtension);
}
// ------------------------------------------------------------------------------------------------
BaseImporter* Importer::FindLoader (const std::string& szExtension)
{ {
for (std::vector<BaseImporter*>::const_iterator for (std::vector<BaseImporter*>::const_iterator
i = this->mImporter.begin(); i = this->mImporter.begin();
i != this->mImporter.end();++i) i != this->mImporter.end();++i)
{ {
// pass the file extension to the CanRead(..,NULL)-method // pass the file extension to the CanRead(..,NULL)-method
if ((*i)->CanRead(szExtension,NULL))return true; if ((*i)->CanRead(szExtension,NULL))return *i;
} }
return false; return NULL;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -519,49 +545,48 @@ void Importer::GetExtensionList(std::string& szOut)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Set a configuration property // Set a configuration property
int Importer::SetProperty(const char* szName, int iValue) void Importer::SetPropertyInteger(const char* szName, int iValue,
bool* bWasExisting /*= NULL*/)
{ {
ai_assert(NULL != szName); SetGenericProperty<int>(mIntProperties, szName,iValue,bWasExisting);
}
// search in the list ... // ------------------------------------------------------------------------------------------------
for (std::vector<IntPropertyInfo>::iterator void Importer::SetPropertyFloat(const char* szName, float iValue,
i = this->mIntProperties.begin(); bool* bWasExisting /*= NULL*/)
i != this->mIntProperties.end();++i) {
{ SetGenericProperty<float>(mFloatProperties, szName,iValue,bWasExisting);
if (0 == ::strcmp( (*i).name.c_str(), szName )) }
{
int iOld = (*i).value; // ------------------------------------------------------------------------------------------------
(*i).value = iValue; void Importer::SetPropertyString(const char* szName, const std::string& value,
return iOld; bool* bWasExisting /*= NULL*/)
} {
} SetGenericProperty<std::string>(mStringProperties, szName,value,bWasExisting);
// the property is not yet in the list ...
this->mIntProperties.push_back( IntPropertyInfo() );
IntPropertyInfo& me = this->mIntProperties.back();
me.name = std::string(szName);
me.value = iValue;
return AI_PROPERTY_WAS_NOT_EXISTING;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a configuration property // Get a configuration property
int Importer::GetProperty(const char* szName, int Importer::GetPropertyInteger(const char* szName,
int iErrorReturn /*= 0xffffffff*/) const int iErrorReturn /*= 0xffffffff*/) const
{ {
ai_assert(NULL != szName); return GetGenericProperty<int>(mIntProperties,szName,iErrorReturn);
// search in the list ...
for (std::vector<IntPropertyInfo>::const_iterator
i = this->mIntProperties.begin();
i != this->mIntProperties.end();++i)
{
if (0 == ::strcmp( (*i).name.c_str(), szName ))
{
return (*i).value;
}
}
return iErrorReturn;
} }
// ------------------------------------------------------------------------------------------------
float Importer::GetPropertyFloat(const char* szName,
float iErrorReturn /*= 10e10*/) const
{
return GetGenericProperty<float>(mFloatProperties,szName,iErrorReturn);
}
// ------------------------------------------------------------------------------------------------
std::string Importer::GetPropertyString(const char* szName,
const std::string& iErrorReturn /*= ""*/) const
{
return GetGenericProperty<std::string>(mStringProperties,szName,iErrorReturn);
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode)
{ {

View File

@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../include/aiTypes.h" #include "../include/aiTypes.h"
struct aiMesh; struct aiMesh;
class JoinVerticesTest;
namespace Assimp namespace Assimp
{ {
@ -62,6 +63,7 @@ namespace Assimp
class ASSIMP_API JoinVerticesProcess : public BaseProcess class ASSIMP_API JoinVerticesProcess : public BaseProcess
{ {
friend class Importer; friend class Importer;
friend class ::JoinVerticesTest;
protected: protected:
/** Constructor to be privately used by Importer */ /** Constructor to be privately used by Importer */

View File

@ -95,8 +95,8 @@ bool LWOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
// Setup configuration properties // Setup configuration properties
void LWOImporter::SetupProperties(const Importer* pImp) void LWOImporter::SetupProperties(const Importer* pImp)
{ {
this->configGradientResX = pImp->GetProperty(AI_CONFIG_IMPORT_LWO_GRADIENT_RESX,512); this->configGradientResX = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWO_GRADIENT_RESX,512);
this->configGradientResY = pImp->GetProperty(AI_CONFIG_IMPORT_LWO_GRADIENT_RESY,512); this->configGradientResY = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWO_GRADIENT_RESY,512);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure. // Imports the given file into the given scene structure.
@ -174,10 +174,10 @@ void LWOImporter::InternReadFile( const std::string& pFile,
std::vector<aiMesh*> apcMeshes; std::vector<aiMesh*> apcMeshes;
std::vector<aiNode*> apcNodes; std::vector<aiNode*> apcNodes;
apcNodes.reserve(mLayers->size()); apcNodes.reserve(mLayers->size());
apcMeshes.reserve(mLayers->size()*std::min((mSurfaces->size()/2u), 1u)); apcMeshes.reserve(mLayers->size()*std::min(((unsigned int)mSurfaces->size()/2u), 1u));
// the RemoveRedundantMaterials step will clean this up later // the RemoveRedundantMaterials step will clean this up later
pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = mSurfaces->size()]; pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = (unsigned int)mSurfaces->size()];
for (unsigned int mat = 0; mat < pScene->mNumMaterials;++mat) for (unsigned int mat = 0; mat < pScene->mNumMaterials;++mat)
{ {
MaterialHelper* pcMat = new MaterialHelper(); MaterialHelper* pcMat = new MaterialHelper();
@ -192,7 +192,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
const LWO::Layer& layer = *lit; const LWO::Layer& layer = *lit;
// I don't know whether there could be dummy layers, but it would be possible // I don't know whether there could be dummy layers, but it would be possible
const unsigned int meshStart = apcMeshes.size(); const unsigned int meshStart = (unsigned int)apcMeshes.size();
if (!layer.mFaces.empty() && !layer.mTempPoints.empty()) if (!layer.mFaces.empty() && !layer.mTempPoints.empty())
{ {
// now sort all faces by the surfaces assigned to them // now sort all faces by the surfaces assigned to them
@ -207,13 +207,13 @@ void LWOImporter::InternReadFile( const std::string& pFile,
if (idx >= mTags->size()) if (idx >= mTags->size())
{ {
DefaultLogger::get()->warn("LWO: Invalid face surface index"); DefaultLogger::get()->warn("LWO: Invalid face surface index");
idx = mTags->size()-1; idx = (unsigned int)mTags->size()-1;
} }
if(0xffffffff == (idx = _mMapping[idx])) if(0xffffffff == (idx = _mMapping[idx]))
{ {
if (0xffffffff == iDefaultSurface) if (0xffffffff == iDefaultSurface)
{ {
iDefaultSurface = mSurfaces->size(); iDefaultSurface = (unsigned int)mSurfaces->size();
mSurfaces->push_back(LWO::Surface()); mSurfaces->push_back(LWO::Surface());
LWO::Surface& surf = mSurfaces->back(); LWO::Surface& surf = mSurfaces->back();
surf.mColor.r = surf.mColor.g = surf.mColor.b = 0.6f; surf.mColor.r = surf.mColor.g = surf.mColor.b = 0.6f;
@ -240,7 +240,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
// generate the mesh // generate the mesh
aiMesh* mesh = new aiMesh(); aiMesh* mesh = new aiMesh();
apcMeshes.push_back(mesh); apcMeshes.push_back(mesh);
mesh->mNumFaces = sorted.size(); mesh->mNumFaces = (unsigned int)sorted.size();
for (SortedRep::const_iterator it = sorted.begin(), end = sorted.end(); for (SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
it != end;++it) it != end;++it)
@ -281,7 +281,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
apcNodes.push_back(pcNode); apcNodes.push_back(pcNode);
pcNode->mName.Set(layer.mName); pcNode->mName.Set(layer.mName);
pcNode->mParent = reinterpret_cast<aiNode*>(layer.mParent); pcNode->mParent = reinterpret_cast<aiNode*>(layer.mParent);
pcNode->mNumMeshes = apcMeshes.size() - meshStart; pcNode->mNumMeshes = (unsigned int)apcMeshes.size() - meshStart;
pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
for (unsigned int p = 0; p < pcNode->mNumMeshes;++p) for (unsigned int p = 0; p < pcNode->mNumMeshes;++p)
pcNode->mMeshes[p] = p + meshStart; pcNode->mMeshes[p] = p + meshStart;
@ -292,7 +292,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
// copy the meshes to the output structure // copy the meshes to the output structure
if (apcMeshes.size()) // shouldn't occur, just to be sure we don't crash if (apcMeshes.size()) // shouldn't occur, just to be sure we don't crash
{ {
pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = apcMeshes.size() ]; pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = (unsigned int)apcMeshes.size() ];
::memcpy(pScene->mMeshes,&apcMeshes[0],pScene->mNumMeshes*sizeof(void*)); ::memcpy(pScene->mMeshes,&apcMeshes[0],pScene->mNumMeshes*sizeof(void*));
} }
} }
@ -414,7 +414,7 @@ void LWOImporter::CopyFaceIndices(FaceList::iterator& it,
if (mi > mCurLayer->mTempPoints.size()) if (mi > mCurLayer->mTempPoints.size())
{ {
DefaultLogger::get()->warn("LWO: face index is out of range"); DefaultLogger::get()->warn("LWO: face index is out of range");
mi = mCurLayer->mTempPoints.size()-1; mi = (unsigned int)mCurLayer->mTempPoints.size()-1;
} }
} }
} }

View File

@ -89,7 +89,7 @@ void LimitBoneWeightsProcess::Execute( aiScene* pScene)
void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp)
{ {
// get the current value of the property // get the current value of the property
this->mMaxWeights = pImp->GetProperty(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS); this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -45,14 +45,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "ByteSwap.h" #include "ByteSwap.h"
#include "MD2NormalTable.h" // shouldn't be included by other units #include "MD2NormalTable.h" // shouldn't be included by other units
#include "../include/IOStream.h" // public ASSIMP headers
#include "../include/IOSystem.h" #include "../include/assimp.hpp"
#include "../include/aiMesh.h"
#include "../include/aiScene.h" #include "../include/aiScene.h"
#include "../include/aiAssert.h" #include "../include/aiAssert.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/DefaultLogger.h" #include "../include/DefaultLogger.h"
#include "../include/assimp.hpp"
// boost headers
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
using namespace Assimp; using namespace Assimp;
@ -117,10 +118,10 @@ void MD2Importer::SetupProperties(const Importer* pImp)
{ {
// The AI_CONFIG_IMPORT_MD2_KEYFRAME option overrides the // The AI_CONFIG_IMPORT_MD2_KEYFRAME option overrides the
// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
if(0xffffffff == (this->configFrameID = pImp->GetProperty( if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
AI_CONFIG_IMPORT_MD2_KEYFRAME,0xffffffff))) AI_CONFIG_IMPORT_MD2_KEYFRAME,0xffffffff)))
{ {
this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -190,255 +191,247 @@ void MD2Importer::InternReadFile( const std::string& pFile,
if( fileSize < sizeof(MD2::Header)) if( fileSize < sizeof(MD2::Header))
throw new ImportErrorException( "MD2 File is too small"); throw new ImportErrorException( "MD2 File is too small");
try std::vector<unsigned char> mBuffer2(fileSize);
file->Read(&mBuffer2[0], 1, fileSize);
this->mBuffer = &mBuffer2[0];
this->m_pcHeader = (const MD2::Header*)this->mBuffer;
#ifdef AI_BUILD_BIG_ENDIAN
ByteSwap::Swap4(&m_pcHeader->frameSize);
ByteSwap::Swap4(&m_pcHeader->magic);
ByteSwap::Swap4(&m_pcHeader->numFrames);
ByteSwap::Swap4(&m_pcHeader->numGlCommands);
ByteSwap::Swap4(&m_pcHeader->numSkins);
ByteSwap::Swap4(&m_pcHeader->numTexCoords);
ByteSwap::Swap4(&m_pcHeader->numTriangles);
ByteSwap::Swap4(&m_pcHeader->numVertices);
ByteSwap::Swap4(&m_pcHeader->offsetEnd);
ByteSwap::Swap4(&m_pcHeader->offsetFrames);
ByteSwap::Swap4(&m_pcHeader->offsetGlCommands);
ByteSwap::Swap4(&m_pcHeader->offsetSkins);
ByteSwap::Swap4(&m_pcHeader->offsetTexCoords);
ByteSwap::Swap4(&m_pcHeader->offsetTriangles);
ByteSwap::Swap4(&m_pcHeader->skinHeight);
ByteSwap::Swap4(&m_pcHeader->skinWidth);
ByteSwap::Swap4(&m_pcHeader->version);
#endif
this->ValidateHeader();
// there won't be more than one mesh inside the file
pScene->mNumMaterials = 1;
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumMeshes = 1;
pScene->mRootNode->mMeshes = new unsigned int[1];
pScene->mRootNode->mMeshes[0] = 0;
pScene->mMaterials = new aiMaterial*[1];
pScene->mMaterials[0] = new MaterialHelper();
pScene->mNumMeshes = 1;
pScene->mMeshes = new aiMesh*[1];
aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
// navigate to the begin of the frame data
const MD2::Frame* pcFrame = (const MD2::Frame*) ((uint8_t*)
this->m_pcHeader + this->m_pcHeader->offsetFrames);
pcFrame += this->configFrameID;
// navigate to the begin of the triangle data
MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*)
this->m_pcHeader + this->m_pcHeader->offsetTriangles);
// navigate to the begin of the tex coords data
const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) ((uint8_t*)
this->m_pcHeader + this->m_pcHeader->offsetTexCoords);
// navigate to the begin of the vertex data
const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices);
#ifdef AI_BUILD_BIG_ENDIAN
for (uint32_t i = 0; i< m_pcHeader->numTriangles)
{ {
// allocate storage and copy the contents of the file to a memory buffer for (unsigned int p = 0; p < 3;++p)
this->mBuffer = new unsigned char[fileSize]; {
file->Read( (void*)mBuffer, 1, fileSize); ByteSwap::Swap2(& pcTriangles[i].textureIndices[p]);
ByteSwap::Swap2(& pcTriangles[i].vertexIndices[p]);
this->m_pcHeader = (const MD2::Header*)this->mBuffer; }
}
#ifdef AI_BUILD_BIG_ENDIAN for (uint32_t i = 0; i < m_pcHeader->offsetTexCoords;++i)
{
ByteSwap::Swap4(&m_pcHeader->frameSize); ByteSwap::Swap2(& pcTexCoords[i].s);
ByteSwap::Swap4(&m_pcHeader->magic); ByteSwap::Swap2(& pcTexCoords[i].t);
ByteSwap::Swap4(&m_pcHeader->numFrames); }
ByteSwap::Swap4(&m_pcHeader->numGlCommands); ByteSwap::Swap4( & pcFrame->scale[0] );
ByteSwap::Swap4(&m_pcHeader->numSkins); ByteSwap::Swap4( & pcFrame->scale[1] );
ByteSwap::Swap4(&m_pcHeader->numTexCoords); ByteSwap::Swap4( & pcFrame->scale[2] );
ByteSwap::Swap4(&m_pcHeader->numTriangles); ByteSwap::Swap4( & pcFrame->translate[0] );
ByteSwap::Swap4(&m_pcHeader->numVertices); ByteSwap::Swap4( & pcFrame->translate[1] );
ByteSwap::Swap4(&m_pcHeader->offsetEnd); ByteSwap::Swap4( & pcFrame->translate[2] );
ByteSwap::Swap4(&m_pcHeader->offsetFrames);
ByteSwap::Swap4(&m_pcHeader->offsetGlCommands);
ByteSwap::Swap4(&m_pcHeader->offsetSkins);
ByteSwap::Swap4(&m_pcHeader->offsetTexCoords);
ByteSwap::Swap4(&m_pcHeader->offsetTriangles);
ByteSwap::Swap4(&m_pcHeader->skinHeight);
ByteSwap::Swap4(&m_pcHeader->skinWidth);
ByteSwap::Swap4(&m_pcHeader->version);
#endif #endif
this->ValidateHeader(); pcMesh->mNumFaces = this->m_pcHeader->numTriangles;
pcMesh->mFaces = new aiFace[this->m_pcHeader->numTriangles];
// there won't be more than one mesh inside the file // allocate output storage
pScene->mNumMaterials = 1; pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3;
pScene->mRootNode = new aiNode(); pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
pScene->mRootNode->mNumMeshes = 1; pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
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];
aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
// navigate to the begin of the frame data // not sure whether there are MD2 files without texture coordinates
const MD2::Frame* pcFrame = (const MD2::Frame*) ((uint8_t*) // NOTE: texture coordinates can be there without a texture,
this->m_pcHeader + this->m_pcHeader->offsetFrames); // but a texture can't be there without a valid UV channel
pcFrame += this->configFrameID; if (this->m_pcHeader->numTexCoords && 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);
// navigate to the begin of the triangle data const int iMode = (int)aiShadingMode_Gouraud;
MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*) MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
this->m_pcHeader + this->m_pcHeader->offsetTriangles); pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
// navigate to the begin of the tex coords data aiColor3D clr;
const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) ((uint8_t*) clr.b = clr.g = clr.r = 1.0f;
this->m_pcHeader + this->m_pcHeader->offsetTexCoords); pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
// navigate to the begin of the vertex data clr.b = clr.g = clr.r = 0.05f;
const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices); pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
#ifdef AI_BUILD_BIG_ENDIAN if (pcSkins->name[0])
for (uint32_t i = 0; i< m_pcHeader->numTriangles)
{ {
for (unsigned int p = 0; p < 3;++p) aiString szString;
{ const size_t iLen = ::strlen(pcSkins->name);
ByteSwap::Swap2(& pcTriangles[i].textureIndices[p]); ::memcpy(szString.data,pcSkins->name,iLen);
ByteSwap::Swap2(& pcTriangles[i].vertexIndices[p]); szString.data[iLen] = '\0';
} szString.length = iLen;
}
for (uint32_t i = 0; i < m_pcHeader->offsetTexCoords;++i)
{
ByteSwap::Swap2(& pcTexCoords[i].s);
ByteSwap::Swap2(& pcTexCoords[i].t);
}
ByteSwap::Swap4( & pcFrame->scale[0] );
ByteSwap::Swap4( & pcFrame->scale[1] );
ByteSwap::Swap4( & pcFrame->scale[2] );
ByteSwap::Swap4( & pcFrame->translate[0] );
ByteSwap::Swap4( & pcFrame->translate[1] );
ByteSwap::Swap4( & pcFrame->translate[2] );
#endif
pcMesh->mNumFaces = this->m_pcHeader->numTriangles; pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
pcMesh->mFaces = new aiFace[this->m_pcHeader->numTriangles];
// allocate output storage
pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3;
pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
// not sure whether there are MD2 files without texture coordinates
// NOTE: texture coordinates can be there without a texture,
// but a texture can't be there without a valid UV channel
if (this->m_pcHeader->numTexCoords && 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);
if (pcSkins->name[0])
{
aiString szString;
const size_t iLen = ::strlen(pcSkins->name);
::memcpy(szString.data,pcSkins->name,iLen);
szString.data[iLen] = '\0';
szString.length = iLen;
pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
}
else
{
DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped.");
}
} }
else else
{ {
// apply a default material DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped.");
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);
aiString szName;
szName.Set(AI_DEFAULT_MATERIAL_NAME);
pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
}
// now read all triangles of the first frame, apply scaling and translation
unsigned int iCurrent = 0;
float fDivisorU,fDivisorV;
if (this->m_pcHeader->numTexCoords)
{
// allocate storage for texture coordinates, too
pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNumUVComponents[0] = 2;
// check whether the skin width or height are zero (this would
// cause a division through zero)
if (!this->m_pcHeader->skinWidth)
{
DefaultLogger::get()->error("Skin width is zero but there are "
"valid absolute texture coordinates. Unable to compute "
"relative texture coordinates ranging from 0 to 1");
fDivisorU = 1.0f;
}
else fDivisorU = (float)this->m_pcHeader->skinWidth;
if (!this->m_pcHeader->skinHeight)
{
DefaultLogger::get()->error("Skin height is zero but there are "
"valid absolute texture coordinates. Unable to compute "
"relative texture coordinates ranging from 0 to 1");
fDivisorV = 1.0f;
}
else fDivisorV = (float)this->m_pcHeader->skinHeight;
}
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
unsigned int iTemp = iCurrent;
for (unsigned int c = 0; c < 3;++c,++iCurrent)
{
// validate vertex indices
if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices)
{
DefaultLogger::get()->error("Vertex index is outside the allowed range");
pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1;
}
// copy face indices
unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
// read x,y, and z component of the vertex
aiVector3D& vec = pcMesh->mVertices[iCurrent];
vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
vec.x += pcFrame->translate[0];
// (flip z and y component)
// FIX: no .... invert y instead
vec.y = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1];
vec.y += pcFrame->translate[1];
vec.y *= -1.0f;
vec.z = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2];
vec.z += pcFrame->translate[2];
// read the normal vector from the precalculated normal table
aiVector3D& vNormal = pcMesh->mNormals[iCurrent];
LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal);
vNormal.y *= -1.0f;
if (this->m_pcHeader->numTexCoords)
{
// validate texture coordinates
if (pcTriangles[iIndex].textureIndices[c] >= this->m_pcHeader->numTexCoords)
{
DefaultLogger::get()->error("UV index is outside the allowed range");
pcTriangles[iIndex].textureIndices[c] = this->m_pcHeader->numTexCoords-1;
}
aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent];
float u,v;
// the texture coordinates are absolute values but we
// need relative values between 0 and 1
u = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].s / fDivisorU;
v = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].t / fDivisorV;
pcOut.x = u;
pcOut.y = 1.0f - v; // FIXME: Is this correct for MD2?
}
}
// FIX: flip the face order for use with OpenGL
pScene->mMeshes[0]->mFaces[i].mIndices[0] = iTemp+2;
pScene->mMeshes[0]->mFaces[i].mIndices[1] = iTemp+1;
pScene->mMeshes[0]->mFaces[i].mIndices[2] = iTemp+0;
} }
} }
catch (ImportErrorException* ex) else
{ {
delete[] this->mBuffer; AI_DEBUG_INVALIDATE_PTR(this->mBuffer); // apply a default material
throw ex; 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);
aiString szName;
szName.Set(AI_DEFAULT_MATERIAL_NAME);
pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
}
// now read all triangles of the first frame, apply scaling and translation
unsigned int iCurrent = 0;
float fDivisorU,fDivisorV;
if (this->m_pcHeader->numTexCoords)
{
// allocate storage for texture coordinates, too
pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNumUVComponents[0] = 2;
// check whether the skin width or height are zero (this would
// cause a division through zero)
if (!this->m_pcHeader->skinWidth)
{
DefaultLogger::get()->error("Skin width is zero but there are "
"valid absolute texture coordinates. Unable to compute "
"relative texture coordinates ranging from 0 to 1");
fDivisorU = 1.0f;
}
else fDivisorU = (float)this->m_pcHeader->skinWidth;
if (!this->m_pcHeader->skinHeight)
{
DefaultLogger::get()->error("Skin height is zero but there are "
"valid absolute texture coordinates. Unable to compute "
"relative texture coordinates ranging from 0 to 1");
fDivisorV = 1.0f;
}
else fDivisorV = (float)this->m_pcHeader->skinHeight;
}
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
unsigned int iTemp = iCurrent;
for (unsigned int c = 0; c < 3;++c,++iCurrent)
{
// validate vertex indices
if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices)
{
DefaultLogger::get()->error("Vertex index is outside the allowed range");
pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1;
}
// copy face indices
unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
// read x,y, and z component of the vertex
aiVector3D& vec = pcMesh->mVertices[iCurrent];
vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
vec.x += pcFrame->translate[0];
// (flip z and y component)
// FIX: no .... invert y instead
vec.y = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1];
vec.y += pcFrame->translate[1];
vec.y *= -1.0f;
vec.z = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2];
vec.z += pcFrame->translate[2];
// read the normal vector from the precalculated normal table
aiVector3D& vNormal = pcMesh->mNormals[iCurrent];
LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal);
vNormal.y *= -1.0f;
if (this->m_pcHeader->numTexCoords)
{
// validate texture coordinates
if (pcTriangles[iIndex].textureIndices[c] >= this->m_pcHeader->numTexCoords)
{
DefaultLogger::get()->error("UV index is outside the allowed range");
pcTriangles[iIndex].textureIndices[c] = this->m_pcHeader->numTexCoords-1;
}
aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent];
float u,v;
// the texture coordinates are absolute values but we
// need relative values between 0 and 1
u = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].s / fDivisorU;
v = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].t / fDivisorV;
pcOut.x = u;
pcOut.y = 1.0f - v; // FIXME: Is this correct for MD2?
}
}
// FIX: flip the face order for use with OpenGL
pScene->mMeshes[0]->mFaces[i].mIndices[0] = iTemp+2;
pScene->mMeshes[0]->mFaces[i].mIndices[1] = iTemp+1;
pScene->mMeshes[0]->mFaces[i].mIndices[2] = iTemp+0;
} }
delete[] this->mBuffer; AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
} }

View File

@ -144,10 +144,10 @@ void MD3Importer::SetupProperties(const Importer* pImp)
{ {
// The AI_CONFIG_IMPORT_MD3_KEYFRAME option overrides the // The AI_CONFIG_IMPORT_MD3_KEYFRAME option overrides the
// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
if(0xffffffff == (this->configFrameID = pImp->GetProperty( if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
AI_CONFIG_IMPORT_MD3_KEYFRAME,0xffffffff))) AI_CONFIG_IMPORT_MD3_KEYFRAME,0xffffffff)))
{ {
this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -61,14 +61,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
// we're just doing this with static buffers whose size is known at
// compile time, so the compiler should automatically expand to
// sprintf<array_length>(...)
#if _MSC_VER >= 1400
# define sprintf sprintf_s
#endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
MD5Importer::MD5Importer() MD5Importer::MD5Importer()
@ -122,9 +114,8 @@ void MD5Importer::InternReadFile(
// make sure we return no incomplete data // make sure we return no incomplete data
if (!bHadMD5Mesh && !bHadMD5Anim) if (!bHadMD5Mesh && !bHadMD5Anim)
{
throw new ImportErrorException("Failed to read valid data from this MD5"); throw new ImportErrorException("Failed to read valid data from this MD5");
}
if (!bHadMD5Mesh)pScene->mFlags |= AI_SCENE_FLAGS_ANIM_SKELETON_ONLY; if (!bHadMD5Mesh)pScene->mFlags |= AI_SCENE_FLAGS_ANIM_SKELETON_ONLY;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -285,30 +276,48 @@ void MD5Importer::LoadMD5MeshFile ()
pScene->mRootNode->mNumChildren = 2; pScene->mRootNode->mNumChildren = 2;
pScene->mRootNode->mChildren = new aiNode*[2]; pScene->mRootNode->mChildren = new aiNode*[2];
aiNode* pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
pcNode->mNumMeshes = (unsigned int)meshParser.mMeshes.size();
pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
pcNode->mName.Set("MD5Mesh");
pcNode->mParent = pScene->mRootNode;
for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
{
pcNode->mMeshes[i] = i;
}
// now create the hierarchy of animated bones // now create the hierarchy of animated bones
pcNode = pScene->mRootNode->mChildren[1] = new aiNode(); aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
pcNode->mName.Set("MD5Anim"); pcNode->mName.Set("MD5Anim");
pcNode->mParent = pScene->mRootNode; pcNode->mParent = pScene->mRootNode;
AttachChilds(-1,pcNode,meshParser.mJoints); AttachChilds(-1,pcNode,meshParser.mJoints);
pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
pcNode->mName.Set("MD5Mesh");
pcNode->mParent = pScene->mRootNode;
std::vector<MD5::MeshDesc>::const_iterator end = meshParser.mMeshes.end();
// FIX: MD5 files exported from Blender can have empty meshes
for (std::vector<MD5::MeshDesc>::const_iterator
it = meshParser.mMeshes.begin(),
end = meshParser.mMeshes.end(); it != end;++it)
{
if (!(*it).mFaces.empty() && !(*it).mVertices.empty())
++pScene->mNumMaterials;
}
// generate all meshes // generate all meshes
pScene->mNumMeshes = pScene->mNumMaterials = (unsigned int)meshParser.mMeshes.size(); pScene->mNumMeshes = pScene->mNumMaterials;
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]; pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
// storage for node mesh indices
pcNode->mNumMeshes = pScene->mNumMeshes;
pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
for (unsigned int m = 0; m < pcNode->mNumMeshes;++m)
pcNode->mMeshes[m] = m;
unsigned int n = 0;
for (std::vector<MD5::MeshDesc>::iterator
it = meshParser.mMeshes.begin(),
end = meshParser.mMeshes.end(); it != end;++it)
{ {
aiMesh* mesh = pScene->mMeshes[i] = new aiMesh(); MD5::MeshDesc& meshSrc = *it;
MD5::MeshDesc& meshSrc = meshParser.mMeshes[i]; if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty())
continue;
aiMesh* mesh = pScene->mMeshes[n] = new aiMesh();
// generate unique vertices in our internal verbose format // generate unique vertices in our internal verbose format
MakeDataUnique(meshSrc); MakeDataUnique(meshSrc);
@ -424,9 +433,9 @@ void MD5Importer::LoadMD5MeshFile ()
// generate a material for the mesh // generate a material for the mesh
MaterialHelper* mat = new MaterialHelper(); MaterialHelper* mat = new MaterialHelper();
pScene->mMaterials[i] = mat; pScene->mMaterials[n] = mat;
mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0)); mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
mesh->mMaterialIndex = i; mesh->mMaterialIndex = n++;
} }
// delete the file again // delete the file again

View File

@ -194,10 +194,10 @@ void MDCImporter::SetupProperties(const Importer* pImp)
{ {
// The AI_CONFIG_IMPORT_MDC_KEYFRAME option overrides the // The AI_CONFIG_IMPORT_MDC_KEYFRAME option overrides the
// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
if(0xffffffff == (this->configFrameID = pImp->GetProperty( if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
AI_CONFIG_IMPORT_MDC_KEYFRAME,0xffffffff))) AI_CONFIG_IMPORT_MDC_KEYFRAME,0xffffffff)))
{ {
this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -443,7 +443,7 @@ void MDCImporter::InternReadFile(
pScene->mMeshes[i]->mTextureCoords[3] = NULL; pScene->mMeshes[i]->mTextureCoords[3] = NULL;
// create materials // create materials
pScene->mNumMaterials = aszShaders.size(); pScene->mNumMaterials = (unsigned int)aszShaders.size();
pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
{ {

View File

@ -51,11 +51,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// public ASSIMP headers // public ASSIMP headers
#include "../include/DefaultLogger.h" #include "../include/DefaultLogger.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h" #include "../include/aiScene.h"
#include "../include/aiAssert.h" #include "../include/aiAssert.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/assimp.hpp" #include "../include/assimp.hpp"
// boost headers // boost headers
@ -119,11 +118,12 @@ void MDLImporter::SetupProperties(const Importer* pImp)
{ {
// The AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the // The AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the
// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
if(0xffffffff == (this->configFrameID = pImp->GetProperty( if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
AI_CONFIG_IMPORT_MDL_KEYFRAME,0xffffffff))) AI_CONFIG_IMPORT_MDL_KEYFRAME,0xffffffff)))
{ {
this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
} }
this->configPalette = pImp->GetPropertyString(AI_CONFIG_IMPORT_MDL_COLORMAP,"colormap.lmp");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure. // Imports the given file into the given scene structure.

View File

@ -55,8 +55,7 @@ struct aiNode;
#include "MDLFileData.h" #include "MDLFileData.h"
#include "HalfLifeFileData.h" #include "HalfLifeFileData.h"
namespace Assimp namespace Assimp {
{
class MaterialHelper; class MaterialHelper;
using namespace MDL; using namespace MDL;
@ -442,6 +441,9 @@ protected:
/** Configuration option: frame to be loaded */ /** Configuration option: frame to be loaded */
unsigned int configFrameID; unsigned int configFrameID;
/** Configuration option: palette to be used to decode palletized images*/
std::string configPalette;
/** Buffer to hold the loaded file */ /** Buffer to hold the loaded file */
unsigned char* mBuffer; unsigned char* mBuffer;
@ -449,16 +451,13 @@ protected:
* (MDL7 doesn't need this, the format has a separate loader) */ * (MDL7 doesn't need this, the format has a separate loader) */
unsigned int iGSFileVersion; unsigned int iGSFileVersion;
/** Output I/O handler. used to load external lmp files /** Output I/O handler. used to load external lmp files */
*/
IOSystem* pIOHandler; IOSystem* pIOHandler;
/** Output scene to be filled /** Output scene to be filled */
*/
aiScene* pScene; aiScene* pScene;
/** Size of the input file in bytes /** Size of the input file in bytes */
*/
unsigned int iFileSize; unsigned int iFileSize;
}; };

View File

@ -61,7 +61,7 @@ using namespace Assimp;
void MDLImporter::SearchPalette(const unsigned char** pszColorMap) void MDLImporter::SearchPalette(const unsigned char** pszColorMap)
{ {
// now try to find the color map in the current directory // now try to find the color map in the current directory
IOStream* pcStream = this->pIOHandler->Open("colormap.lmp","rb"); IOStream* pcStream = this->pIOHandler->Open(configPalette,"rb");
const unsigned char* szColorMap = (const unsigned char*)::g_aclrDefaultColorMap; const unsigned char* szColorMap = (const unsigned char*)::g_aclrDefaultColorMap;
if(pcStream) if(pcStream)

View File

@ -268,7 +268,7 @@ uint32_t MaterialHelper::ComputeHash()
// NOTE: We need to exclude the material name from the hash // NOTE: We need to exclude the material name from the hash
if ((prop = this->mProperties[i]) && 0 != ::strcmp(prop->mKey.data,AI_MATKEY_NAME)) if ((prop = this->mProperties[i]) && 0 != ::strcmp(prop->mKey.data,AI_MATKEY_NAME))
{ {
hash = SuperFastHash(prop->mKey.data,prop->mKey.length,hash); hash = SuperFastHash(prop->mKey.data,(unsigned int)prop->mKey.length,hash);
hash = SuperFastHash(prop->mData,prop->mDataLength,hash); hash = SuperFastHash(prop->mData,prop->mDataLength,hash);
} }
} }
@ -371,8 +371,8 @@ aiReturn MaterialHelper::AddProperty (const aiString* pInput,
const char* pKey) const char* pKey)
{ {
// fix ... don't keep the whole string buffer // fix ... don't keep the whole string buffer
return this->AddBinaryProperty(pInput, return this->AddBinaryProperty(pInput,(unsigned int)pInput->length+1+
pInput->length+1+ (size_t)((uint8_t*)&pInput->data - (uint8_t*)&pInput->length), (unsigned int)(((uint8_t*)&pInput->data - (uint8_t*)&pInput->length)),
pKey,aiPTI_String); pKey,aiPTI_String);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -41,13 +41,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "ObjFileImporter.h" #include "ObjFileImporter.h"
#include "ObjFileParser.h" #include "ObjFileParser.h"
#include "ObjFileData.h" #include "ObjFileData.h"
#include "../include/IOStream.h" #include "MaterialSystem.h"
#include "../include/IOSystem.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h" #include "../include/aiScene.h"
#include "../include/aiAssert.h" #include "../include/aiAssert.h"
#include "../include/DefaultLogger.h" #include "../include/DefaultLogger.h"
#include "MaterialSystem.h" #include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
@ -237,7 +237,7 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile
if ( meshSizeDiff > 0 ) if ( meshSizeDiff > 0 )
{ {
pNode->mMeshes = new unsigned int[ meshSizeDiff ]; pNode->mMeshes = new unsigned int[ meshSizeDiff ];
pNode->mNumMeshes = meshSizeDiff; pNode->mNumMeshes = (unsigned int)meshSizeDiff;
size_t index = 0; size_t index = 0;
for (size_t i = oldMeshSize; i < MeshArray.size(); i++) for (size_t i = oldMeshSize; i < MeshArray.size(); i++)
{ {

View File

@ -306,8 +306,8 @@ void ObjFileParser::getFace()
// Store the face // Store the face
m_pModel->m_pCurrentMesh->m_Faces.push_back( face ); m_pModel->m_pCurrentMesh->m_Faces.push_back( face );
m_pModel->m_pCurrentMesh->m_uiNumIndices += face->m_pVertices->size(); m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int)face->m_pVertices->size();
m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += face->m_pTexturCoords[0].size(); m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += (unsigned int)face->m_pTexturCoords[0].size();
// Skip the rest of the line // Skip the rest of the line
m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
@ -462,7 +462,7 @@ int ObjFileParser::getMaterialIndex( const std::string &strMaterialName )
{ {
if ( strMaterialName == m_pModel->m_MaterialLib[ index ]) if ( strMaterialName == m_pModel->m_MaterialLib[ index ])
{ {
mat_index = index; mat_index = (int)index;
break; break;
} }
} }

View File

@ -0,0 +1,909 @@
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** Implementation of the OptimizeGraphProcess post-processing step*/
#include <vector>
#include <list>
#include "OptimizeGraphProcess.h"
#include "Hash.h"
#include "../include/aiPostProcess.h"
#include "../include/aiScene.h"
#include "../include/aiAssert.h"
#include "../include/assimp.hpp"
#include "../include/DefaultLogger.h"
using namespace Assimp;
// MSB for type unsigned int
#define AI_OG_UINT_MSB (1u<<((sizeof(unsigned int)*8u)-1u))
#define AI_OG_UINT_MSB_2 (AI_OG_UINT_MSB>>1)
// check whether a node/a mesh is locked
#define AI_OG_IS_NODE_LOCKED(nd) (nd->mNumChildren & AI_OG_UINT_MSB)
#define AI_OG_IS_MESH_LOCKED(ms) (ms->mNumBones & AI_OG_UINT_MSB)
// check whether a node has locked meshes in its list
#define AI_OG_HAS_NODE_LOCKED_MESHES(nd) (nd->mNumChildren & AI_OG_UINT_MSB_2)
// unmask the two upper bits of an unsigned int
#define AI_OG_UNMASK(p) (p & (~(AI_OG_UINT_MSB|AI_OG_UINT_MSB_2)))
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
OptimizeGraphProcess::OptimizeGraphProcess()
{
configRemoveAnimations = AI_OG_REMOVE_ANIMATIONS;
configMinNumFaces = AI_OG_MIN_NUM_FACES;
configJoinInequalTransforms = AI_OG_JOIN_INEQUAL_TRANSFORMS;
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
OptimizeGraphProcess::~OptimizeGraphProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_OptimizeGraph) != 0;
}
// ------------------------------------------------------------------------------------------------
// Setup properties of the step
void OptimizeGraphProcess::SetupProperties(const Importer* pImp)
{
// remove animation nodes?
configRemoveAnimations = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_REMOVE_ANIMATIONS,
AI_OG_REMOVE_ANIMATIONS) != 0 ? true : false;
// join nods with inequal transformations?
configRemoveAnimations = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_JOIN_INEQUAL_TRANSFORMS,
AI_OG_JOIN_INEQUAL_TRANSFORMS) != 0 ? true : false;
// minimum face number per node
configMinNumFaces = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_MIN_NUM_FACES,
AI_OG_MIN_NUM_FACES);
}
// ------------------------------------------------------------------------------------------------
aiNode* OptimizeGraphProcess::RemoveAnimationNodes (aiNode* node)
{
ai_assert(NULL != node);
std::vector<aiNode*> out;
RemoveAnimationNodes(node,out);
if (out.empty())
throw new ImportErrorException("OptimizeGraphProcess: no nodes are remaining.");
if (1 == out.size())
return out[0];
aiNode* p = new aiNode();
p->mName.Set("<dummy_root>");
p->mNumChildren = (unsigned int)out.size();
p->mChildren = new aiNode*[p->mNumChildren];
::memcpy(p->mChildren,&out[0],p->mNumChildren*sizeof(void*));
return p;
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::RemoveAnimationNodes (aiNode* node,std::vector<aiNode*>& out)
{
ai_assert(NULL != node);
// if this is an animation node: shift all children on this layer
if (!node->mNumMeshes)
{
unsigned int old = (unsigned int)out.size();
for (unsigned int i = 0; i < node->mNumChildren;++i)
{
RemoveAnimationNodes(node->mChildren[i],out);
}
// update the transformations of all shifted childs
std::vector<aiNode*>::iterator it2 = out.end(),it = out.begin()+old;
for (; it != it2; ++it)
(*it)->mTransformation = node->mTransformation * (*it)->mTransformation;
delete[] node->mChildren;node->mChildren = NULL;
delete node;
}
else
{
// *this* node remains on this layer, and the children, too
out.push_back(node);
std::vector<aiNode*> outNew;
for (unsigned int i = 0; i < node->mNumChildren;++i)
{
RemoveAnimationNodes(node->mChildren[i],outNew);
}
if (outNew.size() > node->mNumChildren)
{
delete[] node->mChildren;
node->mChildren = new aiNode*[outNew.size()];
}
node->mNumChildren = (unsigned int)outNew.size();
::memcpy(node->mChildren,&outNew[0],node->mNumChildren*sizeof(void*));
}
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::FindLockedNodes(aiNode* node)
{
ai_assert(NULL != node);
for (unsigned int i = 0; i < pScene->mNumAnimations;++i)
{
aiAnimation* pani = pScene->mAnimations[i];
for (unsigned int a = 0; a < pani->mNumBones;++a)
{
aiBoneAnim* pba = pani->mBones[a];
if (pba->mBoneName == node->mName)
{
// this node is locked
node->mNumChildren |= AI_OG_UINT_MSB;
}
}
}
// call all children
for (unsigned int i = 0; i < node->mNumChildren;++i)
FindLockedNodes(node->mChildren[i]);
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::FindLockedMeshes(aiNode* node, MeshRefCount* pRefCount)
{
ai_assert(NULL != node && NULL != pRefCount);
for (unsigned int i = 0;i < node->mNumMeshes;++i)
{
unsigned int m = node->mMeshes[i];
if (pRefCount[m].first)
{
// we have already one reference - lock the first node
// that had a referenced to this mesh too if it has only
// one mesh assigned. If there are multiple meshes,
// the others could still be used for optimizations.
if (pRefCount[m].second)
{
pRefCount[m].second->mNumChildren |= (pRefCount[m].second->mNumMeshes <= 1
? AI_OG_UINT_MSB : AI_OG_UINT_MSB_2);
pRefCount[m].second = NULL;
}
pScene->mMeshes[m]->mNumBones |= AI_OG_UINT_MSB;
// lock this node
node->mNumChildren |= (node->mNumMeshes <= 1
? AI_OG_UINT_MSB : AI_OG_UINT_MSB_2);
}
else pRefCount[m].second = node;
++pRefCount[m].first;
}
// call all children
for (unsigned int i = 0; i < node->mNumChildren;++i)
FindLockedMeshes(node->mChildren[i],pRefCount);
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::FindLockedMeshes(aiNode* node)
{
ai_assert(NULL != node);
MeshRefCount* pRefCount = new MeshRefCount[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pRefCount[i] = MeshRefCount();
// execute the algorithm
FindLockedMeshes(node,pRefCount);
delete[] pRefCount;
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::UnlockNodes(aiNode* node)
{
ai_assert(NULL != node);
node->mNumChildren &= ~(AI_OG_UINT_MSB|AI_OG_UINT_MSB_2);
// call all children
for (unsigned int i = 0; i < node->mNumChildren;++i)
UnlockNodes(node->mChildren[i]);
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::UnlockMeshes()
{
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pScene->mMeshes[i]->mNumBones &= ~AI_OG_UINT_MSB;
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::ComputeMeshHashes()
{
mMeshHashes.resize(pScene->mNumMeshes);
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
unsigned int iRet = 0;
aiMesh* pcMesh = pScene->mMeshes[i];
// normals
if (pcMesh->HasNormals())iRet |= 0x1;
// tangents and bitangents
if (pcMesh->HasTangentsAndBitangents())iRet |= 0x2;
// texture coordinates
unsigned int p = 0;
ai_assert(4 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
while (pcMesh->HasTextureCoords(p))
{
iRet |= (0x100 << p++);
// NOTE: meshes with numUVComp != 3 && != 2 aren't handled correctly here
ai_assert(pcMesh->mNumUVComponents[p] == 3 || pcMesh->mNumUVComponents[p] == 2);
if (3 == pcMesh->mNumUVComponents[p])
iRet |= (0x1000 << p++);
}
// vertex colors
p = 0;
ai_assert(4 >= AI_MAX_NUMBER_OF_COLOR_SETS);
while (pcMesh->HasVertexColors(p))iRet |= (0x10000 << p++);
mMeshHashes[i] = iRet;
// material index -store it in the upper 1 1/2 bytes, so
// are able to encode 2^12 material indices.
iRet |= (pcMesh->mMaterialIndex << 20u);
}
}
// ------------------------------------------------------------------------------------------------
inline unsigned int OptimizeGraphProcess::BinarySearch(NodeIndexList& sortedArray,
unsigned int min, unsigned int& index, unsigned int iStart)
{
unsigned int first = iStart,last = (unsigned int)sortedArray.size()-1;
while (first <= last)
{
unsigned int mid = (first + last) / 2;
unsigned int id = sortedArray[mid].second;
if (min > id)
first = mid + 1;
else if (min <= id)
{
last = mid - 1;
if (!mid || min > sortedArray[last].second)
{
index = sortedArray[last].first;
return mid;
}
}
}
return (unsigned int)sortedArray.size();
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::BuildUniqueBoneList(
std::vector<aiMesh*>::const_iterator it,
std::vector<aiMesh*>::const_iterator end,
std::list<BoneWithHash>& asBones)
{
unsigned int iOffset = 0;
for (; it != end;++it)
{
for (unsigned int l = 0; l < (*it)->mNumBones;++l)
{
aiBone* p = (*it)->mBones[l];
uint32_t itml = SuperFastHash(p->mName.data,(unsigned int)p->mName.length);
std::list<BoneWithHash>::iterator it2 = asBones.begin();
std::list<BoneWithHash>::iterator end2 = asBones.end();
for (;it2 != end2;++it2)
{
if ((*it2).first == itml)
{
(*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset));
break;
}
}
if (end2 == it2)
{
// need to begin a new bone entry
asBones.push_back(BoneWithHash());
BoneWithHash& btz = asBones.back();
// setup members
btz.first = itml;
btz.second = &p->mName;
btz.pSrcBones.push_back(BoneSrcIndex(p,iOffset));
}
}
iOffset += (*it)->mNumVertices;
}
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::JoinBones(
std::vector<aiMesh*>::const_iterator it,
std::vector<aiMesh*>::const_iterator end,
aiMesh* out)
{
ai_assert(NULL != out);
// find we need to build an unique list of all bones.
// we work with hashes to make the comparisons MUCH faster,
// at least if we have many bones.
std::list<BoneWithHash> asBones;
BuildUniqueBoneList(it,end,asBones);
// now create the output bones
out->mBones = new aiBone*[asBones.size()];
for (std::list<BoneWithHash>::const_iterator it = asBones.begin(),
end = asBones.end(); it != end;++it)
{
aiBone* pc = out->mBones[out->mNumBones++] = new aiBone();
pc->mName = aiString( *((*it).second ));
// get an itrator to the end of the list
std::vector< BoneSrcIndex >::const_iterator wend = (*it).pSrcBones.end();
// loop through all bones to be joined for this bone
for (std::vector< BoneSrcIndex >::const_iterator
wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)
{
pc->mNumWeights += (*wmit).first->mNumWeights;
// NOTE: different offset matrices for bones with equal names
// are - at the moment - not handled correctly.
if (wmit != (*it).pSrcBones.begin() &&
pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix)
{
DefaultLogger::get()->warn("Bones with equal names but different "
"offset matrices can't be joined at the moment. If this causes "
"problems, deactivate the OptimizeGraph-Step");
continue;
}
pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix;
}
// allocate the vertex weight array
aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights];
// and copy the final weights - adjust the vertex IDs by the
// face index offset of the coresponding mesh.
for (std::vector< BoneSrcIndex >::const_iterator
wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)
{
aiBone* pip = (*wmit).first;
for (unsigned int mp = 0; mp < pip->mNumWeights;++mp)
{
aiVertexWeight& vf = aiVertexWeight(pip->mWeights[mp]);
vf.mVertexId += (*wmit).second;
}
}
}
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::JoinMeshes(std::vector<aiMesh*>& meshList,
aiMesh*& out, unsigned int max)
{
ai_assert(NULL != out && 0 != max);
out->mMaterialIndex = meshList[0]->mMaterialIndex;
// allocate the output mesh
out = new aiMesh();
std::vector<aiMesh*>::const_iterator end = meshList.begin()+max;
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
out->mNumVertices += (*it)->mNumVertices;
out->mNumFaces += (*it)->mNumFaces;
out->mNumBones += AI_OG_UNMASK((*it)->mNumBones);
}
if (out->mNumVertices) // just for safety
{
aiVector3D* pv2;
// copy vertex positions
if (meshList[0]->HasPositions())
{
pv2 = out->mVertices = new aiVector3D[out->mNumVertices];
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D));
pv2 += (*it)->mNumVertices;
}
}
// copy normals
if (meshList[0]->HasNormals())
{
pv2 = out->mNormals = new aiVector3D[out->mNumVertices];
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D));
pv2 += (*it)->mNumVertices;
}
}
// copy tangents and bitangents
if (meshList[0]->HasTangentsAndBitangents())
{
pv2 = out->mTangents = new aiVector3D[out->mNumVertices];
aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices];
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D));
::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D));
pv2 += (*it)->mNumVertices;
pv2b += (*it)->mNumVertices;
}
}
// copy texture coordinates
unsigned int n = 0;
while (meshList[0]->HasTextureCoords(n))
{
out->mNumUVComponents[n] = meshList[0]->mNumUVComponents[n];
pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices];
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D));
pv2 += (*it)->mNumVertices;
}
++n;
}
// copy vertex colors
n = 0;
while (meshList[0]->HasVertexColors(n))
{
aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D));
pv2 += (*it)->mNumVertices;
}
++n;
}
}
if (out->mNumFaces) // just for safety
{
// copy faces
out->mFaces = new aiFace[out->mNumFaces];
aiFace* pf2 = out->mFaces;
unsigned int ofs = 0;
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
for (unsigned int m = 0; m < (*it)->mNumFaces;++m,++pf2)
{
aiFace& face = (*it)->mFaces[m];
pf2->mNumIndices = face.mNumIndices;
pf2->mIndices = face.mIndices;
if (ofs)
{
// add the offset to the vertex
for (unsigned int q = 0; q < face.mNumIndices; ++q)
face.mIndices[q] += ofs;
}
ofs += (*it)->mNumVertices;
face.mIndices = NULL;
}
}
}
// bones - as this is quite lengthy, I moved the code to a separate function
if (out->mNumBones)JoinBones(meshList.begin(),end,out);
// delete all source meshes
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
delete *it;
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::ApplyNodeMeshesOptimization(aiNode* pNode)
{
ai_assert(NULL != pNode);
// find all meshes which are compatible and could therefore be joined.
// we can't join meshes that are locked
std::vector<aiMesh*> apcMeshes(pNode->mNumMeshes);
unsigned int iNumMeshes;
for (unsigned int m = 0, ttt = 0; m < pNode->mNumMeshes;++m)
{
iNumMeshes = 0;
unsigned int nm = pNode->mMeshes[m];
if (0xffffffff == nm || AI_OG_IS_MESH_LOCKED(pScene->mMeshes[nm]))continue;
for (unsigned int q = m+1; q < pNode->mNumMeshes;++q)
{
register unsigned int nq = pNode->mMeshes[q];
// skip locked meshes
if (AI_OG_IS_MESH_LOCKED(pScene->mMeshes[nq]))continue;
// compare the mesh hashes
if (mMeshHashes[nm] == mMeshHashes[nq])
{
apcMeshes[iNumMeshes++] = pScene->mMeshes[nq];
pNode->mMeshes[q] = 0xffffffff;
}
}
aiMesh* out;
if (iNumMeshes > 0)
{
apcMeshes[iNumMeshes++] = pScene->mMeshes[nm];
JoinMeshes(apcMeshes,out,iNumMeshes);
}
else out = pScene->mMeshes[nm];
pNode->mMeshes[ttt++] = (unsigned int)mOutputMeshes.size();
mOutputMeshes.push_back(out);
}
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::TransformMeshes(aiNode* quak,aiNode* pNode)
{
for (unsigned int ä = 0; ä < quak->mNumMeshes;++ä)
{
aiMesh* mariusIsHot = pScene->mMeshes[quak->mMeshes[ä]];
aiMatrix4x4 mMatTransform = pNode->mTransformation;
// transformation: first back to the parent's local space,
// later into the local space of the destination child node
mMatTransform.Inverse();
mMatTransform = quak->mTransformation * mMatTransform;
// transform all vertices
for (unsigned int oo =0; oo < mariusIsHot->mNumVertices;++oo)
mariusIsHot->mVertices[oo] = mMatTransform * mariusIsHot->mVertices[oo];
// transform all normal vectors
if (mariusIsHot->HasNormals())
{
mMatTransform.Inverse().Transpose();
for (unsigned int oo =0; oo < mariusIsHot->mNumVertices;++oo)
mariusIsHot->mNormals[oo] = mMatTransform * mariusIsHot->mNormals[oo];
}
}
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::ApplyOptimizations(aiNode* node)
{
ai_assert(NULL != node);
unsigned int iJoinedIndex = 0;
// first: node index; second: number of faces in node
NodeIndexList aiBelowTreshold;
aiBelowTreshold.reserve(node->mNumChildren);
for (unsigned int i = 0; i < node->mNumChildren;++i)
{
aiNode* pChild = node->mChildren[i];
if (AI_OG_IS_NODE_LOCKED(pChild) || !pChild->mNumMeshes)continue;
// find out how many faces this node is referencing
unsigned int iFaceCnt = 0;
for (unsigned int a = 0; a < pChild->mNumMeshes;++a)
iFaceCnt += pScene->mMeshes[pChild->mMeshes[a]]->mNumFaces;
// are we below the treshold?
if (iFaceCnt < configMinNumFaces)
{
aiBelowTreshold.push_back(NodeIndexEntry());
NodeIndexEntry& p = aiBelowTreshold.back();
p.first = i;
p.second = iFaceCnt;
p.pNode = pChild;
}
}
if (!aiBelowTreshold.empty())
{
// some typedefs for the data structures we'll need
typedef std::pair<unsigned int, unsigned int> JoinListEntry;
std::vector<JoinListEntry> aiJoinList(aiBelowTreshold.size());
std::vector<unsigned int> aiTempList(aiBelowTreshold.size());
unsigned int iNumJoins, iNumTemp;
// sort the list by size
std::sort(aiBelowTreshold.begin(),aiBelowTreshold.end());
unsigned int iStart = 0;
for (NodeIndexList::const_iterator it = aiBelowTreshold.begin(),end = aiBelowTreshold.end();
it != end; /*++it */++iStart)
{
aiNode* pNode = node->mChildren[(*it).first];
// get the hash of the mesh
const unsigned int iMeshVFormat = mMeshHashes[pNode->mMeshes[0]];
// we search for a node with more faces than this ... find
// the one that fits best and continue until we've reached
// treshold size.
int iDiff = configMinNumFaces-(*it).second;
for (;;)
{
// do a binary search and start the iteration there
unsigned int index;
unsigned int start = BinarySearch(aiBelowTreshold,iDiff,index,iStart);
if (index == (*it).first)start++;
if (start >= aiBelowTreshold.size())
{
// there is no node with enough faces. take the first
start = 0;
}
// todo: implement algorithm to find the best possible combination ...
iNumTemp = 0;
while( start < aiBelowTreshold.size())
{
// check whether the node has akready been processed before
const NodeIndexEntry& entry = aiBelowTreshold[start];
if (!entry.pNode)continue;
const aiNode* pip = node->mChildren[entry.first];
if (configJoinInequalTransforms )
{
// we need to check whether this node has locked meshes
// in this case we can't add it here - the meshes will
// be transformed from one to another coordinate space
if (!AI_OG_HAS_NODE_LOCKED_MESHES(pip) || pip->mTransformation == pNode->mTransformation)
aiTempList[iNumTemp++] = start;
}
else if (node->mChildren[entry.first]->mTransformation == pNode->mTransformation)
{
aiTempList[iNumTemp++] = start;
break;
}
++start;
}
if (iNumTemp)
{
// search for a node which has a mesh with
// - the same material index
// - the same vertex layout
unsigned int d = iNumJoins = 0;
for (unsigned int m = 0; m < iNumTemp;++m)
{
register unsigned int mn = aiTempList[m];
aiNode* pip = aiBelowTreshold[mn].pNode;
for (unsigned int tt = 0; tt < pip->mNumMeshes;++tt)
{
register unsigned int mm = pip->mMeshes[tt];
if (mMeshHashes [ mm ] == iMeshVFormat)
{
d = mn;
goto break_out;
}
}
}
break_out:
aiJoinList[iNumJoins++] = JoinListEntry( aiBelowTreshold[d].first, d );
iDiff -= aiBelowTreshold[d].second;
}
// did we reach the target treshold?
if (iDiff <= 0)break;
}
// did we found any nodes to be joined with *this* one?
if (iNumJoins)
{
unsigned int iNumTotalChilds = pNode->mNumChildren;
unsigned int iNumTotalMeshes = pNode->mNumMeshes;
std::vector<JoinListEntry>::const_iterator wend = aiJoinList.begin()+iNumJoins;
// get output array bounds
for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
wit != wend;++wit )
{
aiNode*& quak = node->mChildren[(*wit).first];
iNumTotalChilds += AI_OG_UNMASK( quak->mNumChildren );
iNumTotalMeshes += quak->mNumMeshes;
}
// build the output child list
if (iNumTotalChilds != pNode->mNumChildren)
{
aiNode** ppc = pNode->mChildren;
delete[] pNode->mChildren;
pNode->mChildren = new aiNode*[iNumTotalChilds];
::memcpy(pNode->mChildren,ppc, sizeof(void*)* AI_OG_UNMASK( pNode->mNumChildren ));
for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
wit != wend;++wit )
{
aiNode*& quak = node->mChildren[(*wit).first];
::memcpy(pNode->mChildren+pNode->mNumChildren,
quak->mChildren, sizeof(void*)*quak->mNumChildren);
pNode->mNumChildren += AI_OG_UNMASK( quak->mNumChildren );
}
}
// build the output mesh list
unsigned int* ppc = pNode->mMeshes;
delete[] pNode->mMeshes;
pNode->mMeshes = new unsigned int[iNumTotalMeshes];
::memcpy(pNode->mMeshes,ppc, sizeof(void*)*pNode->mNumMeshes);
for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
wit != wend;++wit )
{
aiNode*& quak = node->mChildren[(*wit).first];
::memcpy(pNode->mMeshes+pNode->mNumMeshes,
quak->mMeshes, sizeof(unsigned int)*quak->mNumMeshes);
// if the node has a transformation matrix that is not equal to ours,
// we'll need to transform all vertices of the mesh into our
// local coordinate space.
if (configJoinInequalTransforms && quak->mTransformation != pNode->mTransformation)
TransformMeshes(quak,pNode);
pNode->mNumMeshes += quak->mNumMeshes;
// remove the joined nodes from all lists.
aiBelowTreshold[(*wit).second].pNode = NULL;
if ((*wit).second == iStart+1)++iStart;
}
// now generate an output name for the joined nodes
if (1 == iNumTotalChilds)
{
pNode->mName.length = ::sprintf( pNode->mName.data, "<Joined_%i_%i>",
iJoinedIndex++,iNumJoins+1);
}
}
// now optimize the meshes in this node
ApplyNodeMeshesOptimization(pNode);
// note - this has been optimized away. The search in the binary
// list starts with iStart, which is incremented each iteration
++it; // = aiBelowTreshold.erase(it);
}
}
// call all children recursively
for (unsigned int i = 0; i < node->mNumChildren;++i)
ApplyOptimizations(node->mChildren[i]);
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::BuildOutputMeshList()
{
// all meshes should have been deleted before if they are
// not contained in the new mesh list
if (pScene->mNumMeshes < mOutputMeshes.size())
{
delete[] pScene->mMeshes;
pScene->mMeshes = new aiMesh*[mOutputMeshes.size()];
}
pScene->mNumMeshes = (unsigned int)mOutputMeshes.size();
::memcpy(pScene->mMeshes,&mOutputMeshes[0],pScene->mNumMeshes*sizeof(void*));
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void OptimizeGraphProcess::Execute( aiScene* pScene)
{
this->pScene = pScene;
/*
a) the term "mesh node" stands for a node with numMeshes > 0
b) the term "animation node" stands for a node with numMeshes == 0,
regardless whether the node is referenced by animation channels.
Algorithm:
1. Compute hashes for all meshes that we're able to check whether
two meshes are compatible.
2. Remove animation nodes if we have been configured to do so
3. Find out which nodes may not be moved, so to speak are "locked" - a
locked node will never be joined with neighbors.
- A node lock is indicated by a set MSB in the aiNode::mNumChildren member
4. Find out which meshes are locked - they are referenced by
more than one node. They will never be joined. Mark all
nodes referencing such a mesh as "locked", too.
- A mesh lock is indicated by a set MSB in the aiMesh::mNumBones member
5. For each unlocked node count the face numbers of all assigned meshes
- if it is below the pre-defined treshold add the node to a list.
For each node in the list - try to find enough joinable nodes to
have enough faces all together.
Two nodes are joined if:
- none of them is locked
- (optional) their world matrices are identical
- nodes whose meshes share the same material indices are prefered
Two meshes in one node are joined if:
- their material indices are identical
- none of them is locked
- they share the same vertex format
6. Build the final mesh list
7. For all meshes and all nodes - remove locks.
*/
throw new ImportErrorException("OG step is still undeer development and not yet finished");
// STEP 1
ComputeMeshHashes();
// STEP 2
if (configRemoveAnimations)
pScene->mRootNode = RemoveAnimationNodes(pScene->mRootNode);
// STEP 3
else FindLockedNodes(pScene->mRootNode);
// STEP 4
FindLockedMeshes(pScene->mRootNode);
// STEP 5
ApplyOptimizations(pScene->mRootNode);
// STEP 6
BuildOutputMeshList();
// STEP 7
UnlockNodes(pScene->mRootNode);
UnlockMeshes();
}

View File

@ -0,0 +1,340 @@
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** Defines a post processing step to refactor the output node graph to
be more compact */
#ifndef AI_OPTIMIZEGRAPHPROCESS_H_INC
#define AI_OPTIMIZEGRAPHPROCESS_H_INC
#include "BaseProcess.h"
struct aiMesh;
struct aiNode;
struct aiBone;
class OptimizeGraphProcessTest;
namespace Assimp {
// NOTE: If you change these limits, don't forget to change the
// corresponding values in all Assimp ports
// **********************************************************
// Java: ConfigProperty.java,
// ConfigProperty.OG_MAX_HIERARCHY_DEPTH
// ConfigProperty.OG_MIN_NUM_FACES
// ConfigProperty.JOIN_INEQUAL_TRANSFORMS
// **********************************************************
#if (!defined AI_OG_MAX_DEPTH)
# define AI_OG_MAX_DEPTH 0x4
#endif // !! AI_LMW_MAX_WEIGHTS
#if (!defined AI_OG_MIN_NUM_FACES)
# define AI_OG_MIN_NUM_FACES 0xffffffff
#endif // !! AI_LMW_MAX_WEIGHTS
#if (!defined AI_OG_REMOVE_ANIMATIONS)
# define AI_OG_REMOVE_ANIMATIONS false
#endif // !! AI_LMW_MAX_WEIGHTS
#if (!defined AI_OG_JOIN_INEQUAL_TRANSFORMS)
# define AI_OG_JOIN_INEQUAL_TRANSFORMS false
#endif // !! AI_LMW_MAX_WEIGHTS
// ---------------------------------------------------------------------------
struct NodeIndexEntry : public std::pair<unsigned int, unsigned int>
{
// binary operator < for use with std::sort
bool operator< (const NodeIndexEntry& other)
{
return second < other.second;
}
// pointer to the original node
aiNode* pNode;
};
typedef std::vector<NodeIndexEntry> NodeIndexList;
typedef std::pair<aiBone*,unsigned int> BoneSrcIndex;
// ---------------------------------------------------------------------------
struct BoneWithHash : public std::pair<uint32_t,aiString*>
{
std::vector<BoneSrcIndex> pSrcBones;
};
// ---------------------------------------------------------------------------
/** This post processing step reformats the output node graph to be more
* compact. There are several options, e.g. maximum hierachy depth or
* minimum mesh size. Some files store every face as a new mesh, so
* some Assimp steps (such as FixInfacingNormals or SmoothNormals) don't
* work properly on these meshes. This step joins such small meshes and
* nodes. Animations are kept during the step.
* @note Use the PretransformVertices step to remove the node graph
* completely (and all animations, too).
*/
class ASSIMP_API OptimizeGraphProcess : public BaseProcess
{
friend class Importer;
friend class ::OptimizeGraphProcessTest;
protected:
/** Constructor to be privately used by Importer */
OptimizeGraphProcess();
/** Destructor, private as well */
~OptimizeGraphProcess();
typedef std::pair< unsigned int, aiNode* > MeshRefCount;
typedef unsigned int MeshHash;
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag.
* @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;
// -------------------------------------------------------------------
/** Called prior to ExecuteOnScene().
* The function is a request to the process to update its configuration
* basing on the Importer's configuration property list.
*/
void SetupProperties(const Importer* pImp);
// set the configMinNumfaces property
inline void SetMinNumFaces(unsigned int n)
{
configMinNumFaces = n;
}
// set the configRemoveAnimations property
inline void SetRemoveAnimations(bool b)
{
configRemoveAnimations = b;
}
protected:
// -------------------------------------------------------------------
/** 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);
// -------------------------------------------------------------------
/** Removes animation nodes from the tree.
* @param node Current node
* @param out Receives a list of replacement nodes for *this* node -
* if *this* node should be kept, it must be added to the list.
*/
void RemoveAnimationNodes (aiNode* node,std::vector<aiNode*>& out);
// -------------------------------------------------------------------
/** Entry point to the RemoveAnimationNodes algorithm.
* @param node Root node to start with
* @return New root node
*/
aiNode* RemoveAnimationNodes (aiNode* node);
// -------------------------------------------------------------------
/** Finds and marks all locked nodes in the tree.
* A node is locked if it is referenced by animations.
* @param node ROot node to start with
* @note A locked node has the MSB set in its mNumChildren member
*/
void FindLockedNodes(aiNode* node);
// -------------------------------------------------------------------
/** Searches for locked meshes. A mesh is locked if it is referenced
* by more than one node in the hierarchy.
* @param node Root node to start with
*/
void FindLockedMeshes(aiNode* node);
void FindLockedMeshes(aiNode* node, MeshRefCount* pRefCount);
// -------------------------------------------------------------------
/** Unlocks all nodes in the output tree.
* @param node Root node to start with
*/
void UnlockNodes(aiNode* node);
// -------------------------------------------------------------------
/** Unlocks all meshes in the output tree.
*/
void UnlockMeshes();
// -------------------------------------------------------------------
/** Apply the final optimization algorithm to the tree.
* See the Execute-method for a detailled description of the algorithm.
* @param node Root node to start with
*/
void ApplyOptimizations(aiNode* node);
// -------------------------------------------------------------------
/** Binary search for the first element that is >= min.
* @param sortedArray Input array
* @param min Treshold
*/
unsigned int BinarySearch(NodeIndexList& sortedArray,
unsigned int min, unsigned int& index, unsigned int iStart);
// -------------------------------------------------------------------
/** Compute stable hashes for all meshes and fill mMeshHashes
* with the results.
*/
void ComputeMeshHashes();
// -------------------------------------------------------------------
/** Optimizes the meshes in a single node aftr the joining process.
* @param pNode Node to be optimized. Childs noded won't be processed
* automatically.
*/
void ApplyNodeMeshesOptimization(aiNode* pNode);
// -------------------------------------------------------------------
/** Join meshes.
* The output meshes are deleted afterwards.
* @param meshList List of meshes to be joined
* @param out Receives a pointer to the output mesh.
*/
void JoinMeshes(std::vector<aiMesh*>& meshList,aiMesh*& out,
unsigned int max);
// -------------------------------------------------------------------
/** Join bones from a collection of meshes.
*
* @param it First mesh to be processed
* @param end Last mesh to be processed
* @param out Valid output mesh to receive the output bone list.
*/
void JoinBones(std::vector<aiMesh*>::const_iterator it,
std::vector<aiMesh*>::const_iterator end,
aiMesh* out);
// -------------------------------------------------------------------
/** Build a list of unique bones from a collection of meshes.
*
* @param it First mesh to be processed
* @param end Last mesh to be processed
* @param asBones Receives a list of unique bones
*/
void BuildUniqueBoneList(std::vector<aiMesh*>::const_iterator it,
std::vector<aiMesh*>::const_iterator end,
std::list<BoneWithHash>& asBones);
// -------------------------------------------------------------------
/** Build the output mesh list.
*/
void BuildOutputMeshList();
// -------------------------------------------------------------------
/** Transform meshes from one coordinate space into another.
* @param quak Input space. All meshes referenced by this node -
* assuming none of them is locked - are transformed in the
* local coordinate space of pNode
* @param pNode Destination coordinate space
*/
void TransformMeshes(aiNode* quak,aiNode* pNode);
private:
/** Configuration option: specifies the minimum number of faces
a node should have. The steps tries to join meshes with less
vertices that are on the same hierarchy level.
If this value is set to a very large value (e.g. 0xffffffff)
all meshes on the same hierarchy level are joined - if they
aren't animation nodes and if they have the same world matrices
*/
unsigned int configMinNumFaces;
/** Configuration option: specifies whether animations are removed
from the node graph. If animations aren't needed by the caller,
this allows for further optimization.
*/
bool configRemoveAnimations;
/** Configuration option: specifies whether nodes with inequal
world matrices are joined if they are on the same hierarchy
level and if it seems to make sense.
*/
bool configJoinInequalTransforms;
/** Working data */
aiScene* pScene;
/** List of hash identifiers for all meshes.
The hashes are build from both the meshes vertex format
and the material indices. Bones are not taken into account.
*/
std::vector<MeshHash> mMeshHashes;
/** List of output meshes.
*/
std::vector<aiMesh*> mOutputMeshes;
};
} // end of namespace Assimp
#endif // AI_LIMITBONEWEIGHTSPROCESS_H_INC

View File

@ -40,16 +40,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** @file Implementation of the PLY importer class */ /** @file Implementation of the PLY importer class */
// internal headers
#include "PlyLoader.h" #include "PlyLoader.h"
#include "MaterialSystem.h" #include "MaterialSystem.h"
#include "StringComparison.h" #include "StringComparison.h"
// public ASSIMP headers
#include "../include/IOStream.h" #include "../include/IOStream.h"
#include "../include/IOSystem.h" #include "../include/IOSystem.h"
#include "../include/aiMesh.h" #include "../include/aiMesh.h"
#include "../include/aiScene.h" #include "../include/aiScene.h"
#include "../include/aiAssert.h" #include "../include/aiAssert.h"
#include "../include/DefaultLogger.h"
// boost headeers
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
using namespace Assimp; using namespace Assimp;
@ -95,24 +100,19 @@ void PLYImporter::InternReadFile(
// Check whether we can read from the file // Check whether we can read from the file
if( file.get() == NULL) if( file.get() == NULL)
{
throw new ImportErrorException( "Failed to open PLY file " + pFile + "."); throw new ImportErrorException( "Failed to open PLY file " + pFile + ".");
}
// check whether the ply file is large enough to contain // check whether the ply file is large enough to contain
// at least the file header // at least the file header
size_t fileSize = file->FileSize(); size_t fileSize = file->FileSize();
if( fileSize < 10) if( fileSize < 10)
{
throw new ImportErrorException( "PLY File is too small."); throw new ImportErrorException( "PLY File is too small.");
}
// allocate storage and copy the contents of the file to a memory buffer // allocate storage and copy the contents of the file to a memory buffer
// (terminate it with zero) // (terminate it with zero)
// FIX: Allocate an extra buffer of 12.5% to be sure we won't crash std::vector<unsigned char> mBuffer2(fileSize+1);
// if an overrun occurs. file->Read( &mBuffer2[0], 1, fileSize);
this->mBuffer = new unsigned char[fileSize+1 + (fileSize>>3)]; this->mBuffer = &mBuffer2[0];
file->Read( (void*)mBuffer, 1, fileSize);
this->mBuffer[fileSize] = '\0'; this->mBuffer[fileSize] = '\0';
// the beginning of the file must be PLY // the beginning of the file must be PLY
@ -120,8 +120,6 @@ void PLYImporter::InternReadFile(
this->mBuffer[1] != 'L' && this->mBuffer[1] != 'l' || this->mBuffer[1] != 'L' && this->mBuffer[1] != 'l' ||
this->mBuffer[2] != 'Y' && this->mBuffer[2] != 'y') this->mBuffer[2] != 'Y' && this->mBuffer[2] != 'y')
{ {
delete[] this->mBuffer;
AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there"); throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there");
} }
char* szMe = (char*)&this->mBuffer[3]; char* szMe = (char*)&this->mBuffer[3];
@ -137,11 +135,7 @@ void PLYImporter::InternReadFile(
szMe += 6; szMe += 6;
SkipLine(szMe,(const char**)&szMe); SkipLine(szMe,(const char**)&szMe);
if(!PLY::DOM::ParseInstance(szMe,&sPlyDom)) if(!PLY::DOM::ParseInstance(szMe,&sPlyDom))
{
delete[] this->mBuffer;
AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#1)"); throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#1)");
}
} }
else if (0 == ASSIMP_strincmp(szMe,"binary_",7)) else if (0 == ASSIMP_strincmp(szMe,"binary_",7))
{ {
@ -159,18 +153,9 @@ void PLYImporter::InternReadFile(
// skip the line, parse the rest of the header and build the DOM // skip the line, parse the rest of the header and build the DOM
SkipLine(szMe,(const char**)&szMe); SkipLine(szMe,(const char**)&szMe);
if(!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE)) if(!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE))
{
delete[] this->mBuffer;
AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#2)"); throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#2)");
}
}
else
{
delete[] this->mBuffer;
AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
throw new ImportErrorException( "Invalid .ply file: Unknown file format");
} }
else throw new ImportErrorException( "Invalid .ply file: Unknown file format");
} }
else else
{ {
@ -185,12 +170,8 @@ void PLYImporter::InternReadFile(
this->LoadVertices(&avPositions,false); this->LoadVertices(&avPositions,false);
if (avPositions.empty()) if (avPositions.empty())
{
delete[] this->mBuffer;
AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
throw new ImportErrorException( "Invalid .ply file: No vertices found. " throw new ImportErrorException( "Invalid .ply file: No vertices found. "
"Unable to interpret the data format of the PLY file"); "Unable to parse the data format of the PLY file.");
}
// now load a list of normals. // now load a list of normals.
std::vector<aiVector3D> avNormals; std::vector<aiVector3D> avNormals;
@ -206,10 +187,8 @@ void PLYImporter::InternReadFile(
{ {
if (avPositions.size() < 3) if (avPositions.size() < 3)
{ {
delete[] this->mBuffer; throw new ImportErrorException( "Invalid .ply file: Not enough "
AI_DEBUG_INVALIDATE_PTR(this->mBuffer); "vertices to build a face list. ");
throw new ImportErrorException( "Invalid .ply file: Not enough vertices to build "
"a face list. ");
} }
unsigned int iNum = (unsigned int)avPositions.size() / 3; unsigned int iNum = (unsigned int)avPositions.size() / 3;
@ -244,11 +223,7 @@ void PLYImporter::InternReadFile(
&avColors,&avTexCoords,&avMaterials,&avMeshes); &avColors,&avTexCoords,&avMaterials,&avMeshes);
if (avMeshes.empty()) if (avMeshes.empty())
{
delete[] this->mBuffer;
AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
throw new ImportErrorException( "Invalid .ply file: Unable to extract mesh data "); throw new ImportErrorException( "Invalid .ply file: Unable to extract mesh data ");
}
// now generate the output scene object. Fill the material list // now generate the output scene object. Fill the material list
pScene->mNumMaterials = (unsigned int)avMaterials.size(); pScene->mNumMaterials = (unsigned int)avMaterials.size();
@ -269,12 +244,6 @@ void PLYImporter::InternReadFile(
for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes;++i) for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes;++i)
pScene->mRootNode->mMeshes[i] = i; pScene->mRootNode->mMeshes[i] = i;
// delete the file buffer
delete[] this->mBuffer;AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
// DOM is lying on the stack, will be deconstructed automatically
return;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void PLYImporter::ConvertMeshes(std::vector<PLY::Face>* avFaces, void PLYImporter::ConvertMeshes(std::vector<PLY::Face>* avFaces,
@ -819,9 +788,8 @@ void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
if (3 > iNum) if (3 > iNum)
{ {
// We must filter out all degenerates. Leave a message // We must filter out all degenerates.
// in the log ... DefaultLogger::get()->warn("PLY: Found degenerated triangle");
// LOG
continue; continue;
} }

View File

@ -52,17 +52,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
PretransformVertices::PretransformVertices() PretransformVertices::PretransformVertices()
{ {
} }
// ------------------------------------------------------------------------------------------------
// Destructor, private as well // Destructor, private as well
PretransformVertices::~PretransformVertices() PretransformVertices::~PretransformVertices()
{ {
// nothing to do here // nothing to do here
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
bool PretransformVertices::IsActive( unsigned int pFlags) const bool PretransformVertices::IsActive( unsigned int pFlags) const

View File

@ -12,18 +12,18 @@ with or without modification, are permitted provided that the following
conditions are met: conditions are met:
* Redistributions of source code must retain the above * Redistributions of source code must retain the above
copyright notice, this list of conditions and the copyright notice, this list of conditions and the
following disclaimer. following disclaimer.
* Redistributions in binary form must reproduce the above * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other following disclaimer in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its * Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products contributors may be used to endorse or promote products
derived from this software without specific prior derived from this software without specific prior
written permission of the ASSIMP Development Team. written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@ -40,66 +40,53 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** @file Implementation of the helper class to quickly find /** @file Implementation of the helper class to quickly find
vertices close to a given position. Special implementation for vertices close to a given position. Special implementation for
the 3ds loader handling smooth groups correctly */ the 3ds loader handling smooth groups correctly */
#include <algorithm> #include <algorithm>
#include "3DSSpatialSort.h" #include "SGSpatialSort.h"
#include "../include/aiAssert.h" #include "../include/aiAssert.h"
using namespace Assimp; using namespace Assimp;
using namespace Assimp::Dot3DS;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
D3DSSpatialSorter::D3DSSpatialSorter() SGSpatialSort::SGSpatialSort()
{ {
// define the reference plane. We choose some arbitrary vector away from all basic axises // 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. // in the hope that no model spreads all its vertices along this plane.
mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f); mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
mPlaneNormal.Normalize(); mPlaneNormal.Normalize();
}
// ------------------------------------------------------------------------------------------------
// Destructor
D3DSSpatialSorter::~D3DSSpatialSorter()
{
// nothing to do here, everything destructs automatically
}
// ------------------------------------------------------------------------------------------------
void D3DSSpatialSorter::AddFace(const Dot3DS::Face* pcFace,
const std::vector<aiVector3D>& vPositions)
{
ai_assert(NULL != pcFace);
// store position by index and distance
float distance = vPositions[pcFace->mIndices[0]] * mPlaneNormal;
mPositions.push_back( Entry( pcFace->mIndices[0], vPositions[pcFace->mIndices[0]],
distance, pcFace->iSmoothGroup));
// triangle vertex 2
distance = vPositions[pcFace->mIndices[1]] * mPlaneNormal;
mPositions.push_back( Entry( pcFace->mIndices[1], vPositions[pcFace->mIndices[1]],
distance, pcFace->iSmoothGroup));
// triangle vertex 3
distance = vPositions[pcFace->mIndices[2]] * mPlaneNormal;
mPositions.push_back( Entry( pcFace->mIndices[2], vPositions[pcFace->mIndices[2]],
distance, pcFace->iSmoothGroup));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void D3DSSpatialSorter::Prepare() // Destructor
SGSpatialSort::~SGSpatialSort()
{
// nothing to do here, everything destructs automatically
}
// ------------------------------------------------------------------------------------------------
void SGSpatialSort::Add(const aiVector3D& vPosition, unsigned int index,
unsigned int smoothingGroup)
{
// store position by index and distance
float distance = vPosition * mPlaneNormal;
mPositions.push_back( Entry( index, vPosition,
distance, smoothingGroup));
}
// ------------------------------------------------------------------------------------------------
void SGSpatialSort::Prepare()
{ {
// now sort the array ascending by distance. // now sort the array ascending by distance.
std::sort( this->mPositions.begin(), this->mPositions.end()); std::sort( this->mPositions.begin(), this->mPositions.end());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns an iterator for all positions close to the given position. // Returns an iterator for all positions close to the given position.
void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition, void SGSpatialSort::FindPositions( const aiVector3D& pPosition,
uint32_t pSG, uint32_t pSG,
float pRadius, float pRadius,
std::vector<unsigned int>& poResults) const std::vector<unsigned int>& poResults) const
{ {
float dist = pPosition * mPlaneNormal; float dist = pPosition * mPlaneNormal;
float minDist = dist - pRadius, maxDist = dist + pRadius; float minDist = dist - pRadius, maxDist = dist + pRadius;
@ -119,14 +106,14 @@ void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition,
unsigned int index = (unsigned int)mPositions.size() / 2; unsigned int index = (unsigned int)mPositions.size() / 2;
unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
while( binaryStepSize > 1) while( binaryStepSize > 1)
{ {
if( mPositions[index].mDistance < minDist) if( mPositions[index].mDistance < minDist)
index += binaryStepSize; index += binaryStepSize;
else else
index -= binaryStepSize; index -= binaryStepSize;
binaryStepSize /= 2; binaryStepSize /= 2;
} }
// depending on the direction of the last step we need to single step a bit back or forth // 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 // to find the actual beginning element of the range
@ -140,33 +127,16 @@ void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition,
float squareEpsilon = pRadius * pRadius; float squareEpsilon = pRadius * pRadius;
std::vector<Entry>::const_iterator it = mPositions.begin() + index; std::vector<Entry>::const_iterator it = mPositions.begin() + index;
if (0 == pSG) while( it->mDistance < maxDist)
{
if((it->mPosition - pPosition).SquareLength() < squareEpsilon &&
(it->mSmoothGroups & pSG || 0 == it->mSmoothGroups || 0 == pSG))
{ {
while( it->mDistance < maxDist) poResults.push_back( it->mIndex);
{
if((it->mPosition - pPosition).SquareLength() < squareEpsilon)
{
poResults.push_back( it->mIndex);
}
++it;
if( it == mPositions.end())
break;
}
} }
else ++it;
{ if( it == mPositions.end())
while( it->mDistance < maxDist) break;
{
if((it->mPosition - pPosition).SquareLength() < squareEpsilon &&
(it->mSmoothGroups & pSG || 0 == it->mSmoothGroups))
{
poResults.push_back( it->mIndex);
}
++it;
if( it == mPositions.end())
break;
}
}
return;
} }
}

View File

@ -44,40 +44,34 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define AI_D3DSSPATIALSORT_H_INC #define AI_D3DSSPATIALSORT_H_INC
#include <vector> #include <vector>
#include "../include/aiVector3D.h" #include "../include/aiTypes.h"
#if (!defined AI_BUILD_NO_ASE_IMPORTER)
# include "3DSHelper.h"
#endif
namespace Assimp namespace Assimp
{ {
using namespace Dot3DS;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
/** Specialized version of SpatialSort to support smoothing groups /** Specialized version of SpatialSort to support smoothing groups
* This is used in the .3ds loader * This is used in the .3ds loader
*/ */
class D3DSSpatialSorter class SGSpatialSort
{ {
public: public:
D3DSSpatialSorter(); SGSpatialSort();
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Construction from a given face array, handling smoothing groups properly /** Construction from a given face array, handling smoothing groups properly
*/ */
D3DSSpatialSorter(const std::vector<aiVector3D>& vPositions); SGSpatialSort(const std::vector<aiVector3D>& vPositions);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Add a face to the spatial sorter /** Add a vertex to the spatial sort
* @param pcFace Face to be added * @param vPosition Vertex position to be added
* @param vPositions Input position list * @param index Index of the vrtex
* @param smoothingGroup SmoothingGroup for this vertex
*/ */
void AddFace(const Dot3DS::Face* pcFace, void Add(const aiVector3D& vPosition, unsigned int index,
const std::vector<aiVector3D>& vPositions); unsigned int smoothingGroup);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Prepare the spatial sorter for use /** Prepare the spatial sorter for use
@ -85,7 +79,7 @@ public:
void Prepare(); void Prepare();
/** Destructor */ /** Destructor */
~D3DSSpatialSorter(); ~SGSpatialSort();
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Returns an iterator for all positions close to the given position. /** Returns an iterator for all positions close to the given position.

View File

@ -110,10 +110,10 @@ void SMDImporter::SetupProperties(const Importer* pImp)
{ {
// The AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the // The AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the
// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
if(0xffffffff == (this->configFrameID = pImp->GetProperty( if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
AI_CONFIG_IMPORT_SMD_KEYFRAME,0xffffffff))) AI_CONFIG_IMPORT_SMD_KEYFRAME,0xffffffff)))
{ {
this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -0,0 +1,103 @@
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Defines the helper data structures for importing 3DS files.
http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */
#ifndef AI_SMOOTHINGGROUPS_H_INC
#define AI_SMOOTHINGGROUPS_H_INC
// ---------------------------------------------------------------------------
/** Helper structure representing a face with smoothing groups assigned */
struct FaceWithSmoothingGroup
{
FaceWithSmoothingGroup() : iSmoothGroup(0)
{
// let the rest uninitialized for performance - in release builds.
// in debug builds set all indices to a common magic value
#ifdef _DEBUG
this->mIndices[0] = 0xffffffff;
this->mIndices[1] = 0xffffffff;
this->mIndices[2] = 0xffffffff;
#endif
}
//! Indices. .3ds is using uint16. However, after
//! an unique vrtex set has been geneerated it might
//! be an index becomes > 2^16
uint32_t mIndices[3];
//! specifies to which smoothing group the face belongs to
uint32_t iSmoothGroup;
};
// ---------------------------------------------------------------------------
/** Helper structure representing a mesh whose faces have smoothing
groups assigned. This allows us to reuse the code for normal computations
from smoothings groups for several loaders (3DS, ASE). All of them
use face structures which inherit from #FaceWithSmoothingGroup,
but as they add extra members and need to be copied by value we
need to use a template here.
*/
template <class T>
struct MeshWithSmoothingGroups
{
//! Vertex positions
std::vector<aiVector3D> mPositions;
//! Face lists
std::vector<T> mFaces;
//! List of normal vectors
std::vector<aiVector3D> mNormals;
};
// ---------------------------------------------------------------------------
/** Computes normal vectors for the mesh
*/
template <class T>
void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh);
// include implementations
#include "SmoothingGroups.inl"
#endif // !! AI_SMOOTHINGGROUPS_H_INC

View File

@ -39,80 +39,81 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
*/ */
/** @file Implementation of the 3ds importer class */ /** @file Generation of normal vectors basing on smoothing groups */
#include "3DSLoader.h"
#include "MaterialSystem.h" #ifndef AI_SMOOTHINGGROUPS_INL_INCLUDED
#define AI_SMOOTHINGGROUPS_INL_INCLUDED
// internal headers
#include "SGSpatialSort.h"
// CRT header
#include <algorithm> #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; using namespace Assimp;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Dot3DSImporter::GenNormals(Dot3DS::Mesh* sMesh) template <class T>
void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh)
{ {
// First generate face normals // First generate face normals
sMesh->mNormals.resize(sMesh->mPositions.size(),aiVector3D()); sMesh.mNormals.resize(sMesh.mPositions.size(),aiVector3D());
for( unsigned int a = 0; a < sMesh->mFaces.size(); a++) for( unsigned int a = 0; a < sMesh.mFaces.size(); a++)
{ {
Dot3DS::Face& face = sMesh->mFaces[a]; T& face = sMesh.mFaces[a];
// assume it is a triangle // assume it is a triangle
aiVector3D* pV1 = &sMesh->mPositions[face.mIndices[0]]; aiVector3D* pV1 = &sMesh.mPositions[face.mIndices[0]];
aiVector3D* pV2 = &sMesh->mPositions[face.mIndices[1]]; aiVector3D* pV2 = &sMesh.mPositions[face.mIndices[1]];
aiVector3D* pV3 = &sMesh->mPositions[face.mIndices[2]]; aiVector3D* pV3 = &sMesh.mPositions[face.mIndices[2]];
// FIX invert all vertex normals aiVector3D pDelta1 = *pV2 - *pV1;
aiVector3D pDelta1 = *pV3 - *pV1; aiVector3D pDelta2 = *pV3 - *pV1;
aiVector3D pDelta2 = *pV2 - *pV1;
aiVector3D vNor = pDelta1 ^ pDelta2; aiVector3D vNor = pDelta1 ^ pDelta2;
sMesh->mNormals[face.mIndices[0]] = vNor; sMesh.mNormals[face.mIndices[0]] = vNor;
sMesh->mNormals[face.mIndices[1]] = vNor; sMesh.mNormals[face.mIndices[1]] = vNor;
sMesh->mNormals[face.mIndices[2]] = vNor; sMesh.mNormals[face.mIndices[2]] = vNor;
} }
// calculate the position bounds so we have a reliable epsilon to // calculate the position bounds so we have a reliable epsilon to
// check position differences against // check position differences against
// @Schrompf: This is the 6th time this snippet is repeated! // @Schrompf: This is the 6th time this snippet is repeated!
aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
for( unsigned int a = 0; a < sMesh->mPositions.size(); a++) for( unsigned int a = 0; a < sMesh.mPositions.size(); a++)
{ {
minVec.x = std::min( minVec.x, sMesh->mPositions[a].x); minVec.x = std::min( minVec.x, sMesh.mPositions[a].x);
minVec.y = std::min( minVec.y, sMesh->mPositions[a].y); minVec.y = std::min( minVec.y, sMesh.mPositions[a].y);
minVec.z = std::min( minVec.z, sMesh->mPositions[a].z); minVec.z = std::min( minVec.z, sMesh.mPositions[a].z);
maxVec.x = std::max( maxVec.x, sMesh->mPositions[a].x); maxVec.x = std::max( maxVec.x, sMesh.mPositions[a].x);
maxVec.y = std::max( maxVec.y, sMesh->mPositions[a].y); maxVec.y = std::max( maxVec.y, sMesh.mPositions[a].y);
maxVec.z = std::max( maxVec.z, sMesh->mPositions[a].z); maxVec.z = std::max( maxVec.z, sMesh.mPositions[a].z);
} }
const float posEpsilon = (maxVec - minVec).Length() * 1e-5f; const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
std::vector<aiVector3D> avNormals; std::vector<aiVector3D> avNormals;
avNormals.resize(sMesh->mNormals.size()); avNormals.resize(sMesh.mNormals.size());
// now generate the spatial sort tree // now generate the spatial sort tree
D3DSSpatialSorter sSort; SGSpatialSort sSort;
for( std::vector<Dot3DS::Face>::iterator for( std::vector<T>::iterator
i = sMesh->mFaces.begin(); i = sMesh.mFaces.begin();
i != sMesh->mFaces.end();++i) i != sMesh.mFaces.end();++i)
{ {
sSort.AddFace(&(*i),sMesh->mPositions); sSort.Add(sMesh.mPositions[(*i).mIndices[0]],(*i).mIndices[0],(*i).iSmoothGroup);
sSort.Add(sMesh.mPositions[(*i).mIndices[1]],(*i).mIndices[1],(*i).iSmoothGroup);
sSort.Add(sMesh.mPositions[(*i).mIndices[2]],(*i).mIndices[2],(*i).iSmoothGroup);
} }
sSort.Prepare(); sSort.Prepare();
for( std::vector<Dot3DS::Face>::iterator for( std::vector<T>::iterator
i = sMesh->mFaces.begin(); i = sMesh.mFaces.begin();
i != sMesh->mFaces.end();++i) i != sMesh.mFaces.end();++i)
{ {
std::vector<unsigned int> poResult; std::vector<unsigned int> poResult;
for (unsigned int c = 0; c < 3;++c) for (unsigned int c = 0; c < 3;++c)
{ {
sSort.FindPositions(sMesh.mPositions[(*i).mIndices[c]],(*i).iSmoothGroup,
sSort.FindPositions(sMesh->mPositions[(*i).mIndices[c]],(*i).iSmoothGroup,
posEpsilon,poResult); posEpsilon,poResult);
aiVector3D vNormals; aiVector3D vNormals;
@ -121,21 +122,16 @@ void Dot3DSImporter::GenNormals(Dot3DS::Mesh* sMesh)
a = poResult.begin(); a = poResult.begin();
a != poResult.end();++a) a != poResult.end();++a)
{ {
vNormals += sMesh->mNormals[(*a)]; vNormals += sMesh.mNormals[(*a)];
fDiv += 1.0f; fDiv += 1.0f;
} }
vNormals.x /= fDiv; vNormals.x /= fDiv;vNormals.y /= fDiv;vNormals.z /= fDiv;
vNormals.y /= fDiv; //vNormals.Normalize();
vNormals.z /= fDiv;
vNormals.Normalize();
// do the common coordinate system adjustment
std::swap(vNormals.y,vNormals.z);
avNormals[(*i).mIndices[c]] = vNormals; avNormals[(*i).mIndices[c]] = vNormals;
poResult.clear(); //poResult.clear();
} }
} }
sMesh->mNormals = avNormals; sMesh.mNormals = avNormals;
return;
} }
#endif // !! AI_SMOOTHINGGROUPS_INL_INCLUDED

View File

@ -101,7 +101,7 @@ void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene)
void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp) void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp)
{ {
// get the current value of the split property // get the current value of the split property
this->LIMIT = pImp->GetProperty(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Update a node after some meshes have been split // Update a node after some meshes have been split
@ -373,7 +373,7 @@ void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene)
// Setup properties // Setup properties
void SplitLargeMeshesProcess_Vertex::SetupProperties( const Importer* pImp) void SplitLargeMeshesProcess_Vertex::SetupProperties( const Importer* pImp)
{ {
this->LIMIT = pImp->GetProperty(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.

View File

@ -34,7 +34,7 @@
/> />
</Configuration> </Configuration>
<Configuration <Configuration
Name="Release|Win32" Name="Debug|x64"
> >
<DebugSettings <DebugSettings
Command="" Command=""
@ -62,7 +62,7 @@
/> />
</Configuration> </Configuration>
<Configuration <Configuration
Name="Debug|x64" Name="Release|Win32"
> >
<DebugSettings <DebugSettings
Command="" Command=""

View File

@ -85,6 +85,7 @@ public:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
virtual size_t Tell(void) const = 0; virtual size_t Tell(void) const = 0;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Returns filesize /** Returns filesize
* *

View File

@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* whether a mesh must be splitted or not. * whether a mesh must be splitted or not.
* \note The default value is AI_SLM_DEFAULT_MAX_VERTICES, defined in * \note The default value is AI_SLM_DEFAULT_MAX_VERTICES, defined in
* the internal header file SplitLargeMeshes.h * the internal header file SplitLargeMeshes.h
* Property type: integer.
*/ */
#define AI_CONFIG_PP_SLM_TRIANGLE_LIMIT "pp.slm.triangle_limit" #define AI_CONFIG_PP_SLM_TRIANGLE_LIMIT "pp.slm.triangle_limit"
@ -61,6 +62,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* whether a mesh must be splitted or not. * whether a mesh must be splitted or not.
* \note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES, defined in * \note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES, defined in
* the internal header file SplitLargeMeshes.h * the internal header file SplitLargeMeshes.h
* Property type: integer.
*/ */
#define AI_CONFIG_PP_SLM_VERTEX_LIMIT "pp.slm.vertex_limit" #define AI_CONFIG_PP_SLM_VERTEX_LIMIT "pp.slm.vertex_limit"
@ -71,6 +73,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* This is used by the aiProcess_LimitBoneWeights PostProcess-Step. * This is used by the aiProcess_LimitBoneWeights PostProcess-Step.
* \note The default value is AI_LBW_MAX_WEIGHTS, defined in * \note The default value is AI_LBW_MAX_WEIGHTS, defined in
* the internal header file LimitBoneWeightsProcess.h * the internal header file LimitBoneWeightsProcess.h
* Property type: integer.
*/ */
#define AI_CONFIG_PP_LBW_MAX_WEIGHTS "pp.lbw.weights_limit" #define AI_CONFIG_PP_LBW_MAX_WEIGHTS "pp.lbw.weights_limit"
@ -86,6 +89,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* for a specific loader. You can use the AI_CONFIG_IMPORT_XXX_KEYFRAME * for a specific loader. You can use the AI_CONFIG_IMPORT_XXX_KEYFRAME
* options (where XXX is a placeholder for the file format for which you * options (where XXX is a placeholder for the file format for which you
* want to override the global setting). * want to override the global setting).
* Property type: integer.
*/ */
#define AI_CONFIG_IMPORT_GLOBAL_KEYFRAME "imp.global.kf" #define AI_CONFIG_IMPORT_GLOBAL_KEYFRAME "imp.global.kf"
@ -101,7 +105,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** \brief Causes the 3DS loader to ignore pivot points in the file /** \brief Causes the 3DS loader to ignore pivot points in the file
* *
* There are some faulty 3DS files which look only correctly with * There are some faulty 3DS files which look only correctly with
* pivot points disabled * pivot points disabled.
* Property type: integer (1: true; !1: false).
*/ */
#define AI_CONFIG_IMPORT_3DS_IGNORE_PIVOT "imp.3ds.nopivot" #define AI_CONFIG_IMPORT_3DS_IGNORE_PIVOT "imp.3ds.nopivot"
@ -112,6 +117,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* LightWave represents the gradients with infinite detail, * LightWave represents the gradients with infinite detail,
* but for use in realtime the loader computes replacement textures. * but for use in realtime the loader computes replacement textures.
* The default size is 512 * 512. * The default size is 512 * 512.
* Property type: integer.
*/ */
#define AI_CONFIG_IMPORT_LWO_GRADIENT_RESX "imp.lwo.gradres_x" #define AI_CONFIG_IMPORT_LWO_GRADIENT_RESX "imp.lwo.gradres_x"
#define AI_CONFIG_IMPORT_LWO_GRADIENT_RESY "imp.lwo.gradres_y" #define AI_CONFIG_IMPORT_LWO_GRADIENT_RESY "imp.lwo.gradres_y"
@ -121,8 +127,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* that their tangents and bitangents are smoothed. * that their tangents and bitangents are smoothed.
* *
* This applies to the CalcTangentSpace-Step. The angle is specified * This applies to the CalcTangentSpace-Step. The angle is specified
* in degrees * 1000, so 180000 is PI. The default value is * in degrees , so 180 is PI. The default value is
* 45 degrees. The maximum value is 180000. * 45 degrees. The maximum value is 180.
* Property type: float.
*/ */
#define AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE "pp.ct.max_smoothing" #define AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE "pp.ct.max_smoothing"
@ -131,15 +138,59 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* at the same vertex position that their are smoothed. * at the same vertex position that their are smoothed.
* *
* This applies to the GenSmoothNormals-Step. The angle is specified * This applies to the GenSmoothNormals-Step. The angle is specified
* in degrees * 1000, so 180000 is PI. The default value is * in degrees, so 180 is PI. The default value is
* 180 degrees (all vertex normals are smoothed). The maximum value is 180000 * 180 degrees (all vertex normals are smoothed). The maximum value is 180
* \note This can be manually overriden by loaders via #aiMesh::mMaxSmoothingAngle; * Property type: float.
*/ */
#define AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE "pp.gsn.max_smoothing" #define AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE "pp.gsn.max_smoothing"
#define AI_CONFIG_PP_OG_MAX_DEPTH "pp.og.max_depth" // ---------------------------------------------------------------------------
#define AI_CONFIG_PP_OG_MIN_TRIS_PER_NODE "pp.og.min_tris" /** \brief Specifies the minimum number of faces a node should have.
#define AI_CONFIG_PP_OG_MAXIMALLY_SMALL "pp.og.maximally_small" * This is an input parameter to the OptimizeGraph-Step.
*
* Nodes whose referenced meshes have less faces than this value
* are propably joined with neighbors with identical world matrices.
* However, it is just a hint to the step.
* Property type: integer
*/
#define AI_CONFIG_PP_OG_MIN_NUM_FACES "pp.og.min_faces"
// ---------------------------------------------------------------------------
/** \brief Specifies whether animations are removed from the asset.
* This is an input parameter to the OptimizeGraph-Step.
*
* If an application does not need the animation data, erasing it at the
* beginning of the post-process pipeline allows some steps - including
* OptimizeGraph itself - to apply further optimizations.
* Property type: integer (1: true; !1: false).
*/
#define AI_CONFIG_PP_OG_REMOVE_ANIMATIONS "pp.og.remove_anims"
// ---------------------------------------------------------------------------
/** \brief Specifies whether the OptimizeGraphProcess joins nodes even if
* their local transformations are inequal.
*
* By default, nodes with different local transformations are never joined.
* The intention is that all vertices should remain in their original
* local coordinate space where they are correctly centered and aligned,
* which does also allow for some significant culling improvements.
*/
#define AI_CONFIG_PP_OG_JOIN_INEQUAL_TRANSFORMS "pp.og.allow_diffwm"
// ---------------------------------------------------------------------------
/** \brief Sets the colormap (= palette) to be used to decode embedded
* textures in MDL files.
*
* This must be a valid path to a file. The file is 768 (256*3) bytes
* large and contains RGB tripels for each of the 256 palette entries.
* The default value is colormap.lmp. If the file is nto found,
* a default palette is used.
* Property type: string.
*/
#define AI_CONFIG_IMPORT_MDL_COLORMAP "imp.mdl.color_map"
#endif // !! AI_CONFIG_H_INC #endif // !! AI_CONFIG_H_INC

View File

@ -53,11 +53,13 @@ extern "C" {
struct aiFileIO; struct aiFileIO;
struct aiFile;
typedef aiFileIO (*aiFileOpenProc)(C_STRUCT aiFileIO*, const char*, const char*); typedef aiFile* (*aiFileOpenProc)(C_STRUCT aiFileIO*, const char*, const char*);
typedef aiReturn (*aiFileCloseProc)(C_STRUCT aiFileIO*); typedef void (*aiFileCloseProc)(C_STRUCT aiFileIO*, C_STRUCT aiFile*);
typedef unsigned long (*aiFileReadWriteProc)(C_STRUCT aiFileIO*, char*, unsigned int, unsigned int); typedef size_t (*aiFileWriteProc)(C_STRUCT aiFile*, const char*, size_t, size_t);
typedef unsigned long (*aiFileTellProc)(C_STRUCT aiFileIO*); typedef size_t (*aiFileReadProc)(C_STRUCT aiFile*, char*, size_t,size_t);
typedef size_t (*aiFileTellProc)(C_STRUCT aiFile*);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** Define seek origins in fseek()-style. /** Define seek origins in fseek()-style.
@ -70,10 +72,26 @@ enum aiOrigin
aiOrigin_END = 0x2 //!< End of file aiOrigin_END = 0x2 //!< End of file
}; };
typedef aiReturn (*aiFileSeek)(aiFileIO*, unsigned long, aiOrigin); typedef aiReturn (*aiFileSeek)(aiFile*, size_t, aiOrigin);
typedef char* aiUserData; typedef char* aiUserData;
// ---------------------------------------------------------------------------
/** Defines how C-Assimp accesses files. Provided are functions to open
* and close files.
*/
// ---------------------------------------------------------------------------
struct aiFileIO
{
//! Function used to open a new file
aiFileOpenProc OpenProc;
//! Function used to close an existing file
aiFileCloseProc CloseProc;
//! User-defined data
aiUserData UserData;
};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** Data structure to wrap a set of fXXXX (e.g fopen) replacement functions /** Data structure to wrap a set of fXXXX (e.g fopen) replacement functions
* *
@ -81,30 +99,27 @@ typedef char* aiUserData;
* counterparts in the CRT. * counterparts in the CRT.
*/ */
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
struct aiFileIO struct aiFile
{ {
//! User data assigned to the structure
aiUserData UserData;
//! Function used to open a new file
aiFileOpenProc OpenFunc;
//! Function used to close an existing file
aiFileCloseProc CloseFunc;
//! Function used to read from a file //! Function used to read from a file
aiFileReadWriteProc ReadFunc; aiFileReadProc ReadProc;
//! Function used to write to a file //! Function used to write to a file
aiFileReadWriteProc WriteFunc; aiFileWriteProc WriteProc;
//! Function used to retrieve the current //! Function used to retrieve the current
//! position of the file cursor (ftell()) //! position of the file cursor (ftell())
aiFileTellProc TellProc; aiFileTellProc TellProc;
//! Function used to retrieve the size of the file, in bytes
aiFileTellProc FileSizeProc;
//! Function used to set the current position //! Function used to set the current position
//! of the file cursor (fseek()) //! of the file cursor (fseek())
aiFileSeek SeekProc; aiFileSeek SeekProc;
//! User-defined data
aiUserData UserData;
}; };

View File

@ -356,16 +356,6 @@ struct aiMesh
*/ */
unsigned int mMaterialIndex; unsigned int mMaterialIndex;
/** The maximum vertex smooth angle for the mesh, in radians
* If the angle between two vertex normals is larger,
* the vertex normals should not be smoothed. The GenVertexNormals-Step
* takes care of this value. The angle is specified in radians.
* It is set to AI_MESH_SMOOTHING_ANGLE_NOT_SET if the source file didn't
* contain any additional information related to the calculation of
* vertex normals.
*/
float mMaxSmoothingAngle;
#ifdef __cplusplus #ifdef __cplusplus
//! Default constructor. Initializes all members to 0 //! Default constructor. Initializes all members to 0
@ -384,7 +374,6 @@ struct aiMesh
mColors[a] = NULL; mColors[a] = NULL;
mNumBones = 0; mBones = NULL; mNumBones = 0; mBones = NULL;
mMaterialIndex = 0; mMaterialIndex = 0;
mMaxSmoothingAngle = AI_MESH_SMOOTHING_ANGLE_NOT_SET;
} }
//! Deletes all storage allocated for the mesh //! Deletes all storage allocated for the mesh

View File

@ -169,14 +169,66 @@ enum aiPostProcessSteps
* the bounding box of all vertices + their normals is compared against * the bounding box of all vertices + their normals is compared against
* the volume of the bounding box of all vertices without their normals. * the volume of the bounding box of all vertices without their normals.
* This works well for most objects, problems might occur with planar * This works well for most objects, problems might occur with planar
* surfaces. However the step tries to filter such cases out. * surfaces. However, the step tries to filter such cases.
* The step inverts all infacing normals. Generally it is recommended * The step inverts all infacing normals. Generally it is recommended
* to enable this step. * to enable this step, although the result is not always correct.
*/ */
aiProcess_FixInfacingNormals = 0x2000 aiProcess_FixInfacingNormals = 0x2000,
/** This step performs some optimizations on the node graph.
*
* It is incompatible to the PreTransformVertices-Step. Some configuration
* options exist, see aiConfig.h for more details.
* Generally, two actions are available:<br>
* 1. Remove animation nodes and data from the scene. This allows other
* steps for further optimizations.<br>
* 2. Combine very small meshes to larger ones. Only if the meshes
* are used by the same node or by nodes on the same hierarchy (with
* equal local transformations). Unlike PreTransformVertices, the
* OptimizeGraph-step doesn't transform vertices from one space
* another.<br>
* 3. Remove hierarchy levels<br>
*
* It is recommended to have this step run with the default configuration.
*/
aiProcess_OptimizeGraph = 0x4000
}; };
/** @def AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST
* @brief Default postprocess configuration targeted at realtime applications
* which need to load models as fast as possible.
*
* If you're using DirectX, don't forget to combine this value with
* the #aiProcess_ConvertToLeftHanded step.
*/
#define AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST \
aiProcess_CalcTangentSpace | \
aiProcess_GenNormals | \
aiProcess_JoinIdenticalVertices | \
aiProcess_Triangulate
/** @def AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST
* @brief Default postprocess configuration targeted at realtime applications.
* Unlike AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST, this configuration
* performs some extra optimizations.
*
* If you're using DirectX, don't forget to combine this value with
* the #aiProcess_ConvertToLeftHanded step.
*/
#define AI_POSTPROCESS_DEFAULT_REALTIME \
aiProcess_CalcTangentSpace | \
aiProcess_GenSmoothNormals | \
aiProcess_JoinIdenticalVertices | \
aiProcess_ImproveCacheLocality | \
aiProcess_LimitBoneWeights | \
aiProcess_RemoveRedundantMaterials | \
aiProcess_SplitLargeMeshes | \
aiProcess_OptimizeGraph | \
aiProcess_Triangulate
#ifdef __cplusplus #ifdef __cplusplus
} // end of extern "C" } // end of extern "C"
#endif #endif

View File

@ -75,6 +75,9 @@ struct aiVector3D
inline bool operator!= (const aiVector3D& other) const inline bool operator!= (const aiVector3D& other) const
{return x != other.x || y != other.y || z != other.z;} {return x != other.x || y != other.y || z != other.z;}
inline aiVector3D& operator= (float f)
{x = y = z = f;return *this;}
#endif // __cplusplus #endif // __cplusplus
float x, y, z; float x, y, z;

View File

@ -66,8 +66,8 @@ struct aiString;
* @param pFile Path and filename of the file to be imported, * @param pFile Path and filename of the file to be imported,
* expected to be a null-terminated c-string. NULL is not a valid value. * expected to be a null-terminated c-string. NULL is not a valid value.
* @param pFlags Optional post processing steps to be executed after * @param pFlags Optional post processing steps to be executed after
* a successful import. Provide a bitwise combination of the #aiPostProcessSteps * a successful import. Provide a bitwise combination of the
* flags. * #aiPostProcessSteps flags.
* @return Pointer to the imported data or NULL if the import failed. * @return Pointer to the imported data or NULL if the import failed.
*/ */
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -85,18 +85,19 @@ ASSIMP_API const C_STRUCT aiScene* aiImportFile( const char* pFile,
* done with it, call aiReleaseImport() to free the resources associated with * done with it, call aiReleaseImport() to free the resources associated with
* this file. If the import fails, NULL is returned instead. Call * this file. If the import fails, NULL is returned instead. Call
* aiGetErrorString() to retrieve a human-readable error text. * aiGetErrorString() to retrieve a human-readable error text.
* @param pFile aiFileIO structure. All functions pointers must be * @param pFile Path and filename of the file to be imported,
* initialized. aiFileIO::OpenFunc() and aiFileIO::CloseFunc() * expected to be a null-terminated c-string. NULL is not a valid value.
* will be used to open other files in the fs if the asset to be * @param pFlags Optional post processing steps to be executed after
* loaded depends on them. NULL is not a valid value. * a successful import. Provide a bitwise combination of the
* @return Pointer to the imported data or NULL if the import failed. * #aiPostProcessSteps flags.
* * @param pFS aiFileIO structure. Will be used to open the model file itself
* @note The C-API creates a new Importer instance internally for each call * and any other files the loader needs to open.
* to this function. Therefore the C-API is thread-safe. * @return Pointer to the imported data or NULL if the import failed.
*/ */
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
ASSIMP_API const C_STRUCT aiScene* aiImportFileEx( ASSIMP_API const C_STRUCT aiScene* aiImportFileEx(
const C_STRUCT aiFileIO* pFile); const char* pFile, unsigned int pFlags,
C_STRUCT aiFileIO* pFS);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -151,6 +152,31 @@ ASSIMP_API void aiGetExtensionList(C_STRUCT aiString* szOut);
ASSIMP_API void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn, ASSIMP_API void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn,
C_STRUCT aiMemoryInfo* in); C_STRUCT aiMemoryInfo* in);
// ---------------------------------------------------------------------------
/** Set an integer property. This is the C-version of
* #Importer::SetPropertyInteger(). In the C-API properties are shared by
* all imports. It is not possible to specify them per asset.
*
* \param szName Name of the configuration property to be set. All constants
* are defined in the aiConfig.h header file.
* \param value New value for the property
*/
// ---------------------------------------------------------------------------
ASSIMP_API void aiSetImportPropertyInteger(const char* szName, int value);
// ---------------------------------------------------------------------------
/** @see aiSetImportPropertyInteger()
*/
ASSIMP_API void aiSetImportPropertyFloat(const char* szName, float value);
// ---------------------------------------------------------------------------
/** @see aiSetImportPropertyInteger()
*/
ASSIMP_API void aiSetImportPropertyString(const char* szName,
const C_STRUCT aiString* st);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// STL headers // STL headers
#include <string> #include <string>
#include <map>
#include <vector> #include <vector>
// public ASSIMP headers // public ASSIMP headers
@ -67,6 +68,8 @@ namespace Assimp
#define AI_PROPERTY_WAS_NOT_EXISTING 0xffffffff #define AI_PROPERTY_WAS_NOT_EXISTING 0xffffffff
struct aiScene; struct aiScene;
struct aiFileIO;
const aiScene* aiImportFileEx( const char*, unsigned int, aiFileIO*);
namespace Assimp namespace Assimp
{ {
@ -101,28 +104,14 @@ class ASSIMP_API Importer
{ {
// used internally // used internally
friend class BaseProcess; friend class BaseProcess;
friend const aiScene* ::aiImportFileEx( const char*, unsigned int, aiFileIO*);
protected: public:
template <typename Type> typedef uint32_t KeyType;
struct PropertyInfo typedef std::map<KeyType, int> IntPropertyMap;
{ typedef std::map<KeyType, float> FloatPropertyMap;
std::string name; typedef std::map<KeyType, std::string> StringPropertyMap;
Type value;
bool operator==(const PropertyInfo<Type>& other) const
{
return other.name == this->name &&
other.value == this->value;
}
bool operator!=(const PropertyInfo<Type>& other) const
{
return !(other == *this);
}
};
typedef PropertyInfo<int> IntPropertyInfo;
public: public:
@ -188,28 +177,66 @@ public:
#endif #endif
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Set a configuration property. /** Set an integer configuration property.
* @param szName Name of the property. All supported properties * @param szName Name of the property. All supported properties
* are defined in the aiConfig.g header (the constants share the * are defined in the aiConfig.g header (all constants share the
* prefix AI_CONFIG_XXX). * prefix AI_CONFIG_XXX).
* @param iValue New value of the property * @param iValue New value of the property
* @return Old value of the property or AI_PROPERTY_WAS_NOT_EXISTING * @param bWasExisting Optional pointer to receive true if the
* if the property has not yet been set. * property was set before. The new value replaced the old value
* in this case.
* @note Property of different types (float, int, string ..) are kept
* on different stacks, so calling SetPropertyInteger() for a
* floating-point property has no effect - the loader will call
* GetPropertyFloat() to read the property, but it won't be there.
*/ */
int SetProperty(const char* szName, int iValue); void SetPropertyInteger(const char* szName, int iValue,
bool* bWasExisting = NULL);
// -------------------------------------------------------------------
/** Set a floating-point configuration property.
* @see SetPropertyInteger()
*/
void SetPropertyFloat(const char* szName, float fValue,
bool* bWasExisting = NULL);
// -------------------------------------------------------------------
/** Set a string configuration property.
* @see SetPropertyInteger()
*/
void SetPropertyString(const char* szName, const std::string& sValue,
bool* bWasExisting = NULL);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Get a configuration property. /** Get a configuration property.
* @param szName Name of the property. All supported properties * @param szName Name of the property. All supported properties
* are defined in the aiConfig.g header (the constants start * are defined in the aiConfig.g header (all constants share the
* with AI_CONFIG_XXX). * prefix AI_CONFIG_XXX).
* @param iErrorReturn Value that is returned if the property * @param iErrorReturn Value that is returned if the property
* is not found. Note that this value, not the default value * is not found.
* for the requested property is returned!
* @return Current value of the property * @return Current value of the property
* @note Property of different types (float, int, string ..) are kept
* on different lists, so calling SetPropertyInteger() for a
* floating-point property has no effect - the loader will call
* GetPropertyFloat() to read the property, but it won't be there.
*/ */
int GetProperty(const char* szName, int iErrorReturn = 0xffffffff) const; int GetPropertyInteger(const char* szName,
int iErrorReturn = 0xffffffff) const;
// -------------------------------------------------------------------
/** Get a floating-point configuration property
* @see GetPropertyInteger()
*/
float GetPropertyFloat(const char* szName,
float fErrorReturn = 10e10f) const;
// -------------------------------------------------------------------
/** Get a string configuration property
* @see GetPropertyInteger()
*/
std::string GetPropertyString(const char* szName,
const std::string& sErrorReturn = "") const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@ -278,10 +305,11 @@ public:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Returns whether a given file extension is supported by ASSIMP /** Returns whether a given file extension is supported by ASSIMP.
* *
* @param szExtension Extension to be checked. * @param szExtension Extension to be checked.
* Must include a leading dot '.'. Example: ".3ds", ".md3" * Must include a trailing dot '.'. Example: ".3ds", ".md3".
* Cases-insensitive.
* @return true if the extension is supported, false otherwise * @return true if the extension is supported, false otherwise
*/ */
bool IsExtensionSupported(const std::string& szExtension); bool IsExtensionSupported(const std::string& szExtension);
@ -298,6 +326,18 @@ public:
void GetExtensionList(std::string& szOut); void GetExtensionList(std::string& szOut);
// -------------------------------------------------------------------
/** Find the loader corresponding to a specific file extension.
*
* This is quite similar to IsExtensionSupported() except a
* BaseImporter instance is returned.
* @param szExtension Extension to be checke, cases insensitive,
* must include a trailing dot.
* @return NULL if there is no loader for the extension.
*/
BaseImporter* FindLoader (const std::string& szExtension);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Returns the scene loaded by the last successful call to ReadFile() /** Returns the scene loaded by the last successful call to ReadFile()
* *
@ -351,9 +391,17 @@ protected:
std::string mErrorString; std::string mErrorString;
/** List of integer properties */ /** List of integer properties */
std::vector<IntPropertyInfo> mIntProperties; IntPropertyMap mIntProperties;
/** Used for testing */ /** List of floating-point properties */
FloatPropertyMap mFloatProperties;
/** List of string properties */
StringPropertyMap mStringProperties;
/** Used for testing - extra verbose mode causes the
validateDataStructure-Step to be executed before
and after every single postprocess step */
bool bExtraVerbose; bool bExtraVerbose;
}; };

View File

@ -44,7 +44,7 @@ package assimp;
/** /**
* Defines configuration properties. * Defines configuration properties.
* * <p/>
* Static helper class, can't be instanced. It defines configuration * Static helper class, can't be instanced. It defines configuration
* property keys to be used with <code> Importer.setPropertyInt</code> * property keys to be used with <code> Importer.setPropertyInt</code>
* *
@ -57,7 +57,6 @@ public class ConfigProperty {
} }
/** /**
* Default value for the <code>CONFIG_PP_SLM_TRIANGLE_LIMIT</code> * Default value for the <code>CONFIG_PP_SLM_TRIANGLE_LIMIT</code>
* configuration property. * configuration property.
@ -79,15 +78,13 @@ public class ConfigProperty {
public static final int DEFAULT_LBW_MAX_WEIGHTS = 4; public static final int DEFAULT_LBW_MAX_WEIGHTS = 4;
/** /**
* Set the maximum number of vertices in a mesh. * Set the maximum number of vertices in a mesh.
* <p/> * <p/>
* This is used by the "SplitLargeMeshes" PostProcess-Step to determine * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
* whether a mesh must be splitted or not. * whether a mesh must be splitted or not.
* \note The default value is <code>DEFAULT_SLM_MAX_TRIANGLES</code> * note: The default value is <code>DEFAULT_SLM_MAX_TRIANGLES</code>.
* The type of the property is int.
*/ */
public static final String CONFIG_PP_SLM_TRIANGLE_LIMIT public static final String CONFIG_PP_SLM_TRIANGLE_LIMIT
= "pp.slm.triangle_limit"; = "pp.slm.triangle_limit";
@ -98,7 +95,8 @@ public class ConfigProperty {
* <p/> * <p/>
* This is used by the "SplitLargeMeshes" PostProcess-Step to determine * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
* whether a mesh must be splitted or not. * whether a mesh must be splitted or not.
* \note The default value is <code>DEFAULT_SLM_MAX_VERTICES</code> * note: The default value is <code>DEFAULT_SLM_MAX_VERTICES</code>.
* The type of the property is int.
*/ */
public static final String CONFIG_PP_SLM_VERTEX_LIMIT public static final String CONFIG_PP_SLM_VERTEX_LIMIT
= "pp.slm.vertex_limit"; = "pp.slm.vertex_limit";
@ -108,7 +106,8 @@ public class ConfigProperty {
* Set the maximum number of bones affecting a single vertex * Set the maximum number of bones affecting a single vertex
* <p/> * <p/>
* This is used by the aiProcess_LimitBoneWeights PostProcess-Step. * This is used by the aiProcess_LimitBoneWeights PostProcess-Step.
* \note The default value is <code>DEFAULT_LBW_MAX_WEIGHTS</code> * note :The default value is <code>DEFAULT_LBW_MAX_WEIGHTS</code>.
* The type of the property is int.
*/ */
public static final String CONFIG_PP_LBW_MAX_WEIGHTS public static final String CONFIG_PP_LBW_MAX_WEIGHTS
= "pp.lbw.weights_limit"; = "pp.lbw.weights_limit";
@ -126,6 +125,7 @@ public class ConfigProperty {
* <code>CONFIG_IMPORT_XXX_KEYFRAME</code> options (where XXX is a * <code>CONFIG_IMPORT_XXX_KEYFRAME</code> options (where XXX is a
* placeholder for the file format for which you want to override the * placeholder for the file format for which you want to override the
* global setting). * global setting).
* The type of the property is int.
*/ */
public static final String CONFIG_IMPORT_GLOBAL_KEYFRAME public static final String CONFIG_IMPORT_GLOBAL_KEYFRAME
= "imp.global.kf"; = "imp.global.kf";
@ -144,11 +144,60 @@ public class ConfigProperty {
* There are some faulty 3DS files on the internet which look * There are some faulty 3DS files on the internet which look
* only correctly with pivot points disabled. By default, * only correctly with pivot points disabled. By default,
* this option is disabled. * this option is disabled.
* note: This is a boolean property stored as an integer, 0 is false
*/ */
public static final String CONFIG_IMPORT_3DS_IGNORE_PIVOT public static final String CONFIG_IMPORT_3DS_IGNORE_PIVOT
= "imp.3ds.nopivot"; = "imp.3ds.nopivot";
public static final String CONFIG_PP_OG_MAX_DEPTH = "pp.og.max_depth";
public static final String CONFIG_PP_OG_MIN_TRIS_PER_NODE = "pp.og.min_tris"; /**
public static final String CONFIG_PP_OG_MAXIMALLY_SMALL = "pp.og.maximally_small"; * Specifies the maximum angle that may be between two vertex tangents
* that their tangents and bitangents are smoothed.
* <p/>
* This applies to the CalcTangentSpace-Step. The angle is specified
* in degrees, so 180 is PI. The default value is
* 45 degrees. The maximum value is 180.f
* The type of the property is float.
*/
public static final String AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE
= "pp.ct.max_smoothing";
/**
* Specifies the maximum angle that may be between two face normals
* at the same vertex position that their are smoothed.
* <p/>
* This applies to the GenSmoothNormals-Step. The angle is specified
* in degrees * 1000, so 180.f is PI. The default value is
* 180 degrees (all vertex normals are smoothed). The maximum value is 180.f
* The type of the property is float.
*/
public static final String AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE
= "pp.gsn.max_smoothing";
/**
* Specifies the minimum number of faces a node should have.
* This is an input parameter to the OptimizeGraph-Step.
* <p/>
* Nodes whose referenced meshes have less faces than this value
* are propably joined with neighbors with identical world matrices.
* However, it is just a hint to the step.
* The type of the property is int.
*/
public static final String AI_CONFIG_PP_OG_MIN_NUM_FACES
= "pp.og.min_faces";
/** \brief Specifies whether animations are removed from the asset.
* This is an input parameter to the OptimizeGraph-Step.
*
* If an application does not need the animation data, erasing it at the
* beginning of the post-process pipeline allows some steps - including
* OptimizeGraph itself - to apply further optimizations.
* note: This is a boolean property stored as an integer, 0 is false
*/
public static final String AI_CONFIG_PP_OG_REMOVE_ANIMATIONS
= "pp.og.remove_anims";
} }

View File

@ -71,6 +71,38 @@ public class Importer {
Type value; Type value;
} }
/**
* Represents a property list
*/
private class PropertyList<Type> extends Vector<Property<Type>> {
public void setProperty(final String prop, final Type val) {
for (Property<Type> i : this) {
if (i.key.equals(prop)) {
i.value = val;
return;
}
}
Property<Type> propNew = new Property<Type>();
propNew.key = prop;
propNew.value = val;
this.add(propNew);
}
public Type getProperty(final String prop) {
for (Property<Type> i : this) {
if (i.key.equals(prop)) {
return i.value;
}
}
return null;
}
}
/** /**
* Default implementation of <code>IOStream</code>. * Default implementation of <code>IOStream</code>.
* <br> * <br>
@ -158,12 +190,14 @@ public class Importer {
/** /**
* I/O system to be used * I/O system to be used
*/ */
private IOSystem ioSystem = null; private IOSystem ioSystem = new DefaultIOSystem();
/** /**
* List of config properties * List of config properties for all supported types: int, float and string
*/ */
private Vector<Property<Integer>> properties; private PropertyList<Integer> properties = new PropertyList<Integer>();
private PropertyList<Float> propertiesFloat = new PropertyList<Float>();
private PropertyList<String> propertiesString = new PropertyList<String>();
/** /**
@ -186,14 +220,11 @@ public class Importer {
* *
* @param iVersion Version of the JNI interface to be used. * @param iVersion Version of the JNI interface to be used.
* @throws NativeException Thrown if the jassimp library could not be loaded * @throws NativeException Thrown if the jassimp library could not be loaded
* or if the entry point to the module wasn't found. if this exception * or if the entry point to the module wasn't found. if this exception
* is not thrown, you can assume that jAssimp is fully available. * is not thrown, you can assume that jAssimp is fully available.
*/ */
public Importer(int iVersion) throws NativeException { public Importer(int iVersion) throws NativeException {
// allocate a default I/O system
ioSystem = new DefaultIOSystem();
if (!bLibInitialized) { if (!bLibInitialized) {
/** try to load the jassimp library. First try to load the /** try to load the jassimp library. First try to load the
@ -319,7 +350,7 @@ public class Importer {
* @param path Path to the file to be read * @param path Path to the file to be read
* @return null if the import failed, otherwise a valid Scene instance * @return null if the import failed, otherwise a valid Scene instance
* @throws NativeException This exception is thrown when an unknown error * @throws NativeException This exception is thrown when an unknown error
* occurs in the JNI bridge module. * occurs in the JNI bridge module.
*/ */
public final Scene readFile(String path) throws NativeException { public final Scene readFile(String path) throws NativeException {
this.scene = new Scene(this); this.scene = new Scene(this);
@ -346,8 +377,10 @@ public class Importer {
else if (step.equals(PostProcessStep.PreTransformVertices)) flags |= 0x100; else if (step.equals(PostProcessStep.PreTransformVertices)) flags |= 0x100;
else if (step.equals(PostProcessStep.LimitBoneWeights)) flags |= 0x200; else if (step.equals(PostProcessStep.LimitBoneWeights)) flags |= 0x200;
else if (step.equals(PostProcessStep.ValidateDataStructure)) flags |= 0x400; else if (step.equals(PostProcessStep.ValidateDataStructure)) flags |= 0x400;
else if (step.equals(PostProcessStep.FixInfacingNormals)) flags |= 0x800; else if (step.equals(PostProcessStep.ImproveVertexLocality)) flags |= 0x800;
else if (step.equals(PostProcessStep.ImproveVertexLocality)) flags |= 0x1600; else if (step.equals(PostProcessStep.RemoveRedundantMaterials)) flags |= 0x1000;
else if (step.equals(PostProcessStep.FixInfacingNormals)) flags |= 0x2000;
else if (step.equals(PostProcessStep.OptimizeGraph)) flags |= 0x4000;
} }
// now load the mesh // now load the mesh
@ -418,30 +451,39 @@ public class Importer {
* *
* @param prop Name of the config property * @param prop Name of the config property
* @param val New value for the config property * @param val New value for the config property
* @return Old value of the property or <code>PROPERTY_WAS_NOT_EXISTING</code>
* if the property has not yet been set.
*/ */
public final int setPropertyInt(final String prop, int val) { public final void setPropertyInt(final String prop, int val) {
for (Property<Integer> i : this.properties) { this.properties.setProperty(prop, val);
if (i.key.equals(prop)) {
int old = i.value;
i.value = val;
// make sure all changes are sent to the native implementation
this._NativeSetPropertyInt(prop, val, this.getContext());
return old;
}
}
Property<Integer> propNew = new Property<Integer>();
propNew.key = prop;
propNew.value = val;
this.properties.add(propNew);
// make sure all changes are sent to the native implementation
this._NativeSetPropertyInt(prop, val, this.getContext()); this._NativeSetPropertyInt(prop, val, this.getContext());
return PROPERTY_WAS_NOT_EXISTING; }
/**
* Set a floating-point property. All supported config properties are
* defined as constants in the <code>ConfigProperty</code> class
*
* @param prop Name of the config property
* @param val New value for the config property
*/
public final void setPropertyFloat(final String prop, float val) {
this.propertiesFloat.setProperty(prop, val);
this._NativeSetPropertyFloat(prop, val, this.getContext());
}
/**
* Set a string property. All supported config properties are
* defined as constants in the <code>ConfigProperty</code> class
*
* @param prop Name of the config property
* @param val New value for the config property
*/
public final void setPropertyString(final String prop, String val) {
this.propertiesString.setProperty(prop, val);
this._NativeSetPropertyString(prop, val, this.getContext());
} }
@ -457,12 +499,36 @@ public class Importer {
*/ */
public final int getPropertyInt(final String prop, int error_return) { public final int getPropertyInt(final String prop, int error_return) {
for (Property<Integer> i : this.properties) { Integer i = this.properties.getProperty(prop);
if (i.key.equals(prop)) { return i != null ? i : error_return;
return i.value; }
}
}
return error_return; /**
* Gets a floating-point config property that has been set using
* <code>setPropertyFloat</code>. All supported config properties are
* defined as constants in the <code>ConfigProperty</code> class
*
* @see <code>getPropertyInt</code>
*/
public final float getPropertyFloat(final String prop, float error_return) {
Float i = this.propertiesFloat.getProperty(prop);
return i != null ? i : error_return;
}
/**
* Gets a string config property that has been set using
* <code>setPropertyString</code>. All supported config properties are
* defined as constants in the <code>ConfigProperty</code> class
*
* @see <code>getPropertyInt</code>
*/
public final String getPropertyString(final String prop, String error_return) {
String i = this.propertiesString.getProperty(prop);
return i != null ? i : error_return;
} }
/** /**
@ -531,4 +597,12 @@ public class Importer {
* @return 0xffffffff if an error occured * @return 0xffffffff if an error occured
*/ */
private native int _NativeSetPropertyInt(String name, int prop, long iContext); private native int _NativeSetPropertyInt(String name, int prop, long iContext);
// float-version
private native int _NativeSetPropertyFloat(String name,
float prop, long iContext);
// String-version
private native int _NativeSetPropertyString(String name,
String prop, long iContext);
} }

View File

@ -174,7 +174,6 @@ public class PostProcessStep {
public static final PostProcessStep LimitBoneWeights = public static final PostProcessStep LimitBoneWeights =
new PostProcessStep("LimitBoneWeights"); new PostProcessStep("LimitBoneWeights");
/** /**
* Validates the aiScene data structure before it is returned. * Validates the aiScene data structure before it is returned.
* This makes sure that all indices are valid, all animations and * This makes sure that all indices are valid, all animations and
@ -187,19 +186,6 @@ public class PostProcessStep {
new PostProcessStep("ValidateDataStructure"); new PostProcessStep("ValidateDataStructure");
/** This step tries to determine which meshes have normal vectors
* that are facing inwards. The algorithm is simple but effective:
* the bounding box of all vertices + their normals is compared against
* the volume of the bounding box of all vertices without their normals.
* This works well for most objects, problems might occur with planar
* surfaces. However the step tries to filter such cases out.
* The step inverts all infacing normals. Generally it is recommended
* to enable this step.
*/
public static final PostProcessStep FixInfacingNormals =
new PostProcessStep("FixInfacingNormals");
/** Reorders triangles for vertex cache locality and thus better performance. /** Reorders triangles for vertex cache locality and thus better performance.
* The step tries to improve the ACMR (average post-transform vertex cache * The step tries to improve the ACMR (average post-transform vertex cache
* miss ratio) for all meshes. The step runs in O(n) and is roughly * miss ratio) for all meshes. The step runs in O(n) and is roughly
@ -210,6 +196,46 @@ public class PostProcessStep {
new PostProcessStep("ImproveVertexLocality"); new PostProcessStep("ImproveVertexLocality");
/** Searches for redundant materials and removes them.
*
* This is especially useful in combination with the PretransformVertices
* and OptimizeGraph steps. Both steps join small meshes, but they
* can't do that if two meshes have different materials.
*/
public static final PostProcessStep RemoveRedundantMaterials =
new PostProcessStep("RemoveRedundantMaterials");
/** This step tries to determine which meshes have normal vectors
* that are facing inwards. The algorithm is simple but effective:
* the bounding box of all vertices + their normals is compared against
* the volume of the bounding box of all vertices without their normals.
* This works well for most objects, problems might occur with planar
* surfaces. However, the step tries to filter such cases.
* The step inverts all infacing normals. Generally it is recommended
* to enable this step, although the result is not always correct.
*/
public static final PostProcessStep FixInfacingNormals =
new PostProcessStep("FixInfacingNormals");
/** This step performs some optimizations on the node graph.
*
* It is incompatible to the PreTransformVertices-Step. Some configuration
* options exist, see aiConfig.h for more details.
* Generally, two actions are available:<br>
* 1. Remove animation nodes and data from the scene. This allows other
* steps for further optimizations.<br>
* 2. Combine very small meshes to larger ones. Only if the meshes
* are used by the same node or by nodes on the same hierarchy (with
* equal local transformations). Unlike PreTransformVertices, the
* OptimizeGraph-step doesn't transform vertices from one space
* another.<br>
* 3. Remove hierarchy levels<br>
*
* It is recommended to have this step run with the default configuration.
*/
public static final PostProcessStep OptimizeGraph =
new PostProcessStep("OptimizeGraph");
private final String myName; // for debug only private final String myName; // for debug only
private PostProcessStep(String name) { private PostProcessStep(String name) {

View File

@ -0,0 +1,107 @@
#include "utImporter.h"
CPPUNIT_TEST_SUITE_REGISTRATION (ImporterTest);
#define AIUT_DEF_ERROR_TEXT "sorry, this is a test"
bool TestPlugin :: CanRead( const std::string& pFile,
IOSystem* pIOHandler) const
{
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);
// todo ... make case-insensitive
return (extension == ".apple" || extension == ".mac" ||
extension == ".linux" || extension == ".windows" );
}
void TestPlugin :: GetExtensionList(std::string& append)
{
append.append("*.apple;*.mac;*.linux;*.windows");
}
void TestPlugin :: InternReadFile( const std::string& pFile,
aiScene* pScene, IOSystem* pIOHandler)
{
throw new ImportErrorException(AIUT_DEF_ERROR_TEXT);
}
void ImporterTest :: setUp (void)
{
pImp = new Importer();
}
void ImporterTest :: tearDown (void)
{
delete pImp;
}
void ImporterTest :: testIntProperty (void)
{
bool b;
pImp->SetPropertyInteger("quakquak",1503,&b);
CPPUNIT_ASSERT(!b);
CPPUNIT_ASSERT(1503 == pImp->GetPropertyInteger("quakquak",0));
CPPUNIT_ASSERT(314159 == pImp->GetPropertyInteger("not_there",314159));
pImp->SetPropertyInteger("quakquak",1504,&b);
CPPUNIT_ASSERT(b);
}
void ImporterTest :: testFloatProperty (void)
{
bool b;
pImp->SetPropertyFloat("quakquak",1503.f,&b);
CPPUNIT_ASSERT(!b);
CPPUNIT_ASSERT(1503.f == pImp->GetPropertyFloat("quakquak",0.f));
CPPUNIT_ASSERT(314159.f == pImp->GetPropertyFloat("not_there",314159.f));
}
void ImporterTest :: testStringProperty (void)
{
bool b;
pImp->SetPropertyString("quakquak","test",&b);
CPPUNIT_ASSERT(!b);
CPPUNIT_ASSERT("test" == pImp->GetPropertyString("quakquak","weghwekg"));
CPPUNIT_ASSERT("ILoveYou" == pImp->GetPropertyString("not_there","ILoveYou"));
}
void ImporterTest :: testPluginInterface (void)
{
pImp->RegisterLoader(new TestPlugin());
CPPUNIT_ASSERT(pImp->IsExtensionSupported(".apple"));
CPPUNIT_ASSERT(pImp->IsExtensionSupported(".mac"));
CPPUNIT_ASSERT(pImp->IsExtensionSupported(".linux"));
CPPUNIT_ASSERT(pImp->IsExtensionSupported(".windows"));
TestPlugin* p = (TestPlugin*) pImp->FindLoader(".windows");
CPPUNIT_ASSERT(NULL != p);
try {
p->InternReadFile("",0,NULL);
}
catch ( ImportErrorException* ex)
{
CPPUNIT_ASSERT(ex->GetErrorText() == AIUT_DEF_ERROR_TEXT);
// unregister the plugin and delete it
pImp->UnregisterLoader(p);
delete p;
return;
}
CPPUNIT_ASSERT(false); // control shouldn't reach this point
}
void ImporterTest :: testExtensionCheck (void)
{
std::string s;
pImp->GetExtensionList(s);
// todo ..
}

View File

@ -0,0 +1,57 @@
#ifndef TESTIMPORTER_H
#define TESTIMPORTER_H
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <assimp.hpp>
using namespace std;
using namespace Assimp;
class ImporterTest : public CPPUNIT_NS :: TestFixture
{
CPPUNIT_TEST_SUITE (ImporterTest);
CPPUNIT_TEST (testIntProperty);
CPPUNIT_TEST (testFloatProperty);
CPPUNIT_TEST (testStringProperty);
CPPUNIT_TEST (testPluginInterface);
CPPUNIT_TEST (testExtensionCheck);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp (void);
void tearDown (void);
protected:
void testIntProperty (void);
void testFloatProperty (void);
void testStringProperty (void);
void testPluginInterface (void);
void testExtensionCheck (void);
private:
Importer* pImp;
};
class TestPlugin : public BaseImporter
{
public:
// overriden
bool CanRead( const std::string& pFile,
IOSystem* pIOHandler) const;
// overriden
void GetExtensionList(std::string& append);
// overriden
void InternReadFile( const std::string& pFile,
aiScene* pScene, IOSystem* pIOHandler);
};
#endif

View File

@ -0,0 +1,85 @@
#include "utJoinVertices.h"
CPPUNIT_TEST_SUITE_REGISTRATION (JoinVerticesTest);
void JoinVerticesTest :: setUp (void)
{
// construct the process
this->piProcess = new JoinVerticesProcess();
// create a quite small mesh for testing purposes -
// the mesh itself is *something* but it has redundant vertices
this->pcMesh = new aiMesh();
pcMesh->mNumVertices = 900;
aiVector3D*& pv = pcMesh->mVertices = new aiVector3D[900];
for (unsigned int i = 0; i < 3;++i)
{
const unsigned int base = i*300;
for (unsigned int a = 0; a < 300;++a)
{
pv[base+a].x = pv[base+a].y = pv[base+a].z = (float)a;
}
}
// generate faces - each vertex is referenced once
pcMesh->mNumFaces = 300;
pcMesh->mFaces = new aiFace[300];
for (unsigned int i = 0,p = 0; i < 300;++i)
{
aiFace& face = pcMesh->mFaces[i];
face.mIndices = new unsigned int[ face.mNumIndices = 3 ];
for (unsigned int a = 0; a < 3;++a)
face.mIndices[a] = p++;
}
// generate extra members - set them to zero to make sure they're identical
pcMesh->mTextureCoords[0] = new aiVector3D[900];
for (unsigned int i = 0; i < 900;++i)pcMesh->mTextureCoords[0][i] = 0.f;
pcMesh->mNormals = new aiVector3D[900];
for (unsigned int i = 0; i < 900;++i)pcMesh->mNormals[i] = 0.f;
pcMesh->mTangents = new aiVector3D[900];
for (unsigned int i = 0; i < 900;++i)pcMesh->mTangents[i] = 0.f;
pcMesh->mBitangents = new aiVector3D[900];
for (unsigned int i = 0; i < 900;++i)pcMesh->mBitangents[i] = 0.f;
}
void JoinVerticesTest :: tearDown (void)
{
delete this->pcMesh;
delete this->piProcess;
}
void JoinVerticesTest :: testProcess(void)
{
// execute the step on the given data
this->piProcess->ProcessMesh(this->pcMesh,0);
// the number of faces shouldn't change
CPPUNIT_ASSERT(pcMesh->mNumFaces == 300);
CPPUNIT_ASSERT(pcMesh->mNumVertices == 300);
CPPUNIT_ASSERT(NULL != pcMesh->mNormals);
CPPUNIT_ASSERT(NULL != pcMesh->mTangents);
CPPUNIT_ASSERT(NULL != pcMesh->mBitangents);
CPPUNIT_ASSERT(NULL != pcMesh->mTextureCoords[0]);
// the order doesn't care
float fSum = 0.f;
for (unsigned int i = 0; i < 300;++i)
{
aiVector3D& v = pcMesh->mVertices[i];
fSum += v.x + v.y + v.z;
CPPUNIT_ASSERT(!pcMesh->mNormals[i].x);
CPPUNIT_ASSERT(!pcMesh->mTangents[i].x);
CPPUNIT_ASSERT(!pcMesh->mBitangents[i].x);
CPPUNIT_ASSERT(!pcMesh->mTextureCoords[0][i].x);
}
CPPUNIT_ASSERT(fSum == 150.f*299.f*3.f); // gaussian sum equation
}

View File

@ -0,0 +1,36 @@
#ifndef TESTLBW_H
#define TESTLBW_H
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <aiScene.h>
#include <JoinVerticesProcess.h>
using namespace std;
using namespace Assimp;
class JoinVerticesTest : public CPPUNIT_NS :: TestFixture
{
CPPUNIT_TEST_SUITE (JoinVerticesTest);
CPPUNIT_TEST (testProcess);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp (void);
void tearDown (void);
protected:
void testProcess (void);
private:
JoinVerticesProcess* piProcess;
aiMesh* pcMesh;
};
#endif

View File

@ -4,8 +4,6 @@
#include <cppunit/TestFixture.h> #include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/HelperMacros.h>
#include <aiTypes.h>
#include <aiMesh.h>
#include <aiScene.h> #include <aiScene.h>
#include <LimitBoneWeightsProcess.h> #include <LimitBoneWeightsProcess.h>

View File

View File

@ -0,0 +1,35 @@
#ifndef TESTOG_H
#define TESTOG_H
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <aiScene.h>
#include <OptimizeGraphProcess.h>
using namespace std;
using namespace Assimp;
class OptimizeGraphProcessTest : public CPPUNIT_NS :: TestFixture
{
CPPUNIT_TEST_SUITE (OptimizeGraphProcessTest);
CPPUNIT_TEST (testProcess);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp (void);
void tearDown (void);
protected:
void testProcess (void);
private:
OptimizeGraphProcess* piProcess;
aiScene* pcMesh;
};
#endif

View File

@ -134,7 +134,7 @@ DWORD WINAPI LoadThreadProc(LPVOID lpParameter)
aiProcess_ConvertToLeftHanded | // convert everything to D3D left handed space aiProcess_ConvertToLeftHanded | // convert everything to D3D left handed space
aiProcess_SplitLargeMeshes | // split large, unrenderable meshes into submeshes aiProcess_SplitLargeMeshes | // split large, unrenderable meshes into submeshes
aiProcess_ValidateDataStructure | aiProcess_ImproveCacheLocality aiProcess_ValidateDataStructure | aiProcess_ImproveCacheLocality
| aiProcess_RemoveRedundantMaterials | aiProcess_FixInfacingNormals); // validate the output data structure | aiProcess_RemoveRedundantMaterials ); // validate the output data structure
// get the end time of zje operation, calculate delta t // get the end time of zje operation, calculate delta t
double fEnd = (double)timeGetTime(); double fEnd = (double)timeGetTime();

View File

@ -178,7 +178,7 @@
</component> </component>
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://J:/Programmieren/ASSIMP/ASSIMP3/port/jAssimp/assimp.iml" filepath="J:/Programmieren/ASSIMP/ASSIMP3/port/jAssimp/assimp.iml" /> <module fileurl="file://J:/Programmieren/ASSIMP/assimp3/trunk/port/jAssimp/assimp.iml" filepath="J:/Programmieren/ASSIMP/assimp3/trunk/port/jAssimp/assimp.iml" />
</modules> </modules>
</component> </component>
<component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" /> <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" />

View File

@ -683,6 +683,10 @@
RelativePath="..\..\test\unit\utGenNormals.cpp" RelativePath="..\..\test\unit\utGenNormals.cpp"
> >
</File> </File>
<File
RelativePath="..\..\test\unit\utImporter.cpp"
>
</File>
<File <File
RelativePath="..\..\test\unit\utImproveCacheLocality.cpp" RelativePath="..\..\test\unit\utImproveCacheLocality.cpp"
> >
@ -699,6 +703,10 @@
RelativePath="..\..\test\unit\utMaterialSystem.cpp" RelativePath="..\..\test\unit\utMaterialSystem.cpp"
> >
</File> </File>
<File
RelativePath="..\..\test\unit\utOptimizeGraph.cpp"
>
</File>
<File <File
RelativePath="..\..\test\unit\utPretransformVertices.cpp" RelativePath="..\..\test\unit\utPretransformVertices.cpp"
> >
@ -733,6 +741,14 @@
RelativePath="..\..\test\unit\utGenNormals.h" RelativePath="..\..\test\unit\utGenNormals.h"
> >
</File> </File>
<File
RelativePath="..\..\test\unit\utImporter.h"
>
</File>
<File
RelativePath="..\..\test\unit\utJoinVertices.h"
>
</File>
<File <File
RelativePath="..\..\test\unit\utLimitBoneWeights.h" RelativePath="..\..\test\unit\utLimitBoneWeights.h"
> >
@ -741,6 +757,10 @@
RelativePath="..\..\test\unit\utMaterialSystem.h" RelativePath="..\..\test\unit\utMaterialSystem.h"
> >
</File> </File>
<File
RelativePath="..\..\test\unit\utOptimizeGraph.h"
>
</File>
<File <File
RelativePath="..\..\test\unit\utPretransformVertices.h" RelativePath="..\..\test\unit\utPretransformVertices.h"
> >

View File

@ -734,6 +734,10 @@
RelativePath="..\..\code\FixNormalsStep.h" RelativePath="..\..\code\FixNormalsStep.h"
> >
</File> </File>
<File
RelativePath="..\..\code\GenericProperty.h"
>
</File>
<File <File
RelativePath="..\..\code\GenFaceNormalsProcess.h" RelativePath="..\..\code\GenFaceNormalsProcess.h"
> >
@ -770,6 +774,10 @@
RelativePath="..\..\code\MaterialSystem.h" RelativePath="..\..\code\MaterialSystem.h"
> >
</File> </File>
<File
RelativePath="..\..\code\OptimizeGraphProcess.h"
>
</File>
<File <File
RelativePath="..\..\code\ParsingUtils.h" RelativePath="..\..\code\ParsingUtils.h"
> >
@ -790,6 +798,18 @@
RelativePath="..\..\code\RemoveRedundantMaterials.h" RelativePath="..\..\code\RemoveRedundantMaterials.h"
> >
</File> </File>
<File
RelativePath="..\..\code\SGSpatialSort.h"
>
</File>
<File
RelativePath="..\..\code\SmoothingGroups.h"
>
</File>
<File
RelativePath="..\..\code\SmoothingGroups.inl"
>
</File>
<File <File
RelativePath="..\..\code\SpatialSort.h" RelativePath="..\..\code\SpatialSort.h"
> >
@ -868,10 +888,6 @@
RelativePath="..\..\code\3DSLoader.h" RelativePath="..\..\code\3DSLoader.h"
> >
</File> </File>
<File
RelativePath="..\..\code\3DSSpatialSort.h"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="ASE" Name="ASE"
@ -886,7 +902,7 @@
</File> </File>
</Filter> </Filter>
<Filter <Filter
Name="HMPLoader" Name="HMP"
> >
<File <File
RelativePath="..\..\code\HMPFileData.h" RelativePath="..\..\code\HMPFileData.h"
@ -1061,6 +1077,14 @@
> >
</File> </File>
</Filter> </Filter>
<Filter
Name="DXF"
>
<File
RelativePath="..\..\code\DXFLoader.h"
>
</File>
</Filter>
</Filter> </Filter>
</Filter> </Filter>
<Filter <Filter
@ -1138,6 +1162,10 @@
RelativePath="..\..\code\MaterialSystem.cpp" RelativePath="..\..\code\MaterialSystem.cpp"
> >
</File> </File>
<File
RelativePath="..\..\code\OptimizeGraphProcess.cpp"
>
</File>
<File <File
RelativePath="..\..\code\PretransformVertices.cpp" RelativePath="..\..\code\PretransformVertices.cpp"
> >
@ -1150,6 +1178,10 @@
RelativePath="..\..\code\RemoveRedundantMaterials.cpp" RelativePath="..\..\code\RemoveRedundantMaterials.cpp"
> >
</File> </File>
<File
RelativePath="..\..\code\SGSpatialSort.cpp"
>
</File>
<File <File
RelativePath="..\..\code\SpatialSort.cpp" RelativePath="..\..\code\SpatialSort.cpp"
> >
@ -1196,18 +1228,10 @@
RelativePath="..\..\code\3DSConverter.cpp" RelativePath="..\..\code\3DSConverter.cpp"
> >
</File> </File>
<File
RelativePath="..\..\code\3DSGenNormals.cpp"
>
</File>
<File <File
RelativePath="..\..\code\3DSLoader.cpp" RelativePath="..\..\code\3DSLoader.cpp"
> >
</File> </File>
<File
RelativePath="..\..\code\3DSSpatialSort.cpp"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="ASE" Name="ASE"
@ -1345,6 +1369,14 @@
> >
</File> </File>
</Filter> </Filter>
<Filter
Name="DXF"
>
<File
RelativePath="..\..\code\DXFLoader.cpp"
>
</File>
</Filter>
</Filter> </Filter>
</Filter> </Filter>
<Filter <Filter

View File

@ -238,22 +238,6 @@
Filter="h;hpp;hxx;hm;inl;inc;xsd" Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
> >
<File
RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer.h"
>
</File>
<File
RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_DefaultIOStream.h"
>
</File>
<File
RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_DefaultIOSystem.h"
>
</File>
<File
RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_Property.h"
>
</File>
<File <File
RelativePath="..\..\port\jAssimp\jni_bridge\JNIEnvironment.h" RelativePath="..\..\port\jAssimp\jni_bridge\JNIEnvironment.h"
> >
@ -262,6 +246,26 @@
RelativePath="..\..\port\jAssimp\jni_bridge\JNILogger.h" RelativePath="..\..\port\jAssimp\jni_bridge\JNILogger.h"
> >
</File> </File>
<Filter
Name="javah"
>
<File
RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer.h"
>
</File>
<File
RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_DefaultIOStream.h"
>
</File>
<File
RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_DefaultIOSystem.h"
>
</File>
<File
RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_Property.h"
>
</File>
</Filter>
</Filter> </Filter>
<Filter <Filter
Name="resources" Name="resources"