- Added format auto-detection to most loaders
  - Simplified BaseImporter::CanRead() with some utility methods
  - improved fast_atof -> no overruns anymore. Fuck you, irrlicht.
  - added assimp_cmd tool to allow command line model processing. Mainly adebugging tool for internal purposes, but others might find it useful, too.
  - vc8/vc9: revision number is now written to DLL version header
  - mkutil: some batch scripts to simplify tagging & building of release versions
  - some API cleanup
  - fixing some doxygen markup (+now explicit use of @file <filename>)
  - Icon for assimp_view and assimp_cmd

3DS
  - Normal vectors are not anymore inverted in some cases
  - Improved pivot handling
  - Improved handling of x-flipped meshes

Collada
  - fixed a minor bug (visual_scene element)

LWS 
  - WIP implementation. No animations yet, some bugs and crashes.
  - Animation system remains disabled, WIP code
  - many test files for LWS, but most of them test the anim support, which is, read above, currently disabled.

STL
  - fixing a log warning which appears for every model
  - added binary&ascii test spider, exported from truespace

MD3 
  - Cleaning up output tags for automatically joined player models.


IRR
  - Fixing coordinate system issues. 
  - Instance handling improved.
  - Some of the reported crashes not yet fixed.

PretransformVertices
  - Numerous performance improvements.
  - Added config option to preserve the hierarchy during the step.

RemoveRedundantMaterials
  - Added config option to specify a list of materials which are kept in every case.

UNREAL
  - Added support for the old unreal data format (*.a,*.d,*.uc)
  - tested only with exports from Milkshape
  - more Unreal stuff to come soon



git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@356 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2009-03-05 22:32:13 +00:00
parent caaf02b0e4
commit 4bbc03332b
237 changed files with 33463 additions and 2245 deletions

39
INSTALL
View File

@ -1,7 +1,32 @@
Open Asset Import Library (Assimp) Install
-----------------------------------------
To take a look into the ASSIMP library just get the code, go to the
workspaces-directory and open your prefered build enviroment. Now just build
the engine, start the ASSIMP-Viewer application and select one of our basic test-files.
You need boost-1.35 to build the Asset Import Library.
Open Asset Import Library (Assimp) Install
------------------------------------------------
Please see the doxygen documentation to learn how to build & use Assimp.
A CHM file is included in the SVN repos: ./doc/lib_htmp/AssimpDoc.chm.
At least Windows should be able to read it.
To build the doxygen doc on your own follow these steps:
a) download & install latest doxygen
b) ensure doxygen is in the executable search path
c) navigate to ./doc
d) and run 'doxygen'
Open the generated HTML (lib_htmp/index.html) in the browser of your choice.
Windows only: To generate the CHM doc install the 'Microsoft HTML Workshop'
and configure the path to it in the DOXYFILE. Run doxygen again.
You can also find a copy of the doc on our web site:
http://assimp.sourceforge.net/lib_html/index.html
Beware, it could be outdated. If you're in serious doubt it might be,
rebuilding the doc is probably a wise choice.

View File

@ -1,6 +0,0 @@
cd code
mingw32-make -f makefile.mingw clean
cd ..
del /Q /S obj bin lib

View File

@ -54,14 +54,13 @@ using namespace Assimp;
// Setup final material indices, generae a default material if necessary
void Discreet3DSImporter::ReplaceDefaultMaterial()
{
//////////////////////////////////////////////////////////////////////////
// Try to find an existing material that matches the
// typical default material setting:
// - no textures
// - diffuse color (in grey!)
// NOTE: This is here to workaround the fact that some
// exporters are writing a default material, too.
//////////////////////////////////////////////////////////////////////////
unsigned int idx = 0xcdcdcdcd;
for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
{
@ -438,57 +437,65 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
iArray.reserve(3);
aiMatrix4x4 abs;
/*if (pcIn->mName == "$$$DUMMY") {
// FIX: Append the "real" name of the dummy to the string
pcIn->mName = "Dummy." + pcIn->mDummyName;
// Find all meshes with the same name as the node
for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
{
const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0];
ai_assert(NULL != pcMesh);
if (pcIn->mName == pcMesh->mName)
iArray.push_back(a);
}
else*/ // if (pcIn->mName != "$$$DUMMY")
{
// Find all meshes with the same name as the node
for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
{
const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0];
ai_assert(NULL != pcMesh);
if (!iArray.empty())
{
// The matrix should be identical for all meshes with the
// same name. It HAS to be identical for all meshes .....
D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]);
if (pcIn->mName == pcMesh->mName)
iArray.push_back(a);
}
if (!iArray.empty())
{
// The matrix should be identical for all meshes with the
// same name. It HAS to be identical for all meshes .....
aiMatrix4x4 mInv = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0])->mMat;
mInv.Inverse();
const aiVector3D& pivot = pcIn->vPivot;
// Compute the inverse of the transformation matrix to move the
// vertices back to their relative and local space
aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat;
mInv.Inverse();mInvTransposed.Transpose();
aiVector3D pivot = pcIn->vPivot;
pcOut->mNumMeshes = (unsigned int)iArray.size();
pcOut->mMeshes = new unsigned int[iArray.size()];
for (unsigned int i = 0;i < iArray.size();++i)
{
const unsigned int iIndex = iArray[i];
aiMesh* const mesh = pcSOut->mMeshes[iIndex];
pcOut->mNumMeshes = (unsigned int)iArray.size();
pcOut->mMeshes = new unsigned int[iArray.size()];
for (unsigned int i = 0;i < iArray.size();++i) {
const unsigned int iIndex = iArray[i];
aiMesh* const mesh = pcSOut->mMeshes[iIndex];
// Pivot point adjustment
// See: http://www.zfx.info/DisplayThread.php?MID=235690#235690
const aiVector3D* const pvEnd = mesh->mVertices+mesh->mNumVertices;
aiVector3D* pvCurrent = mesh->mVertices;
// Transform the vertices back into their local space
// fixme: consider computing normals after this, so we don't need to transform them
const aiVector3D* const pvEnd = mesh->mVertices+mesh->mNumVertices;
aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
if(pivot.x || pivot.y || pivot.z)
{
for (;pvCurrent != pvEnd;++pvCurrent)
{
*pvCurrent = mInv * (*pvCurrent);
*pvCurrent -= pivot;
}
}
else
{
for (;pvCurrent != pvEnd;++pvCurrent)
*pvCurrent = mInv * (*pvCurrent);
}
// Setup the mesh index
pcOut->mMeshes[i] = iIndex;
for (;pvCurrent != pvEnd;++pvCurrent,++t2) {
*pvCurrent = mInv * (*pvCurrent);
*t2 = mInvTransposed * (*t2);
}
// Handle negative transformation matrix determinant -> invert vertex x
if (imesh->mMat.Determinant() < 0.0f)
{
/* we *must* have normals */
for (pvCurrent = mesh->mVertices,t2 = mesh->mNormals;pvCurrent != pvEnd;++pvCurrent,++t2) {
pvCurrent->x *= -1.f;
t2->x *= -1.f;
}
DefaultLogger::get()->info("3DS: Flipping mesh X-Axis");
}
// Handle pivot point
if(pivot.x || pivot.y || pivot.z)
{
for (pvCurrent = mesh->mVertices;pvCurrent != pvEnd;++pvCurrent) {
*pvCurrent -= pivot;
}
}
// Setup the mesh index
pcOut->mMeshes[i] = iIndex;
}
}
@ -526,7 +533,7 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
}
// Generate animation channels for the node
if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
pcIn->aTargetPositionKeys.size() > 1)
{

View File

@ -124,11 +124,10 @@ public:
CHUNK_PERCENTF = 0x0031, // float4 percentage
// ********************************************************************
// Unknown and ignored. Possibly a chunk used by PROJ (
// Discreet 3DS max Project File)?
// Prj master chunk
CHUNK_PRJ = 0xC23D,
// Unknown. Possibly a reference to an external .mli file?
// MDLI master chunk
CHUNK_MLI = 0x3DAA,
// Primary main chunk of the .3ds file
@ -178,7 +177,6 @@ public:
CHUNK_MESHCOLOR = 0x4165,
CHUNK_TXTINFO = 0x4170,
CHUNK_LIGHT = 0x4600,
CHUNK_SPOTLIGHT = 0x4610,
CHUNK_CAMERA = 0x4700,
CHUNK_HIERARCHY = 0x4F00,
@ -330,7 +328,7 @@ struct Texture
, mMapMode (aiTextureMapMode_Wrap)
, iUVSrc (0)
{
mTextureBlend = std::numeric_limits<float>::quiet_NaN();
mTextureBlend = get_qnan();
}
//! Specifies the blend factor for the texture

View File

@ -39,7 +39,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the 3ds importer class */
/** @file 3DSLoader.cpp
* @brief Implementation of the 3ds importer class
*
* http://www.the-labs.com/Blender/3DS-details.html
*/
#include "AssimpPCH.h"
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
@ -53,12 +57,14 @@ using namespace Assimp;
// Begins a new parsing block
// - Reads the current chunk and validates it
// - computes its length
#define ASSIMP_3DS_BEGIN_CHUNK() \
Discreet3DS::Chunk chunk; \
ReadChunk(&chunk); \
int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \
const int oldReadLimit = stream->GetReadLimit(); \
stream->SetReadLimit(stream->GetCurrentPos() + chunkSize);
#define ASSIMP_3DS_BEGIN_CHUNK() \
if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)) \
return; \
Discreet3DS::Chunk chunk; \
ReadChunk(&chunk); \
int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \
const int oldReadLimit = stream->GetReadLimit(); \
stream->SetReadLimit(stream->GetCurrentPos() + chunkSize);
// ------------------------------------------------------------------------------------------------
@ -82,18 +88,27 @@ Discreet3DSImporter::~Discreet3DSImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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);
for (std::string::iterator i = extension.begin(); i != extension.end();++i)
*i = ::tolower(*i);
std::string extension = GetExtension(pFile);
if(extension == "3ds" || extension == "prj" ) {
return true;
}
if (!extension.length() || checkSig) {
uint16_t token[3];
token[0] = 0x4d4d;
token[1] = 0x3dc2;
//token[2] = 0x3daa;
return CheckMagicToken(pIOHandler,pFile,token,2,0,2);
}
return false;
}
return (extension == ".3ds");
// ------------------------------------------------------------------------------------------------
// Get list of all extension supported by this loader
void Discreet3DSImporter::GetExtensionList(std::string& append)
{
append.append("*.3ds;*.prj");
}
// ------------------------------------------------------------------------------------------------
@ -128,6 +143,7 @@ void Discreet3DSImporter::InternReadFile( const std::string& pFile,
mMasterScale = 1.0f;
mBackgroundImage = "";
bHasBG = false;
bIsPrj = false;
// Parse the file
ParseMainChunk();
@ -138,8 +154,7 @@ void Discreet3DSImporter::InternReadFile( const std::string& pFile,
// vectors from the smoothing groups we read from the
// file.
for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(),
end = mScene->mMeshes.end(); i != end;++i)
{
end = mScene->mMeshes.end(); i != end;++i) {
CheckIndices(*i);
MakeUnique (*i);
ComputeNormalsWithSmoothingsGroups<D3DS::Face>(*i);
@ -226,6 +241,9 @@ void Discreet3DSImporter::ParseMainChunk()
// get chunk type
switch (chunk.Flag)
{
case Discreet3DS::CHUNK_PRJ:
bIsPrj = true;
case Discreet3DS::CHUNK_MAIN:
ParseEditorChunk();
break;
@ -371,9 +389,9 @@ void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num)
light->mColorDiffuse = aiColor3D(1.f,1.f,1.f);
// Now check for further subchunks (excluding color)
int8_t* p = stream->GetPtr();
ParseLightChunk();
// Now check for further subchunks
if (!bIsPrj) /* fixme */
ParseLightChunk();
// The specular light color is identical the the diffuse light
// color. The ambient light color is equal to the ambient base
@ -424,6 +442,10 @@ void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num)
if (camera->mHorizontalFOV < 0.001f)
camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f);
}
// Now check for further subchunks
if (!bIsPrj) /* fixme */
ParseCameraChunk();
break;
};
ASSIMP_3DS_END_CHUNK();
@ -440,7 +462,7 @@ void Discreet3DSImporter::ParseLightChunk()
// get chunk type
switch (chunk.Flag)
{
case Discreet3DS::CHUNK_SPOTLIGHT:
case Discreet3DS::CHUNK_DL_SPOTLIGHT:
// Now we can be sure that the light is a spot light
light->mType = aiLightSource_SPOT;
@ -511,7 +533,7 @@ void Discreet3DSImporter::ParseKeyframeChunk()
switch (chunk.Flag)
{
case Discreet3DS::CHUNK_TRACKCAMTGT:
case Discreet3DS::CHUNK_SPOTLIGHT:
case Discreet3DS::CHUNK_TRACKSPOTL:
case Discreet3DS::CHUNK_TRACKCAMERA:
case Discreet3DS::CHUNK_TRACKINFO:
case Discreet3DS::CHUNK_TRACKLIGHT:
@ -574,14 +596,11 @@ void Discreet3DSImporter::SkipTCBInfo()
{
unsigned int flags = stream->GetI2();
if (!flags)
{
//////////////////////////////////////////////////////////////////////////
if (!flags) {
// Currently we can't do anything with these values. They occur
// quite rare, so it wouldn't be worth the effort implementing
// them. 3DS ist not really suitable for complex animations,
// so full support is not required.
//////////////////////////////////////////////////////////////////////////
DefaultLogger::get()->warn("3DS: Skipping TCB animation info");
}
@ -1015,35 +1034,7 @@ void Discreet3DSImporter::ParseMeshChunk()
mMesh.mMat.a4 = stream->GetF4();
mMesh.mMat.b4 = stream->GetF4();
mMesh.mMat.c4 = stream->GetF4();
// Now check whether the matrix has got a negative determinant
// If yes, we need to flip all vertices' Z axis ....
// This code has been taken from lib3ds
if (mMesh.mMat.Determinant() < 0.0f) {
// Compute the inverse of the matrix
aiMatrix4x4 mInv = mMesh.mMat;
mInv.Inverse();
aiMatrix4x4 mMe = mMesh.mMat;
mMe.c1 *= -1.0f;
mMe.c2 *= -1.0f;
mMe.c3 *= -1.0f;
mMe.c4 *= -1.0f;
mInv = mInv * mMe;
// Now transform all vertices
for (unsigned int i = 0; i < (unsigned int)mMesh.mPositions.size();++i)
{
aiVector3D a,c;
a = mMesh.mPositions[i];
c[0]= mInv[0][0]*a[0] + mInv[1][0]*a[1] + mInv[2][0]*a[2] + mInv[3][0];
c[1]= mInv[0][1]*a[0] + mInv[1][1]*a[1] + mInv[2][1]*a[2] + mInv[3][1];
c[2]= mInv[0][2]*a[0] + mInv[1][2]*a[1] + mInv[2][2]*a[2] + mInv[3][2];
mMesh.mPositions[i] = c;
}
DefaultLogger::get()->info("3DS: Flipping mesh Z-Axis");
}}
}
break;
case Discreet3DS::CHUNK_MAPLIST:
@ -1353,7 +1344,7 @@ float Discreet3DSImporter::ParsePercentageChunk()
return stream->GetF4();
else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag)
return (float)((uint16_t)stream->GetI2()) / (float)0xFFFF;
return std::numeric_limits<float>::quiet_NaN();
return get_qnan();
}
// ------------------------------------------------------------------------------------------------
@ -1364,7 +1355,7 @@ void Discreet3DSImporter::ParseColorChunk(aiColor3D* out,
ai_assert(out != NULL);
// error return value
const float qnan = std::numeric_limits<float>::quiet_NaN();
const float qnan = get_qnan();
static const aiColor3D clrError = aiColor3D(qnan,qnan,qnan);
Discreet3DS::Chunk chunk;

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Definition of the .3ds importer class. */
/** @file 3DSLoader.h
* @brief 3DS File format loader
*/
#ifndef AI_3DSIMPORTER_H_INC
#define AI_3DSIMPORTER_H_INC
@ -49,16 +51,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
struct aiNode;
#include "3DSHelper.h"
namespace Assimp
{
namespace Assimp {
class MaterialHelper;
using namespace D3DS;
// ---------------------------------------------------------------------------
/** The Discreet3DSImporter is a worker class capable of importing a scene from a
* 3ds Max 4/5 Files (.3ds)
*/
// ---------------------------------------------------------------------------------
/** Importer class for 3D Studio r3 and r4 3DS files
*/
class Discreet3DSImporter : public BaseImporter
{
friend class Importer;
@ -74,14 +74,16 @@ 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;
* See BaseImporter::CanRead() for details.
*/
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
// -------------------------------------------------------------------
/** Called prior to ReadFile().
* The function is a request to the importer to update its configuration
* basing on the Importer's configuration property list.
*/
* The function is a request to the importer to update its configuration
* basing on the Importer's configuration property list.
*/
void SetupProperties(const Importer* pImp);
protected:
@ -90,21 +92,18 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.3ds");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene,
IOSystem* pIOHandler);
// -------------------------------------------------------------------
/** Converts a temporary material to the outer representation
*/
*/
void ConvertMaterial(D3DS::Material& p_cMat,
MaterialHelper& p_pcOut);
@ -112,7 +111,7 @@ protected:
/** Read a chunk
*
* @param pcOut Receives the current chunk
*/
*/
void ReadChunk(Discreet3DS::Chunk* pcOut);
// -------------------------------------------------------------------
@ -271,6 +270,9 @@ protected:
/** Path to the background image of the scene */
std::string mBackgroundImage;
bool bHasBG;
/** true if PRJ file */
bool bIsPrj;
};
} // end of namespace Assimp

View File

@ -108,28 +108,38 @@ using namespace Assimp;
// Constructor to be privately used by Importer
AC3DImporter::AC3DImporter()
{
// nothing to be done here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
AC3DImporter::~AC3DImporter()
{
// nothing to be done here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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);
std::string extension = GetExtension(pFile);
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
*it = tolower( *it);
// fixme: are acc and ac3d *really* used? Some sources say they are
if(extension == "ac" || extension == "ac3d" || extension == "acc") {
return true;
}
if (!extension.length() || checkSig) {
uint32_t token = AI_MAKE_MAGIC("AC3D");
return CheckMagicToken(pIOHandler,pFile,&token,1,0);
}
return false;
}
return( extension == ".ac3d" || extension == ".ac");
// ------------------------------------------------------------------------------------------------
// Get list of file extensions handled by this loader
void AC3DImporter::GetExtensionList(std::string& append)
{
append.append("*.ac;*.acc;*.ac3d");
}
// ------------------------------------------------------------------------------------------------
@ -140,7 +150,6 @@ bool AC3DImporter::GetNextLine( )
return SkipSpaces(&buffer);
}
// ------------------------------------------------------------------------------------------------
// Parse an object section in an AC file
void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)

View File

@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the .ac importer class. */
/** @file ACLoader.h
* @brief Declaration of the .ac importer class.
*/
#ifndef AI_AC3DLOADER_H_INCLUDED
#define AI_AC3DLOADER_H_INCLUDED
@ -166,8 +168,10 @@ 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;
* See BaseImporter::CanRead() for details.
*/
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -175,10 +179,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.ac;*.acc");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the ASE importer class */
/** @file ASELoader.cpp
* @brief Implementation of the ASE importer class
*/
#include "AssimpPCH.h"
#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
@ -69,23 +71,25 @@ ASEImporter::~ASEImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) 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;
// check file extension
const std::string extension = GetExtension(pFile);
if( extension == "ase" || extension == "ask")
return true;
std::string extension = pFile.substr( pos);
if ((!extension.length() || cs) && pIOHandler) {
const char* tokens[] = {"*3dsmax_asciiexport"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
}
// Either ASE, ASC or ASK
return !(extension.length() < 4 || extension[0] != '.' ||
extension[1] != 'a' && extension[1] != 'A' ||
extension[2] != 's' && extension[2] != 'S' ||
extension[3] != 'e' && extension[3] != 'E' &&
extension[3] != 'k' && extension[3] != 'K' &&
extension[3] != 'c' && extension[3] != 'C');
// ------------------------------------------------------------------------------------------------
void ASEImporter::GetExtensionList(std::string& append)
{
append.append("*.ase;*.ask");
}
// ------------------------------------------------------------------------------------------------
@ -108,6 +112,8 @@ void ASEImporter::InternReadFile( const std::string& pFile,
throw new ImportErrorException( "Failed to open ASE file " + pFile + ".");
size_t fileSize = file->FileSize();
if (!fileSize)
throw new ImportErrorException( "ASE: File is empty");
// Allocate storage and copy the contents of the file to a memory buffer
// (terminate it with zero)
@ -778,8 +784,7 @@ void ASEImporter::BuildNodes()
delete pc;
}
// The root node should not have at least one child or the file is invalid
else if (!pcScene->mRootNode->mNumChildren)
{
else if (!pcScene->mRootNode->mNumChildren) {
throw new ImportErrorException("No nodes loaded. The ASE/ASK file is either empty or corrupt");
}
return;

View File

@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Definition of the .ASE importer class. */
/** @file ASELoader.h
* @brief Definition of the .ASE importer class.
*/
#ifndef AI_ASELOADER_H_INCLUDED
#define AI_ASELOADER_H_INCLUDED
@ -48,15 +50,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
struct aiNode;
#include "ASEParser.h"
namespace Assimp
{
namespace Assimp {
class MaterialHelper;
using namespace ASE;
// ---------------------------------------------------------------------------
/** Used to load ASE files
*/
// --------------------------------------------------------------------------------
/** Importer class for the 3DS ASE ASCII format
*
* fixme: consider code cleanup
*/
class ASEImporter : public BaseImporter
{
friend class Importer;
@ -72,8 +75,10 @@ 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;
* See BaseImporter::CanRead() for details.
*/
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -81,10 +86,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.ase;*.ask;*.asc");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the ASE parser class */
/** @file ASEParser.cpp
* @brief Implementation of the ASE parser class
*/
#include "AssimpPCH.h"
@ -120,8 +122,8 @@ Parser::Parser (const char* szFile, unsigned int fileFormatDefault)
iFileFormat = fileFormatDefault;
// make sure that the color values are invalid
m_clrBackground.r = std::numeric_limits<float>::quiet_NaN();
m_clrAmbient.r = std::numeric_limits<float>::quiet_NaN();
m_clrBackground.r = get_qnan();
m_clrAmbient.r = get_qnan();
// setup some default values
iLineNumber = 0;

View File

@ -227,7 +227,7 @@ struct BaseNode
mName = szTemp;
// Set mTargetPosition to qnan
const float qnan = std::numeric_limits<float>::quiet_NaN();
const float qnan = get_qnan();
mTargetPosition.x = qnan;
}

View File

@ -1,5 +1,5 @@
// Actually just a dummyy, used by the compiler to build the precompiled header.
// Actually just a dummy, used by the compiler to build the precompiled header.
#include "AssimpPCH.h"
#include "./../include/aiVersion.h"
@ -12,7 +12,7 @@ static const char* LEGAL_INFORMATION =
"A free C/C++ library to import various 3D file formats into applications\n\n"
"(c) ASSIMP Development Team, 2008-2009\n"
"License: BSD\n"
"License: 3-clause BSD license\n"
"Website: http://assimp.sourceforge.net\n"
;
@ -59,10 +59,12 @@ ASSIMP_API unsigned int aiGetCompileFlags () {
return flags;
}
// include current build revision
#include "../mkutil/revision.h"
// ------------------------------------------------------------------------------------------------
ASSIMP_API unsigned int aiGetVersionRevision ()
{
// TODO: find a way to update the revision number automatically
return 306;
return SVNRevision;
}

View File

@ -67,6 +67,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
#endif
// size_t to unsigned int, possible loss of data.
// Yes, the compiler is right with his warning, but this loss of data
// won't be a problem for us. So shut up little boy.
#ifdef _MSC_VER
# pragma warning (disable : 4267)
#endif
// Actually that's not required for MSVC (it is included somewhere in
// the STL ..) but it is necessary for build with STLport.
#include <ctype.h>
@ -75,6 +82,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector>
#include <list>
#include <map>
#include <set>
#include <string>
#include <sstream>
#include <iomanip>

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the b3d importer class */
/** @file B3DImporter.cpp
* @brief Implementation of the b3d importer class
*/
#include "AssimpPCH.h"
#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
@ -52,7 +54,7 @@ using namespace Assimp;
using namespace std;
// ------------------------------------------------------------------------------------------------
bool B3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const{
bool B3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const{
int pos=pFile.find_last_of( '.' );
if( pos==string::npos ) return false;

View File

@ -56,7 +56,7 @@ namespace Assimp{
class B3DImporter : public BaseImporter{
public:
virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
protected:

View File

@ -61,18 +61,19 @@ BVHLoader::~BVHLoader()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
{
// check file extension
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);
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
*it = tolower( *it);
const std::string extension = GetExtension(pFile);
if( extension == "bvh")
return true;
return ( extension == ".bvh");
if ((!extension.length() || cs) && pIOHandler) {
const char* tokens[] = {"HIERARCHY"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
}
// ------------------------------------------------------------------------------------------------

View File

@ -40,6 +40,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file BVHLoader.h
* @brief Biovision BVH import
*/
#ifndef AI_BVHLOADER_H_INC
#define AI_BVHLOADER_H_INC
@ -48,9 +52,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp
{
/** Loader class to read Motion Capturing data from a .bvh file. This format only contains a
* hierarchy of joints and a series of keyframes for the hierarchy. It contains no actual mesh data,
* but we generate a dummy mesh inside the loader just to be able to see something.
// --------------------------------------------------------------------------------
/** Loader class to read Motion Capturing data from a .bvh file.
*
* This format only contains a hierarchy of joints and a series of keyframes for
* the hierarchy. It contains no actual mesh data, but we generate a dummy mesh
* inside the loader just to be able to see something.
*/
class BVHLoader : public BaseImporter
{
@ -88,7 +95,7 @@ protected:
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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const;
protected:
/** Called by Importer::GetExtensionList() for each loaded importer.

View File

@ -39,12 +39,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of BaseImporter */
/** @file BaseImporter.cpp
* @brief Implementation of BaseImporter
*/
#include "AssimpPCH.h"
#include "BaseImporter.h"
using namespace Assimp;
@ -98,7 +99,7 @@ void BaseImporter::SetupProperties(const Importer* pImp)
}
// ------------------------------------------------------------------------------------------------
bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler,
/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler,
const std::string& pFile,
const char** tokens,
unsigned int numTokens,
@ -142,17 +143,105 @@ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler,
return false;
}
// ------------------------------------------------------------------------------------------------
// Simple check for file extension
/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile,
const char* ext0,
const char* ext1,
const char* ext2)
{
std::string::size_type pos = pFile.find_last_of('.');
// no file extension - can't read
if( pos == std::string::npos)
return false;
const char* ext_real = & pFile[ pos+1 ];
if( !ASSIMP_stricmp(ext_real,ext0) )
return true;
// check for other, optional, file extensions
if (ext1 && !ASSIMP_stricmp(ext_real,ext1))
return true;
if (ext2 && !ASSIMP_stricmp(ext_real,ext2))
return true;
return false;
}
// ------------------------------------------------------------------------------------------------
// Get file extension from path
/*static*/ std::string BaseImporter::GetExtension (const std::string& pFile)
{
std::string::size_type pos = pFile.find_last_of('.');
// no file extension at all
if( pos == std::string::npos)
return "";
std::string ret = pFile.substr(pos+1);
std::transform(ret.begin(),ret.end(),ret.begin(),::tolower); // thanks to Andy Maloney for the hint
return ret;
}
// ------------------------------------------------------------------------------------------------
// Check for magic bytes at the beginning of the file.
/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile,
const void* _magic, unsigned int num, unsigned int offset, unsigned int size)
{
ai_assert(size <= 16 && _magic && num && pIOHandler);
const char* magic = (const char*)_magic;
boost::scoped_ptr<IOStream> pStream (pIOHandler->Open(pFile));
if (pStream.get() )
{
// skip to offset
pStream->Seek(offset,aiOrigin_SET);
// read 'size' characters from the file
char data[16];
if(size != pStream->Read(data,1,size))
return false;
for (unsigned int i = 0; i < num; ++i) {
// also check against big endian versions of tokens with size 2,4
// that's just for convinience, the chance that we cause conflicts
// is quite low and it can save some lines and prevent nasty bugs
if (2 == size) {
int16_t rev = *((int16_t*)magic);
ByteSwap::Swap(&rev);
if (*((int16_t*)data) == ((int16_t*)magic)[i] || *((int16_t*)data) == rev)
return true;
}
else if (4 == size) {
int32_t rev = *((int32_t*)magic);
ByteSwap::Swap(&rev);
if (*((int32_t*)data) == ((int32_t*)magic)[i] || *((int32_t*)data) == rev)
return true;
}
else {
// any length ... just compare
if(!::memcmp(magic,data,size))
return true;
}
magic += size;
}
}
return false;
}
// ------------------------------------------------------------------------------------------------
// Represents an import request
struct LoadRequest
{
LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map)
LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id)
: file (_file)
, flags (_flags)
, refCnt (1)
, scene (NULL)
, loaded (false)
, id (_id)
{
if (_map)
map = *_map;
@ -164,6 +253,7 @@ struct LoadRequest
aiScene* scene;
bool loaded;
BatchLoader::PropertyMap map;
unsigned int id;
bool operator== (const std::string& f) {
return file == f;
@ -174,6 +264,10 @@ struct LoadRequest
// BatchLoader::pimpl data structure
struct Assimp::BatchData
{
BatchData()
: next_id(0xffff)
{}
// IO system to be used for all imports
IOSystem* pIOSystem;
@ -185,6 +279,9 @@ struct Assimp::BatchData
// Base path
std::string pathBase;
// Id for next item
unsigned int next_id;
};
// ------------------------------------------------------------------------------------------------
@ -219,13 +316,15 @@ void BatchLoader::SetBasePath (const std::string& pBase)
std::string::size_type ss,ss2;
if (std::string::npos != (ss = data->pathBase.find_first_of('.')))
{
if (std::string::npos != (ss2 = data->pathBase.find_last_of('\\')) ||
std::string::npos != (ss2 = data->pathBase.find_last_of('/')))
if (std::string::npos != (ss2 = data->pathBase.find_last_of("\\/")))
{
if (ss > ss2)
data->pathBase.erase(ss2,data->pathBase.length()-ss2);
}
else return;
else {
data->pathBase = "";
return;
}
}
// make sure the directory is terminated properly
@ -235,7 +334,7 @@ void BatchLoader::SetBasePath (const std::string& pBase)
}
// ------------------------------------------------------------------------------------------------
void BatchLoader::AddLoadRequest (const std::string& file,
unsigned int BatchLoader::AddLoadRequest (const std::string& file,
unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/)
{
ai_assert(!file.empty());
@ -245,8 +344,7 @@ void BatchLoader::AddLoadRequest (const std::string& file,
// build a full path if this is a relative path and
// we have a new base directory given
if (file.length() > 2 && file[1] != ':' && data->pathBase.length())
{
if (file.length() > 2 && file[1] != ':' && data->pathBase.length()) {
real = data->pathBase + file;
}
else real = file;
@ -258,32 +356,29 @@ void BatchLoader::AddLoadRequest (const std::string& file,
// Call IOSystem's path comparison function here
if (data->pIOSystem->ComparePaths((*it).file,real))
{
if (map) {
if (!((*it).map == *map))
continue;
}
else if (!(*it).map.empty())
continue;
(*it).refCnt++;
return;
return (*it).id;
}
}
// no, we don't have it. So add it to the queue ...
data->requests.push_back(LoadRequest(real,steps,map));
data->requests.push_back(LoadRequest(real,steps,map,data->next_id));
return data->next_id++;
}
// ------------------------------------------------------------------------------------------------
aiScene* BatchLoader::GetImport (const std::string& file)
aiScene* BatchLoader::GetImport (unsigned int which)
{
// no threaded implementation for the moment
std::string real;
// build a full path if this is a relative path and
// we have a new base directory given
if (file.length() > 2 && file[1] != ':' && data->pathBase.length())
{
real = data->pathBase + file;
}
else real = file;
for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it)
{
// Call IOSystem's path comparison function here
if (data->pIOSystem->ComparePaths((*it).file,real) && (*it).loaded)
if ((*it).id == which && (*it).loaded)
{
aiScene* sc = (*it).scene;
if (!(--(*it).refCnt))

View File

@ -52,6 +52,10 @@ namespace Assimp {
class IOSystem;
class Importer;
// utility to do char4 to uint32 in a portable manner
#define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \
(string[1] << 16) + (string[2] << 8) + string[3]))
// ---------------------------------------------------------------------------
/** Simple exception class to be thrown if an error occurs while importing. */
class ASSIMP_API ImportErrorException
@ -97,36 +101,48 @@ protected:
public:
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* @param pFile Path and file name of the file to be examined.
* @param pIOHandler The IO handler to use for accessing any file.
* @return true if the class can read this file, false if not.
*
* @note Sometimes ASSIMP uses this method to determine whether a
* a given file extension is generally supported. In this case the
* file extension is passed in the pFile parameter, pIOHandler is NULL
*/
*
* The implementation should be as quick as possible. A check for
* the file extension is enough. If no suitable loader is found with
* this strategy, CanRead() is called again, the 'checkSig' parameter
* set to true this time. Now the implementation is expected to
* perform a full check of the file format, possibly searching the
* first bytes of the file for magic identifiers or keywords.
*
* @param pFile Path and file name of the file to be examined.
* @param pIOHandler The IO handler to use for accessing any file.
* @param checkSig Set to true if this method is called a second time.
* This time, the implementation may take more time to examine the
* contents of the file to be loaded for magic bytes, keywords, etc
* to be able to load files with unknown/not existent file extensions.
* @return true if the class can read this file, false if not.
*
* @note Sometimes ASSIMP uses this method to determine whether a
* a given file extension is generally supported. In this case the
* file extension is passed in the pFile parameter, pIOHandler is NULL
*/
virtual bool CanRead( const std::string& pFile,
IOSystem* pIOHandler) const = 0;
IOSystem* pIOHandler, bool checkSig) const = 0;
// -------------------------------------------------------------------
/** Imports the given file and returns the imported data.
* If the import succeeds, ownership of the data is transferred to
* the caller. If the import fails, NULL is returned. The function
* takes care that any partially constructed data is destroyed
* beforehand.
*
* @param pFile Path of the file to be imported.
* @param pIOHandler IO-Handler used to open this and possible other files.
* @return The imported data or NULL if failed. If it failed a
* human-readable error description can be retrieved by calling
* GetErrorText()
*
* @note This function is not intended to be overridden. Implement
* InternReadFile() to do the import. If an exception is thrown somewhere
* in InternReadFile(), this function will catch it and transform it into
* a suitable response to the caller.
*/
* If the import succeeds, ownership of the data is transferred to
* the caller. If the import fails, NULL is returned. The function
* takes care that any partially constructed data is destroyed
* beforehand.
*
* @param pFile Path of the file to be imported.
* @param pIOHandler IO-Handler used to open this and possible other files.
* @return The imported data or NULL if failed. If it failed a
* human-readable error description can be retrieved by calling
* GetErrorText()
*
* @note This function is not intended to be overridden. Implement
* InternReadFile() to do the import. If an exception is thrown somewhere
* in InternReadFile(), this function will catch it and transform it into
* a suitable response to the caller.
*/
aiScene* ReadFile( const std::string& pFile, IOSystem* pIOHandler);
@ -135,19 +151,20 @@ public:
* @return A description of the last error that occured. An empty
* string if there was no error.
*/
inline const std::string& GetErrorText() const
{ return mErrorText; }
const std::string& GetErrorText() const {
return mErrorText;
}
// -------------------------------------------------------------------
/** Called prior to ReadFile().
* The function is a request to the importer to update its configuration
* basing on the Importer's configuration property list.
* @param pImp Importer instance
* @param ppFlags Post-processing steps to be executed on the data
* returned by the loaders. This value is provided to allow some
* internal optimizations.
*/
* The function is a request to the importer to update its configuration
* basing on the Importer's configuration property list.
* @param pImp Importer instance
* @param ppFlags Post-processing steps to be executed on the data
* returned by the loaders. This value is provided to allow some
* internal optimizations.
*/
virtual void SetupProperties(const Importer* pImp /*,
unsigned int ppFlags*/);
@ -157,7 +174,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* Importer implementations should append all file extensions
* which they supported to the passed string.
* Example: "*.blabb;*.quak;*.gug;*.foo" (no comma after the last!)
* Example: "*.blabb;*.quak;*.gug;*.foo" (no delimiter after the last!)
* @param append Output string
*/
virtual void GetExtensionList(std::string& append) = 0;
@ -169,23 +186,36 @@ protected:
* expected to be correct. Override this function to implement the
* actual importing.
* <br>
* The output scene must meet the following requirements:<br>
* - at least a root node must be there<br>
* - aiMesh::mPrimitiveTypes may be 0. The types of primitives
* in the mesh are determined automatically in this case.<br>
* - the vertex data is stored in a pseudo-indexed "verbose" format.
* The output scene must meet the following requirements:<br>
* <ul>
* <li>At least a root node must be there, even if its only purpose
* is to reference one mesh.</li>
* <li>aiMesh::mPrimitiveTypes may be 0. The types of primitives
* in the mesh are determined automatically in this case.</li>
* <li>the vertex data is stored in a pseudo-indexed "verbose" format.
* In fact this means that every vertex that is referenced by
* a face is unique. Or the other way round: a vertex index may
* not occur twice in a single aiMesh.
* - aiAnimation::mDuration may be -1. Assimp determines the length
* not occur twice in a single aiMesh.</li>
* <li>aiAnimation::mDuration may be -1. Assimp determines the length
* of the animation automatically in this case as the length of
* the longest animation channel.
*
* If the AI_SCENE_FLAGS_INCOMPLETE-Flag is not set:<br>
* - at least one mesh must be there<br>
* - at least one material must be there<br>
* - there may be no meshes with 0 vertices or faces<br>
* This won't be checked (except by the validation step), Assimp will
* the longest animation channel.</li>
* <li>aiMesh::mBitangents may be NULL if tangents and normals are
* given. In this case bitangents are computed as the cross product
* between normal and tangent.</li>
* <li>There needn't be a material. If none is there a default material
* is generated. However, it is recommended practice for loaders
* to generate a default material for yourself that matches the
* default material setting for the file format better than Assimp's
* generic default material. Note that default materials *should*
* be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded
* or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy)
* texture. </li>
* </ul>
* If the AI_SCENE_FLAGS_INCOMPLETE-Flag is <b>not</b> set:<ul>
* <li> at least one mesh must be there</li>
* <li> there may be no meshes with 0 vertices or faces</li>
* </ul>
* This won't be checked (except by the validation step): Assimp will
* crash if one of the conditions is not met!
*
* @param pFile Path of the file to be imported.
@ -218,12 +248,53 @@ protected:
unsigned int numTokens,
unsigned int searchBytes = 200);
// -------------------------------------------------------------------
/** @brief Check whether a file has a specific file extension
* @param pFile Input file
* @param ext0 Extension to check for. Lowercase characters only, no dot!
* @param ext1 Optional second extension
* @param ext2 Optional third extension
* @note Case-insensitive
*/
static bool SimpleExtensionCheck (const std::string& pFile,
const char* ext0,
const char* ext1 = NULL,
const char* ext2 = NULL);
// -------------------------------------------------------------------
/** @brief Extract file extension from a string
* @param pFile Input file
* @return Extension without trailing dot, all lowercase
*/
static std::string GetExtension (const std::string& pFile);
// -------------------------------------------------------------------
/** @brief Check whether a file starts with one or more magic tokens
* @param pFile Input file
* @param pIOHandler IO system to be used
* @param magic n magic tokens
* @params num Size of magic
* @param offset Offset from file start where tokens are located
* @param Size of one token, in bytes. Maximally 16 bytes.
* @return true if one of the given tokens was found
*
* @note For convinence, the check is also performed for the
* byte-swapped variant of all tokens (big endian). Only for
* tokens of size 2,4.
*/
static bool CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile,
const void* magic,
unsigned int num,
unsigned int offset = 0,
unsigned int size = 4);
#if 0 /** TODO **/
// -------------------------------------------------------------------
/** An utility for all text file loaders. It converts a file to our
* ASCII/UTF8 character set. Special unicode characters are lost.
*
* @param buffer Input buffer. Needn't be terminated with zero.
* ASCII/UTF8 character set. Special unicode characters are lost.
*
* @param buffer Input buffer. Needn't be terminated with zero.
* @param length Length of the input buffer, in bytes. Receives the
* number of output characters, excluding the terminal char.
* @return true if the source format did not match our internal
@ -266,10 +337,18 @@ public:
Importer::IntPropertyMap ints;
Importer::FloatPropertyMap floats;
Importer::StringPropertyMap strings;
bool operator == (const PropertyMap& prop) const {
return ints == prop.ints && floats == prop.floats && strings == prop.strings;
}
bool empty () const {
return ints.empty() && floats.empty() && strings.empty();
}
};
public:
/** Construct a batch loader from a given IO system
*/
@ -293,8 +372,10 @@ public:
* @param file File to be loaded
* @param steps Steps to be executed on the file
* @param map Optional configuration properties
* @return 'Load request channel' - an unique ID that can later
* be used to access the imported file data.
*/
void AddLoadRequest (const std::string& file,
unsigned int AddLoadRequest (const std::string& file,
unsigned int steps = 0, const PropertyMap* map = NULL);
@ -304,11 +385,11 @@ public:
* If an import is requested several times, this function
* can be called several times, too.
*
* @param file File name of the scene
* @param which LRWC returned by AddLoadRequest().
* @return NULL if there is no scene with this file name
* in the queue of the scene hasn't been loaded yet.
*/
aiScene* GetImport (const std::string& file);
aiScene* GetImport (unsigned int which);
/** Waits until all scenes have been loaded.
@ -321,7 +402,6 @@ private:
BatchData* data;
};
} // end of namespace Assimp
#endif // AI_BASEIMPORTER_H_INC

View File

@ -69,7 +69,7 @@ void BaseProcess::ExecuteOnScene( Importer* pImp)
// catch exceptions thrown inside the PostProcess-Step
try
{
this->Execute(pImp->mScene);
Execute(pImp->mScene);
} catch( ImportErrorException* exception)
{

View File

@ -126,7 +126,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
const float angleEpsilon = 0.9999f;
std::vector<bool> vertexDone( pMesh->mNumVertices, false);
const float qnan = std::numeric_limits<float>::quiet_NaN();
const float qnan = get_qnan();
// create space for the tangents and bitangents
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
@ -149,8 +149,10 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// their tangent vectors are set to qnan.
for (unsigned int i = 0; i < face.mNumIndices;++i)
{
vertexDone[face.mIndices[i]] = true;
meshTang [face.mIndices[i]] = qnan;
register unsigned int idx = face.mIndices[i];
vertexDone [idx] = true;
meshTang [idx] = qnan;
meshBitang [idx] = qnan;
}
continue;
@ -218,10 +220,10 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
vertexFinder = &_vertexFinder;
posEpsilon = ComputePositionEpsilon(pMesh);
}
std::vector<unsigned int> verticesFound;
const float fLimit = cosf(this->configMaxAngle);
std::vector<unsigned int> closeVertices;
// in the second pass we now smooth out all tangents and bitangents at the same local position
// if they are not too far off.
@ -234,12 +236,14 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
const aiVector3D& origNorm = pMesh->mNormals[a];
const aiVector3D& origTang = pMesh->mTangents[a];
const aiVector3D& origBitang = pMesh->mBitangents[a];
std::vector<unsigned int> closeVertices;
closeVertices.push_back( a);
closeVertices.clear();
// find all vertices close to that position
vertexFinder->FindPositions( origPos, posEpsilon, verticesFound);
closeVertices.reserve (verticesFound.size()+5);
closeVertices.push_back( a);
// look among them for other vertices sharing the same normal and a close-enough tangent/bitangent
for( unsigned int b = 0; b < verticesFound.size(); b++)
{

View File

@ -67,22 +67,16 @@ ColladaLoader::~ColladaLoader()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
// check file extension
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);
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
*it = tolower( *it);
if( extension == ".dae")
std::string extension = GetExtension(pFile);
if( extension == "dae")
return true;
// XML - too generic, we need to open the file and search for typical keywords
if( extension == ".xml") {
if( extension == "xml" || !extension.length() || checkSig) {
/* If CanRead() is called in order to check whether we
* support a specific file extension in general pIOHandler
* might be NULL and it's our duty to return true here.
@ -94,6 +88,13 @@ bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler) con
return false;
}
// ------------------------------------------------------------------------------------------------
// Get file extension list
void ColladaLoader::GetExtensionList( std::string& append)
{
append.append("*.dae");
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
@ -166,7 +167,8 @@ aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Colla
aiNode* node = new aiNode();
// now setup the name of the node. We take the name if not empty, otherwise the collada ID
if (!pNode->mName.empty())
// FIX: Workaround for XSI calling the instanced visual scene 'untitled' by default.
if (!pNode->mName.empty() && pNode->mName != "untitled")
node->mName.Set(pNode->mName);
else if (!pNode->mID.empty())
node->mName.Set(pNode->mID);
@ -632,7 +634,7 @@ void ColladaLoader::AddTexture ( Assimp::MaterialHelper& mat, const ColladaParse
_AI_MATKEY_TEXTURE_BASE,type,idx);
// mapping mode
int map = map = aiTextureMapMode_Clamp;
int map = aiTextureMapMode_Clamp;
if (sampler.mWrapU)
map = aiTextureMapMode_Wrap;
if (sampler.mWrapU && sampler.mMirrorU)
@ -896,4 +898,4 @@ void ColladaLoader::ConvertPath (aiString& ss)
}
}
#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER
#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER

View File

@ -90,16 +90,13 @@ protected:
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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList( std::string& append)
{
append.append("*.dae");
}
void GetExtensionList( std::string& append);
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details

View File

@ -1618,8 +1618,8 @@ void ColladaParser::ReadSceneLibrary()
}
else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
{
if( strcmp( mReader->getNodeName(), "library_visual_scenes") != 0)
ThrowException( "Expected end of \"library_visual_scenes\" element.");
if( strcmp( mReader->getNodeName(), "library_visual_scenes") == 0)
//ThrowException( "Expected end of \"library_visual_scenes\" element.");
break;
}
@ -2106,4 +2106,4 @@ Collada::InputType ColladaParser::GetTypeForSemantic( const std::string& pSemant
return IT_Invalid;
}
#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER
#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER

View File

@ -202,7 +202,7 @@ protected:
/** Reads the text contents of an element, returns NULL if not given.
Skips leading whitespace. */
const char* ColladaParser::TestTextContent();
const char* TestTextContent();
/** Reads a single bool from current text content */
bool ReadBoolFromTextContent();

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the DXF importer class */
/** @file DXFLoader.cpp
* @brief Implementation of the DXF importer class
*/
#include "AssimpPCH.h"
#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
@ -77,7 +79,7 @@ static aiColor4D g_aclrDxfIndexColors[] =
#define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0]))
// invalid/unassigned color value
aiColor4D g_clrInvalid = aiColor4D(std::numeric_limits<float>::quiet_NaN(),0.f,0.f,1.f);
aiColor4D g_clrInvalid = aiColor4D(get_qnan(),0.f,0.f,1.f);
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
@ -95,19 +97,15 @@ DXFImporter::~DXFImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool DXFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool DXFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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 SimpleExtensionCheck(pFile,"dxf");
}
return !(extension.length() != 4 || extension[0] != '.' ||
extension[1] != 'd' && extension[1] != 'D' ||
extension[2] != 'x' && extension[2] != 'X' ||
extension[3] != 'f' && extension[3] != 'F');
// ------------------------------------------------------------------------------------------------
void DXFImporter::GetExtensionList(std::string& append)
{
append.append("*.dxf");
}
// ------------------------------------------------------------------------------------------------

View File

@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the .dxf importer class. */
/** @file DXFLoader.h
* @brief Declaration of the .dxf importer class.
*/
#ifndef AI_DXFLOADER_H_INCLUDED
#define AI_DXFLOADER_H_INCLUDED
@ -85,7 +87,8 @@ 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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -93,10 +96,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.dxf");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -51,6 +51,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "StdOStreamLogStream.h"
#include "FileLogStream.h"
#ifndef ASSIMP_BUILD_SINGLETHREADED
# include <boost/thread/thread.hpp>
# include <boost/thread/mutex.hpp>
boost::mutex loggerMutex;
#endif
namespace Assimp {
// ----------------------------------------------------------------------------------
@ -119,6 +126,11 @@ Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/,
unsigned int defStreams /*= DLS_DEBUGGER | DLS_FILE*/,
IOSystem* io /*= NULL*/)
{
// enter the mutex here to avoid concurrency problems
#ifndef ASSIMP_BUILD_SINGLETHREADED
boost::mutex::scoped_lock lock(loggerMutex);
#endif
if (m_pLogger && !isNullLogger() )
delete m_pLogger;
@ -146,31 +158,56 @@ Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/,
// ----------------------------------------------------------------------------------
void Logger::debug(const std::string &message) {
ai_assert(message.length()<=Logger::MAX_LOG_MESSAGE_LENGTH);
// SECURITY FIX: otherwise it's easy to produce overruns ...
if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
ai_assert(false);
return;
}
return OnDebug(message.c_str());
}
// ----------------------------------------------------------------------------------
void Logger::info(const std::string &message) {
ai_assert(message.length()<=Logger::MAX_LOG_MESSAGE_LENGTH);
// SECURITY FIX: otherwise it's easy to produce overruns ...
if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
ai_assert(false);
return;
}
return OnInfo(message.c_str());
}
// ----------------------------------------------------------------------------------
void Logger::warn(const std::string &message) {
ai_assert(message.length()<=Logger::MAX_LOG_MESSAGE_LENGTH);
// SECURITY FIX: otherwise it's easy to produce overruns ...
if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
ai_assert(false);
return;
}
return OnWarn(message.c_str());
}
// ----------------------------------------------------------------------------------
void Logger::error(const std::string &message) {
ai_assert(message.length()<=Logger::MAX_LOG_MESSAGE_LENGTH);
// SECURITY FIX: otherwise it's easy to produce overruns ...
if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
ai_assert(false);
return;
}
return OnError(message.c_str());
}
// ----------------------------------------------------------------------------------
void DefaultLogger::set( Logger *logger )
{
// enter the mutex here to avoid concurrency problems
#ifndef ASSIMP_BUILD_SINGLETHREADED
boost::mutex::scoped_lock lock(loggerMutex);
#endif
if (!logger)logger = &s_pNullLogger;
if (m_pLogger && !isNullLogger() )
delete m_pLogger;
@ -195,6 +232,11 @@ Logger *DefaultLogger::get()
// Kills the only instance
void DefaultLogger::kill()
{
// enter the mutex here to avoid concurrency problems
#ifndef ASSIMP_BUILD_SINGLETHREADED
boost::mutex::scoped_lock lock(loggerMutex);
#endif
if (m_pLogger != &s_pNullLogger)return;
delete m_pLogger;
m_pLogger = &s_pNullLogger;
@ -243,23 +285,14 @@ void DefaultLogger::OnError( const char* message )
WriteToStreams( msg, Logger::ERR );
}
// ----------------------------------------------------------------------------------
// Severity setter
void DefaultLogger::setLogSeverity( LogSeverity log_severity )
{
m_Severity = log_severity;
}
// ----------------------------------------------------------------------------------
// Attachs a new stream
void DefaultLogger::attachStream( LogStream *pStream, unsigned int severity )
bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity )
{
if (!pStream)
return;
return false;
// fix (Aramis)
if (0 == severity)
{
if (0 == severity) {
severity = Logger::INFO | Logger::ERR | Logger::WARN | Logger::DEBUGGING;
}
@ -270,24 +303,23 @@ void DefaultLogger::attachStream( LogStream *pStream, unsigned int severity )
if ( (*it)->m_pStream == pStream )
{
(*it)->m_uiErrorSeverity |= severity;
return;
return true;
}
}
LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream );
m_StreamArray.push_back( pInfo );
return true;
}
// ----------------------------------------------------------------------------------
// Detatch a stream
void DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity )
bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity )
{
if (!pStream)
return;
return false;
// fix (Aramis)
if (0 == severity)
{
if (0 == severity) {
severity = Logger::INFO | Logger::ERR | Logger::WARN | Logger::DEBUGGING;
}
@ -303,15 +335,17 @@ void DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity )
m_StreamArray.erase( it );
break;
}
return true;
}
}
return false;
}
// ----------------------------------------------------------------------------------
// Constructor
DefaultLogger::DefaultLogger(LogSeverity severity)
: m_Severity ( severity )
: Logger ( severity )
, noRepeatMsg (false)
, lastLen( 0 )
{

View File

@ -39,8 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the DeterminePTypeHelperProcess and
* SortByPTypeProcess post-process steps.
/** @file FindDegenerates.cpp
* @brief Implementation of the FindDegenerates post-process step.
*/
#include "AssimpPCH.h"
@ -54,8 +54,8 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FindDegeneratesProcess::FindDegeneratesProcess()
{
}
: configRemoveDegenerates (false)
{}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
@ -71,83 +71,144 @@ bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const
return 0 != (pFlags & aiProcess_FindDegenerates);
}
// ------------------------------------------------------------------------------------------------
// Setup import configuration
void FindDegeneratesProcess::SetupProperties(const Importer* pImp)
{
// Get the current value of AI_CONFIG_PP_FD_REMOVE
configRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FindDegeneratesProcess::Execute( aiScene* pScene)
{
DefaultLogger::get()->debug("FindDegeneratesProcess begin");
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
aiMesh* mesh = pScene->mMeshes[i];
mesh->mPrimitiveTypes = 0;
unsigned int deg = 0;
for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
{
aiFace& face = mesh->mFaces[a];
bool first = true;
// check whether the face contains degenerated entries
for (register unsigned int i = 0; i < face.mNumIndices; ++i)
{
for (register unsigned int a = i+1; a < face.mNumIndices; ++a)
{
if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[a]])
{
// we have found a matching vertex position
// remove the corresponding index from the array
for (unsigned int m = a; m < face.mNumIndices-1; ++m)
{
face.mIndices[m] = face.mIndices[m+1];
}
--a;
--face.mNumIndices;
// NOTE: we set the removed vertex index to an unique value
// to make sure the developer gets notified when his
// application attemps to access this data.
face.mIndices[face.mNumIndices] = 0xdeadbeef;
if(first)
{
++deg;
first = false;
}
}
}
}
// We need to update the primitive flags array of the mesh.
// Unfortunately it is not possible to execute
// FindDegenerates before DeterminePType. The latter does
// nothing if the primitive flags have already been set by
// the loader - our changes would be ignored. Although
// we could use some tricks regarding - i.e setting
// mPrimitiveTypes to 0 in every case - but this is the cleanest
// way and causes no additional dependencies in the pipeline.
switch (face.mNumIndices)
{
case 1u:
mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
break;
case 2u:
mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
break;
case 3u:
mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
break;
default:
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
break;
};
}
if (deg && !DefaultLogger::isNullLogger())
{
char s[64];
ASSIMP_itoa10(s,deg);
DefaultLogger::get()->warn(std::string("Found ") + s + " degenerated primitives");
}
for (unsigned int i = 0; i < pScene->mNumMeshes;++i){
ExecuteOnMesh( pScene->mMeshes[i]);
}
DefaultLogger::get()->debug("FindDegeneratesProcess finished");
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported mesh
void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh)
{
mesh->mPrimitiveTypes = 0;
std::vector<bool> remove_me;
if (configRemoveDegenerates)
remove_me.resize(mesh->mNumFaces,false);
unsigned int deg = 0;
for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
{
aiFace& face = mesh->mFaces[a];
bool first = true;
// check whether the face contains degenerated entries
for (register unsigned int i = 0; i < face.mNumIndices; ++i)
{
for (register unsigned int t = i+1; t < face.mNumIndices; ++t)
{
if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]])
{
// we have found a matching vertex position
// remove the corresponding index from the array
--face.mNumIndices;
for (unsigned int m = t; m < face.mNumIndices; ++m)
{
face.mIndices[m] = face.mIndices[m+1];
}
--t;
// NOTE: we set the removed vertex index to an unique value
// to make sure the developer gets notified when his
// application attemps to access this data.
face.mIndices[face.mNumIndices] = 0xdeadbeef;
if(first)
{
++deg;
first = false;
}
if (configRemoveDegenerates) {
remove_me[a] = true;
goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
}
}
}
}
// We need to update the primitive flags array of the mesh.
// Unfortunately it is not possible to execute
// FindDegenerates before DeterminePType. The latter does
// nothing if the primitive flags have already been set by
// the loader - our changes would be ignored. Although
// we could use some tricks regarding - i.e setting
// mPrimitiveTypes to 0 in every case - but this is the cleanest
// way and causes no additional dependencies in the pipeline.
switch (face.mNumIndices)
{
case 1u:
mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
break;
case 2u:
mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
break;
case 3u:
mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
break;
default:
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
break;
};
evil_jump_outside:
continue;
}
// If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
if (configRemoveDegenerates && deg) {
unsigned int n = 0;
for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
{
aiFace& face_src = mesh->mFaces[a];
if (!remove_me[a]) {
aiFace& face_dest = mesh->mFaces[n++];
// Do a manual copy, keep the index array
face_dest.mNumIndices = face_src.mNumIndices;
face_dest.mIndices = face_src.mIndices;
// clear source
face_src.mNumIndices = 0;
face_src.mIndices = NULL;
}
else {
// Otherwise delete it if we don't need this face
delete[] face_src.mIndices;
face_src.mIndices = NULL;
face_src.mNumIndices = 0;
}
}
// Just leave the rest of the array unreferenced, we don't care for now
mesh->mNumFaces = n;
if (!mesh->mNumFaces) {
// WTF!?
// OK ... for completeness and because I'm not yet tired,
// let's write code that willl hopefully never be called
// (famous last words)
// OK ... bad idea.
throw new ImportErrorException("Mesh is empty after removal of degenerated primitives ... WTF!?");
}
}
if (deg && !DefaultLogger::isNullLogger())
{
char s[64];
ASSIMP_itoa10(s,deg);
DefaultLogger::get()->warn(std::string("Found ") + s + " degenerated primitives");
}
}

View File

@ -66,14 +66,44 @@ protected:
~FindDegeneratesProcess();
public:
// -------------------------------------------------------------------
// Check whether step is active
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
// Execute step on a given scene
void Execute( aiScene* pScene);
// -------------------------------------------------------------------
// Setup import settings
void SetupProperties(const Importer* pImp);
// -------------------------------------------------------------------
// Execute step on a given mesh
void ExecuteOnMesh( aiMesh* mesh);
// -------------------------------------------------------------------
/** @brief Enable the instant removal of degenerated primitives
* @param d hm ... difficult to guess what this means, hu!?
*/
void EnableInstantRemoval(bool d) {
configRemoveDegenerates = d;
}
// -------------------------------------------------------------------
/** @brief Check whether instant removal is currently enabled
* @return ...
*/
bool IsInstantRemoval() const {
return configRemoveDegenerates;
}
private:
//! Configuration option: remove degenerates faces immediately
bool configRemoveDegenerates;
};
}

View File

@ -111,7 +111,7 @@ bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
// allocate an array to hold the output normals
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
const float qnan = std::numeric_limits<float>::quiet_NaN();
const float qnan = get_qnan();
// iterate through all faces and compute per-face normals but store
// them per-vertex.

View File

@ -46,17 +46,15 @@ namespace HMP {
#include "./../include/Compiler/pushpack1.h"
// to make it easier for ourselfes, we test the magic word against both "endianesses"
#define HMP_MAKE(string) ((uint32_t)((string[0] << 24) + (string[1] << 16) + (string[2] << 8) + string[3]))
// to make it easier for us, we test the magic word against both "endianesses"
#define AI_HMP_MAGIC_NUMBER_BE_4 AI_MAKE_MAGIC("HMP4")
#define AI_HMP_MAGIC_NUMBER_LE_4 AI_MAKE_MAGIC("4PMH")
#define AI_HMP_MAGIC_NUMBER_BE_4 HMP_MAKE("HMP4")
#define AI_HMP_MAGIC_NUMBER_LE_4 HMP_MAKE("4PMH")
#define AI_HMP_MAGIC_NUMBER_BE_5 AI_MAKE_MAGIC("HMP5")
#define AI_HMP_MAGIC_NUMBER_LE_5 AI_MAKE_MAGIC("5PMH")
#define AI_HMP_MAGIC_NUMBER_BE_5 HMP_MAKE("HMP5")
#define AI_HMP_MAGIC_NUMBER_LE_5 HMP_MAKE("5PMH")
#define AI_HMP_MAGIC_NUMBER_BE_7 HMP_MAKE("HMP7")
#define AI_HMP_MAGIC_NUMBER_LE_7 HMP_MAKE("7PMH")
#define AI_HMP_MAGIC_NUMBER_BE_7 AI_MAKE_MAGIC("HMP7")
#define AI_HMP_MAGIC_NUMBER_LE_7 AI_MAKE_MAGIC("7PMH")
// ---------------------------------------------------------------------------
/** Data structure for the header of a HMP5 file.

View File

@ -67,25 +67,34 @@ HMPImporter::~HMPImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool HMPImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool HMPImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
{
(void)pIOHandler; //this avoids the compiler warning of unused element
// 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);
const std::string extension = GetExtension(pFile);
if (extension == "hmp" )
return true;
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
*it = tolower( *it);
// if check for extension is not enough, check for the magic tokens
if (!extension.length() || cs) {
uint32_t tokens[3];
tokens[0] = AI_HMP_MAGIC_NUMBER_LE_4;
tokens[1] = AI_HMP_MAGIC_NUMBER_LE_5;
tokens[2] = AI_HMP_MAGIC_NUMBER_LE_7;
return CheckMagicToken(pIOHandler,pFile,tokens,3,0);
}
return false;
}
return extension == ".hmp";
// ------------------------------------------------------------------------------------------------
// Get list of all file extensions that are handled by this loader
void HMPImporter::GetExtensionList(std::string& append)
{
append.append("*.hmp");
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void HMPImporter::InternReadFile( const std::string& pFile,
aiScene* _pScene, IOSystem* _pIOHandler)
aiScene* _pScene, IOSystem* _pIOHandler)
{
pScene = _pScene;
pIOHandler = _pIOHandler;
@ -225,7 +234,8 @@ void HMPImporter::InternReadFile_HMP5( )
}
// generate texture coordinates if necessary
if (pcHeader->numskins)GenerateTextureCoords(width,height);
if (pcHeader->numskins)
GenerateTextureCoords(width,height);
// now build a list of faces
CreateOutputFaceList(width,height);
@ -477,7 +487,7 @@ void HMPImporter::GenerateTextureCoords(
for (unsigned int y = 0; y < height;++y) {
for (unsigned int x = 0; x < width;++x,++uv) {
uv->y = 1.0f-fY*y;
uv->y = fY*y;
uv->x = fX*x;
uv->z = 0.0f;
}

View File

@ -37,9 +37,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
//!
//! @file Declaration of the HMP importer class
//!
/** @file HMPLoader.h
* @brief Declaration of the HMP importer class
*/
#ifndef AI_HMPLOADER_H_INCLUDED
#define AI_HMPLOADER_H_INCLUDED
@ -75,8 +75,10 @@ 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;
* See BaseImporter::CanRead() for details.
*/
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -85,10 +87,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.hmp");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -57,10 +57,10 @@ namespace Assimp {
namespace MDL {
// magic bytes used in Half Life 2 MDL models
#define AI_MDL_MAGIC_NUMBER_BE_HL2a MDL_MAKE("IDST")
#define AI_MDL_MAGIC_NUMBER_LE_HL2a MDL_MAKE("TSDI")
#define AI_MDL_MAGIC_NUMBER_BE_HL2b MDL_MAKE("IDSQ")
#define AI_MDL_MAGIC_NUMBER_LE_HL2b MDL_MAKE("QSDI")
#define AI_MDL_MAGIC_NUMBER_BE_HL2a AI_MAKE_MAGIC("IDST")
#define AI_MDL_MAGIC_NUMBER_LE_HL2a AI_MAKE_MAGIC("TSDI")
#define AI_MDL_MAGIC_NUMBER_BE_HL2b AI_MAKE_MAGIC("IDSQ")
#define AI_MDL_MAGIC_NUMBER_LE_HL2b AI_MAKE_MAGIC("QSDI")
// ---------------------------------------------------------------------------
/** \struct Header_HL2

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the Irr importer class */
/** @file IRRLoader.cpp
* @brief Implementation of the Irr importer class
*/
#include "AssimpPCH.h"
@ -81,26 +83,17 @@ IRRImporter::~IRRImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool IRRImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool IRRImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
/* NOTE: A simple check for the file extension is not enough
* here. Irrmesh and irr are easy, but xml is too generic
* and could be collada, too. So we need to open the file and
* search for typical tokens.
*/
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);
for (std::string::iterator i = extension.begin(); i != extension.end();++i)
*i = ::tolower(*i);
if (extension == ".irr")return true;
else if (extension == ".xml")
const std::string extension = GetExtension(pFile);
if (extension == "irr")return true;
else if (extension == "xml" || checkSig)
{
/* If CanRead() is called in order to check whether we
* support a specific file extension in general pIOHandler
@ -128,11 +121,13 @@ void IRRImporter::SetupProperties(const Importer* pImp)
{
// read the output frame rate of all node animation channels
fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS,100);
if (!fps)
{
if (fps < 10.) {
DefaultLogger::get()->error("IRR: Invalid FPS configuration");
fps = 100;
}
// AI_CONFIG_FAVOUR_SPEED
configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
}
// ------------------------------------------------------------------------------------------------
@ -595,7 +590,7 @@ void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vector<aiNode
// ------------------------------------------------------------------------------------------------
// This function is maybe more generic than we'd need it here
void SetupMapping (MaterialHelper* mat, aiTextureMapping mode, const aiVector3D& axis = aiVector3D(0.f,1.f,0.f))
void SetupMapping (MaterialHelper* mat, aiTextureMapping mode, const aiVector3D& axis = aiVector3D(0.f,0.f,-1.f))
{
// Check whether there are texture properties defined - setup
// the desired texture mapping mode for all of them and ignore
@ -650,7 +645,9 @@ void SetupMapping (MaterialHelper* mat, aiTextureMapping mode, const aiVector3D&
// rebuild the output array
if (p.size() > mat->mNumAllocated) {
delete[] mat->mProperties;
mat->mProperties = new aiMaterialProperty*[p.size()];
mat->mProperties = new aiMaterialProperty*[p.size()*2];
mat->mNumAllocated = p.size()*2;
}
mat->mNumProperties = (unsigned int)p.size();
::memcpy(mat->mProperties,&p[0],sizeof(void*)*mat->mNumProperties);
@ -680,7 +677,7 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
// Get the loaded mesh from the scene and add it to
// the list of all scenes to be attached to the
// graph we're currently building
aiScene* scene = batch.GetImport(root->meshPath);
aiScene* scene = batch.GetImport(root->id);
if (!scene)
{
DefaultLogger::get()->error("IRR: Unable to load external file: "
@ -688,6 +685,8 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
break;
}
attach.push_back(AttachmentInfo(scene,rootOut));
#if 0
meshTrafoAssign = 1;
// If the root node of the scene is animated - and *this* node
@ -721,8 +720,9 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
}
}
}
if (1 == meshTrafoAssign)
scene->mRootNode->mTransformation *= AI_TO_IRR_MATRIX;
#endif
// if (1 == meshTrafoAssign)
// scene->mRootNode->mTransformation = AI_TO_IRR_MATRIX * scene->mRootNode->mTransformation;
// Now combine the material we've loaded for this mesh
@ -748,7 +748,7 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
// NOTE: Each mesh should have exactly one material assigned,
// but we do it in a separate loop if this behaviour changes
// in the future.
// in future.
for (unsigned int i = 0; i < scene->mNumMeshes;++i)
{
// Process material flags
@ -901,8 +901,7 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
rootOut->mNumMeshes = (unsigned int)meshes.size() - oldMeshSize;
rootOut->mMeshes = new unsigned int[rootOut->mNumMeshes];
for (unsigned int a = 0; a < rootOut->mNumMeshes;++a)
{
for (unsigned int a = 0; a < rootOut->mNumMeshes;++a) {
rootOut->mMeshes[a] = oldMeshSize+a;
}
}
@ -913,6 +912,7 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
// Now compute the final local transformation matrix of the
// node from the given translation, rotation and scaling values.
// (the rotation is given in Euler angles, XYZ order)
std::swap((float&)root->rotation.z,(float&)root->rotation.y);
rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation) );
// apply scaling
@ -920,20 +920,20 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
mat.a1 *= root->scaling.x;
mat.b1 *= root->scaling.x;
mat.c1 *= root->scaling.x;
mat.a2 *= root->scaling.y;
mat.b2 *= root->scaling.y;
mat.c2 *= root->scaling.y;
mat.a3 *= root->scaling.z;
mat.b3 *= root->scaling.z;
mat.c3 *= root->scaling.z;
mat.a2 *= root->scaling.z;
mat.b2 *= root->scaling.z;
mat.c2 *= root->scaling.z;
mat.a3 *= root->scaling.y;
mat.b3 *= root->scaling.y;
mat.c3 *= root->scaling.y;
// apply translation
mat.a4 += root->position.x;
mat.b4 += root->position.y;
mat.c4 += root->position.z;
mat.b4 += root->position.z;
mat.c4 += root->position.y;
if (meshTrafoAssign == 2)
mat *= AI_TO_IRR_MATRIX;
//if (meshTrafoAssign == 2)
// mat *= AI_TO_IRR_MATRIX;
// now compute animations for the node
ComputeAnimations(root,rootOut, anims);
@ -1446,7 +1446,7 @@ void IRRImporter::InternReadFile( const std::string& pFile,
}
else
{
batch.AddLoadRequest(prop.value,pp,&map);
curNode->id = batch.AddLoadRequest(prop.value,pp,&map);
curNode->meshPath = prop.value;
}
}
@ -1528,8 +1528,7 @@ void IRRImporter::InternReadFile( const std::string& pFile,
/* Now iterate through all cameras and compute their final (horizontal) FOV
*/
for (std::vector<aiCamera*>::iterator it = cameras.begin(), end = cameras.end();
it != end; ++it)
{
it != end; ++it) {
aiCamera* cam = *it;
if (cam->mAspect) // screen aspect could be missing
{
@ -1625,8 +1624,8 @@ void IRRImporter::InternReadFile( const std::string& pFile,
* attachment points in the scenegraph.
*/
SceneCombiner::MergeScenes(&pScene,tempScene,attach,
AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES |
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES);
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) : 0));
/* If we have no meshes | no materials now set the INCOMPLETE
@ -1639,11 +1638,6 @@ void IRRImporter::InternReadFile( const std::string& pFile,
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
}
// transformation matrix to convert from IRRMESH to ASSIMP coordinates
pScene->mRootNode->mTransformation *= AI_TO_IRR_MATRIX;
/* Finished ... everything destructs automatically and all
* temporary scenes have already been deleted by MergeScenes()
*/

View File

@ -39,8 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file Declaration of the .irrMesh (Irrlight Engine Mesh Format)
importer class.
/** @file IRRLoader.h
* @brief Declaration of the .irrMesh (Irrlight Engine Mesh Format)
* importer class.
*/
#ifndef AI_IRRLOADER_H_INCLUDED
#define AI_IRRLOADER_H_INCLUDED
@ -75,7 +76,8 @@ 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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -203,6 +205,7 @@ private:
// Meshes: path to the mesh to be loaded
std::string meshPath;
unsigned int id;
// Meshes: List of materials to be assigned
// along with their corresponding material flags
@ -297,8 +300,11 @@ private:
private:
/** Configuration option: desired output FPS */
double fps;
/** Configuration option: speed flag was set? */
bool configSpeedFlag;
};
} // end of namespace Assimp

View File

@ -67,26 +67,17 @@ IRRMeshImporter::~IRRMeshImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool IRRMeshImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool IRRMeshImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
/* NOTE: A simple check for the file extension is not enough
* here. Irrmesh and irr are easy, but xml is too generic
* and could be collada, too. So we need to open the file and
* search for typical tokens.
*/
const std::string extension = GetExtension(pFile);
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);
for (std::string::iterator i = extension.begin(); i != extension.end();++i)
*i = ::tolower(*i);
if (extension == ".irrmesh")return true;
else if (extension == ".xml")
if (extension == "irrmesh")return true;
else if (extension == "xml" || checkSig)
{
/* If CanRead() is called to check whether the loader
* supports a specific file extension in general we
@ -99,6 +90,14 @@ bool IRRMeshImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) c
return false;
}
// ------------------------------------------------------------------------------------------------
// Get a list of all file extensions which are handled by this class
void IRRMeshImporter::GetExtensionList(std::string& append)
{
// fixme: consider cleaner handling of xml extension
append.append("*.xml;*.irrmesh");
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void IRRMeshImporter::InternReadFile( const std::string& pFile,

View File

@ -38,8 +38,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the .irrMesh (Irrlight Engine Mesh Format)
importer class. */
/** @file IRRMeshLoader.h
* @brief Declaration of the .irrMesh (Irrlight Engine Mesh Format)
* importer class.
*/
#ifndef AI_IRRMESHLOADER_H_INCLUDED
#define AI_IRRMESHLOADER_H_INCLUDED
@ -72,7 +74,8 @@ 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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -80,16 +83,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
/* NOTE: The file extenxsion .xml is too generic. We'll
* need to open the file in CanRead() and check whether it is
* a real irrlicht file
*/
append.append("*.xml;*.irrmesh");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -294,7 +294,11 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags)
// material type (shader)
if (prop.name == "Type")
{
if (prop.value == "trans_vertex_alpha")
if (prop.value == "solid")
{
// default material ...
}
else if (prop.value == "trans_vertex_alpha")
{
matFlags = AI_IRRMESH_MAT_trans_vertex_alpha;
}

View File

@ -43,6 +43,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "AssimpPCH.h"
/* Uncomment this line to prevent Assimp from catching unknown exceptions.
*
* Note that any Exception except ImportErrorException may lead to
* undefined behaviour -> loaders could remain in an unusable state and
* further imports with the same Importer instance could fail/crash/burn ...
*/
#define ASSIMP_CATCH_GLOBAL_EXCEPTIONS
// =======================================================================================
// Internal headers
// =======================================================================================
@ -135,6 +143,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_BUILD_NO_TERRAGEN_IMPORTER
# include "TerragenLoader.h"
#endif
//#ifndef AI_BUILD_NO_CSM_IMPORTER
//# include "CSMLoader.h"
//#endif
#ifndef AI_BUILD_NO_3D_IMPORTER
# include "UnrealLoader.h"
#endif
#ifndef AI_BUILD_NO_LWS_IMPORTER
# include "LWSLoader.h"
#endif
// =======================================================================================
// PostProcess-Steps
@ -325,6 +347,18 @@ Importer::Importer()
#if (!defined AI_BUILD_NO_TERRAGEN_IMPORTER)
mImporter.push_back( new TerragenImporter());
#endif
//#if (!defined AI_BUILD_NO_CSM_IMPORTER)
// mImporter.push_back( new CSMImporter());
//#endif
#if (!defined AI_BUILD_NO_3D_IMPORTER)
mImporter.push_back( new UnrealImporter());
#endif
#if (!defined AI_BUILD_NO_LWS_IMPORTER)
mImporter.push_back( new LWSImporter());
#endif
// ======================================================================
// Add an instance of each post processing step here in the order
@ -333,10 +367,6 @@ Importer::Importer()
// ======================================================================
mPostProcessingSteps.reserve(25);
#if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS)
mPostProcessingSteps.push_back( new ValidateDSProcess());
#endif
#if (!defined AI_BUILD_NO_REMOVEVC_PROCESS)
mPostProcessingSteps.push_back( new RemoveVCProcess());
#endif
@ -548,10 +578,9 @@ bool Importer::IsDefaultIOHandler()
return mIsDefaultHandler;
}
#ifdef _DEBUG
// ------------------------------------------------------------------------------------------------
// Validate post process step flags
bool ValidateFlags(unsigned int pFlags)
bool _ValidateFlags(unsigned int pFlags)
{
if (pFlags & aiProcess_GenSmoothNormals &&
pFlags & aiProcess_GenNormals)
@ -562,7 +591,6 @@ bool ValidateFlags(unsigned int pFlags)
}
return true;
}
#endif // ! DEBUG
// ------------------------------------------------------------------------------------------------
// Free the current scene
@ -602,12 +630,50 @@ aiScene* Importer::GetOrphanedScene()
return s;
}
// ------------------------------------------------------------------------------------------------
// Validate post-processing flags
bool Importer::ValidateFlags(unsigned int pFlags)
{
// run basic checks for mutually exclusive flags
if(!_ValidateFlags(pFlags))
return false;
// ValidateDS does not anymore occur in the pp list, it plays
// an awesome extra role ...
#ifdef AI_BUILD_NO_VALIDATEDS_PROCESS
if (pFlags & aiProcess_ValidateDataStructure)
return false;
#endif
pFlags &= ~aiProcess_ValidateDataStructure;
// Now iterate through all bits which are set in
// the flags and check whether we find at least
// one pp plugin which handles it.
for (unsigned int mask = 1; mask < (1 << (sizeof(unsigned int)*8-1));mask <<= 1) {
if (pFlags & mask) {
bool have = false;
for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) {
if (mPostProcessingSteps[a]-> IsActive(mask) ) {
have = true;
break;
}
}
if (!have)
return false;
}
}
return true;
}
// ------------------------------------------------------------------------------------------------
// Reads the given file and returns its contents if successful.
const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
{
// Validate the flags
ai_assert(ValidateFlags(pFlags));
// In debug builds, run a basic flag validation
ai_assert(_ValidateFlags(pFlags));
const std::string pFile(_pFile);
@ -616,7 +682,9 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
// that might be thrown by STL containers or by new().
// ImportErrorException's are throw by ourselves and caught elsewhere.
// ======================================================================
#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
try
#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
{
// Check whether this Importer instance has already loaded
// a scene. In this case we need to delete the old one
@ -636,21 +704,35 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
// Find an worker class which can handle the file
BaseImporter* imp = NULL;
for( unsigned int a = 0; a < mImporter.size(); a++)
{
if( mImporter[a]->CanRead( pFile, mIOHandler))
{
for( unsigned int a = 0; a < mImporter.size(); a++) {
if( mImporter[a]->CanRead( pFile, mIOHandler, false)) {
imp = mImporter[a];
break;
}
}
// Put a proper error message if no suitable importer was found
if( !imp)
if (!imp)
{
mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
DefaultLogger::get()->error(mErrorString);
return NULL;
// not so bad yet ... try format auto detection.
std::string::size_type s = pFile.find_last_of('.');
if (s != std::string::npos) {
DefaultLogger::get()->info("File extension now known, trying signature-based detection");
for( unsigned int a = 0; a < mImporter.size(); a++) {
if( mImporter[a]->CanRead( pFile, mIOHandler, true)) {
imp = mImporter[a];
break;
}
}
}
// Put a proper error message if no suitable importer was found
if( !imp)
{
mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
DefaultLogger::get()->error(mErrorString);
return NULL;
}
}
// Dispatch the reading to the worker class for this format
@ -661,7 +743,19 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
// If successful, apply all active post processing steps to the imported data
if( mScene)
{
// FIRST of all - preprocess the scene
#ifndef AI_BUILD_NO_VALIDATEDS_PROCESS
// The ValidateDS process is an exception. It is executed first,
// even before ScenePreprocessor is called.
if (pFlags & aiProcess_ValidateDataStructure)
{
ValidateDSProcess ds;
ds.ExecuteOnScene (this);
if (!mScene)
return NULL;
}
#endif // no validation
// Preprocess the scene
ScenePreprocessor pre(mScene);
pre.ProcessScene();
@ -669,11 +763,11 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
#ifdef _DEBUG
if (bExtraVerbose)
{
#if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS)
#ifndef AI_BUILD_NO_VALIDATEDS_PROCESS
DefaultLogger::get()->error("Extra verbose mode not available, library"
" wasn't build with the ValidateDS-Step");
#endif
#endif // no validation
pFlags |= aiProcess_ValidateDataStructure;
@ -694,14 +788,16 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
#ifndef AI_BUILD_NO_VALIDATEDS_PROCESS
continue;
#endif
#endif // no validation
// if the extra verbose mode is active execute the
// If the extra verbose mode is active execute the
// VaidateDataStructureStep again after each step
if (bExtraVerbose && a)
if (bExtraVerbose)
{
DefaultLogger::get()->debug("Extra verbose: revalidating data structures");
((ValidateDSProcess*)mPostProcessingSteps[0])->ExecuteOnScene (this);
ValidateDSProcess ds;
ds.ExecuteOnScene (this);
if( !mScene)
{
DefaultLogger::get()->error("Extra verbose: failed to revalidate data structures");
@ -712,11 +808,13 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
}
}
// if failed, extract the error string
else if( !mScene)mErrorString = imp->GetErrorText();
else if( !mScene)
mErrorString = imp->GetErrorText();
// clear any data allocated by post-process steps
mPPShared->Clean();
}
#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
catch (std::exception &e)
{
#if (defined _MSC_VER) && (defined _CPPRTTI)
@ -729,6 +827,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
DefaultLogger::get()->error(mErrorString);
delete mScene;mScene = NULL;
}
#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
// either successful or failure - the pointer expresses it anyways
return mScene;
@ -750,7 +849,7 @@ BaseImporter* Importer::FindLoader (const char* _szExtension)
i != mImporter.end();++i)
{
// pass the file extension to the CanRead(..,NULL)-method
if ((*i)->CanRead(szExtension,NULL))return *i;
if ((*i)->CanRead(szExtension,NULL,false))return *i;
}
return NULL;
}
@ -765,8 +864,10 @@ void Importer::GetExtensionList(aiString& szOut)
i = mImporter.begin();
i != mImporter.end();++i,++iNum)
{
// insert a comma as delimiter character
if (0 != iNum)
// Insert a comma as delimiter character
// FIX: to take lazy loader implementations into account, we are
// slightly more tolerant here than we'd need to be.
if (0 != iNum && ';' != *(tmp.end()-1))
tmp.append(";");
(*i)->GetExtensionList(tmp);

View File

@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* <br>
* The algorithm is roughly basing on this paper:
* http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf
* ... TODO: implement overdraw reduction
*/
#include "AssimpPCH.h"
@ -54,16 +55,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
#if _MSC_VER >= 1400
# define sprintf sprintf_s
#endif
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
ImproveCacheLocalityProcess::ImproveCacheLocalityProcess()
{
// nothing to do here
configCacheDepth = 12; // hardcoded to 12 at the moment
ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() {
configCacheDepth = PP_ICL_PTCACHE_SIZE;
}
// ------------------------------------------------------------------------------------------------
@ -80,28 +75,48 @@ bool ImproveCacheLocalityProcess::IsActive( unsigned int pFlags) const
return (pFlags & aiProcess_ImproveCacheLocality) != 0;
}
// ------------------------------------------------------------------------------------------------
// Setup configuration
void ImproveCacheLocalityProcess::SetupProperties(const Importer* pImp)
{
// AI_CONFIG_PP_ICL_PTCACHE_SIZE
configCacheDepth = pImp->GetPropertyInteger(AI_CONFIG_PP_ICL_PTCACHE_SIZE,PP_ICL_PTCACHE_SIZE);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void ImproveCacheLocalityProcess::Execute( aiScene* pScene)
{
if (!pScene->mNumMeshes)
{
if (!pScene->mNumMeshes) {
DefaultLogger::get()->debug("ImproveCacheLocalityProcess skipped; there are no meshes");
return;
}
DefaultLogger::get()->debug("ImproveCacheLocalityProcess begin");
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{
this->ProcessMesh( pScene->mMeshes[a],a);
float out = 0.f;
unsigned int numf = 0, numm = 0;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++){
const float res = ProcessMesh( pScene->mMeshes[a],a);
if (res) {
numf += pScene->mMeshes[a]->mNumFaces;
out += res;
++numm;
}
}
if (!DefaultLogger::isNullLogger()) {
char szBuff[128]; // should be sufficiently large in every case
::sprintf(szBuff,"Cache relevant are %i meshes (%i faces). Average output ACMR is %f",
numm,numf,out/numf);
DefaultLogger::get()->info(szBuff);
DefaultLogger::get()->debug("ImproveCacheLocalityProcess finished. ");
}
DefaultLogger::get()->debug("ImproveCacheLocalityProcess finished. ");
}
// ------------------------------------------------------------------------------------------------
// Improves the cache coherency of a specific mesh
void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum)
float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum)
{
ai_assert(NULL != pMesh);
@ -109,52 +124,50 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
// - there must be vertices and faces (haha)
// - all faces must be triangulated
if (!pMesh->HasFaces() || !pMesh->HasPositions())
return;
return 0.f;
if (pMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE)
{
if (pMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) {
DefaultLogger::get()->error("This algorithm works on triangle meshes only");
return;
return 0.f;
}
// find the input ACMR ...
unsigned int* piFIFOStack = new unsigned int[this->configCacheDepth];
::memset(piFIFOStack,0xff,this->configCacheDepth*sizeof(unsigned int));
unsigned int* piCur = piFIFOStack;
const unsigned int* const piCurEnd = piFIFOStack + this->configCacheDepth;
// count the number of cache misses
unsigned int iCacheMisses = 0;
float fACMR = 3.f;
const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces;
for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace)
// Input ACMR is for logging purposes only
if (!DefaultLogger::isNullLogger())
{
for (unsigned int qq = 0; qq < 3;++qq)
unsigned int* piFIFOStack = new unsigned int[configCacheDepth];
::memset(piFIFOStack,0xff,configCacheDepth*sizeof(unsigned int));
unsigned int* piCur = piFIFOStack;
const unsigned int* const piCurEnd = piFIFOStack + configCacheDepth;
// count the number of cache misses
unsigned int iCacheMisses = 0;
for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace)
{
bool bInCache = false;
for (unsigned int* pp = piFIFOStack;pp < piCurEnd;++pp)
for (unsigned int qq = 0; qq < 3;++qq)
{
if (*pp == pcFace->mIndices[qq])
bool bInCache = false;
for (unsigned int* pp = piFIFOStack;pp < piCurEnd;++pp)
{
// the vertex is in cache
bInCache = true;
break;
if (*pp == pcFace->mIndices[qq]) {
// the vertex is in cache
bInCache = true;
break;
}
}
if (!bInCache)
{
++iCacheMisses;
if (piCurEnd == piCur)piCur = piFIFOStack;
*piCur++ = pcFace->mIndices[qq];
}
}
if (!bInCache)
{
++iCacheMisses;
if (piCurEnd == piCur)piCur = piFIFOStack;
*piCur++ = pcFace->mIndices[qq];
}
}
}
delete[] piFIFOStack;
float fACMR = (float)iCacheMisses / pMesh->mNumFaces;
if (3.0 == fACMR)
{
if (!DefaultLogger::isNullLogger())
{
delete[] piFIFOStack;
fACMR = (float)iCacheMisses / pMesh->mNumFaces;
if (3.0 == fACMR) {
char szBuff[128]; // should be sufficiently large in every case
// the JoinIdenticalVertices process has not been executed on this
@ -162,8 +175,8 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
// smaller than 3.0 ...
::sprintf(szBuff,"Mesh %i: Not suitable for vcache optimization",meshNum);
DefaultLogger::get()->warn(szBuff);
return 0.f;
}
return;
}
// first we need to build a vertex-triangle adjacency list
@ -204,7 +217,7 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
iMaxRefTris = std::max(iMaxRefTris,*piCur);
}
unsigned int* piCandidates = new unsigned int[iMaxRefTris*3];
iCacheMisses = 0;
unsigned int iCacheMisses = 0;
/** PSEUDOCODE for the algorithm
@ -236,7 +249,7 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
int ivdx = 0;
int ics = 1;
int iStampCnt = this->configCacheDepth+1;
int iStampCnt = configCacheDepth+1;
while (ivdx >= 0)
{
unsigned int icnt = piNumTriPtrNoModify[ivdx];
@ -274,7 +287,7 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
*piCSIter++ = dp;
// if the vertex is not yet in cache, set its cache count
if (iStampCnt-piCachingStamps[dp] > this->configCacheDepth)
if (iStampCnt-piCachingStamps[dp] > configCacheDepth)
{
piCachingStamps[dp] = iStampCnt++;
++iCacheMisses;
@ -302,7 +315,7 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
// will the vertex be in cache, even after fanning occurs?
unsigned int tmp;
if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= this->configCacheDepth)
if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= configCacheDepth)
priority = tmp;
// keep best candidate
if (priority > max_priority)
@ -344,18 +357,24 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
}
}
}
float fACMR2 = 0.0f;
if (!DefaultLogger::isNullLogger())
{
char szBuff[128]; // should be sufficiently large in every case
float fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
::sprintf(szBuff,"Mesh %i | ACMR in: %f out: %f | ~%.1f%%",meshNum,fACMR,fACMR2,
((fACMR - fACMR2) / fACMR) * 100.f);
DefaultLogger::get()->info(szBuff);
// very intense verbose logging ... prepare for much text if there are many meshes
if ( DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
char szBuff[128]; // should be sufficiently large in every case
::sprintf(szBuff,"Mesh %i | ACMR in: %f out: %f | ~%.1f%%",meshNum,fACMR,fACMR2,
((fACMR - fACMR2) / fACMR) * 100.f);
DefaultLogger::get()->debug(szBuff);
}
fACMR2 *= pMesh->mNumFaces;
}
// sort the output index buffer back to the input array
piCSIter = piIBOutput;
for (aiFace* pcFace = pMesh->mFaces; pcFace != pcEnd;++pcFace)
{
pcFace->mIndices[0] = *piCSIter++;
@ -368,4 +387,5 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
delete[] piIBOutput;
delete[] piCandidates;
delete[] piNumTriPtrNoModify;
return fACMR2;
}

View File

@ -70,30 +70,28 @@ protected:
~ImproveCacheLocalityProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
// Check whether the pp step is active
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
// Executes the pp step on a given scene
void Execute( aiScene* pScene);
// -------------------------------------------------------------------
// Configures the pp step
void SetupProperties(const Importer* pImp);
protected:
// -------------------------------------------------------------------
/** Executes the postprocessing step on the given mesh
* @param pMesh The mesh to process.
* @param meshNum Index of the mesh to process
*/
void ProcessMesh( aiMesh* pMesh, unsigned int meshNum);
float ProcessMesh( aiMesh* pMesh, unsigned int meshNum);
private:
//! Configuration parameter: specifies the size of the cache to
//! optimize the vertex data for.
unsigned int configCacheDepth;

View File

@ -51,9 +51,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
#if _MSC_VER >= 1400
# define sprintf sprintf_s
#endif
// Data structure to keep a vertex in an interlaced format
struct Vertex
{
aiVector3D mPosition;
aiVector3D mNormal;
aiVector3D mTangent, mBitangent;
aiColor4D mColors [AI_MAX_NUMBER_OF_COLOR_SETS];
aiVector3D mTexCoords [AI_MAX_NUMBER_OF_TEXTURECOORDS];
};
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
@ -75,7 +81,6 @@ bool JoinVerticesProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void JoinVerticesProcess::Execute( aiScene* pScene)
@ -84,18 +89,19 @@ void JoinVerticesProcess::Execute( aiScene* pScene)
// get the total number of vertices BEFORE the step is executed
int iNumOldVertices = 0;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{
iNumOldVertices += pScene->mMeshes[a]->mNumVertices;
if (!DefaultLogger::isNullLogger()) {
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
iNumOldVertices += pScene->mMeshes[a]->mNumVertices;
}
}
// execute the step
int iNumVertices = 0;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{
iNumVertices += this->ProcessMesh( pScene->mMeshes[a],a);
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
iNumVertices += ProcessMesh( pScene->mMeshes[a],a);
}
// if logging is active, print detailled statistics
// if logging is active, print detailed statistics
if (!DefaultLogger::isNullLogger())
{
if (iNumOldVertices == iNumVertices)DefaultLogger::get()->debug("JoinVerticesProcess finished ");
@ -117,81 +123,103 @@ void JoinVerticesProcess::Execute( aiScene* pScene)
// Unites identical vertices in the given mesh
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
{
// helper structure to hold all the data a single vertex can possibly have
typedef struct Vertex vertex;
BOOST_STATIC_ASSERT( AI_MAX_NUMBER_OF_COLOR_SETS == 4);
BOOST_STATIC_ASSERT( AI_MAX_NUMBER_OF_TEXTURECOORDS == 4);
// Return early if we don't have any positions
if (!pMesh->HasPositions() || !pMesh->HasFaces())
return 0;
struct Vertex
{
aiVector3D mPosition;
aiVector3D mNormal;
aiVector3D mTangent, mBitangent;
aiColor4D mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
aiVector3D mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
};
// We'll never have more vertices afterwards.
std::vector<Vertex> uniqueVertices;
uniqueVertices.reserve( pMesh->mNumVertices);
//unsigned int iOldVerts = pMesh->mNumVertices;
// For each vertex the index of the vertex it was replaced by.
std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
// for each vertex whether it was replaced by an existing unique vertex (true) or a new vertex was created for it (false)
std::vector<bool> isVertexUnique( pMesh->mNumVertices, false);
// a little helper to find locally close vertices faster
// A little helper to find locally close vertices faster
// FIX: check whether we can reuse the SpatialSort of a previous step
const float epsilon = 1e-5f;
float posEpsilon;
SpatialSort* vertexFinder = NULL;
const static float epsilon = 1e-5f;
float posEpsilonSqr;
SpatialSort* vertexFinder = NULL;
SpatialSort _vertexFinder;
if (shared)
{
std::vector<std::pair<SpatialSort,float> >* avf;
shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
if (avf)
{
if (avf) {
std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
vertexFinder = &blubb.first;
posEpsilon = blubb.second;
vertexFinder = &blubb.first;
posEpsilonSqr = blubb.second;
}
}
if (!vertexFinder)
{
if (!vertexFinder) {
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
vertexFinder = &_vertexFinder;
posEpsilon = ComputePositionEpsilon(pMesh);
posEpsilonSqr = ComputePositionEpsilon(pMesh);
}
// squared because we check against squared length of the vector difference
const float squareEpsilon = epsilon * epsilon;
std::vector<unsigned int> verticesFound;
static const float squareEpsilon = epsilon * epsilon;
// now check each vertex if it brings something new to the table
// again, better waste some bytes than a realloc ...
std::vector<unsigned int> verticesFound;
verticesFound.reserve(10);
// run an optimized code path if we don't have multiple UVs or vertex colors
const bool complex = (
pMesh->mTextureCoords[1] ||
pMesh->mTextureCoords[2] ||
pMesh->mTextureCoords[3] ||
pMesh->mColors[0] ||
pMesh->mColors[1] ||
pMesh->mColors[2] ||
pMesh->mColors[3] );
// Now check each vertex if it brings something new to the table
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
{
// collect the vertex data
Vertex v;
v.mPosition = pMesh->mVertices[a];
v.mNormal = (pMesh->mNormals != NULL) ? pMesh->mNormals[a] : aiVector3D( 0, 0, 0);
v.mTangent = (pMesh->mTangents != NULL) ? pMesh->mTangents[a] : aiVector3D( 0, 0, 0);
v.mBitangent = (pMesh->mBitangents != NULL) ? pMesh->mBitangents[a] : aiVector3D( 0, 0, 0);
for( unsigned int b = 0; b < AI_MAX_NUMBER_OF_COLOR_SETS; b++)
v.mColors[b] = (pMesh->mColors[b] != NULL) ? pMesh->mColors[b][a] : aiColor4D( 0, 0, 0, 0);
for( unsigned int b = 0; b < AI_MAX_NUMBER_OF_TEXTURECOORDS; b++)
v.mTexCoords[b] = (pMesh->mTextureCoords[b] != NULL) ? pMesh->mTextureCoords[b][a] : aiVector3D( 0, 0, 0);
if (pMesh->mNormals)
v.mNormal = pMesh->mNormals[a];
if (pMesh->mTangents)
v.mTangent = pMesh->mTangents[a];
if (pMesh->mBitangents)
v.mBitangent = pMesh->mBitangents[a];
if (pMesh->mColors[0]) { // manually unrolled here
v.mColors[0] = pMesh->mColors[0][a];
if (pMesh->mColors[1]) {
v.mColors[1] = pMesh->mColors[1][a];
if (pMesh->mColors[2]) {
v.mColors[2] = pMesh->mColors[2][a];
if (pMesh->mColors[3]) {
v.mColors[3] = pMesh->mColors[3][a];
}}}}
if (pMesh->mTextureCoords[0]) { // manually unrolled here
v.mTexCoords[0] = pMesh->mTextureCoords[0][a];
if (pMesh->mTextureCoords[1]) {
v.mTexCoords[1] = pMesh->mTextureCoords[1][a];
if (pMesh->mTextureCoords[2]) {
v.mTexCoords[2] = pMesh->mTextureCoords[2][a];
if (pMesh->mTextureCoords[3]) {
v.mTexCoords[3] = pMesh->mTextureCoords[3][a];
}}}}
// collect all vertices that are close enough to the given position
vertexFinder->FindPositions( v.mPosition, posEpsilon, verticesFound);
vertexFinder->FindPositions( v.mPosition, posEpsilonSqr, verticesFound);
unsigned int matchIndex = 0xffffffff;
// check all unique vertices close to the position if this vertex is already present among them
for( unsigned int b = 0; b < verticesFound.size(); b++)
{
unsigned int vidx = verticesFound[b];
unsigned int uidx = replaceIndex[ vidx];
const unsigned int vidx = verticesFound[b];
const unsigned int uidx = replaceIndex[ vidx];
if( uidx == 0xffffffff || !isVertexUnique[ vidx])
continue;
@ -201,33 +229,38 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// We just test the other attributes even if they're not present in the mesh.
// In this case they're initialized to 0 so the comparision succeeds.
// By this method the non-present attributes are effectively ignored in the comparision.
if( (uv.mNormal - v.mNormal).SquareLength() > squareEpsilon)
continue;
if( (uv.mTangent - v.mTangent).SquareLength() > squareEpsilon)
continue;
if( (uv.mBitangent - v.mBitangent).SquareLength() > squareEpsilon)
continue;
// manually unrolled because continue wouldn't work as desired in an inner loop
BOOST_STATIC_ASSERT(4 == AI_MAX_NUMBER_OF_COLOR_SETS);
if( GetColorDifference( uv.mColors[0], v.mColors[0]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[1], v.mColors[1]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[2], v.mColors[2]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[3], v.mColors[3]) > squareEpsilon)
continue;
// texture coord matching manually unrolled as well
BOOST_STATIC_ASSERT(4 == AI_MAX_NUMBER_OF_TEXTURECOORDS);
if( (uv.mTexCoords[0] - v.mTexCoords[0]).SquareLength() > squareEpsilon)
continue;
if( (uv.mTexCoords[1] - v.mTexCoords[1]).SquareLength() > squareEpsilon)
continue;
if( (uv.mTexCoords[2] - v.mTexCoords[2]).SquareLength() > squareEpsilon)
continue;
if( (uv.mTexCoords[3] - v.mTexCoords[3]).SquareLength() > squareEpsilon)
continue;
// Usually we won't have vertex colors or multiple UVs, so we can skip from here
// Actually this increases runtime performance slightly.
if (complex)
{
// manually unrolled because continue wouldn't work as desired in an inner loop
if( GetColorDifference( uv.mColors[0], v.mColors[0]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[1], v.mColors[1]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[2], v.mColors[2]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[3], v.mColors[3]) > squareEpsilon)
continue;
// texture coord matching manually unrolled as well
if( (uv.mTexCoords[1] - v.mTexCoords[1]).SquareLength() > squareEpsilon)
continue;
if( (uv.mTexCoords[2] - v.mTexCoords[2]).SquareLength() > squareEpsilon)
continue;
if( (uv.mTexCoords[3] - v.mTexCoords[3]).SquareLength() > squareEpsilon)
continue;
}
// we're still here -> this vertex perfectly matches our given vertex
matchIndex = uidx;
@ -250,24 +283,26 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
}
}
if (!DefaultLogger::isNullLogger())
if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE)
{
char szBuff[128]; // should be sufficiently large in every case
sprintf(szBuff,"Mesh %i | Verts in: %i out: %i | ~%.1f%%",
::sprintf(szBuff,"Mesh %i | Verts in: %i out: %i | ~%.1f%%",
meshIndex,
pMesh->mNumVertices,
(int)uniqueVertices.size(),
((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f);
DefaultLogger::get()->info(szBuff);
DefaultLogger::get()->debug(szBuff);
}
// replace vertex data with the unique data sets
pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
// Position
delete [] pMesh->mVertices;
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mVertices[a] = uniqueVertices[a].mPosition;
// Normals, if present
if( pMesh->mNormals)
{
@ -296,7 +331,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
{
if( !pMesh->mColors[a])
continue;
break;
delete [] pMesh->mColors[a];
pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
@ -307,7 +342,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
{
if( !pMesh->mTextureCoords[a])
continue;
break;
delete [] pMesh->mTextureCoords[a];
pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
@ -319,10 +354,8 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
aiFace& face = pMesh->mFaces[a];
for( unsigned int b = 0; b < face.mNumIndices; b++)
{
const size_t index = face.mIndices[b];
face.mIndices[b] = replaceIndex[index];
for( unsigned int b = 0; b < face.mNumIndices; b++) {
face.mIndices[b] = replaceIndex[face.mIndices[b]];
}
}

View File

@ -95,6 +95,8 @@ protected:
* @param meshIndex Index of the mesh to process
*/
int ProcessMesh( aiMesh* pMesh, unsigned int meshIndex);
private:
};
} // end of namespace Assimp

View File

@ -0,0 +1,567 @@
/*
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 LWOAnimation.cpp
* @brief LWOAnimationResolver utility class
*
* It's a very generic implementation of LightWave's system of
* componentwise-animated stuff. The one and only fully free
* implementation of LightWave envelopes of which I know.
*/
#include "AssimpPCH.h"
#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER) && (!defined ASSIMP_BUILD_NO_LWS_IMPORTER)
// internal headers
#include "LWOFileData.h"
using namespace Assimp;
using namespace Assimp::LWO;
// ------------------------------------------------------------------------------------------------
// Construct an animation resolver from a given list of envelopes
AnimResolver::AnimResolver(std::list<Envelope>& _envelopes,double tick)
: envelopes (_envelopes)
, sample_rate (0.)
{
trans_x = trans_y = trans_z = NULL;
rotat_x = rotat_y = rotat_z = NULL;
scale_x = scale_y = scale_z = NULL;
first = last = 150392.;
// find transformation envelopes
for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
(*it).old_first = 0;
(*it).old_last = (*it).keys.size()-1;
if ((*it).keys.empty()) continue;
switch ((*it).type) {
// translation
case LWO::EnvelopeType_Position_X:
trans_x = &*it;break;
case LWO::EnvelopeType_Position_Y:
trans_y = &*it;break;
case LWO::EnvelopeType_Position_Z:
trans_z = &*it;break;
// rotation
case LWO::EnvelopeType_Rotation_Heading:
rotat_x = &*it;break;
case LWO::EnvelopeType_Rotation_Pitch:
rotat_y = &*it;break;
case LWO::EnvelopeType_Rotation_Bank:
rotat_z = &*it;break;
// scaling
case LWO::EnvelopeType_Scaling_X:
scale_x = &*it;break;
case LWO::EnvelopeType_Scaling_Y:
scale_y = &*it;break;
case LWO::EnvelopeType_Scaling_Z:
scale_z = &*it;break;
default:
continue;
};
// convert from seconds to ticks
for (std::vector<LWO::Key>::iterator d = (*it).keys.begin(); d != (*it).keys.end(); ++d)
(*d).time *= tick;
// set default animation range (minimum and maximum time value for which we have a keyframe)
first = std::min(first, (*it).keys.front().time );
last = std::max(last, (*it).keys.back().time );
}
// deferred setup of animation range to increase performance.
// typically the application will want to specify its own.
need_to_setup = true;
}
// ------------------------------------------------------------------------------------------------
// Reset all envelopes to their original contents
void AnimResolver::ClearAnimRangeSetup()
{
for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
(*it).keys.erase((*it).keys.begin(),(*it).keys.begin()+(*it).old_first);
(*it).keys.erase((*it).keys.begin()+(*it).old_last+1,(*it).keys.end());
}
}
// ------------------------------------------------------------------------------------------------
// Insert additional keys to match LWO's pre& post behaviours.
void AnimResolver::UpdateAnimRangeSetup()
{
for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
if ((*it).keys.empty()) continue;
const double my_first = (*it).keys.front().time;
const double my_last = (*it).keys.back().time;
const double delta = my_last-my_first;
const size_t old_size = (*it).keys.size();
const float value_delta = (*it).keys.back().value - (*it).keys.front().value;
// NOTE: We won't handle reset, linear and constant here.
// See DoInterpolation() for their implementation.
// process pre behaviour
switch ((*it).pre) {
case LWO::PrePostBehaviour_OffsetRepeat:
case LWO::PrePostBehaviour_Repeat:
case LWO::PrePostBehaviour_Oscillate:
const double start_time = delta - fmod(my_first-first,delta);
std::vector<LWO::Key>::iterator n = std::find_if((*it).keys.begin(),(*it).keys.end(),
std::bind1st(std::greater<double>(),start_time)),m;
size_t ofs = 0;
if (n != (*it).keys.end()) {
// copy from here - don't use iterators, insert() would invalidate them
ofs = (*it).keys.end()-n;
(*it).keys.insert((*it).keys.begin(),ofs,LWO::Key());
std::copy((*it).keys.end()-ofs,(*it).keys.end(),(*it).keys.begin());
}
// do full copies. again, no iterators
const unsigned int num = (unsigned int)((my_first-first) / delta);
(*it).keys.resize((*it).keys.size() + num*old_size);
n = (*it).keys.begin()+ofs;
bool reverse = false;
for (unsigned int i = 0; i < num; ++i) {
m = n+old_size*(i+1);
std::copy(n,n+old_size,m);
if ((*it).pre == LWO::PrePostBehaviour_Oscillate && (reverse = !reverse))
std::reverse(m,m+old_size-1);
}
// update time values
n = (*it).keys.end() - (old_size+1);
double cur_minus = delta;
unsigned int tt = 1;
for (const double tmp = delta*(num+1);cur_minus <= tmp;cur_minus += delta,++tt) {
m = (delta == tmp ? (*it).keys.begin() : n - (old_size+1));
for (;m != n; --n) {
(*n).time -= cur_minus;
// offset repeat? add delta offset to key value
if ((*it).pre == LWO::PrePostBehaviour_OffsetRepeat) {
(*n).value += tt * value_delta;
}
}
}
break;
}
// process post behaviour
switch ((*it).post) {
case LWO::PrePostBehaviour_OffsetRepeat:
case LWO::PrePostBehaviour_Repeat:
case LWO::PrePostBehaviour_Oscillate:
break;
}
}
}
// ------------------------------------------------------------------------------------------------
// Extract bind pose matrix
void AnimResolver::ExtractBindPose(aiMatrix4x4& out)
{
// If we have no envelopes, return identity
if (envelopes.empty()) {
out = aiMatrix4x4();
return;
}
aiVector3D angles, scaling(1.f,1.f,1.f), translation;
if (trans_x) translation.x = trans_x->keys[0].value;
if (trans_y) translation.y = trans_y->keys[0].value;
if (trans_z) translation.z = trans_z->keys[0].value;
if (rotat_x) angles.x = rotat_x->keys[0].value;
if (rotat_y) angles.y = rotat_y->keys[0].value;
if (rotat_z) angles.z = rotat_z->keys[0].value;
if (scale_x) scaling.x = scale_x->keys[0].value;
if (scale_y) scaling.y = scale_y->keys[0].value;
if (scale_z) scaling.z = scale_z->keys[0].value;
// build the final matrix
aiMatrix4x4 s,r,t;
r.FromEulerAnglesXYZ(angles);
//aiMatrix4x4::RotationY(angles.y,r);
// fixme: make FromEulerAngles static, too
aiMatrix4x4::Translation(translation,t);
aiMatrix4x4::Scaling(scaling,s);
out = s*r*t;
}
// ------------------------------------------------------------------------------------------------
// Do a single interpolation on a channel
void AnimResolver::DoInterpolation(std::vector<LWO::Key>::const_iterator cur,
LWO::Envelope* envl,double time, float& fill)
{
if (envl->keys.size() == 1) {
fill = envl->keys[0].value;
return;
}
// check whether we're at the beginning of the animation track
if (cur == envl->keys.begin()) {
// ok ... this depends on pre behaviour now
// we don't need to handle repeat&offset repeat&oszillate here, see UpdateAnimRangeSetup()
switch (envl->pre)
{
case LWO::PrePostBehaviour_Linear:
DoInterpolation2(cur,cur+1,time,fill);
return;
case LWO::PrePostBehaviour_Reset:
fill = 0.f;
return;
default : //case LWO::PrePostBehaviour_Constant:
fill = (*cur).value;
return;
}
}
// check whether we're at the end of the animation track
else if (cur == envl->keys.end()-1 && time > envl->keys.rbegin()->time) {
// ok ... this depends on post behaviour now
switch (envl->post)
{
case LWO::PrePostBehaviour_Linear:
DoInterpolation2(cur,cur-1,time,fill);
return;
case LWO::PrePostBehaviour_Reset:
fill = 0.f;
return;
default : //case LWO::PrePostBehaviour_Constant:
fill = (*cur).value;
return;
}
}
// Otherwise do a simple interpolation
DoInterpolation2(cur-1,cur,time,fill);
}
// ------------------------------------------------------------------------------------------------
// Almost the same, except we won't handle pre/post conditions here
void AnimResolver::DoInterpolation2(std::vector<LWO::Key>::const_iterator beg,
std::vector<LWO::Key>::const_iterator end,double time, float& fill)
{
switch ((*end).inter) {
case LWO::IT_STEP:
// no interpolation at all - take the value of the last key
fill = (*beg).value;
return;
}
// linear interpolation - default
fill = (*beg).value + ((*end).value - (*beg).value)*(float)(((time - (*beg).time) / ((*end).time - (*beg).time)));
}
// ------------------------------------------------------------------------------------------------
// Subsample animation track by given key values
void AnimResolver::SubsampleAnimTrack(std::vector<aiVectorKey>& out,
double time,double sample_delta)
{
ai_assert(!out.empty() && sample_delta);
const double time_start = out.back().mTime;
// for ()
}
// ------------------------------------------------------------------------------------------------
// Track interpolation
void AnimResolver::InterpolateTrack(std::vector<aiVectorKey>& out,aiVectorKey& fill,double time)
{
// subsample animation track?
if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
SubsampleAnimTrack(out,time, sample_delta);
}
fill.mTime = time;
// get x
if ((*cur_x).time == time) {
fill.mValue.x = (*cur_x).value;
if (cur_x != envl_x->keys.end()-1) /* increment x */
++cur_x;
else end_x = true;
}
else DoInterpolation(cur_x,envl_x,time,(float&)fill.mValue.x);
// get y
if ((*cur_y).time == time) {
fill.mValue.y = (*cur_y).value;
if (cur_y != envl_y->keys.end()-1) /* increment y */
++cur_y;
else end_y = true;
}
else DoInterpolation(cur_y,envl_y,time,(float&)fill.mValue.y);
// get z
if ((*cur_z).time == time) {
fill.mValue.z = (*cur_z).value;
if (cur_z != envl_z->keys.end()-1) /* increment z */
++cur_z;
else end_x = true;
}
else DoInterpolation(cur_z,envl_z,time,(float&)fill.mValue.z);
}
// ------------------------------------------------------------------------------------------------
// Build linearly subsampled keys from three single envelopes, one for each component (x,y,z)
void AnimResolver::GetKeys(std::vector<aiVectorKey>& out,
LWO::Envelope* _envl_x,
LWO::Envelope* _envl_y,
LWO::Envelope* _envl_z,
unsigned int _flags)
{
envl_x = _envl_x;
envl_y = _envl_y;
envl_z = _envl_z;
flags = _flags;
// generate default channels if none are given
LWO::Envelope def_x, def_y, def_z;
LWO::Key key_dummy;
key_dummy.time = 0.f;
if (envl_x && envl_x->type == LWO::EnvelopeType_Scaling_X ||
envl_y && envl_y->type == LWO::EnvelopeType_Scaling_Y ||
envl_z && envl_z->type == LWO::EnvelopeType_Scaling_Z) {
key_dummy.value = 1.f;
}
else key_dummy.value = 0.f;
if (!envl_x) {
envl_x = &def_x;
envl_x->keys.push_back(key_dummy);
}
if (!envl_y) {
envl_y = &def_y;
envl_y->keys.push_back(key_dummy);
}
if (!envl_z) {
envl_z = &def_z;
envl_z->keys.push_back(key_dummy);
}
// guess how many keys we'll get
size_t reserve;
double sr = 1.;
if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
if (!sample_rate)
sr = 100.f;
else sr = sample_rate;
sample_delta = 1.f / sr;
reserve = (size_t)(
std::max( envl_x->keys.end()->time,
std::max( envl_y->keys.end()->time, envl_z->keys.end()->time )) * sr);
}
else reserve = std::max(envl_x->keys.size(),std::max(envl_x->keys.size(),envl_z->keys.size()));
out.reserve(reserve+(reserve>>1));
// Iterate through all three arrays at once - it's tricky, but
// rather interesting to implement.
double lasttime = std::min(envl_x->keys[0].time,std::min(envl_y->keys[0].time,envl_z->keys[0].time));
cur_x = envl_x->keys.begin();
cur_y = envl_y->keys.begin();
cur_z = envl_z->keys.begin();
end_x = end_y = end_z = false;
while (1) {
aiVectorKey fill;
if ((*cur_x).time == (*cur_y).time && (*cur_x).time == (*cur_z).time ) {
// we have a keyframe for all of them defined .. great,
// we don't need to fucking interpolate here ...
fill.mTime = (*cur_x).time;
fill.mValue.x = (*cur_x).value;
fill.mValue.y = (*cur_y).value;
fill.mValue.z = (*cur_z).value;
// subsample animation track
if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
//SubsampleAnimTrack(out,cur_x, cur_y, cur_z, d, sample_delta);
}
if (cur_x != envl_x->keys.end()-1)
++cur_x;
else end_x = true;
if (cur_y != envl_y->keys.end()-1)
++cur_y;
else end_y = true;
if (cur_z != envl_z->keys.end()-1)
++cur_z;
else end_z = true;
}
// Find key with lowest time value
else if ((*cur_x).time <= (*cur_y).time && !end_x) {
if ((*cur_z).time <= (*cur_x).time && !end_z) {
InterpolateTrack(out,fill,(*cur_z).time);
}
else {
InterpolateTrack(out,fill,(*cur_x).time);
}
}
else if ((*cur_z).time <= (*cur_y).time && !end_z) {
InterpolateTrack(out,fill,(*cur_z).time);
}
else if (!end_y) {
// welcome on the server, y
InterpolateTrack(out,fill,(*cur_y).time);
}
else {
// we have reached the end of at least 2 channels,
// only one is remaining. Extrapolate the 2.
if (end_y) {
InterpolateTrack(out,fill,(end_x ? (*cur_z) : (*cur_x)).time);
}
else if (end_x) {
InterpolateTrack(out,fill,(end_z ? (*cur_y) : (*cur_z)).time);
}
else { // if (end_z)
InterpolateTrack(out,fill,(end_y ? (*cur_x) : (*cur_y)).time);
}
}
lasttime = fill.mTime;
out.push_back(fill);
if( end_x && end_y && end_z ) /* finished? */
break;
}
if (flags & AI_LWO_ANIM_FLAG_START_AT_ZERO) {
for (std::vector<aiVectorKey>::iterator it = out.begin(); it != out.end(); ++it)
(*it).mTime -= first;
}
}
// ------------------------------------------------------------------------------------------------
// Extract animation channel
void AnimResolver::ExtractAnimChannel(aiNodeAnim** out, unsigned int flags /*= 0*/)
{
*out = NULL;
// If we have no envelopes, return NULL
if (envelopes.empty()) {
return;
}
// We won't spawn an animation channel if we don't have at least one
// envelope with more than one keyframe defined.
const bool trans = (trans_x && trans_x->keys.size() > 1 || trans_y && trans_y->keys.size() > 1 || trans_z && trans_z->keys.size() > 1);
const bool rotat = (rotat_x && rotat_x->keys.size() > 1 || rotat_y && rotat_y->keys.size() > 1 || rotat_z && rotat_z->keys.size() > 1);
const bool scale = (scale_x && scale_x->keys.size() > 1 || scale_y && scale_y->keys.size() > 1 || scale_z && scale_z->keys.size() > 1);
if (!trans && !rotat && !scale)
return;
// Allocate the output animation
aiNodeAnim* anim = *out = new aiNodeAnim();
// Setup default animation setup if necessary
if (need_to_setup) {
UpdateAnimRangeSetup();
need_to_setup = false;
}
// copy translation keys
if (trans) {
std::vector<aiVectorKey> keys;
GetKeys(keys,trans_x,trans_y,trans_z,flags);
anim->mPositionKeys = new aiVectorKey[ anim->mNumPositionKeys = keys.size() ];
std::copy(keys.begin(),keys.end(),anim->mPositionKeys);
}
// copy rotation keys
if (rotat) {
std::vector<aiVectorKey> keys;
GetKeys(keys,rotat_x,rotat_y,rotat_z,flags);
anim->mRotationKeys = new aiQuatKey[ anim->mNumRotationKeys = keys.size() ];
// convert heading, pitch, bank to quaternion
for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
aiQuatKey& qk = anim->mRotationKeys[i];
qk.mTime = keys[i].mTime;
qk.mValue = aiQuaternion( keys[i].mValue.x ,keys[i].mValue.y ,keys[i].mValue.z );
}
}
// copy scaling keys
if (scale) {
std::vector<aiVectorKey> keys;
GetKeys(keys,scale_x,scale_y,scale_z,flags);
anim->mScalingKeys = new aiVectorKey[ anim->mNumScalingKeys = keys.size() ];
std::copy(keys.begin(),keys.end(),anim->mScalingKeys);
}
}
#endif // no lwo or no lws

336
code/LWOAnimation.h 100644
View File

@ -0,0 +1,336 @@
/*
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 LWOAnimation.h
* @brief LWOAnimationResolver utility class
*
* This is for all lightwave-related file format, not only LWO.
* LWS isthe main purpose.
*/
#ifndef AI_LWO_ANIMATION_INCLUDED
#define AI_LWO_ANIMATION_INCLUDED
namespace Assimp {
namespace LWO {
// ---------------------------------------------------------------------------
/** \brief List of recognized LWO envelopes
*/
enum EnvelopeType
{
EnvelopeType_Position_X = 0x1,
EnvelopeType_Position_Y = 0x2,
EnvelopeType_Position_Z = 0x3,
EnvelopeType_Rotation_Heading = 0x4,
EnvelopeType_Rotation_Pitch = 0x5,
EnvelopeType_Rotation_Bank = 0x6,
EnvelopeType_Scaling_X = 0x7,
EnvelopeType_Scaling_Y = 0x8,
EnvelopeType_Scaling_Z = 0x9,
// -- currently not yet handled
EnvelopeType_Color_R = 0xa,
EnvelopeType_Color_G = 0xb,
EnvelopeType_Color_B = 0xc,
EnvelopeType_Falloff_X = 0xd,
EnvelopeType_Falloff_Y = 0xe,
EnvelopeType_Falloff_Z = 0xf,
EnvelopeType_Unknown
};
// ---------------------------------------------------------------------------
/** \brief List of recognized LWO interpolation modes
*/
enum InterpolationType
{
IT_STEP, IT_LINE, IT_TCB, IT_HERM, IT_BEZI, IT_BEZ2
};
// ---------------------------------------------------------------------------
/** \brief List of recognized LWO pre or post range behaviours
*/
enum PrePostBehaviour
{
PrePostBehaviour_Reset = 0x0,
PrePostBehaviour_Constant = 0x1,
PrePostBehaviour_Repeat = 0x2,
PrePostBehaviour_Oscillate = 0x3,
PrePostBehaviour_OffsetRepeat = 0x4,
PrePostBehaviour_Linear = 0x5
};
// ---------------------------------------------------------------------------
/** \brief Data structure for a LWO animation keyframe
*/
struct Key
{
Key()
: inter (IT_LINE)
{}
//! Current time
double time;
//! Current value
float value;
//! How to interpolate this key with previous key?
InterpolationType inter;
//! Interpolation parameters
float params[5];
// for std::find()
operator double () {
return time;
}
};
// ---------------------------------------------------------------------------
/** \brief Data structure for a LWO animation envelope
*/
struct Envelope
{
Envelope()
: type (EnvelopeType_Unknown)
, pre (PrePostBehaviour_Constant)
, post (PrePostBehaviour_Constant)
, old_first (0)
, old_last (0)
{}
//! Index of this envelope
unsigned int index;
//! Type of envelope
EnvelopeType type;
//! Pre and post-behaviour
PrePostBehaviour pre,post;
//! Keyframes for this envelope
std::vector<Key> keys;
// temporary data for AnimResolver
size_t old_first,old_last;
};
// ---------------------------------------------------------------------------
//! @def AI_LWO_ANIM_FLAG_SAMPLE_ANIMS
//! Flag for AnimResolver, subsamples the input data with the rate specified
//! by AnimResolver::SetSampleRate().
#define AI_LWO_ANIM_FLAG_SAMPLE_ANIMS 0x1
// ---------------------------------------------------------------------------
//! @def AI_LWO_ANIM_FLAG_START_AT_ZERO
//! Flag for AnimResolver, ensures that the animations starts at zero.
#define AI_LWO_ANIM_FLAG_START_AT_ZERO 0x2
// ---------------------------------------------------------------------------
/** @brief Utility class to build Assimp animations from LWO envelopes.
*
* Used for both LWO and LWS (MOT also).
*/
class AnimResolver
{
public:
// ------------------------------------------------------------------
/** @brief Construct an AnimResolver from a given list of envelopes
* @param envelopes Input envelopes. May be empty.
* @param Output tick rate, per second
* @note The input envelopes are possibly modified.
*/
AnimResolver(std::list<Envelope>& envelopes,
double tick);
public:
// ------------------------------------------------------------------
/** @brief Extract the bind-pose transformation matrix.
* @param out Receives bind-pose transformation matrix
*/
void ExtractBindPose(aiMatrix4x4& out);
// ------------------------------------------------------------------
/** @brief Extract a node animation channel
* @param out Receives a pointer to a newly allocated node anim.
* If there's just one keyframe defined, *out is set to NULL and
* no animation channel is computed.
* @param flags Any combination of the AI_LWO_ANIM_FLAG_XXX flags.
*/
void ExtractAnimChannel(aiNodeAnim** out, unsigned int flags = 0);
// ------------------------------------------------------------------
/** @brief Set the sampling rate for ExtractAnimChannel().
*
* Non-linear interpolations are subsampled with this rate (keys
* per second). Closer sampling positions, if existent, are kept.
* The sampling rate defaults to 0, if this value is not changed and
* AI_LWO_ANIM_FLAG_SAMPLE_ANIMS is specified for ExtractAnimChannel(),
* the class finds a suitable sample rate by itself.
*/
void SetSampleRate(double sr) {
sample_rate = sr;
}
// ------------------------------------------------------------------
/** @brief Getter for SetSampleRate()
*/
double GetSampleRate() const {
return sample_rate;
}
// ------------------------------------------------------------------
/** @brief Set the animation time range
*
* @param first Time where the animation starts, in ticks
* @param last Time where the animation ends, in ticks
*/
void SetAnimationRange(double _first, double _last) {
first = _first;
last = _last;
ClearAnimRangeSetup();
UpdateAnimRangeSetup();
}
protected:
// ------------------------------------------------------------------
/** @brief Build linearly subsampled keys from 3 single envelopes
* @param out Receives output keys
* @param envl_x X-component envelope
* @param envl_y Y-component envelope
* @param envl_z Z-component envelope
* @param flags Any combination of the AI_LWO_ANIM_FLAG_XXX flags.
* @note Up to two input envelopes may be NULL
*/
void GetKeys(std::vector<aiVectorKey>& out,
LWO::Envelope* envl_x,
LWO::Envelope* envl_y,
LWO::Envelope* envl_z,
unsigned int flags);
// ------------------------------------------------------------------
/** @brief Resolve a single animation key by applying the right
* interpolation to it.
* @param cur Current key
* @param envl Envelope working on
* @param time time to be interpolated
* @param fill Receives the interpolated output value.
*/
void DoInterpolation(std::vector<LWO::Key>::const_iterator cur,
LWO::Envelope* envl,double time, float& fill);
// ------------------------------------------------------------------
/** @brief Almost the same, except we won't handle pre/post
* conditions here.
* @see DoInterpolation
*/
void DoInterpolation2(std::vector<LWO::Key>::const_iterator beg,
std::vector<LWO::Key>::const_iterator end,double time, float& fill);
// ------------------------------------------------------------------
/** @brief Interpolate 2 tracks if one is given
*
* @param out Receives extra output keys
* @param key_out Primary output key
* @param time Time to interpolate for
*/
void InterpolateTrack(std::vector<aiVectorKey>& out,
aiVectorKey& key_out,double time);
// ------------------------------------------------------------------
/** @brief Subsample an animation track by a given sampling rate
*
* @param out Receives output keys. Last key at input defines the
* time where subsampling starts.
* @param time Time to end subsampling at
* @param sample_delta Time delta between two samples
*/
void SubsampleAnimTrack(std::vector<aiVectorKey>& out,
double time,double sample_delta);
// ------------------------------------------------------------------
/** @brief Delete all keys which we inserted to match anim setup
*/
void ClearAnimRangeSetup();
// ------------------------------------------------------------------
/** @brief Insert extra keys to match LWO's pre and post behaviours
* in a given time range [first...last]
*/
void UpdateAnimRangeSetup();
private:
std::list<Envelope>& envelopes;
double sample_rate;
LWO::Envelope* trans_x, *trans_y, *trans_z;
LWO::Envelope* rotat_x, *rotat_y, *rotat_z;
LWO::Envelope* scale_x, *scale_y, *scale_z;
double first, last;
bool need_to_setup;
// temporary storage
LWO::Envelope* envl_x, * envl_y, * envl_z;
std::vector<LWO::Key>::const_iterator cur_x,cur_y,cur_z;
bool end_x, end_y, end_z;
unsigned int flags;
double sample_delta;
};
} // end namespace LWO
} // end namespace Assimp
#endif // !! AI_LWO_ANIMATION_INCLUDED

View File

@ -178,8 +178,8 @@ void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator& it,
{
surface = -surface;
// there are detail polygons
uint16_t numPolygons = *cursor++;
// there are detail polygons.
const uint16_t numPolygons = *cursor++;
if (cursor < end)CopyFaceIndicesLWOB(it,cursor,end,numPolygons);
}
face.surfaceIndex = surface-1;
@ -194,6 +194,27 @@ LWO::Texture* LWOImporter::SetupNewTextureLWOB(LWO::TextureList& list,unsigned i
std::string type;
GetS0(type,size);
const char* s = type.c_str();
if(strstr(s, "Image Map"))
{
// Determine mapping type
if(strstr(s, "Planar"))
tex->mapMode = LWO::Texture::Planar;
else if(strstr(s, "Cylindrical"))
tex->mapMode = LWO::Texture::Cylindrical;
else if(strstr(s, "Spherical"))
tex->mapMode = LWO::Texture::Spherical;
else if(strstr(s, "Cubic"))
tex->mapMode = LWO::Texture::Cubic;
else if(strstr(s, "Front"))
tex->mapMode = LWO::Texture::FrontProjection;
}
else
{
// procedural or gradient, not supported
DefaultLogger::get()->error("LWOB: Unsupported legacy texture: " + type);
}
return tex;
}
@ -271,7 +292,7 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
case AI_LWO_SMAN:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
surf.mMaximumSmoothAngle = GetF4();
surf.mMaximumSmoothAngle = fabs( GetF4() );
break;
}
// glossiness
@ -323,8 +344,7 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
{
GetS0(pTex->mFileName,head->length);
}
else DefaultLogger::get()->warn("LWOB: TIMG tag was encuntered although "
"there was no xTEX tag before");
else DefaultLogger::get()->warn("LWOB: Unexpected TIMG chunk");
break;
}
// texture strength
@ -332,8 +352,28 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TVAL,1);
if (pTex)pTex->mStrength = (float)GetU1()/ 255.f;
else DefaultLogger::get()->warn("LWOB: TVAL tag was encuntered "
"although there was no xTEX tag before");
else DefaultLogger::get()->warn("LWOB: Unexpected TVAL chunk");
break;
}
// texture flags
case AI_LWO_TFLG:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TFLG,2);
if (pTex)
{
const uint16_t s = GetU2();
if (s & 1)
pTex->majorAxis = LWO::Texture::AXIS_X;
else if (s & 2)
pTex->majorAxis = LWO::Texture::AXIS_Y;
else if (s & 4)
pTex->majorAxis = LWO::Texture::AXIS_Z;
if (s & 16)
DefaultLogger::get()->warn("LWOB: Ignoring \'negate\' flag on texture");
}
else DefaultLogger::get()->warn("LWOB: Unexpected TFLG chunk");
break;
}
}

View File

@ -38,10 +38,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Defines chunk constants used by the LWO file format
/** @file LWOFileData.h
* @brief Defines chunk constants used by the LWO file format
The chunks are taken from the official LightWave SDK headers.
Original copyright notice: "Ernie Wright 17 Sep 00"
*/
#ifndef AI_LWO_FILEDATA_INCLUDED
@ -56,6 +56,7 @@ Original copyright notice: "Ernie Wright 17 Sep 00"
// internal headers
#include "IFF.h"
#include "LWOAnimation.h"
namespace Assimp {
namespace LWO {
@ -254,29 +255,45 @@ namespace LWO {
*/
struct Face : public aiFace
{
//! Default construction
Face()
: surfaceIndex (0)
, smoothGroup (0)
, type (AI_LWO_FACE)
{}
Face(const Face& f)
{
//! Construction from given type
Face(uint32_t _type)
: surfaceIndex (0)
, smoothGroup (0)
, type (_type)
{}
//! Copy construction
Face(const Face& f) {
*this = f;
}
//! Zero-based index into tags chunk
unsigned int surfaceIndex;
//! Smooth group this face is assigned to
unsigned int smoothGroup;
Face& operator=(const LWO::Face& f)
{
//! Type of face
uint32_t type;
//! Assignment operator
Face& operator=(const LWO::Face& f) {
aiFace::operator =(f);
surfaceIndex = f.surfaceIndex;
smoothGroup = f.smoothGroup;
type = f.type;
return *this;
}
};
// ---------------------------------------------------------------------------
/** \brief Base structure for all vertex map representations
*/
@ -473,8 +490,9 @@ struct Clip
} type;
Clip()
: type (UNSUPPORTED)
, idx (0)
: type (UNSUPPORTED)
, idx (0)
, negate (false)
{}
//! path to the base texture -
@ -485,6 +503,9 @@ struct Clip
//! index of the clip
unsigned int idx;
//! Negate the clip?
bool negate;
};
@ -529,6 +550,7 @@ struct Surface
, mIOR (1.f) // vakuum
, mBumpIntensity (1.f)
, mWireframe (false)
, mAdditiveTransparency (10e10f)
{}
//! Name of the surface
@ -571,6 +593,9 @@ struct Surface
//! Wireframe flag
bool mWireframe;
//! Intensity of additive blending
float mAdditiveTransparency;
};
// ---------------------------------------------------------------------------
@ -592,7 +617,7 @@ typedef std::vector < WeightChannel > WeightChannelList;
typedef std::vector < VColorChannel > VColorChannelList;
typedef std::vector < UVChannel > UVChannelList;
typedef std::vector < Clip > ClipList;
typedef std::vector < Envelope > EnvelopeList;
// ---------------------------------------------------------------------------
/** \brief Represents a layer in the file

View File

@ -68,21 +68,21 @@ LWOImporter::~LWOImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool LWOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool LWOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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);
const std::string extension = GetExtension(pFile);
if (extension == "lwo" || extension == "lxo")
return true;
if (extension.length() < 4)return false;
if (extension[0] != '.')return false;
return ! (extension[1] != 'l' && extension[1] != 'L' ||
extension[2] != 'w' && extension[2] != 'W' &&
extension[2] != 'x' && extension[2] != 'X' ||
extension[3] != 'o' && extension[3] != 'O');
// if check for extension is not enough, check for the magic tokens
if (!extension.length() || checkSig) {
uint32_t tokens[3];
tokens[0] = AI_LWO_FOURCC_LWOB;
tokens[1] = AI_LWO_FOURCC_LWO2;
tokens[2] = AI_LWO_FOURCC_LXOB;
return CheckMagicToken(pIOHandler,pFile,tokens,3,8);
}
return false;
}
// ------------------------------------------------------------------------------------------------
@ -109,21 +109,23 @@ void LWOImporter::InternReadFile( const std::string& pFile,
if((this->fileSize = (unsigned int)file->FileSize()) < 12)
throw new ImportErrorException("LWO: The file is too small to contain the IFF header");
// 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
std::vector< uint8_t > mBuffer(fileSize);
file->Read( &mBuffer[0], 1, fileSize);
this->pScene = pScene;
// determine the type of the file
// Determine the type of the file
uint32_t fileType;
const char* sz = IFF::ReadHeader(&mBuffer[0],fileType);
if (sz)throw new ImportErrorException(sz);
mFileBuffer = &mBuffer[0] + 12;
fileSize -= 12;
hasNamedLayer = false;
// create temporary storage on the stack but store pointers to it in the class
// Initialize some members with their default values
hasNamedLayer = false;
// Create temporary storage on the stack but store pointers to it in the class
// instance. Therefore everything will be destructed properly if an exception
// is thrown and we needn't take care of that.
LayerList _mLayers;
@ -218,6 +220,11 @@ void LWOImporter::InternReadFile( const std::string& pFile,
for (FaceList::iterator it = layer.mFaces.begin(), end = layer.mFaces.end();
it != end;++it,++i)
{
// Check whether we support this face's type
if ((*it).type != AI_LWO_FACE && (*it).type != AI_LWO_PTCH) {
continue;
}
unsigned int idx = (*it).surfaceIndex;
if (idx >= mTags->size())
{
@ -282,7 +289,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
if (0xffffffff == vUVChannelIndices[mui])break;
pvUV[mui] = mesh->mTextureCoords[mui] = new aiVector3D[mesh->mNumVertices];
// LightWave doesn't support more than 2 UV components
// LightWave doesn't support more than 2 UV components (?)
// so we can directly setup this value
mesh->mNumUVComponents[0] = 2;
}
@ -318,6 +325,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
register unsigned int idx = face.mIndices[q];
*pv = layer.mTempPoints[idx] + layer.mPivot;
pv->z *= -1.0f; // DX to OGL
//std::swap(pv->z,pv->y);
pv++;
// process UV coordinates
@ -585,7 +593,8 @@ void LWOImporter::GenerateNodeGraph(std::vector<aiNode*>& apcNodes)
pScene->mRootNode->mChildren = apcNewNodes;
pScene->mRootNode->mNumChildren = newSize;
}
if (!pScene->mRootNode->mNumChildren)throw new ImportErrorException("LWO: Unable to build a valid node graph");
if (!pScene->mRootNode->mNumChildren)
throw new ImportErrorException("LWO: Unable to build a valid node graph");
// remove a single root node
// TODO: implement directly in the above loop, no need to deallocate here
@ -717,31 +726,44 @@ void LWOImporter::LoadLWOPoints(unsigned int length)
void LWOImporter::LoadLWO2Polygons(unsigned int length)
{
LE_NCONST uint16_t* const end = (LE_NCONST uint16_t*)(mFileBuffer+length);
uint32_t type = GetU4();
const uint32_t type = GetU4();
// Determine the type of the polygons
switch (type)
{
// read unsupported stuff too (although we wont process it)
case AI_LWO_BONE:
DefaultLogger::get()->warn("LWO2: Encountered unsupported primitive chunk (BONE)");
break;
case AI_LWO_MBAL:
DefaultLogger::get()->warn("LWO2: Encountered unsupported primitive chunk (METABALL)");
break;
case AI_LWO_CURV:
DefaultLogger::get()->warn("LWO2: Encountered unsupported primitive chunk (SPLINE)");;
break;
// These are ok with no restrictions
case AI_LWO_PTCH:
case AI_LWO_FACE:
break;
default:
DefaultLogger::get()->warn("LWO2: Unsupported polygon type (PTCH and FACE are supported)");
// hm!? wtf is this? ok ...
DefaultLogger::get()->error("LWO2: Encountered unknown polygon type");
break;
}
// first find out how many faces and vertices we'll finally need
uint16_t* cursor = (uint16_t*)mFileBuffer;
uint16_t* cursor= (uint16_t*)mFileBuffer;
unsigned int iNumFaces = 0,iNumVertices = 0;
CountVertsAndFacesLWO2(iNumVertices,iNumFaces,cursor,end);
// allocate the output array and copy face indices
if (iNumFaces)
{
if (iNumFaces) {
cursor = (uint16_t*)mFileBuffer;
mCurLayer->mFaces.resize(iNumFaces);
mCurLayer->mFaces.resize(iNumFaces,LWO::Face(type));
FaceList::iterator it = mCurLayer->mFaces.begin();
CopyFaceIndicesLWO2(it,cursor,end);
}
@ -898,6 +920,7 @@ inline void AddToSingleLinkedList(ReferrerList& refList, unsigned int srcIdx, un
}
// ------------------------------------------------------------------------------------------------
// Load LWO2 vertex map
void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
{
LE_NCONST uint8_t* const end = mFileBuffer+length;
@ -915,24 +938,24 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
switch (type)
{
case AI_LWO_TXUV:
if (dims != 2)
{
if (dims != 2) {
DefaultLogger::get()->warn("LWO2: Found UV channel with != 2 components");
return;
}
base = FindEntry(mCurLayer->mUVChannels,name,perPoly);
break;
case AI_LWO_WGHT:
if (dims != 1)
{
if (dims != 1) {
DefaultLogger::get()->warn("LWO2: found vertex weight map with != 1 components");
return;
}
base = FindEntry(mCurLayer->mWeightChannels,name,perPoly);
break;
case AI_LWO_RGB:
case AI_LWO_RGBA:
if (dims != 3 && dims != 4)
{
if (dims != 3 && dims != 4) {
DefaultLogger::get()->warn("LWO2: found vertex color map with != 3&4 components");
return;
}
base = FindEntry(mCurLayer->mVColorChannels,name,perPoly);
break;
@ -1028,6 +1051,7 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
}
// ------------------------------------------------------------------------------------------------
// Load LWO2 clip
void LWOImporter::LoadLWO2Clip(unsigned int length)
{
AI_LWO_VALIDATE_CHUNK_LENGTH(length,CLIP,10);
@ -1042,6 +1066,7 @@ void LWOImporter::LoadLWO2Clip(unsigned int length)
switch (head->type)
{
case AI_LWO_STIL:
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,STIL,1);
// "Normal" texture
GetS0(clip.path,head->length);
@ -1049,7 +1074,7 @@ void LWOImporter::LoadLWO2Clip(unsigned int length)
break;
case AI_LWO_ISEQ:
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ISEQ,16);
// Image sequence. We'll later take the first.
{
uint8_t digits = GetU1(); mFileBuffer++;
@ -1078,18 +1103,124 @@ void LWOImporter::LoadLWO2Clip(unsigned int length)
break;
case AI_LWO_XREF:
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,XREF,4);
// Just a cross-reference to another CLIp
clip.type = Clip::REF;
clip.clipRef = GetU4();
break;
case AI_LWO_NEGA:
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,NEGA,2);
clip.negate = (0 != GetU2());
break;
default:
DefaultLogger::get()->warn("LWO2: Encountered unknown CLIP subchunk");
}
}
// ------------------------------------------------------------------------------------------------
// Load envelope description
void LWOImporter::LoadLWO2Envelope(unsigned int length)
{
LE_NCONST uint8_t* const end = mFileBuffer + length;
AI_LWO_VALIDATE_CHUNK_LENGTH(length,ENVL,4);
mEnvelopes.push_back(LWO::Envelope());
LWO::Envelope& envelope = mEnvelopes.back();
// Get the index of the envelope
envelope.index = ReadVSizedIntLWO2(mFileBuffer);
// ... and read all subchunks
while (true)
{
if (mFileBuffer + 6 >= end)break;
LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
if (mFileBuffer + head->length > end)
throw new ImportErrorException("LWO2: Invalid envelope chunk length");
uint8_t* const next = mFileBuffer+head->length;
switch (head->type)
{
// Type & representation of the envelope
case AI_LWO_TYPE:
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TYPE,2);
mFileBuffer++; // skip user format
// Determine type of envelope
envelope.type = (LWO::EnvelopeType)*mFileBuffer;
++mFileBuffer;
break;
// precondition
case AI_LWO_PRE:
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,PRE,2);
envelope.pre = (LWO::PrePostBehaviour)GetU2();
break;
// postcondition
case AI_LWO_POST:
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,POST,2);
envelope.post = (LWO::PrePostBehaviour)GetU2();
break;
// keyframe
case AI_LWO_KEY:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,KEY,8);
envelope.keys.push_back(LWO::Key());
LWO::Key& key = envelope.keys.back();
key.time = GetF4();
key.value = GetF4();
break;
}
// interval interpolation
case AI_LWO_SPAN:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPAN,4);
if (envelope.keys.size()<2)
DefaultLogger::get()->warn("LWO2: Unexpected SPAN chunk");
else {
LWO::Key& key = envelope.keys.back();
switch (GetU4())
{
case AI_LWO_STEP:
key.inter = LWO::IT_STEP;break;
case AI_LWO_LINE:
key.inter = LWO::IT_LINE;break;
case AI_LWO_TCB:
key.inter = LWO::IT_TCB;break;
case AI_LWO_HERM:
key.inter = LWO::IT_HERM;break;
case AI_LWO_BEZI:
key.inter = LWO::IT_BEZI;break;
case AI_LWO_BEZ2:
key.inter = LWO::IT_BEZ2;break;
default:
DefaultLogger::get()->warn("LWO2: Unknown interval interpolation mode");
};
// todo ... read params
}
break;
}
default:
DefaultLogger::get()->warn("LWO2: Encountered unknown ENVL subchunk");
}
// regardless how much we did actually read, go to the next chunk
mFileBuffer = next;
}
}
// ------------------------------------------------------------------------------------------------
// Load file - master function
void LWOImporter::LoadLWO2File()
{
bool skip = false;
@ -1121,8 +1252,7 @@ void LWOImporter::LoadLWO2File()
// load this layer or ignore it? Check the layer index property
// NOTE: The first layer is the default layer, so the layer
// index is one-based now
if (0xffffffff != configLayerIndex && configLayerIndex != mLayers->size())
{
if (0xffffffff != configLayerIndex && configLayerIndex != mLayers->size()-1) {
skip = true;
}
else skip = false;
@ -1138,16 +1268,14 @@ void LWOImporter::LoadLWO2File()
GetS0(layer.mName,head->length-16);
// if the name is empty, generate a default name
if (layer.mName.empty())
{
if (layer.mName.empty()) {
char buffer[128]; // should be sufficiently large
::sprintf(buffer,"Layer_%i", iUnnamed++);
layer.mName = buffer;
}
// load this layer or ignore it? Check the layer name property
if (configLayerName.length() && configLayerName != layer.mName)
{
if (configLayerName.length() && configLayerName != layer.mName) {
skip = true;
}
else hasNamedLayer = true;
@ -1161,7 +1289,8 @@ void LWOImporter::LoadLWO2File()
// vertex list
case AI_LWO_PNTS:
{
if (skip)break;
if (skip)
break;
unsigned int old = (unsigned int)mCurLayer->mTempPoints.size();
LoadLWOPoints(head->length);
@ -1178,7 +1307,8 @@ void LWOImporter::LoadLWO2File()
// --- intentionally no break here
case AI_LWO_VMAP:
{
if (skip)break;
if (skip)
break;
if (mCurLayer->mTempPoints.empty())
DefaultLogger::get()->warn("LWO2: Unexpected VMAP chunk");
@ -1188,7 +1318,8 @@ void LWOImporter::LoadLWO2File()
// face list
case AI_LWO_POLS:
{
if (skip)break;
if (skip)
break;
unsigned int old = (unsigned int)mCurLayer->mFaces.size();
LoadLWO2Polygons(head->length);
@ -1198,7 +1329,8 @@ void LWOImporter::LoadLWO2File()
// polygon tags
case AI_LWO_PTAG:
{
if (skip)break;
if (skip)
break;
if (mCurLayer->mFaces.empty())
DefaultLogger::get()->warn("LWO2: Unexpected PTAG");
@ -1227,9 +1359,16 @@ void LWOImporter::LoadLWO2File()
LoadLWO2Clip(head->length);
break;
}
// envelope chunk
case AI_LWO_ENVL:
{
LoadLWO2Envelope(head->length);
break;
}
}
mFileBuffer = next;
}
}
#endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER
#endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER

View File

@ -62,7 +62,7 @@ using namespace LWO;
* Methods named "xxxLWOB[xxx]" are used with the older LWOB format.
* Methods named "xxxLWO[xxx]" are used with both formats.
* Methods named "xxx" are used to preprocess the loaded data -
* they aren't specific to one format version, either
* they aren't specific to one format version
*/
// ---------------------------------------------------------------------------
class LWOImporter : public BaseImporter
@ -81,8 +81,10 @@ 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;
* See BaseImporter::CanRead() for details.
*/
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
// -------------------------------------------------------------------
@ -211,6 +213,11 @@ private:
*/
void LoadLWO2Clip(unsigned int length);
// -------------------------------------------------------------------
/** Load an envelope from an EVL chunk
* @param length Size of the chunk
*/
void LoadLWO2Envelope(unsigned int length);
// -------------------------------------------------------------------
/** Count vertices and faces in a LWOB/LWO2 file
@ -372,6 +379,9 @@ protected:
/** Temporary clip list from the file */
ClipList mClips;
/** Temporary envelope list from the file */
EnvelopeList mEnvelopes;
/** file buffer */
uint8_t* mFileBuffer;
@ -381,9 +391,16 @@ protected:
/** Output scene */
aiScene* pScene;
/** Configuration option: speed flag set? */
bool configSpeedFlag;
/** Configuration option: index of layer to be loaded */
unsigned int configLayerIndex;
/** Configuration option: name of layer to be loaded */
std::string configLayerName;
/** True if we have a named layer */
bool hasNamedLayer;
};

View File

@ -183,13 +183,30 @@ bool LWOImporter::HandleTextures(MaterialHelper* pcMat, const TextureList& in, a
if (mClips.end() == clip) {
DefaultLogger::get()->error("LWO2: Clip index is out of bounds");
temp = 0;
// fixme: appearently some LWO files shipping with Doom3 don't
// have clips at all ... check whether that's true or whether
// it's a bug in the loader.
s.Set("$texture.png");
//continue;
}
if (Clip::UNSUPPORTED == (*clip).type) {
DefaultLogger::get()->error("LWO2: Clip type is not supported");
continue;
else {
if (Clip::UNSUPPORTED == (*clip).type) {
DefaultLogger::get()->error("LWO2: Clip type is not supported");
continue;
}
AdjustTexturePath((*clip).path);
s.Set((*clip).path);
// Additional image settings
int flags = 0;
if ((*clip).negate) {
flags |= aiTextureFlags_Invert;
}
pcMat->AddProperty(&flags,1,AI_MATKEY_TEXFLAGS(type,cur));
}
AdjustTexturePath((*clip).path);
s.Set((*clip).path);
}
else
{
@ -232,6 +249,7 @@ bool LWOImporter::HandleTextures(MaterialHelper* pcMat, const TextureList& in, a
DefaultLogger::get()->warn("LWO2: Unsupported texture blend mode: alpha or displacement");
}
// Setup texture operation
pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_TEXOP(type,cur));
// setup the mapping mode
@ -258,12 +276,12 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat
st.Set(surf.mName);
pcMat->AddProperty(&st,AI_MATKEY_NAME);
int i = surf.bDoubleSided ? 1 : 0;
pcMat->AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
const int i = surf.bDoubleSided ? 1 : 0;
pcMat->AddProperty(&i,1,AI_MATKEY_TWOSIDED);
// add the refraction index and the bump intensity
pcMat->AddProperty<float>(&surf.mIOR,1,AI_MATKEY_REFRACTI);
pcMat->AddProperty<float>(&surf.mBumpIntensity,1,AI_MATKEY_BUMPSCALING);
pcMat->AddProperty(&surf.mIOR,1,AI_MATKEY_REFRACTI);
pcMat->AddProperty(&surf.mBumpIntensity,1,AI_MATKEY_BUMPSCALING);
aiShadingMode m;
if (surf.mSpecularValue && surf.mGlossiness)
@ -281,16 +299,16 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat
else fGloss = 80.0f;
}
pcMat->AddProperty<float>(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
pcMat->AddProperty<float>(&fGloss,1,AI_MATKEY_SHININESS);
pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
pcMat->AddProperty(&fGloss,1,AI_MATKEY_SHININESS);
m = aiShadingMode_Phong;
}
else m = aiShadingMode_Gouraud;
// specular color
aiColor3D clr = lerp( aiColor3D(1.f,1.f,1.f), surf.mColor, surf.mColorHighlights );
pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_SPECULAR);
pcMat->AddProperty<float>(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
pcMat->AddProperty(&clr,1,AI_MATKEY_COLOR_SPECULAR);
pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
// emissive color
// (luminosity is not really the same but it affects the surface in
@ -298,12 +316,21 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat
clr.g = clr.b = clr.r = surf.mLuminosity*0.8f;
pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
// opacity
if (10e10f != surf.mTransparency)
// opacity ... either additive or default-blended, please
if (10e10f != surf.mAdditiveTransparency)
{
float f = 1.0f-surf.mTransparency;
pcMat->AddProperty<float>(&f,1,AI_MATKEY_OPACITY);
const int add = aiBlendMode_Additive;
pcMat->AddProperty(&surf.mAdditiveTransparency,1,AI_MATKEY_OPACITY);
pcMat->AddProperty(&add,1,AI_MATKEY_BLEND_FUNC);
}
else if (10e10f != surf.mTransparency)
{
const int def = aiBlendMode_Default;
const float f = 1.0f-surf.mTransparency;
pcMat->AddProperty(&f,1,AI_MATKEY_OPACITY);
pcMat->AddProperty(&def,1,AI_MATKEY_BLEND_FUNC);
}
// ADD TEXTURES to the material
// TODO: find out how we can handle COLOR textures correctly...
@ -315,25 +342,21 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat
HandleTextures(pcMat,surf.mOpacityTextures,aiTextureType_OPACITY);
HandleTextures(pcMat,surf.mReflectionTextures,aiTextureType_REFLECTION);
// now we need to know which shader we must use
// Now we need to know which shader we must use
// iterate through the shader list of the surface and
// search for a name we know
// search for a name which we know ...
for (ShaderList::const_iterator it = surf.mShaders.begin(), end = surf.mShaders.end();
it != end;++it)
{
//if (!(*it).enabled)continue;
if ((*it).functionName == "LW_SuperCelShader" ||
(*it).functionName == "AH_CelShader")
{
if ((*it).functionName == "LW_SuperCelShader" || (*it).functionName == "AH_CelShader") {
DefaultLogger::get()->info("LWO2: Mapping LW_SuperCelShader/AH_CelShader "
"to aiShadingMode_Toon");
m = aiShadingMode_Toon;
break;
}
else if ((*it).functionName == "LW_RealFresnel" ||
(*it).functionName == "LW_FastFresnel")
{
else if ((*it).functionName == "LW_RealFresnel" || (*it).functionName == "LW_FastFresnel") {
DefaultLogger::get()->info("LWO2: Mapping LW_RealFresnel/LW_FastFresnel "
"to aiShadingMode_Fresnel");
@ -345,12 +368,13 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat
DefaultLogger::get()->warn("LWO2: Unknown surface shader: " + (*it).functionName);
}
}
if (surf.mMaximumSmoothAngle <= 0.0f)m = aiShadingMode_Flat;
if (surf.mMaximumSmoothAngle <= 0.0f)
m = aiShadingMode_Flat;
pcMat->AddProperty((int*)&m,1,AI_MATKEY_SHADING_MODEL);
// (the diffuse value is just a scaling factor)
// If a diffuse texture is set, we set this value to 1.0
clr = (b ? aiColor3D(1.f,1.f,1.f) : surf.mColor);
clr = (b && false ? aiColor3D(1.f,1.f,1.f) : surf.mColor);
clr.r *= surf.mDiffuseValue;
clr.g *= surf.mDiffuseValue;
clr.b *= surf.mDiffuseValue;
@ -365,9 +389,7 @@ void LWOImporter::FindUVChannels(LWO::TextureList& list, LWO::Layer& layer,
it != end;++it)
{
// Ignore textures with non-UV mappings for the moment.
if (!(*it).enabled || !(*it).bCanUse || 0xffffffff != (*it).mRealUVIndex ||
(*it).mapMode != LWO::Texture::UV)
{
if (!(*it).enabled || !(*it).bCanUse || 0xffffffff != (*it).mRealUVIndex || (*it).mapMode != LWO::Texture::UV) {
continue;
}
for (unsigned int i = 0; i < layer.mUVChannels.size();++i)
@ -379,6 +401,7 @@ void LWOImporter::FindUVChannels(LWO::TextureList& list, LWO::Layer& layer,
{
if (i == out[m]) {
(*it).mRealUVIndex = m;
break;
}
}
if (0xffffffff == (*it).mRealUVIndex)
@ -710,7 +733,9 @@ void LWOImporter::LoadLWO2Surface(unsigned int size)
// transparency
case AI_LWO_TRAN:
{
if (surf.mTransparency == 10e10f)break;
// transparency explicitly disabled?
if (surf.mTransparency == 10e10f)
break;
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TRAN,4);
surf.mTransparency = GetF4();
@ -741,6 +766,13 @@ void LWOImporter::LoadLWO2Surface(unsigned int size)
}
break;
}
// additive transparency
case AI_LWO_ADTR:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ADTR,4);
surf.mAdditiveTransparency = GetF4();
break;
}
// wireframe mode
case AI_LWO_LINE:
{
@ -788,7 +820,7 @@ void LWOImporter::LoadLWO2Surface(unsigned int size)
case AI_LWO_SMAN:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
surf.mMaximumSmoothAngle = GetF4();
surf.mMaximumSmoothAngle = fabs( GetF4() );
break;
}
// vertex color channel to be applied to the surface

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the LWS importer class */
/** @file LWSLoader.cpp
* @brief Implementation of the LWS importer class
*/
#include "AssimpPCH.h"
@ -48,9 +50,60 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "fast_atof.h"
#include "SceneCombiner.h"
#include "GenericProperty.h"
#include "SkeletonMeshBuilder.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Recursive parsing of LWS files
void LWS::Element::Parse (const char*& buffer)
{
for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) {
// begin of a new element with children
bool sub = false;
if (*buffer == '{') {
++buffer;
SkipSpaces(&buffer);
sub = true;
}
else if (*buffer == '}')
return;
children.push_back(Element());
// copy data line - read token per token
const char* cur = buffer;
while (!IsSpaceOrNewLine(*buffer)) ++buffer;
children.back().tokens[0] = std::string(cur,(size_t) (buffer-cur));
SkipSpaces(&buffer);
if (children.back().tokens[0] == "Plugin")
{
DefaultLogger::get()->debug("LWS: Skipping over plugin-specific data");
// strange stuff inside Plugin/Endplugin blocks. Needn't
// follow LWS syntax, so we skip over it
for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) {
if (!::strncmp(buffer,"EndPlugin",9)) {
//SkipLine(&buffer);
break;
}
}
continue;
}
cur = buffer;
while (!IsLineEnd(*buffer)) ++buffer;
children.back().tokens[1] = std::string(cur,(size_t) (buffer-cur));
// parse more elements recursively
if (sub)
children.back().Parse(buffer);
}
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
@ -68,30 +121,755 @@ LWSImporter::~LWSImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool LWSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool LWSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler,bool checkSig) const
{
std::string::size_type pos = pFile.find_last_of('.');
const std::string extension = GetExtension(pFile);
if (extension == "lws" || extension == "mot")
return true;
// no file extension - can't read
if( pos == std::string::npos)
return false;
std::string extension = pFile.substr( pos);
for (std::string::iterator i = extension.begin(); i != extension.end();++i)
*i = ::tolower(*i);
return extension == ".lws";
// if check for extension is not enough, check for the magic tokens LWSC and LWMO
if (!extension.length() || checkSig) {
uint32_t tokens[2];
tokens[0] = AI_MAKE_MAGIC("LWSC");
tokens[1] = AI_MAKE_MAGIC("LWMO");
return CheckMagicToken(pIOHandler,pFile,tokens,2);
}
return false;
}
// ------------------------------------------------------------------------------------------------
// Get list of file extensions
void LWSImporter::GetExtensionList(std::string& append)
{
append.append("*.lws");
append.append("*.lws;*.mot");
}
// ------------------------------------------------------------------------------------------------
// Setup configuration properties
void LWSImporter::SetupProperties(const Importer* pImp)
{
// AI_CONFIG_FAVOUR_SPEED
configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
// AI_CONFIG_IMPORT_LWS_ANIM_START
first = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_START,
150392 /* magic hack */);
// AI_CONFIG_IMPORT_LWS_ANIM_END
last = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_END,
150392 /* magic hack */);
if (last < first) {
std::swap(last,first);
}
}
// ------------------------------------------------------------------------------------------------
// Read an envelope description
void LWSImporter::ReadEnvelope(const LWS::Element& dad, LWO::Envelope& fill )
{
if (dad.children.empty()) {
DefaultLogger::get()->error("LWS: Envelope descriptions must not be empty");
return;
}
// reserve enough storage
std::list< LWS::Element >::const_iterator it = dad.children.begin();;
fill.keys.reserve(strtol10(it->tokens[1].c_str()));
for (++it; it != dad.children.end(); ++it) {
const char* c = (*it).tokens[1].c_str();
if ((*it).tokens[0] == "Key") {
fill.keys.push_back(LWO::Key());
LWO::Key& key = fill.keys.back();
float f;
SkipSpaces(&c);
c = fast_atof_move(c,key.value);
SkipSpaces(&c);
c = fast_atof_move(c,f);
key.time = f;
unsigned int span = strtol10(c,&c), num = 0;
switch (span) {
case 0:
key.inter = LWO::IT_TCB;
num = 5;
break;
case 1:
case 2:
key.inter = LWO::IT_HERM;
num = 5;
break;
case 3:
key.inter = LWO::IT_LINE;
num = 0;
break;
case 4:
key.inter = LWO::IT_STEP;
num = 0;
break;
case 5:
key.inter = LWO::IT_BEZ2;
num = 4;
break;
default:
DefaultLogger::get()->error("LWS: Unknown span type");
}
for (unsigned int i = 0; i < num;++i) {
SkipSpaces(&c);
c = fast_atof_move(c,key.params[i]);
}
}
else if ((*it).tokens[0] == "Behaviors") {
SkipSpaces(&c);
fill.pre = (LWO::PrePostBehaviour) strtol10(c,&c);
SkipSpaces(&c);
fill.post = (LWO::PrePostBehaviour) strtol10(c,&c);
}
}
}
// ------------------------------------------------------------------------------------------------
// Read animation channels in the old LightWave animation format
void LWSImporter::ReadEnvelope_Old(
std::list< LWS::Element >::const_iterator& it,
const std::list< LWS::Element >::const_iterator& end,
LWS::NodeDesc& nodes,
unsigned int version)
{
unsigned int num,sub_num;
if (++it == end)goto unexpected_end;
num = strtol10((*it).tokens[0].c_str());
for (unsigned int i = 0; i < num; ++i) {
nodes.channels.push_back(LWO::Envelope());
LWO::Envelope& envl = nodes.channels.back();
envl.index = i;
envl.type = (LWO::EnvelopeType)(i+1);
if (++it == end)goto unexpected_end;
sub_num = strtol10((*it).tokens[0].c_str());
for (unsigned int n = 0; n < sub_num;++n) {
if (++it == end)goto unexpected_end;
// parse value and time, skip the rest for the moment.
LWO::Key key;
const char* c = fast_atof_move((*it).tokens[0].c_str(),key.value);
SkipSpaces(&c);
float f;
fast_atof_move((*it).tokens[0].c_str(),f);
key.time = f;
envl.keys.push_back(key);
}
}
return;
unexpected_end:
DefaultLogger::get()->error("LWS: Encountered unexpected end of file while parsing object motion");
}
// ------------------------------------------------------------------------------------------------
// Setup a nice name for a node
void LWSImporter::SetupNodeName(aiNode* nd, LWS::NodeDesc& src)
{
const unsigned int combined = src.number | ((unsigned int)src.type) << 28u;
// the name depends on the type. We break LWS's strange naming convention
// and return human-readable, but still machine-parsable and unique, strings.
if (src.type == LWS::NodeDesc::OBJECT) {
if (src.path.length()) {
std::string::size_type s = src.path.find_last_of("\\/");
if (s == std::string::npos)
s = 0;
else ++s;
nd->mName.length = ::sprintf(nd->mName.data,"%s_(%08X)",src.path.substr(s).c_str(),combined);
return;
}
}
nd->mName.length = ::sprintf(nd->mName.data,"%s_(%08X)",src.name,combined);
}
// ------------------------------------------------------------------------------------------------
// Recursively build the scenegraph
void LWSImporter::BuildGraph(aiNode* nd, LWS::NodeDesc& src, std::vector<AttachmentInfo>& attach,
BatchLoader& batch,
aiCamera**& camOut,
aiLight**& lightOut,
std::vector<aiNodeAnim*>& animOut)
{
// Setup a very cryptic name for the node, we want the user to be happy
SetupNodeName(nd,src);
// If this is an object from an external file - get the scene
// and setup proper attachment tags
if (src.type == LWS::NodeDesc::OBJECT && src.path.length() ) {
aiScene* obj = batch.GetImport(src.id);
if (!obj) {
DefaultLogger::get()->error("LWS: Failed to read external file " + src.path);
}
else {
attach.push_back(AttachmentInfo(obj,nd));
}
}
// If object is a light source - setup a corresponding ai structure
else if (src.type == LWS::NodeDesc::LIGHT) {
aiLight* lit = *lightOut++ = new aiLight();
// compute final light color
lit->mColorDiffuse = lit->mColorSpecular = src.lightColor*src.lightIntensity;
// name to attach light to node -> unique due to LWs indexing system
lit->mName = nd->mName;
// detemine light type and setup additional members
if (src.lightType == 2) { /* spot light */
lit->mType = aiLightSource_SPOT;
lit->mAngleInnerCone = (float)AI_DEG_TO_RAD( src.lightConeAngle );
lit->mAngleOuterCone = lit->mAngleInnerCone+(float)AI_DEG_TO_RAD( src.lightEdgeAngle );
}
else if (src.lightType == 1) { /* directional light source */
lit->mType = aiLightSource_DIRECTIONAL;
}
else lit->mType = aiLightSource_POINT;
// fixme: no proper handling of light falloffs yet
if (src.lightFalloffType == 1)
lit->mAttenuationConstant = 1.f;
else if (src.lightFalloffType == 1)
lit->mAttenuationLinear = 1.f;
else
lit->mAttenuationQuadratic = 1.f;
}
// If object is a camera - setup a corresponding ai structure
else if (src.type == LWS::NodeDesc::CAMERA) {
aiCamera* cam = *camOut++ = new aiCamera();
// name to attach cam to node -> unique due to LWs indexing system
cam->mName = nd->mName;
}
// Get the node transformation from the LWO key
LWO::AnimResolver resolver(src.channels,fps);
resolver.ExtractBindPose(nd->mTransformation);
// .. and construct animation channels
aiNodeAnim* anim = NULL;
#if 0 /* not yet */
if (first != last) {
resolver.SetAnimationRange(first,last);
resolver.ExtractAnimChannel(&anim,AI_LWO_ANIM_FLAG_SAMPLE_ANIMS|AI_LWO_ANIM_FLAG_START_AT_ZERO);
if (anim) {
anim->mNodeName = nd->mName;
animOut.push_back(anim);
}
}
#endif
// process pivot point, if any
if (src.pivotPos != aiVector3D()) {
aiMatrix4x4 tmp;
aiMatrix4x4::Translation(-src.pivotPos,tmp);
if (anim) {
// We have an animation channel for this node. Problem: to combine the pivot
// point with the node anims, we'd need to interpolate *all* keys, get
// transformation matrices from them, apply the translation and decompose
// the resulting matrices again in order to reconstruct the keys. This
// solution here is *much* easier ... we're just inserting an extra node
// in the hierarchy.
// Maybe the final optimization here will be done during postprocessing.
aiNode* pivot = new aiNode();
pivot->mName.Set("$Pivot");
pivot->mTransformation = tmp;
pivot->mChildren = new aiNode*[pivot->mNumChildren = 1];
pivot->mChildren[0] = nd;
pivot->mParent = nd->mParent;
nd->mParent = pivot;
// swap children ad hope the parents wont see a huge difference
pivot->mParent->mChildren[pivot->mParent->mNumChildren-1] = pivot;
}
else {
nd->mTransformation = tmp * nd->mTransformation;
}
}
// Add children
if (src.children.size()) {
nd->mChildren = new aiNode*[src.children.size()];
for (std::list<LWS::NodeDesc*>::iterator it = src.children.begin(); it != src.children.end(); ++it) {
aiNode* ndd = nd->mChildren[nd->mNumChildren++] = new aiNode();
ndd->mParent = nd;
BuildGraph(ndd,**it,attach,batch,camOut,lightOut,animOut);
}
}
}
// ------------------------------------------------------------------------------------------------
// Determine the exact location of a LWO file
std::string LWSImporter::FindLWOFile(const std::string& in)
{
// insert missing directory seperator if necessary
std::string tmp;
if (in.length() > 3 && in[1] == ':'&& in[2] != '\\' && in[2] != '/')
{
tmp = in[0] + ":\\" + in.substr(2);
}
else tmp = in;
if (io->Exists(tmp))
return in;
// file is not accessible for us ... maybe it's packed by
// LightWave's 'Package Scene' command?
// Relevant for us are the following two directories:
// <folder>\Objects\<hh>\<*>.lwo
// <folder>\Scenes\<hh>\<*>.lws
// where <hh> is optional.
std::string test = ".." + io->getOsSeparator() + tmp;
if (io->Exists(test))
return test;
test = ".." + io->getOsSeparator() + test;
if (io->Exists(test))
return test;
// return original path, maybe the IOsystem knows better
return tmp;
}
// ------------------------------------------------------------------------------------------------
// Read file into given scene data structure
void LWSImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
IOSystem* pIOHandler)
{
return;
io = pIOHandler;
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
// Check whether we can read from the file
if( file.get() == NULL)
throw new ImportErrorException( "Failed to open LWS file " + pFile + ".");
// Allocate storage and copy the contents of the file to a memory buffer
const size_t fileSize = file->FileSize();
std::vector< char > mBuffer(fileSize);
file->Read( &mBuffer[0], 1, fileSize);
// Parse the file structure
LWS::Element root; const char* dummy = &mBuffer[0];
root.Parse(dummy);
// Construct a Batchimporter to read more files recursively
BatchLoader batch(pIOHandler);
batch.SetBasePath(pFile);
// Construct an array to receive the flat output graph
std::list<LWS::NodeDesc> nodes;
unsigned int cur_light = 0, cur_camera = 0, cur_object = 0;
unsigned int num_light = 0, num_camera = 0, num_object = 0;
// check magic identifier, 'LWSC'
bool motion_file = false;
std::list< LWS::Element >::const_iterator it = root.children.begin();
if ((*it).tokens[0] == "LWMO")
motion_file = true;
if ((*it).tokens[0] != "LWSC" && !motion_file)
throw new ImportErrorException("LWS: Not a LightWave scene, magic tag LWSC not found");
// get file format version and print to log
++it;
unsigned int version = strtol10((*it).tokens[0].c_str());
DefaultLogger::get()->info("LWS file format version is " + (*it).tokens[0]);
first = 0.;
last = 60.;
fps = 25.; /* seems to be a good default frame rate */
// Now read all elements in a very straghtforward manner
for (; it != root.children.end(); ++it) {
const char* c = (*it).tokens[1].c_str();
// 'FirstFrame': begin of animation slice
if ((*it).tokens[0] == "FirstFrame") {
if (150392. != first /* see SetupProperties() */)
first = strtol10(c,&c)-1.; /* we're zero-based */
}
// 'LastFrame': end of animation slice
else if ((*it).tokens[0] == "LastFrame") {
if (150392. != last /* see SetupProperties() */)
last = strtol10(c,&c)-1.; /* we're zero-based */
}
// 'FramesPerSecond': frames per second
else if ((*it).tokens[0] == "FramesPerSecond") {
fps = strtol10(c,&c);
}
// 'LoadObjectLayer': load a layer of a specific LWO file
else if ((*it).tokens[0] == "LoadObjectLayer") {
// get layer index
const int layer = strtol10(c,&c);
// setup the layer to be loaded
BatchLoader::PropertyMap props;
SetGenericProperty(props.ints,AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,layer);
// add node to list
LWS::NodeDesc d;
d.type = LWS::NodeDesc::OBJECT;
if (version >= 4) { // handle LWSC 4 explicit ID
SkipSpaces(&c);
d.number = strtol16(c,&c) & AI_LWS_MASK;
}
else d.number = cur_object++;
// and add the file to the import list
SkipSpaces(&c);
std::string path = FindLWOFile( c );
d.path = path;
d.id = batch.AddLoadRequest(path,0,&props);
nodes.push_back(d);
num_object++;
}
// 'LoadObject': load a LWO file into the scenegraph
else if ((*it).tokens[0] == "LoadObject") {
// add node to list
LWS::NodeDesc d;
d.type = LWS::NodeDesc::OBJECT;
if (version >= 4) { // handle LWSC 4 explicit ID
d.number = strtol16(c,&c) & AI_LWS_MASK;
SkipSpaces(&c);
}
else d.number = cur_object++;
std::string path = FindLWOFile( c );
d.id = batch.AddLoadRequest(path,0,NULL);
d.path = path;
nodes.push_back(d);
num_object++;
}
// 'AddNullObject': add a dummy node to the hierarchy
else if ((*it).tokens[0] == "AddNullObject") {
// add node to list
LWS::NodeDesc d;
d.type = LWS::NodeDesc::OBJECT;
d.name = c;
if (version >= 4) { // handle LWSC 4 explicit ID
d.number = strtol16(c,&c) & AI_LWS_MASK;
}
else d.number = cur_object++;
nodes.push_back(d);
num_object++;
}
// 'NumChannels': Number of envelope channels assigned to last layer
else if ((*it).tokens[0] == "NumChannels") {
// ignore for now
}
// 'Channel': preceedes any envelope description
else if ((*it).tokens[0] == "Channel") {
if (nodes.empty()) {
if (motion_file) {
// LightWave motion file. Add dummy node
LWS::NodeDesc d;
d.type = LWS::NodeDesc::OBJECT;
d.name = c;
d.number = cur_object++;
nodes.push_back(d);
}
else DefaultLogger::get()->error("LWS: Unexpected keyword: \'Channel\'");
}
// important: index of channel
nodes.back().channels.push_back(LWO::Envelope());
LWO::Envelope& env = nodes.back().channels.back();
env.index = strtol10(c);
// currently we can just interpret the standard channels 0...9
// (hack) assume that index-i yields the binary channel type from LWO
env.type = (LWO::EnvelopeType)(env.index+1);
}
// 'Envelope': a single animation channel
else if ((*it).tokens[0] == "Envelope") {
if (nodes.empty() || nodes.back().channels.empty())
DefaultLogger::get()->error("LWS: Unexpected keyword: \'Envelope\'");
else {
ReadEnvelope((*it),nodes.back().channels.back());
}
}
// 'ObjectMotion': animation information for older lightwave formats
else if (version < 3 && ((*it).tokens[0] == "ObjectMotion" ||
(*it).tokens[0] == "CameraMotion" ||
(*it).tokens[0] == "LightMotion")) {
if (nodes.empty())
DefaultLogger::get()->error("LWS: Unexpected keyword: \'<Light|Object|Camera>Motion\'");
else {
ReadEnvelope_Old(it,root.children.end(),nodes.back(),version);
}
}
// 'Pre/PostBehavior': pre/post animation behaviour for LWSC 2
else if (version == 2 && (*it).tokens[0] == "Pre/PostBehavior") {
if (nodes.empty())
DefaultLogger::get()->error("LWS: Unexpected keyword: \'Pre/PostBehavior'");
else {
for (std::list<LWO::Envelope>::iterator it = nodes.back().channels.begin(); it != nodes.back().channels.end(); ++it) {
// two ints per envelope
LWO::Envelope& env = *it;
env.pre = (LWO::PrePostBehaviour) strtol10(c,&c); SkipSpaces(&c);
env.post = (LWO::PrePostBehaviour) strtol10(c,&c); SkipSpaces(&c);
}
}
}
// 'ParentItem': specifies the parent of the current element
else if ((*it).tokens[0] == "ParentItem") {
if (nodes.empty())
DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentItem\'");
else nodes.back().parent = strtol16(c,&c);
}
// 'ParentObject': deprecated one for older formats
else if (version < 3 && (*it).tokens[0] == "ParentObject") {
if (nodes.empty())
DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentObject\'");
else {
nodes.back().parent = strtol10(c,&c) | (1u << 28u);
}
}
// 'AddCamera': add a camera to the scenegraph
else if ((*it).tokens[0] == "AddCamera") {
// add node to list
LWS::NodeDesc d;
d.type = LWS::NodeDesc::CAMERA;
if (version >= 4) { // handle LWSC 4 explicit ID
d.number = strtol16(c,&c) & AI_LWS_MASK;
}
else d.number = cur_camera++;
nodes.push_back(d);
num_camera++;
}
// 'CameraName': set name of currently active camera
else if ((*it).tokens[0] == "CameraName") {
if (nodes.empty() || nodes.back().type != LWS::NodeDesc::CAMERA)
DefaultLogger::get()->error("LWS: Unexpected keyword: \'CameraName\'");
else nodes.back().name = c;
}
// 'AddLight': add a light to the scenegraph
else if ((*it).tokens[0] == "AddLight") {
// add node to list
LWS::NodeDesc d;
d.type = LWS::NodeDesc::LIGHT;
if (version >= 4) { // handle LWSC 4 explicit ID
d.number = strtol16(c,&c) & AI_LWS_MASK;
}
else d.number = cur_light++;
nodes.push_back(d);
num_light++;
}
// 'LightName': set name of currently active light
else if ((*it).tokens[0] == "LightName") {
if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightName\'");
else nodes.back().name = c;
}
// 'LightIntensity': set intensity of currently active light
else if ((*it).tokens[0] == "LightIntensity" || (*it).tokens[0] == "LgtIntensity" ) {
if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightIntensity\'");
else fast_atof_move(c, nodes.back().lightIntensity );
}
// 'LightType': set type of currently active light
else if ((*it).tokens[0] == "LightType") {
if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightType\'");
else nodes.back().lightType = strtol10(c);
}
// 'LightFalloffType': set falloff type of currently active light
else if ((*it).tokens[0] == "LightFalloffType") {
if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightFalloffType\'");
else nodes.back().lightFalloffType = strtol10(c);
}
// 'LightConeAngle': set cone angle of currently active light
else if ((*it).tokens[0] == "LightConeAngle") {
if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightConeAngle\'");
else nodes.back().lightConeAngle = fast_atof(c);
}
// 'LightEdgeAngle': set area where we're smoothing from min to max intensity
else if ((*it).tokens[0] == "LightEdgeAngle") {
if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightEdgeAngle\'");
else nodes.back().lightEdgeAngle = fast_atof(c);
}
// 'LightColor': set color of currently active light
else if ((*it).tokens[0] == "LightColor") {
if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightColor\'");
else {
c = fast_atof_move(c, (float&) nodes.back().lightColor.r );
SkipSpaces(&c);
c = fast_atof_move(c, (float&) nodes.back().lightColor.g );
SkipSpaces(&c);
c = fast_atof_move(c, (float&) nodes.back().lightColor.b );
}
}
// 'PivotPosition': position of local transformation origin
else if ((*it).tokens[0] == "PivotPosition" || (*it).tokens[0] == "PivotPoint") {
if (nodes.empty())
DefaultLogger::get()->error("LWS: Unexpected keyword: \'PivotPosition\'");
else {
c = fast_atof_move(c, (float&) nodes.back().pivotPos.x );
SkipSpaces(&c);
c = fast_atof_move(c, (float&) nodes.back().pivotPos.y );
SkipSpaces(&c);
c = fast_atof_move(c, (float&) nodes.back().pivotPos.z );
}
}
}
// resolve parenting
for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
// check whether there is another node which calls us a parent
for (std::list<LWS::NodeDesc>::iterator dit = nodes.begin(); dit != nodes.end(); ++dit) {
if (dit != it && *it == (*dit).parent) {
if ((*dit).parent_resolved) {
// fixme: it's still possible to produce an overflow due to cross references ..
DefaultLogger::get()->error("LWS: Found cross reference in scenegraph");
continue;
}
(*it).children.push_back(&*dit);
(*dit).parent_resolved = &*it;
}
}
}
// find out how many nodes have no parent yet
unsigned int no_parent = 0;
for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
if (!(*it).parent_resolved)
++ no_parent;
}
if (!no_parent)
throw new ImportErrorException("LWS: Unable to find scene root node");
// Load all subsequent files
batch.LoadAll();
// and build the final output graph by attaching the loaded external
// files to ourselves. first build a master graph
aiScene* master = new aiScene();
aiNode* nd = master->mRootNode = new aiNode();
// allocate storage for cameras&lights
if (num_camera) {
master->mCameras = new aiCamera*[master->mNumCameras = num_camera];
}
aiCamera** cams = master->mCameras;
if (num_light) {
master->mLights = new aiLight*[master->mNumLights = num_light];
}
aiLight** lights = master->mLights;
std::vector<AttachmentInfo> attach;
std::vector<aiNodeAnim*> anims;
nd->mName.Set("<LWSRoot>");
nd->mChildren = new aiNode*[no_parent];
for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
if (!(*it).parent_resolved) {
aiNode* ro = nd->mChildren[ nd->mNumChildren++ ] = new aiNode();
ro->mParent = nd;
// ... and build the scene graph. If we encounter object nodes,
// add then to our attachment table.
BuildGraph(ro,*it, attach, batch, cams, lights, anims);
}
}
// create a master animation channel for us
if (anims.size()) {
master->mAnimations = new aiAnimation*[master->mNumAnimations = 1];
aiAnimation* anim = master->mAnimations[0] = new aiAnimation();
anim->mName.Set("LWSMasterAnim");
// LWS uses seconds as time units, but we convert to frames
anim->mTicksPerSecond = fps;
anim->mDuration = last-(first-1); /* fixme ... zero or one-based?*/
anim->mChannels = new aiNodeAnim*[anim->mNumChannels = anims.size()];
std::copy(anims.begin(),anims.end(),anim->mChannels);
}
// OK ... finally build the output graph
SceneCombiner::MergeScenes(&pScene,master,attach,
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) : 0));
// Check flags
if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
if (pScene->mNumAnimations) {
// construct skeleton mesh
SkeletonMeshBuilder builder(pScene);
}
}
}

View File

@ -38,10 +38,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the .LWS (LightWave Scene Format) importer class. */
/** @file LWSLoader.h
* @brief Declaration of the LightWave scene importer class.
*/
#ifndef AI_LWSLOADER_H_INCLUDED
#define AI_LWSLOADER_H_INCLUDED
#include "LWOFileData.h"
#include "SceneCombiner.h"
namespace Assimp {
namespace LWS {
@ -49,17 +53,107 @@ namespace Assimp {
// ---------------------------------------------------------------------------
/** Represents an element in a LWS file.
*
* This can either be a single data line - <name> <value> or it can
* be a data group - { name <data_line0> ... n }
* This can either be a single data line - <name> <value> or a data
* group - { name <data_line0> ... n }
*/
class Element
{
std::string name, data;
public:
Element()
{}
// first: name, second: rest
std::string tokens[2];
std::list<Element> children;
void Parse (const char* buffer);
//! Recursive parsing function
void Parse (const char*& buffer);
};
#define AI_LWS_MASK (0xffffffff >> 4u)
// ---------------------------------------------------------------------------
/** Represents a LWS scenegraph element
*/
struct NodeDesc
{
NodeDesc()
: number (0)
, parent (0)
, name ("")
, parent_resolved (NULL)
, lightIntensity (1.f)
, lightColor (1.f,1.f,1.f)
, lightType (0)
, lightFalloffType (0)
, lightConeAngle (45.f)
{}
enum {
OBJECT = 1,
LIGHT = 2,
CAMERA = 3,
BONE = 4,
} type; // type of node
// if object: path
std::string path;
unsigned int id;
// number of object
unsigned int number;
// index of parent index
unsigned int parent;
// lights & cameras & dummies: name
const char* name;
// animation channels
std::list< LWO::Envelope > channels;
// position of pivot point
aiVector3D pivotPos;
// color of light source
aiColor3D lightColor;
// intensity of light source
float lightIntensity;
// type of light source
unsigned int lightType;
// falloff type of light source
unsigned int lightFalloffType;
// cone angle of (spot) light source
float lightConeAngle;
// soft cone angle of (spot) light source
float lightEdgeAngle;
// list of resolved children
std::list< NodeDesc* > children;
// resolved parent node
NodeDesc* parent_resolved;
// for std::find()
bool operator == (unsigned int num) const {
if (!num)
return false;
unsigned int _type = num >> 28u;
return _type == type && (num & AI_LWS_MASK) == number;
}
};
} // end namespace LWS
@ -84,28 +178,63 @@ protected:
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;
// Check whether we can read a specific file
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
// -------------------------------------------------------------------
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
// Get list of supported extensions
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
// Import file into given scene data structure
void InternReadFile( const std::string& pFile, aiScene* pScene,
IOSystem* pIOHandler);
// -------------------------------------------------------------------
// Setup import properties
void SetupProperties(const Importer* pImp);
private:
// -------------------------------------------------------------------
// Read an envelope description
void ReadEnvelope(const LWS::Element& dad, LWO::Envelope& out );
// -------------------------------------------------------------------
// Read an envelope description for the older LW file format
void ReadEnvelope_Old(std::list< LWS::Element >::const_iterator& it,
const std::list< LWS::Element >::const_iterator& end,
LWS::NodeDesc& nodes,
unsigned int version);
// -------------------------------------------------------------------
// Setup a nice name for a node
void SetupNodeName(aiNode* nd, LWS::NodeDesc& src);
// -------------------------------------------------------------------
// Recursively build the scenegraph
void BuildGraph(aiNode* nd,
LWS::NodeDesc& src,
std::vector<AttachmentInfo>& attach,
BatchLoader& batch,
aiCamera**& camOut,
aiLight**& lightOut,
std::vector<aiNodeAnim*>& animOut);
// -------------------------------------------------------------------
// Try several dirs until we find the right location of a LWS file.
std::string FindLWOFile(const std::string& in);
private:
bool configSpeedFlag;
IOSystem* io;
double first,last,fps;
};
} // end of namespace Assimp

View File

@ -38,21 +38,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Defines the helper data structures for importing MD2 files
**********************************************************************
File format specification:
//http://linux.ucla.edu/~phaethon/q3/formats/md2-schoenblum.html
**********************************************************************
*/
/** @file MD2FileData.h
* @brief Defines helper data structures for importing MD2 files
* http://linux.ucla.edu/~phaethon/q3/formats/md2-schoenblum.html
*/
#ifndef AI_MD2FILEHELPER_H_INC
#define AI_MD2FILEHELPER_H_INC
#include <string>
#include <vector>
#include <sstream>
#include "../include/aiTypes.h"
#include "../include/aiMesh.h"
#include "../include/aiAnim.h"
@ -62,11 +54,9 @@ File format specification:
namespace Assimp {
namespace MD2 {
// to make it easier for ourselfes, we test the magic word against both "endianesses"
#define MD2_MAKE(string) ((uint32_t)((string[0] << 24) + (string[1] << 16) + (string[2] << 8) + string[3]))
#define AI_MD2_MAGIC_NUMBER_BE MD2_MAKE("IDP2")
#define AI_MD2_MAGIC_NUMBER_LE MD2_MAKE("2PDI")
// to make it easier for us, we test the magic word against both "endianesses"
#define AI_MD2_MAGIC_NUMBER_BE AI_MAKE_MAGIC("IDP2")
#define AI_MD2_MAGIC_NUMBER_LE AI_MAKE_MAGIC("2PDI")
// common limitations
#define AI_MD2_VERSION 15

View File

@ -62,11 +62,8 @@ using namespace Assimp::MD2;
void MD2::LookupNormalIndex(uint8_t iNormalIndex,aiVector3D& vOut)
{
// make sure the normal index has a valid value
if (iNormalIndex >= ARRAYSIZE(g_avNormals))
{
DefaultLogger::get()->warn("Index overflow in Quake II normal vector list (the "
" LUT has only 162 entries). ");
if (iNormalIndex >= ARRAYSIZE(g_avNormals)) {
DefaultLogger::get()->warn("Index overflow in Quake II normal vector list");
iNormalIndex = ARRAYSIZE(g_avNormals) - 1;
}
vOut = *((const aiVector3D*)(&g_avNormals[iNormalIndex]));
@ -76,31 +73,37 @@ void MD2::LookupNormalIndex(uint8_t iNormalIndex,aiVector3D& vOut)
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
MD2Importer::MD2Importer()
{
}
{}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
MD2Importer::~MD2Importer()
{
}
{}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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);
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
*it = tolower( *it);
const std::string extension = GetExtension(pFile);
if (extension == "md2")
return true;
return ( extension == ".md2");
// if check for extension is not enough, check for the magic tokens
if (!extension.length() || checkSig) {
uint32_t tokens[1];
tokens[0] = AI_MD2_MAGIC_NUMBER_LE;
return CheckMagicToken(pIOHandler,pFile,tokens,1);
}
return false;
}
// ------------------------------------------------------------------------------------------------
// Get a list of all extensions supported by this loader
void MD2Importer::GetExtensionList(std::string& append)
{
append.append("*.md2");
}
// ------------------------------------------------------------------------------------------------
// Setup configuration properties
void MD2Importer::SetupProperties(const Importer* pImp)
@ -161,8 +164,8 @@ void MD2Importer::ValidateHeader( )
if (m_pcHeader->numFrames <= configFrameID )
throw new ImportErrorException("The requested frame is not existing the file");
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void MD2Importer::InternReadFile( const std::string& pFile,
@ -319,14 +322,14 @@ void MD2Importer::InternReadFile( const std::string& pFile,
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
aiString szName;
szName.Set("MD2Default");
szName.Set(AI_DEFAULT_TEXTURED_MATERIAL_NAME);
pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
aiString sz;
// TODO: Try to guess the name of the texture file from the model file name
sz.Set("texture_dummmy.bmp");
sz.Set("$texture_dummy.bmp");
pcHelper->AddProperty(&sz,AI_MATKEY_TEXTURE_DIFFUSE(0));
}

View File

@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Definition of the .MD2 importer class. */
/** @file MD2Loader.h
* @brief Declaration of the .MD2 importer class.
*/
#ifndef AI_MD2LOADER_H_INCLUDED
#define AI_MD2LOADER_H_INCLUDED
@ -48,14 +50,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
struct aiNode;
#include "MD2FileData.h"
namespace Assimp
{
namespace Assimp {
class MaterialHelper;
using namespace MD2;
// ---------------------------------------------------------------------------
/** Used to load MD2 files
/** Importer class for MD2
*/
class MD2Importer : public BaseImporter
{
@ -73,7 +74,8 @@ 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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
// -------------------------------------------------------------------
@ -89,10 +91,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.md2");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -59,11 +59,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
namespace MD3 {
// to make it easier for ourselfes, we test the magic word against both "endianesses"
#define MD3_MAKE(string) ((uint32_t)((string[0] << 24) + (string[1] << 16) + (string[2] << 8) + string[3]))
#define AI_MD3_MAGIC_NUMBER_BE MD3_MAKE("IDP3")
#define AI_MD3_MAGIC_NUMBER_LE MD3_MAKE("3PDI")
// to make it easier for us, we test the magic word against both "endianesses"
#define AI_MD3_MAGIC_NUMBER_BE AI_MAKE_MAGIC("IDP3")
#define AI_MD3_MAGIC_NUMBER_LE AI_MAKE_MAGIC("3PDI")
// common limitations
#define AI_MD3_VERSION 15

View File

@ -344,18 +344,19 @@ MD3Importer::~MD3Importer()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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);
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
*it = tolower( *it);
const std::string extension = GetExtension(pFile);
if (extension == "md3")
return true;
return ( extension == ".md3");
// if check for extension is not enough, check for the magic tokens
if (!extension.length() || checkSig) {
uint32_t tokens[1];
tokens[0] = AI_MD3_MAGIC_NUMBER_LE;
return CheckMagicToken(pIOHandler,pFile,tokens,1);
}
return false;
}
// ------------------------------------------------------------------------------------------------
@ -436,6 +437,9 @@ void MD3Importer::SetupProperties(const Importer* pImp)
// AI_CONFIG_IMPORT_MD3_SHADER_SRC
configShaderFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SHADER_SRC,""));
// AI_CONFIG_FAVOUR_SPEED
configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
}
// ------------------------------------------------------------------------------------------------
@ -458,10 +462,7 @@ void MD3Importer::ReadSkin(Q3Shader::SkinData& fill) const
void MD3Importer::ReadShader(Q3Shader::ShaderData& fill) const
{
// Determine Q3 model name from given path
std::string::size_type s = path.find_last_of('\\',path.length()-2);
if (s == std::string::npos)
s = path.find_last_of('/',path.length()-2);
std::string::size_type s = path.find_last_of("\\/",path.length()-2);
const std::string model_file = path.substr(s+1,path.length()-(s+2));
// If no specific dir or file is given, use our default search behaviour
@ -486,6 +487,24 @@ void MD3Importer::ReadShader(Q3Shader::ShaderData& fill) const
}
}
// ------------------------------------------------------------------------------------------------
// Tiny helper to remove a single node from its parent' list
void RemoveSingleNodeFromList(aiNode* nd)
{
if (!nd || nd->mNumChildren || !nd->mParent)return;
aiNode* par = nd->mParent;
for (unsigned int i = 0; i < par->mNumChildren;++i) {
if (par->mChildren[i] == nd) {
--par->mNumChildren;
for (;i < par->mNumChildren;++i) {
par->mChildren[i] = par->mChildren[i+1];
}
delete nd;
break;
}
}
}
// ------------------------------------------------------------------------------------------------
// Read a multi-part Q3 player model
bool MD3Importer::ReadMultipartFile()
@ -520,32 +539,32 @@ bool MD3Importer::ReadMultipartFile()
// now read these three files
BatchLoader batch(mIOHandler);
batch.AddLoadRequest(lower,0,&props);
batch.AddLoadRequest(upper,0,&props);
batch.AddLoadRequest(head,0,&props);
unsigned int _lower = batch.AddLoadRequest(lower,0,&props);
unsigned int _upper = batch.AddLoadRequest(upper,0,&props);
unsigned int _head = batch.AddLoadRequest(head,0,&props);
batch.LoadAll();
// now construct a dummy scene to place these three parts in
aiScene* master = new aiScene();
aiNode* nd = master->mRootNode = new aiNode();
nd->mName.Set("<M3D_Player>");
nd->mName.Set("<MD3_Player>");
// ... and get them. We need all of them.
scene_lower = batch.GetImport(lower);
scene_lower = batch.GetImport(_lower);
if (!scene_lower) {
DefaultLogger::get()->error("M3D: Failed to read multipart model, lower.md3 fails to load");
failure = "lower";
goto error_cleanup;
}
scene_upper = batch.GetImport(upper);
scene_upper = batch.GetImport(_upper);
if (!scene_upper) {
DefaultLogger::get()->error("M3D: Failed to read multipart model, upper.md3 fails to load");
failure = "upper";
goto error_cleanup;
}
scene_head = batch.GetImport(head);
scene_head = batch.GetImport(_head);
if (!scene_head) {
DefaultLogger::get()->error("M3D: Failed to read multipart model, head.md3 fails to load");
failure = "head";
@ -555,6 +574,7 @@ bool MD3Importer::ReadMultipartFile()
// build attachment infos. search for typical Q3 tags
// original root
scene_lower->mRootNode->mName.Set("lower");
attach.push_back(AttachmentInfo(scene_lower, nd));
// tag_torso
@ -563,6 +583,7 @@ bool MD3Importer::ReadMultipartFile()
DefaultLogger::get()->error("M3D: Failed to find attachment tag for multipart model: tag_torso expected");
goto error_cleanup;
}
scene_upper->mRootNode->mName.Set("upper");
attach.push_back(AttachmentInfo(scene_upper,tag_torso));
// tag_head
@ -571,13 +592,21 @@ bool MD3Importer::ReadMultipartFile()
DefaultLogger::get()->error("M3D: Failed to find attachment tag for multipart model: tag_head expected");
goto error_cleanup;
}
scene_head->mRootNode->mName.Set("head");
attach.push_back(AttachmentInfo(scene_head,tag_head));
// Remove tag_head and tag_torso from all other model parts ...
// this ensures (together with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY)
// that tag_torso/tag_head is also the name of the (unique) output node
RemoveSingleNodeFromList (scene_upper->mRootNode->FindNode("tag_torso"));
RemoveSingleNodeFromList (scene_head-> mRootNode->FindNode("tag_head" ));
// and merge the scenes
SceneCombiner::MergeScenes(&mScene,master, attach,
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES |
AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES |
AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS);
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES |
AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES |
AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS |
(!configSpeedFlag ? AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY : 0));
return true;

View File

@ -225,7 +225,8 @@ 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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
// -------------------------------------------------------------------
@ -297,6 +298,9 @@ protected:
/** Configuration option: name or path of shader */
std::string configShaderFile;
/** Configuration option: speed flag was set? */
bool configSpeedFlag;
/** Header of the MD3 file */
BE_NCONST MD3::Header* pcHeader;

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the MD5 importer class */
/** @file MD5Loader.cpp
* @brief Implementation of the MD5 importer class
*/
#include "AssimpPCH.h"
#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
@ -56,34 +58,35 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
MD5Importer::MD5Importer()
{
// nothing to do here
}
{}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
MD5Importer::~MD5Importer()
{
// nothing to do here
}
{}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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);
const std::string extension = GetExtension(pFile);
if (extension.length() < 4)return false;
if (extension[0] != '.')return false;
if (extension == "md5anim" || extension == "md5mesh")
return true;
else if (!extension.length() || checkSig) {
if (!pIOHandler)
return true;
const char* tokens[] = {"MD5Version"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
}
if (extension[1] != 'm' && extension[1] != 'M')return false;
if (extension[2] != 'd' && extension[2] != 'D')return false;
if (extension[3] != '5')return false;
return true;
// ------------------------------------------------------------------------------------------------
// Get list of all supported extensions
void MD5Importer::GetExtensionList(std::string& append)
{
append.append("*.md5mesh;*.md5anim");
}
// ------------------------------------------------------------------------------------------------
@ -93,17 +96,17 @@ void MD5Importer::InternReadFile(
{
// remove the file extension
std::string::size_type pos = pFile.find_last_of('.');
this->mFile = pFile.substr(0,pos+1);
mFile = pFile.substr(0,pos+1);
this->pIOHandler = pIOHandler;
this->pScene = pScene;
bHadMD5Mesh = bHadMD5Anim = false;
// load the animation keyframes
this->LoadMD5AnimFile();
LoadMD5AnimFile();
// load the mesh vertices and bones
this->LoadMD5MeshFile();
LoadMD5MeshFile();
// make sure we return no incomplete data
if (!bHadMD5Mesh && !bHadMD5Anim)
@ -260,7 +263,7 @@ void MD5Importer::LoadMD5MeshFile ()
bHadMD5Mesh = true;
// now load the file into memory
this->LoadFileIntoMemory(file.get());
LoadFileIntoMemory(file.get());
// now construct a parser and parse the file
MD5::MD5Parser parser(mBuffer,fileSize);
@ -341,10 +344,7 @@ void MD5Importer::LoadMD5MeshFile ()
unsigned int* piCount = new unsigned int[meshParser.mJoints.size()];
::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size());
for (MD5::VertexList::const_iterator
iter = meshSrc.mVertices.begin();
iter != meshSrc.mVertices.end();++iter,++pv)
{
for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
{
MD5::WeightDesc& desc = meshSrc.mWeights[w];
@ -444,7 +444,7 @@ void MD5Importer::LoadMD5MeshFile ()
// ------------------------------------------------------------------------------------------------
void MD5Importer::LoadMD5AnimFile ()
{
std::string pFile = this->mFile + "MD5ANIM";
std::string pFile = mFile + "MD5ANIM";
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
// Check whether we can read from the file

View File

@ -39,8 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file Definition of the .MD5 importer class.
http://www.modwiki.net/wiki/MD5_(file_format)
/** @file MD5Loader.h
* @brief Definition of the .MD5 importer class.
* http://www.modwiki.net/wiki/MD5_(file_format)
*/
#ifndef AI_MD5LOADER_H_INCLUDED
#define AI_MD5LOADER_H_INCLUDED
@ -74,7 +75,8 @@ 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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -82,11 +84,8 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.md5mesh;*.md5anim");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details

View File

@ -112,21 +112,22 @@ void MD5Parser::ParseHeader()
if (0 != ASSIMP_strincmp(buffer,"MD5Version",10) ||
!IsSpace(*(buffer+=10)))
{
this->ReportError("Invalid MD5 file: MD5Version tag has not been found");
ReportError("Invalid MD5 file: MD5Version tag has not been found");
}
SkipSpaces();
unsigned int iVer = ::strtol10(buffer,(const char**)&buffer);
if (10 != iVer)
{
this->ReportWarning("MD5 version tag is unknown (10 is expected)");
ReportWarning("MD5 version tag is unknown (10 is expected)");
}
this->SkipLine();
SkipLine();
// print the command line options to the console
// fix: can break the log length limit ...
char* sz = buffer;
while (!IsLineEnd( *buffer++));
DefaultLogger::get()->info(std::string(sz,(uintptr_t)(buffer-sz)));
this->SkipSpacesAndLineEnd();
DefaultLogger::get()->info(std::string(sz,std::min((uintptr_t)MAX_LOG_MESSAGE_LENGTH, (uintptr_t)(buffer-sz))));
SkipSpacesAndLineEnd();
}
// ------------------------------------------------------------------------------------------------
bool MD5Parser::ParseSection(Section& out)

View File

@ -39,21 +39,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file Definition of the .MD5 parser class.
http://www.modwiki.net/wiki/MD5_(file_format)
*/
/** @file MD5Parser.h
* @brief Definition of the .MD5 parser class.
* http://www.modwiki.net/wiki/MD5_(file_format)
*/
#ifndef AI_MD5PARSER_H_INCLUDED
#define AI_MD5PARSER_H_INCLUDED
#include "../include/aiTypes.h"
#include "ParsingUtils.h"
#include <vector>
struct aiFace;
namespace Assimp {
namespace MD5 {
namespace MD5 {
// ---------------------------------------------------------------------------
/** Represents a single element in a MD5 file
@ -335,12 +334,13 @@ public:
static void ReportWarning (const char* warn, unsigned int line);
inline void ReportError (const char* error)
{return ReportError(error, this->lineNumber);}
inline void ReportWarning (const char* warn)
{return ReportWarning(warn, this->lineNumber);}
void ReportError (const char* error) {
return ReportError(error, lineNumber);
}
void ReportWarning (const char* warn) {
return ReportWarning(warn, lineNumber);
}
public:
@ -367,24 +367,22 @@ private:
// override these functions to make sure the line counter gets incremented
// -------------------------------------------------------------------
inline bool SkipLine( const char* in, const char** out)
bool SkipLine( const char* in, const char** out)
{
++lineNumber;
return Assimp::SkipLine(in,out);
}
// -------------------------------------------------------------------
inline bool SkipLine( )
bool SkipLine( )
{
return SkipLine(buffer,(const char**)&buffer);
}
// -------------------------------------------------------------------
inline bool SkipSpacesAndLineEnd( const char* in, const char** out)
bool SkipSpacesAndLineEnd( const char* in, const char** out)
{
bool bHad = false;
while (true)
{
if( *in == '\r' || *in == '\n')
{
while (true) {
if( *in == '\r' || *in == '\n') {
if (!bHad) // we open files in binary mode, so there could be \r\n sequences ...
{
bHad = true;
@ -400,12 +398,12 @@ private:
return *in != '\0';
}
// -------------------------------------------------------------------
inline bool SkipSpacesAndLineEnd( )
bool SkipSpacesAndLineEnd( )
{
return SkipSpacesAndLineEnd(buffer,(const char**)&buffer);
}
// -------------------------------------------------------------------
inline bool SkipSpaces( )
bool SkipSpaces( )
{
return Assimp::SkipSpaces((const char**)&buffer);
}

View File

@ -60,11 +60,9 @@ namespace Assimp {
namespace MDC {
// to make it easier for ourselfes, we test the magic word against both "endianesses"
#define MDC_MAKE(string) ((uint32_t)((string[0] << 24) + (string[1] << 16) + (string[2] << 8) + string[3]))
#define AI_MDC_MAGIC_NUMBER_BE MDC_MAKE("CPDI")
#define AI_MDC_MAGIC_NUMBER_LE MDC_MAKE("IDPC")
// to make it easier for us, we test the magic word against both "endianesses"
#define AI_MDC_MAGIC_NUMBER_BE AI_MAKE_MAGIC("CPDI")
#define AI_MDC_MAGIC_NUMBER_LE AI_MAKE_MAGIC("IDPC")
// common limitations
#define AI_MDC_VERSION 2

View File

@ -87,23 +87,25 @@ MDCImporter::~MDCImporter()
}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool MDCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool MDCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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);
const std::string extension = GetExtension(pFile);
if (extension == "mdc")
return true;
if (extension.length() < 4)return false;
if (extension[0] != '.')return false;
if( extension[1] != 'M' && extension[1] != 'm')return false;
if( extension[2] != 'D' && extension[2] != 'd')return false;
if( extension[3] != 'C' && extension[3] != 'c')return false;
// if check for extension is not enough, check for the magic tokens
if (!extension.length() || checkSig) {
uint32_t tokens[1];
tokens[0] = AI_MDC_MAGIC_NUMBER_LE;
return CheckMagicToken(pIOHandler,pFile,tokens,1);
}
return false;
}
return true;
// ------------------------------------------------------------------------------------------------
void MDCImporter::GetExtensionList(std::string& append)
{
append.append("*.mdc");
}
// ------------------------------------------------------------------------------------------------

View File

@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Definition of the MDC importer class. */
/** @file MDCLoader.h
* @brief Definition of the MDC importer class.
*/
#ifndef AI_MDCLOADER_H_INCLUDED
#define AI_MDCLOADER_H_INCLUDED
@ -48,12 +50,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "MDCFileData.h"
#include "ByteSwap.h"
namespace Assimp
{
namespace Assimp {
using namespace MDC;
// ---------------------------------------------------------------------------
/** Used to load MDC files
/** Importer class to load the RtCW MDC file format
*/
class MDCImporter : public BaseImporter
{
@ -71,7 +72,8 @@ 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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
// -------------------------------------------------------------------
/** Called prior to ReadFile().
@ -86,10 +88,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.mdc");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -58,30 +58,29 @@ namespace Assimp {
namespace MDL {
// -------------------------------------------------------------------------------------
// to make it easier for ourselfes, we test the magic word against both "endianesses"
#define MDL_MAKE(string) ((uint32_t)((string[0] << 24) + (string[1] << 16) + (string[2] << 8) + string[3]))
// to make it easier for us, we test the magic word against both "endianesses"
// magic bytes used in Quake 1 MDL meshes
#define AI_MDL_MAGIC_NUMBER_BE MDL_MAKE("IDPO")
#define AI_MDL_MAGIC_NUMBER_LE MDL_MAKE("OPDI")
#define AI_MDL_MAGIC_NUMBER_BE AI_MAKE_MAGIC("IDPO")
#define AI_MDL_MAGIC_NUMBER_LE AI_MAKE_MAGIC("OPDI")
// magic bytes used in GameStudio A<very low> MDL meshes
#define AI_MDL_MAGIC_NUMBER_BE_GS3 MDL_MAKE("MDL2")
#define AI_MDL_MAGIC_NUMBER_LE_GS3 MDL_MAKE("2LDM")
#define AI_MDL_MAGIC_NUMBER_BE_GS3 AI_MAKE_MAGIC("MDL2")
#define AI_MDL_MAGIC_NUMBER_LE_GS3 AI_MAKE_MAGIC("2LDM")
// magic bytes used in GameStudio A4 MDL meshes
#define AI_MDL_MAGIC_NUMBER_BE_GS4 MDL_MAKE("MDL3")
#define AI_MDL_MAGIC_NUMBER_LE_GS4 MDL_MAKE("3LDM")
#define AI_MDL_MAGIC_NUMBER_BE_GS4 AI_MAKE_MAGIC("MDL3")
#define AI_MDL_MAGIC_NUMBER_LE_GS4 AI_MAKE_MAGIC("3LDM")
// magic bytes used in GameStudio A5+ MDL meshes
#define AI_MDL_MAGIC_NUMBER_BE_GS5a MDL_MAKE("MDL4")
#define AI_MDL_MAGIC_NUMBER_LE_GS5a MDL_MAKE("4LDM")
#define AI_MDL_MAGIC_NUMBER_BE_GS5b MDL_MAKE("MDL5")
#define AI_MDL_MAGIC_NUMBER_LE_GS5b MDL_MAKE("5LDM")
#define AI_MDL_MAGIC_NUMBER_BE_GS5a AI_MAKE_MAGIC("MDL4")
#define AI_MDL_MAGIC_NUMBER_LE_GS5a AI_MAKE_MAGIC("4LDM")
#define AI_MDL_MAGIC_NUMBER_BE_GS5b AI_MAKE_MAGIC("MDL5")
#define AI_MDL_MAGIC_NUMBER_LE_GS5b AI_MAKE_MAGIC("5LDM")
// magic bytes used in GameStudio A7+ MDL meshes
#define AI_MDL_MAGIC_NUMBER_BE_GS7 MDL_MAKE("MDL7")
#define AI_MDL_MAGIC_NUMBER_LE_GS7 MDL_MAKE("7LDM")
#define AI_MDL_MAGIC_NUMBER_BE_GS7 AI_MAKE_MAGIC("MDL7")
#define AI_MDL_MAGIC_NUMBER_LE_GS7 AI_MAKE_MAGIC("7LDM")
// common limitations for Quake1 meshes. The loader does not check them,

View File

@ -77,32 +77,41 @@ MDLImporter::~MDLImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool MDLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool MDLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
(void)pIOHandler; //this avoids the compiler warning of unused element
// 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;
const std::string extension = GetExtension(pFile);
if (extension == "mdl" )
return true;
std::string extension = pFile.substr( pos);
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
*it = tolower( *it);
return extension == ".mdl";
// if check for extension is not enough, check for the magic tokens
if (!extension.length() || checkSig) {
uint32_t tokens[8];
tokens[0] = AI_MDL_MAGIC_NUMBER_LE_HL2a;
tokens[1] = AI_MDL_MAGIC_NUMBER_LE_HL2b;
tokens[2] = AI_MDL_MAGIC_NUMBER_LE_GS7;
tokens[3] = AI_MDL_MAGIC_NUMBER_LE_GS5b;
tokens[4] = AI_MDL_MAGIC_NUMBER_LE_GS5a;
tokens[5] = AI_MDL_MAGIC_NUMBER_LE_GS4;
tokens[6] = AI_MDL_MAGIC_NUMBER_LE_GS3;
tokens[7] = AI_MDL_MAGIC_NUMBER_LE;
return CheckMagicToken(pIOHandler,pFile,tokens,7,0);
}
return false;
}
// ------------------------------------------------------------------------------------------------
// Setup configuration properties
void MDLImporter::SetupProperties(const Importer* pImp)
{
// The AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the
configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MDL_KEYFRAME,0xffffffff);
// The
// AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the
// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
if(0xffffffff == (configFrameID = pImp->GetPropertyInteger(
AI_CONFIG_IMPORT_MDL_KEYFRAME,0xffffffff)))
{
if(0xffffffff == configFrameID) {
configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
}
// AI_CONFIG_IMPORT_MDL_COLORMAP - pallette file
configPalette = pImp->GetPropertyString(AI_CONFIG_IMPORT_MDL_COLORMAP,"colormap.lmp");
}

View File

@ -96,7 +96,8 @@ 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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
// -------------------------------------------------------------------

View File

@ -89,7 +89,7 @@ aiColor4D MDLImporter::ReplaceTextureWithColor(const aiTexture* pcTexture)
ai_assert(NULL != pcTexture);
aiColor4D clrOut;
clrOut.r = std::numeric_limits<float>::quiet_NaN();
clrOut.r = get_qnan();
if (!pcTexture->mHeight || !pcTexture->mWidth)
return clrOut;
@ -572,7 +572,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
// been converted to MDL7 from other formats, such as MDL5
aiColor4D clrTexture;
if (pcNew)clrTexture = this->ReplaceTextureWithColor(pcNew);
else clrTexture.r = std::numeric_limits<float>::quiet_NaN();
else clrTexture.r = get_qnan();
// check whether a material definition is contained in the skin
if (iType & AI_MDL7_SKINTYPE_MATERIAL)

View File

@ -65,22 +65,16 @@ NFFImporter::~NFFImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool NFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool NFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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 SimpleExtensionCheck(pFile,"nff","enff");
}
// extensions: enff and nff
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
*it = tolower( *it);
if( extension == ".nff" || extension == ".enff")
return true;
return false;
// ------------------------------------------------------------------------------------------------
// Get the list of all supported file extensions
void NFFImporter::GetExtensionList(std::string& append)
{
append.append("*.nff;*.enff");
}
// ------------------------------------------------------------------------------------------------
@ -96,7 +90,7 @@ bool NFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
// ------------------------------------------------------------------------------------------------
#define AI_NFF_PARSE_SHAPE_INFORMATION() \
aiVector3D center, radius(1.0f,std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN()); \
aiVector3D center, radius(1.0f,get_qnan(),get_qnan()); \
AI_NFF_PARSE_TRIPLE(center); \
AI_NFF_PARSE_TRIPLE(radius); \
if (is_qnan(radius.z))radius.z = radius.x; \
@ -282,7 +276,7 @@ void NFFImporter::InternReadFile( const std::string& pFile,
// check whether this is the NFF2 file format
if (TokenMatch(buffer,"nff",3))
{
const float qnan = std::numeric_limits<float>::quiet_NaN();
const float qnan = get_qnan();
const aiColor4D cQNAN = aiColor4D (qnan,0.f,0.f,1.f);
const aiVector3D vQNAN = aiVector3D(qnan,0.f,0.f);

View File

@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the NFF importer class. */
/** @file NFFLoader.h
* @brief Declaration of the NFF importer class.
*/
#ifndef AI_NFFLOADER_H_INCLUDED
#define AI_NFFLOADER_H_INCLUDED
@ -49,7 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
// ---------------------------------------------------------------------------
// ----------------------------------------------------------------------------------
/** NFF (Neutral File Format) Importer class.
*
* The class implements both Eric Haynes NFF format and Sense8's NFF (NFF2) format.
@ -71,8 +73,10 @@ 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;
* See BaseImporter::CanRead() for details.
*/
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -80,10 +84,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.nff;*.enff");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the OFF importer class */
/** @file OFFLoader.cpp
* @brief Implementation of the OFF importer class
*/
#include "AssimpPCH.h"
#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
@ -64,25 +66,31 @@ OFFImporter::~OFFImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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);
const std::string extension = GetExtension(pFile);
if (extension == "off")
return true;
else if (!extension.length() || checkSig)
{
if (!pIOHandler)return true;
const char* tokens[] = {"off"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
}
return !(extension.length() != 4 || extension[0] != '.' ||
extension[1] != 'o' && extension[1] != 'O' ||
extension[2] != 'f' && extension[2] != 'F' ||
extension[3] != 'f' && extension[3] != 'F');
// ------------------------------------------------------------------------------------------------
void OFFImporter::GetExtensionList(std::string& append)
{
append.append("*.off");
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void OFFImporter::InternReadFile( const std::string& pFile,
aiScene* pScene, IOSystem* pIOHandler)
aiScene* pScene, IOSystem* pIOHandler)
{
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));

View File

@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the OFF importer class. */
/** @file OFFLoader.h
* @brief Declaration of the OFF importer class.
*/
#ifndef AI_OFFLOADER_H_INCLUDED
#define AI_OFFLOADER_H_INCLUDED
@ -67,7 +69,8 @@ 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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -75,10 +78,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.off");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -51,9 +51,6 @@ namespace Assimp {
using namespace std;
//! Obj-file-format extention
const string ObjFileImporter::OBJ_EXT = "obj";
// ------------------------------------------------------------------------------------------------
// Default constructor
ObjFileImporter::ObjFileImporter() :
@ -76,20 +73,10 @@ ObjFileImporter::~ObjFileImporter()
// ------------------------------------------------------------------------------------------------
// Returns true, fi file is an obj file
bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
if (pFile.empty())
return false;
string::size_type pos = pFile.find_last_of(".");
if (string::npos == pos)
return false;
const string ext = pFile.substr(pos+1, pFile.size() - pos - 1);
if (ext == OBJ_EXT)
return true;
return false;
// fixme: auto detection
return SimpleExtensionCheck(pFile,"obj");
}
// ------------------------------------------------------------------------------------------------

View File

@ -66,9 +66,6 @@ class ObjFileImporter :
{
friend class Importer;
//! OB file extention
static const std::string OBJ_EXT;
protected:
/// \brief Default constructor
ObjFileImporter();
@ -79,7 +76,7 @@ protected:
public:
/// \brief Returns whether the class can handle the format of the given file.
/// \remark See BaseImporter::CanRead() for details.
bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
private:

View File

@ -64,27 +64,31 @@ PLYImporter::~PLYImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool PLYImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool PLYImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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);
const std::string extension = GetExtension(pFile);
if (extension.length() < 4)return false;
if (extension[0] != '.') return false;
if (extension[1] != 'p' && extension[1] != 'P')return false;
if (extension[2] != 'l' && extension[2] != 'L')return false;
if (extension[3] != 'y' && extension[3] != 'Y')return false;
return true;
if (extension == "ply")
return true;
else if (!extension.length() || checkSig)
{
if (!pIOHandler)return true;
const char* tokens[] = {"ply"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
}
// ------------------------------------------------------------------------------------------------
void PLYImporter::GetExtensionList(std::string& append)
{
append.append("*.ply");
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void PLYImporter::InternReadFile(
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
@ -108,8 +112,7 @@ void PLYImporter::InternReadFile(
// the beginning of the file must be PLY - magic, magic
if (mBuffer[0] != 'P' && mBuffer[0] != 'p' ||
mBuffer[1] != 'L' && mBuffer[1] != 'l' ||
mBuffer[2] != 'Y' && mBuffer[2] != 'y')
{
mBuffer[2] != 'Y' && mBuffer[2] != 'y') {
throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there");
}
@ -789,19 +792,17 @@ void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
}
else // triangle strips
{
// HUGE TODO: MAKE THAT FUCK WORK!
// normally we have only one triangle strip instance where
// a value of -1 indicates a restart of the strip
for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();
i != pcList->alInstances.end();++i)
{
const std::vector<PLY::PropertyInstance::ValueUnion>& quak = (*i).alProperties[iProperty].avList;
pvOut->reserve(pvOut->size() + quak.size() + (quak.size()>>2u));
int aiTable[2] = {-1,-1};
for (std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator
a = (*i).alProperties[iProperty].avList.begin();
a != (*i).alProperties[iProperty].avList.end();++a)
{
int p = PLY::PropertyInstance::ConvertTo<int>(*a,eType);
for (std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator a = quak.begin();a != quak.end();++a) {
const int p = PLY::PropertyInstance::ConvertTo<int>(*a,eType);
if (-1 == p)
{
// restart the strip ...
@ -819,12 +820,12 @@ void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
continue;
}
PLY::Face sFace;
pvOut->push_back(PLY::Face());
PLY::Face& sFace = pvOut->back();
sFace.mIndices.push_back((unsigned int)aiTable[0]);
sFace.mIndices.push_back((unsigned int)aiTable[1]);
sFace.mIndices.push_back((unsigned int)p);
pvOut->push_back(sFace);
aiTable[0] = aiTable[1];
aiTable[1] = p;
}
@ -1054,4 +1055,4 @@ void PLYImporter::LoadMaterial(std::vector<MaterialHelper*>* pvOut)
}
}
#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER
#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER

View File

@ -38,8 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the .ply importer class. */
/** @file PLYLoader.h
* @brief Declaration of the .ply importer class.
*/
#ifndef AI_PLYLOADER_H_INCLUDED
#define AI_PLYLOADER_H_INCLUDED
@ -50,15 +51,13 @@ struct aiNode;
#include "PlyParser.h"
namespace Assimp
{
namespace Assimp {
class MaterialHelper;
using namespace PLY;
// ---------------------------------------------------------------------------
/** Used to load PLY files
/** Importer class to load the stanford PLY file format
*/
class PLYImporter : public BaseImporter
{
@ -75,8 +74,10 @@ 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;
* See BaseImporter::CanRead() for details.
*/
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -84,10 +85,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.ply");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -744,10 +744,9 @@ bool PLY::PropertyInstance::ParseInstanceBinary (
unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v,prop->eFirstType);
// parse all list elements
for (unsigned int i = 0; i < iNum;++i)
{
PLY::PropertyInstance::ParseValueBinary(pCur, &pCur,prop->eType,&v,p_bBE);
p_pcOut->avList.push_back(v);
p_pcOut->avList.resize(iNum);
for (unsigned int i = 0; i < iNum;++i){
PLY::PropertyInstance::ParseValueBinary(pCur, &pCur,prop->eType,&p_pcOut->avList[i],p_bBE);
}
}
else

View File

@ -39,12 +39,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the "PretransformVertices" post processing step
/** @file PretransformVertices.cpp
* @brief Implementation of the "PretransformVertices" post processing step
*/
#include "AssimpPCH.h"
#include "PretransformVertices.h"
#include "ProcessHelper.h"
#include "SceneCombiner.h"
using namespace Assimp;
@ -55,6 +57,7 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
PretransformVertices::PretransformVertices()
: configKeepHierarchy (false)
{
}
@ -72,9 +75,17 @@ bool PretransformVertices::IsActive( unsigned int pFlags) const
return (pFlags & aiProcess_PreTransformVertices) != 0;
}
// ------------------------------------------------------------------------------------------------
// Setup import configuration
void PretransformVertices::SetupProperties(const Importer* pImp)
{
// Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY
configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY,0));
}
// ------------------------------------------------------------------------------------------------
// Count the number of nodes
unsigned int CountNodes( aiNode* pcNode )
unsigned int PretransformVertices::CountNodes( aiNode* pcNode )
{
unsigned int iRet = 1;
for (unsigned int i = 0;i < pcNode->mNumChildren;++i)
@ -86,7 +97,7 @@ unsigned int CountNodes( aiNode* pcNode )
// ------------------------------------------------------------------------------------------------
// Get a bitwise combination identifying the vertex format of a mesh
unsigned int GetMeshVFormat(aiMesh* pcMesh)
unsigned int PretransformVertices::GetMeshVFormat(aiMesh* pcMesh)
{
// the vertex format is stored in aiMesh::mBones for later retrieval.
// there isn't a good reason to compute it a few hundred times
@ -106,7 +117,7 @@ unsigned int GetMeshVFormat(aiMesh* pcMesh)
// ------------------------------------------------------------------------------------------------
// Count the number of vertices in the whole scene and a given
// material index
void CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
void PretransformVertices::CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
unsigned int iVFormat, unsigned int* piFaces, unsigned int* piVertices)
{
for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
@ -127,39 +138,73 @@ void CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
// ------------------------------------------------------------------------------------------------
// Collect vertex/face data
void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
unsigned int iVFormat, aiMesh* pcMeshOut,
unsigned int aiCurrent[2])
unsigned int aiCurrent[2], unsigned int* num_refs)
{
// No need to multiply if there's no transformation
const bool identity = pcNode->mTransformation.IsIdentity();
for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
{
aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
{
// copy positions, transform them to worldspace
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)
pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n];
// Decrement mesh reference counter
unsigned int& num_ref = num_refs[pcNode->mMeshes[i]];
ai_assert(0 != num_ref);
--num_ref;
aiMatrix4x4 mWorldIT = pcNode->mTransformation;
mWorldIT.Inverse().Transpose();
if (identity) {
// copy positions without modifying them
::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX],
pcMesh->mVertices,
pcMesh->mNumVertices * sizeof(aiVector3D));
// TODO: implement Inverse() for aiMatrix3x3
aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
if (iVFormat & 0x2)
{
// copy normals, transform them to worldspace
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mNormals[n];
if (iVFormat & 0x2) {
// copy normals without modifying them
::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX],
pcMesh->mNormals,
pcMesh->mNumVertices * sizeof(aiVector3D));
}
if (iVFormat & 0x4)
{
// copy tangents without modifying them
::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX],
pcMesh->mTangents,
pcMesh->mNumVertices * sizeof(aiVector3D));
// copy bitangents without modifying them
::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX],
pcMesh->mBitangents,
pcMesh->mNumVertices * sizeof(aiVector3D));
}
}
if (iVFormat & 0x4)
else
{
// copy tangents and bitangents, transform them to worldspace
// copy positions, transform them to worldspace
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
pcMeshOut->mTangents [aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mTangents[n];
pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mBitangents[n];
pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n];
}
aiMatrix4x4 mWorldIT = pcNode->mTransformation;
mWorldIT.Inverse().Transpose();
// TODO: implement Inverse() for aiMatrix3x3
aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
if (iVFormat & 0x2)
{
// copy normals, transform them to worldspace
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] =
m * pcMesh->mNormals[n];
}
}
if (iVFormat & 0x4)
{
// copy tangents and bitangents, transform them to worldspace
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
pcMeshOut->mTangents [aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mTangents[n];
pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mBitangents[n];
}
}
}
unsigned int p = 0;
@ -180,26 +225,35 @@ void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
pcMesh->mNumVertices * sizeof(aiColor4D));
++p;
}
// now we need to copy all faces
// since we will delete the source mesh afterwards,
// we don't need to reallocate the array of indices
for (unsigned int planck = 0;planck<pcMesh->mNumFaces;++planck)
// now we need to copy all faces. since we will delete the source mesh afterwards,
// we don't need to reallocate the array of indices except if this mesh is
// referenced multiple times.
for (unsigned int planck = 0;planck < pcMesh->mNumFaces;++planck)
{
pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck].mNumIndices =
pcMesh->mFaces[planck].mNumIndices;
aiFace& f_src = pcMesh->mFaces[planck];
aiFace& f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck];
unsigned int* pi = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck].
mIndices = pcMesh->mFaces[planck].mIndices;
const unsigned int num_idx = f_src.mNumIndices;
// offset all vrtex indices
for (unsigned int hahn = 0; hahn < pcMesh->mFaces[planck].mNumIndices;++hahn)
{
pi[hahn] += aiCurrent[AI_PTVS_VERTEX];
f_dst.mNumIndices = num_idx;
unsigned int* pi;
if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */
pi = f_dst.mIndices = f_src.mIndices;
// offset all vertex indices
for (unsigned int hahn = 0; hahn < num_idx;++hahn){
pi[hahn] += aiCurrent[AI_PTVS_VERTEX];
}
}
else {
pi = f_dst.mIndices = new unsigned int[num_idx];
// copy and offset all vertex indices
for (unsigned int hahn = 0; hahn < num_idx;++hahn){
pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX];
}
}
// just make sure the array won't be deleted by the
// aiFace destructor ...
pcMesh->mFaces[planck].mIndices = NULL;
// Update the mPrimitiveTypes member of the mesh
switch (pcMesh->mFaces[planck].mNumIndices)
@ -222,24 +276,24 @@ void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
aiCurrent[AI_PTVS_FACE] += pcMesh->mNumFaces;
}
}
for (unsigned int i = 0;i < pcNode->mNumChildren;++i)
{
// append all children of us
for (unsigned int i = 0;i < pcNode->mNumChildren;++i) {
CollectData(pcScene,pcNode->mChildren[i],iMat,
iVFormat,pcMeshOut,aiCurrent);
iVFormat,pcMeshOut,aiCurrent,num_refs);
}
}
// ------------------------------------------------------------------------------------------------
// Get a list of all vertex formats that occur for a given material index
// The output list contains duplicate elements
void GetVFormatList( aiScene* pcScene, unsigned int iMat,
void PretransformVertices::GetVFormatList( aiScene* pcScene, unsigned int iMat,
std::list<unsigned int>& aiOut)
{
for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
{
aiMesh* pcMesh = pcScene->mMeshes[ i ];
if (iMat == pcMesh->mMaterialIndex)
{
if (iMat == pcMesh->mMaterialIndex) {
aiOut.push_back(GetMeshVFormat(pcMesh));
}
}
@ -247,7 +301,7 @@ void GetVFormatList( aiScene* pcScene, unsigned int iMat,
// ------------------------------------------------------------------------------------------------
// Compute the absolute transformation matrices of each node
void ComputeAbsoluteTransform( aiNode* pcNode )
void PretransformVertices::ComputeAbsoluteTransform( aiNode* pcNode )
{
if (pcNode->mParent) {
pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation;
@ -258,12 +312,126 @@ void ComputeAbsoluteTransform( aiNode* pcNode )
}
}
// ------------------------------------------------------------------------------------------------
// Apply the node transformation to a mesh
void PretransformVertices::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)
{
// Check whether we need to transform the coordinates at all
if (!mat.IsIdentity()) {
if (mesh->HasPositions()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mVertices[i] = mat * mesh->mVertices[i];
}
}
if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
aiMatrix4x4 mWorldIT = mat;
mWorldIT.Inverse().Transpose();
// TODO: implement Inverse() for aiMatrix3x3
aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
if (mesh->HasNormals()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mNormals[i] = m * mesh->mNormals[i];
}
}
if (mesh->HasTangentsAndBitangents()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mTangents[i] = m * mesh->mTangents[i];
mesh->mBitangents[i] = m * mesh->mBitangents[i];
}
}
}
}
}
// ------------------------------------------------------------------------------------------------
// Simple routine to build meshes in worldspace, no further optimization
void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in,
unsigned int numIn, aiNode* node)
{
// NOTE:
// aiMesh::mNumBones store original source mesh, or 0xffffffff if not a copy
// aiMesh::mBones store reference to abs. transform we multiplied with
// process meshes
for (unsigned int i = 0; i < node->mNumMeshes;++i) {
aiMesh* mesh = in[node->mMeshes[i]];
// check whether we can operate on this mesh
if (!mesh->mBones || *reinterpret_cast<aiMatrix4x4*>(mesh->mBones) == node->mTransformation) {
// yes, we can.
mesh->mBones = reinterpret_cast<aiBone**> (&node->mTransformation);
mesh->mNumBones = 0xffffffff;
}
else {
// try to find us in the list of newly created meshes
for (unsigned int n = 0; n < out.size(); ++n) {
aiMesh* ctz = out[n];
if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4*>(ctz->mBones) == node->mTransformation) {
// ok, use this one. Update node mesh index
node->mMeshes[i] = numIn + n;
}
}
if (node->mMeshes[i] < numIn) {
// Worst case. Need to operate on a full copy of the mesh
DefaultLogger::get()->info("PretransformVertices: Copying mesh due to mismatching transforms");
aiMesh* ntz;
const unsigned int tmp = mesh->mNumBones; //
mesh->mNumBones = 0;
SceneCombiner::Copy(&ntz,mesh);
mesh->mNumBones = tmp;
ntz->mNumBones = node->mMeshes[i];
ntz->mBones = reinterpret_cast<aiBone**> (&node->mTransformation);
out.push_back(ntz);
}
}
}
// call children
for (unsigned int i = 0; i < node->mNumChildren;++i)
BuildWCSMeshes(out,in,numIn,node->mChildren[i]);
}
// ------------------------------------------------------------------------------------------------
// Reset transformation matrices to identity
void PretransformVertices::MakeIdentityTransform(aiNode* nd)
{
nd->mTransformation = aiMatrix4x4();
// call children
for (unsigned int i = 0; i < nd->mNumChildren;++i)
MakeIdentityTransform(nd->mChildren[i]);
}
// ------------------------------------------------------------------------------------------------
// Build reference counters for all meshes
void PretransformVertices::BuildMeshRefCountArray(aiNode* nd, unsigned int * refs)
{
for (unsigned int i = 0; i< nd->mNumMeshes;++i)
refs[nd->mMeshes[i]]++;
// call children
for (unsigned int i = 0; i < nd->mNumChildren;++i)
BuildMeshRefCountArray(nd->mChildren[i],refs);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void PretransformVertices::Execute( aiScene* pScene)
{
DefaultLogger::get()->debug("PretransformVerticesProcess begin");
// Return immediately if we have no meshes
if (!pScene->mNumMeshes)
return;
const unsigned int iOldMeshes = pScene->mNumMeshes;
const unsigned int iOldAnimationChannels = pScene->mNumAnimations;
const unsigned int iOldNodes = CountNodes(pScene->mRootNode);
@ -271,11 +439,10 @@ void PretransformVertices::Execute( aiScene* pScene)
// first compute absolute transformation matrices for all nodes
ComputeAbsoluteTransform(pScene->mRootNode);
// delete aiMesh::mBones for all meshes. The bones are
// Delete aiMesh::mBones for all meshes. The bones are
// removed during this step and we need the pointer as
// temporary storage
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
for (unsigned int i = 0; i < pScene->mNumMeshes;++i) {
aiMesh* mesh = pScene->mMeshes[i];
for (unsigned int a = 0; a < mesh->mNumBones;++a)
@ -287,50 +454,123 @@ void PretransformVertices::Execute( aiScene* pScene)
// now build a list of output meshes
std::vector<aiMesh*> apcOutMeshes;
apcOutMeshes.reserve(pScene->mNumMaterials<<1u);
std::list<unsigned int> aiVFormats;
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
// get the list of all vertex formats for this material
aiVFormats.clear();
GetVFormatList(pScene,i,aiVFormats);
aiVFormats.sort();
aiVFormats.unique();
for (std::list<unsigned int>::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) {
unsigned int iVertices = 0;
unsigned int iFaces = 0;
CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices);
if (iFaces && iVertices)
{
apcOutMeshes.push_back(new aiMesh());
aiMesh* pcMesh = apcOutMeshes.back();
pcMesh->mNumFaces = iFaces;
pcMesh->mNumVertices = iVertices;
pcMesh->mFaces = new aiFace[iFaces];
pcMesh->mVertices = new aiVector3D[iVertices];
pcMesh->mMaterialIndex = i;
if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices];
if ((*j) & 0x4)
{
pcMesh->mTangents = new aiVector3D[iVertices];
pcMesh->mBitangents = new aiVector3D[iVertices];
}
iFaces = 0;
while ((*j) & (0x100 << iFaces))
{
pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3;
else pcMesh->mNumUVComponents[iFaces] = 2;
iFaces++;
}
iFaces = 0;
while ((*j) & (0x1000000 << iFaces))
pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
// fill the mesh ...
unsigned int aiTemp[2] = {0,0};
CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp);
// Keep scene hierarchy? It's an easy job in this case ...
// we go on and transform all meshes, if one is referenced by nodes
// with different absolute transformations a depth copy of the mesh
// is required.
if( configKeepHierarchy ) {
// Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
BuildWCSMeshes(apcOutMeshes,pScene->mMeshes,pScene->mNumMeshes, pScene->mRootNode);
// ... if new meshes have been generated, append them to the end of the scene
if (apcOutMeshes.size() > 0) {
aiMesh** npp = new aiMesh*[pScene->mNumMeshes + apcOutMeshes.size()];
::memcpy(npp,pScene->mMeshes,sizeof(aiMesh*)*pScene->mNumMeshes);
::memcpy(npp+pScene->mNumMeshes,&apcOutMeshes[0],sizeof(aiMesh*)*apcOutMeshes.size());
pScene->mNumMeshes += apcOutMeshes.size();
delete[] pScene->mMeshes; pScene->mMeshes = npp;
}
// now iterate through all meshes and transform them to worldspace
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
ApplyTransform(pScene->mMeshes[i],*reinterpret_cast<aiMatrix4x4*>( pScene->mMeshes[i]->mBones ));
// prevent improper destruction
pScene->mMeshes[i]->mBones = NULL;
pScene->mMeshes[i]->mNumBones = 0;
}
}
else {
apcOutMeshes.reserve(pScene->mNumMaterials<<1u);
std::list<unsigned int> aiVFormats;
std::vector<unsigned int> s(pScene->mNumMeshes,0);
BuildMeshRefCountArray(pScene->mRootNode,&s[0]);
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
// get the list of all vertex formats for this material
aiVFormats.clear();
GetVFormatList(pScene,i,aiVFormats);
aiVFormats.sort();
aiVFormats.unique();
for (std::list<unsigned int>::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) {
unsigned int iVertices = 0;
unsigned int iFaces = 0;
CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices);
if (0 != iFaces && 0 != iVertices)
{
apcOutMeshes.push_back(new aiMesh());
aiMesh* pcMesh = apcOutMeshes.back();
pcMesh->mNumFaces = iFaces;
pcMesh->mNumVertices = iVertices;
pcMesh->mFaces = new aiFace[iFaces];
pcMesh->mVertices = new aiVector3D[iVertices];
pcMesh->mMaterialIndex = i;
if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices];
if ((*j) & 0x4)
{
pcMesh->mTangents = new aiVector3D[iVertices];
pcMesh->mBitangents = new aiVector3D[iVertices];
}
iFaces = 0;
while ((*j) & (0x100 << iFaces))
{
pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3;
else pcMesh->mNumUVComponents[iFaces] = 2;
iFaces++;
}
iFaces = 0;
while ((*j) & (0x1000000 << iFaces))
pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
// fill the mesh ...
unsigned int aiTemp[2] = {0,0};
CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp,&s[0]);
}
}
}
// now delete all meshes in the scene and build a new mesh list
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
aiMesh* mesh = pScene->mMeshes[i];
mesh->mNumBones = 0;
mesh->mBones = NULL;
// we're reusing the face index arrays. avoid destruction
for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
mesh->mFaces[a].mNumIndices = 0;
mesh->mFaces[a].mIndices = NULL;
}
delete mesh;
// Invalidate the contents of the old mesh array. We will most
// likely have less output meshes now, so the last entries of
// the mesh array are not overridden. We set them to NULL to
// make sure the developer gets notified when his application
// attempts to access these fields ...
mesh = NULL;
}
// If no meshes are referenced in the node graph it is possible that we get no output meshes.
if (apcOutMeshes.empty()) {
throw new ImportErrorException("No output meshes: all meshes are orphaned and are not referenced by nodes");
}
else
{
// It is impossible that we have more output meshes than
// input meshes, so we can easily reuse the old mesh array
pScene->mNumMeshes = (unsigned int)apcOutMeshes.size();
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pScene->mMeshes[i] = apcOutMeshes[i];
}
}
// remove all animations from the scene
@ -341,40 +581,6 @@ void PretransformVertices::Execute( aiScene* pScene)
pScene->mAnimations = NULL;
pScene->mNumAnimations = 0;
// now delete all meshes in the scene and build a new mesh list
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
pScene->mMeshes[i]->mBones = NULL;
delete pScene->mMeshes[i];
// invalidate the contents of the old mesh array. We will most
// likely have less output meshes now, so the last entries of
// the mesh array are not overridden. We set them to NULL to
// make sure the developer gets notified when his application
// attempts to access these fields ...
pScene->mMeshes[i] = NULL;
}
// If no meshes are referenced in the node graph it is
// possible that we get no output meshes. However, this
// is OK if we had no input meshes, too
if (apcOutMeshes.empty())
{
if (pScene->mNumMeshes)
{
throw new ImportErrorException("No output meshes: all meshes are orphaned "
"and have no node references");
}
}
else
{
// It is impossible that we have more output meshes than
// input meshes, so we can easily reuse the old mesh array
pScene->mNumMeshes = (unsigned int)apcOutMeshes.size();
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pScene->mMeshes[i] = apcOutMeshes[i];
}
// --- we need to keep all cameras and lights
for (unsigned int i = 0; i < pScene->mNumCameras;++i)
{
@ -401,51 +607,58 @@ void PretransformVertices::Execute( aiScene* pScene)
l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection;
}
// now delete all nodes in the scene and build a new
// flat node graph with a root node and some level 1 children
delete pScene->mRootNode;
pScene->mRootNode = new aiNode();
pScene->mRootNode->mName.Set("<dummy_root>");
if( !configKeepHierarchy ) {
if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras)
{
pScene->mRootNode->mNumMeshes = 1;
pScene->mRootNode->mMeshes = new unsigned int[1];
pScene->mRootNode->mMeshes[0] = 0;
// now delete all nodes in the scene and build a new
// flat node graph with a root node and some level 1 children
delete pScene->mRootNode;
pScene->mRootNode = new aiNode();
pScene->mRootNode->mName.Set("<dummy_root>");
if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras)
{
pScene->mRootNode->mNumMeshes = 1;
pScene->mRootNode->mMeshes = new unsigned int[1];
pScene->mRootNode->mMeshes[0] = 0;
}
else
{
pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras;
aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
// generate mesh nodes
for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes)
{
aiNode* pcNode = *nodes = new aiNode();
pcNode->mParent = pScene->mRootNode;
pcNode->mName.length = ::sprintf(pcNode->mName.data,"mesh_%i",i);
// setup mesh indices
pcNode->mNumMeshes = 1;
pcNode->mMeshes = new unsigned int[1];
pcNode->mMeshes[0] = i;
}
// generate light nodes
for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes)
{
aiNode* pcNode = *nodes = new aiNode();
pcNode->mParent = pScene->mRootNode;
pcNode->mName.length = ::sprintf(pcNode->mName.data,"light_%i",i);
pScene->mLights[i]->mName = pcNode->mName;
}
// generate camera nodes
for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes)
{
aiNode* pcNode = *nodes = new aiNode();
pcNode->mParent = pScene->mRootNode;
pcNode->mName.length = ::sprintf(pcNode->mName.data,"cam_%i",i);
pScene->mCameras[i]->mName = pcNode->mName;
}
}
}
else
{
pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras;
aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
// generate mesh nodes
for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes)
{
aiNode* pcNode = *nodes = new aiNode();
pcNode->mParent = pScene->mRootNode;
pcNode->mName.length = ::sprintf(pcNode->mName.data,"mesh_%i",i);
// setup mesh indices
pcNode->mNumMeshes = 1;
pcNode->mMeshes = new unsigned int[1];
pcNode->mMeshes[0] = i;
}
// generate light nodes
for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes)
{
aiNode* pcNode = *nodes = new aiNode();
pcNode->mParent = pScene->mRootNode;
pcNode->mName.length = ::sprintf(pcNode->mName.data,"light_%i",i);
pScene->mLights[i]->mName = pcNode->mName;
}
// generate camera nodes
for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes)
{
aiNode* pcNode = *nodes = new aiNode();
pcNode->mParent = pScene->mRootNode;
pcNode->mName.length = ::sprintf(pcNode->mName.data,"cam_%i",i);
pScene->mCameras[i]->mName = pcNode->mName;
}
else {
// ... and finally set the transformation matrix of all nodes to identity
MakeIdentityTransform(pScene->mRootNode);
}
// print statistics
@ -467,7 +680,5 @@ void PretransformVertices::Execute( aiScene* pScene)
iOldMeshes,pScene->mNumMeshes);
DefaultLogger::get()->info(buffer);
}
return;
}

View File

@ -38,16 +38,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Defines a post processing step to pretransform all
vertices in the scenegraph */
/** @file PretransformVertices.h
* @brief Defines a post processing step to pretransform all
* vertices in the scenegraph
*/
#ifndef AI_PRETRANSFORMVERTICES_H_INC
#define AI_PRETRANSFORMVERTICES_H_INC
#include "BaseProcess.h"
#include "../include/aiMesh.h"
namespace Assimp
{
class PretransformVerticesTest;
namespace Assimp {
// ---------------------------------------------------------------------------
/** The PretransformVertices pretransforms all vertices in the nodegraph
@ -57,6 +59,7 @@ namespace Assimp
class ASSIMP_API PretransformVertices : public BaseProcess
{
friend class Importer;
friend class ::PretransformVerticesTest;
protected:
/** Constructor to be privately used by Importer */
@ -66,21 +69,96 @@ protected:
~PretransformVertices ();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
// Check whether step is active
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
// Execute step on a given scene
void Execute( aiScene* pScene);
// -------------------------------------------------------------------
// Setup import settings
void SetupProperties(const Importer* pImp);
// -------------------------------------------------------------------
/** @brief Toggle the 'keep hierarchy' option
* @param d hm ... difficult to guess what this means, hu!?
*/
void KeepHierarchy(bool d) {
configKeepHierarchy = d;
}
// -------------------------------------------------------------------
/** @brief Check whether 'keep hierarchy' is currently enabled.
* @return ...
*/
bool IsHierarchyKept() const {
return configKeepHierarchy;
}
private:
// -------------------------------------------------------------------
// Count the number of nodes
unsigned int CountNodes( aiNode* pcNode );
// -------------------------------------------------------------------
// Get a bitwise combination identifying the vertex format of a mesh
unsigned int GetMeshVFormat(aiMesh* pcMesh);
// -------------------------------------------------------------------
// Count the number of vertices in the whole scene and a given
// material index
void CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode,
unsigned int iMat,
unsigned int iVFormat,
unsigned int* piFaces,
unsigned int* piVertices);
// -------------------------------------------------------------------
// Collect vertex/face data
void CollectData( aiScene* pcScene, aiNode* pcNode,
unsigned int iMat,
unsigned int iVFormat,
aiMesh* pcMeshOut,
unsigned int aiCurrent[2],
unsigned int* num_refs);
// -------------------------------------------------------------------
// Get a list of all vertex formats that occur for a given material
// The output list contains duplicate elements
void GetVFormatList( aiScene* pcScene, unsigned int iMat,
std::list<unsigned int>& aiOut);
// -------------------------------------------------------------------
// Compute the absolute transformation matrices of each node
void ComputeAbsoluteTransform( aiNode* pcNode );
// -------------------------------------------------------------------
// Simple routine to build meshes in worldspace, no further optimization
void BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in,
unsigned int numIn, aiNode* node);
// -------------------------------------------------------------------
// Apply the node transformation to a mesh
void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat);
// -------------------------------------------------------------------
// Reset transformation matrices to identity
void MakeIdentityTransform(aiNode* nd);
// -------------------------------------------------------------------
// Build reference counters for all meshes
void BuildMeshRefCountArray(aiNode* nd, unsigned int * refs);
//! Configuration option: keep scene hierarchy as long as possible
bool configKeepHierarchy;
};
} // end of namespace Assimp

View File

@ -46,11 +46,144 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SpatialSort.h"
#include "BaseProcess.h"
// -------------------------------------------------------------------------------
// Some extensions to std namespace. Mainly std::min and std::max for all
// flat data types in the aiScene. They're used to quickly determine the
// min/max bounds of data arrays.
#ifdef __cplusplus
namespace std {
// std::min for aiVector3D
inline ::aiVector3D min (const ::aiVector3D& a, const ::aiVector3D& b) {
return ::aiVector3D (min(a.x,b.x),min(a.y,b.y),min(a.z,b.z));
}
// std::max for aiVector3D
inline ::aiVector3D max (const ::aiVector3D& a, const ::aiVector3D& b) {
return ::aiVector3D (max(a.x,b.x),max(a.y,b.y),max(a.z,b.z));
}
// std::min for aiColor4D
inline ::aiColor4D min (const ::aiColor4D& a, const ::aiColor4D& b) {
return ::aiColor4D (min(a.r,b.r),min(a.g,b.g),min(a.b,b.b),min(a.a,b.a));
}
// std::max for aiColor4D
inline ::aiColor4D max (const ::aiColor4D& a, const ::aiColor4D& b) {
return ::aiColor4D (max(a.r,b.r),max(a.g,b.g),max(a.b,b.b),max(a.a,b.a));
}
// std::min for aiQuaternion
inline ::aiQuaternion min (const ::aiQuaternion& a, const ::aiQuaternion& b) {
return ::aiQuaternion (min(a.w,b.w),min(a.x,b.x),min(a.y,b.y),min(a.z,b.z));
}
// std::max for aiQuaternion
inline ::aiQuaternion max (const ::aiQuaternion& a, const ::aiQuaternion& b) {
return ::aiQuaternion (max(a.w,b.w),max(a.x,b.x),max(a.y,b.y),max(a.z,b.z));
}
// std::min for aiVectorKey
inline ::aiVectorKey min (const ::aiVectorKey& a, const ::aiVectorKey& b) {
return ::aiVectorKey (min(a.mTime,b.mTime),min(a.mValue,b.mValue));
}
// std::max for aiVectorKey
inline ::aiVectorKey max (const ::aiVectorKey& a, const ::aiVectorKey& b) {
return ::aiVectorKey (max(a.mTime,b.mTime),max(a.mValue,b.mValue));
}
// std::min for aiQuatKey
inline ::aiQuatKey min (const ::aiQuatKey& a, const ::aiQuatKey& b) {
return ::aiQuatKey (min(a.mTime,b.mTime),min(a.mValue,b.mValue));
}
// std::max for aiQuatKey
inline ::aiQuatKey max (const ::aiQuatKey& a, const ::aiQuatKey& b) {
return ::aiQuatKey (max(a.mTime,b.mTime),max(a.mValue,b.mValue));
}
// std::min for aiVertexWeight
inline ::aiVertexWeight min (const ::aiVertexWeight& a, const ::aiVertexWeight& b) {
return ::aiVertexWeight (min(a.mVertexId,b.mVertexId),min(a.mWeight,b.mWeight));
}
// std::max for aiVertexWeight
inline ::aiVertexWeight max (const ::aiVertexWeight& a, const ::aiVertexWeight& b) {
return ::aiVertexWeight (max(a.mVertexId,b.mVertexId),max(a.mWeight,b.mWeight));
}
} // end namespace std
#endif // !! C++
namespace Assimp {
// some aliases to make the whole stuff easier to read
typedef std::pair < unsigned int,float > PerVertexWeight;
typedef std::vector < PerVertexWeight > VertexWeightTable;
// -------------------------------------------------------------------------------
// Start points for ArrayBounds<T> for all supported Ts
template <typename T>
struct MinMaxChooser;
template <> struct MinMaxChooser<float> {
void operator ()(float& min,float& max) {
max = -10e10f;
min = 10e10f;
}};
template <> struct MinMaxChooser<double> {
void operator ()(double& min,double& max) {
max = -10e10;
min = 10e10;
}};
template <> struct MinMaxChooser<unsigned int> {
void operator ()(unsigned int& min,unsigned int& max) {
max = 0;
min = (1u<<(sizeof(unsigned int)*8-1));
}};
template <> struct MinMaxChooser<aiVector3D> {
void operator ()(aiVector3D& min,aiVector3D& max) {
max = aiVector3D(-10e10f,-10e10f,-10e10f);
min = aiVector3D( 10e10f, 10e10f, 10e10f);
}};
template <> struct MinMaxChooser<aiColor4D> {
void operator ()(aiColor4D& min,aiColor4D& max) {
max = aiColor4D(-10e10f,-10e10f,-10e10f,-10e10f);
min = aiColor4D( 10e10f, 10e10f, 10e10f, 10e10f);
}};
template <> struct MinMaxChooser<aiQuaternion> {
void operator ()(aiQuaternion& min,aiQuaternion& max) {
max = aiQuaternion(-10e10f,-10e10f,-10e10f,-10e10f);
min = aiQuaternion( 10e10f, 10e10f, 10e10f, 10e10f);
}};
template <> struct MinMaxChooser<aiVectorKey> {
void operator ()(aiVectorKey& min,aiVectorKey& max) {
MinMaxChooser<double>()(min.mTime,max.mTime);
MinMaxChooser<aiVector3D>()(min.mValue,max.mValue);
}};
template <> struct MinMaxChooser<aiQuatKey> {
void operator ()(aiQuatKey& min,aiQuatKey& max) {
MinMaxChooser<double>()(min.mTime,max.mTime);
MinMaxChooser<aiQuaternion>()(min.mValue,max.mValue);
}};
template <> struct MinMaxChooser<aiVertexWeight> {
void operator ()(aiVertexWeight& min,aiVertexWeight& max) {
MinMaxChooser<unsigned int>()(min.mVertexId,max.mVertexId);
MinMaxChooser<float>()(min.mWeight,max.mWeight);
}};
// -------------------------------------------------------------------------------
// Find the min/max values of an array of Ts
template <typename T>
inline void ArrayBounds(const T* in, unsigned int size, T& min, T& max)
{
MinMaxChooser<T> ()(min,max);
for (unsigned int i = 0; i < size;++i) {
min = std::min(in[i],min);
max = std::max(in[i],max);
}
}
// -------------------------------------------------------------------------------
/** Little helper function to calculate the quadratic difference
@ -67,24 +200,6 @@ inline float GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pCol
return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a;
}
// -------------------------------------------------------------------------------
// Compute the AABB of a mesh
inline void FindAABB (const aiMesh* mesh, aiVector3D& min, aiVector3D& max)
{
min = aiVector3D (10e10f, 10e10f, 10e10f);
max = aiVector3D (-10e10f,-10e10f,-10e10f);
for (unsigned int i = 0;i < mesh->mNumVertices;++i)
{
const aiVector3D& v = mesh->mVertices[i];
min.x = ::std::min(v.x,min.x);
min.y = ::std::min(v.y,min.y);
min.z = ::std::min(v.z,min.z);
max.x = ::std::max(v.x,max.x);
max.y = ::std::max(v.y,max.y);
max.z = ::std::max(v.z,max.z);
}
}
// -------------------------------------------------------------------------------
// Compute the AABB of a mesh after applying a given transform
inline void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max,
@ -95,12 +210,8 @@ inline void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D
for (unsigned int i = 0;i < mesh->mNumVertices;++i)
{
const aiVector3D v = m * mesh->mVertices[i];
min.x = ::std::min(v.x,min.x);
min.y = ::std::min(v.y,min.y);
min.z = ::std::min(v.z,min.z);
max.x = ::std::max(v.x,max.x);
max.y = ::std::max(v.y,max.y);
max.z = ::std::max(v.z,max.z);
min = std::min(v,min);
max = std::max(v,max);
}
}
@ -108,7 +219,7 @@ inline void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D
// Helper function to determine the 'real' center of a mesh
inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max)
{
FindAABB(mesh,min,max);
ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max);
out = min + (max-min)*0.5f;
}
@ -146,7 +257,7 @@ inline float ComputePositionEpsilon(const aiMesh* pMesh)
// calculate the position bounds so we have a reliable epsilon to check position differences against
aiVector3D minVec, maxVec;
FindAABB(pMesh,minVec,maxVec);
ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec);
return (maxVec - minVec).Length() * epsilon;
}
@ -165,8 +276,10 @@ inline unsigned int GetMeshVFormatUnique(aiMesh* pcMesh)
// tangents and bitangents
if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4;
#ifdef BOOST_STATIC_ASSERT
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
#endif
// texture coordinates
unsigned int p = 0;
@ -184,6 +297,9 @@ inline unsigned int GetMeshVFormatUnique(aiMesh* pcMesh)
return iRet;
}
typedef std::pair <unsigned int,float> PerVertexWeight;
typedef std::vector <PerVertexWeight> VertexWeightTable;
// -------------------------------------------------------------------------------
// Compute a per-vertex bone weight table
// please .... delete result with operator delete[] ...
@ -205,13 +321,14 @@ inline VertexWeightTable* ComputeVertexBoneWeightTable(aiMesh* pMesh)
return avPerVertexWeights;
}
// -------------------------------------------------------------------------------
// Get a string for a given aiTextureType
inline const char* TextureTypeToString(aiTextureType in)
{
switch (in)
{
case aiTextureType_NONE:
return "n/a";
case aiTextureType_DIFFUSE:
return "Diffuse";
case aiTextureType_SPECULAR:
@ -237,7 +354,7 @@ inline const char* TextureTypeToString(aiTextureType in)
case aiTextureType_UNKNOWN:
return "Unknown";
default:
return "HUGE ERROR, please leave the room immediately and call the police";
return "HUGE ERROR. Expect BSOD (linux guys: kernel panic ...).";
}
}
@ -260,7 +377,7 @@ inline const char* MappingTypeToString(aiTextureMapping in)
case aiTextureMapping_OTHER:
return "Other";
default:
return "HUGE ERROR, please leave the room immediately and call the police";
return "HUGE ERROR. Expect BSOD (linux guys: kernel panic ...).";
}
}

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the Q3D importer class */
/** @file Q3DLoader.cpp
* @brief Implementation of the Q3D importer class
*/
#include "AssimpPCH.h"
#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
@ -54,30 +56,34 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
Q3DImporter::Q3DImporter()
{
}
{}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
Q3DImporter::~Q3DImporter()
{
}
{}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool Q3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool Q3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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;
const std::string extension = GetExtension(pFile);
std::string extension = pFile.substr( pos);
for (std::string::iterator it = extension.begin();
it != extension.end(); ++it)
*it = ::tolower(*it);
if (extension == "q3s" || extension == "q3o")
return true;
else if (!extension.length() || checkSig) {
if (!pIOHandler)
return true;
const char* tokens[] = {"quick3Do","quick3Ds"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,2);
}
return false;
}
return (extension == ".q3o" || extension == ".q3s");
// ------------------------------------------------------------------------------------------------
void Q3DImporter::GetExtensionList(std::string& append)
{
append.append("*.q3o;*.q3s");
}
// ------------------------------------------------------------------------------------------------
@ -91,11 +97,11 @@ void Q3DImporter::InternReadFile( const std::string& pFile,
if (stream.GetRemainingSize() < 22)
throw new ImportErrorException("File is either empty or corrupt: " + pFile);
// Check the file signature
// Check the file's signature
if (ASSIMP_strincmp( (const char*)stream.GetPtr(), "quick3Do", 8 ) &&
ASSIMP_strincmp( (const char*)stream.GetPtr(), "quick3Ds", 8 ))
{
throw new ImportErrorException("No Quick3D file. Signature is: " +
throw new ImportErrorException("Not a Quick3D file. Signature string is: " +
std::string((const char*)stream.GetPtr(),8));
}

View File

@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the Q3D importer class. */
/** @file Q3DLoader.h
* @brief Declaration of the Q3D importer class.
*/
#ifndef AI_Q3DLOADER_H_INCLUDED
#define AI_Q3DLOADER_H_INCLUDED
@ -67,7 +69,8 @@ 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;
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -75,10 +78,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.q3o;*.q3s");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the RAW importer class */
/** @file RawLoader.cpp
* @brief Implementation of the RAW importer class
*/
#include "AssimpPCH.h"
#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
@ -63,21 +65,16 @@ RAWImporter::~RAWImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool RAWImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool RAWImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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 SimpleExtensionCheck(pFile,"raw");
}
if (extension.length() < 4)return false;
if (extension[0] != '.')return false;
return !(extension.length() != 4 || extension[0] != '.' ||
extension[1] != 'r' && extension[1] != 'R' ||
extension[2] != 'a' && extension[2] != 'A' ||
extension[3] != 'w' && extension[3] != 'W');
// ------------------------------------------------------------------------------------------------
// Get the list of all supported file extensions
void RAWImporter::GetExtensionList(std::string& append)
{
append.append("*.raw");
}
// ------------------------------------------------------------------------------------------------

View File

@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the RAW importer class. */
/** @file RAWLoader.h
* @brief Declaration of the RAW importer class.
*/
#ifndef AI_RAWLOADER_H_INCLUDED
#define AI_RAWLOADER_H_INCLUDED
@ -66,8 +68,10 @@ 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;
* See BaseImporter::CanRead() for details.
*/
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -75,10 +79,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.raw");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -38,12 +38,14 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the "RemoveRedundantMaterials" post processing step
/** @file RemoveRedundantMaterials.cpp
* @brief Implementation of the "RemoveRedundantMaterials" post processing step
*/
// internal headers
#include "AssimpPCH.h"
#include "RemoveRedundantMaterials.h"
#include "ParsingUtils.h"
using namespace Assimp;
@ -68,6 +70,39 @@ bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const
return (pFlags & aiProcess_RemoveRedundantMaterials) != 0;
}
// ------------------------------------------------------------------------------------------------
// Setup import properties
void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp)
{
// Get value of AI_CONFIG_PP_RRM_EXCLUDE_LIST
configFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,"");
}
// ------------------------------------------------------------------------------------------------
// Extract single strings from a list of identifiers
void ConvertListToStrings(const std::string& in, std::list<std::string>& out)
{
const char* s = in.c_str();
while (*s) {
SkipSpacesAndLineEnd(&s);
if (*s == '\'') {
const char* base = ++s;
while (*s != '\'') {
++s;
if (*s == '\0') {
DefaultLogger::get()->error("RemoveRedundantMaterials: String list is ill-formatted");
return;
}
}
out.push_back(std::string(base,(size_t)(s-base)));
++s;
}
else {
out.push_back(GetNextToken(s));
}
}
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
@ -77,16 +112,49 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
unsigned int iCnt = 0, unreferenced = 0;
if (pScene->mNumMaterials)
{
// Find out which materials are referenced by meshes
std::vector<bool> abReferenced(pScene->mNumMaterials,false);
for (unsigned int i = 0;i < pScene->mNumMeshes;++i)
abReferenced[pScene->mMeshes[i]->mMaterialIndex] = true;
// If a list of materials to be excluded was given, match the list with
// our imported materials and 'salt' all positive matches to ensure that
// we get unique hashes later.
if (configFixedMaterials.length()) {
std::list<std::string> strings;
ConvertListToStrings(configFixedMaterials,strings);
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
aiMaterial* mat = pScene->mMaterials[i];
aiString name;
mat->Get(AI_MATKEY_NAME,name);
if (name.length) {
std::list<std::string>::const_iterator it = std::find(strings.begin(), strings.end(), name.data);
if (it != strings.end()) {
// Our brilliant 'salt': A single material property with ~ as first
// character to mark it as internal and temporary.
const int dummy = 1;
((MaterialHelper*)mat)->AddProperty(&dummy,1,"~RRM.UniqueMaterial",0,0);
// Keep this material even if no mesh references it
abReferenced[i] = true;
DefaultLogger::get()->debug(std::string("Found positive match in exclusion list: \'") + name.data + "\'");
}
}
}
}
// TODO: reimplement this algorithm to work in-place
unsigned int* aiMappingTable = new unsigned int[pScene->mNumMaterials];
unsigned int iNewNum = 0;
std::vector<bool> abReferenced(pScene->mNumMaterials,false);
for (unsigned int i = 0;i < pScene->mNumMeshes;++i)
abReferenced[pScene->mMeshes[i]->mMaterialIndex] = true;
// iterate through all materials and calculate a hash for them
// Iterate through all materials and calculate a hash for them
// store all hashes in a list and so a quick search whether
// we do already have a specific hash. This allows us to
// determine which materials are identical.
@ -136,8 +204,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
else ppcMaterials[idx] = pScene->mMaterials[p];
}
// update all material indices
for (unsigned int p = 0; p < pScene->mNumMeshes;++p)
{
for (unsigned int p = 0; p < pScene->mNumMeshes;++p) {
aiMesh* mesh = pScene->mMeshes[p];
mesh->mMaterialIndex = aiMappingTable[mesh->mMaterialIndex];
}

View File

@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Defines a post processing step to remove redundant materials */
/** @file RemoveRedundantMaterials.h
* @brief Defines a post processing step to remove redundant materials
*/
#ifndef AI_REMOVEREDUNDANTMATERIALS_H_INC
#define AI_REMOVEREDUNDANTMATERIALS_H_INC
@ -46,12 +48,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../include/aiMesh.h"
class RemoveRedundantMatsTest;
namespace Assimp
{
namespace Assimp {
// ---------------------------------------------------------------------------
/** RemoveRedundantMatsProcess: Class to remove redundant materials
*/
/** RemoveRedundantMatsProcess: Postprocessing steo to remove redundant
* materials from the imported scene.
*/
class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess
{
friend class Importer;
@ -66,19 +68,38 @@ protected:
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
// Check whether step is active
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
// Execute step on a given scene
void Execute( aiScene* pScene);
// -------------------------------------------------------------------
// Setup import settings
void SetupProperties(const Importer* pImp);
// -------------------------------------------------------------------
/** @brief Set list of fixed (unmutable) materials
* @param fixed See #AI_CONFIG_PP_RRM_EXCLUDE_LIST
*/
void SetFixedMaterialsString(const std::string& fixed = "") {
configFixedMaterials = fixed;
}
// -------------------------------------------------------------------
/** @brief Get list of fixed (unmutable) materials
* @return See #AI_CONFIG_PP_RRM_EXCLUDE_LIST
*/
const std::string& GetFixedMaterialsString() const {
return configFixedMaterials;
}
private:
//! Configuration option: list of all fixed materials
std::string configFixedMaterials;
};
} // end of namespace Assimp

View File

@ -51,15 +51,12 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
RemoveVCProcess::RemoveVCProcess()
{
}
{}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
RemoveVCProcess::~RemoveVCProcess()
{
// nothing to do here
}
{}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.

View File

@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the SMD importer class */
/** @file SMDLoader.cpp
* @brief Implementation of the SMD importer class
*/
#include "AssimpPCH.h"
#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
@ -54,56 +56,37 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
SMDImporter::SMDImporter()
{
// nothing to do here
}
{}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
SMDImporter::~SMDImporter()
{
// nothing to do here
}
{}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool SMDImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool SMDImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool) 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);
// fixme: auto format detection
return SimpleExtensionCheck(pFile,"smd","vta");
}
if (extension.length() < 4)return false;
if (extension[0] != '.')return false;
// VTA is not really supported as it contains vertex animations.
// However, at least the first keyframe can be loaded
if ((extension[1] != 's' && extension[1] != 'S') ||
(extension[2] != 'm' && extension[2] != 'M') ||
(extension[3] != 'd' && extension[3] != 'D'))
{
if ((extension[1] != 'v' && extension[1] != 'V') ||
(extension[2] != 't' && extension[2] != 'T') ||
(extension[3] != 'a' && extension[3] != 'A'))
{
return false;
}
}
return true;
// ------------------------------------------------------------------------------------------------
// Get a list of all supported file extensions
void SMDImporter::GetExtensionList(std::string& append)
{
append.append("*.smd;*.vta");
}
// ------------------------------------------------------------------------------------------------
// Setup configuration properties
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.
if(0xffffffff == (configFrameID = pImp->GetPropertyInteger(
AI_CONFIG_IMPORT_SMD_KEYFRAME,0xffffffff)))
{
configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_SMD_KEYFRAME,0xffffffff);
if(0xffffffff == configFrameID) {
configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
}
}

View File

@ -38,10 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
//!
//! @file Definition of SMD importer class
//!
/** @file SMDLoader.h
* @brief Defintion of the Valve SMD file format
*/
#ifndef AI_SMDLOADER_H_INCLUDED
#define AI_SMDLOADER_H_INCLUDED
@ -186,14 +185,16 @@ 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;
* See BaseImporter::CanRead() for details.
*/
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
// -------------------------------------------------------------------
/** Called prior to ReadFile().
* The function is a request to the importer to update its configuration
* basing on the Importer's configuration property list.
*/
* The function is a request to the importer to update its configuration
* basing on the Importer's configuration property list.
*/
void SetupProperties(const Importer* pImp);
protected:
@ -203,10 +204,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.smd;*.vta");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -64,27 +64,31 @@ STLImporter::~STLImporter()
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool STLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
bool STLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) 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);
const std::string extension = GetExtension(pFile);
if (extension.length() < 4)return false;
if (extension[0] != '.')return false;
if (extension[1] != 's' && extension[1] != 'S')return false;
if (extension[2] != 't' && extension[2] != 'T')return false;
if (extension[3] != 'l' && extension[3] != 'L')return false;
return true;
if (extension == "stl")
return true;
else if (!extension.length() || checkSig) {
if (!pIOHandler)
return true;
const char* tokens[] = {"STL","solid"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,2);
}
return false;
}
// ------------------------------------------------------------------------------------------------
void STLImporter::GetExtensionList(std::string& append)
{
append.append("*.stl");
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void STLImporter::InternReadFile(
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
@ -183,7 +187,7 @@ void STLImporter::LoadASCIIFile()
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
unsigned int curFace = 0, curVertex = 0;
unsigned int curFace = 0, curVertex = 3;
while (true)
{
// go to the next token

View File

@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the STL importer class. */
/** @file STLLoader.h
* Declaration of the STL importer class.
*/
#ifndef AI_STLLOADER_H_INCLUDED
#define AI_STLLOADER_H_INCLUDED
@ -48,7 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
// ---------------------------------------------------------------------------
/** Clas to load STL files
/** Importer class for the sterolithography STL file format
*/
class STLImporter : public BaseImporter
{
@ -65,8 +67,10 @@ 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;
* See BaseImporter::CanRead() for details.
*/
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
@ -74,10 +78,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.stl");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -74,40 +74,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
// ------------------------------------------------------------------------------------------------
/** This is a small helper data structure simplifying our work
*/
struct SceneHelper
{
SceneHelper ()
: scene (NULL)
, idlen (0)
{
id[0] = 0;
}
SceneHelper (aiScene* _scene)
: scene (_scene)
, idlen (0)
{
id[0] = 0;
}
AI_FORCE_INLINE aiScene* operator-> () const
{
return scene;
}
// scene we're working on
aiScene* scene;
// prefix to be added to all identifiers in the scene ...
char id [32];
// and its strlen()
unsigned int idlen;
};
// ------------------------------------------------------------------------------------------------
// Add a prefix to a string
inline void PrefixString(aiString& string,const char* prefix, unsigned int len)
@ -124,6 +90,21 @@ inline void PrefixString(aiString& string,const char* prefix, unsigned int len)
string.length += len;
}
// ------------------------------------------------------------------------------------------------
// Add node identifiers to a hashing set
void SceneCombiner::AddNodeHashes(aiNode* node, std::set<unsigned int>& hashes)
{
// Add node name to hashing set if it is non-empty - empty nodes are allowed
// and they can't have any anims assigned so its absolutely safe to duplicate them.
if (node->mName.length) {
hashes.insert( SuperFastHash(node->mName.data,node->mName.length) );
}
// Process all children recursively
for (unsigned int i = 0; i < node->mNumChildren;++i)
AddNodeHashes(node->mChildren[i],hashes);
}
// ------------------------------------------------------------------------------------------------
// Add a name prefix to all nodes in a hierarchy
void SceneCombiner::AddNodePrefixes(aiNode* node, const char* prefix, unsigned int len)
@ -136,6 +117,44 @@ void SceneCombiner::AddNodePrefixes(aiNode* node, const char* prefix, unsigned i
AddNodePrefixes(node->mChildren[i],prefix,len);
}
// ------------------------------------------------------------------------------------------------
// Search for matching names
bool SceneCombiner::FindNameMatch(const aiString& name, std::vector<SceneHelper>& input, unsigned int cur)
{
const unsigned int hash = SuperFastHash(name.data, name.length);
// Check whether we find a positive match in one of the given sets
for (unsigned int i = 0; i < input.size(); ++i) {
if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) {
return true;
}
}
return false;
}
// ------------------------------------------------------------------------------------------------
// Add a name prefix to all nodes in a hierarchy if a hash match is found
void SceneCombiner::AddNodePrefixesChecked(aiNode* node, const char* prefix, unsigned int len,
std::vector<SceneHelper>& input, unsigned int cur)
{
ai_assert(NULL != prefix);
const unsigned int hash = SuperFastHash(node->mName.data,node->mName.length);
// Check whether we find a positive match in one of the given sets
for (unsigned int i = 0; i < input.size(); ++i) {
if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) {
PrefixString(node->mName,prefix,len);
break;
}
}
// Process all children recursively
for (unsigned int i = 0; i < node->mNumChildren;++i)
AddNodePrefixesChecked(node->mChildren[i],prefix,len,input,cur);
}
// ------------------------------------------------------------------------------------------------
// Add an offset to all mesh indices in a node graph
void SceneCombiner::OffsetNodeMeshIndices (aiNode* node, unsigned int offset)
@ -300,6 +319,20 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
const unsigned int random = rndGen();
src[i].idlen = ::sprintf(src[i].id,"$%.6X$_",random);
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
// Compute hashes for all identifiers in this scene and store them
// in a sorted table (for convenience I'm using std::set). We hash
// just the node and animation channel names, all identifiers except
// the material names should be caught by doing this.
AddNodeHashes(src[i]->mRootNode,src[i].hashes);
for (unsigned int a = 0; a < src[i]->mNumAnimations;++a) {
aiAnimation* anim = src[i]->mAnimations[a];
src[i].hashes.insert(SuperFastHash(anim->mName.data,anim->mName.length));
}
}
}
}
@ -321,7 +354,11 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
dest->mNumAnimations += (*cur)->mNumAnimations;
// Combine the flags of all scenes
dest->mFlags |= (*cur)->mFlags;
// We need to process them flag-by-flag here to get correct results
// dest->mFlags ; //|= (*cur)->mFlags;
if ((*cur)->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
dest->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
}
}
// generate the output texture list + an offset table for all texture indices
@ -352,11 +389,10 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
// generate the output material list + an offset table for all material indices
if (dest->mNumMaterials)
{
{
aiMaterial** pip = dest->mMaterials = new aiMaterial*[dest->mNumMaterials];
cnt = 0;
for ( unsigned int n = 0; n < src.size();++n )
{
for ( unsigned int n = 0; n < src.size();++n ) {
SceneHelper* cur = &src[n];
for (unsigned int i = 0; i < (*cur)->mNumMaterials;++i)
{
@ -369,8 +405,7 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
}
else *pip = (*cur)->mMaterials[i];
if ((*cur)->mNumTextures != dest->mNumTextures)
{
if ((*cur)->mNumTextures != dest->mNumTextures) {
// We need to update all texture indices of the mesh. So we need to search for
// a material property called '$tex.file'
@ -391,8 +426,7 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
}
// Need to generate new, unique material names?
else if (!::strcmp( prop->mKey.data,"$mat.name" ) &&
flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES)
else if (!::strcmp( prop->mKey.data,"$mat.name" ) && flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES)
{
aiString* pcSrc = (aiString*) prop->mData;
PrefixString(*pcSrc, (*cur).id, (*cur).idlen);
@ -458,7 +492,7 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
aiAnimation** ppAnims = dest->mAnimations = (dest->mNumAnimations
? new aiAnimation*[dest->mNumAnimations] : NULL);
for ( unsigned int n = 0; n < src.size();++n )
for ( int n = src.size()-1; n >= 0 ;--n ) /* !!! important !!! */
{
SceneHelper* cur = &src[n];
aiNode* node;
@ -466,7 +500,9 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
// To offset or not to offset, this is the question
if (n != duplicates[n])
{
// Get full scenegraph copy
Copy( &node, (*cur)->mRootNode );
OffsetNodeMeshIndices(node,offset[duplicates[n]]);
if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
// (note:) they are already 'offseted' by offset[duplicates[n]]
@ -481,6 +517,30 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
if (n) // src[0] is the master node
nodes.push_back(NodeAttachmentInfo( node,srcList[n-1].attachToNode,n ));
// add name prefixes?
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
// or the whole scenegraph
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
AddNodePrefixesChecked(node,(*cur).id,(*cur).idlen,src,n);
}
else AddNodePrefixes(node,(*cur).id,(*cur).idlen);
// meshes
for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i) {
aiMesh* mesh = (*cur)->mMeshes[i];
// rename all bones
for (unsigned int a = 0; a < mesh->mNumBones;++a) {
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
if (!FindNameMatch(mesh->mBones[a]->mName,src,n))
continue;
}
PrefixString(mesh->mBones[a]->mName,(*cur).id,(*cur).idlen);
}
}
}
// --------------------------------------------------------------------
// Copy light sources
for (unsigned int i = 0; i < (*cur)->mNumLights;++i,++ppLights)
@ -490,6 +550,17 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
Copy(ppLights, (*cur)->mLights[i]);
}
else *ppLights = (*cur)->mLights[i];
// Add name prefixes?
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
if (!FindNameMatch((*ppLights)->mName,src,n))
continue;
}
PrefixString((*ppLights)->mName,(*cur).id,(*cur).idlen);
}
}
// --------------------------------------------------------------------
@ -500,6 +571,16 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
Copy(ppCameras, (*cur)->mCameras[i]);
}
else *ppCameras = (*cur)->mCameras[i];
// Add name prefixes?
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
if (!FindNameMatch((*ppCameras)->mName,src,n))
continue;
}
PrefixString((*ppCameras)->mName,(*cur).id,(*cur).idlen);
}
}
// --------------------------------------------------------------------
@ -510,30 +591,26 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
Copy(ppAnims, (*cur)->mAnimations[i]);
}
else *ppAnims = (*cur)->mAnimations[i];
}
}
for ( unsigned int n = 1; n < src.size();++n ) {
SceneHelper* cur = &src[n];
// --------------------------------------------------------------------
// Add prefixes
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES)
{
for (unsigned int i = 0; i < (*cur)->mNumLights;++i)
PrefixString(dest->mLights[i]->mName,(*cur).id,(*cur).idlen);
// Add name prefixes?
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
if (!FindNameMatch((*ppAnims)->mName,src,n))
continue;
}
for (unsigned int i = 0; i < (*cur)->mNumCameras;++i)
PrefixString(dest->mCameras[i]->mName,(*cur).id,(*cur).idlen);
for (unsigned int i = 0; i < (*cur)->mNumAnimations;++i) {
aiAnimation* anim = dest->mAnimations[i];
PrefixString(anim->mName,(*cur).id,(*cur).idlen);
PrefixString((*ppAnims)->mName,(*cur).id,(*cur).idlen);
// don't forget to update all node animation channels
for (unsigned int a = 0; a < anim->mNumChannels;++a)
PrefixString(anim->mChannels[a]->mNodeName,(*cur).id,(*cur).idlen);
for (unsigned int a = 0; a < (*ppAnims)->mNumChannels;++a) {
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
if (!FindNameMatch((*ppAnims)->mChannels[a]->mNodeName,src,n))
continue;
}
PrefixString((*ppAnims)->mChannels[a]->mNodeName,(*cur).id,(*cur).idlen);
}
}
AddNodePrefixes(nodes[n-1].node,(*cur).id,(*cur).idlen);
}
}
@ -585,6 +662,11 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
delete deleteMe;
}
// Check flags
if (!dest->mNumMeshes || !dest->mNumMaterials) {
dest->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
}
// We're finished
}
@ -605,10 +687,8 @@ void SceneCombiner::BuildUniqueBoneList(std::list<BoneWithHash>& asBones,
std::list<BoneWithHash>::iterator it2 = asBones.begin();
std::list<BoneWithHash>::iterator end2 = asBones.end();
for (;it2 != end2;++it2)
{
if ((*it2).first == itml)
{
for (;it2 != end2;++it2) {
if ((*it2).first == itml) {
(*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset));
break;
}
@ -666,10 +746,7 @@ void SceneCombiner::MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator
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");
DefaultLogger::get()->warn("Bones with equal names but different offset matrices can't be joined at the moment");
continue;
}
pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix;
@ -1026,7 +1103,7 @@ void SceneCombiner::Copy (aiAnimation** _dest, const aiAnimation* src)
::memcpy(dest,src,sizeof(aiAnimation));
// and reallocate all arrays
GetArrayCopy( dest->mChannels, dest->mNumChannels );
CopyPtrArray( dest->mChannels, src->mChannels, dest->mNumChannels );
}
// ------------------------------------------------------------------------------------------------

View File

@ -118,7 +118,7 @@ struct NodeAttachmentInfo
/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY
* Can be combined with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES.
* Unique names are generated, but only if this is absolutely
* required (if there would be conflicts otherwuse.)
* required to avoid name conflicts.
*/
#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY 0x10
@ -133,6 +133,43 @@ struct BoneWithHash : public std::pair<uint32_t,aiString*> {
};
// ---------------------------------------------------------------------------
/** @brief Utility for SceneCombiner
*/
struct SceneHelper
{
SceneHelper ()
: scene (NULL)
, idlen (0)
{
id[0] = 0;
}
SceneHelper (aiScene* _scene)
: scene (_scene)
, idlen (0)
{
id[0] = 0;
}
AI_FORCE_INLINE aiScene* operator-> () const
{
return scene;
}
// scene we're working on
aiScene* scene;
// prefix to be added to all identifiers in the scene ...
char id [32];
// and its strlen()
unsigned int idlen;
// hash table to quickly check whether a name is contained in the scene
std::set<unsigned int> hashes;
};
// ---------------------------------------------------------------------------
/** \brief Static helper class providing various utilities to merge two
* scenes. It is intended as internal utility and NOT for use by
@ -301,6 +338,26 @@ public:
// recursive, of course
static void Copy (aiNode** dest, const aiNode* src);
private:
// -------------------------------------------------------------------
// Same as AddNodePrefixes, but with an additional check
static void AddNodePrefixesChecked(aiNode* node, const char* prefix,
unsigned int len,
std::vector<SceneHelper>& input,
unsigned int cur);
// -------------------------------------------------------------------
// Add node identifiers to a hashing set
static void AddNodeHashes(aiNode* node, std::set<unsigned int>& hashes);
// -------------------------------------------------------------------
// Search for duplicate names
static bool FindNameMatch(const aiString& name,
std::vector<SceneHelper>& input, unsigned int cur);
};
}

View File

@ -64,26 +64,56 @@ void ScenePreprocessor::ProcessScene ()
// Generate a default material if none was specified
if (!scene->mNumMaterials && scene->mNumMeshes)
{
scene->mMaterials = new aiMaterial*[scene->mNumMaterials = 1];
MaterialHelper* helper = new MaterialHelper();
scene->mMaterials[0] = helper;
scene->mMaterials = new aiMaterial*[2];
MaterialHelper* helper;
// gray
aiColor3D clr(0.6f,0.6f,0.6f);
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
aiString name;
// add a small ambient color value
clr = aiColor3D(0.05f,0.05f,0.05f);
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
// Check whether there are meshes with at least one set of uv coordinates ... add a dummy texture for them
// meshes without texture coordinates receive a boring gray default material.
unsigned int mat0 = 0xffffffff, mat1 = 0xffffffff;
for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
if (scene->mMeshes[i]->mTextureCoords[0]) {
// setup the default name
aiString name(AI_DEFAULT_MATERIAL_NAME);
helper->AddProperty(&name,AI_MATKEY_NAME);
if (mat0 == 0xffffffff) {
scene->mMaterials[scene->mNumMaterials] = helper = new MaterialHelper();
for (unsigned int i = 0; i < scene->mNumMeshes;++i)
scene->mMeshes[i]->mMaterialIndex = 0;
// dummy texture
name.Set("texture.png");
helper->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(0));
DefaultLogger::get()->debug("ScenePreprocessor: Added default material \'" AI_DEFAULT_MATERIAL_NAME "\'");
// setup default name
name.Set(AI_DEFAULT_TEXTURED_MATERIAL_NAME);
helper->AddProperty(&name,AI_MATKEY_NAME);
mat0 = scene->mNumMaterials++;
DefaultLogger::get()->debug("ScenePreprocessor: Adding textured material \'" AI_DEFAULT_TEXTURED_MATERIAL_NAME "\'");
}
scene->mMeshes[i]->mMaterialIndex = mat0;
}
else
{
if (mat1 == 0xffffffff) {
scene->mMaterials[scene->mNumMaterials] = helper = new MaterialHelper();
// gray
aiColor3D clr(0.6f,0.6f,0.6f);
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
// add a small ambient color value
clr = aiColor3D(0.05f,0.05f,0.05f);
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
// setup the default name
name.Set(AI_DEFAULT_MATERIAL_NAME);
helper->AddProperty(&name,AI_MATKEY_NAME);
mat1 = scene->mNumMaterials++;
DefaultLogger::get()->debug("ScenePreprocessor: Adding grey material \'" AI_DEFAULT_MATERIAL_NAME "\'");
}
scene->mMeshes[i]->mMaterialIndex = mat1;
}
}
}
}
@ -96,8 +126,22 @@ void ScenePreprocessor::ProcessMesh (aiMesh* mesh)
if (!mesh->mTextureCoords[i])
mesh->mNumUVComponents[i] = 0;
else if( !mesh->mNumUVComponents[i])
mesh->mNumUVComponents[i] = 2;
else {
if( !mesh->mNumUVComponents[i])
mesh->mNumUVComponents[i] = 2;
// Ensure unsued components are zeroed. This will make 1D texture channels work
// as if they were 2D channels .. just in case an application doesn't handle
// this case
if (2 == mesh->mNumUVComponents[i]) {
for (aiVector3D* p = mesh->mTextureCoords[i], *end = p+mesh->mNumVertices; p != end; ++p)
p->z = 0.f;
}
else if (1 == mesh->mNumUVComponents[i]) {
for (aiVector3D* p = mesh->mTextureCoords[i], *end = p+mesh->mNumVertices; p != end; ++p)
p->z = p->y = 0.f;
}
}
}
// If the information which primitive types are there in the
@ -127,6 +171,15 @@ void ScenePreprocessor::ProcessMesh (aiMesh* mesh)
}
}
}
// If tangents and normals are given but no bitangents compute them
if (mesh->mTangents && mesh->mNormals && !mesh->mBitangents)
{
mesh->mBitangents = new aiVector3D[mesh->mNumVertices];
for (unsigned int i = 0; i < mesh->mNumVertices;++i) {
mesh->mBitangents[i] = mesh->mNormals[i] ^ mesh->mTangents[i];
}
}
}
// ---------------------------------------------------------------------------------------------

View File

@ -120,11 +120,12 @@ void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode)
mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8));
mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11));
}
} else
}
else
{
// if the node has no children, it's an end node. Put a little knob there instead
aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4);
float sizeEstimate = ownpos.Length() * 0.2f;
float sizeEstimate = ownpos.Length() * 0.18f;
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));

View File

@ -303,7 +303,7 @@ void SortByPTypeProcess::Execute( aiScene* pScene)
if (vert)
{
*vert++ = mesh->mVertices[idx];
//mesh->mVertices[idx].x = std::numeric_limits<float>::quiet_NaN();
//mesh->mVertices[idx].x = get_qnan();
}
if (nor )*nor++ = mesh->mNormals[idx];
if (tan )

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