General
- 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-9d2fd5bffc1fpull/1/head
parent
caaf02b0e4
commit
4bbc03332b
37
INSTALL
37
INSTALL
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
cd code
|
||||
mingw32-make -f makefile.mingw clean
|
||||
|
||||
cd ..
|
||||
del /Q /S obj bin lib
|
||||
|
|
@ -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,12 +437,7 @@ 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;
|
||||
}
|
||||
else*/ // if (pcIn->mName != "$$$DUMMY")
|
||||
{
|
||||
|
||||
// Find all meshes with the same name as the node
|
||||
for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
|
||||
{
|
||||
|
@ -457,40 +451,53 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
|||
{
|
||||
// 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;
|
||||
D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]);
|
||||
|
||||
// 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)
|
||||
{
|
||||
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
|
||||
// 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;
|
||||
aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
|
||||
|
||||
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 != pvEnd;++pvCurrent)
|
||||
{
|
||||
*pvCurrent = mInv * (*pvCurrent);
|
||||
for (pvCurrent = mesh->mVertices;pvCurrent != pvEnd;++pvCurrent) {
|
||||
*pvCurrent -= pivot;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (;pvCurrent != pvEnd;++pvCurrent)
|
||||
*pvCurrent = mInv * (*pvCurrent);
|
||||
}
|
||||
|
||||
// Setup the mesh index
|
||||
pcOut->mMeshes[i] = iIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the name of the node
|
||||
pcOut->mName.Set(pcIn->mName);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -54,6 +58,8 @@ using namespace Assimp;
|
|||
// - Reads the current chunk and validates it
|
||||
// - computes its length
|
||||
#define ASSIMP_3DS_BEGIN_CHUNK() \
|
||||
if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)) \
|
||||
return; \
|
||||
Discreet3DS::Chunk chunk; \
|
||||
ReadChunk(&chunk); \
|
||||
int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \
|
||||
|
@ -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)
|
||||
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;
|
||||
std::string extension = pFile.substr( pos);
|
||||
for (std::string::iterator i = extension.begin(); i != extension.end();++i)
|
||||
*i = ::tolower(*i);
|
||||
}
|
||||
|
||||
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,8 +389,8 @@ 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();
|
||||
// Now check for further subchunks
|
||||
if (!bIsPrj) /* fixme */
|
||||
ParseLightChunk();
|
||||
|
||||
// The specular light color is identical the the diffuse light
|
||||
|
@ -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;
|
||||
|
|
|
@ -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,15 +51,13 @@ 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
|
||||
{
|
||||
|
@ -74,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;
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Called prior to ReadFile().
|
||||
|
@ -90,10 +92,7 @@ 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.
|
||||
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
// check file extension
|
||||
const std::string extension = GetExtension(pFile);
|
||||
|
||||
if( extension == "ase" || extension == "ask")
|
||||
return true;
|
||||
|
||||
if ((!extension.length() || cs) && pIOHandler) {
|
||||
const char* tokens[] = {"*3dsmax_asciiexport"};
|
||||
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string extension = pFile.substr( pos);
|
||||
|
||||
// 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;
|
||||
|
|
|
@ -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,14 +50,15 @@ 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
|
||||
{
|
||||
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
return ( extension == ".bvh");
|
||||
if( extension == "bvh")
|
||||
return true;
|
||||
|
||||
if ((!extension.length() || cs) && pIOHandler) {
|
||||
const char* tokens[] = {"HIERARCHY"};
|
||||
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,8 +101,20 @@ protected:
|
|||
public:
|
||||
// -------------------------------------------------------------------
|
||||
/** Returns whether the class can handle the format of the given file.
|
||||
*
|
||||
* 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
|
||||
|
@ -106,7 +122,7 @@ public:
|
|||
* 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;
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
@ -135,8 +151,9 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
@ -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;
|
||||
|
@ -170,22 +187,35 @@ protected:
|
|||
* 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.
|
||||
* <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,6 +248,47 @@ 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
|
||||
|
@ -266,11 +337,19 @@ 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
|
||||
*/
|
||||
BatchLoader(IOSystem* pIO);
|
||||
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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++)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
std::string extension = GetExtension(pFile);
|
||||
|
||||
if( extension == ".dae")
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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,16 +71,35 @@ 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)
|
||||
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)
|
||||
{
|
||||
aiMesh* mesh = pScene->mMeshes[i];
|
||||
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)
|
||||
{
|
||||
|
@ -90,30 +109,34 @@ void FindDegeneratesProcess::Execute( aiScene* pScene)
|
|||
// 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)
|
||||
for (register unsigned int t = i+1; t < face.mNumIndices; ++t)
|
||||
{
|
||||
if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[a]])
|
||||
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
|
||||
for (unsigned int m = a; m < face.mNumIndices-1; ++m)
|
||||
--face.mNumIndices;
|
||||
for (unsigned int m = t; m < face.mNumIndices; ++m)
|
||||
{
|
||||
face.mIndices[m] = face.mIndices[m+1];
|
||||
}
|
||||
--a;
|
||||
--face.mNumIndices;
|
||||
--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!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +164,47 @@ void FindDegeneratesProcess::Execute( aiScene* pScene)
|
|||
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];
|
||||
|
@ -149,5 +212,3 @@ void FindDegeneratesProcess::Execute( aiScene* pScene)
|
|||
DefaultLogger::get()->warn(std::string("Found ") + s + " degenerated primitives");
|
||||
}
|
||||
}
|
||||
DefaultLogger::get()->debug("FindDegeneratesProcess finished");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -67,19 +67,28 @@ 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");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
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 == ".irr")return true;
|
||||
else if (extension == ".xml")
|
||||
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()
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,15 +704,28 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!imp)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
|
@ -652,6 +733,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
|
|||
DefaultLogger::get()->error(mErrorString);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch the reading to the worker class for this format
|
||||
DefaultLogger::get()->info("Found a matching importer for this file 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);
|
||||
|
|
|
@ -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. ");
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// 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,24 +124,26 @@ 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));
|
||||
float fACMR = 3.f;
|
||||
const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces;
|
||||
|
||||
// Input ACMR is for logging purposes only
|
||||
if (!DefaultLogger::isNullLogger())
|
||||
{
|
||||
unsigned int* piFIFOStack = new unsigned int[configCacheDepth];
|
||||
::memset(piFIFOStack,0xff,configCacheDepth*sizeof(unsigned int));
|
||||
unsigned int* piCur = piFIFOStack;
|
||||
const unsigned int* const piCurEnd = piFIFOStack + this->configCacheDepth;
|
||||
const unsigned int* const piCurEnd = piFIFOStack + configCacheDepth;
|
||||
|
||||
// count the number of cache misses
|
||||
unsigned int iCacheMisses = 0;
|
||||
|
||||
const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces;
|
||||
for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace)
|
||||
{
|
||||
for (unsigned int qq = 0; qq < 3;++qq)
|
||||
|
@ -134,8 +151,7 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
|
|||
bool bInCache = false;
|
||||
for (unsigned int* pp = piFIFOStack;pp < piCurEnd;++pp)
|
||||
{
|
||||
if (*pp == pcFace->mIndices[qq])
|
||||
{
|
||||
if (*pp == pcFace->mIndices[qq]) {
|
||||
// the vertex is in cache
|
||||
bInCache = true;
|
||||
break;
|
||||
|
@ -150,11 +166,8 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
|
|||
}
|
||||
}
|
||||
delete[] piFIFOStack;
|
||||
float fACMR = (float)iCacheMisses / pMesh->mNumFaces;
|
||||
if (3.0 == fACMR)
|
||||
{
|
||||
if (!DefaultLogger::isNullLogger())
|
||||
{
|
||||
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())
|
||||
{
|
||||
fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
|
||||
|
||||
// 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
|
||||
float 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);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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++)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
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,15 +229,21 @@ 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;
|
||||
|
||||
if( (uv.mTexCoords[0] - v.mTexCoords[0]).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
|
||||
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)
|
||||
|
@ -218,16 +252,15 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
|||
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;
|
||||
}
|
||||
|
||||
// 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]];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
@ -475,6 +492,7 @@ struct Clip
|
|||
Clip()
|
||||
: 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
|
||||
|
|
|
@ -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;
|
||||
|
||||
// Initialize some members with their default values
|
||||
hasNamedLayer = false;
|
||||
|
||||
// create temporary storage on the stack but store pointers to it in the class
|
||||
// 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,17 +726,31 @@ 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
|
||||
|
@ -737,11 +760,10 @@ void LWOImporter::LoadLWO2Polygons(unsigned int length)
|
|||
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,6 +1359,13 @@ void LWOImporter::LoadLWO2File()
|
|||
LoadLWO2Clip(head->length);
|
||||
break;
|
||||
}
|
||||
|
||||
// envelope chunk
|
||||
case AI_LWO_ENVL:
|
||||
{
|
||||
LoadLWO2Envelope(head->length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mFileBuffer = next;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
// 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;
|
||||
|
||||
std::string extension = pFile.substr( pos);
|
||||
for (std::string::iterator i = extension.begin(); i != extension.end();++i)
|
||||
*i = ::tolower(*i);
|
||||
|
||||
return extension == ".lws";
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
157
code/LWSLoader.h
157
code/LWSLoader.h
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
const std::string extension = GetExtension(pFile);
|
||||
if (extension == "md2")
|
||||
return true;
|
||||
|
||||
std::string extension = pFile.substr( pos);
|
||||
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
|
||||
*it = tolower( *it);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_RESOLVE_CROSS_ATTACHMENTS |
|
||||
(!configSpeedFlag ? AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY : 0));
|
||||
|
||||
return true;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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[1] != 'm' && extension[1] != 'M')return false;
|
||||
if (extension[2] != 'd' && extension[2] != 'D')return false;
|
||||
if (extension[3] != '5')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;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// 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
|
||||
|
|
|
@ -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,10 +84,7 @@ 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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -39,22 +39,21 @@ 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 {
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
/** 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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
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;
|
||||
|
||||
const std::string extension = GetExtension(pFile);
|
||||
if (extension == "mdc")
|
||||
return true;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MDCImporter::GetExtensionList(std::string& append)
|
||||
{
|
||||
append.append("*.mdc");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
const std::string extension = GetExtension(pFile);
|
||||
if (extension == "mdl" )
|
||||
return true;
|
||||
|
||||
// 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;
|
||||
|
||||
std::string extension = pFile.substr( pos);
|
||||
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
|
||||
*it = tolower( *it);
|
||||
|
||||
return extension == ".mdl";
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// 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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,19 +66,25 @@ 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");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -64,21 +64,25 @@ 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;
|
||||
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");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
@ -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,11 +820,11 @@ 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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,20 +138,52 @@ 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))
|
||||
{
|
||||
// Decrement mesh reference counter
|
||||
unsigned int& num_ref = num_refs[pcNode->mMeshes[i]];
|
||||
ai_assert(0 != num_ref);
|
||||
--num_ref;
|
||||
|
||||
if (identity) {
|
||||
// copy positions without modifying them
|
||||
::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX],
|
||||
pcMesh->mVertices,
|
||||
pcMesh->mNumVertices * sizeof(aiVector3D));
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy positions, transform them to worldspace
|
||||
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)
|
||||
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
|
||||
pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n];
|
||||
|
||||
|
||||
}
|
||||
aiMatrix4x4 mWorldIT = pcNode->mTransformation;
|
||||
mWorldIT.Inverse().Transpose();
|
||||
|
||||
|
@ -151,7 +194,8 @@ void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
|
|||
{
|
||||
// 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];
|
||||
pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] =
|
||||
m * pcMesh->mNormals[n];
|
||||
}
|
||||
}
|
||||
if (iVFormat & 0x4)
|
||||
|
@ -162,6 +206,7 @@ void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
|
|||
pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mBitangents[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int p = 0;
|
||||
while (iVFormat & (0x100 << p))
|
||||
{
|
||||
|
@ -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
|
||||
// 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)
|
||||
{
|
||||
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];
|
||||
|
||||
// just make sure the array won't be deleted by the
|
||||
// aiFace destructor ...
|
||||
pcMesh->mFaces[planck].mIndices = NULL;
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
// 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,8 +454,44 @@ void PretransformVertices::Execute( aiScene* pScene)
|
|||
|
||||
// now build a list of output meshes
|
||||
std::vector<aiMesh*> apcOutMeshes;
|
||||
|
||||
// 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();
|
||||
|
@ -299,7 +502,7 @@ void PretransformVertices::Execute( aiScene* pScene)
|
|||
unsigned int iVertices = 0;
|
||||
unsigned int iFaces = 0;
|
||||
CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices);
|
||||
if (iFaces && iVertices)
|
||||
if (0 != iFaces && 0 != iVertices)
|
||||
{
|
||||
apcOutMeshes.push_back(new aiMesh());
|
||||
aiMesh* pcMesh = apcOutMeshes.back();
|
||||
|
@ -328,11 +531,48 @@ void PretransformVertices::Execute( aiScene* pScene)
|
|||
|
||||
// fill the mesh ...
|
||||
unsigned int aiTemp[2] = {0,0};
|
||||
CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp);
|
||||
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
|
||||
for (unsigned int i = 0; i < pScene->mNumAnimations;++i)
|
||||
delete pScene->mAnimations[i];
|
||||
|
@ -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,6 +607,8 @@ void PretransformVertices::Execute( aiScene* pScene)
|
|||
l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection;
|
||||
}
|
||||
|
||||
if( !configKeepHierarchy ) {
|
||||
|
||||
// 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;
|
||||
|
@ -447,6 +655,11 @@ void PretransformVertices::Execute( aiScene* pScene)
|
|||
pScene->mCameras[i]->mName = pcNode->mName;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// ... and finally set the transformation matrix of all nodes to identity
|
||||
MakeIdentityTransform(pScene->mRootNode);
|
||||
}
|
||||
|
||||
// print statistics
|
||||
if (!DefaultLogger::isNullLogger())
|
||||
|
@ -467,7 +680,5 @@ void PretransformVertices::Execute( aiScene* pScene)
|
|||
iOldMeshes,pScene->mNumMeshes);
|
||||
DefaultLogger::get()->info(buffer);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ...).";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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,11 +48,11 @@ 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
|
||||
{
|
||||
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
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;
|
||||
// fixme: auto format detection
|
||||
return SimpleExtensionCheck(pFile,"smd","vta");
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,8 +185,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;
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Called prior to ReadFile().
|
||||
|
@ -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.
|
||||
|
|
|
@ -64,23 +64,27 @@ 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);
|
||||
|
||||
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;
|
||||
const std::string extension = GetExtension(pFile);
|
||||
|
||||
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(
|
||||
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
@ -355,8 +392,7 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
|
|||
{
|
||||
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];
|
||||
}
|
||||
|
||||
// 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 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);
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -64,9 +64,37 @@ 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;
|
||||
|
||||
aiString name;
|
||||
|
||||
// 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]) {
|
||||
|
||||
if (mat0 == 0xffffffff) {
|
||||
scene->mMaterials[scene->mNumMaterials] = helper = new MaterialHelper();
|
||||
|
||||
// dummy texture
|
||||
name.Set("texture.png");
|
||||
helper->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(0));
|
||||
|
||||
// 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);
|
||||
|
@ -77,13 +105,15 @@ void ScenePreprocessor::ProcessScene ()
|
|||
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
|
||||
|
||||
// setup the default name
|
||||
aiString name(AI_DEFAULT_MATERIAL_NAME);
|
||||
name.Set(AI_DEFAULT_MATERIAL_NAME);
|
||||
helper->AddProperty(&name,AI_MATKEY_NAME);
|
||||
|
||||
for (unsigned int i = 0; i < scene->mNumMeshes;++i)
|
||||
scene->mMeshes[i]->mMaterialIndex = 0;
|
||||
|
||||
DefaultLogger::get()->debug("ScenePreprocessor: Added default material \'" AI_DEFAULT_MATERIAL_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])
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue