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
|
// Setup final material indices, generae a default material if necessary
|
||||||
void Discreet3DSImporter::ReplaceDefaultMaterial()
|
void Discreet3DSImporter::ReplaceDefaultMaterial()
|
||||||
{
|
{
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Try to find an existing material that matches the
|
// Try to find an existing material that matches the
|
||||||
// typical default material setting:
|
// typical default material setting:
|
||||||
// - no textures
|
// - no textures
|
||||||
// - diffuse color (in grey!)
|
// - diffuse color (in grey!)
|
||||||
// NOTE: This is here to workaround the fact that some
|
// NOTE: This is here to workaround the fact that some
|
||||||
// exporters are writing a default material, too.
|
// exporters are writing a default material, too.
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
unsigned int idx = 0xcdcdcdcd;
|
unsigned int idx = 0xcdcdcdcd;
|
||||||
for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
|
for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
|
||||||
{
|
{
|
||||||
|
@ -438,57 +437,65 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
||||||
iArray.reserve(3);
|
iArray.reserve(3);
|
||||||
|
|
||||||
aiMatrix4x4 abs;
|
aiMatrix4x4 abs;
|
||||||
/*if (pcIn->mName == "$$$DUMMY") {
|
|
||||||
// FIX: Append the "real" name of the dummy to the string
|
// Find all meshes with the same name as the node
|
||||||
pcIn->mName = "Dummy." + pcIn->mDummyName;
|
for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
|
||||||
}
|
|
||||||
else*/ // if (pcIn->mName != "$$$DUMMY")
|
|
||||||
{
|
{
|
||||||
// Find all meshes with the same name as the node
|
const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0];
|
||||||
for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
|
ai_assert(NULL != pcMesh);
|
||||||
{
|
|
||||||
const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0];
|
|
||||||
ai_assert(NULL != pcMesh);
|
|
||||||
|
|
||||||
if (pcIn->mName == pcMesh->mName)
|
if (pcIn->mName == pcMesh->mName)
|
||||||
iArray.push_back(a);
|
iArray.push_back(a);
|
||||||
}
|
}
|
||||||
if (!iArray.empty())
|
if (!iArray.empty())
|
||||||
{
|
{
|
||||||
// The matrix should be identical for all meshes with the
|
// The matrix should be identical for all meshes with the
|
||||||
// same name. It HAS to be identical for all meshes .....
|
// same name. It HAS to be identical for all meshes .....
|
||||||
aiMatrix4x4 mInv = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0])->mMat;
|
D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]);
|
||||||
mInv.Inverse();
|
|
||||||
const aiVector3D& pivot = pcIn->vPivot;
|
|
||||||
|
|
||||||
pcOut->mNumMeshes = (unsigned int)iArray.size();
|
// Compute the inverse of the transformation matrix to move the
|
||||||
pcOut->mMeshes = new unsigned int[iArray.size()];
|
// vertices back to their relative and local space
|
||||||
for (unsigned int i = 0;i < iArray.size();++i)
|
aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat;
|
||||||
{
|
mInv.Inverse();mInvTransposed.Transpose();
|
||||||
const unsigned int iIndex = iArray[i];
|
aiVector3D pivot = pcIn->vPivot;
|
||||||
aiMesh* const mesh = pcSOut->mMeshes[iIndex];
|
|
||||||
|
|
||||||
// Pivot point adjustment
|
pcOut->mNumMeshes = (unsigned int)iArray.size();
|
||||||
// See: http://www.zfx.info/DisplayThread.php?MID=235690#235690
|
pcOut->mMeshes = new unsigned int[iArray.size()];
|
||||||
const aiVector3D* const pvEnd = mesh->mVertices+mesh->mNumVertices;
|
for (unsigned int i = 0;i < iArray.size();++i) {
|
||||||
aiVector3D* pvCurrent = mesh->mVertices;
|
const unsigned int iIndex = iArray[i];
|
||||||
|
aiMesh* const mesh = pcSOut->mMeshes[iIndex];
|
||||||
|
|
||||||
if(pivot.x || pivot.y || pivot.z)
|
// Transform the vertices back into their local space
|
||||||
{
|
// fixme: consider computing normals after this, so we don't need to transform them
|
||||||
for (;pvCurrent != pvEnd;++pvCurrent)
|
const aiVector3D* const pvEnd = mesh->mVertices+mesh->mNumVertices;
|
||||||
{
|
aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
|
||||||
*pvCurrent = mInv * (*pvCurrent);
|
|
||||||
*pvCurrent -= pivot;
|
for (;pvCurrent != pvEnd;++pvCurrent,++t2) {
|
||||||
}
|
*pvCurrent = mInv * (*pvCurrent);
|
||||||
}
|
*t2 = mInvTransposed * (*t2);
|
||||||
else
|
|
||||||
{
|
|
||||||
for (;pvCurrent != pvEnd;++pvCurrent)
|
|
||||||
*pvCurrent = mInv * (*pvCurrent);
|
|
||||||
}
|
|
||||||
// Setup the mesh index
|
|
||||||
pcOut->mMeshes[i] = iIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle negative transformation matrix determinant -> invert vertex x
|
||||||
|
if (imesh->mMat.Determinant() < 0.0f)
|
||||||
|
{
|
||||||
|
/* we *must* have normals */
|
||||||
|
for (pvCurrent = mesh->mVertices,t2 = mesh->mNormals;pvCurrent != pvEnd;++pvCurrent,++t2) {
|
||||||
|
pvCurrent->x *= -1.f;
|
||||||
|
t2->x *= -1.f;
|
||||||
|
}
|
||||||
|
DefaultLogger::get()->info("3DS: Flipping mesh X-Axis");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle pivot point
|
||||||
|
if(pivot.x || pivot.y || pivot.z)
|
||||||
|
{
|
||||||
|
for (pvCurrent = mesh->mVertices;pvCurrent != pvEnd;++pvCurrent) {
|
||||||
|
*pvCurrent -= pivot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the mesh index
|
||||||
|
pcOut->mMeshes[i] = iIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,7 +533,7 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate animation channels for the node
|
// Generate animation channels for the node
|
||||||
if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
|
if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
|
||||||
pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
|
pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
|
||||||
pcIn->aTargetPositionKeys.size() > 1)
|
pcIn->aTargetPositionKeys.size() > 1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -124,11 +124,10 @@ public:
|
||||||
CHUNK_PERCENTF = 0x0031, // float4 percentage
|
CHUNK_PERCENTF = 0x0031, // float4 percentage
|
||||||
// ********************************************************************
|
// ********************************************************************
|
||||||
|
|
||||||
// Unknown and ignored. Possibly a chunk used by PROJ (
|
// Prj master chunk
|
||||||
// Discreet 3DS max Project File)?
|
|
||||||
CHUNK_PRJ = 0xC23D,
|
CHUNK_PRJ = 0xC23D,
|
||||||
|
|
||||||
// Unknown. Possibly a reference to an external .mli file?
|
// MDLI master chunk
|
||||||
CHUNK_MLI = 0x3DAA,
|
CHUNK_MLI = 0x3DAA,
|
||||||
|
|
||||||
// Primary main chunk of the .3ds file
|
// Primary main chunk of the .3ds file
|
||||||
|
@ -178,7 +177,6 @@ public:
|
||||||
CHUNK_MESHCOLOR = 0x4165,
|
CHUNK_MESHCOLOR = 0x4165,
|
||||||
CHUNK_TXTINFO = 0x4170,
|
CHUNK_TXTINFO = 0x4170,
|
||||||
CHUNK_LIGHT = 0x4600,
|
CHUNK_LIGHT = 0x4600,
|
||||||
CHUNK_SPOTLIGHT = 0x4610,
|
|
||||||
CHUNK_CAMERA = 0x4700,
|
CHUNK_CAMERA = 0x4700,
|
||||||
CHUNK_HIERARCHY = 0x4F00,
|
CHUNK_HIERARCHY = 0x4F00,
|
||||||
|
|
||||||
|
@ -330,7 +328,7 @@ struct Texture
|
||||||
, mMapMode (aiTextureMapMode_Wrap)
|
, mMapMode (aiTextureMapMode_Wrap)
|
||||||
, iUVSrc (0)
|
, iUVSrc (0)
|
||||||
{
|
{
|
||||||
mTextureBlend = std::numeric_limits<float>::quiet_NaN();
|
mTextureBlend = get_qnan();
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Specifies the blend factor for the texture
|
//! 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"
|
#include "AssimpPCH.h"
|
||||||
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
|
||||||
|
@ -53,11 +57,13 @@ using namespace Assimp;
|
||||||
// Begins a new parsing block
|
// Begins a new parsing block
|
||||||
// - Reads the current chunk and validates it
|
// - Reads the current chunk and validates it
|
||||||
// - computes its length
|
// - computes its length
|
||||||
#define ASSIMP_3DS_BEGIN_CHUNK() \
|
#define ASSIMP_3DS_BEGIN_CHUNK() \
|
||||||
Discreet3DS::Chunk chunk; \
|
if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)) \
|
||||||
ReadChunk(&chunk); \
|
return; \
|
||||||
int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \
|
Discreet3DS::Chunk chunk; \
|
||||||
const int oldReadLimit = stream->GetReadLimit(); \
|
ReadChunk(&chunk); \
|
||||||
|
int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \
|
||||||
|
const int oldReadLimit = stream->GetReadLimit(); \
|
||||||
stream->SetReadLimit(stream->GetCurrentPos() + chunkSize);
|
stream->SetReadLimit(stream->GetCurrentPos() + chunkSize);
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,18 +88,27 @@ Discreet3DSImporter::~Discreet3DSImporter()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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 extension = GetExtension(pFile);
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
if(extension == "3ds" || extension == "prj" ) {
|
||||||
// no file extension - can't read
|
return true;
|
||||||
if( pos == std::string::npos)
|
}
|
||||||
return false;
|
if (!extension.length() || checkSig) {
|
||||||
std::string extension = pFile.substr( pos);
|
uint16_t token[3];
|
||||||
for (std::string::iterator i = extension.begin(); i != extension.end();++i)
|
token[0] = 0x4d4d;
|
||||||
*i = ::tolower(*i);
|
token[1] = 0x3dc2;
|
||||||
|
//token[2] = 0x3daa;
|
||||||
|
return CheckMagicToken(pIOHandler,pFile,token,2,0,2);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return (extension == ".3ds");
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Get list of all extension supported by this loader
|
||||||
|
void Discreet3DSImporter::GetExtensionList(std::string& append)
|
||||||
|
{
|
||||||
|
append.append("*.3ds;*.prj");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -128,6 +143,7 @@ void Discreet3DSImporter::InternReadFile( const std::string& pFile,
|
||||||
mMasterScale = 1.0f;
|
mMasterScale = 1.0f;
|
||||||
mBackgroundImage = "";
|
mBackgroundImage = "";
|
||||||
bHasBG = false;
|
bHasBG = false;
|
||||||
|
bIsPrj = false;
|
||||||
|
|
||||||
// Parse the file
|
// Parse the file
|
||||||
ParseMainChunk();
|
ParseMainChunk();
|
||||||
|
@ -138,8 +154,7 @@ void Discreet3DSImporter::InternReadFile( const std::string& pFile,
|
||||||
// vectors from the smoothing groups we read from the
|
// vectors from the smoothing groups we read from the
|
||||||
// file.
|
// file.
|
||||||
for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(),
|
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);
|
CheckIndices(*i);
|
||||||
MakeUnique (*i);
|
MakeUnique (*i);
|
||||||
ComputeNormalsWithSmoothingsGroups<D3DS::Face>(*i);
|
ComputeNormalsWithSmoothingsGroups<D3DS::Face>(*i);
|
||||||
|
@ -226,6 +241,9 @@ void Discreet3DSImporter::ParseMainChunk()
|
||||||
// get chunk type
|
// get chunk type
|
||||||
switch (chunk.Flag)
|
switch (chunk.Flag)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
case Discreet3DS::CHUNK_PRJ:
|
||||||
|
bIsPrj = true;
|
||||||
case Discreet3DS::CHUNK_MAIN:
|
case Discreet3DS::CHUNK_MAIN:
|
||||||
ParseEditorChunk();
|
ParseEditorChunk();
|
||||||
break;
|
break;
|
||||||
|
@ -371,9 +389,9 @@ void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num)
|
||||||
|
|
||||||
light->mColorDiffuse = aiColor3D(1.f,1.f,1.f);
|
light->mColorDiffuse = aiColor3D(1.f,1.f,1.f);
|
||||||
|
|
||||||
// Now check for further subchunks (excluding color)
|
// Now check for further subchunks
|
||||||
int8_t* p = stream->GetPtr();
|
if (!bIsPrj) /* fixme */
|
||||||
ParseLightChunk();
|
ParseLightChunk();
|
||||||
|
|
||||||
// The specular light color is identical the the diffuse light
|
// The specular light color is identical the the diffuse light
|
||||||
// color. The ambient light color is equal to the ambient base
|
// color. The ambient light color is equal to the ambient base
|
||||||
|
@ -424,6 +442,10 @@ void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num)
|
||||||
if (camera->mHorizontalFOV < 0.001f)
|
if (camera->mHorizontalFOV < 0.001f)
|
||||||
camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f);
|
camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now check for further subchunks
|
||||||
|
if (!bIsPrj) /* fixme */
|
||||||
|
ParseCameraChunk();
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
ASSIMP_3DS_END_CHUNK();
|
ASSIMP_3DS_END_CHUNK();
|
||||||
|
@ -440,7 +462,7 @@ void Discreet3DSImporter::ParseLightChunk()
|
||||||
// get chunk type
|
// get chunk type
|
||||||
switch (chunk.Flag)
|
switch (chunk.Flag)
|
||||||
{
|
{
|
||||||
case Discreet3DS::CHUNK_SPOTLIGHT:
|
case Discreet3DS::CHUNK_DL_SPOTLIGHT:
|
||||||
// Now we can be sure that the light is a spot light
|
// Now we can be sure that the light is a spot light
|
||||||
light->mType = aiLightSource_SPOT;
|
light->mType = aiLightSource_SPOT;
|
||||||
|
|
||||||
|
@ -511,7 +533,7 @@ void Discreet3DSImporter::ParseKeyframeChunk()
|
||||||
switch (chunk.Flag)
|
switch (chunk.Flag)
|
||||||
{
|
{
|
||||||
case Discreet3DS::CHUNK_TRACKCAMTGT:
|
case Discreet3DS::CHUNK_TRACKCAMTGT:
|
||||||
case Discreet3DS::CHUNK_SPOTLIGHT:
|
case Discreet3DS::CHUNK_TRACKSPOTL:
|
||||||
case Discreet3DS::CHUNK_TRACKCAMERA:
|
case Discreet3DS::CHUNK_TRACKCAMERA:
|
||||||
case Discreet3DS::CHUNK_TRACKINFO:
|
case Discreet3DS::CHUNK_TRACKINFO:
|
||||||
case Discreet3DS::CHUNK_TRACKLIGHT:
|
case Discreet3DS::CHUNK_TRACKLIGHT:
|
||||||
|
@ -574,14 +596,11 @@ void Discreet3DSImporter::SkipTCBInfo()
|
||||||
{
|
{
|
||||||
unsigned int flags = stream->GetI2();
|
unsigned int flags = stream->GetI2();
|
||||||
|
|
||||||
if (!flags)
|
if (!flags) {
|
||||||
{
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Currently we can't do anything with these values. They occur
|
// Currently we can't do anything with these values. They occur
|
||||||
// quite rare, so it wouldn't be worth the effort implementing
|
// quite rare, so it wouldn't be worth the effort implementing
|
||||||
// them. 3DS ist not really suitable for complex animations,
|
// them. 3DS ist not really suitable for complex animations,
|
||||||
// so full support is not required.
|
// so full support is not required.
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
DefaultLogger::get()->warn("3DS: Skipping TCB animation info");
|
DefaultLogger::get()->warn("3DS: Skipping TCB animation info");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1015,35 +1034,7 @@ void Discreet3DSImporter::ParseMeshChunk()
|
||||||
mMesh.mMat.a4 = stream->GetF4();
|
mMesh.mMat.a4 = stream->GetF4();
|
||||||
mMesh.mMat.b4 = stream->GetF4();
|
mMesh.mMat.b4 = stream->GetF4();
|
||||||
mMesh.mMat.c4 = 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;
|
break;
|
||||||
|
|
||||||
case Discreet3DS::CHUNK_MAPLIST:
|
case Discreet3DS::CHUNK_MAPLIST:
|
||||||
|
@ -1353,7 +1344,7 @@ float Discreet3DSImporter::ParsePercentageChunk()
|
||||||
return stream->GetF4();
|
return stream->GetF4();
|
||||||
else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag)
|
else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag)
|
||||||
return (float)((uint16_t)stream->GetI2()) / (float)0xFFFF;
|
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);
|
ai_assert(out != NULL);
|
||||||
|
|
||||||
// error return value
|
// 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);
|
static const aiColor3D clrError = aiColor3D(qnan,qnan,qnan);
|
||||||
|
|
||||||
Discreet3DS::Chunk chunk;
|
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
|
#ifndef AI_3DSIMPORTER_H_INC
|
||||||
#define AI_3DSIMPORTER_H_INC
|
#define AI_3DSIMPORTER_H_INC
|
||||||
|
|
||||||
|
@ -49,16 +51,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
struct aiNode;
|
struct aiNode;
|
||||||
#include "3DSHelper.h"
|
#include "3DSHelper.h"
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp {
|
||||||
{
|
|
||||||
class MaterialHelper;
|
class MaterialHelper;
|
||||||
|
|
||||||
using namespace D3DS;
|
using namespace D3DS;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------
|
||||||
/** The Discreet3DSImporter is a worker class capable of importing a scene from a
|
/** Importer class for 3D Studio r3 and r4 3DS files
|
||||||
* 3ds Max 4/5 Files (.3ds)
|
*/
|
||||||
*/
|
|
||||||
class Discreet3DSImporter : public BaseImporter
|
class Discreet3DSImporter : public BaseImporter
|
||||||
{
|
{
|
||||||
friend class Importer;
|
friend class Importer;
|
||||||
|
@ -74,14 +74,16 @@ public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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().
|
/** Called prior to ReadFile().
|
||||||
* The function is a request to the importer to update its configuration
|
* The function is a request to the importer to update its configuration
|
||||||
* basing on the Importer's configuration property list.
|
* basing on the Importer's configuration property list.
|
||||||
*/
|
*/
|
||||||
void SetupProperties(const Importer* pImp);
|
void SetupProperties(const Importer* pImp);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -90,21 +92,18 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.3ds");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** Imports the given file into the given scene structure.
|
||||||
* See BaseImporter::InternReadFile() for details
|
* See BaseImporter::InternReadFile() for details
|
||||||
*/
|
*/
|
||||||
void InternReadFile( const std::string& pFile, aiScene* pScene,
|
void InternReadFile( const std::string& pFile, aiScene* pScene,
|
||||||
IOSystem* pIOHandler);
|
IOSystem* pIOHandler);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Converts a temporary material to the outer representation
|
/** Converts a temporary material to the outer representation
|
||||||
*/
|
*/
|
||||||
void ConvertMaterial(D3DS::Material& p_cMat,
|
void ConvertMaterial(D3DS::Material& p_cMat,
|
||||||
MaterialHelper& p_pcOut);
|
MaterialHelper& p_pcOut);
|
||||||
|
|
||||||
|
@ -112,7 +111,7 @@ protected:
|
||||||
/** Read a chunk
|
/** Read a chunk
|
||||||
*
|
*
|
||||||
* @param pcOut Receives the current chunk
|
* @param pcOut Receives the current chunk
|
||||||
*/
|
*/
|
||||||
void ReadChunk(Discreet3DS::Chunk* pcOut);
|
void ReadChunk(Discreet3DS::Chunk* pcOut);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
@ -271,6 +270,9 @@ protected:
|
||||||
/** Path to the background image of the scene */
|
/** Path to the background image of the scene */
|
||||||
std::string mBackgroundImage;
|
std::string mBackgroundImage;
|
||||||
bool bHasBG;
|
bool bHasBG;
|
||||||
|
|
||||||
|
/** true if PRJ file */
|
||||||
|
bool bIsPrj;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace Assimp
|
} // end of namespace Assimp
|
||||||
|
|
|
@ -108,28 +108,38 @@ using namespace Assimp;
|
||||||
// Constructor to be privately used by Importer
|
// Constructor to be privately used by Importer
|
||||||
AC3DImporter::AC3DImporter()
|
AC3DImporter::AC3DImporter()
|
||||||
{
|
{
|
||||||
|
// nothing to be done here
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Destructor, private as well
|
// Destructor, private as well
|
||||||
AC3DImporter::~AC3DImporter()
|
AC3DImporter::~AC3DImporter()
|
||||||
{
|
{
|
||||||
|
// nothing to be done here
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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 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 it = extension.begin(); it != extension.end(); ++it)
|
// fixme: are acc and ac3d *really* used? Some sources say they are
|
||||||
*it = tolower( *it);
|
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);
|
return SkipSpaces(&buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Parse an object section in an AC file
|
// Parse an object section in an AC file
|
||||||
void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
|
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
|
#ifndef AI_AC3DLOADER_H_INCLUDED
|
||||||
#define 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.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -175,10 +179,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.ac;*.acc");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** 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"
|
#include "AssimpPCH.h"
|
||||||
#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
|
||||||
|
@ -69,23 +71,25 @@ ASEImporter::~ASEImporter()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
// check file extension
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
const std::string extension = GetExtension(pFile);
|
||||||
// no file extension - can't read
|
|
||||||
if( pos == std::string::npos)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string extension = pFile.substr( pos);
|
if( extension == "ase" || extension == "ask")
|
||||||
|
return true;
|
||||||
|
|
||||||
// Either ASE, ASC or ASK
|
if ((!extension.length() || cs) && pIOHandler) {
|
||||||
return !(extension.length() < 4 || extension[0] != '.' ||
|
const char* tokens[] = {"*3dsmax_asciiexport"};
|
||||||
extension[1] != 'a' && extension[1] != 'A' ||
|
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
|
||||||
extension[2] != 's' && extension[2] != 'S' ||
|
}
|
||||||
extension[3] != 'e' && extension[3] != 'E' &&
|
return false;
|
||||||
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 + ".");
|
throw new ImportErrorException( "Failed to open ASE file " + pFile + ".");
|
||||||
|
|
||||||
size_t fileSize = file->FileSize();
|
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
|
// Allocate storage and copy the contents of the file to a memory buffer
|
||||||
// (terminate it with zero)
|
// (terminate it with zero)
|
||||||
|
@ -778,8 +784,7 @@ void ASEImporter::BuildNodes()
|
||||||
delete pc;
|
delete pc;
|
||||||
}
|
}
|
||||||
// The root node should not have at least one child or the file is invalid
|
// 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");
|
throw new ImportErrorException("No nodes loaded. The ASE/ASK file is either empty or corrupt");
|
||||||
}
|
}
|
||||||
return;
|
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
|
#ifndef AI_ASELOADER_H_INCLUDED
|
||||||
#define AI_ASELOADER_H_INCLUDED
|
#define AI_ASELOADER_H_INCLUDED
|
||||||
|
|
||||||
|
@ -48,15 +50,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
struct aiNode;
|
struct aiNode;
|
||||||
#include "ASEParser.h"
|
#include "ASEParser.h"
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp {
|
||||||
{
|
|
||||||
class MaterialHelper;
|
class MaterialHelper;
|
||||||
|
|
||||||
using namespace ASE;
|
using namespace ASE;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------
|
||||||
/** Used to load ASE files
|
/** Importer class for the 3DS ASE ASCII format
|
||||||
*/
|
*
|
||||||
|
* fixme: consider code cleanup
|
||||||
|
*/
|
||||||
class ASEImporter : public BaseImporter
|
class ASEImporter : public BaseImporter
|
||||||
{
|
{
|
||||||
friend class Importer;
|
friend class Importer;
|
||||||
|
@ -72,8 +75,10 @@ public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -81,10 +86,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.ase;*.ask;*.asc");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** 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"
|
#include "AssimpPCH.h"
|
||||||
|
|
||||||
|
@ -120,8 +122,8 @@ Parser::Parser (const char* szFile, unsigned int fileFormatDefault)
|
||||||
iFileFormat = fileFormatDefault;
|
iFileFormat = fileFormatDefault;
|
||||||
|
|
||||||
// make sure that the color values are invalid
|
// make sure that the color values are invalid
|
||||||
m_clrBackground.r = std::numeric_limits<float>::quiet_NaN();
|
m_clrBackground.r = get_qnan();
|
||||||
m_clrAmbient.r = std::numeric_limits<float>::quiet_NaN();
|
m_clrAmbient.r = get_qnan();
|
||||||
|
|
||||||
// setup some default values
|
// setup some default values
|
||||||
iLineNumber = 0;
|
iLineNumber = 0;
|
||||||
|
|
|
@ -227,7 +227,7 @@ struct BaseNode
|
||||||
mName = szTemp;
|
mName = szTemp;
|
||||||
|
|
||||||
// Set mTargetPosition to qnan
|
// Set mTargetPosition to qnan
|
||||||
const float qnan = std::numeric_limits<float>::quiet_NaN();
|
const float qnan = get_qnan();
|
||||||
mTargetPosition.x = 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 "AssimpPCH.h"
|
||||||
#include "./../include/aiVersion.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"
|
"A free C/C++ library to import various 3D file formats into applications\n\n"
|
||||||
|
|
||||||
"(c) ASSIMP Development Team, 2008-2009\n"
|
"(c) ASSIMP Development Team, 2008-2009\n"
|
||||||
"License: BSD\n"
|
"License: 3-clause BSD license\n"
|
||||||
"Website: http://assimp.sourceforge.net\n"
|
"Website: http://assimp.sourceforge.net\n"
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -59,10 +59,12 @@ ASSIMP_API unsigned int aiGetCompileFlags () {
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// include current build revision
|
||||||
|
#include "../mkutil/revision.h"
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
ASSIMP_API unsigned int aiGetVersionRevision ()
|
ASSIMP_API unsigned int aiGetVersionRevision ()
|
||||||
{
|
{
|
||||||
// TODO: find a way to update the revision number automatically
|
return SVNRevision;
|
||||||
return 306;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
# define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
|
# define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
|
||||||
#endif
|
#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
|
// Actually that's not required for MSVC (it is included somewhere in
|
||||||
// the STL ..) but it is necessary for build with STLport.
|
// the STL ..) but it is necessary for build with STLport.
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
@ -75,6 +82,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#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"
|
#include "AssimpPCH.h"
|
||||||
#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
|
||||||
|
@ -52,7 +54,7 @@ using namespace Assimp;
|
||||||
using namespace std;
|
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( '.' );
|
int pos=pFile.find_last_of( '.' );
|
||||||
if( pos==string::npos ) return false;
|
if( pos==string::npos ) return false;
|
||||||
|
|
|
@ -56,7 +56,7 @@ namespace Assimp{
|
||||||
class B3DImporter : public BaseImporter{
|
class B3DImporter : public BaseImporter{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
|
virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
|
@ -61,18 +61,19 @@ BVHLoader::~BVHLoader()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
// check file extension
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
const std::string extension = GetExtension(pFile);
|
||||||
// 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);
|
|
||||||
|
|
||||||
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
|
#ifndef AI_BVHLOADER_H_INC
|
||||||
#define 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
|
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,
|
/** Loader class to read Motion Capturing data from a .bvh file.
|
||||||
* but we generate a dummy mesh inside the loader just to be able to see something.
|
*
|
||||||
|
* 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
|
class BVHLoader : public BaseImporter
|
||||||
{
|
{
|
||||||
|
@ -88,7 +95,7 @@ protected:
|
||||||
public:
|
public:
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** 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 "AssimpPCH.h"
|
||||||
#include "BaseImporter.h"
|
#include "BaseImporter.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace Assimp;
|
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 std::string& pFile,
|
||||||
const char** tokens,
|
const char** tokens,
|
||||||
unsigned int numTokens,
|
unsigned int numTokens,
|
||||||
|
@ -142,17 +143,105 @@ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler,
|
||||||
return false;
|
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
|
// Represents an import request
|
||||||
struct LoadRequest
|
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)
|
: file (_file)
|
||||||
, flags (_flags)
|
, flags (_flags)
|
||||||
, refCnt (1)
|
, refCnt (1)
|
||||||
, scene (NULL)
|
, scene (NULL)
|
||||||
, loaded (false)
|
, loaded (false)
|
||||||
|
, id (_id)
|
||||||
{
|
{
|
||||||
if (_map)
|
if (_map)
|
||||||
map = *_map;
|
map = *_map;
|
||||||
|
@ -164,6 +253,7 @@ struct LoadRequest
|
||||||
aiScene* scene;
|
aiScene* scene;
|
||||||
bool loaded;
|
bool loaded;
|
||||||
BatchLoader::PropertyMap map;
|
BatchLoader::PropertyMap map;
|
||||||
|
unsigned int id;
|
||||||
|
|
||||||
bool operator== (const std::string& f) {
|
bool operator== (const std::string& f) {
|
||||||
return file == f;
|
return file == f;
|
||||||
|
@ -174,6 +264,10 @@ struct LoadRequest
|
||||||
// BatchLoader::pimpl data structure
|
// BatchLoader::pimpl data structure
|
||||||
struct Assimp::BatchData
|
struct Assimp::BatchData
|
||||||
{
|
{
|
||||||
|
BatchData()
|
||||||
|
: next_id(0xffff)
|
||||||
|
{}
|
||||||
|
|
||||||
// IO system to be used for all imports
|
// IO system to be used for all imports
|
||||||
IOSystem* pIOSystem;
|
IOSystem* pIOSystem;
|
||||||
|
|
||||||
|
@ -185,6 +279,9 @@ struct Assimp::BatchData
|
||||||
|
|
||||||
// Base path
|
// Base path
|
||||||
std::string pathBase;
|
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;
|
std::string::size_type ss,ss2;
|
||||||
if (std::string::npos != (ss = data->pathBase.find_first_of('.')))
|
if (std::string::npos != (ss = data->pathBase.find_first_of('.')))
|
||||||
{
|
{
|
||||||
if (std::string::npos != (ss2 = data->pathBase.find_last_of('\\')) ||
|
if (std::string::npos != (ss2 = data->pathBase.find_last_of("\\/")))
|
||||||
std::string::npos != (ss2 = data->pathBase.find_last_of('/')))
|
|
||||||
{
|
{
|
||||||
if (ss > ss2)
|
if (ss > ss2)
|
||||||
data->pathBase.erase(ss2,data->pathBase.length()-ss2);
|
data->pathBase.erase(ss2,data->pathBase.length()-ss2);
|
||||||
}
|
}
|
||||||
else return;
|
else {
|
||||||
|
data->pathBase = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure the directory is terminated properly
|
// 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*/)
|
unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/)
|
||||||
{
|
{
|
||||||
ai_assert(!file.empty());
|
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
|
// build a full path if this is a relative path and
|
||||||
// we have a new base directory given
|
// 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;
|
real = data->pathBase + file;
|
||||||
}
|
}
|
||||||
else real = file;
|
else real = file;
|
||||||
|
@ -258,32 +356,29 @@ void BatchLoader::AddLoadRequest (const std::string& file,
|
||||||
// Call IOSystem's path comparison function here
|
// Call IOSystem's path comparison function here
|
||||||
if (data->pIOSystem->ComparePaths((*it).file,real))
|
if (data->pIOSystem->ComparePaths((*it).file,real))
|
||||||
{
|
{
|
||||||
|
if (map) {
|
||||||
|
if (!((*it).map == *map))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (!(*it).map.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
(*it).refCnt++;
|
(*it).refCnt++;
|
||||||
return;
|
return (*it).id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// no, we don't have it. So add it to the queue ...
|
// 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)
|
for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it)
|
||||||
{
|
{
|
||||||
// Call IOSystem's path comparison function here
|
if ((*it).id == which && (*it).loaded)
|
||||||
if (data->pIOSystem->ComparePaths((*it).file,real) && (*it).loaded)
|
|
||||||
{
|
{
|
||||||
aiScene* sc = (*it).scene;
|
aiScene* sc = (*it).scene;
|
||||||
if (!(--(*it).refCnt))
|
if (!(--(*it).refCnt))
|
||||||
|
|
|
@ -52,6 +52,10 @@ namespace Assimp {
|
||||||
class IOSystem;
|
class IOSystem;
|
||||||
class Importer;
|
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. */
|
/** Simple exception class to be thrown if an error occurs while importing. */
|
||||||
class ASSIMP_API ImportErrorException
|
class ASSIMP_API ImportErrorException
|
||||||
|
@ -97,36 +101,48 @@ protected:
|
||||||
public:
|
public:
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* @param pFile Path and file name of the file to be examined.
|
*
|
||||||
* @param pIOHandler The IO handler to use for accessing any file.
|
* The implementation should be as quick as possible. A check for
|
||||||
* @return true if the class can read this file, false if not.
|
* the file extension is enough. If no suitable loader is found with
|
||||||
*
|
* this strategy, CanRead() is called again, the 'checkSig' parameter
|
||||||
* @note Sometimes ASSIMP uses this method to determine whether a
|
* set to true this time. Now the implementation is expected to
|
||||||
* a given file extension is generally supported. In this case the
|
* perform a full check of the file format, possibly searching the
|
||||||
* file extension is passed in the pFile parameter, pIOHandler is NULL
|
* first bytes of the file for magic identifiers or keywords.
|
||||||
*/
|
*
|
||||||
|
* @param pFile Path and file name of the file to be examined.
|
||||||
|
* @param pIOHandler The IO handler to use for accessing any file.
|
||||||
|
* @param checkSig Set to true if this method is called a second time.
|
||||||
|
* This time, the implementation may take more time to examine the
|
||||||
|
* contents of the file to be loaded for magic bytes, keywords, etc
|
||||||
|
* to be able to load files with unknown/not existent file extensions.
|
||||||
|
* @return true if the class can read this file, false if not.
|
||||||
|
*
|
||||||
|
* @note Sometimes ASSIMP uses this method to determine whether a
|
||||||
|
* a given file extension is generally supported. In this case the
|
||||||
|
* file extension is passed in the pFile parameter, pIOHandler is NULL
|
||||||
|
*/
|
||||||
virtual bool CanRead( const std::string& pFile,
|
virtual bool CanRead( const std::string& pFile,
|
||||||
IOSystem* pIOHandler) const = 0;
|
IOSystem* pIOHandler, bool checkSig) const = 0;
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file and returns the imported data.
|
/** Imports the given file and returns the imported data.
|
||||||
* If the import succeeds, ownership of the data is transferred to
|
* If the import succeeds, ownership of the data is transferred to
|
||||||
* the caller. If the import fails, NULL is returned. The function
|
* the caller. If the import fails, NULL is returned. The function
|
||||||
* takes care that any partially constructed data is destroyed
|
* takes care that any partially constructed data is destroyed
|
||||||
* beforehand.
|
* beforehand.
|
||||||
*
|
*
|
||||||
* @param pFile Path of the file to be imported.
|
* @param pFile Path of the file to be imported.
|
||||||
* @param pIOHandler IO-Handler used to open this and possible other files.
|
* @param pIOHandler IO-Handler used to open this and possible other files.
|
||||||
* @return The imported data or NULL if failed. If it failed a
|
* @return The imported data or NULL if failed. If it failed a
|
||||||
* human-readable error description can be retrieved by calling
|
* human-readable error description can be retrieved by calling
|
||||||
* GetErrorText()
|
* GetErrorText()
|
||||||
*
|
*
|
||||||
* @note This function is not intended to be overridden. Implement
|
* @note This function is not intended to be overridden. Implement
|
||||||
* InternReadFile() to do the import. If an exception is thrown somewhere
|
* InternReadFile() to do the import. If an exception is thrown somewhere
|
||||||
* in InternReadFile(), this function will catch it and transform it into
|
* in InternReadFile(), this function will catch it and transform it into
|
||||||
* a suitable response to the caller.
|
* a suitable response to the caller.
|
||||||
*/
|
*/
|
||||||
aiScene* ReadFile( const std::string& pFile, IOSystem* pIOHandler);
|
aiScene* ReadFile( const std::string& pFile, IOSystem* pIOHandler);
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,19 +151,20 @@ public:
|
||||||
* @return A description of the last error that occured. An empty
|
* @return A description of the last error that occured. An empty
|
||||||
* string if there was no error.
|
* string if there was no error.
|
||||||
*/
|
*/
|
||||||
inline const std::string& GetErrorText() const
|
const std::string& GetErrorText() const {
|
||||||
{ return mErrorText; }
|
return mErrorText;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Called prior to ReadFile().
|
/** Called prior to ReadFile().
|
||||||
* The function is a request to the importer to update its configuration
|
* The function is a request to the importer to update its configuration
|
||||||
* basing on the Importer's configuration property list.
|
* basing on the Importer's configuration property list.
|
||||||
* @param pImp Importer instance
|
* @param pImp Importer instance
|
||||||
* @param ppFlags Post-processing steps to be executed on the data
|
* @param ppFlags Post-processing steps to be executed on the data
|
||||||
* returned by the loaders. This value is provided to allow some
|
* returned by the loaders. This value is provided to allow some
|
||||||
* internal optimizations.
|
* internal optimizations.
|
||||||
*/
|
*/
|
||||||
virtual void SetupProperties(const Importer* pImp /*,
|
virtual void SetupProperties(const Importer* pImp /*,
|
||||||
unsigned int ppFlags*/);
|
unsigned int ppFlags*/);
|
||||||
|
|
||||||
|
@ -157,7 +174,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* Importer implementations should append all file extensions
|
* Importer implementations should append all file extensions
|
||||||
* which they supported to the passed string.
|
* 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
|
* @param append Output string
|
||||||
*/
|
*/
|
||||||
virtual void GetExtensionList(std::string& append) = 0;
|
virtual void GetExtensionList(std::string& append) = 0;
|
||||||
|
@ -169,23 +186,36 @@ protected:
|
||||||
* expected to be correct. Override this function to implement the
|
* expected to be correct. Override this function to implement the
|
||||||
* actual importing.
|
* actual importing.
|
||||||
* <br>
|
* <br>
|
||||||
* The output scene must meet the following requirements:<br>
|
* The output scene must meet the following requirements:<br>
|
||||||
* - at least a root node must be there<br>
|
* <ul>
|
||||||
* - aiMesh::mPrimitiveTypes may be 0. The types of primitives
|
* <li>At least a root node must be there, even if its only purpose
|
||||||
* in the mesh are determined automatically in this case.<br>
|
* is to reference one mesh.</li>
|
||||||
* - the vertex data is stored in a pseudo-indexed "verbose" format.
|
* <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
|
* In fact this means that every vertex that is referenced by
|
||||||
* a face is unique. Or the other way round: a vertex index may
|
* a face is unique. Or the other way round: a vertex index may
|
||||||
* not occur twice in a single aiMesh.
|
* not occur twice in a single aiMesh.</li>
|
||||||
* - aiAnimation::mDuration may be -1. Assimp determines the length
|
* <li>aiAnimation::mDuration may be -1. Assimp determines the length
|
||||||
* of the animation automatically in this case as the length of
|
* of the animation automatically in this case as the length of
|
||||||
* the longest animation channel.
|
* the longest animation channel.</li>
|
||||||
*
|
* <li>aiMesh::mBitangents may be NULL if tangents and normals are
|
||||||
* If the AI_SCENE_FLAGS_INCOMPLETE-Flag is not set:<br>
|
* given. In this case bitangents are computed as the cross product
|
||||||
* - at least one mesh must be there<br>
|
* between normal and tangent.</li>
|
||||||
* - at least one material must be there<br>
|
* <li>There needn't be a material. If none is there a default material
|
||||||
* - there may be no meshes with 0 vertices or faces<br>
|
* is generated. However, it is recommended practice for loaders
|
||||||
* This won't be checked (except by the validation step), Assimp will
|
* 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!
|
* crash if one of the conditions is not met!
|
||||||
*
|
*
|
||||||
* @param pFile Path of the file to be imported.
|
* @param pFile Path of the file to be imported.
|
||||||
|
@ -218,12 +248,53 @@ protected:
|
||||||
unsigned int numTokens,
|
unsigned int numTokens,
|
||||||
unsigned int searchBytes = 200);
|
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 **/
|
#if 0 /** TODO **/
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** An utility for all text file loaders. It converts a file to our
|
/** An utility for all text file loaders. It converts a file to our
|
||||||
* ASCII/UTF8 character set. Special unicode characters are lost.
|
* ASCII/UTF8 character set. Special unicode characters are lost.
|
||||||
*
|
*
|
||||||
* @param buffer Input buffer. Needn't be terminated with zero.
|
* @param buffer Input buffer. Needn't be terminated with zero.
|
||||||
* @param length Length of the input buffer, in bytes. Receives the
|
* @param length Length of the input buffer, in bytes. Receives the
|
||||||
* number of output characters, excluding the terminal char.
|
* number of output characters, excluding the terminal char.
|
||||||
* @return true if the source format did not match our internal
|
* @return true if the source format did not match our internal
|
||||||
|
@ -266,11 +337,19 @@ public:
|
||||||
Importer::IntPropertyMap ints;
|
Importer::IntPropertyMap ints;
|
||||||
Importer::FloatPropertyMap floats;
|
Importer::FloatPropertyMap floats;
|
||||||
Importer::StringPropertyMap strings;
|
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:
|
public:
|
||||||
|
|
||||||
|
|
||||||
/** Construct a batch loader from a given IO system
|
/** Construct a batch loader from a given IO system
|
||||||
*/
|
*/
|
||||||
BatchLoader(IOSystem* pIO);
|
BatchLoader(IOSystem* pIO);
|
||||||
|
@ -293,8 +372,10 @@ public:
|
||||||
* @param file File to be loaded
|
* @param file File to be loaded
|
||||||
* @param steps Steps to be executed on the file
|
* @param steps Steps to be executed on the file
|
||||||
* @param map Optional configuration properties
|
* @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);
|
unsigned int steps = 0, const PropertyMap* map = NULL);
|
||||||
|
|
||||||
|
|
||||||
|
@ -304,11 +385,11 @@ public:
|
||||||
* If an import is requested several times, this function
|
* If an import is requested several times, this function
|
||||||
* can be called several times, too.
|
* 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
|
* @return NULL if there is no scene with this file name
|
||||||
* in the queue of the scene hasn't been loaded yet.
|
* 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.
|
/** Waits until all scenes have been loaded.
|
||||||
|
@ -321,7 +402,6 @@ private:
|
||||||
BatchData* data;
|
BatchData* data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // end of namespace Assimp
|
} // end of namespace Assimp
|
||||||
|
|
||||||
#endif // AI_BASEIMPORTER_H_INC
|
#endif // AI_BASEIMPORTER_H_INC
|
||||||
|
|
|
@ -69,7 +69,7 @@ void BaseProcess::ExecuteOnScene( Importer* pImp)
|
||||||
// catch exceptions thrown inside the PostProcess-Step
|
// catch exceptions thrown inside the PostProcess-Step
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->Execute(pImp->mScene);
|
Execute(pImp->mScene);
|
||||||
|
|
||||||
} catch( ImportErrorException* exception)
|
} catch( ImportErrorException* exception)
|
||||||
{
|
{
|
||||||
|
|
|
@ -126,7 +126,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
const float angleEpsilon = 0.9999f;
|
const float angleEpsilon = 0.9999f;
|
||||||
|
|
||||||
std::vector<bool> vertexDone( pMesh->mNumVertices, false);
|
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
|
// create space for the tangents and bitangents
|
||||||
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
|
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.
|
// their tangent vectors are set to qnan.
|
||||||
for (unsigned int i = 0; i < face.mNumIndices;++i)
|
for (unsigned int i = 0; i < face.mNumIndices;++i)
|
||||||
{
|
{
|
||||||
vertexDone[face.mIndices[i]] = true;
|
register unsigned int idx = face.mIndices[i];
|
||||||
meshTang [face.mIndices[i]] = qnan;
|
vertexDone [idx] = true;
|
||||||
|
meshTang [idx] = qnan;
|
||||||
|
meshBitang [idx] = qnan;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
@ -218,10 +220,10 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
vertexFinder = &_vertexFinder;
|
vertexFinder = &_vertexFinder;
|
||||||
posEpsilon = ComputePositionEpsilon(pMesh);
|
posEpsilon = ComputePositionEpsilon(pMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<unsigned int> verticesFound;
|
std::vector<unsigned int> verticesFound;
|
||||||
|
|
||||||
const float fLimit = cosf(this->configMaxAngle);
|
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
|
// in the second pass we now smooth out all tangents and bitangents at the same local position
|
||||||
// if they are not too far off.
|
// 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& origNorm = pMesh->mNormals[a];
|
||||||
const aiVector3D& origTang = pMesh->mTangents[a];
|
const aiVector3D& origTang = pMesh->mTangents[a];
|
||||||
const aiVector3D& origBitang = pMesh->mBitangents[a];
|
const aiVector3D& origBitang = pMesh->mBitangents[a];
|
||||||
std::vector<unsigned int> closeVertices;
|
closeVertices.clear();
|
||||||
closeVertices.push_back( a);
|
|
||||||
|
|
||||||
// find all vertices close to that position
|
// find all vertices close to that position
|
||||||
vertexFinder->FindPositions( origPos, posEpsilon, verticesFound);
|
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
|
// 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++)
|
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.
|
// 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
|
// check file extension
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
std::string extension = GetExtension(pFile);
|
||||||
// no file extension - can't read
|
|
||||||
if( pos == std::string::npos)
|
|
||||||
return false;
|
|
||||||
std::string extension = pFile.substr( pos);
|
|
||||||
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
|
|
||||||
*it = tolower( *it);
|
|
||||||
|
|
||||||
if( extension == ".dae")
|
if( extension == "dae")
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// XML - too generic, we need to open the file and search for typical keywords
|
// 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
|
/* If CanRead() is called in order to check whether we
|
||||||
* support a specific file extension in general pIOHandler
|
* support a specific file extension in general pIOHandler
|
||||||
* might be NULL and it's our duty to return true here.
|
* 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Get file extension list
|
||||||
|
void ColladaLoader::GetExtensionList( std::string& append)
|
||||||
|
{
|
||||||
|
append.append("*.dae");
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Imports the given file into the given scene structure.
|
// Imports the given file into the given scene structure.
|
||||||
void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
|
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();
|
aiNode* node = new aiNode();
|
||||||
|
|
||||||
// now setup the name of the node. We take the name if not empty, otherwise the collada ID
|
// 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);
|
node->mName.Set(pNode->mName);
|
||||||
else if (!pNode->mID.empty())
|
else if (!pNode->mID.empty())
|
||||||
node->mName.Set(pNode->mID);
|
node->mName.Set(pNode->mID);
|
||||||
|
@ -632,7 +634,7 @@ void ColladaLoader::AddTexture ( Assimp::MaterialHelper& mat, const ColladaParse
|
||||||
_AI_MATKEY_TEXTURE_BASE,type,idx);
|
_AI_MATKEY_TEXTURE_BASE,type,idx);
|
||||||
|
|
||||||
// mapping mode
|
// mapping mode
|
||||||
int map = map = aiTextureMapMode_Clamp;
|
int map = aiTextureMapMode_Clamp;
|
||||||
if (sampler.mWrapU)
|
if (sampler.mWrapU)
|
||||||
map = aiTextureMapMode_Wrap;
|
map = aiTextureMapMode_Wrap;
|
||||||
if (sampler.mWrapU && sampler.mMirrorU)
|
if (sampler.mWrapU && sampler.mMirrorU)
|
||||||
|
|
|
@ -90,16 +90,13 @@ protected:
|
||||||
public:
|
public:
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList( std::string& append)
|
void GetExtensionList( std::string& append);
|
||||||
{
|
|
||||||
append.append("*.dae");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Imports the given file into the given scene structure.
|
/** Imports the given file into the given scene structure.
|
||||||
* See BaseImporter::InternReadFile() for details
|
* See BaseImporter::InternReadFile() for details
|
||||||
|
|
|
@ -1618,8 +1618,8 @@ void ColladaParser::ReadSceneLibrary()
|
||||||
}
|
}
|
||||||
else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
|
else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
|
||||||
{
|
{
|
||||||
if( strcmp( mReader->getNodeName(), "library_visual_scenes") != 0)
|
if( strcmp( mReader->getNodeName(), "library_visual_scenes") == 0)
|
||||||
ThrowException( "Expected end of \"library_visual_scenes\" element.");
|
//ThrowException( "Expected end of \"library_visual_scenes\" element.");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,7 +202,7 @@ protected:
|
||||||
|
|
||||||
/** Reads the text contents of an element, returns NULL if not given.
|
/** Reads the text contents of an element, returns NULL if not given.
|
||||||
Skips leading whitespace. */
|
Skips leading whitespace. */
|
||||||
const char* ColladaParser::TestTextContent();
|
const char* TestTextContent();
|
||||||
|
|
||||||
/** Reads a single bool from current text content */
|
/** Reads a single bool from current text content */
|
||||||
bool ReadBoolFromTextContent();
|
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"
|
#include "AssimpPCH.h"
|
||||||
#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
|
#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]))
|
#define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0]))
|
||||||
|
|
||||||
// invalid/unassigned color value
|
// 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
|
// 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.
|
// 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
|
return SimpleExtensionCheck(pFile,"dxf");
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
}
|
||||||
// no file extension - can't read
|
|
||||||
if( pos == std::string::npos)
|
|
||||||
return false;
|
|
||||||
std::string extension = pFile.substr( pos);
|
|
||||||
|
|
||||||
return !(extension.length() != 4 || extension[0] != '.' ||
|
// ------------------------------------------------------------------------------------------------
|
||||||
extension[1] != 'd' && extension[1] != 'D' ||
|
void DXFImporter::GetExtensionList(std::string& append)
|
||||||
extension[2] != 'x' && extension[2] != 'X' ||
|
{
|
||||||
extension[3] != 'f' && extension[3] != 'F');
|
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
|
#ifndef AI_DXFLOADER_H_INCLUDED
|
||||||
#define 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.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -93,10 +96,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.dxf");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** 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 "StdOStreamLogStream.h"
|
||||||
#include "FileLogStream.h"
|
#include "FileLogStream.h"
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_SINGLETHREADED
|
||||||
|
# include <boost/thread/thread.hpp>
|
||||||
|
# include <boost/thread/mutex.hpp>
|
||||||
|
|
||||||
|
boost::mutex loggerMutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
|
@ -119,6 +126,11 @@ Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/,
|
||||||
unsigned int defStreams /*= DLS_DEBUGGER | DLS_FILE*/,
|
unsigned int defStreams /*= DLS_DEBUGGER | DLS_FILE*/,
|
||||||
IOSystem* io /*= NULL*/)
|
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() )
|
if (m_pLogger && !isNullLogger() )
|
||||||
delete m_pLogger;
|
delete m_pLogger;
|
||||||
|
|
||||||
|
@ -146,31 +158,56 @@ Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/,
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
void Logger::debug(const std::string &message) {
|
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());
|
return OnDebug(message.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
void Logger::info(const std::string &message) {
|
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());
|
return OnInfo(message.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
void Logger::warn(const std::string &message) {
|
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());
|
return OnWarn(message.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
void Logger::error(const std::string &message) {
|
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());
|
return OnError(message.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
void DefaultLogger::set( Logger *logger )
|
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 (!logger)logger = &s_pNullLogger;
|
||||||
if (m_pLogger && !isNullLogger() )
|
if (m_pLogger && !isNullLogger() )
|
||||||
delete m_pLogger;
|
delete m_pLogger;
|
||||||
|
@ -195,6 +232,11 @@ Logger *DefaultLogger::get()
|
||||||
// Kills the only instance
|
// Kills the only instance
|
||||||
void DefaultLogger::kill()
|
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;
|
if (m_pLogger != &s_pNullLogger)return;
|
||||||
delete m_pLogger;
|
delete m_pLogger;
|
||||||
m_pLogger = &s_pNullLogger;
|
m_pLogger = &s_pNullLogger;
|
||||||
|
@ -243,23 +285,14 @@ void DefaultLogger::OnError( const char* message )
|
||||||
WriteToStreams( msg, Logger::ERR );
|
WriteToStreams( msg, Logger::ERR );
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
|
||||||
// Severity setter
|
|
||||||
void DefaultLogger::setLogSeverity( LogSeverity log_severity )
|
|
||||||
{
|
|
||||||
m_Severity = log_severity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
// Attachs a new stream
|
// Attachs a new stream
|
||||||
void DefaultLogger::attachStream( LogStream *pStream, unsigned int severity )
|
bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity )
|
||||||
{
|
{
|
||||||
if (!pStream)
|
if (!pStream)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
// fix (Aramis)
|
if (0 == severity) {
|
||||||
if (0 == severity)
|
|
||||||
{
|
|
||||||
severity = Logger::INFO | Logger::ERR | Logger::WARN | Logger::DEBUGGING;
|
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 )
|
if ( (*it)->m_pStream == pStream )
|
||||||
{
|
{
|
||||||
(*it)->m_uiErrorSeverity |= severity;
|
(*it)->m_uiErrorSeverity |= severity;
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream );
|
LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream );
|
||||||
m_StreamArray.push_back( pInfo );
|
m_StreamArray.push_back( pInfo );
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
// Detatch a stream
|
// Detatch a stream
|
||||||
void DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity )
|
bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity )
|
||||||
{
|
{
|
||||||
if (!pStream)
|
if (!pStream)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
// fix (Aramis)
|
if (0 == severity) {
|
||||||
if (0 == severity)
|
|
||||||
{
|
|
||||||
severity = Logger::INFO | Logger::ERR | Logger::WARN | Logger::DEBUGGING;
|
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 );
|
m_StreamArray.erase( it );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
// Constructor
|
// Constructor
|
||||||
DefaultLogger::DefaultLogger(LogSeverity severity)
|
DefaultLogger::DefaultLogger(LogSeverity severity)
|
||||||
|
|
||||||
: m_Severity ( severity )
|
: Logger ( severity )
|
||||||
, noRepeatMsg (false)
|
, noRepeatMsg (false)
|
||||||
, lastLen( 0 )
|
, lastLen( 0 )
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,8 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file Implementation of the DeterminePTypeHelperProcess and
|
/** @file FindDegenerates.cpp
|
||||||
* SortByPTypeProcess post-process steps.
|
* @brief Implementation of the FindDegenerates post-process step.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AssimpPCH.h"
|
#include "AssimpPCH.h"
|
||||||
|
@ -54,8 +54,8 @@ using namespace Assimp;
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Constructor to be privately used by Importer
|
// Constructor to be privately used by Importer
|
||||||
FindDegeneratesProcess::FindDegeneratesProcess()
|
FindDegeneratesProcess::FindDegeneratesProcess()
|
||||||
{
|
: configRemoveDegenerates (false)
|
||||||
}
|
{}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Destructor, private as well
|
// Destructor, private as well
|
||||||
|
@ -71,83 +71,144 @@ bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const
|
||||||
return 0 != (pFlags & aiProcess_FindDegenerates);
|
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.
|
// Executes the post processing step on the given imported data.
|
||||||
void FindDegeneratesProcess::Execute( aiScene* pScene)
|
void FindDegeneratesProcess::Execute( aiScene* pScene)
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->debug("FindDegeneratesProcess begin");
|
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]);
|
||||||
aiMesh* mesh = pScene->mMeshes[i];
|
|
||||||
mesh->mPrimitiveTypes = 0;
|
|
||||||
|
|
||||||
unsigned int deg = 0;
|
|
||||||
for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
|
|
||||||
{
|
|
||||||
aiFace& face = mesh->mFaces[a];
|
|
||||||
bool first = true;
|
|
||||||
|
|
||||||
// check whether the face contains degenerated entries
|
|
||||||
for (register unsigned int i = 0; i < face.mNumIndices; ++i)
|
|
||||||
{
|
|
||||||
for (register unsigned int a = i+1; a < face.mNumIndices; ++a)
|
|
||||||
{
|
|
||||||
if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[a]])
|
|
||||||
{
|
|
||||||
// we have found a matching vertex position
|
|
||||||
// remove the corresponding index from the array
|
|
||||||
for (unsigned int m = a; m < face.mNumIndices-1; ++m)
|
|
||||||
{
|
|
||||||
face.mIndices[m] = face.mIndices[m+1];
|
|
||||||
}
|
|
||||||
--a;
|
|
||||||
--face.mNumIndices;
|
|
||||||
|
|
||||||
// NOTE: we set the removed vertex index to an unique value
|
|
||||||
// to make sure the developer gets notified when his
|
|
||||||
// application attemps to access this data.
|
|
||||||
face.mIndices[face.mNumIndices] = 0xdeadbeef;
|
|
||||||
|
|
||||||
|
|
||||||
if(first)
|
|
||||||
{
|
|
||||||
++deg;
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to update the primitive flags array of the mesh.
|
|
||||||
// Unfortunately it is not possible to execute
|
|
||||||
// FindDegenerates before DeterminePType. The latter does
|
|
||||||
// nothing if the primitive flags have already been set by
|
|
||||||
// the loader - our changes would be ignored. Although
|
|
||||||
// we could use some tricks regarding - i.e setting
|
|
||||||
// mPrimitiveTypes to 0 in every case - but this is the cleanest
|
|
||||||
// way and causes no additional dependencies in the pipeline.
|
|
||||||
switch (face.mNumIndices)
|
|
||||||
{
|
|
||||||
case 1u:
|
|
||||||
mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
|
|
||||||
break;
|
|
||||||
case 2u:
|
|
||||||
mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
|
|
||||||
break;
|
|
||||||
case 3u:
|
|
||||||
mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (deg && !DefaultLogger::isNullLogger())
|
|
||||||
{
|
|
||||||
char s[64];
|
|
||||||
ASSIMP_itoa10(s,deg);
|
|
||||||
DefaultLogger::get()->warn(std::string("Found ") + s + " degenerated primitives");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DefaultLogger::get()->debug("FindDegeneratesProcess finished");
|
DefaultLogger::get()->debug("FindDegeneratesProcess finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Executes the post processing step on the given imported mesh
|
||||||
|
void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh)
|
||||||
|
{
|
||||||
|
mesh->mPrimitiveTypes = 0;
|
||||||
|
|
||||||
|
std::vector<bool> remove_me;
|
||||||
|
if (configRemoveDegenerates)
|
||||||
|
remove_me.resize(mesh->mNumFaces,false);
|
||||||
|
|
||||||
|
unsigned int deg = 0;
|
||||||
|
for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
|
||||||
|
{
|
||||||
|
aiFace& face = mesh->mFaces[a];
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
// check whether the face contains degenerated entries
|
||||||
|
for (register unsigned int i = 0; i < face.mNumIndices; ++i)
|
||||||
|
{
|
||||||
|
for (register unsigned int t = i+1; t < face.mNumIndices; ++t)
|
||||||
|
{
|
||||||
|
if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]])
|
||||||
|
{
|
||||||
|
// we have found a matching vertex position
|
||||||
|
// remove the corresponding index from the array
|
||||||
|
--face.mNumIndices;
|
||||||
|
for (unsigned int m = t; m < face.mNumIndices; ++m)
|
||||||
|
{
|
||||||
|
face.mIndices[m] = face.mIndices[m+1];
|
||||||
|
}
|
||||||
|
--t;
|
||||||
|
|
||||||
|
// NOTE: we set the removed vertex index to an unique value
|
||||||
|
// to make sure the developer gets notified when his
|
||||||
|
// application attemps to access this data.
|
||||||
|
face.mIndices[face.mNumIndices] = 0xdeadbeef;
|
||||||
|
|
||||||
|
if(first)
|
||||||
|
{
|
||||||
|
++deg;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configRemoveDegenerates) {
|
||||||
|
remove_me[a] = true;
|
||||||
|
goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to update the primitive flags array of the mesh.
|
||||||
|
// Unfortunately it is not possible to execute
|
||||||
|
// FindDegenerates before DeterminePType. The latter does
|
||||||
|
// nothing if the primitive flags have already been set by
|
||||||
|
// the loader - our changes would be ignored. Although
|
||||||
|
// we could use some tricks regarding - i.e setting
|
||||||
|
// mPrimitiveTypes to 0 in every case - but this is the cleanest
|
||||||
|
// way and causes no additional dependencies in the pipeline.
|
||||||
|
switch (face.mNumIndices)
|
||||||
|
{
|
||||||
|
case 1u:
|
||||||
|
mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
|
||||||
|
break;
|
||||||
|
case 2u:
|
||||||
|
mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
|
||||||
|
break;
|
||||||
|
case 3u:
|
||||||
|
mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
evil_jump_outside:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
|
||||||
|
if (configRemoveDegenerates && deg) {
|
||||||
|
unsigned int n = 0;
|
||||||
|
for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
|
||||||
|
{
|
||||||
|
aiFace& face_src = mesh->mFaces[a];
|
||||||
|
if (!remove_me[a]) {
|
||||||
|
aiFace& face_dest = mesh->mFaces[n++];
|
||||||
|
|
||||||
|
// Do a manual copy, keep the index array
|
||||||
|
face_dest.mNumIndices = face_src.mNumIndices;
|
||||||
|
face_dest.mIndices = face_src.mIndices;
|
||||||
|
|
||||||
|
// clear source
|
||||||
|
face_src.mNumIndices = 0;
|
||||||
|
face_src.mIndices = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Otherwise delete it if we don't need this face
|
||||||
|
delete[] face_src.mIndices;
|
||||||
|
face_src.mIndices = NULL;
|
||||||
|
face_src.mNumIndices = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Just leave the rest of the array unreferenced, we don't care for now
|
||||||
|
mesh->mNumFaces = n;
|
||||||
|
if (!mesh->mNumFaces) {
|
||||||
|
// WTF!?
|
||||||
|
// OK ... for completeness and because I'm not yet tired,
|
||||||
|
// let's write code that willl hopefully never be called
|
||||||
|
// (famous last words)
|
||||||
|
|
||||||
|
// OK ... bad idea.
|
||||||
|
throw new ImportErrorException("Mesh is empty after removal of degenerated primitives ... WTF!?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deg && !DefaultLogger::isNullLogger())
|
||||||
|
{
|
||||||
|
char s[64];
|
||||||
|
ASSIMP_itoa10(s,deg);
|
||||||
|
DefaultLogger::get()->warn(std::string("Found ") + s + " degenerated primitives");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -66,14 +66,44 @@ protected:
|
||||||
~FindDegeneratesProcess();
|
~FindDegeneratesProcess();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
// Check whether step is active
|
||||||
bool IsActive( unsigned int pFlags) const;
|
bool IsActive( unsigned int pFlags) const;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
// Execute step on a given scene
|
||||||
void Execute( aiScene* pScene);
|
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:
|
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
|
// allocate an array to hold the output normals
|
||||||
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
|
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
|
// iterate through all faces and compute per-face normals but store
|
||||||
// them per-vertex.
|
// them per-vertex.
|
||||||
|
|
|
@ -46,17 +46,15 @@ namespace HMP {
|
||||||
|
|
||||||
#include "./../include/Compiler/pushpack1.h"
|
#include "./../include/Compiler/pushpack1.h"
|
||||||
|
|
||||||
// to make it easier for ourselfes, we test the magic word against both "endianesses"
|
// to make it easier for us, 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]))
|
#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_BE_5 AI_MAKE_MAGIC("HMP5")
|
||||||
#define AI_HMP_MAGIC_NUMBER_LE_4 HMP_MAKE("4PMH")
|
#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_BE_7 AI_MAKE_MAGIC("HMP7")
|
||||||
#define AI_HMP_MAGIC_NUMBER_LE_5 HMP_MAKE("5PMH")
|
#define AI_HMP_MAGIC_NUMBER_LE_7 AI_MAKE_MAGIC("7PMH")
|
||||||
|
|
||||||
#define AI_HMP_MAGIC_NUMBER_BE_7 HMP_MAKE("HMP7")
|
|
||||||
#define AI_HMP_MAGIC_NUMBER_LE_7 HMP_MAKE("7PMH")
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Data structure for the header of a HMP5 file.
|
/** Data structure for the header of a HMP5 file.
|
||||||
|
|
|
@ -67,25 +67,34 @@ HMPImporter::~HMPImporter()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
const std::string extension = GetExtension(pFile);
|
||||||
// simple check of file extension is enough for the moment
|
if (extension == "hmp" )
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
return true;
|
||||||
// no file extension - can't read
|
|
||||||
if( pos == std::string::npos)return false;
|
|
||||||
std::string extension = pFile.substr( pos);
|
|
||||||
|
|
||||||
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
|
// if check for extension is not enough, check for the magic tokens
|
||||||
*it = tolower( *it);
|
if (!extension.length() || cs) {
|
||||||
|
uint32_t tokens[3];
|
||||||
|
tokens[0] = AI_HMP_MAGIC_NUMBER_LE_4;
|
||||||
|
tokens[1] = AI_HMP_MAGIC_NUMBER_LE_5;
|
||||||
|
tokens[2] = AI_HMP_MAGIC_NUMBER_LE_7;
|
||||||
|
return CheckMagicToken(pIOHandler,pFile,tokens,3,0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return extension == ".hmp";
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Get list of all file extensions that are handled by this loader
|
||||||
|
void HMPImporter::GetExtensionList(std::string& append)
|
||||||
|
{
|
||||||
|
append.append("*.hmp");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Imports the given file into the given scene structure.
|
// Imports the given file into the given scene structure.
|
||||||
void HMPImporter::InternReadFile( const std::string& pFile,
|
void HMPImporter::InternReadFile( const std::string& pFile,
|
||||||
aiScene* _pScene, IOSystem* _pIOHandler)
|
aiScene* _pScene, IOSystem* _pIOHandler)
|
||||||
{
|
{
|
||||||
pScene = _pScene;
|
pScene = _pScene;
|
||||||
pIOHandler = _pIOHandler;
|
pIOHandler = _pIOHandler;
|
||||||
|
@ -225,7 +234,8 @@ void HMPImporter::InternReadFile_HMP5( )
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate texture coordinates if necessary
|
// generate texture coordinates if necessary
|
||||||
if (pcHeader->numskins)GenerateTextureCoords(width,height);
|
if (pcHeader->numskins)
|
||||||
|
GenerateTextureCoords(width,height);
|
||||||
|
|
||||||
// now build a list of faces
|
// now build a list of faces
|
||||||
CreateOutputFaceList(width,height);
|
CreateOutputFaceList(width,height);
|
||||||
|
@ -477,7 +487,7 @@ void HMPImporter::GenerateTextureCoords(
|
||||||
|
|
||||||
for (unsigned int y = 0; y < height;++y) {
|
for (unsigned int y = 0; y < height;++y) {
|
||||||
for (unsigned int x = 0; x < width;++x,++uv) {
|
for (unsigned int x = 0; x < width;++x,++uv) {
|
||||||
uv->y = 1.0f-fY*y;
|
uv->y = fY*y;
|
||||||
uv->x = fX*x;
|
uv->x = fX*x;
|
||||||
uv->z = 0.0f;
|
uv->z = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
//!
|
/** @file HMPLoader.h
|
||||||
//! @file Declaration of the HMP importer class
|
* @brief Declaration of the HMP importer class
|
||||||
//!
|
*/
|
||||||
|
|
||||||
#ifndef AI_HMPLOADER_H_INCLUDED
|
#ifndef AI_HMPLOADER_H_INCLUDED
|
||||||
#define 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.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -85,10 +87,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.hmp");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** Imports the given file into the given scene structure.
|
||||||
|
|
|
@ -57,10 +57,10 @@ namespace Assimp {
|
||||||
namespace MDL {
|
namespace MDL {
|
||||||
|
|
||||||
// magic bytes used in Half Life 2 MDL models
|
// magic bytes used in Half Life 2 MDL models
|
||||||
#define AI_MDL_MAGIC_NUMBER_BE_HL2a MDL_MAKE("IDST")
|
#define AI_MDL_MAGIC_NUMBER_BE_HL2a AI_MAKE_MAGIC("IDST")
|
||||||
#define AI_MDL_MAGIC_NUMBER_LE_HL2a MDL_MAKE("TSDI")
|
#define AI_MDL_MAGIC_NUMBER_LE_HL2a AI_MAKE_MAGIC("TSDI")
|
||||||
#define AI_MDL_MAGIC_NUMBER_BE_HL2b MDL_MAKE("IDSQ")
|
#define AI_MDL_MAGIC_NUMBER_BE_HL2b AI_MAKE_MAGIC("IDSQ")
|
||||||
#define AI_MDL_MAGIC_NUMBER_LE_HL2b MDL_MAKE("QSDI")
|
#define AI_MDL_MAGIC_NUMBER_LE_HL2b AI_MAKE_MAGIC("QSDI")
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** \struct Header_HL2
|
/** \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"
|
#include "AssimpPCH.h"
|
||||||
|
|
||||||
|
@ -81,26 +83,17 @@ IRRImporter::~IRRImporter()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
/* NOTE: A simple check for the file extension is not enough
|
||||||
* here. Irrmesh and irr are easy, but xml is too generic
|
* here. Irrmesh and irr are easy, but xml is too generic
|
||||||
* and could be collada, too. So we need to open the file and
|
* and could be collada, too. So we need to open the file and
|
||||||
* search for typical tokens.
|
* search for typical tokens.
|
||||||
*/
|
*/
|
||||||
|
const std::string extension = GetExtension(pFile);
|
||||||
|
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
if (extension == "irr")return true;
|
||||||
|
else if (extension == "xml" || checkSig)
|
||||||
// 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 CanRead() is called in order to check whether we
|
/* If CanRead() is called in order to check whether we
|
||||||
* support a specific file extension in general pIOHandler
|
* 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
|
// read the output frame rate of all node animation channels
|
||||||
fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS,100);
|
fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS,100);
|
||||||
if (!fps)
|
if (fps < 10.) {
|
||||||
{
|
|
||||||
DefaultLogger::get()->error("IRR: Invalid FPS configuration");
|
DefaultLogger::get()->error("IRR: Invalid FPS configuration");
|
||||||
fps = 100;
|
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
|
// 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
|
// Check whether there are texture properties defined - setup
|
||||||
// the desired texture mapping mode for all of them and ignore
|
// 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
|
// rebuild the output array
|
||||||
if (p.size() > mat->mNumAllocated) {
|
if (p.size() > mat->mNumAllocated) {
|
||||||
delete[] mat->mProperties;
|
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();
|
mat->mNumProperties = (unsigned int)p.size();
|
||||||
::memcpy(mat->mProperties,&p[0],sizeof(void*)*mat->mNumProperties);
|
::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
|
// Get the loaded mesh from the scene and add it to
|
||||||
// the list of all scenes to be attached to the
|
// the list of all scenes to be attached to the
|
||||||
// graph we're currently building
|
// graph we're currently building
|
||||||
aiScene* scene = batch.GetImport(root->meshPath);
|
aiScene* scene = batch.GetImport(root->id);
|
||||||
if (!scene)
|
if (!scene)
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->error("IRR: Unable to load external file: "
|
DefaultLogger::get()->error("IRR: Unable to load external file: "
|
||||||
|
@ -688,6 +685,8 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
attach.push_back(AttachmentInfo(scene,rootOut));
|
attach.push_back(AttachmentInfo(scene,rootOut));
|
||||||
|
|
||||||
|
#if 0
|
||||||
meshTrafoAssign = 1;
|
meshTrafoAssign = 1;
|
||||||
|
|
||||||
// If the root node of the scene is animated - and *this* node
|
// 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)
|
#endif
|
||||||
scene->mRootNode->mTransformation *= AI_TO_IRR_MATRIX;
|
// if (1 == meshTrafoAssign)
|
||||||
|
// scene->mRootNode->mTransformation = AI_TO_IRR_MATRIX * scene->mRootNode->mTransformation;
|
||||||
|
|
||||||
|
|
||||||
// Now combine the material we've loaded for this mesh
|
// 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,
|
// NOTE: Each mesh should have exactly one material assigned,
|
||||||
// but we do it in a separate loop if this behaviour changes
|
// 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)
|
for (unsigned int i = 0; i < scene->mNumMeshes;++i)
|
||||||
{
|
{
|
||||||
// Process material flags
|
// Process material flags
|
||||||
|
@ -901,8 +901,7 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
|
||||||
rootOut->mNumMeshes = (unsigned int)meshes.size() - oldMeshSize;
|
rootOut->mNumMeshes = (unsigned int)meshes.size() - oldMeshSize;
|
||||||
rootOut->mMeshes = new unsigned int[rootOut->mNumMeshes];
|
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;
|
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
|
// Now compute the final local transformation matrix of the
|
||||||
// node from the given translation, rotation and scaling values.
|
// node from the given translation, rotation and scaling values.
|
||||||
// (the rotation is given in Euler angles, XYZ order)
|
// (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) );
|
rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation) );
|
||||||
|
|
||||||
// apply scaling
|
// apply scaling
|
||||||
|
@ -920,20 +920,20 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
|
||||||
mat.a1 *= root->scaling.x;
|
mat.a1 *= root->scaling.x;
|
||||||
mat.b1 *= root->scaling.x;
|
mat.b1 *= root->scaling.x;
|
||||||
mat.c1 *= root->scaling.x;
|
mat.c1 *= root->scaling.x;
|
||||||
mat.a2 *= root->scaling.y;
|
mat.a2 *= root->scaling.z;
|
||||||
mat.b2 *= root->scaling.y;
|
mat.b2 *= root->scaling.z;
|
||||||
mat.c2 *= root->scaling.y;
|
mat.c2 *= root->scaling.z;
|
||||||
mat.a3 *= root->scaling.z;
|
mat.a3 *= root->scaling.y;
|
||||||
mat.b3 *= root->scaling.z;
|
mat.b3 *= root->scaling.y;
|
||||||
mat.c3 *= root->scaling.z;
|
mat.c3 *= root->scaling.y;
|
||||||
|
|
||||||
// apply translation
|
// apply translation
|
||||||
mat.a4 += root->position.x;
|
mat.a4 += root->position.x;
|
||||||
mat.b4 += root->position.y;
|
mat.b4 += root->position.z;
|
||||||
mat.c4 += root->position.z;
|
mat.c4 += root->position.y;
|
||||||
|
|
||||||
if (meshTrafoAssign == 2)
|
//if (meshTrafoAssign == 2)
|
||||||
mat *= AI_TO_IRR_MATRIX;
|
// mat *= AI_TO_IRR_MATRIX;
|
||||||
|
|
||||||
// now compute animations for the node
|
// now compute animations for the node
|
||||||
ComputeAnimations(root,rootOut, anims);
|
ComputeAnimations(root,rootOut, anims);
|
||||||
|
@ -1446,7 +1446,7 @@ void IRRImporter::InternReadFile( const std::string& pFile,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
batch.AddLoadRequest(prop.value,pp,&map);
|
curNode->id = batch.AddLoadRequest(prop.value,pp,&map);
|
||||||
curNode->meshPath = prop.value;
|
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
|
/* Now iterate through all cameras and compute their final (horizontal) FOV
|
||||||
*/
|
*/
|
||||||
for (std::vector<aiCamera*>::iterator it = cameras.begin(), end = cameras.end();
|
for (std::vector<aiCamera*>::iterator it = cameras.begin(), end = cameras.end();
|
||||||
it != end; ++it)
|
it != end; ++it) {
|
||||||
{
|
|
||||||
aiCamera* cam = *it;
|
aiCamera* cam = *it;
|
||||||
if (cam->mAspect) // screen aspect could be missing
|
if (cam->mAspect) // screen aspect could be missing
|
||||||
{
|
{
|
||||||
|
@ -1625,8 +1624,8 @@ void IRRImporter::InternReadFile( const std::string& pFile,
|
||||||
* attachment points in the scenegraph.
|
* attachment points in the scenegraph.
|
||||||
*/
|
*/
|
||||||
SceneCombiner::MergeScenes(&pScene,tempScene,attach,
|
SceneCombiner::MergeScenes(&pScene,tempScene,attach,
|
||||||
AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES |
|
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
|
||||||
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES);
|
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
|
/* 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;
|
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
|
/* Finished ... everything destructs automatically and all
|
||||||
* temporary scenes have already been deleted by MergeScenes()
|
* 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)
|
/** @file IRRLoader.h
|
||||||
importer class.
|
* @brief Declaration of the .irrMesh (Irrlight Engine Mesh Format)
|
||||||
|
* importer class.
|
||||||
*/
|
*/
|
||||||
#ifndef AI_IRRLOADER_H_INCLUDED
|
#ifndef AI_IRRLOADER_H_INCLUDED
|
||||||
#define 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.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details.
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -203,6 +205,7 @@ private:
|
||||||
|
|
||||||
// Meshes: path to the mesh to be loaded
|
// Meshes: path to the mesh to be loaded
|
||||||
std::string meshPath;
|
std::string meshPath;
|
||||||
|
unsigned int id;
|
||||||
|
|
||||||
// Meshes: List of materials to be assigned
|
// Meshes: List of materials to be assigned
|
||||||
// along with their corresponding material flags
|
// along with their corresponding material flags
|
||||||
|
@ -297,8 +300,11 @@ private:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
/** Configuration option: desired output FPS */
|
||||||
double fps;
|
double fps;
|
||||||
|
|
||||||
|
/** Configuration option: speed flag was set? */
|
||||||
|
bool configSpeedFlag;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace Assimp
|
} // end of namespace Assimp
|
||||||
|
|
|
@ -67,26 +67,17 @@ IRRMeshImporter::~IRRMeshImporter()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
/* NOTE: A simple check for the file extension is not enough
|
||||||
* here. Irrmesh and irr are easy, but xml is too generic
|
* here. Irrmesh and irr are easy, but xml is too generic
|
||||||
* and could be collada, too. So we need to open the file and
|
* and could be collada, too. So we need to open the file and
|
||||||
* search for typical tokens.
|
* search for typical tokens.
|
||||||
*/
|
*/
|
||||||
|
const std::string extension = GetExtension(pFile);
|
||||||
|
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
if (extension == "irrmesh")return true;
|
||||||
|
else if (extension == "xml" || checkSig)
|
||||||
// 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 CanRead() is called to check whether the loader
|
/* If CanRead() is called to check whether the loader
|
||||||
* supports a specific file extension in general we
|
* supports a specific file extension in general we
|
||||||
|
@ -99,6 +90,14 @@ bool IRRMeshImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) c
|
||||||
return false;
|
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.
|
// Imports the given file into the given scene structure.
|
||||||
void IRRMeshImporter::InternReadFile( const std::string& pFile,
|
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)
|
/** @file IRRMeshLoader.h
|
||||||
importer class. */
|
* @brief Declaration of the .irrMesh (Irrlight Engine Mesh Format)
|
||||||
|
* importer class.
|
||||||
|
*/
|
||||||
#ifndef AI_IRRMESHLOADER_H_INCLUDED
|
#ifndef AI_IRRMESHLOADER_H_INCLUDED
|
||||||
#define 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.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details.
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -80,16 +83,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** Imports the given file into the given scene structure.
|
||||||
|
|
|
@ -294,7 +294,11 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags)
|
||||||
// material type (shader)
|
// material type (shader)
|
||||||
if (prop.name == "Type")
|
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;
|
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"
|
#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
|
// Internal headers
|
||||||
// =======================================================================================
|
// =======================================================================================
|
||||||
|
@ -135,6 +143,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#ifndef AI_BUILD_NO_TERRAGEN_IMPORTER
|
#ifndef AI_BUILD_NO_TERRAGEN_IMPORTER
|
||||||
# include "TerragenLoader.h"
|
# include "TerragenLoader.h"
|
||||||
#endif
|
#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
|
// PostProcess-Steps
|
||||||
|
@ -325,6 +347,18 @@ Importer::Importer()
|
||||||
#if (!defined AI_BUILD_NO_TERRAGEN_IMPORTER)
|
#if (!defined AI_BUILD_NO_TERRAGEN_IMPORTER)
|
||||||
mImporter.push_back( new TerragenImporter());
|
mImporter.push_back( new TerragenImporter());
|
||||||
#endif
|
#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
|
// Add an instance of each post processing step here in the order
|
||||||
|
@ -333,10 +367,6 @@ Importer::Importer()
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
mPostProcessingSteps.reserve(25);
|
mPostProcessingSteps.reserve(25);
|
||||||
|
|
||||||
#if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS)
|
|
||||||
mPostProcessingSteps.push_back( new ValidateDSProcess());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (!defined AI_BUILD_NO_REMOVEVC_PROCESS)
|
#if (!defined AI_BUILD_NO_REMOVEVC_PROCESS)
|
||||||
mPostProcessingSteps.push_back( new RemoveVCProcess());
|
mPostProcessingSteps.push_back( new RemoveVCProcess());
|
||||||
#endif
|
#endif
|
||||||
|
@ -548,10 +578,9 @@ bool Importer::IsDefaultIOHandler()
|
||||||
return mIsDefaultHandler;
|
return mIsDefaultHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Validate post process step flags
|
// Validate post process step flags
|
||||||
bool ValidateFlags(unsigned int pFlags)
|
bool _ValidateFlags(unsigned int pFlags)
|
||||||
{
|
{
|
||||||
if (pFlags & aiProcess_GenSmoothNormals &&
|
if (pFlags & aiProcess_GenSmoothNormals &&
|
||||||
pFlags & aiProcess_GenNormals)
|
pFlags & aiProcess_GenNormals)
|
||||||
|
@ -562,7 +591,6 @@ bool ValidateFlags(unsigned int pFlags)
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif // ! DEBUG
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Free the current scene
|
// Free the current scene
|
||||||
|
@ -602,12 +630,50 @@ aiScene* Importer::GetOrphanedScene()
|
||||||
return s;
|
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.
|
// Reads the given file and returns its contents if successful.
|
||||||
const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
|
const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
|
||||||
{
|
{
|
||||||
// Validate the flags
|
// In debug builds, run a basic flag validation
|
||||||
ai_assert(ValidateFlags(pFlags));
|
ai_assert(_ValidateFlags(pFlags));
|
||||||
|
|
||||||
const std::string pFile(_pFile);
|
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().
|
// that might be thrown by STL containers or by new().
|
||||||
// ImportErrorException's are throw by ourselves and caught elsewhere.
|
// ImportErrorException's are throw by ourselves and caught elsewhere.
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
|
#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
|
||||||
try
|
try
|
||||||
|
#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
|
||||||
{
|
{
|
||||||
// Check whether this Importer instance has already loaded
|
// Check whether this Importer instance has already loaded
|
||||||
// a scene. In this case we need to delete the old one
|
// a scene. In this case we need to delete the old one
|
||||||
|
@ -636,21 +704,35 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
|
||||||
|
|
||||||
// Find an worker class which can handle the file
|
// Find an worker class which can handle the file
|
||||||
BaseImporter* imp = NULL;
|
BaseImporter* imp = NULL;
|
||||||
for( unsigned int a = 0; a < mImporter.size(); a++)
|
for( unsigned int a = 0; a < mImporter.size(); a++) {
|
||||||
{
|
|
||||||
if( mImporter[a]->CanRead( pFile, mIOHandler))
|
if( mImporter[a]->CanRead( pFile, mIOHandler, false)) {
|
||||||
{
|
|
||||||
imp = mImporter[a];
|
imp = mImporter[a];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put a proper error message if no suitable importer was found
|
if (!imp)
|
||||||
if( !imp)
|
|
||||||
{
|
{
|
||||||
mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
|
// not so bad yet ... try format auto detection.
|
||||||
DefaultLogger::get()->error(mErrorString);
|
std::string::size_type s = pFile.find_last_of('.');
|
||||||
return NULL;
|
if (s != std::string::npos) {
|
||||||
|
DefaultLogger::get()->info("File extension now known, trying signature-based detection");
|
||||||
|
for( unsigned int a = 0; a < mImporter.size(); a++) {
|
||||||
|
|
||||||
|
if( mImporter[a]->CanRead( pFile, mIOHandler, true)) {
|
||||||
|
imp = mImporter[a];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Put a proper error message if no suitable importer was found
|
||||||
|
if( !imp)
|
||||||
|
{
|
||||||
|
mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
|
||||||
|
DefaultLogger::get()->error(mErrorString);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch the reading to the worker class for this format
|
// Dispatch the reading to the worker class for this format
|
||||||
|
@ -661,7 +743,19 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
|
||||||
// If successful, apply all active post processing steps to the imported data
|
// If successful, apply all active post processing steps to the imported data
|
||||||
if( mScene)
|
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);
|
ScenePreprocessor pre(mScene);
|
||||||
pre.ProcessScene();
|
pre.ProcessScene();
|
||||||
|
|
||||||
|
@ -669,11 +763,11 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
if (bExtraVerbose)
|
if (bExtraVerbose)
|
||||||
{
|
{
|
||||||
#if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS)
|
#ifndef AI_BUILD_NO_VALIDATEDS_PROCESS
|
||||||
|
|
||||||
DefaultLogger::get()->error("Extra verbose mode not available, library"
|
DefaultLogger::get()->error("Extra verbose mode not available, library"
|
||||||
" wasn't build with the ValidateDS-Step");
|
" wasn't build with the ValidateDS-Step");
|
||||||
#endif
|
#endif // no validation
|
||||||
|
|
||||||
|
|
||||||
pFlags |= aiProcess_ValidateDataStructure;
|
pFlags |= aiProcess_ValidateDataStructure;
|
||||||
|
@ -694,14 +788,16 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
|
||||||
|
|
||||||
#ifndef AI_BUILD_NO_VALIDATEDS_PROCESS
|
#ifndef AI_BUILD_NO_VALIDATEDS_PROCESS
|
||||||
continue;
|
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
|
// VaidateDataStructureStep again after each step
|
||||||
if (bExtraVerbose && a)
|
if (bExtraVerbose)
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->debug("Extra verbose: revalidating data structures");
|
DefaultLogger::get()->debug("Extra verbose: revalidating data structures");
|
||||||
((ValidateDSProcess*)mPostProcessingSteps[0])->ExecuteOnScene (this);
|
|
||||||
|
ValidateDSProcess ds;
|
||||||
|
ds.ExecuteOnScene (this);
|
||||||
if( !mScene)
|
if( !mScene)
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->error("Extra verbose: failed to revalidate data structures");
|
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
|
// 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
|
// clear any data allocated by post-process steps
|
||||||
mPPShared->Clean();
|
mPPShared->Clean();
|
||||||
}
|
}
|
||||||
|
#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
|
||||||
catch (std::exception &e)
|
catch (std::exception &e)
|
||||||
{
|
{
|
||||||
#if (defined _MSC_VER) && (defined _CPPRTTI)
|
#if (defined _MSC_VER) && (defined _CPPRTTI)
|
||||||
|
@ -729,6 +827,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
|
||||||
DefaultLogger::get()->error(mErrorString);
|
DefaultLogger::get()->error(mErrorString);
|
||||||
delete mScene;mScene = NULL;
|
delete mScene;mScene = NULL;
|
||||||
}
|
}
|
||||||
|
#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
|
||||||
|
|
||||||
// either successful or failure - the pointer expresses it anyways
|
// either successful or failure - the pointer expresses it anyways
|
||||||
return mScene;
|
return mScene;
|
||||||
|
@ -750,7 +849,7 @@ BaseImporter* Importer::FindLoader (const char* _szExtension)
|
||||||
i != mImporter.end();++i)
|
i != mImporter.end();++i)
|
||||||
{
|
{
|
||||||
// pass the file extension to the CanRead(..,NULL)-method
|
// pass the file extension to the CanRead(..,NULL)-method
|
||||||
if ((*i)->CanRead(szExtension,NULL))return *i;
|
if ((*i)->CanRead(szExtension,NULL,false))return *i;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -765,8 +864,10 @@ void Importer::GetExtensionList(aiString& szOut)
|
||||||
i = mImporter.begin();
|
i = mImporter.begin();
|
||||||
i != mImporter.end();++i,++iNum)
|
i != mImporter.end();++i,++iNum)
|
||||||
{
|
{
|
||||||
// insert a comma as delimiter character
|
// Insert a comma as delimiter character
|
||||||
if (0 != iNum)
|
// 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(";");
|
tmp.append(";");
|
||||||
|
|
||||||
(*i)->GetExtensionList(tmp);
|
(*i)->GetExtensionList(tmp);
|
||||||
|
|
|
@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
* <br>
|
* <br>
|
||||||
* The algorithm is roughly basing on this paper:
|
* The algorithm is roughly basing on this paper:
|
||||||
* http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf
|
* http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf
|
||||||
|
* ... TODO: implement overdraw reduction
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AssimpPCH.h"
|
#include "AssimpPCH.h"
|
||||||
|
@ -54,16 +55,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
#if _MSC_VER >= 1400
|
|
||||||
# define sprintf sprintf_s
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Constructor to be privately used by Importer
|
// Constructor to be privately used by Importer
|
||||||
ImproveCacheLocalityProcess::ImproveCacheLocalityProcess()
|
ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() {
|
||||||
{
|
configCacheDepth = PP_ICL_PTCACHE_SIZE;
|
||||||
// nothing to do here
|
|
||||||
configCacheDepth = 12; // hardcoded to 12 at the moment
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -80,28 +75,48 @@ bool ImproveCacheLocalityProcess::IsActive( unsigned int pFlags) const
|
||||||
return (pFlags & aiProcess_ImproveCacheLocality) != 0;
|
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.
|
// Executes the post processing step on the given imported data.
|
||||||
void ImproveCacheLocalityProcess::Execute( aiScene* pScene)
|
void ImproveCacheLocalityProcess::Execute( aiScene* pScene)
|
||||||
{
|
{
|
||||||
if (!pScene->mNumMeshes)
|
if (!pScene->mNumMeshes) {
|
||||||
{
|
|
||||||
DefaultLogger::get()->debug("ImproveCacheLocalityProcess skipped; there are no meshes");
|
DefaultLogger::get()->debug("ImproveCacheLocalityProcess skipped; there are no meshes");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultLogger::get()->debug("ImproveCacheLocalityProcess begin");
|
DefaultLogger::get()->debug("ImproveCacheLocalityProcess begin");
|
||||||
|
|
||||||
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
|
float out = 0.f;
|
||||||
{
|
unsigned int numf = 0, numm = 0;
|
||||||
this->ProcessMesh( pScene->mMeshes[a],a);
|
for( unsigned int a = 0; a < pScene->mNumMeshes; a++){
|
||||||
|
const float res = ProcessMesh( pScene->mMeshes[a],a);
|
||||||
|
if (res) {
|
||||||
|
numf += pScene->mMeshes[a]->mNumFaces;
|
||||||
|
out += res;
|
||||||
|
++numm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!DefaultLogger::isNullLogger()) {
|
||||||
|
char szBuff[128]; // should be sufficiently large in every case
|
||||||
|
::sprintf(szBuff,"Cache relevant are %i meshes (%i faces). Average output ACMR is %f",
|
||||||
|
numm,numf,out/numf);
|
||||||
|
|
||||||
|
DefaultLogger::get()->info(szBuff);
|
||||||
|
DefaultLogger::get()->debug("ImproveCacheLocalityProcess finished. ");
|
||||||
}
|
}
|
||||||
DefaultLogger::get()->debug("ImproveCacheLocalityProcess finished. ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Improves the cache coherency of a specific mesh
|
// 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);
|
ai_assert(NULL != pMesh);
|
||||||
|
|
||||||
|
@ -109,52 +124,50 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
|
||||||
// - there must be vertices and faces (haha)
|
// - there must be vertices and faces (haha)
|
||||||
// - all faces must be triangulated
|
// - all faces must be triangulated
|
||||||
if (!pMesh->HasFaces() || !pMesh->HasPositions())
|
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");
|
DefaultLogger::get()->error("This algorithm works on triangle meshes only");
|
||||||
return;
|
return 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the input ACMR ...
|
float fACMR = 3.f;
|
||||||
unsigned int* piFIFOStack = new unsigned int[this->configCacheDepth];
|
|
||||||
::memset(piFIFOStack,0xff,this->configCacheDepth*sizeof(unsigned int));
|
|
||||||
unsigned int* piCur = piFIFOStack;
|
|
||||||
const unsigned int* const piCurEnd = piFIFOStack + this->configCacheDepth;
|
|
||||||
|
|
||||||
// count the number of cache misses
|
|
||||||
unsigned int iCacheMisses = 0;
|
|
||||||
|
|
||||||
const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces;
|
const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces;
|
||||||
for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace)
|
|
||||||
|
// Input ACMR is for logging purposes only
|
||||||
|
if (!DefaultLogger::isNullLogger())
|
||||||
{
|
{
|
||||||
for (unsigned int qq = 0; qq < 3;++qq)
|
unsigned int* piFIFOStack = new unsigned int[configCacheDepth];
|
||||||
|
::memset(piFIFOStack,0xff,configCacheDepth*sizeof(unsigned int));
|
||||||
|
unsigned int* piCur = piFIFOStack;
|
||||||
|
const unsigned int* const piCurEnd = piFIFOStack + configCacheDepth;
|
||||||
|
|
||||||
|
// count the number of cache misses
|
||||||
|
unsigned int iCacheMisses = 0;
|
||||||
|
for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace)
|
||||||
{
|
{
|
||||||
bool bInCache = false;
|
for (unsigned int qq = 0; qq < 3;++qq)
|
||||||
for (unsigned int* pp = piFIFOStack;pp < piCurEnd;++pp)
|
|
||||||
{
|
{
|
||||||
if (*pp == pcFace->mIndices[qq])
|
bool bInCache = false;
|
||||||
|
for (unsigned int* pp = piFIFOStack;pp < piCurEnd;++pp)
|
||||||
{
|
{
|
||||||
// the vertex is in cache
|
if (*pp == pcFace->mIndices[qq]) {
|
||||||
bInCache = true;
|
// the vertex is in cache
|
||||||
break;
|
bInCache = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!bInCache)
|
||||||
|
{
|
||||||
|
++iCacheMisses;
|
||||||
|
if (piCurEnd == piCur)piCur = piFIFOStack;
|
||||||
|
*piCur++ = pcFace->mIndices[qq];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!bInCache)
|
|
||||||
{
|
|
||||||
++iCacheMisses;
|
|
||||||
if (piCurEnd == piCur)piCur = piFIFOStack;
|
|
||||||
*piCur++ = pcFace->mIndices[qq];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
delete[] piFIFOStack;
|
||||||
delete[] piFIFOStack;
|
fACMR = (float)iCacheMisses / pMesh->mNumFaces;
|
||||||
float fACMR = (float)iCacheMisses / pMesh->mNumFaces;
|
if (3.0 == fACMR) {
|
||||||
if (3.0 == fACMR)
|
|
||||||
{
|
|
||||||
if (!DefaultLogger::isNullLogger())
|
|
||||||
{
|
|
||||||
char szBuff[128]; // should be sufficiently large in every case
|
char szBuff[128]; // should be sufficiently large in every case
|
||||||
|
|
||||||
// the JoinIdenticalVertices process has not been executed on this
|
// 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 ...
|
// smaller than 3.0 ...
|
||||||
::sprintf(szBuff,"Mesh %i: Not suitable for vcache optimization",meshNum);
|
::sprintf(szBuff,"Mesh %i: Not suitable for vcache optimization",meshNum);
|
||||||
DefaultLogger::get()->warn(szBuff);
|
DefaultLogger::get()->warn(szBuff);
|
||||||
|
return 0.f;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// first we need to build a vertex-triangle adjacency list
|
// 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);
|
iMaxRefTris = std::max(iMaxRefTris,*piCur);
|
||||||
}
|
}
|
||||||
unsigned int* piCandidates = new unsigned int[iMaxRefTris*3];
|
unsigned int* piCandidates = new unsigned int[iMaxRefTris*3];
|
||||||
iCacheMisses = 0;
|
unsigned int iCacheMisses = 0;
|
||||||
|
|
||||||
/** PSEUDOCODE for the algorithm
|
/** PSEUDOCODE for the algorithm
|
||||||
|
|
||||||
|
@ -236,7 +249,7 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
|
||||||
|
|
||||||
int ivdx = 0;
|
int ivdx = 0;
|
||||||
int ics = 1;
|
int ics = 1;
|
||||||
int iStampCnt = this->configCacheDepth+1;
|
int iStampCnt = configCacheDepth+1;
|
||||||
while (ivdx >= 0)
|
while (ivdx >= 0)
|
||||||
{
|
{
|
||||||
unsigned int icnt = piNumTriPtrNoModify[ivdx];
|
unsigned int icnt = piNumTriPtrNoModify[ivdx];
|
||||||
|
@ -274,7 +287,7 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
|
||||||
*piCSIter++ = dp;
|
*piCSIter++ = dp;
|
||||||
|
|
||||||
// if the vertex is not yet in cache, set its cache count
|
// 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++;
|
piCachingStamps[dp] = iStampCnt++;
|
||||||
++iCacheMisses;
|
++iCacheMisses;
|
||||||
|
@ -302,7 +315,7 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
|
||||||
|
|
||||||
// will the vertex be in cache, even after fanning occurs?
|
// will the vertex be in cache, even after fanning occurs?
|
||||||
unsigned int tmp;
|
unsigned int tmp;
|
||||||
if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= this->configCacheDepth)
|
if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= configCacheDepth)
|
||||||
priority = tmp;
|
priority = tmp;
|
||||||
// keep best candidate
|
// keep best candidate
|
||||||
if (priority > max_priority)
|
if (priority > max_priority)
|
||||||
|
@ -344,18 +357,24 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
float fACMR2 = 0.0f;
|
||||||
if (!DefaultLogger::isNullLogger())
|
if (!DefaultLogger::isNullLogger())
|
||||||
{
|
{
|
||||||
char szBuff[128]; // should be sufficiently large in every case
|
fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
|
||||||
float fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
|
|
||||||
|
|
||||||
::sprintf(szBuff,"Mesh %i | ACMR in: %f out: %f | ~%.1f%%",meshNum,fACMR,fACMR2,
|
// very intense verbose logging ... prepare for much text if there are many meshes
|
||||||
((fACMR - fACMR2) / fACMR) * 100.f);
|
if ( DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
|
||||||
DefaultLogger::get()->info(szBuff);
|
char szBuff[128]; // should be sufficiently large in every case
|
||||||
|
|
||||||
|
::sprintf(szBuff,"Mesh %i | ACMR in: %f out: %f | ~%.1f%%",meshNum,fACMR,fACMR2,
|
||||||
|
((fACMR - fACMR2) / fACMR) * 100.f);
|
||||||
|
DefaultLogger::get()->debug(szBuff);
|
||||||
|
}
|
||||||
|
|
||||||
|
fACMR2 *= pMesh->mNumFaces;
|
||||||
}
|
}
|
||||||
// sort the output index buffer back to the input array
|
// sort the output index buffer back to the input array
|
||||||
piCSIter = piIBOutput;
|
piCSIter = piIBOutput;
|
||||||
|
|
||||||
for (aiFace* pcFace = pMesh->mFaces; pcFace != pcEnd;++pcFace)
|
for (aiFace* pcFace = pMesh->mFaces; pcFace != pcEnd;++pcFace)
|
||||||
{
|
{
|
||||||
pcFace->mIndices[0] = *piCSIter++;
|
pcFace->mIndices[0] = *piCSIter++;
|
||||||
|
@ -368,4 +387,5 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
|
||||||
delete[] piIBOutput;
|
delete[] piIBOutput;
|
||||||
delete[] piCandidates;
|
delete[] piCandidates;
|
||||||
delete[] piNumTriPtrNoModify;
|
delete[] piNumTriPtrNoModify;
|
||||||
|
return fACMR2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,30 +70,28 @@ protected:
|
||||||
~ImproveCacheLocalityProcess();
|
~ImproveCacheLocalityProcess();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the processing step is present in the given flag field.
|
// Check whether the pp step is active
|
||||||
* @param pFlags The processing flags the importer was called with. A bitwise
|
|
||||||
* combination of #aiPostProcessSteps.
|
|
||||||
* @return true if the process is present in this flag fields, false if not.
|
|
||||||
*/
|
|
||||||
bool IsActive( unsigned int pFlags) const;
|
bool IsActive( unsigned int pFlags) const;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Executes the post processing step on the given imported data.
|
// Executes the pp step on a given scene
|
||||||
* At the moment a process is not supposed to fail.
|
|
||||||
* @param pScene The imported data to work at.
|
|
||||||
*/
|
|
||||||
void Execute( aiScene* pScene);
|
void Execute( aiScene* pScene);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Configures the pp step
|
||||||
|
void SetupProperties(const Importer* pImp);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Executes the postprocessing step on the given mesh
|
/** Executes the postprocessing step on the given mesh
|
||||||
* @param pMesh The mesh to process.
|
* @param pMesh The mesh to process.
|
||||||
* @param meshNum Index of 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
|
//! Configuration parameter: specifies the size of the cache to
|
||||||
//! optimize the vertex data for.
|
//! optimize the vertex data for.
|
||||||
unsigned int configCacheDepth;
|
unsigned int configCacheDepth;
|
||||||
|
|
|
@ -51,9 +51,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
#if _MSC_VER >= 1400
|
// Data structure to keep a vertex in an interlaced format
|
||||||
# define sprintf sprintf_s
|
struct Vertex
|
||||||
#endif
|
{
|
||||||
|
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
|
// Constructor to be privately used by Importer
|
||||||
|
@ -75,7 +81,6 @@ bool JoinVerticesProcess::IsActive( unsigned int pFlags) const
|
||||||
{
|
{
|
||||||
return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
|
return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Executes the post processing step on the given imported data.
|
// Executes the post processing step on the given imported data.
|
||||||
void JoinVerticesProcess::Execute( aiScene* pScene)
|
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
|
// get the total number of vertices BEFORE the step is executed
|
||||||
int iNumOldVertices = 0;
|
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;
|
iNumOldVertices += pScene->mMeshes[a]->mNumVertices;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute the step
|
// execute the step
|
||||||
int iNumVertices = 0;
|
int iNumVertices = 0;
|
||||||
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
|
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
|
||||||
{
|
iNumVertices += ProcessMesh( pScene->mMeshes[a],a);
|
||||||
iNumVertices += this->ProcessMesh( pScene->mMeshes[a],a);
|
|
||||||
}
|
}
|
||||||
// if logging is active, print detailled statistics
|
|
||||||
|
// if logging is active, print detailed statistics
|
||||||
if (!DefaultLogger::isNullLogger())
|
if (!DefaultLogger::isNullLogger())
|
||||||
{
|
{
|
||||||
if (iNumOldVertices == iNumVertices)DefaultLogger::get()->debug("JoinVerticesProcess finished ");
|
if (iNumOldVertices == iNumVertices)DefaultLogger::get()->debug("JoinVerticesProcess finished ");
|
||||||
|
@ -117,81 +123,103 @@ void JoinVerticesProcess::Execute( aiScene* pScene)
|
||||||
// Unites identical vertices in the given mesh
|
// Unites identical vertices in the given mesh
|
||||||
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
{
|
{
|
||||||
// helper structure to hold all the data a single vertex can possibly have
|
BOOST_STATIC_ASSERT( AI_MAX_NUMBER_OF_COLOR_SETS == 4);
|
||||||
typedef struct Vertex vertex;
|
BOOST_STATIC_ASSERT( AI_MAX_NUMBER_OF_TEXTURECOORDS == 4);
|
||||||
|
|
||||||
|
// Return early if we don't have any positions
|
||||||
if (!pMesh->HasPositions() || !pMesh->HasFaces())
|
if (!pMesh->HasPositions() || !pMesh->HasFaces())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
struct Vertex
|
// We'll never have more vertices afterwards.
|
||||||
{
|
|
||||||
aiVector3D mPosition;
|
|
||||||
aiVector3D mNormal;
|
|
||||||
aiVector3D mTangent, mBitangent;
|
|
||||||
aiColor4D mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
|
|
||||||
aiVector3D mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
|
|
||||||
};
|
|
||||||
std::vector<Vertex> uniqueVertices;
|
std::vector<Vertex> uniqueVertices;
|
||||||
uniqueVertices.reserve( pMesh->mNumVertices);
|
uniqueVertices.reserve( pMesh->mNumVertices);
|
||||||
|
|
||||||
//unsigned int iOldVerts = pMesh->mNumVertices;
|
|
||||||
|
|
||||||
// For each vertex the index of the vertex it was replaced by.
|
// For each vertex the index of the vertex it was replaced by.
|
||||||
std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
|
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)
|
// 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);
|
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
|
// FIX: check whether we can reuse the SpatialSort of a previous step
|
||||||
const float epsilon = 1e-5f;
|
const static float epsilon = 1e-5f;
|
||||||
float posEpsilon;
|
float posEpsilonSqr;
|
||||||
SpatialSort* vertexFinder = NULL;
|
SpatialSort* vertexFinder = NULL;
|
||||||
SpatialSort _vertexFinder;
|
SpatialSort _vertexFinder;
|
||||||
if (shared)
|
if (shared)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<SpatialSort,float> >* avf;
|
std::vector<std::pair<SpatialSort,float> >* avf;
|
||||||
shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
|
shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
|
||||||
if (avf)
|
if (avf) {
|
||||||
{
|
|
||||||
std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
|
std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
|
||||||
vertexFinder = &blubb.first;
|
vertexFinder = &blubb.first;
|
||||||
posEpsilon = blubb.second;
|
posEpsilonSqr = blubb.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!vertexFinder)
|
if (!vertexFinder) {
|
||||||
{
|
|
||||||
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
|
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
|
||||||
vertexFinder = &_vertexFinder;
|
vertexFinder = &_vertexFinder;
|
||||||
posEpsilon = ComputePositionEpsilon(pMesh);
|
posEpsilonSqr = ComputePositionEpsilon(pMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
// squared because we check against squared length of the vector difference
|
// squared because we check against squared length of the vector difference
|
||||||
const float squareEpsilon = epsilon * epsilon;
|
static const float squareEpsilon = epsilon * epsilon;
|
||||||
std::vector<unsigned int> verticesFound;
|
|
||||||
|
|
||||||
// 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++)
|
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
|
||||||
{
|
{
|
||||||
// collect the vertex data
|
// collect the vertex data
|
||||||
Vertex v;
|
Vertex v;
|
||||||
v.mPosition = pMesh->mVertices[a];
|
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);
|
if (pMesh->mNormals)
|
||||||
v.mBitangent = (pMesh->mBitangents != NULL) ? pMesh->mBitangents[a] : aiVector3D( 0, 0, 0);
|
v.mNormal = pMesh->mNormals[a];
|
||||||
for( unsigned int b = 0; b < AI_MAX_NUMBER_OF_COLOR_SETS; b++)
|
if (pMesh->mTangents)
|
||||||
v.mColors[b] = (pMesh->mColors[b] != NULL) ? pMesh->mColors[b][a] : aiColor4D( 0, 0, 0, 0);
|
v.mTangent = pMesh->mTangents[a];
|
||||||
for( unsigned int b = 0; b < AI_MAX_NUMBER_OF_TEXTURECOORDS; b++)
|
if (pMesh->mBitangents)
|
||||||
v.mTexCoords[b] = (pMesh->mTextureCoords[b] != NULL) ? pMesh->mTextureCoords[b][a] : aiVector3D( 0, 0, 0);
|
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
|
// 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;
|
unsigned int matchIndex = 0xffffffff;
|
||||||
// check all unique vertices close to the position if this vertex is already present among them
|
// 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++)
|
for( unsigned int b = 0; b < verticesFound.size(); b++)
|
||||||
{
|
{
|
||||||
unsigned int vidx = verticesFound[b];
|
const unsigned int vidx = verticesFound[b];
|
||||||
unsigned int uidx = replaceIndex[ vidx];
|
const unsigned int uidx = replaceIndex[ vidx];
|
||||||
if( uidx == 0xffffffff || !isVertexUnique[ vidx])
|
if( uidx == 0xffffffff || !isVertexUnique[ vidx])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -201,33 +229,38 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
// We just test the other attributes even if they're not present in the mesh.
|
// 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.
|
// 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.
|
// By this method the non-present attributes are effectively ignored in the comparision.
|
||||||
|
|
||||||
if( (uv.mNormal - v.mNormal).SquareLength() > squareEpsilon)
|
if( (uv.mNormal - v.mNormal).SquareLength() > squareEpsilon)
|
||||||
continue;
|
continue;
|
||||||
if( (uv.mTangent - v.mTangent).SquareLength() > squareEpsilon)
|
if( (uv.mTangent - v.mTangent).SquareLength() > squareEpsilon)
|
||||||
continue;
|
continue;
|
||||||
if( (uv.mBitangent - v.mBitangent).SquareLength() > squareEpsilon)
|
if( (uv.mBitangent - v.mBitangent).SquareLength() > squareEpsilon)
|
||||||
continue;
|
continue;
|
||||||
// manually unrolled because continue wouldn't work as desired in an inner loop
|
|
||||||
BOOST_STATIC_ASSERT(4 == AI_MAX_NUMBER_OF_COLOR_SETS);
|
|
||||||
if( GetColorDifference( uv.mColors[0], v.mColors[0]) > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( GetColorDifference( uv.mColors[1], v.mColors[1]) > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( GetColorDifference( uv.mColors[2], v.mColors[2]) > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( GetColorDifference( uv.mColors[3], v.mColors[3]) > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
// texture coord matching manually unrolled as well
|
|
||||||
BOOST_STATIC_ASSERT(4 == AI_MAX_NUMBER_OF_TEXTURECOORDS);
|
|
||||||
if( (uv.mTexCoords[0] - v.mTexCoords[0]).SquareLength() > squareEpsilon)
|
if( (uv.mTexCoords[0] - v.mTexCoords[0]).SquareLength() > squareEpsilon)
|
||||||
continue;
|
continue;
|
||||||
if( (uv.mTexCoords[1] - v.mTexCoords[1]).SquareLength() > squareEpsilon)
|
|
||||||
continue;
|
// Usually we won't have vertex colors or multiple UVs, so we can skip from here
|
||||||
if( (uv.mTexCoords[2] - v.mTexCoords[2]).SquareLength() > squareEpsilon)
|
// Actually this increases runtime performance slightly.
|
||||||
continue;
|
if (complex)
|
||||||
if( (uv.mTexCoords[3] - v.mTexCoords[3]).SquareLength() > squareEpsilon)
|
{
|
||||||
continue;
|
// manually unrolled because continue wouldn't work as desired in an inner loop
|
||||||
|
if( GetColorDifference( uv.mColors[0], v.mColors[0]) > squareEpsilon)
|
||||||
|
continue;
|
||||||
|
if( GetColorDifference( uv.mColors[1], v.mColors[1]) > squareEpsilon)
|
||||||
|
continue;
|
||||||
|
if( GetColorDifference( uv.mColors[2], v.mColors[2]) > squareEpsilon)
|
||||||
|
continue;
|
||||||
|
if( GetColorDifference( uv.mColors[3], v.mColors[3]) > squareEpsilon)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// texture coord matching manually unrolled as well
|
||||||
|
if( (uv.mTexCoords[1] - v.mTexCoords[1]).SquareLength() > squareEpsilon)
|
||||||
|
continue;
|
||||||
|
if( (uv.mTexCoords[2] - v.mTexCoords[2]).SquareLength() > squareEpsilon)
|
||||||
|
continue;
|
||||||
|
if( (uv.mTexCoords[3] - v.mTexCoords[3]).SquareLength() > squareEpsilon)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// we're still here -> this vertex perfectly matches our given vertex
|
// we're still here -> this vertex perfectly matches our given vertex
|
||||||
matchIndex = uidx;
|
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
|
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,
|
meshIndex,
|
||||||
pMesh->mNumVertices,
|
pMesh->mNumVertices,
|
||||||
(int)uniqueVertices.size(),
|
(int)uniqueVertices.size(),
|
||||||
((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f);
|
((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
|
// replace vertex data with the unique data sets
|
||||||
pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
|
pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
|
||||||
|
|
||||||
// Position
|
// Position
|
||||||
delete [] pMesh->mVertices;
|
delete [] pMesh->mVertices;
|
||||||
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
|
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
|
||||||
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
|
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
|
||||||
pMesh->mVertices[a] = uniqueVertices[a].mPosition;
|
pMesh->mVertices[a] = uniqueVertices[a].mPosition;
|
||||||
|
|
||||||
// Normals, if present
|
// Normals, if present
|
||||||
if( pMesh->mNormals)
|
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++)
|
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
|
||||||
{
|
{
|
||||||
if( !pMesh->mColors[a])
|
if( !pMesh->mColors[a])
|
||||||
continue;
|
break;
|
||||||
|
|
||||||
delete [] pMesh->mColors[a];
|
delete [] pMesh->mColors[a];
|
||||||
pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
|
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++)
|
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
|
||||||
{
|
{
|
||||||
if( !pMesh->mTextureCoords[a])
|
if( !pMesh->mTextureCoords[a])
|
||||||
continue;
|
break;
|
||||||
|
|
||||||
delete [] pMesh->mTextureCoords[a];
|
delete [] pMesh->mTextureCoords[a];
|
||||||
pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
|
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++)
|
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
|
||||||
{
|
{
|
||||||
aiFace& face = pMesh->mFaces[a];
|
aiFace& face = pMesh->mFaces[a];
|
||||||
for( unsigned int b = 0; b < face.mNumIndices; b++)
|
for( unsigned int b = 0; b < face.mNumIndices; b++) {
|
||||||
{
|
face.mIndices[b] = replaceIndex[face.mIndices[b]];
|
||||||
const size_t index = face.mIndices[b];
|
|
||||||
face.mIndices[b] = replaceIndex[index];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,8 @@ protected:
|
||||||
* @param meshIndex Index of the mesh to process
|
* @param meshIndex Index of the mesh to process
|
||||||
*/
|
*/
|
||||||
int ProcessMesh( aiMesh* pMesh, unsigned int meshIndex);
|
int ProcessMesh( aiMesh* pMesh, unsigned int meshIndex);
|
||||||
|
|
||||||
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace Assimp
|
} // 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;
|
surface = -surface;
|
||||||
|
|
||||||
// there are detail polygons
|
// there are detail polygons.
|
||||||
uint16_t numPolygons = *cursor++;
|
const uint16_t numPolygons = *cursor++;
|
||||||
if (cursor < end)CopyFaceIndicesLWOB(it,cursor,end,numPolygons);
|
if (cursor < end)CopyFaceIndicesLWOB(it,cursor,end,numPolygons);
|
||||||
}
|
}
|
||||||
face.surfaceIndex = surface-1;
|
face.surfaceIndex = surface-1;
|
||||||
|
@ -194,6 +194,27 @@ LWO::Texture* LWOImporter::SetupNewTextureLWOB(LWO::TextureList& list,unsigned i
|
||||||
|
|
||||||
std::string type;
|
std::string type;
|
||||||
GetS0(type,size);
|
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;
|
return tex;
|
||||||
}
|
}
|
||||||
|
@ -271,7 +292,7 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
|
||||||
case AI_LWO_SMAN:
|
case AI_LWO_SMAN:
|
||||||
{
|
{
|
||||||
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
|
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
|
||||||
surf.mMaximumSmoothAngle = GetF4();
|
surf.mMaximumSmoothAngle = fabs( GetF4() );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// glossiness
|
// glossiness
|
||||||
|
@ -323,8 +344,7 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
|
||||||
{
|
{
|
||||||
GetS0(pTex->mFileName,head->length);
|
GetS0(pTex->mFileName,head->length);
|
||||||
}
|
}
|
||||||
else DefaultLogger::get()->warn("LWOB: TIMG tag was encuntered although "
|
else DefaultLogger::get()->warn("LWOB: Unexpected TIMG chunk");
|
||||||
"there was no xTEX tag before");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// texture strength
|
// texture strength
|
||||||
|
@ -332,8 +352,28 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
|
||||||
{
|
{
|
||||||
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TVAL,1);
|
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TVAL,1);
|
||||||
if (pTex)pTex->mStrength = (float)GetU1()/ 255.f;
|
if (pTex)pTex->mStrength = (float)GetU1()/ 255.f;
|
||||||
else DefaultLogger::get()->warn("LWOB: TVAL tag was encuntered "
|
else DefaultLogger::get()->warn("LWOB: Unexpected TVAL chunk");
|
||||||
"although there was no xTEX tag before");
|
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;
|
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.
|
The chunks are taken from the official LightWave SDK headers.
|
||||||
Original copyright notice: "Ernie Wright 17 Sep 00"
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
#ifndef AI_LWO_FILEDATA_INCLUDED
|
#ifndef AI_LWO_FILEDATA_INCLUDED
|
||||||
|
@ -56,6 +56,7 @@ Original copyright notice: "Ernie Wright 17 Sep 00"
|
||||||
|
|
||||||
// internal headers
|
// internal headers
|
||||||
#include "IFF.h"
|
#include "IFF.h"
|
||||||
|
#include "LWOAnimation.h"
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
namespace LWO {
|
namespace LWO {
|
||||||
|
@ -254,29 +255,45 @@ namespace LWO {
|
||||||
*/
|
*/
|
||||||
struct Face : public aiFace
|
struct Face : public aiFace
|
||||||
{
|
{
|
||||||
|
//! Default construction
|
||||||
Face()
|
Face()
|
||||||
: surfaceIndex (0)
|
: surfaceIndex (0)
|
||||||
, smoothGroup (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;
|
*this = f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! Zero-based index into tags chunk
|
||||||
unsigned int surfaceIndex;
|
unsigned int surfaceIndex;
|
||||||
|
|
||||||
|
//! Smooth group this face is assigned to
|
||||||
unsigned int smoothGroup;
|
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);
|
aiFace::operator =(f);
|
||||||
surfaceIndex = f.surfaceIndex;
|
surfaceIndex = f.surfaceIndex;
|
||||||
smoothGroup = f.smoothGroup;
|
smoothGroup = f.smoothGroup;
|
||||||
|
type = f.type;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** \brief Base structure for all vertex map representations
|
/** \brief Base structure for all vertex map representations
|
||||||
*/
|
*/
|
||||||
|
@ -473,8 +490,9 @@ struct Clip
|
||||||
} type;
|
} type;
|
||||||
|
|
||||||
Clip()
|
Clip()
|
||||||
: type (UNSUPPORTED)
|
: type (UNSUPPORTED)
|
||||||
, idx (0)
|
, idx (0)
|
||||||
|
, negate (false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
//! path to the base texture -
|
//! path to the base texture -
|
||||||
|
@ -485,6 +503,9 @@ struct Clip
|
||||||
|
|
||||||
//! index of the clip
|
//! index of the clip
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
|
|
||||||
|
//! Negate the clip?
|
||||||
|
bool negate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -529,6 +550,7 @@ struct Surface
|
||||||
, mIOR (1.f) // vakuum
|
, mIOR (1.f) // vakuum
|
||||||
, mBumpIntensity (1.f)
|
, mBumpIntensity (1.f)
|
||||||
, mWireframe (false)
|
, mWireframe (false)
|
||||||
|
, mAdditiveTransparency (10e10f)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
//! Name of the surface
|
//! Name of the surface
|
||||||
|
@ -571,6 +593,9 @@ struct Surface
|
||||||
|
|
||||||
//! Wireframe flag
|
//! Wireframe flag
|
||||||
bool mWireframe;
|
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 < VColorChannel > VColorChannelList;
|
||||||
typedef std::vector < UVChannel > UVChannelList;
|
typedef std::vector < UVChannel > UVChannelList;
|
||||||
typedef std::vector < Clip > ClipList;
|
typedef std::vector < Clip > ClipList;
|
||||||
|
typedef std::vector < Envelope > EnvelopeList;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** \brief Represents a layer in the file
|
/** \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.
|
// 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
|
const std::string extension = GetExtension(pFile);
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
if (extension == "lwo" || extension == "lxo")
|
||||||
// no file extension - can't read
|
return true;
|
||||||
if( pos == std::string::npos)return false;
|
|
||||||
std::string extension = pFile.substr( pos);
|
|
||||||
|
|
||||||
if (extension.length() < 4)return false;
|
// if check for extension is not enough, check for the magic tokens
|
||||||
if (extension[0] != '.')return false;
|
if (!extension.length() || checkSig) {
|
||||||
|
uint32_t tokens[3];
|
||||||
return ! (extension[1] != 'l' && extension[1] != 'L' ||
|
tokens[0] = AI_LWO_FOURCC_LWOB;
|
||||||
extension[2] != 'w' && extension[2] != 'W' &&
|
tokens[1] = AI_LWO_FOURCC_LWO2;
|
||||||
extension[2] != 'x' && extension[2] != 'X' ||
|
tokens[2] = AI_LWO_FOURCC_LXOB;
|
||||||
extension[3] != 'o' && extension[3] != 'O');
|
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)
|
if((this->fileSize = (unsigned int)file->FileSize()) < 12)
|
||||||
throw new ImportErrorException("LWO: The file is too small to contain the IFF header");
|
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);
|
std::vector< uint8_t > mBuffer(fileSize);
|
||||||
file->Read( &mBuffer[0], 1, fileSize);
|
file->Read( &mBuffer[0], 1, fileSize);
|
||||||
this->pScene = pScene;
|
this->pScene = pScene;
|
||||||
|
|
||||||
// determine the type of the file
|
// Determine the type of the file
|
||||||
uint32_t fileType;
|
uint32_t fileType;
|
||||||
const char* sz = IFF::ReadHeader(&mBuffer[0],fileType);
|
const char* sz = IFF::ReadHeader(&mBuffer[0],fileType);
|
||||||
if (sz)throw new ImportErrorException(sz);
|
if (sz)throw new ImportErrorException(sz);
|
||||||
|
|
||||||
mFileBuffer = &mBuffer[0] + 12;
|
mFileBuffer = &mBuffer[0] + 12;
|
||||||
fileSize -= 12;
|
fileSize -= 12;
|
||||||
hasNamedLayer = false;
|
|
||||||
|
|
||||||
// create temporary storage on the stack but store pointers to it in the class
|
// Initialize some members with their default values
|
||||||
|
hasNamedLayer = false;
|
||||||
|
|
||||||
|
// Create temporary storage on the stack but store pointers to it in the class
|
||||||
// instance. Therefore everything will be destructed properly if an exception
|
// instance. Therefore everything will be destructed properly if an exception
|
||||||
// is thrown and we needn't take care of that.
|
// is thrown and we needn't take care of that.
|
||||||
LayerList _mLayers;
|
LayerList _mLayers;
|
||||||
|
@ -218,6 +220,11 @@ void LWOImporter::InternReadFile( const std::string& pFile,
|
||||||
for (FaceList::iterator it = layer.mFaces.begin(), end = layer.mFaces.end();
|
for (FaceList::iterator it = layer.mFaces.begin(), end = layer.mFaces.end();
|
||||||
it != end;++it,++i)
|
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;
|
unsigned int idx = (*it).surfaceIndex;
|
||||||
if (idx >= mTags->size())
|
if (idx >= mTags->size())
|
||||||
{
|
{
|
||||||
|
@ -282,7 +289,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
|
||||||
if (0xffffffff == vUVChannelIndices[mui])break;
|
if (0xffffffff == vUVChannelIndices[mui])break;
|
||||||
pvUV[mui] = mesh->mTextureCoords[mui] = new aiVector3D[mesh->mNumVertices];
|
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
|
// so we can directly setup this value
|
||||||
mesh->mNumUVComponents[0] = 2;
|
mesh->mNumUVComponents[0] = 2;
|
||||||
}
|
}
|
||||||
|
@ -318,6 +325,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
|
||||||
register unsigned int idx = face.mIndices[q];
|
register unsigned int idx = face.mIndices[q];
|
||||||
*pv = layer.mTempPoints[idx] + layer.mPivot;
|
*pv = layer.mTempPoints[idx] + layer.mPivot;
|
||||||
pv->z *= -1.0f; // DX to OGL
|
pv->z *= -1.0f; // DX to OGL
|
||||||
|
//std::swap(pv->z,pv->y);
|
||||||
pv++;
|
pv++;
|
||||||
|
|
||||||
// process UV coordinates
|
// process UV coordinates
|
||||||
|
@ -585,7 +593,8 @@ void LWOImporter::GenerateNodeGraph(std::vector<aiNode*>& apcNodes)
|
||||||
pScene->mRootNode->mChildren = apcNewNodes;
|
pScene->mRootNode->mChildren = apcNewNodes;
|
||||||
pScene->mRootNode->mNumChildren = newSize;
|
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
|
// remove a single root node
|
||||||
// TODO: implement directly in the above loop, no need to deallocate here
|
// TODO: implement directly in the above loop, no need to deallocate here
|
||||||
|
@ -717,31 +726,44 @@ void LWOImporter::LoadLWOPoints(unsigned int length)
|
||||||
void LWOImporter::LoadLWO2Polygons(unsigned int length)
|
void LWOImporter::LoadLWO2Polygons(unsigned int length)
|
||||||
{
|
{
|
||||||
LE_NCONST uint16_t* const end = (LE_NCONST uint16_t*)(mFileBuffer+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
|
// Determine the type of the polygons
|
||||||
switch (type)
|
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_PTCH:
|
||||||
case AI_LWO_FACE:
|
case AI_LWO_FACE:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
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
|
// first find out how many faces and vertices we'll finally need
|
||||||
uint16_t* cursor = (uint16_t*)mFileBuffer;
|
uint16_t* cursor= (uint16_t*)mFileBuffer;
|
||||||
|
|
||||||
unsigned int iNumFaces = 0,iNumVertices = 0;
|
unsigned int iNumFaces = 0,iNumVertices = 0;
|
||||||
CountVertsAndFacesLWO2(iNumVertices,iNumFaces,cursor,end);
|
CountVertsAndFacesLWO2(iNumVertices,iNumFaces,cursor,end);
|
||||||
|
|
||||||
// allocate the output array and copy face indices
|
// allocate the output array and copy face indices
|
||||||
if (iNumFaces)
|
if (iNumFaces) {
|
||||||
{
|
|
||||||
cursor = (uint16_t*)mFileBuffer;
|
cursor = (uint16_t*)mFileBuffer;
|
||||||
|
|
||||||
mCurLayer->mFaces.resize(iNumFaces);
|
mCurLayer->mFaces.resize(iNumFaces,LWO::Face(type));
|
||||||
FaceList::iterator it = mCurLayer->mFaces.begin();
|
FaceList::iterator it = mCurLayer->mFaces.begin();
|
||||||
CopyFaceIndicesLWO2(it,cursor,end);
|
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)
|
void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
|
||||||
{
|
{
|
||||||
LE_NCONST uint8_t* const end = mFileBuffer+length;
|
LE_NCONST uint8_t* const end = mFileBuffer+length;
|
||||||
|
@ -915,24 +938,24 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case AI_LWO_TXUV:
|
case AI_LWO_TXUV:
|
||||||
if (dims != 2)
|
if (dims != 2) {
|
||||||
{
|
|
||||||
DefaultLogger::get()->warn("LWO2: Found UV channel with != 2 components");
|
DefaultLogger::get()->warn("LWO2: Found UV channel with != 2 components");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
base = FindEntry(mCurLayer->mUVChannels,name,perPoly);
|
base = FindEntry(mCurLayer->mUVChannels,name,perPoly);
|
||||||
break;
|
break;
|
||||||
case AI_LWO_WGHT:
|
case AI_LWO_WGHT:
|
||||||
if (dims != 1)
|
if (dims != 1) {
|
||||||
{
|
|
||||||
DefaultLogger::get()->warn("LWO2: found vertex weight map with != 1 components");
|
DefaultLogger::get()->warn("LWO2: found vertex weight map with != 1 components");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
base = FindEntry(mCurLayer->mWeightChannels,name,perPoly);
|
base = FindEntry(mCurLayer->mWeightChannels,name,perPoly);
|
||||||
break;
|
break;
|
||||||
case AI_LWO_RGB:
|
case AI_LWO_RGB:
|
||||||
case AI_LWO_RGBA:
|
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");
|
DefaultLogger::get()->warn("LWO2: found vertex color map with != 3&4 components");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
base = FindEntry(mCurLayer->mVColorChannels,name,perPoly);
|
base = FindEntry(mCurLayer->mVColorChannels,name,perPoly);
|
||||||
break;
|
break;
|
||||||
|
@ -1028,6 +1051,7 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Load LWO2 clip
|
||||||
void LWOImporter::LoadLWO2Clip(unsigned int length)
|
void LWOImporter::LoadLWO2Clip(unsigned int length)
|
||||||
{
|
{
|
||||||
AI_LWO_VALIDATE_CHUNK_LENGTH(length,CLIP,10);
|
AI_LWO_VALIDATE_CHUNK_LENGTH(length,CLIP,10);
|
||||||
|
@ -1042,6 +1066,7 @@ void LWOImporter::LoadLWO2Clip(unsigned int length)
|
||||||
switch (head->type)
|
switch (head->type)
|
||||||
{
|
{
|
||||||
case AI_LWO_STIL:
|
case AI_LWO_STIL:
|
||||||
|
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,STIL,1);
|
||||||
|
|
||||||
// "Normal" texture
|
// "Normal" texture
|
||||||
GetS0(clip.path,head->length);
|
GetS0(clip.path,head->length);
|
||||||
|
@ -1049,7 +1074,7 @@ void LWOImporter::LoadLWO2Clip(unsigned int length)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AI_LWO_ISEQ:
|
case AI_LWO_ISEQ:
|
||||||
|
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ISEQ,16);
|
||||||
// Image sequence. We'll later take the first.
|
// Image sequence. We'll later take the first.
|
||||||
{
|
{
|
||||||
uint8_t digits = GetU1(); mFileBuffer++;
|
uint8_t digits = GetU1(); mFileBuffer++;
|
||||||
|
@ -1078,18 +1103,124 @@ void LWOImporter::LoadLWO2Clip(unsigned int length)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AI_LWO_XREF:
|
case AI_LWO_XREF:
|
||||||
|
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,XREF,4);
|
||||||
|
|
||||||
// Just a cross-reference to another CLIp
|
// Just a cross-reference to another CLIp
|
||||||
clip.type = Clip::REF;
|
clip.type = Clip::REF;
|
||||||
clip.clipRef = GetU4();
|
clip.clipRef = GetU4();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AI_LWO_NEGA:
|
||||||
|
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,NEGA,2);
|
||||||
|
clip.negate = (0 != GetU2());
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DefaultLogger::get()->warn("LWO2: Encountered unknown CLIP subchunk");
|
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()
|
void LWOImporter::LoadLWO2File()
|
||||||
{
|
{
|
||||||
bool skip = false;
|
bool skip = false;
|
||||||
|
@ -1121,8 +1252,7 @@ void LWOImporter::LoadLWO2File()
|
||||||
// load this layer or ignore it? Check the layer index property
|
// load this layer or ignore it? Check the layer index property
|
||||||
// NOTE: The first layer is the default layer, so the layer
|
// NOTE: The first layer is the default layer, so the layer
|
||||||
// index is one-based now
|
// index is one-based now
|
||||||
if (0xffffffff != configLayerIndex && configLayerIndex != mLayers->size())
|
if (0xffffffff != configLayerIndex && configLayerIndex != mLayers->size()-1) {
|
||||||
{
|
|
||||||
skip = true;
|
skip = true;
|
||||||
}
|
}
|
||||||
else skip = false;
|
else skip = false;
|
||||||
|
@ -1138,16 +1268,14 @@ void LWOImporter::LoadLWO2File()
|
||||||
GetS0(layer.mName,head->length-16);
|
GetS0(layer.mName,head->length-16);
|
||||||
|
|
||||||
// if the name is empty, generate a default name
|
// if the name is empty, generate a default name
|
||||||
if (layer.mName.empty())
|
if (layer.mName.empty()) {
|
||||||
{
|
|
||||||
char buffer[128]; // should be sufficiently large
|
char buffer[128]; // should be sufficiently large
|
||||||
::sprintf(buffer,"Layer_%i", iUnnamed++);
|
::sprintf(buffer,"Layer_%i", iUnnamed++);
|
||||||
layer.mName = buffer;
|
layer.mName = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// load this layer or ignore it? Check the layer name property
|
// 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;
|
skip = true;
|
||||||
}
|
}
|
||||||
else hasNamedLayer = true;
|
else hasNamedLayer = true;
|
||||||
|
@ -1161,7 +1289,8 @@ void LWOImporter::LoadLWO2File()
|
||||||
// vertex list
|
// vertex list
|
||||||
case AI_LWO_PNTS:
|
case AI_LWO_PNTS:
|
||||||
{
|
{
|
||||||
if (skip)break;
|
if (skip)
|
||||||
|
break;
|
||||||
|
|
||||||
unsigned int old = (unsigned int)mCurLayer->mTempPoints.size();
|
unsigned int old = (unsigned int)mCurLayer->mTempPoints.size();
|
||||||
LoadLWOPoints(head->length);
|
LoadLWOPoints(head->length);
|
||||||
|
@ -1178,7 +1307,8 @@ void LWOImporter::LoadLWO2File()
|
||||||
// --- intentionally no break here
|
// --- intentionally no break here
|
||||||
case AI_LWO_VMAP:
|
case AI_LWO_VMAP:
|
||||||
{
|
{
|
||||||
if (skip)break;
|
if (skip)
|
||||||
|
break;
|
||||||
|
|
||||||
if (mCurLayer->mTempPoints.empty())
|
if (mCurLayer->mTempPoints.empty())
|
||||||
DefaultLogger::get()->warn("LWO2: Unexpected VMAP chunk");
|
DefaultLogger::get()->warn("LWO2: Unexpected VMAP chunk");
|
||||||
|
@ -1188,7 +1318,8 @@ void LWOImporter::LoadLWO2File()
|
||||||
// face list
|
// face list
|
||||||
case AI_LWO_POLS:
|
case AI_LWO_POLS:
|
||||||
{
|
{
|
||||||
if (skip)break;
|
if (skip)
|
||||||
|
break;
|
||||||
|
|
||||||
unsigned int old = (unsigned int)mCurLayer->mFaces.size();
|
unsigned int old = (unsigned int)mCurLayer->mFaces.size();
|
||||||
LoadLWO2Polygons(head->length);
|
LoadLWO2Polygons(head->length);
|
||||||
|
@ -1198,7 +1329,8 @@ void LWOImporter::LoadLWO2File()
|
||||||
// polygon tags
|
// polygon tags
|
||||||
case AI_LWO_PTAG:
|
case AI_LWO_PTAG:
|
||||||
{
|
{
|
||||||
if (skip)break;
|
if (skip)
|
||||||
|
break;
|
||||||
|
|
||||||
if (mCurLayer->mFaces.empty())
|
if (mCurLayer->mFaces.empty())
|
||||||
DefaultLogger::get()->warn("LWO2: Unexpected PTAG");
|
DefaultLogger::get()->warn("LWO2: Unexpected PTAG");
|
||||||
|
@ -1227,6 +1359,13 @@ void LWOImporter::LoadLWO2File()
|
||||||
LoadLWO2Clip(head->length);
|
LoadLWO2Clip(head->length);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// envelope chunk
|
||||||
|
case AI_LWO_ENVL:
|
||||||
|
{
|
||||||
|
LoadLWO2Envelope(head->length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mFileBuffer = next;
|
mFileBuffer = next;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ using namespace LWO;
|
||||||
* Methods named "xxxLWOB[xxx]" are used with the older LWOB format.
|
* Methods named "xxxLWOB[xxx]" are used with the older LWOB format.
|
||||||
* Methods named "xxxLWO[xxx]" are used with both formats.
|
* Methods named "xxxLWO[xxx]" are used with both formats.
|
||||||
* Methods named "xxx" are used to preprocess the loaded data -
|
* 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
|
class LWOImporter : public BaseImporter
|
||||||
|
@ -81,8 +81,10 @@ public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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;
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
@ -211,6 +213,11 @@ private:
|
||||||
*/
|
*/
|
||||||
void LoadLWO2Clip(unsigned int length);
|
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
|
/** Count vertices and faces in a LWOB/LWO2 file
|
||||||
|
@ -372,6 +379,9 @@ protected:
|
||||||
/** Temporary clip list from the file */
|
/** Temporary clip list from the file */
|
||||||
ClipList mClips;
|
ClipList mClips;
|
||||||
|
|
||||||
|
/** Temporary envelope list from the file */
|
||||||
|
EnvelopeList mEnvelopes;
|
||||||
|
|
||||||
/** file buffer */
|
/** file buffer */
|
||||||
uint8_t* mFileBuffer;
|
uint8_t* mFileBuffer;
|
||||||
|
|
||||||
|
@ -381,9 +391,16 @@ protected:
|
||||||
/** Output scene */
|
/** Output scene */
|
||||||
aiScene* pScene;
|
aiScene* pScene;
|
||||||
|
|
||||||
|
/** Configuration option: speed flag set? */
|
||||||
bool configSpeedFlag;
|
bool configSpeedFlag;
|
||||||
|
|
||||||
|
/** Configuration option: index of layer to be loaded */
|
||||||
unsigned int configLayerIndex;
|
unsigned int configLayerIndex;
|
||||||
|
|
||||||
|
/** Configuration option: name of layer to be loaded */
|
||||||
std::string configLayerName;
|
std::string configLayerName;
|
||||||
|
|
||||||
|
/** True if we have a named layer */
|
||||||
bool hasNamedLayer;
|
bool hasNamedLayer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -183,13 +183,30 @@ bool LWOImporter::HandleTextures(MaterialHelper* pcMat, const TextureList& in, a
|
||||||
if (mClips.end() == clip) {
|
if (mClips.end() == clip) {
|
||||||
DefaultLogger::get()->error("LWO2: Clip index is out of bounds");
|
DefaultLogger::get()->error("LWO2: Clip index is out of bounds");
|
||||||
temp = 0;
|
temp = 0;
|
||||||
|
|
||||||
|
// fixme: appearently some LWO files shipping with Doom3 don't
|
||||||
|
// have clips at all ... check whether that's true or whether
|
||||||
|
// it's a bug in the loader.
|
||||||
|
|
||||||
|
s.Set("$texture.png");
|
||||||
|
|
||||||
|
//continue;
|
||||||
}
|
}
|
||||||
if (Clip::UNSUPPORTED == (*clip).type) {
|
else {
|
||||||
DefaultLogger::get()->error("LWO2: Clip type is not supported");
|
if (Clip::UNSUPPORTED == (*clip).type) {
|
||||||
continue;
|
DefaultLogger::get()->error("LWO2: Clip type is not supported");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AdjustTexturePath((*clip).path);
|
||||||
|
s.Set((*clip).path);
|
||||||
|
|
||||||
|
// Additional image settings
|
||||||
|
int flags = 0;
|
||||||
|
if ((*clip).negate) {
|
||||||
|
flags |= aiTextureFlags_Invert;
|
||||||
|
}
|
||||||
|
pcMat->AddProperty(&flags,1,AI_MATKEY_TEXFLAGS(type,cur));
|
||||||
}
|
}
|
||||||
AdjustTexturePath((*clip).path);
|
|
||||||
s.Set((*clip).path);
|
|
||||||
}
|
}
|
||||||
else
|
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");
|
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));
|
pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_TEXOP(type,cur));
|
||||||
|
|
||||||
// setup the mapping mode
|
// setup the mapping mode
|
||||||
|
@ -258,12 +276,12 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat
|
||||||
st.Set(surf.mName);
|
st.Set(surf.mName);
|
||||||
pcMat->AddProperty(&st,AI_MATKEY_NAME);
|
pcMat->AddProperty(&st,AI_MATKEY_NAME);
|
||||||
|
|
||||||
int i = surf.bDoubleSided ? 1 : 0;
|
const int i = surf.bDoubleSided ? 1 : 0;
|
||||||
pcMat->AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
|
pcMat->AddProperty(&i,1,AI_MATKEY_TWOSIDED);
|
||||||
|
|
||||||
// add the refraction index and the bump intensity
|
// add the refraction index and the bump intensity
|
||||||
pcMat->AddProperty<float>(&surf.mIOR,1,AI_MATKEY_REFRACTI);
|
pcMat->AddProperty(&surf.mIOR,1,AI_MATKEY_REFRACTI);
|
||||||
pcMat->AddProperty<float>(&surf.mBumpIntensity,1,AI_MATKEY_BUMPSCALING);
|
pcMat->AddProperty(&surf.mBumpIntensity,1,AI_MATKEY_BUMPSCALING);
|
||||||
|
|
||||||
aiShadingMode m;
|
aiShadingMode m;
|
||||||
if (surf.mSpecularValue && surf.mGlossiness)
|
if (surf.mSpecularValue && surf.mGlossiness)
|
||||||
|
@ -281,16 +299,16 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat
|
||||||
else fGloss = 80.0f;
|
else fGloss = 80.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcMat->AddProperty<float>(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
|
pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
|
||||||
pcMat->AddProperty<float>(&fGloss,1,AI_MATKEY_SHININESS);
|
pcMat->AddProperty(&fGloss,1,AI_MATKEY_SHININESS);
|
||||||
m = aiShadingMode_Phong;
|
m = aiShadingMode_Phong;
|
||||||
}
|
}
|
||||||
else m = aiShadingMode_Gouraud;
|
else m = aiShadingMode_Gouraud;
|
||||||
|
|
||||||
// specular color
|
// specular color
|
||||||
aiColor3D clr = lerp( aiColor3D(1.f,1.f,1.f), surf.mColor, surf.mColorHighlights );
|
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(&clr,1,AI_MATKEY_COLOR_SPECULAR);
|
||||||
pcMat->AddProperty<float>(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
|
pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
|
||||||
|
|
||||||
// emissive color
|
// emissive color
|
||||||
// (luminosity is not really the same but it affects the surface in
|
// (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;
|
clr.g = clr.b = clr.r = surf.mLuminosity*0.8f;
|
||||||
pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
|
pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
|
||||||
|
|
||||||
// opacity
|
// opacity ... either additive or default-blended, please
|
||||||
if (10e10f != surf.mTransparency)
|
if (10e10f != surf.mAdditiveTransparency)
|
||||||
{
|
{
|
||||||
float f = 1.0f-surf.mTransparency;
|
const int add = aiBlendMode_Additive;
|
||||||
pcMat->AddProperty<float>(&f,1,AI_MATKEY_OPACITY);
|
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
|
// ADD TEXTURES to the material
|
||||||
// TODO: find out how we can handle COLOR textures correctly...
|
// 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.mOpacityTextures,aiTextureType_OPACITY);
|
||||||
HandleTextures(pcMat,surf.mReflectionTextures,aiTextureType_REFLECTION);
|
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
|
// 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();
|
for (ShaderList::const_iterator it = surf.mShaders.begin(), end = surf.mShaders.end();
|
||||||
it != end;++it)
|
it != end;++it)
|
||||||
{
|
{
|
||||||
//if (!(*it).enabled)continue;
|
//if (!(*it).enabled)continue;
|
||||||
if ((*it).functionName == "LW_SuperCelShader" ||
|
if ((*it).functionName == "LW_SuperCelShader" || (*it).functionName == "AH_CelShader") {
|
||||||
(*it).functionName == "AH_CelShader")
|
|
||||||
{
|
|
||||||
DefaultLogger::get()->info("LWO2: Mapping LW_SuperCelShader/AH_CelShader "
|
DefaultLogger::get()->info("LWO2: Mapping LW_SuperCelShader/AH_CelShader "
|
||||||
"to aiShadingMode_Toon");
|
"to aiShadingMode_Toon");
|
||||||
|
|
||||||
m = aiShadingMode_Toon;
|
m = aiShadingMode_Toon;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if ((*it).functionName == "LW_RealFresnel" ||
|
else if ((*it).functionName == "LW_RealFresnel" || (*it).functionName == "LW_FastFresnel") {
|
||||||
(*it).functionName == "LW_FastFresnel")
|
|
||||||
{
|
|
||||||
DefaultLogger::get()->info("LWO2: Mapping LW_RealFresnel/LW_FastFresnel "
|
DefaultLogger::get()->info("LWO2: Mapping LW_RealFresnel/LW_FastFresnel "
|
||||||
"to aiShadingMode_Fresnel");
|
"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);
|
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);
|
pcMat->AddProperty((int*)&m,1,AI_MATKEY_SHADING_MODEL);
|
||||||
|
|
||||||
// (the diffuse value is just a scaling factor)
|
// (the diffuse value is just a scaling factor)
|
||||||
// If a diffuse texture is set, we set this value to 1.0
|
// 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.r *= surf.mDiffuseValue;
|
||||||
clr.g *= surf.mDiffuseValue;
|
clr.g *= surf.mDiffuseValue;
|
||||||
clr.b *= surf.mDiffuseValue;
|
clr.b *= surf.mDiffuseValue;
|
||||||
|
@ -365,9 +389,7 @@ void LWOImporter::FindUVChannels(LWO::TextureList& list, LWO::Layer& layer,
|
||||||
it != end;++it)
|
it != end;++it)
|
||||||
{
|
{
|
||||||
// Ignore textures with non-UV mappings for the moment.
|
// Ignore textures with non-UV mappings for the moment.
|
||||||
if (!(*it).enabled || !(*it).bCanUse || 0xffffffff != (*it).mRealUVIndex ||
|
if (!(*it).enabled || !(*it).bCanUse || 0xffffffff != (*it).mRealUVIndex || (*it).mapMode != LWO::Texture::UV) {
|
||||||
(*it).mapMode != LWO::Texture::UV)
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (unsigned int i = 0; i < layer.mUVChannels.size();++i)
|
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]) {
|
if (i == out[m]) {
|
||||||
(*it).mRealUVIndex = m;
|
(*it).mRealUVIndex = m;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (0xffffffff == (*it).mRealUVIndex)
|
if (0xffffffff == (*it).mRealUVIndex)
|
||||||
|
@ -710,7 +733,9 @@ void LWOImporter::LoadLWO2Surface(unsigned int size)
|
||||||
// transparency
|
// transparency
|
||||||
case AI_LWO_TRAN:
|
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);
|
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TRAN,4);
|
||||||
surf.mTransparency = GetF4();
|
surf.mTransparency = GetF4();
|
||||||
|
@ -741,6 +766,13 @@ void LWOImporter::LoadLWO2Surface(unsigned int size)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// additive transparency
|
||||||
|
case AI_LWO_ADTR:
|
||||||
|
{
|
||||||
|
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ADTR,4);
|
||||||
|
surf.mAdditiveTransparency = GetF4();
|
||||||
|
break;
|
||||||
|
}
|
||||||
// wireframe mode
|
// wireframe mode
|
||||||
case AI_LWO_LINE:
|
case AI_LWO_LINE:
|
||||||
{
|
{
|
||||||
|
@ -788,7 +820,7 @@ void LWOImporter::LoadLWO2Surface(unsigned int size)
|
||||||
case AI_LWO_SMAN:
|
case AI_LWO_SMAN:
|
||||||
{
|
{
|
||||||
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
|
AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
|
||||||
surf.mMaximumSmoothAngle = GetF4();
|
surf.mMaximumSmoothAngle = fabs( GetF4() );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// vertex color channel to be applied to the surface
|
// 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"
|
#include "AssimpPCH.h"
|
||||||
|
|
||||||
|
@ -48,9 +50,60 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "fast_atof.h"
|
#include "fast_atof.h"
|
||||||
|
|
||||||
#include "SceneCombiner.h"
|
#include "SceneCombiner.h"
|
||||||
|
#include "GenericProperty.h"
|
||||||
|
#include "SkeletonMeshBuilder.h"
|
||||||
|
|
||||||
using namespace Assimp;
|
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
|
// 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.
|
// 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 check for extension is not enough, check for the magic tokens LWSC and LWMO
|
||||||
if( pos == std::string::npos)
|
if (!extension.length() || checkSig) {
|
||||||
return false;
|
uint32_t tokens[2];
|
||||||
|
tokens[0] = AI_MAKE_MAGIC("LWSC");
|
||||||
std::string extension = pFile.substr( pos);
|
tokens[1] = AI_MAKE_MAGIC("LWMO");
|
||||||
for (std::string::iterator i = extension.begin(); i != extension.end();++i)
|
return CheckMagicToken(pIOHandler,pFile,tokens,2);
|
||||||
*i = ::tolower(*i);
|
}
|
||||||
|
return false;
|
||||||
return extension == ".lws";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Get list of file extensions
|
||||||
void LWSImporter::GetExtensionList(std::string& append)
|
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,
|
void LWSImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
|
||||||
IOSystem* pIOHandler)
|
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
|
#ifndef AI_LWSLOADER_H_INCLUDED
|
||||||
#define AI_LWSLOADER_H_INCLUDED
|
#define AI_LWSLOADER_H_INCLUDED
|
||||||
|
|
||||||
|
#include "LWOFileData.h"
|
||||||
|
#include "SceneCombiner.h"
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
namespace LWS {
|
namespace LWS {
|
||||||
|
@ -49,17 +53,107 @@ namespace Assimp {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Represents an element in a LWS file.
|
/** Represents an element in a LWS file.
|
||||||
*
|
*
|
||||||
* This can either be a single data line - <name> <value> or it can
|
* This can either be a single data line - <name> <value> or a data
|
||||||
* be a data group - { name <data_line0> ... n }
|
* group - { name <data_line0> ... n }
|
||||||
*/
|
*/
|
||||||
class Element
|
class Element
|
||||||
{
|
{
|
||||||
std::string name, data;
|
public:
|
||||||
|
Element()
|
||||||
|
{}
|
||||||
|
|
||||||
|
// first: name, second: rest
|
||||||
|
std::string tokens[2];
|
||||||
std::list<Element> children;
|
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
|
} // end namespace LWS
|
||||||
|
|
||||||
|
@ -84,28 +178,63 @@ protected:
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
// Check whether we can read a specific file
|
||||||
* See BaseImporter::CanRead() for details. */
|
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
|
||||||
bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
|
bool checkSig) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
// Get list of supported extensions
|
||||||
* See BaseImporter::GetExtensionList() for details
|
|
||||||
*/
|
|
||||||
void GetExtensionList(std::string& append);
|
void GetExtensionList(std::string& append);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
// Import file into given scene data structure
|
||||||
* See BaseImporter::InternReadFile() for details
|
|
||||||
*/
|
|
||||||
void InternReadFile( const std::string& pFile, aiScene* pScene,
|
void InternReadFile( const std::string& pFile, aiScene* pScene,
|
||||||
IOSystem* pIOHandler);
|
IOSystem* pIOHandler);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Setup import properties
|
||||||
|
void SetupProperties(const Importer* pImp);
|
||||||
|
|
||||||
private:
|
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
|
} // 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 MD2FileData.h
|
||||||
|
* @brief Defines helper data structures for importing MD2 files
|
||||||
**********************************************************************
|
* http://linux.ucla.edu/~phaethon/q3/formats/md2-schoenblum.html
|
||||||
File format specification:
|
*/
|
||||||
//http://linux.ucla.edu/~phaethon/q3/formats/md2-schoenblum.html
|
|
||||||
**********************************************************************
|
|
||||||
|
|
||||||
*/
|
|
||||||
#ifndef AI_MD2FILEHELPER_H_INC
|
#ifndef AI_MD2FILEHELPER_H_INC
|
||||||
#define AI_MD2FILEHELPER_H_INC
|
#define AI_MD2FILEHELPER_H_INC
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "../include/aiTypes.h"
|
#include "../include/aiTypes.h"
|
||||||
#include "../include/aiMesh.h"
|
#include "../include/aiMesh.h"
|
||||||
#include "../include/aiAnim.h"
|
#include "../include/aiAnim.h"
|
||||||
|
@ -62,11 +54,9 @@ File format specification:
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
namespace MD2 {
|
namespace MD2 {
|
||||||
|
|
||||||
// to make it easier for ourselfes, we test the magic word against both "endianesses"
|
// to make it easier for us, 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 AI_MAKE_MAGIC("IDP2")
|
||||||
|
#define AI_MD2_MAGIC_NUMBER_LE AI_MAKE_MAGIC("2PDI")
|
||||||
#define AI_MD2_MAGIC_NUMBER_BE MD2_MAKE("IDP2")
|
|
||||||
#define AI_MD2_MAGIC_NUMBER_LE MD2_MAKE("2PDI")
|
|
||||||
|
|
||||||
// common limitations
|
// common limitations
|
||||||
#define AI_MD2_VERSION 15
|
#define AI_MD2_VERSION 15
|
||||||
|
|
|
@ -62,11 +62,8 @@ using namespace Assimp::MD2;
|
||||||
void MD2::LookupNormalIndex(uint8_t iNormalIndex,aiVector3D& vOut)
|
void MD2::LookupNormalIndex(uint8_t iNormalIndex,aiVector3D& vOut)
|
||||||
{
|
{
|
||||||
// make sure the normal index has a valid value
|
// make sure the normal index has a valid value
|
||||||
if (iNormalIndex >= ARRAYSIZE(g_avNormals))
|
if (iNormalIndex >= ARRAYSIZE(g_avNormals)) {
|
||||||
{
|
DefaultLogger::get()->warn("Index overflow in Quake II normal vector list");
|
||||||
DefaultLogger::get()->warn("Index overflow in Quake II normal vector list (the "
|
|
||||||
" LUT has only 162 entries). ");
|
|
||||||
|
|
||||||
iNormalIndex = ARRAYSIZE(g_avNormals) - 1;
|
iNormalIndex = ARRAYSIZE(g_avNormals) - 1;
|
||||||
}
|
}
|
||||||
vOut = *((const aiVector3D*)(&g_avNormals[iNormalIndex]));
|
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
|
// Constructor to be privately used by Importer
|
||||||
MD2Importer::MD2Importer()
|
MD2Importer::MD2Importer()
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Destructor, private as well
|
// Destructor, private as well
|
||||||
MD2Importer::~MD2Importer()
|
MD2Importer::~MD2Importer()
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
const std::string extension = GetExtension(pFile);
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
if (extension == "md2")
|
||||||
// no file extension - can't read
|
return true;
|
||||||
if( pos == std::string::npos)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string extension = pFile.substr( pos);
|
// if check for extension is not enough, check for the magic tokens
|
||||||
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
|
if (!extension.length() || checkSig) {
|
||||||
*it = tolower( *it);
|
uint32_t tokens[1];
|
||||||
|
tokens[0] = AI_MD2_MAGIC_NUMBER_LE;
|
||||||
return ( extension == ".md2");
|
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
|
// Setup configuration properties
|
||||||
void MD2Importer::SetupProperties(const Importer* pImp)
|
void MD2Importer::SetupProperties(const Importer* pImp)
|
||||||
|
@ -161,8 +164,8 @@ void MD2Importer::ValidateHeader( )
|
||||||
|
|
||||||
if (m_pcHeader->numFrames <= configFrameID )
|
if (m_pcHeader->numFrames <= configFrameID )
|
||||||
throw new ImportErrorException("The requested frame is not existing the file");
|
throw new ImportErrorException("The requested frame is not existing the file");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Imports the given file into the given scene structure.
|
// Imports the given file into the given scene structure.
|
||||||
void MD2Importer::InternReadFile( const std::string& pFile,
|
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);
|
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
|
||||||
|
|
||||||
aiString szName;
|
aiString szName;
|
||||||
szName.Set("MD2Default");
|
szName.Set(AI_DEFAULT_TEXTURED_MATERIAL_NAME);
|
||||||
pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
|
pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
|
||||||
|
|
||||||
aiString sz;
|
aiString sz;
|
||||||
|
|
||||||
// TODO: Try to guess the name of the texture file from the model file name
|
// 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));
|
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
|
#ifndef AI_MD2LOADER_H_INCLUDED
|
||||||
#define 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;
|
struct aiNode;
|
||||||
#include "MD2FileData.h"
|
#include "MD2FileData.h"
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp {
|
||||||
{
|
|
||||||
class MaterialHelper;
|
class MaterialHelper;
|
||||||
|
|
||||||
using namespace MD2;
|
using namespace MD2;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Used to load MD2 files
|
/** Importer class for MD2
|
||||||
*/
|
*/
|
||||||
class MD2Importer : public BaseImporter
|
class MD2Importer : public BaseImporter
|
||||||
{
|
{
|
||||||
|
@ -73,7 +74,8 @@ public:
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.md2");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** 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 Assimp {
|
||||||
namespace MD3 {
|
namespace MD3 {
|
||||||
|
|
||||||
// to make it easier for ourselfes, we test the magic word against both "endianesses"
|
// to make it easier for us, 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 AI_MAKE_MAGIC("IDP3")
|
||||||
|
#define AI_MD3_MAGIC_NUMBER_LE AI_MAKE_MAGIC("3PDI")
|
||||||
#define AI_MD3_MAGIC_NUMBER_BE MD3_MAKE("IDP3")
|
|
||||||
#define AI_MD3_MAGIC_NUMBER_LE MD3_MAKE("3PDI")
|
|
||||||
|
|
||||||
// common limitations
|
// common limitations
|
||||||
#define AI_MD3_VERSION 15
|
#define AI_MD3_VERSION 15
|
||||||
|
|
|
@ -344,18 +344,19 @@ MD3Importer::~MD3Importer()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
const std::string extension = GetExtension(pFile);
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
if (extension == "md3")
|
||||||
// no file extension - can't read
|
return true;
|
||||||
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);
|
|
||||||
|
|
||||||
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
|
// AI_CONFIG_IMPORT_MD3_SHADER_SRC
|
||||||
configShaderFile = (pImp->GetPropertyString(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
|
void MD3Importer::ReadShader(Q3Shader::ShaderData& fill) const
|
||||||
{
|
{
|
||||||
// Determine Q3 model name from given path
|
// Determine Q3 model name from given path
|
||||||
std::string::size_type s = path.find_last_of('\\',path.length()-2);
|
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);
|
|
||||||
|
|
||||||
const std::string model_file = path.substr(s+1,path.length()-(s+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
|
// 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
|
// Read a multi-part Q3 player model
|
||||||
bool MD3Importer::ReadMultipartFile()
|
bool MD3Importer::ReadMultipartFile()
|
||||||
|
@ -520,32 +539,32 @@ bool MD3Importer::ReadMultipartFile()
|
||||||
|
|
||||||
// now read these three files
|
// now read these three files
|
||||||
BatchLoader batch(mIOHandler);
|
BatchLoader batch(mIOHandler);
|
||||||
batch.AddLoadRequest(lower,0,&props);
|
unsigned int _lower = batch.AddLoadRequest(lower,0,&props);
|
||||||
batch.AddLoadRequest(upper,0,&props);
|
unsigned int _upper = batch.AddLoadRequest(upper,0,&props);
|
||||||
batch.AddLoadRequest(head,0,&props);
|
unsigned int _head = batch.AddLoadRequest(head,0,&props);
|
||||||
batch.LoadAll();
|
batch.LoadAll();
|
||||||
|
|
||||||
// now construct a dummy scene to place these three parts in
|
// now construct a dummy scene to place these three parts in
|
||||||
aiScene* master = new aiScene();
|
aiScene* master = new aiScene();
|
||||||
aiNode* nd = master->mRootNode = new aiNode();
|
aiNode* nd = master->mRootNode = new aiNode();
|
||||||
nd->mName.Set("<M3D_Player>");
|
nd->mName.Set("<MD3_Player>");
|
||||||
|
|
||||||
// ... and get them. We need all of them.
|
// ... and get them. We need all of them.
|
||||||
scene_lower = batch.GetImport(lower);
|
scene_lower = batch.GetImport(_lower);
|
||||||
if (!scene_lower) {
|
if (!scene_lower) {
|
||||||
DefaultLogger::get()->error("M3D: Failed to read multipart model, lower.md3 fails to load");
|
DefaultLogger::get()->error("M3D: Failed to read multipart model, lower.md3 fails to load");
|
||||||
failure = "lower";
|
failure = "lower";
|
||||||
goto error_cleanup;
|
goto error_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
scene_upper = batch.GetImport(upper);
|
scene_upper = batch.GetImport(_upper);
|
||||||
if (!scene_upper) {
|
if (!scene_upper) {
|
||||||
DefaultLogger::get()->error("M3D: Failed to read multipart model, upper.md3 fails to load");
|
DefaultLogger::get()->error("M3D: Failed to read multipart model, upper.md3 fails to load");
|
||||||
failure = "upper";
|
failure = "upper";
|
||||||
goto error_cleanup;
|
goto error_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
scene_head = batch.GetImport(head);
|
scene_head = batch.GetImport(_head);
|
||||||
if (!scene_head) {
|
if (!scene_head) {
|
||||||
DefaultLogger::get()->error("M3D: Failed to read multipart model, head.md3 fails to load");
|
DefaultLogger::get()->error("M3D: Failed to read multipart model, head.md3 fails to load");
|
||||||
failure = "head";
|
failure = "head";
|
||||||
|
@ -555,6 +574,7 @@ bool MD3Importer::ReadMultipartFile()
|
||||||
// build attachment infos. search for typical Q3 tags
|
// build attachment infos. search for typical Q3 tags
|
||||||
|
|
||||||
// original root
|
// original root
|
||||||
|
scene_lower->mRootNode->mName.Set("lower");
|
||||||
attach.push_back(AttachmentInfo(scene_lower, nd));
|
attach.push_back(AttachmentInfo(scene_lower, nd));
|
||||||
|
|
||||||
// tag_torso
|
// tag_torso
|
||||||
|
@ -563,6 +583,7 @@ bool MD3Importer::ReadMultipartFile()
|
||||||
DefaultLogger::get()->error("M3D: Failed to find attachment tag for multipart model: tag_torso expected");
|
DefaultLogger::get()->error("M3D: Failed to find attachment tag for multipart model: tag_torso expected");
|
||||||
goto error_cleanup;
|
goto error_cleanup;
|
||||||
}
|
}
|
||||||
|
scene_upper->mRootNode->mName.Set("upper");
|
||||||
attach.push_back(AttachmentInfo(scene_upper,tag_torso));
|
attach.push_back(AttachmentInfo(scene_upper,tag_torso));
|
||||||
|
|
||||||
// tag_head
|
// tag_head
|
||||||
|
@ -571,13 +592,21 @@ bool MD3Importer::ReadMultipartFile()
|
||||||
DefaultLogger::get()->error("M3D: Failed to find attachment tag for multipart model: tag_head expected");
|
DefaultLogger::get()->error("M3D: Failed to find attachment tag for multipart model: tag_head expected");
|
||||||
goto error_cleanup;
|
goto error_cleanup;
|
||||||
}
|
}
|
||||||
|
scene_head->mRootNode->mName.Set("head");
|
||||||
attach.push_back(AttachmentInfo(scene_head,tag_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
|
// and merge the scenes
|
||||||
SceneCombiner::MergeScenes(&mScene,master, attach,
|
SceneCombiner::MergeScenes(&mScene,master, attach,
|
||||||
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES |
|
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES |
|
||||||
AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES |
|
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;
|
return true;
|
||||||
|
|
||||||
|
|
|
@ -225,7 +225,8 @@ public:
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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 */
|
/** Configuration option: name or path of shader */
|
||||||
std::string configShaderFile;
|
std::string configShaderFile;
|
||||||
|
|
||||||
|
/** Configuration option: speed flag was set? */
|
||||||
|
bool configSpeedFlag;
|
||||||
|
|
||||||
/** Header of the MD3 file */
|
/** Header of the MD3 file */
|
||||||
BE_NCONST MD3::Header* pcHeader;
|
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"
|
#include "AssimpPCH.h"
|
||||||
#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
|
||||||
|
@ -56,34 +58,35 @@ using namespace Assimp;
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Constructor to be privately used by Importer
|
// Constructor to be privately used by Importer
|
||||||
MD5Importer::MD5Importer()
|
MD5Importer::MD5Importer()
|
||||||
{
|
{}
|
||||||
// nothing to do here
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Destructor, private as well
|
// Destructor, private as well
|
||||||
MD5Importer::~MD5Importer()
|
MD5Importer::~MD5Importer()
|
||||||
{
|
{}
|
||||||
// nothing to do here
|
|
||||||
}
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
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);
|
|
||||||
|
|
||||||
if (extension.length() < 4)return false;
|
if (extension == "md5anim" || extension == "md5mesh")
|
||||||
if (extension[0] != '.')return false;
|
return true;
|
||||||
|
else if (!extension.length() || checkSig) {
|
||||||
|
if (!pIOHandler)
|
||||||
|
return true;
|
||||||
|
const char* tokens[] = {"MD5Version"};
|
||||||
|
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (extension[1] != 'm' && extension[1] != 'M')return false;
|
// ------------------------------------------------------------------------------------------------
|
||||||
if (extension[2] != 'd' && extension[2] != 'D')return false;
|
// Get list of all supported extensions
|
||||||
if (extension[3] != '5')return false;
|
void MD5Importer::GetExtensionList(std::string& append)
|
||||||
return true;
|
{
|
||||||
|
append.append("*.md5mesh;*.md5anim");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -93,17 +96,17 @@ void MD5Importer::InternReadFile(
|
||||||
{
|
{
|
||||||
// remove the file extension
|
// remove the file extension
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
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->pIOHandler = pIOHandler;
|
||||||
this->pScene = pScene;
|
this->pScene = pScene;
|
||||||
|
|
||||||
bHadMD5Mesh = bHadMD5Anim = false;
|
bHadMD5Mesh = bHadMD5Anim = false;
|
||||||
|
|
||||||
// load the animation keyframes
|
// load the animation keyframes
|
||||||
this->LoadMD5AnimFile();
|
LoadMD5AnimFile();
|
||||||
|
|
||||||
// load the mesh vertices and bones
|
// load the mesh vertices and bones
|
||||||
this->LoadMD5MeshFile();
|
LoadMD5MeshFile();
|
||||||
|
|
||||||
// make sure we return no incomplete data
|
// make sure we return no incomplete data
|
||||||
if (!bHadMD5Mesh && !bHadMD5Anim)
|
if (!bHadMD5Mesh && !bHadMD5Anim)
|
||||||
|
@ -260,7 +263,7 @@ void MD5Importer::LoadMD5MeshFile ()
|
||||||
bHadMD5Mesh = true;
|
bHadMD5Mesh = true;
|
||||||
|
|
||||||
// now load the file into memory
|
// now load the file into memory
|
||||||
this->LoadFileIntoMemory(file.get());
|
LoadFileIntoMemory(file.get());
|
||||||
|
|
||||||
// now construct a parser and parse the file
|
// now construct a parser and parse the file
|
||||||
MD5::MD5Parser parser(mBuffer,fileSize);
|
MD5::MD5Parser parser(mBuffer,fileSize);
|
||||||
|
@ -341,10 +344,7 @@ void MD5Importer::LoadMD5MeshFile ()
|
||||||
unsigned int* piCount = new unsigned int[meshParser.mJoints.size()];
|
unsigned int* piCount = new unsigned int[meshParser.mJoints.size()];
|
||||||
::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size());
|
::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size());
|
||||||
|
|
||||||
for (MD5::VertexList::const_iterator
|
for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
|
||||||
iter = meshSrc.mVertices.begin();
|
|
||||||
iter != meshSrc.mVertices.end();++iter,++pv)
|
|
||||||
{
|
|
||||||
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
|
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
|
||||||
{
|
{
|
||||||
MD5::WeightDesc& desc = meshSrc.mWeights[w];
|
MD5::WeightDesc& desc = meshSrc.mWeights[w];
|
||||||
|
@ -444,7 +444,7 @@ void MD5Importer::LoadMD5MeshFile ()
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void MD5Importer::LoadMD5AnimFile ()
|
void MD5Importer::LoadMD5AnimFile ()
|
||||||
{
|
{
|
||||||
std::string pFile = this->mFile + "MD5ANIM";
|
std::string pFile = mFile + "MD5ANIM";
|
||||||
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
||||||
|
|
||||||
// Check whether we can read from the file
|
// 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.
|
/** @file MD5Loader.h
|
||||||
http://www.modwiki.net/wiki/MD5_(file_format)
|
* @brief Definition of the .MD5 importer class.
|
||||||
|
* http://www.modwiki.net/wiki/MD5_(file_format)
|
||||||
*/
|
*/
|
||||||
#ifndef AI_MD5LOADER_H_INCLUDED
|
#ifndef AI_MD5LOADER_H_INCLUDED
|
||||||
#define 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.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -82,10 +84,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.md5mesh;*.md5anim");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** Imports the given file into the given scene structure.
|
||||||
|
|
|
@ -112,21 +112,22 @@ void MD5Parser::ParseHeader()
|
||||||
if (0 != ASSIMP_strincmp(buffer,"MD5Version",10) ||
|
if (0 != ASSIMP_strincmp(buffer,"MD5Version",10) ||
|
||||||
!IsSpace(*(buffer+=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();
|
SkipSpaces();
|
||||||
unsigned int iVer = ::strtol10(buffer,(const char**)&buffer);
|
unsigned int iVer = ::strtol10(buffer,(const char**)&buffer);
|
||||||
if (10 != iVer)
|
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
|
// print the command line options to the console
|
||||||
|
// fix: can break the log length limit ...
|
||||||
char* sz = buffer;
|
char* sz = buffer;
|
||||||
while (!IsLineEnd( *buffer++));
|
while (!IsLineEnd( *buffer++));
|
||||||
DefaultLogger::get()->info(std::string(sz,(uintptr_t)(buffer-sz)));
|
DefaultLogger::get()->info(std::string(sz,std::min((uintptr_t)MAX_LOG_MESSAGE_LENGTH, (uintptr_t)(buffer-sz))));
|
||||||
this->SkipSpacesAndLineEnd();
|
SkipSpacesAndLineEnd();
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool MD5Parser::ParseSection(Section& out)
|
bool MD5Parser::ParseSection(Section& out)
|
||||||
|
|
|
@ -39,21 +39,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/** @file Definition of the .MD5 parser class.
|
/** @file MD5Parser.h
|
||||||
http://www.modwiki.net/wiki/MD5_(file_format)
|
* @brief Definition of the .MD5 parser class.
|
||||||
*/
|
* http://www.modwiki.net/wiki/MD5_(file_format)
|
||||||
|
*/
|
||||||
#ifndef AI_MD5PARSER_H_INCLUDED
|
#ifndef AI_MD5PARSER_H_INCLUDED
|
||||||
#define AI_MD5PARSER_H_INCLUDED
|
#define AI_MD5PARSER_H_INCLUDED
|
||||||
|
|
||||||
#include "../include/aiTypes.h"
|
#include "../include/aiTypes.h"
|
||||||
#include "ParsingUtils.h"
|
#include "ParsingUtils.h"
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct aiFace;
|
struct aiFace;
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
namespace MD5 {
|
namespace MD5 {
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Represents a single element in a MD5 file
|
/** Represents a single element in a MD5 file
|
||||||
|
@ -335,12 +334,13 @@ public:
|
||||||
static void ReportWarning (const char* warn, unsigned int line);
|
static void ReportWarning (const char* warn, unsigned int line);
|
||||||
|
|
||||||
|
|
||||||
inline void ReportError (const char* error)
|
void ReportError (const char* error) {
|
||||||
{return ReportError(error, this->lineNumber);}
|
return ReportError(error, lineNumber);
|
||||||
|
}
|
||||||
inline void ReportWarning (const char* warn)
|
|
||||||
{return ReportWarning(warn, this->lineNumber);}
|
|
||||||
|
|
||||||
|
void ReportWarning (const char* warn) {
|
||||||
|
return ReportWarning(warn, lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -367,24 +367,22 @@ private:
|
||||||
|
|
||||||
// override these functions to make sure the line counter gets incremented
|
// 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;
|
++lineNumber;
|
||||||
return Assimp::SkipLine(in,out);
|
return Assimp::SkipLine(in,out);
|
||||||
}
|
}
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
inline bool SkipLine( )
|
bool SkipLine( )
|
||||||
{
|
{
|
||||||
return SkipLine(buffer,(const char**)&buffer);
|
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;
|
bool bHad = false;
|
||||||
while (true)
|
while (true) {
|
||||||
{
|
if( *in == '\r' || *in == '\n') {
|
||||||
if( *in == '\r' || *in == '\n')
|
|
||||||
{
|
|
||||||
if (!bHad) // we open files in binary mode, so there could be \r\n sequences ...
|
if (!bHad) // we open files in binary mode, so there could be \r\n sequences ...
|
||||||
{
|
{
|
||||||
bHad = true;
|
bHad = true;
|
||||||
|
@ -400,12 +398,12 @@ private:
|
||||||
return *in != '\0';
|
return *in != '\0';
|
||||||
}
|
}
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
inline bool SkipSpacesAndLineEnd( )
|
bool SkipSpacesAndLineEnd( )
|
||||||
{
|
{
|
||||||
return SkipSpacesAndLineEnd(buffer,(const char**)&buffer);
|
return SkipSpacesAndLineEnd(buffer,(const char**)&buffer);
|
||||||
}
|
}
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
inline bool SkipSpaces( )
|
bool SkipSpaces( )
|
||||||
{
|
{
|
||||||
return Assimp::SkipSpaces((const char**)&buffer);
|
return Assimp::SkipSpaces((const char**)&buffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,11 +60,9 @@ namespace Assimp {
|
||||||
namespace MDC {
|
namespace MDC {
|
||||||
|
|
||||||
|
|
||||||
// to make it easier for ourselfes, we test the magic word against both "endianesses"
|
// to make it easier for us, 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 AI_MAKE_MAGIC("CPDI")
|
||||||
|
#define AI_MDC_MAGIC_NUMBER_LE AI_MAKE_MAGIC("IDPC")
|
||||||
#define AI_MDC_MAGIC_NUMBER_BE MDC_MAKE("CPDI")
|
|
||||||
#define AI_MDC_MAGIC_NUMBER_LE MDC_MAKE("IDPC")
|
|
||||||
|
|
||||||
// common limitations
|
// common limitations
|
||||||
#define AI_MDC_VERSION 2
|
#define AI_MDC_VERSION 2
|
||||||
|
|
|
@ -87,23 +87,25 @@ MDCImporter::~MDCImporter()
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
const std::string extension = GetExtension(pFile);
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
if (extension == "mdc")
|
||||||
// no file extension - can't read
|
return true;
|
||||||
if( pos == std::string::npos)
|
|
||||||
return false;
|
|
||||||
std::string extension = pFile.substr( pos);
|
|
||||||
|
|
||||||
if (extension.length() < 4)return false;
|
// if check for extension is not enough, check for the magic tokens
|
||||||
if (extension[0] != '.')return false;
|
if (!extension.length() || checkSig) {
|
||||||
|
uint32_t tokens[1];
|
||||||
|
tokens[0] = AI_MDC_MAGIC_NUMBER_LE;
|
||||||
|
return CheckMagicToken(pIOHandler,pFile,tokens,1);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if( extension[1] != 'M' && extension[1] != 'm')return false;
|
// ------------------------------------------------------------------------------------------------
|
||||||
if( extension[2] != 'D' && extension[2] != 'd')return false;
|
void MDCImporter::GetExtensionList(std::string& append)
|
||||||
if( extension[3] != 'C' && extension[3] != 'c')return false;
|
{
|
||||||
|
append.append("*.mdc");
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -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
|
#ifndef AI_MDCLOADER_H_INCLUDED
|
||||||
#define 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 "MDCFileData.h"
|
||||||
#include "ByteSwap.h"
|
#include "ByteSwap.h"
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp {
|
||||||
{
|
|
||||||
using namespace MDC;
|
using namespace MDC;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Used to load MDC files
|
/** Importer class to load the RtCW MDC file format
|
||||||
*/
|
*/
|
||||||
class MDCImporter : public BaseImporter
|
class MDCImporter : public BaseImporter
|
||||||
{
|
{
|
||||||
|
@ -71,7 +72,8 @@ public:
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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().
|
/** Called prior to ReadFile().
|
||||||
|
@ -86,10 +88,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.mdc");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** Imports the given file into the given scene structure.
|
||||||
|
|
|
@ -58,30 +58,29 @@ namespace Assimp {
|
||||||
namespace MDL {
|
namespace MDL {
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------
|
||||||
// to make it easier for ourselfes, we test the magic word against both "endianesses"
|
// to make it easier for us, 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]))
|
|
||||||
|
|
||||||
// magic bytes used in Quake 1 MDL meshes
|
// magic bytes used in Quake 1 MDL meshes
|
||||||
#define AI_MDL_MAGIC_NUMBER_BE MDL_MAKE("IDPO")
|
#define AI_MDL_MAGIC_NUMBER_BE AI_MAKE_MAGIC("IDPO")
|
||||||
#define AI_MDL_MAGIC_NUMBER_LE MDL_MAKE("OPDI")
|
#define AI_MDL_MAGIC_NUMBER_LE AI_MAKE_MAGIC("OPDI")
|
||||||
|
|
||||||
// magic bytes used in GameStudio A<very low> MDL meshes
|
// 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_BE_GS3 AI_MAKE_MAGIC("MDL2")
|
||||||
#define AI_MDL_MAGIC_NUMBER_LE_GS3 MDL_MAKE("2LDM")
|
#define AI_MDL_MAGIC_NUMBER_LE_GS3 AI_MAKE_MAGIC("2LDM")
|
||||||
|
|
||||||
// magic bytes used in GameStudio A4 MDL meshes
|
// magic bytes used in GameStudio A4 MDL meshes
|
||||||
#define AI_MDL_MAGIC_NUMBER_BE_GS4 MDL_MAKE("MDL3")
|
#define AI_MDL_MAGIC_NUMBER_BE_GS4 AI_MAKE_MAGIC("MDL3")
|
||||||
#define AI_MDL_MAGIC_NUMBER_LE_GS4 MDL_MAKE("3LDM")
|
#define AI_MDL_MAGIC_NUMBER_LE_GS4 AI_MAKE_MAGIC("3LDM")
|
||||||
|
|
||||||
// magic bytes used in GameStudio A5+ MDL meshes
|
// magic bytes used in GameStudio A5+ MDL meshes
|
||||||
#define AI_MDL_MAGIC_NUMBER_BE_GS5a MDL_MAKE("MDL4")
|
#define AI_MDL_MAGIC_NUMBER_BE_GS5a AI_MAKE_MAGIC("MDL4")
|
||||||
#define AI_MDL_MAGIC_NUMBER_LE_GS5a MDL_MAKE("4LDM")
|
#define AI_MDL_MAGIC_NUMBER_LE_GS5a AI_MAKE_MAGIC("4LDM")
|
||||||
#define AI_MDL_MAGIC_NUMBER_BE_GS5b MDL_MAKE("MDL5")
|
#define AI_MDL_MAGIC_NUMBER_BE_GS5b AI_MAKE_MAGIC("MDL5")
|
||||||
#define AI_MDL_MAGIC_NUMBER_LE_GS5b MDL_MAKE("5LDM")
|
#define AI_MDL_MAGIC_NUMBER_LE_GS5b AI_MAKE_MAGIC("5LDM")
|
||||||
|
|
||||||
// magic bytes used in GameStudio A7+ MDL meshes
|
// magic bytes used in GameStudio A7+ MDL meshes
|
||||||
#define AI_MDL_MAGIC_NUMBER_BE_GS7 MDL_MAKE("MDL7")
|
#define AI_MDL_MAGIC_NUMBER_BE_GS7 AI_MAKE_MAGIC("MDL7")
|
||||||
#define AI_MDL_MAGIC_NUMBER_LE_GS7 MDL_MAKE("7LDM")
|
#define AI_MDL_MAGIC_NUMBER_LE_GS7 AI_MAKE_MAGIC("7LDM")
|
||||||
|
|
||||||
|
|
||||||
// common limitations for Quake1 meshes. The loader does not check them,
|
// 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.
|
// 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
|
const std::string extension = GetExtension(pFile);
|
||||||
// simple check of file extension is enough for the moment
|
if (extension == "mdl" )
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
return true;
|
||||||
// no file extension - can't read
|
|
||||||
if( pos == std::string::npos)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string extension = pFile.substr( pos);
|
// if check for extension is not enough, check for the magic tokens
|
||||||
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
|
if (!extension.length() || checkSig) {
|
||||||
*it = tolower( *it);
|
uint32_t tokens[8];
|
||||||
|
tokens[0] = AI_MDL_MAGIC_NUMBER_LE_HL2a;
|
||||||
return extension == ".mdl";
|
tokens[1] = AI_MDL_MAGIC_NUMBER_LE_HL2b;
|
||||||
|
tokens[2] = AI_MDL_MAGIC_NUMBER_LE_GS7;
|
||||||
|
tokens[3] = AI_MDL_MAGIC_NUMBER_LE_GS5b;
|
||||||
|
tokens[4] = AI_MDL_MAGIC_NUMBER_LE_GS5a;
|
||||||
|
tokens[5] = AI_MDL_MAGIC_NUMBER_LE_GS4;
|
||||||
|
tokens[6] = AI_MDL_MAGIC_NUMBER_LE_GS3;
|
||||||
|
tokens[7] = AI_MDL_MAGIC_NUMBER_LE;
|
||||||
|
return CheckMagicToken(pIOHandler,pFile,tokens,7,0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Setup configuration properties
|
// Setup configuration properties
|
||||||
void MDLImporter::SetupProperties(const Importer* pImp)
|
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.
|
// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
|
||||||
if(0xffffffff == (configFrameID = pImp->GetPropertyInteger(
|
if(0xffffffff == configFrameID) {
|
||||||
AI_CONFIG_IMPORT_MDL_KEYFRAME,0xffffffff)))
|
|
||||||
{
|
|
||||||
configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
|
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");
|
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.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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);
|
ai_assert(NULL != pcTexture);
|
||||||
|
|
||||||
aiColor4D clrOut;
|
aiColor4D clrOut;
|
||||||
clrOut.r = std::numeric_limits<float>::quiet_NaN();
|
clrOut.r = get_qnan();
|
||||||
if (!pcTexture->mHeight || !pcTexture->mWidth)
|
if (!pcTexture->mHeight || !pcTexture->mWidth)
|
||||||
return clrOut;
|
return clrOut;
|
||||||
|
|
||||||
|
@ -572,7 +572,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
|
||||||
// been converted to MDL7 from other formats, such as MDL5
|
// been converted to MDL7 from other formats, such as MDL5
|
||||||
aiColor4D clrTexture;
|
aiColor4D clrTexture;
|
||||||
if (pcNew)clrTexture = this->ReplaceTextureWithColor(pcNew);
|
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
|
// check whether a material definition is contained in the skin
|
||||||
if (iType & AI_MDL7_SKINTYPE_MATERIAL)
|
if (iType & AI_MDL7_SKINTYPE_MATERIAL)
|
||||||
|
|
|
@ -65,22 +65,16 @@ NFFImporter::~NFFImporter()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
return SimpleExtensionCheck(pFile,"nff","enff");
|
||||||
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);
|
|
||||||
|
|
||||||
// extensions: enff and nff
|
// ------------------------------------------------------------------------------------------------
|
||||||
for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
|
// Get the list of all supported file extensions
|
||||||
*it = tolower( *it);
|
void NFFImporter::GetExtensionList(std::string& append)
|
||||||
|
{
|
||||||
if( extension == ".nff" || extension == ".enff")
|
append.append("*.nff;*.enff");
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -96,7 +90,7 @@ bool NFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
#define AI_NFF_PARSE_SHAPE_INFORMATION() \
|
#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(center); \
|
||||||
AI_NFF_PARSE_TRIPLE(radius); \
|
AI_NFF_PARSE_TRIPLE(radius); \
|
||||||
if (is_qnan(radius.z))radius.z = radius.x; \
|
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
|
// check whether this is the NFF2 file format
|
||||||
if (TokenMatch(buffer,"nff",3))
|
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 aiColor4D cQNAN = aiColor4D (qnan,0.f,0.f,1.f);
|
||||||
const aiVector3D vQNAN = aiVector3D(qnan,0.f,0.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
|
#ifndef AI_NFFLOADER_H_INCLUDED
|
||||||
#define 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 {
|
namespace Assimp {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
/** NFF (Neutral File Format) Importer class.
|
/** NFF (Neutral File Format) Importer class.
|
||||||
*
|
*
|
||||||
* The class implements both Eric Haynes NFF format and Sense8's NFF (NFF2) format.
|
* 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.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -80,10 +84,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.nff;*.enff");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** 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"
|
#include "AssimpPCH.h"
|
||||||
#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
|
||||||
|
@ -64,25 +66,31 @@ OFFImporter::~OFFImporter()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
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);
|
|
||||||
|
|
||||||
|
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' ||
|
void OFFImporter::GetExtensionList(std::string& append)
|
||||||
extension[2] != 'f' && extension[2] != 'F' ||
|
{
|
||||||
extension[3] != 'f' && extension[3] != 'F');
|
append.append("*.off");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Imports the given file into the given scene structure.
|
// Imports the given file into the given scene structure.
|
||||||
void OFFImporter::InternReadFile( const std::string& pFile,
|
void OFFImporter::InternReadFile( const std::string& pFile,
|
||||||
aiScene* pScene, IOSystem* pIOHandler)
|
aiScene* pScene, IOSystem* pIOHandler)
|
||||||
{
|
{
|
||||||
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
||||||
|
|
||||||
|
|
|
@ -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
|
#ifndef AI_OFFLOADER_H_INCLUDED
|
||||||
#define 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.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -75,10 +78,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.off");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** Imports the given file into the given scene structure.
|
||||||
|
|
|
@ -51,9 +51,6 @@ namespace Assimp {
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
//! Obj-file-format extention
|
|
||||||
const string ObjFileImporter::OBJ_EXT = "obj";
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Default constructor
|
// Default constructor
|
||||||
ObjFileImporter::ObjFileImporter() :
|
ObjFileImporter::ObjFileImporter() :
|
||||||
|
@ -76,20 +73,10 @@ ObjFileImporter::~ObjFileImporter()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns true, fi file is an obj file
|
// 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())
|
// fixme: auto detection
|
||||||
return false;
|
return SimpleExtensionCheck(pFile,"obj");
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -66,9 +66,6 @@ class ObjFileImporter :
|
||||||
{
|
{
|
||||||
friend class Importer;
|
friend class Importer;
|
||||||
|
|
||||||
//! OB file extention
|
|
||||||
static const std::string OBJ_EXT;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// \brief Default constructor
|
/// \brief Default constructor
|
||||||
ObjFileImporter();
|
ObjFileImporter();
|
||||||
|
@ -79,7 +76,7 @@ protected:
|
||||||
public:
|
public:
|
||||||
/// \brief Returns whether the class can handle the format of the given file.
|
/// \brief Returns whether the class can handle the format of the given file.
|
||||||
/// \remark See BaseImporter::CanRead() for details.
|
/// \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:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -64,27 +64,31 @@ PLYImporter::~PLYImporter()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
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);
|
|
||||||
|
|
||||||
if (extension.length() < 4)return false;
|
if (extension == "ply")
|
||||||
if (extension[0] != '.') return false;
|
return true;
|
||||||
if (extension[1] != 'p' && extension[1] != 'P')return false;
|
else if (!extension.length() || checkSig)
|
||||||
if (extension[2] != 'l' && extension[2] != 'L')return false;
|
{
|
||||||
if (extension[3] != 'y' && extension[3] != 'Y')return false;
|
if (!pIOHandler)return true;
|
||||||
return true;
|
const char* tokens[] = {"ply"};
|
||||||
|
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void PLYImporter::GetExtensionList(std::string& append)
|
||||||
|
{
|
||||||
|
append.append("*.ply");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Imports the given file into the given scene structure.
|
// Imports the given file into the given scene structure.
|
||||||
void PLYImporter::InternReadFile(
|
void PLYImporter::InternReadFile(
|
||||||
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
|
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
|
||||||
{
|
{
|
||||||
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
|
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
|
||||||
|
|
||||||
|
@ -108,8 +112,7 @@ void PLYImporter::InternReadFile(
|
||||||
// the beginning of the file must be PLY - magic, magic
|
// the beginning of the file must be PLY - magic, magic
|
||||||
if (mBuffer[0] != 'P' && mBuffer[0] != 'p' ||
|
if (mBuffer[0] != 'P' && mBuffer[0] != 'p' ||
|
||||||
mBuffer[1] != 'L' && mBuffer[1] != 'l' ||
|
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");
|
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
|
else // triangle strips
|
||||||
{
|
{
|
||||||
// HUGE TODO: MAKE THAT FUCK WORK!
|
|
||||||
|
|
||||||
// normally we have only one triangle strip instance where
|
// normally we have only one triangle strip instance where
|
||||||
// a value of -1 indicates a restart of the strip
|
// a value of -1 indicates a restart of the strip
|
||||||
for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();
|
for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();
|
||||||
i != pcList->alInstances.end();++i)
|
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};
|
int aiTable[2] = {-1,-1};
|
||||||
for (std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator
|
for (std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator a = quak.begin();a != quak.end();++a) {
|
||||||
a = (*i).alProperties[iProperty].avList.begin();
|
const int p = PLY::PropertyInstance::ConvertTo<int>(*a,eType);
|
||||||
a != (*i).alProperties[iProperty].avList.end();++a)
|
|
||||||
{
|
|
||||||
int p = PLY::PropertyInstance::ConvertTo<int>(*a,eType);
|
|
||||||
if (-1 == p)
|
if (-1 == p)
|
||||||
{
|
{
|
||||||
// restart the strip ...
|
// restart the strip ...
|
||||||
|
@ -819,11 +820,11 @@ void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
|
||||||
continue;
|
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[0]);
|
||||||
sFace.mIndices.push_back((unsigned int)aiTable[1]);
|
sFace.mIndices.push_back((unsigned int)aiTable[1]);
|
||||||
sFace.mIndices.push_back((unsigned int)p);
|
sFace.mIndices.push_back((unsigned int)p);
|
||||||
pvOut->push_back(sFace);
|
|
||||||
|
|
||||||
aiTable[0] = aiTable[1];
|
aiTable[0] = aiTable[1];
|
||||||
aiTable[1] = p;
|
aiTable[1] = p;
|
||||||
|
|
|
@ -38,8 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/** @file PLYLoader.h
|
||||||
/** @file Declaration of the .ply importer class. */
|
* @brief Declaration of the .ply importer class.
|
||||||
|
*/
|
||||||
#ifndef AI_PLYLOADER_H_INCLUDED
|
#ifndef AI_PLYLOADER_H_INCLUDED
|
||||||
#define AI_PLYLOADER_H_INCLUDED
|
#define AI_PLYLOADER_H_INCLUDED
|
||||||
|
|
||||||
|
@ -50,15 +51,13 @@ struct aiNode;
|
||||||
|
|
||||||
#include "PlyParser.h"
|
#include "PlyParser.h"
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp {
|
||||||
{
|
|
||||||
class MaterialHelper;
|
class MaterialHelper;
|
||||||
|
|
||||||
|
|
||||||
using namespace PLY;
|
using namespace PLY;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Used to load PLY files
|
/** Importer class to load the stanford PLY file format
|
||||||
*/
|
*/
|
||||||
class PLYImporter : public BaseImporter
|
class PLYImporter : public BaseImporter
|
||||||
{
|
{
|
||||||
|
@ -75,8 +74,10 @@ public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -84,10 +85,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.ply");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** 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);
|
unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v,prop->eFirstType);
|
||||||
|
|
||||||
// parse all list elements
|
// parse all list elements
|
||||||
for (unsigned int i = 0; i < iNum;++i)
|
p_pcOut->avList.resize(iNum);
|
||||||
{
|
for (unsigned int i = 0; i < iNum;++i){
|
||||||
PLY::PropertyInstance::ParseValueBinary(pCur, &pCur,prop->eType,&v,p_bBE);
|
PLY::PropertyInstance::ParseValueBinary(pCur, &pCur,prop->eType,&p_pcOut->avList[i],p_bBE);
|
||||||
p_pcOut->avList.push_back(v);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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 "AssimpPCH.h"
|
||||||
#include "PretransformVertices.h"
|
#include "PretransformVertices.h"
|
||||||
#include "ProcessHelper.h"
|
#include "ProcessHelper.h"
|
||||||
|
#include "SceneCombiner.h"
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
|
@ -55,6 +57,7 @@ using namespace Assimp;
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Constructor to be privately used by Importer
|
// Constructor to be privately used by Importer
|
||||||
PretransformVertices::PretransformVertices()
|
PretransformVertices::PretransformVertices()
|
||||||
|
: configKeepHierarchy (false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,9 +75,17 @@ bool PretransformVertices::IsActive( unsigned int pFlags) const
|
||||||
return (pFlags & aiProcess_PreTransformVertices) != 0;
|
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
|
// Count the number of nodes
|
||||||
unsigned int CountNodes( aiNode* pcNode )
|
unsigned int PretransformVertices::CountNodes( aiNode* pcNode )
|
||||||
{
|
{
|
||||||
unsigned int iRet = 1;
|
unsigned int iRet = 1;
|
||||||
for (unsigned int i = 0;i < pcNode->mNumChildren;++i)
|
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
|
// 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.
|
// the vertex format is stored in aiMesh::mBones for later retrieval.
|
||||||
// there isn't a good reason to compute it a few hundred times
|
// 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
|
// Count the number of vertices in the whole scene and a given
|
||||||
// material index
|
// 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)
|
unsigned int iVFormat, unsigned int* piFaces, unsigned int* piVertices)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
|
for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
|
||||||
|
@ -127,39 +138,73 @@ void CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Collect vertex/face data
|
// 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 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)
|
for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
|
||||||
{
|
{
|
||||||
aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
|
aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
|
||||||
if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
|
if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
|
||||||
{
|
{
|
||||||
// copy positions, transform them to worldspace
|
// Decrement mesh reference counter
|
||||||
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)
|
unsigned int& num_ref = num_refs[pcNode->mMeshes[i]];
|
||||||
pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n];
|
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));
|
||||||
|
|
||||||
aiMatrix4x4 mWorldIT = pcNode->mTransformation;
|
if (iVFormat & 0x2) {
|
||||||
mWorldIT.Inverse().Transpose();
|
// copy normals without modifying them
|
||||||
|
::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX],
|
||||||
// TODO: implement Inverse() for aiMatrix3x3
|
pcMesh->mNormals,
|
||||||
aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
|
pcMesh->mNumVertices * sizeof(aiVector3D));
|
||||||
|
}
|
||||||
if (iVFormat & 0x2)
|
if (iVFormat & 0x4)
|
||||||
{
|
{
|
||||||
// copy normals, transform them to worldspace
|
// copy tangents without modifying them
|
||||||
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
|
::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX],
|
||||||
pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mNormals[n];
|
pcMesh->mTangents,
|
||||||
|
pcMesh->mNumVertices * sizeof(aiVector3D));
|
||||||
|
// copy bitangents without modifying them
|
||||||
|
::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX],
|
||||||
|
pcMesh->mBitangents,
|
||||||
|
pcMesh->mNumVertices * sizeof(aiVector3D));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (iVFormat & 0x4)
|
else
|
||||||
{
|
{
|
||||||
// copy tangents and bitangents, transform them to worldspace
|
// copy positions, transform them to worldspace
|
||||||
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
|
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
|
||||||
pcMeshOut->mTangents [aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mTangents[n];
|
pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n];
|
||||||
pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mBitangents[n];
|
}
|
||||||
|
aiMatrix4x4 mWorldIT = pcNode->mTransformation;
|
||||||
|
mWorldIT.Inverse().Transpose();
|
||||||
|
|
||||||
|
// TODO: implement Inverse() for aiMatrix3x3
|
||||||
|
aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
|
||||||
|
|
||||||
|
if (iVFormat & 0x2)
|
||||||
|
{
|
||||||
|
// copy normals, transform them to worldspace
|
||||||
|
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
|
||||||
|
pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] =
|
||||||
|
m * pcMesh->mNormals[n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (iVFormat & 0x4)
|
||||||
|
{
|
||||||
|
// copy tangents and bitangents, transform them to worldspace
|
||||||
|
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
|
||||||
|
pcMeshOut->mTangents [aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mTangents[n];
|
||||||
|
pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mBitangents[n];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsigned int p = 0;
|
unsigned int p = 0;
|
||||||
|
@ -180,26 +225,35 @@ void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
|
||||||
pcMesh->mNumVertices * sizeof(aiColor4D));
|
pcMesh->mNumVertices * sizeof(aiColor4D));
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
// now we need to copy all faces
|
// now we need to copy all faces. since we will delete the source mesh afterwards,
|
||||||
// since we will delete the source mesh afterwards,
|
// we don't need to reallocate the array of indices except if this mesh is
|
||||||
// we don't need to reallocate the array of indices
|
// referenced multiple times.
|
||||||
for (unsigned int planck = 0;planck<pcMesh->mNumFaces;++planck)
|
for (unsigned int planck = 0;planck < pcMesh->mNumFaces;++planck)
|
||||||
{
|
{
|
||||||
pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck].mNumIndices =
|
aiFace& f_src = pcMesh->mFaces[planck];
|
||||||
pcMesh->mFaces[planck].mNumIndices;
|
aiFace& f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck];
|
||||||
|
|
||||||
unsigned int* pi = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck].
|
const unsigned int num_idx = f_src.mNumIndices;
|
||||||
mIndices = pcMesh->mFaces[planck].mIndices;
|
|
||||||
|
|
||||||
// offset all vrtex indices
|
f_dst.mNumIndices = num_idx;
|
||||||
for (unsigned int hahn = 0; hahn < pcMesh->mFaces[planck].mNumIndices;++hahn)
|
|
||||||
{
|
unsigned int* pi;
|
||||||
pi[hahn] += aiCurrent[AI_PTVS_VERTEX];
|
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
|
// copy and offset all vertex indices
|
||||||
// aiFace destructor ...
|
for (unsigned int hahn = 0; hahn < num_idx;++hahn){
|
||||||
pcMesh->mFaces[planck].mIndices = NULL;
|
pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update the mPrimitiveTypes member of the mesh
|
// Update the mPrimitiveTypes member of the mesh
|
||||||
switch (pcMesh->mFaces[planck].mNumIndices)
|
switch (pcMesh->mFaces[planck].mNumIndices)
|
||||||
|
@ -222,24 +276,24 @@ void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
|
||||||
aiCurrent[AI_PTVS_FACE] += pcMesh->mNumFaces;
|
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,
|
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
|
// Get a list of all vertex formats that occur for a given material index
|
||||||
// The output list contains duplicate elements
|
// 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)
|
std::list<unsigned int>& aiOut)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
|
for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
|
||||||
{
|
{
|
||||||
aiMesh* pcMesh = pcScene->mMeshes[ i ];
|
aiMesh* pcMesh = pcScene->mMeshes[ i ];
|
||||||
if (iMat == pcMesh->mMaterialIndex)
|
if (iMat == pcMesh->mMaterialIndex) {
|
||||||
{
|
|
||||||
aiOut.push_back(GetMeshVFormat(pcMesh));
|
aiOut.push_back(GetMeshVFormat(pcMesh));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,7 +301,7 @@ void GetVFormatList( aiScene* pcScene, unsigned int iMat,
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Compute the absolute transformation matrices of each node
|
// Compute the absolute transformation matrices of each node
|
||||||
void ComputeAbsoluteTransform( aiNode* pcNode )
|
void PretransformVertices::ComputeAbsoluteTransform( aiNode* pcNode )
|
||||||
{
|
{
|
||||||
if (pcNode->mParent) {
|
if (pcNode->mParent) {
|
||||||
pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation;
|
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.
|
// Executes the post processing step on the given imported data.
|
||||||
void PretransformVertices::Execute( aiScene* pScene)
|
void PretransformVertices::Execute( aiScene* pScene)
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->debug("PretransformVerticesProcess begin");
|
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 iOldMeshes = pScene->mNumMeshes;
|
||||||
const unsigned int iOldAnimationChannels = pScene->mNumAnimations;
|
const unsigned int iOldAnimationChannels = pScene->mNumAnimations;
|
||||||
const unsigned int iOldNodes = CountNodes(pScene->mRootNode);
|
const unsigned int iOldNodes = CountNodes(pScene->mRootNode);
|
||||||
|
@ -271,11 +439,10 @@ void PretransformVertices::Execute( aiScene* pScene)
|
||||||
// first compute absolute transformation matrices for all nodes
|
// first compute absolute transformation matrices for all nodes
|
||||||
ComputeAbsoluteTransform(pScene->mRootNode);
|
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
|
// removed during this step and we need the pointer as
|
||||||
// temporary storage
|
// 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];
|
aiMesh* mesh = pScene->mMeshes[i];
|
||||||
|
|
||||||
for (unsigned int a = 0; a < mesh->mNumBones;++a)
|
for (unsigned int a = 0; a < mesh->mNumBones;++a)
|
||||||
|
@ -287,50 +454,123 @@ void PretransformVertices::Execute( aiScene* pScene)
|
||||||
|
|
||||||
// now build a list of output meshes
|
// now build a list of output meshes
|
||||||
std::vector<aiMesh*> apcOutMeshes;
|
std::vector<aiMesh*> apcOutMeshes;
|
||||||
apcOutMeshes.reserve(pScene->mNumMaterials<<1u);
|
|
||||||
std::list<unsigned int> aiVFormats;
|
|
||||||
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
|
|
||||||
// get the list of all vertex formats for this material
|
|
||||||
aiVFormats.clear();
|
|
||||||
GetVFormatList(pScene,i,aiVFormats);
|
|
||||||
aiVFormats.sort();
|
|
||||||
aiVFormats.unique();
|
|
||||||
for (std::list<unsigned int>::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) {
|
|
||||||
unsigned int iVertices = 0;
|
|
||||||
unsigned int iFaces = 0;
|
|
||||||
CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices);
|
|
||||||
if (iFaces && iVertices)
|
|
||||||
{
|
|
||||||
apcOutMeshes.push_back(new aiMesh());
|
|
||||||
aiMesh* pcMesh = apcOutMeshes.back();
|
|
||||||
pcMesh->mNumFaces = iFaces;
|
|
||||||
pcMesh->mNumVertices = iVertices;
|
|
||||||
pcMesh->mFaces = new aiFace[iFaces];
|
|
||||||
pcMesh->mVertices = new aiVector3D[iVertices];
|
|
||||||
pcMesh->mMaterialIndex = i;
|
|
||||||
if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices];
|
|
||||||
if ((*j) & 0x4)
|
|
||||||
{
|
|
||||||
pcMesh->mTangents = new aiVector3D[iVertices];
|
|
||||||
pcMesh->mBitangents = new aiVector3D[iVertices];
|
|
||||||
}
|
|
||||||
iFaces = 0;
|
|
||||||
while ((*j) & (0x100 << iFaces))
|
|
||||||
{
|
|
||||||
pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
|
|
||||||
if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3;
|
|
||||||
else pcMesh->mNumUVComponents[iFaces] = 2;
|
|
||||||
iFaces++;
|
|
||||||
}
|
|
||||||
iFaces = 0;
|
|
||||||
while ((*j) & (0x1000000 << iFaces))
|
|
||||||
pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
|
|
||||||
|
|
||||||
// fill the mesh ...
|
// Keep scene hierarchy? It's an easy job in this case ...
|
||||||
unsigned int aiTemp[2] = {0,0};
|
// we go on and transform all meshes, if one is referenced by nodes
|
||||||
CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp);
|
// with different absolute transformations a depth copy of the mesh
|
||||||
|
// is required.
|
||||||
|
if( configKeepHierarchy ) {
|
||||||
|
|
||||||
|
// Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
|
||||||
|
BuildWCSMeshes(apcOutMeshes,pScene->mMeshes,pScene->mNumMeshes, pScene->mRootNode);
|
||||||
|
|
||||||
|
// ... if new meshes have been generated, append them to the end of the scene
|
||||||
|
if (apcOutMeshes.size() > 0) {
|
||||||
|
aiMesh** npp = new aiMesh*[pScene->mNumMeshes + apcOutMeshes.size()];
|
||||||
|
|
||||||
|
::memcpy(npp,pScene->mMeshes,sizeof(aiMesh*)*pScene->mNumMeshes);
|
||||||
|
::memcpy(npp+pScene->mNumMeshes,&apcOutMeshes[0],sizeof(aiMesh*)*apcOutMeshes.size());
|
||||||
|
|
||||||
|
pScene->mNumMeshes += apcOutMeshes.size();
|
||||||
|
delete[] pScene->mMeshes; pScene->mMeshes = npp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now iterate through all meshes and transform them to worldspace
|
||||||
|
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
|
||||||
|
ApplyTransform(pScene->mMeshes[i],*reinterpret_cast<aiMatrix4x4*>( pScene->mMeshes[i]->mBones ));
|
||||||
|
|
||||||
|
// prevent improper destruction
|
||||||
|
pScene->mMeshes[i]->mBones = NULL;
|
||||||
|
pScene->mMeshes[i]->mNumBones = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
apcOutMeshes.reserve(pScene->mNumMaterials<<1u);
|
||||||
|
std::list<unsigned int> aiVFormats;
|
||||||
|
|
||||||
|
std::vector<unsigned int> s(pScene->mNumMeshes,0);
|
||||||
|
BuildMeshRefCountArray(pScene->mRootNode,&s[0]);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
|
||||||
|
// get the list of all vertex formats for this material
|
||||||
|
aiVFormats.clear();
|
||||||
|
GetVFormatList(pScene,i,aiVFormats);
|
||||||
|
aiVFormats.sort();
|
||||||
|
aiVFormats.unique();
|
||||||
|
for (std::list<unsigned int>::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) {
|
||||||
|
unsigned int iVertices = 0;
|
||||||
|
unsigned int iFaces = 0;
|
||||||
|
CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices);
|
||||||
|
if (0 != iFaces && 0 != iVertices)
|
||||||
|
{
|
||||||
|
apcOutMeshes.push_back(new aiMesh());
|
||||||
|
aiMesh* pcMesh = apcOutMeshes.back();
|
||||||
|
pcMesh->mNumFaces = iFaces;
|
||||||
|
pcMesh->mNumVertices = iVertices;
|
||||||
|
pcMesh->mFaces = new aiFace[iFaces];
|
||||||
|
pcMesh->mVertices = new aiVector3D[iVertices];
|
||||||
|
pcMesh->mMaterialIndex = i;
|
||||||
|
if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices];
|
||||||
|
if ((*j) & 0x4)
|
||||||
|
{
|
||||||
|
pcMesh->mTangents = new aiVector3D[iVertices];
|
||||||
|
pcMesh->mBitangents = new aiVector3D[iVertices];
|
||||||
|
}
|
||||||
|
iFaces = 0;
|
||||||
|
while ((*j) & (0x100 << iFaces))
|
||||||
|
{
|
||||||
|
pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
|
||||||
|
if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3;
|
||||||
|
else pcMesh->mNumUVComponents[iFaces] = 2;
|
||||||
|
iFaces++;
|
||||||
|
}
|
||||||
|
iFaces = 0;
|
||||||
|
while ((*j) & (0x1000000 << iFaces))
|
||||||
|
pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
|
||||||
|
|
||||||
|
// fill the mesh ...
|
||||||
|
unsigned int aiTemp[2] = {0,0};
|
||||||
|
CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp,&s[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now delete all meshes in the scene and build a new mesh list
|
||||||
|
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
|
||||||
|
{
|
||||||
|
aiMesh* mesh = pScene->mMeshes[i];
|
||||||
|
mesh->mNumBones = 0;
|
||||||
|
mesh->mBones = NULL;
|
||||||
|
|
||||||
|
// we're reusing the face index arrays. avoid destruction
|
||||||
|
for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
|
||||||
|
mesh->mFaces[a].mNumIndices = 0;
|
||||||
|
mesh->mFaces[a].mIndices = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete mesh;
|
||||||
|
|
||||||
|
// Invalidate the contents of the old mesh array. We will most
|
||||||
|
// likely have less output meshes now, so the last entries of
|
||||||
|
// the mesh array are not overridden. We set them to NULL to
|
||||||
|
// make sure the developer gets notified when his application
|
||||||
|
// attempts to access these fields ...
|
||||||
|
mesh = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no meshes are referenced in the node graph it is possible that we get no output meshes.
|
||||||
|
if (apcOutMeshes.empty()) {
|
||||||
|
throw new ImportErrorException("No output meshes: all meshes are orphaned and are not referenced by nodes");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// It is impossible that we have more output meshes than
|
||||||
|
// input meshes, so we can easily reuse the old mesh array
|
||||||
|
pScene->mNumMeshes = (unsigned int)apcOutMeshes.size();
|
||||||
|
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
|
||||||
|
pScene->mMeshes[i] = apcOutMeshes[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all animations from the scene
|
// remove all animations from the scene
|
||||||
|
@ -341,40 +581,6 @@ void PretransformVertices::Execute( aiScene* pScene)
|
||||||
pScene->mAnimations = NULL;
|
pScene->mAnimations = NULL;
|
||||||
pScene->mNumAnimations = 0;
|
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
|
// --- we need to keep all cameras and lights
|
||||||
for (unsigned int i = 0; i < pScene->mNumCameras;++i)
|
for (unsigned int i = 0; i < pScene->mNumCameras;++i)
|
||||||
{
|
{
|
||||||
|
@ -401,51 +607,58 @@ void PretransformVertices::Execute( aiScene* pScene)
|
||||||
l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection;
|
l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// now delete all nodes in the scene and build a new
|
if( !configKeepHierarchy ) {
|
||||||
// flat node graph with a root node and some level 1 children
|
|
||||||
delete pScene->mRootNode;
|
|
||||||
pScene->mRootNode = new aiNode();
|
|
||||||
pScene->mRootNode->mName.Set("<dummy_root>");
|
|
||||||
|
|
||||||
if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras)
|
// now delete all nodes in the scene and build a new
|
||||||
{
|
// flat node graph with a root node and some level 1 children
|
||||||
pScene->mRootNode->mNumMeshes = 1;
|
delete pScene->mRootNode;
|
||||||
pScene->mRootNode->mMeshes = new unsigned int[1];
|
pScene->mRootNode = new aiNode();
|
||||||
pScene->mRootNode->mMeshes[0] = 0;
|
pScene->mRootNode->mName.Set("<dummy_root>");
|
||||||
|
|
||||||
|
if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras)
|
||||||
|
{
|
||||||
|
pScene->mRootNode->mNumMeshes = 1;
|
||||||
|
pScene->mRootNode->mMeshes = new unsigned int[1];
|
||||||
|
pScene->mRootNode->mMeshes[0] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras;
|
||||||
|
aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
|
||||||
|
|
||||||
|
// generate mesh nodes
|
||||||
|
for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes)
|
||||||
|
{
|
||||||
|
aiNode* pcNode = *nodes = new aiNode();
|
||||||
|
pcNode->mParent = pScene->mRootNode;
|
||||||
|
pcNode->mName.length = ::sprintf(pcNode->mName.data,"mesh_%i",i);
|
||||||
|
|
||||||
|
// setup mesh indices
|
||||||
|
pcNode->mNumMeshes = 1;
|
||||||
|
pcNode->mMeshes = new unsigned int[1];
|
||||||
|
pcNode->mMeshes[0] = i;
|
||||||
|
}
|
||||||
|
// generate light nodes
|
||||||
|
for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes)
|
||||||
|
{
|
||||||
|
aiNode* pcNode = *nodes = new aiNode();
|
||||||
|
pcNode->mParent = pScene->mRootNode;
|
||||||
|
pcNode->mName.length = ::sprintf(pcNode->mName.data,"light_%i",i);
|
||||||
|
pScene->mLights[i]->mName = pcNode->mName;
|
||||||
|
}
|
||||||
|
// generate camera nodes
|
||||||
|
for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes)
|
||||||
|
{
|
||||||
|
aiNode* pcNode = *nodes = new aiNode();
|
||||||
|
pcNode->mParent = pScene->mRootNode;
|
||||||
|
pcNode->mName.length = ::sprintf(pcNode->mName.data,"cam_%i",i);
|
||||||
|
pScene->mCameras[i]->mName = pcNode->mName;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
// ... and finally set the transformation matrix of all nodes to identity
|
||||||
pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras;
|
MakeIdentityTransform(pScene->mRootNode);
|
||||||
aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
|
|
||||||
|
|
||||||
// generate mesh nodes
|
|
||||||
for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes)
|
|
||||||
{
|
|
||||||
aiNode* pcNode = *nodes = new aiNode();
|
|
||||||
pcNode->mParent = pScene->mRootNode;
|
|
||||||
pcNode->mName.length = ::sprintf(pcNode->mName.data,"mesh_%i",i);
|
|
||||||
|
|
||||||
// setup mesh indices
|
|
||||||
pcNode->mNumMeshes = 1;
|
|
||||||
pcNode->mMeshes = new unsigned int[1];
|
|
||||||
pcNode->mMeshes[0] = i;
|
|
||||||
}
|
|
||||||
// generate light nodes
|
|
||||||
for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes)
|
|
||||||
{
|
|
||||||
aiNode* pcNode = *nodes = new aiNode();
|
|
||||||
pcNode->mParent = pScene->mRootNode;
|
|
||||||
pcNode->mName.length = ::sprintf(pcNode->mName.data,"light_%i",i);
|
|
||||||
pScene->mLights[i]->mName = pcNode->mName;
|
|
||||||
}
|
|
||||||
// generate camera nodes
|
|
||||||
for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes)
|
|
||||||
{
|
|
||||||
aiNode* pcNode = *nodes = new aiNode();
|
|
||||||
pcNode->mParent = pScene->mRootNode;
|
|
||||||
pcNode->mName.length = ::sprintf(pcNode->mName.data,"cam_%i",i);
|
|
||||||
pScene->mCameras[i]->mName = pcNode->mName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// print statistics
|
// print statistics
|
||||||
|
@ -467,7 +680,5 @@ void PretransformVertices::Execute( aiScene* pScene)
|
||||||
iOldMeshes,pScene->mNumMeshes);
|
iOldMeshes,pScene->mNumMeshes);
|
||||||
DefaultLogger::get()->info(buffer);
|
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
|
/** @file PretransformVertices.h
|
||||||
vertices in the scenegraph */
|
* @brief Defines a post processing step to pretransform all
|
||||||
|
* vertices in the scenegraph
|
||||||
|
*/
|
||||||
#ifndef AI_PRETRANSFORMVERTICES_H_INC
|
#ifndef AI_PRETRANSFORMVERTICES_H_INC
|
||||||
#define AI_PRETRANSFORMVERTICES_H_INC
|
#define AI_PRETRANSFORMVERTICES_H_INC
|
||||||
|
|
||||||
#include "BaseProcess.h"
|
#include "BaseProcess.h"
|
||||||
#include "../include/aiMesh.h"
|
#include "../include/aiMesh.h"
|
||||||
|
|
||||||
namespace Assimp
|
class PretransformVerticesTest;
|
||||||
{
|
namespace Assimp {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** The PretransformVertices pretransforms all vertices in the nodegraph
|
/** The PretransformVertices pretransforms all vertices in the nodegraph
|
||||||
|
@ -57,6 +59,7 @@ namespace Assimp
|
||||||
class ASSIMP_API PretransformVertices : public BaseProcess
|
class ASSIMP_API PretransformVertices : public BaseProcess
|
||||||
{
|
{
|
||||||
friend class Importer;
|
friend class Importer;
|
||||||
|
friend class ::PretransformVerticesTest;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** Constructor to be privately used by Importer */
|
/** Constructor to be privately used by Importer */
|
||||||
|
@ -66,21 +69,96 @@ protected:
|
||||||
~PretransformVertices ();
|
~PretransformVertices ();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the processing step is present in the given flag field.
|
// Check whether step is active
|
||||||
* @param pFlags The processing flags the importer was called with. A bitwise
|
|
||||||
* combination of #aiPostProcessSteps.
|
|
||||||
* @return true if the process is present in this flag fields, false if not.
|
|
||||||
*/
|
|
||||||
bool IsActive( unsigned int pFlags) const;
|
bool IsActive( unsigned int pFlags) const;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Executes the post processing step on the given imported data.
|
// Execute step on a given scene
|
||||||
* At the moment a process is not supposed to fail.
|
|
||||||
* @param pScene The imported data to work at.
|
|
||||||
*/
|
|
||||||
void Execute( aiScene* pScene);
|
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
|
} // end of namespace Assimp
|
||||||
|
|
|
@ -46,11 +46,144 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "SpatialSort.h"
|
#include "SpatialSort.h"
|
||||||
#include "BaseProcess.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 {
|
namespace Assimp {
|
||||||
|
|
||||||
// some aliases to make the whole stuff easier to read
|
// -------------------------------------------------------------------------------
|
||||||
typedef std::pair < unsigned int,float > PerVertexWeight;
|
// Start points for ArrayBounds<T> for all supported Ts
|
||||||
typedef std::vector < PerVertexWeight > VertexWeightTable;
|
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
|
/** 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;
|
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
|
// Compute the AABB of a mesh after applying a given transform
|
||||||
inline void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max,
|
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)
|
for (unsigned int i = 0;i < mesh->mNumVertices;++i)
|
||||||
{
|
{
|
||||||
const aiVector3D v = m * mesh->mVertices[i];
|
const aiVector3D v = m * mesh->mVertices[i];
|
||||||
min.x = ::std::min(v.x,min.x);
|
min = std::min(v,min);
|
||||||
min.y = ::std::min(v.y,min.y);
|
max = std::max(v,max);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +219,7 @@ inline void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D
|
||||||
// Helper function to determine the 'real' center of a mesh
|
// Helper function to determine the 'real' center of a mesh
|
||||||
inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max)
|
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;
|
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
|
// calculate the position bounds so we have a reliable epsilon to check position differences against
|
||||||
aiVector3D minVec, maxVec;
|
aiVector3D minVec, maxVec;
|
||||||
FindAABB(pMesh,minVec,maxVec);
|
ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec);
|
||||||
return (maxVec - minVec).Length() * epsilon;
|
return (maxVec - minVec).Length() * epsilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,8 +276,10 @@ inline unsigned int GetMeshVFormatUnique(aiMesh* pcMesh)
|
||||||
// tangents and bitangents
|
// tangents and bitangents
|
||||||
if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4;
|
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_COLOR_SETS);
|
||||||
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
|
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
|
||||||
|
#endif
|
||||||
|
|
||||||
// texture coordinates
|
// texture coordinates
|
||||||
unsigned int p = 0;
|
unsigned int p = 0;
|
||||||
|
@ -184,6 +297,9 @@ inline unsigned int GetMeshVFormatUnique(aiMesh* pcMesh)
|
||||||
return iRet;
|
return iRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef std::pair <unsigned int,float> PerVertexWeight;
|
||||||
|
typedef std::vector <PerVertexWeight> VertexWeightTable;
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
// Compute a per-vertex bone weight table
|
// Compute a per-vertex bone weight table
|
||||||
// please .... delete result with operator delete[] ...
|
// please .... delete result with operator delete[] ...
|
||||||
|
@ -205,13 +321,14 @@ inline VertexWeightTable* ComputeVertexBoneWeightTable(aiMesh* pMesh)
|
||||||
return avPerVertexWeights;
|
return avPerVertexWeights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
// Get a string for a given aiTextureType
|
// Get a string for a given aiTextureType
|
||||||
inline const char* TextureTypeToString(aiTextureType in)
|
inline const char* TextureTypeToString(aiTextureType in)
|
||||||
{
|
{
|
||||||
switch (in)
|
switch (in)
|
||||||
{
|
{
|
||||||
|
case aiTextureType_NONE:
|
||||||
|
return "n/a";
|
||||||
case aiTextureType_DIFFUSE:
|
case aiTextureType_DIFFUSE:
|
||||||
return "Diffuse";
|
return "Diffuse";
|
||||||
case aiTextureType_SPECULAR:
|
case aiTextureType_SPECULAR:
|
||||||
|
@ -237,7 +354,7 @@ inline const char* TextureTypeToString(aiTextureType in)
|
||||||
case aiTextureType_UNKNOWN:
|
case aiTextureType_UNKNOWN:
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
default:
|
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:
|
case aiTextureMapping_OTHER:
|
||||||
return "Other";
|
return "Other";
|
||||||
default:
|
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"
|
#include "AssimpPCH.h"
|
||||||
#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
|
||||||
|
@ -54,30 +56,34 @@ using namespace Assimp;
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Constructor to be privately used by Importer
|
// Constructor to be privately used by Importer
|
||||||
Q3DImporter::Q3DImporter()
|
Q3DImporter::Q3DImporter()
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Destructor, private as well
|
// Destructor, private as well
|
||||||
Q3DImporter::~Q3DImporter()
|
Q3DImporter::~Q3DImporter()
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
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);
|
if (extension == "q3s" || extension == "q3o")
|
||||||
for (std::string::iterator it = extension.begin();
|
return true;
|
||||||
it != extension.end(); ++it)
|
else if (!extension.length() || checkSig) {
|
||||||
*it = ::tolower(*it);
|
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)
|
if (stream.GetRemainingSize() < 22)
|
||||||
throw new ImportErrorException("File is either empty or corrupt: " + pFile);
|
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 ) &&
|
if (ASSIMP_strincmp( (const char*)stream.GetPtr(), "quick3Do", 8 ) &&
|
||||||
ASSIMP_strincmp( (const char*)stream.GetPtr(), "quick3Ds", 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));
|
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
|
#ifndef AI_Q3DLOADER_H_INCLUDED
|
||||||
#define 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.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -75,10 +78,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.q3o;*.q3s");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** 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"
|
#include "AssimpPCH.h"
|
||||||
#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
|
||||||
|
@ -63,21 +65,16 @@ RAWImporter::~RAWImporter()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
return SimpleExtensionCheck(pFile,"raw");
|
||||||
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;
|
// Get the list of all supported file extensions
|
||||||
|
void RAWImporter::GetExtensionList(std::string& append)
|
||||||
return !(extension.length() != 4 || extension[0] != '.' ||
|
{
|
||||||
extension[1] != 'r' && extension[1] != 'R' ||
|
append.append("*.raw");
|
||||||
extension[2] != 'a' && extension[2] != 'A' ||
|
|
||||||
extension[3] != 'w' && extension[3] != 'W');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -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
|
#ifndef AI_RAWLOADER_H_INCLUDED
|
||||||
#define 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.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -75,10 +79,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.raw");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** 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.
|
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
|
// internal headers
|
||||||
#include "AssimpPCH.h"
|
#include "AssimpPCH.h"
|
||||||
#include "RemoveRedundantMaterials.h"
|
#include "RemoveRedundantMaterials.h"
|
||||||
|
#include "ParsingUtils.h"
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
|
@ -68,6 +70,39 @@ bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const
|
||||||
return (pFlags & aiProcess_RemoveRedundantMaterials) != 0;
|
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.
|
// Executes the post processing step on the given imported data.
|
||||||
void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
|
void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
|
||||||
|
@ -77,16 +112,49 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
|
||||||
unsigned int iCnt = 0, unreferenced = 0;
|
unsigned int iCnt = 0, unreferenced = 0;
|
||||||
if (pScene->mNumMaterials)
|
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
|
// TODO: reimplement this algorithm to work in-place
|
||||||
|
|
||||||
unsigned int* aiMappingTable = new unsigned int[pScene->mNumMaterials];
|
unsigned int* aiMappingTable = new unsigned int[pScene->mNumMaterials];
|
||||||
unsigned int iNewNum = 0;
|
unsigned int iNewNum = 0;
|
||||||
|
|
||||||
std::vector<bool> abReferenced(pScene->mNumMaterials,false);
|
// Iterate through all materials and calculate a hash for them
|
||||||
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
|
|
||||||
// store all hashes in a list and so a quick search whether
|
// store all hashes in a list and so a quick search whether
|
||||||
// we do already have a specific hash. This allows us to
|
// we do already have a specific hash. This allows us to
|
||||||
// determine which materials are identical.
|
// determine which materials are identical.
|
||||||
|
@ -136,8 +204,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
|
||||||
else ppcMaterials[idx] = pScene->mMaterials[p];
|
else ppcMaterials[idx] = pScene->mMaterials[p];
|
||||||
}
|
}
|
||||||
// update all material indices
|
// 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];
|
aiMesh* mesh = pScene->mMeshes[p];
|
||||||
mesh->mMaterialIndex = aiMappingTable[mesh->mMaterialIndex];
|
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
|
#ifndef AI_REMOVEREDUNDANTMATERIALS_H_INC
|
||||||
#define AI_REMOVEREDUNDANTMATERIALS_H_INC
|
#define AI_REMOVEREDUNDANTMATERIALS_H_INC
|
||||||
|
|
||||||
|
@ -46,12 +48,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "../include/aiMesh.h"
|
#include "../include/aiMesh.h"
|
||||||
|
|
||||||
class RemoveRedundantMatsTest;
|
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
|
class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess
|
||||||
{
|
{
|
||||||
friend class Importer;
|
friend class Importer;
|
||||||
|
@ -66,19 +68,38 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the processing step is present in the given flag field.
|
// Check whether step is active
|
||||||
* @param pFlags The processing flags the importer was called with. A bitwise
|
|
||||||
* combination of #aiPostProcessSteps.
|
|
||||||
* @return true if the process is present in this flag fields, false if not.
|
|
||||||
*/
|
|
||||||
bool IsActive( unsigned int pFlags) const;
|
bool IsActive( unsigned int pFlags) const;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Executes the post processing step on the given imported data.
|
// Execute step on a given scene
|
||||||
* At the moment a process is not supposed to fail.
|
|
||||||
* @param pScene The imported data to work at.
|
|
||||||
*/
|
|
||||||
void Execute( aiScene* pScene);
|
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
|
} // end of namespace Assimp
|
||||||
|
|
|
@ -51,15 +51,12 @@ using namespace Assimp;
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Constructor to be privately used by Importer
|
// Constructor to be privately used by Importer
|
||||||
RemoveVCProcess::RemoveVCProcess()
|
RemoveVCProcess::RemoveVCProcess()
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Destructor, private as well
|
// Destructor, private as well
|
||||||
RemoveVCProcess::~RemoveVCProcess()
|
RemoveVCProcess::~RemoveVCProcess()
|
||||||
{
|
{}
|
||||||
// nothing to do here
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the processing step is present in the given flag field.
|
// Returns whether the processing step is present in the given flag field.
|
||||||
|
|
|
@ -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"
|
#include "AssimpPCH.h"
|
||||||
#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
|
||||||
|
@ -54,56 +56,37 @@ using namespace Assimp;
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Constructor to be privately used by Importer
|
// Constructor to be privately used by Importer
|
||||||
SMDImporter::SMDImporter()
|
SMDImporter::SMDImporter()
|
||||||
{
|
{}
|
||||||
// nothing to do here
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Destructor, private as well
|
// Destructor, private as well
|
||||||
SMDImporter::~SMDImporter()
|
SMDImporter::~SMDImporter()
|
||||||
{
|
{}
|
||||||
// nothing to do here
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
// fixme: auto format detection
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
return SimpleExtensionCheck(pFile,"smd","vta");
|
||||||
// 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;
|
// Get a list of all supported file extensions
|
||||||
|
void SMDImporter::GetExtensionList(std::string& append)
|
||||||
// VTA is not really supported as it contains vertex animations.
|
{
|
||||||
// However, at least the first keyframe can be loaded
|
append.append("*.smd;*.vta");
|
||||||
if ((extension[1] != 's' && extension[1] != 'S') ||
|
|
||||||
(extension[2] != 'm' && extension[2] != 'M') ||
|
|
||||||
(extension[3] != 'd' && extension[3] != 'D'))
|
|
||||||
{
|
|
||||||
if ((extension[1] != 'v' && extension[1] != 'V') ||
|
|
||||||
(extension[2] != 't' && extension[2] != 'T') ||
|
|
||||||
(extension[3] != 'a' && extension[3] != 'A'))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Setup configuration properties
|
// Setup configuration properties
|
||||||
void SMDImporter::SetupProperties(const Importer* pImp)
|
void SMDImporter::SetupProperties(const Importer* pImp)
|
||||||
{
|
{
|
||||||
// The AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the
|
// The
|
||||||
|
// AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the
|
||||||
// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
|
// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
|
||||||
if(0xffffffff == (configFrameID = pImp->GetPropertyInteger(
|
configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_SMD_KEYFRAME,0xffffffff);
|
||||||
AI_CONFIG_IMPORT_SMD_KEYFRAME,0xffffffff)))
|
if(0xffffffff == configFrameID) {
|
||||||
{
|
|
||||||
configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
|
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 SMDLoader.h
|
||||||
//!
|
* @brief Defintion of the Valve SMD file format
|
||||||
//! @file Definition of SMD importer class
|
*/
|
||||||
//!
|
|
||||||
|
|
||||||
#ifndef AI_SMDLOADER_H_INCLUDED
|
#ifndef AI_SMDLOADER_H_INCLUDED
|
||||||
#define AI_SMDLOADER_H_INCLUDED
|
#define AI_SMDLOADER_H_INCLUDED
|
||||||
|
@ -186,14 +185,16 @@ public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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().
|
/** Called prior to ReadFile().
|
||||||
* The function is a request to the importer to update its configuration
|
* The function is a request to the importer to update its configuration
|
||||||
* basing on the Importer's configuration property list.
|
* basing on the Importer's configuration property list.
|
||||||
*/
|
*/
|
||||||
void SetupProperties(const Importer* pImp);
|
void SetupProperties(const Importer* pImp);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -203,10 +204,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.smd;*.vta");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** Imports the given file into the given scene structure.
|
||||||
|
|
|
@ -64,27 +64,31 @@ STLImporter::~STLImporter()
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the class can handle the format of the given file.
|
// 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
|
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);
|
|
||||||
|
|
||||||
if (extension.length() < 4)return false;
|
if (extension == "stl")
|
||||||
if (extension[0] != '.')return false;
|
return true;
|
||||||
|
else if (!extension.length() || checkSig) {
|
||||||
if (extension[1] != 's' && extension[1] != 'S')return false;
|
if (!pIOHandler)
|
||||||
if (extension[2] != 't' && extension[2] != 'T')return false;
|
return true;
|
||||||
if (extension[3] != 'l' && extension[3] != 'L')return false;
|
const char* tokens[] = {"STL","solid"};
|
||||||
|
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,2);
|
||||||
return true;
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void STLImporter::GetExtensionList(std::string& append)
|
||||||
|
{
|
||||||
|
append.append("*.stl");
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Imports the given file into the given scene structure.
|
// Imports the given file into the given scene structure.
|
||||||
void STLImporter::InternReadFile(
|
void STLImporter::InternReadFile(
|
||||||
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
|
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
|
||||||
{
|
{
|
||||||
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
||||||
|
|
||||||
|
@ -183,7 +187,7 @@ void STLImporter::LoadASCIIFile()
|
||||||
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
|
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
|
||||||
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
|
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
|
||||||
|
|
||||||
unsigned int curFace = 0, curVertex = 0;
|
unsigned int curFace = 0, curVertex = 3;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
// go to the next token
|
// 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
|
#ifndef AI_STLLOADER_H_INCLUDED
|
||||||
#define 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 {
|
namespace Assimp {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Clas to load STL files
|
/** Importer class for the sterolithography STL file format
|
||||||
*/
|
*/
|
||||||
class STLImporter : public BaseImporter
|
class STLImporter : public BaseImporter
|
||||||
{
|
{
|
||||||
|
@ -65,8 +67,10 @@ public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* 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:
|
protected:
|
||||||
|
|
||||||
|
@ -74,10 +78,7 @@ protected:
|
||||||
/** Called by Importer::GetExtensionList() for each loaded importer.
|
/** Called by Importer::GetExtensionList() for each loaded importer.
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append)
|
void GetExtensionList(std::string& append);
|
||||||
{
|
|
||||||
append.append("*.stl");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** 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 {
|
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
|
// Add a prefix to a string
|
||||||
inline void PrefixString(aiString& string,const char* prefix, unsigned int len)
|
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;
|
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
|
// Add a name prefix to all nodes in a hierarchy
|
||||||
void SceneCombiner::AddNodePrefixes(aiNode* node, const char* prefix, unsigned int len)
|
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);
|
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
|
// Add an offset to all mesh indices in a node graph
|
||||||
void SceneCombiner::OffsetNodeMeshIndices (aiNode* node, unsigned int offset)
|
void SceneCombiner::OffsetNodeMeshIndices (aiNode* node, unsigned int offset)
|
||||||
|
@ -300,6 +319,20 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
|
||||||
|
|
||||||
const unsigned int random = rndGen();
|
const unsigned int random = rndGen();
|
||||||
src[i].idlen = ::sprintf(src[i].id,"$%.6X$_",random);
|
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;
|
dest->mNumAnimations += (*cur)->mNumAnimations;
|
||||||
|
|
||||||
// Combine the flags of all scenes
|
// 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
|
// 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];
|
aiMaterial** pip = dest->mMaterials = new aiMaterial*[dest->mNumMaterials];
|
||||||
cnt = 0;
|
cnt = 0;
|
||||||
for ( unsigned int n = 0; n < src.size();++n )
|
for ( unsigned int n = 0; n < src.size();++n ) {
|
||||||
{
|
|
||||||
SceneHelper* cur = &src[n];
|
SceneHelper* cur = &src[n];
|
||||||
for (unsigned int i = 0; i < (*cur)->mNumMaterials;++i)
|
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];
|
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
|
// We need to update all texture indices of the mesh. So we need to search for
|
||||||
// a material property called '$tex.file'
|
// a material property called '$tex.file'
|
||||||
|
|
||||||
|
@ -391,8 +426,7 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to generate new, unique material names?
|
// Need to generate new, unique material names?
|
||||||
else if (!::strcmp( prop->mKey.data,"$mat.name" ) &&
|
else if (!::strcmp( prop->mKey.data,"$mat.name" ) && flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES)
|
||||||
flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES)
|
|
||||||
{
|
{
|
||||||
aiString* pcSrc = (aiString*) prop->mData;
|
aiString* pcSrc = (aiString*) prop->mData;
|
||||||
PrefixString(*pcSrc, (*cur).id, (*cur).idlen);
|
PrefixString(*pcSrc, (*cur).id, (*cur).idlen);
|
||||||
|
@ -458,7 +492,7 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
|
||||||
aiAnimation** ppAnims = dest->mAnimations = (dest->mNumAnimations
|
aiAnimation** ppAnims = dest->mAnimations = (dest->mNumAnimations
|
||||||
? new aiAnimation*[dest->mNumAnimations] : NULL);
|
? 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];
|
SceneHelper* cur = &src[n];
|
||||||
aiNode* node;
|
aiNode* node;
|
||||||
|
@ -466,7 +500,9 @@ void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
|
||||||
// To offset or not to offset, this is the question
|
// To offset or not to offset, this is the question
|
||||||
if (n != duplicates[n])
|
if (n != duplicates[n])
|
||||||
{
|
{
|
||||||
|
// Get full scenegraph copy
|
||||||
Copy( &node, (*cur)->mRootNode );
|
Copy( &node, (*cur)->mRootNode );
|
||||||
|
OffsetNodeMeshIndices(node,offset[duplicates[n]]);
|
||||||
|
|
||||||
if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
|
if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
|
||||||
// (note:) they are already 'offseted' by offset[duplicates[n]]
|
// (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
|
if (n) // src[0] is the master node
|
||||||
nodes.push_back(NodeAttachmentInfo( node,srcList[n-1].attachToNode,n ));
|
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
|
// Copy light sources
|
||||||
for (unsigned int i = 0; i < (*cur)->mNumLights;++i,++ppLights)
|
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]);
|
Copy(ppLights, (*cur)->mLights[i]);
|
||||||
}
|
}
|
||||||
else *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]);
|
Copy(ppCameras, (*cur)->mCameras[i]);
|
||||||
}
|
}
|
||||||
else *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]);
|
Copy(ppAnims, (*cur)->mAnimations[i]);
|
||||||
}
|
}
|
||||||
else *ppAnims = (*cur)->mAnimations[i];
|
else *ppAnims = (*cur)->mAnimations[i];
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( unsigned int n = 1; n < src.size();++n ) {
|
// Add name prefixes?
|
||||||
SceneHelper* cur = &src[n];
|
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
|
||||||
// --------------------------------------------------------------------
|
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
|
||||||
// Add prefixes
|
if (!FindNameMatch((*ppAnims)->mName,src,n))
|
||||||
if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES)
|
continue;
|
||||||
{
|
}
|
||||||
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((*ppAnims)->mName,(*cur).id,(*cur).idlen);
|
||||||
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);
|
|
||||||
|
|
||||||
// don't forget to update all node animation channels
|
// don't forget to update all node animation channels
|
||||||
for (unsigned int a = 0; a < anim->mNumChannels;++a)
|
for (unsigned int a = 0; a < (*ppAnims)->mNumChannels;++a) {
|
||||||
PrefixString(anim->mChannels[a]->mNodeName,(*cur).id,(*cur).idlen);
|
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;
|
delete deleteMe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check flags
|
||||||
|
if (!dest->mNumMeshes || !dest->mNumMaterials) {
|
||||||
|
dest->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
|
||||||
|
}
|
||||||
|
|
||||||
// We're finished
|
// 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 it2 = asBones.begin();
|
||||||
std::list<BoneWithHash>::iterator end2 = asBones.end();
|
std::list<BoneWithHash>::iterator end2 = asBones.end();
|
||||||
|
|
||||||
for (;it2 != end2;++it2)
|
for (;it2 != end2;++it2) {
|
||||||
{
|
if ((*it2).first == itml) {
|
||||||
if ((*it2).first == itml)
|
|
||||||
{
|
|
||||||
(*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset));
|
(*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -666,10 +746,7 @@ void SceneCombiner::MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator
|
||||||
if (wmit != (*it).pSrcBones.begin() &&
|
if (wmit != (*it).pSrcBones.begin() &&
|
||||||
pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix)
|
pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix)
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->warn("Bones with equal names but different "
|
DefaultLogger::get()->warn("Bones with equal names but different offset matrices can't be joined at the moment");
|
||||||
"offset matrices can't be joined at the moment. If this causes "
|
|
||||||
"problems, deactivate the OptimizeGraph-Step");
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix;
|
pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix;
|
||||||
|
@ -1026,7 +1103,7 @@ void SceneCombiner::Copy (aiAnimation** _dest, const aiAnimation* src)
|
||||||
::memcpy(dest,src,sizeof(aiAnimation));
|
::memcpy(dest,src,sizeof(aiAnimation));
|
||||||
|
|
||||||
// and reallocate all arrays
|
// 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
|
/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY
|
||||||
* Can be combined with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES.
|
* Can be combined with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES.
|
||||||
* Unique names are generated, but only if this is absolutely
|
* 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
|
#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
|
/** \brief Static helper class providing various utilities to merge two
|
||||||
* scenes. It is intended as internal utility and NOT for use by
|
* scenes. It is intended as internal utility and NOT for use by
|
||||||
|
@ -301,6 +338,26 @@ public:
|
||||||
|
|
||||||
// recursive, of course
|
// recursive, of course
|
||||||
static void Copy (aiNode** dest, const aiNode* src);
|
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,26 +64,56 @@ void ScenePreprocessor::ProcessScene ()
|
||||||
// Generate a default material if none was specified
|
// Generate a default material if none was specified
|
||||||
if (!scene->mNumMaterials && scene->mNumMeshes)
|
if (!scene->mNumMaterials && scene->mNumMeshes)
|
||||||
{
|
{
|
||||||
scene->mMaterials = new aiMaterial*[scene->mNumMaterials = 1];
|
scene->mMaterials = new aiMaterial*[2];
|
||||||
MaterialHelper* helper = new MaterialHelper();
|
MaterialHelper* helper;
|
||||||
scene->mMaterials[0] = helper;
|
|
||||||
|
|
||||||
// gray
|
aiString name;
|
||||||
aiColor3D clr(0.6f,0.6f,0.6f);
|
|
||||||
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
|
|
||||||
|
|
||||||
// add a small ambient color value
|
// Check whether there are meshes with at least one set of uv coordinates ... add a dummy texture for them
|
||||||
clr = aiColor3D(0.05f,0.05f,0.05f);
|
// meshes without texture coordinates receive a boring gray default material.
|
||||||
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
|
unsigned int mat0 = 0xffffffff, mat1 = 0xffffffff;
|
||||||
|
for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
|
||||||
|
if (scene->mMeshes[i]->mTextureCoords[0]) {
|
||||||
|
|
||||||
// setup the default name
|
if (mat0 == 0xffffffff) {
|
||||||
aiString name(AI_DEFAULT_MATERIAL_NAME);
|
scene->mMaterials[scene->mNumMaterials] = helper = new MaterialHelper();
|
||||||
helper->AddProperty(&name,AI_MATKEY_NAME);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < scene->mNumMeshes;++i)
|
// dummy texture
|
||||||
scene->mMeshes[i]->mMaterialIndex = 0;
|
name.Set("texture.png");
|
||||||
|
helper->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(0));
|
||||||
|
|
||||||
DefaultLogger::get()->debug("ScenePreprocessor: Added default material \'" AI_DEFAULT_MATERIAL_NAME "\'");
|
// setup default name
|
||||||
|
name.Set(AI_DEFAULT_TEXTURED_MATERIAL_NAME);
|
||||||
|
helper->AddProperty(&name,AI_MATKEY_NAME);
|
||||||
|
|
||||||
|
mat0 = scene->mNumMaterials++;
|
||||||
|
DefaultLogger::get()->debug("ScenePreprocessor: Adding textured material \'" AI_DEFAULT_TEXTURED_MATERIAL_NAME "\'");
|
||||||
|
}
|
||||||
|
scene->mMeshes[i]->mMaterialIndex = mat0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (mat1 == 0xffffffff) {
|
||||||
|
scene->mMaterials[scene->mNumMaterials] = helper = new MaterialHelper();
|
||||||
|
|
||||||
|
// gray
|
||||||
|
aiColor3D clr(0.6f,0.6f,0.6f);
|
||||||
|
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
|
||||||
|
|
||||||
|
// add a small ambient color value
|
||||||
|
clr = aiColor3D(0.05f,0.05f,0.05f);
|
||||||
|
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
|
||||||
|
|
||||||
|
// setup the default name
|
||||||
|
name.Set(AI_DEFAULT_MATERIAL_NAME);
|
||||||
|
helper->AddProperty(&name,AI_MATKEY_NAME);
|
||||||
|
|
||||||
|
mat1 = scene->mNumMaterials++;
|
||||||
|
DefaultLogger::get()->debug("ScenePreprocessor: Adding grey material \'" AI_DEFAULT_MATERIAL_NAME "\'");
|
||||||
|
}
|
||||||
|
scene->mMeshes[i]->mMaterialIndex = mat1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,8 +126,22 @@ void ScenePreprocessor::ProcessMesh (aiMesh* mesh)
|
||||||
if (!mesh->mTextureCoords[i])
|
if (!mesh->mTextureCoords[i])
|
||||||
mesh->mNumUVComponents[i] = 0;
|
mesh->mNumUVComponents[i] = 0;
|
||||||
|
|
||||||
else if( !mesh->mNumUVComponents[i])
|
else {
|
||||||
mesh->mNumUVComponents[i] = 2;
|
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
|
// 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 + 6, localVertexStart + 7, localVertexStart + 8));
|
||||||
mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11));
|
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
|
// 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);
|
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( -sizeEstimate, 0.0f, 0.0f));
|
||||||
mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
|
mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
|
||||||
|
|
|
@ -303,7 +303,7 @@ void SortByPTypeProcess::Execute( aiScene* pScene)
|
||||||
if (vert)
|
if (vert)
|
||||||
{
|
{
|
||||||
*vert++ = mesh->mVertices[idx];
|
*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 (nor )*nor++ = mesh->mNormals[idx];
|
||||||
if (tan )
|
if (tan )
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue