Merge branch 'master' into fixTexcoord

pull/2829/head
Malcolm Tyrrell 2019-12-12 12:19:02 +00:00 committed by GitHub
commit b6553b8a78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 2405 additions and 1985 deletions

View File

@ -253,7 +253,7 @@ ELSEIF(MSVC)
IF(MSVC12) IF(MSVC12)
ADD_COMPILE_OPTIONS(/wd4351) ADD_COMPILE_OPTIONS(/wd4351)
ENDIF() ENDIF()
SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob2 /Zi /O0") SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Zi /Od")
ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
IF(NOT HUNTER_ENABLED) IF(NOT HUNTER_ENABLED)
SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}") SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}")

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team Copyright (c) 2006-2019, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -78,7 +76,6 @@ static const aiImporterDesc desc = {
"b3d" "b3d"
}; };
// (fixme, Aramis) quick workaround to get rid of all those signed to unsigned warnings
#ifdef _MSC_VER #ifdef _MSC_VER
# pragma warning (disable: 4018) # pragma warning (disable: 4018)
#endif #endif
@ -86,10 +83,8 @@ static const aiImporterDesc desc = {
//#define DEBUG_B3D //#define DEBUG_B3D
template<typename T> template<typename T>
void DeleteAllBarePointers(std::vector<T>& x) void DeleteAllBarePointers(std::vector<T>& x) {
{ for(auto p : x) {
for(auto p : x)
{
delete p; delete p;
} }
} }
@ -102,10 +97,14 @@ B3DImporter::~B3DImporter()
bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const{ bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const{
size_t pos=pFile.find_last_of( '.' ); size_t pos=pFile.find_last_of( '.' );
if( pos==string::npos ) return false; if( pos==string::npos ) {
return false;
}
string ext=pFile.substr( pos+1 ); string ext=pFile.substr( pos+1 );
if( ext.size()!=3 ) return false; if( ext.size()!=3 ) {
return false;
}
return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D'); return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D');
} }
@ -117,30 +116,21 @@ const aiImporterDesc* B3DImporter::GetInfo () const
return &desc; return &desc;
} }
#ifdef DEBUG_B3D
extern "C"{ void _stdcall AllocConsole(); }
#endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){ void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){
#ifdef DEBUG_B3D
AllocConsole();
freopen( "conin$","r",stdin );
freopen( "conout$","w",stdout );
freopen( "conout$","w",stderr );
cout<<"Hello world from the B3DImporter!"<<endl;
#endif
std::unique_ptr<IOStream> file( pIOHandler->Open( pFile)); std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
// Check whether we can read from the file // Check whether we can read from the file
if( file.get() == NULL) if( file.get() == nullptr) {
throw DeadlyImportError( "Failed to open B3D file " + pFile + "."); throw DeadlyImportError( "Failed to open B3D file " + pFile + ".");
}
// check whether the .b3d file is large enough to contain // check whether the .b3d file is large enough to contain
// at least one chunk. // at least one chunk.
size_t fileSize = file->FileSize(); size_t fileSize = file->FileSize();
if( fileSize<8 ) throw DeadlyImportError( "B3D File is too small."); if( fileSize<8 ) {
throw DeadlyImportError( "B3D File is too small.");
}
_pos=0; _pos=0;
_buf.resize( fileSize ); _buf.resize( fileSize );
@ -158,14 +148,17 @@ AI_WONT_RETURN void B3DImporter::Oops(){
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
AI_WONT_RETURN void B3DImporter::Fail( string str ){ AI_WONT_RETURN void B3DImporter::Fail( string str ){
#ifdef DEBUG_B3D #ifdef DEBUG_B3D
cout<<"Error in B3D file data: "<<str<<endl; ASSIMP_LOG_ERROR_F("Error in B3D file data: ", str);
#endif #endif
throw DeadlyImportError( "B3D Importer - error in B3D file data: "+str ); throw DeadlyImportError( "B3D Importer - error in B3D file data: "+str );
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
int B3DImporter::ReadByte(){ int B3DImporter::ReadByte(){
if( _pos<_buf.size() ) return _buf[_pos++]; if( _pos<_buf.size() ) {
return _buf[_pos++];
}
Fail( "EOF" ); Fail( "EOF" );
return 0; return 0;
} }
@ -224,7 +217,9 @@ string B3DImporter::ReadString(){
string str; string str;
while( _pos<_buf.size() ){ while( _pos<_buf.size() ){
char c=(char)ReadByte(); char c=(char)ReadByte();
if( !c ) return str; if( !c ) {
return str;
}
str+=c; str+=c;
} }
Fail( "EOF" ); Fail( "EOF" );
@ -238,7 +233,7 @@ string B3DImporter::ReadChunk(){
tag+=char( ReadByte() ); tag+=char( ReadByte() );
} }
#ifdef DEBUG_B3D #ifdef DEBUG_B3D
// cout<<"ReadChunk:"<<tag<<endl; ASSIMP_LOG_DEBUG_F("ReadChunk: ", tag);
#endif #endif
unsigned sz=(unsigned)ReadInt(); unsigned sz=(unsigned)ReadInt();
_stack.push_back( _pos+sz ); _stack.push_back( _pos+sz );
@ -269,7 +264,6 @@ T *B3DImporter::to_array( const vector<T> &v ){
return p; return p;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
template<class T> template<class T>
T **unique_to_array( vector<std::unique_ptr<T> > &v ){ T **unique_to_array( vector<std::unique_ptr<T> > &v ){
@ -283,7 +277,6 @@ T **unique_to_array( vector<std::unique_ptr<T> > &v ){
return p; return p;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void B3DImporter::ReadTEXS(){ void B3DImporter::ReadTEXS(){
while( ChunkSize() ){ while( ChunkSize() ){
@ -376,9 +369,13 @@ void B3DImporter::ReadVRTS(){
v.vertex=ReadVec3(); v.vertex=ReadVec3();
if( _vflags & 1 ) v.normal=ReadVec3(); if( _vflags & 1 ) {
v.normal=ReadVec3();
}
if( _vflags & 2 ) ReadQuat(); //skip v 4bytes... if( _vflags & 2 ) {
ReadQuat(); //skip v 4bytes...
}
for( int i=0;i<_tcsets;++i ){ for( int i=0;i<_tcsets;++i ){
float t[4]={0,0,0,0}; float t[4]={0,0,0,0};
@ -386,53 +383,55 @@ void B3DImporter::ReadVRTS(){
t[j]=ReadFloat(); t[j]=ReadFloat();
} }
t[1]=1-t[1]; t[1]=1-t[1];
if( !i ) v.texcoords=aiVector3D( t[0],t[1],t[2] ); if( !i ) {
v.texcoords=aiVector3D( t[0],t[1],t[2] );
}
} }
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void B3DImporter::ReadTRIS( int v0 ){ void B3DImporter::ReadTRIS(int v0) {
int matid=ReadInt(); int matid = ReadInt();
if( matid==-1 ){ if (matid == -1) {
matid=0; matid = 0;
}else if( matid<0 || matid>=(int)_materials.size() ){ } else if (matid < 0 || matid >= (int)_materials.size()) {
#ifdef DEBUG_B3D #ifdef DEBUG_B3D
cout<<"material id="<<matid<<endl; ASSIMP_LOG_ERROR_F("material id=", matid);
#endif #endif
Fail( "Bad material id" ); Fail("Bad material id");
} }
std::unique_ptr<aiMesh> mesh(new aiMesh); std::unique_ptr<aiMesh> mesh(new aiMesh);
mesh->mMaterialIndex=matid; mesh->mMaterialIndex = matid;
mesh->mNumFaces=0; mesh->mNumFaces = 0;
mesh->mPrimitiveTypes=aiPrimitiveType_TRIANGLE; mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
int n_tris=ChunkSize()/12; int n_tris = ChunkSize() / 12;
aiFace *face=mesh->mFaces=new aiFace[n_tris]; aiFace *face = mesh->mFaces = new aiFace[n_tris];
for( int i=0;i<n_tris;++i ){ for (int i = 0; i < n_tris; ++i) {
int i0=ReadInt()+v0; int i0 = ReadInt() + v0;
int i1=ReadInt()+v0; int i1 = ReadInt() + v0;
int i2=ReadInt()+v0; int i2 = ReadInt() + v0;
if( i0<0 || i0>=(int)_vertices.size() || i1<0 || i1>=(int)_vertices.size() || i2<0 || i2>=(int)_vertices.size() ){ if (i0 < 0 || i0 >= (int)_vertices.size() || i1 < 0 || i1 >= (int)_vertices.size() || i2 < 0 || i2 >= (int)_vertices.size()) {
#ifdef DEBUG_B3D #ifdef DEBUG_B3D
cout<<"Bad triangle index: i0="<<i0<<", i1="<<i1<<", i2="<<i2<<endl; ASSIMP_LOG_ERROR_F("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2);
#endif #endif
Fail( "Bad triangle index" ); Fail("Bad triangle index");
continue; continue;
} }
face->mNumIndices=3; face->mNumIndices = 3;
face->mIndices=new unsigned[3]; face->mIndices = new unsigned[3];
face->mIndices[0]=i0; face->mIndices[0] = i0;
face->mIndices[1]=i1; face->mIndices[1] = i1;
face->mIndices[2]=i2; face->mIndices[2] = i2;
++mesh->mNumFaces; ++mesh->mNumFaces;
++face; ++face;
} }
_meshes.emplace_back( std::move(mesh) ); _meshes.emplace_back(std::move(mesh));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -453,28 +452,22 @@ void B3DImporter::ReadMESH(){
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void B3DImporter::ReadBONE( int id ){ void B3DImporter::ReadBONE(int id) {
while( ChunkSize() ){ while (ChunkSize()) {
int vertex=ReadInt(); int vertex = ReadInt();
float weight=ReadFloat(); float weight = ReadFloat();
if( vertex<0 || vertex>=(int)_vertices.size() ){ if (vertex < 0 || vertex >= (int)_vertices.size()) {
Fail( "Bad vertex index" ); Fail("Bad vertex index");
} }
Vertex &v=_vertices[vertex]; Vertex &v = _vertices[vertex];
int i; for (int i = 0; i < 4; ++i) {
for( i=0;i<4;++i ){ if (!v.weights[i]) {
if( !v.weights[i] ){ v.bones[i] = id;
v.bones[i]=id; v.weights[i] = weight;
v.weights[i]=weight;
break; break;
} }
} }
#ifdef DEBUG_B3D
if( i==4 ){
cout<<"Too many bone weights"<<endl;
}
#endif
} }
} }
@ -633,11 +626,15 @@ void B3DImporter::ReadBB3D( aiScene *scene ){
} }
ExitChunk(); ExitChunk();
if( !_nodes.size() ) Fail( "No nodes" ); if( !_nodes.size() ) {
Fail( "No nodes" );
}
if( !_meshes.size() ) Fail( "No meshes" ); if( !_meshes.size() ) {
Fail( "No meshes" );
}
//Fix nodes/meshes/bones // Fix nodes/meshes/bones
for(size_t i=0;i<_nodes.size();++i ){ for(size_t i=0;i<_nodes.size();++i ){
aiNode *node=_nodes[i]; aiNode *node=_nodes[i];
@ -648,8 +645,12 @@ void B3DImporter::ReadBB3D( aiScene *scene ){
int n_verts=mesh->mNumVertices=n_tris * 3; int n_verts=mesh->mNumVertices=n_tris * 3;
aiVector3D *mv=mesh->mVertices=new aiVector3D[ n_verts ],*mn=0,*mc=0; aiVector3D *mv=mesh->mVertices=new aiVector3D[ n_verts ],*mn=0,*mc=0;
if( _vflags & 1 ) mn=mesh->mNormals=new aiVector3D[ n_verts ]; if( _vflags & 1 ) {
if( _tcsets ) mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ]; mn=mesh->mNormals=new aiVector3D[ n_verts ];
}
if( _tcsets ) {
mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ];
}
aiFace *face=mesh->mFaces; aiFace *face=mesh->mFaces;

View File

@ -411,6 +411,8 @@ ADD_ASSIMP_IMPORTER( M3D
M3D/M3DMaterials.h M3D/M3DMaterials.h
M3D/M3DImporter.h M3D/M3DImporter.h
M3D/M3DImporter.cpp M3D/M3DImporter.cpp
M3D/M3DWrapper.h
M3D/M3DWrapper.cpp
M3D/m3d.h M3D/m3d.h
) )

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team Copyright (c) 2006-2019, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -78,6 +76,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/TinyFormatter.h> #include <assimp/TinyFormatter.h>
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
#include <assimp/Profiler.h> #include <assimp/Profiler.h>
#include <assimp/commonMetaData.h>
#include <set> #include <set>
#include <memory> #include <memory>
#include <cctype> #include <cctype>
@ -119,7 +119,7 @@ void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothro
return AllocateFromAssimpHeap::operator new( num_bytes ); return AllocateFromAssimpHeap::operator new( num_bytes );
} }
catch( ... ) { catch( ... ) {
return NULL; return nullptr;
} }
} }
@ -134,9 +134,8 @@ void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes) {
void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() { void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() {
try { try {
return AllocateFromAssimpHeap::operator new[]( num_bytes ); return AllocateFromAssimpHeap::operator new[]( num_bytes );
} } catch( ... ) {
catch( ... ) { return nullptr;
return NULL;
} }
} }
@ -148,7 +147,7 @@ void AllocateFromAssimpHeap::operator delete[] ( void* data) {
// Importer constructor. // Importer constructor.
Importer::Importer() Importer::Importer()
: pimpl( new ImporterPimpl ) { : pimpl( new ImporterPimpl ) {
pimpl->mScene = NULL; pimpl->mScene = nullptr;
pimpl->mErrorString = ""; pimpl->mErrorString = "";
// Allocate a default IO handler // Allocate a default IO handler
@ -174,14 +173,14 @@ Importer::Importer()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor of Importer // Destructor of Importer
Importer::~Importer() Importer::~Importer() {
{
// Delete all import plugins // Delete all import plugins
DeleteImporterInstanceList(pimpl->mImporter); DeleteImporterInstanceList(pimpl->mImporter);
// Delete all post-processing plug-ins // Delete all post-processing plug-ins
for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); ++a ) {
delete pimpl->mPostProcessingSteps[a]; delete pimpl->mPostProcessingSteps[a];
}
// Delete the assigned IO and progress handler // Delete the assigned IO and progress handler
delete pimpl->mIOHandler; delete pimpl->mIOHandler;
@ -199,9 +198,9 @@ Importer::~Importer()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Register a custom post-processing step // Register a custom post-processing step
aiReturn Importer::RegisterPPStep(BaseProcess* pImp) aiReturn Importer::RegisterPPStep(BaseProcess* pImp) {
{ ai_assert( nullptr != pImp );
ai_assert(NULL != pImp);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
pimpl->mPostProcessingSteps.push_back(pImp); pimpl->mPostProcessingSteps.push_back(pImp);
@ -213,9 +212,9 @@ aiReturn Importer::RegisterPPStep(BaseProcess* pImp)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Register a custom loader plugin // Register a custom loader plugin
aiReturn Importer::RegisterLoader(BaseImporter* pImp) aiReturn Importer::RegisterLoader(BaseImporter* pImp) {
{ ai_assert(nullptr != pImp);
ai_assert(NULL != pImp);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@ -242,13 +241,13 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp)
pimpl->mImporter.push_back(pImp); pimpl->mImporter.push_back(pImp);
ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked); ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked);
ASSIMP_END_EXCEPTION_REGION(aiReturn); ASSIMP_END_EXCEPTION_REGION(aiReturn);
return AI_SUCCESS; return AI_SUCCESS;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Unregister a custom loader plugin // Unregister a custom loader plugin
aiReturn Importer::UnregisterLoader(BaseImporter* pImp) aiReturn Importer::UnregisterLoader(BaseImporter* pImp) {
{
if(!pImp) { if(!pImp) {
// unregistering a NULL importer is no problem for us ... really! // unregistering a NULL importer is no problem for us ... really!
return AI_SUCCESS; return AI_SUCCESS;
@ -265,13 +264,13 @@ aiReturn Importer::UnregisterLoader(BaseImporter* pImp)
} }
ASSIMP_LOG_WARN("Unable to remove custom importer: I can't find you ..."); ASSIMP_LOG_WARN("Unable to remove custom importer: I can't find you ...");
ASSIMP_END_EXCEPTION_REGION(aiReturn); ASSIMP_END_EXCEPTION_REGION(aiReturn);
return AI_FAILURE; return AI_FAILURE;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Unregister a custom loader plugin // Unregister a custom loader plugin
aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) {
{
if(!pImp) { if(!pImp) {
// unregistering a NULL ppstep is no problem for us ... really! // unregistering a NULL ppstep is no problem for us ... really!
return AI_SUCCESS; return AI_SUCCESS;
@ -288,24 +287,22 @@ aiReturn Importer::UnregisterPPStep(BaseProcess* pImp)
} }
ASSIMP_LOG_WARN("Unable to remove custom post-processing step: I can't find you .."); ASSIMP_LOG_WARN("Unable to remove custom post-processing step: I can't find you ..");
ASSIMP_END_EXCEPTION_REGION(aiReturn); ASSIMP_END_EXCEPTION_REGION(aiReturn);
return AI_FAILURE; return AI_FAILURE;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Supplies a custom IO handler to the importer to open and access files. // Supplies a custom IO handler to the importer to open and access files.
void Importer::SetIOHandler( IOSystem* pIOHandler) void Importer::SetIOHandler( IOSystem* pIOHandler) {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// If the new handler is zero, allocate a default IO implementation. // If the new handler is zero, allocate a default IO implementation.
if (!pIOHandler) if (!pIOHandler) {
{
// Release pointer in the possession of the caller // Release pointer in the possession of the caller
pimpl->mIOHandler = new DefaultIOSystem(); pimpl->mIOHandler = new DefaultIOSystem();
pimpl->mIsDefaultHandler = true; pimpl->mIsDefaultHandler = true;
} } else if (pimpl->mIOHandler != pIOHandler) { // Otherwise register the custom handler
// Otherwise register the custom handler
else if (pimpl->mIOHandler != pIOHandler)
{
delete pimpl->mIOHandler; delete pimpl->mIOHandler;
pimpl->mIOHandler = pIOHandler; pimpl->mIOHandler = pIOHandler;
pimpl->mIsDefaultHandler = false; pimpl->mIsDefaultHandler = false;
@ -316,29 +313,32 @@ void Importer::SetIOHandler( IOSystem* pIOHandler)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the currently set IO handler // Get the currently set IO handler
IOSystem* Importer::GetIOHandler() const { IOSystem* Importer::GetIOHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIOHandler; return pimpl->mIOHandler;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Check whether a custom IO handler is currently set // Check whether a custom IO handler is currently set
bool Importer::IsDefaultIOHandler() const { bool Importer::IsDefaultIOHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIsDefaultHandler; return pimpl->mIsDefaultHandler;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Supplies a custom progress handler to get regular callbacks during importing // Supplies a custom progress handler to get regular callbacks during importing
void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { void Importer::SetProgressHandler ( ProgressHandler* pHandler ) {
ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// If the new handler is zero, allocate a default implementation. // If the new handler is zero, allocate a default implementation.
if (!pHandler) if (!pHandler) {
{
// Release pointer in the possession of the caller // Release pointer in the possession of the caller
pimpl->mProgressHandler = new DefaultProgressHandler(); pimpl->mProgressHandler = new DefaultProgressHandler();
pimpl->mIsDefaultProgressHandler = true; pimpl->mIsDefaultProgressHandler = true;
} } else if (pimpl->mProgressHandler != pHandler) { // Otherwise register the custom handler
// Otherwise register the custom handler
else if (pimpl->mProgressHandler != pHandler)
{
delete pimpl->mProgressHandler; delete pimpl->mProgressHandler;
pimpl->mProgressHandler = pHandler; pimpl->mProgressHandler = pHandler;
pimpl->mIsDefaultProgressHandler = false; pimpl->mIsDefaultProgressHandler = false;
@ -349,19 +349,22 @@ void Importer::SetProgressHandler ( ProgressHandler* pHandler ) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the currently set progress handler // Get the currently set progress handler
ProgressHandler* Importer::GetProgressHandler() const { ProgressHandler* Importer::GetProgressHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mProgressHandler; return pimpl->mProgressHandler;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Check whether a custom progress handler is currently set // Check whether a custom progress handler is currently set
bool Importer::IsDefaultProgressHandler() const { bool Importer::IsDefaultProgressHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIsDefaultProgressHandler; return pimpl->mIsDefaultProgressHandler;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Validate post process step flags // Validate post process step flags
bool _ValidateFlags(unsigned int pFlags) bool _ValidateFlags(unsigned int pFlags) {
{
if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) { if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) {
ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible"); ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible");
return false; return false;
@ -375,12 +378,13 @@ bool _ValidateFlags(unsigned int pFlags)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Free the current scene // Free the current scene
void Importer::FreeScene( ) void Importer::FreeScene( ) {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
delete pimpl->mScene; delete pimpl->mScene;
pimpl->mScene = NULL; pimpl->mScene = nullptr;
pimpl->mErrorString = ""; pimpl->mErrorString = "";
ASSIMP_END_EXCEPTION_REGION(void); ASSIMP_END_EXCEPTION_REGION(void);
@ -388,44 +392,48 @@ void Importer::FreeScene( )
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the current error string, if any // Get the current error string, if any
const char* Importer::GetErrorString() const const char* Importer::GetErrorString() const {
{ ai_assert(nullptr != pimpl);
/* Must remain valid as long as ReadFile() or FreeFile() are not called */
// Must remain valid as long as ReadFile() or FreeFile() are not called
return pimpl->mErrorString.c_str(); return pimpl->mErrorString.c_str();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Enable extra-verbose mode // Enable extra-verbose mode
void Importer::SetExtraVerbose(bool bDo) void Importer::SetExtraVerbose(bool bDo) {
{ ai_assert(nullptr != pimpl);
pimpl->bExtraVerbose = bDo; pimpl->bExtraVerbose = bDo;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the current scene // Get the current scene
const aiScene* Importer::GetScene() const const aiScene* Importer::GetScene() const {
{ ai_assert(nullptr != pimpl);
return pimpl->mScene; return pimpl->mScene;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Orphan the current scene and return it. // Orphan the current scene and return it.
aiScene* Importer::GetOrphanedScene() aiScene* Importer::GetOrphanedScene() {
{ ai_assert(nullptr != pimpl);
aiScene* s = pimpl->mScene; aiScene* s = pimpl->mScene;
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
pimpl->mScene = NULL; pimpl->mScene = nullptr;
pimpl->mErrorString = ""; /* reset error string */ pimpl->mErrorString = ""; // reset error string
ASSIMP_END_EXCEPTION_REGION(aiScene*); ASSIMP_END_EXCEPTION_REGION(aiScene*);
return s; return s;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Validate post-processing flags // Validate post-processing flags
bool Importer::ValidateFlags(unsigned int pFlags) const bool Importer::ValidateFlags(unsigned int pFlags) const {
{
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// run basic checks for mutually exclusive flags // run basic checks for mutually exclusive flags
if(!_ValidateFlags(pFlags)) { if(!_ValidateFlags(pFlags)) {
@ -467,8 +475,9 @@ bool Importer::ValidateFlags(unsigned int pFlags) const
const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
size_t pLength, size_t pLength,
unsigned int pFlags, unsigned int pFlags,
const char* pHint /*= ""*/) const char* pHint /*= ""*/) {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
if (!pHint) { if (!pHint) {
pHint = ""; pHint = "";
@ -476,12 +485,12 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) { if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) {
pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()"; pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()";
return NULL; return nullptr;
} }
// prevent deletion of the previous IOHandler // prevent deletion of the previous IOHandler
IOSystem* io = pimpl->mIOHandler; IOSystem* io = pimpl->mIOHandler;
pimpl->mIOHandler = NULL; pimpl->mIOHandler = nullptr;
SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io)); SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io));
@ -498,8 +507,8 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void WriteLogOpening(const std::string& file) void WriteLogOpening(const std::string& file) {
{
ASSIMP_LOG_INFO_F("Load ", file); ASSIMP_LOG_INFO_F("Load ", file);
// print a full version dump. This is nice because we don't // print a full version dump. This is nice because we don't
@ -550,8 +559,9 @@ void WriteLogOpening(const std::string& file)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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) {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
const std::string pFile(_pFile); const std::string pFile(_pFile);
@ -580,7 +590,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
pimpl->mErrorString = "Unable to open file \"" + pFile + "\"."; pimpl->mErrorString = "Unable to open file \"" + pFile + "\".";
ASSIMP_LOG_ERROR(pimpl->mErrorString); ASSIMP_LOG_ERROR(pimpl->mErrorString);
return NULL; return nullptr;
} }
std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
@ -589,7 +599,7 @@ 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 = nullptr;
SetPropertyInteger("importerIndex", -1); SetPropertyInteger("importerIndex", -1);
for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
@ -617,7 +627,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
if( !imp) { if( !imp) {
pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\"."; pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
ASSIMP_LOG_ERROR(pimpl->mErrorString); ASSIMP_LOG_ERROR(pimpl->mErrorString);
return NULL; return nullptr;
} }
} }
@ -633,7 +643,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
// Dispatch the reading to the worker class for this format // Dispatch the reading to the worker class for this format
const aiImporterDesc *desc( imp->GetInfo() ); const aiImporterDesc *desc( imp->GetInfo() );
std::string ext( "unknown" ); std::string ext( "unknown" );
if ( NULL != desc ) { if ( nullptr != desc ) {
ext = desc->mName; ext = desc->mName;
} }
ASSIMP_LOG_INFO("Found a matching importer for this file format: " + ext + "." ); ASSIMP_LOG_INFO("Found a matching importer for this file format: " + ext + "." );
@ -654,15 +664,20 @@ 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( pimpl->mScene) { if( pimpl->mScene) {
if (!pimpl->mScene->mMetaData || !pimpl->mScene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT)) {
if (!pimpl->mScene->mMetaData) {
pimpl->mScene->mMetaData = new aiMetadata;
}
pimpl->mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT, aiString(ext));
}
#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
// The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called. // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called.
if (pFlags & aiProcess_ValidateDataStructure) if (pFlags & aiProcess_ValidateDataStructure) {
{
ValidateDSProcess ds; ValidateDSProcess ds;
ds.ExecuteOnScene (this); ds.ExecuteOnScene (this);
if (!pimpl->mScene) { if (!pimpl->mScene) {
return NULL; return nullptr;
} }
} }
#endif // no validation #endif // no validation
@ -695,8 +710,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
} }
} }
#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS #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)
// if we have RTTI get the full name of the exception that occurred // if we have RTTI get the full name of the exception that occurred
pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what(); pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what();
@ -705,24 +719,26 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
#endif #endif
ASSIMP_LOG_ERROR(pimpl->mErrorString); ASSIMP_LOG_ERROR(pimpl->mErrorString);
delete pimpl->mScene; pimpl->mScene = NULL; delete pimpl->mScene; pimpl->mScene = nullptr;
} }
#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS #endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
// either successful or failure - the pointer expresses it anyways // either successful or failure - the pointer expresses it anyways
ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString); ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString);
return pimpl->mScene; return pimpl->mScene;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Apply post-processing to the currently bound scene // Apply post-processing to the currently bound scene
const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// Return immediately if no scene is active // Return immediately if no scene is active
if (!pimpl->mScene) { if (!pimpl->mScene) {
return NULL; return nullptr;
} }
// If no flags are given, return the current scene with no further action // If no flags are given, return the current scene with no further action
@ -737,12 +753,11 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
// The ValidateDS process plays an exceptional role. It isn't contained in the global // The ValidateDS process plays an exceptional role. It isn't contained in the global
// list of post-processing steps, so we need to call it manually. // list of post-processing steps, so we need to call it manually.
if (pFlags & aiProcess_ValidateDataStructure) if (pFlags & aiProcess_ValidateDataStructure) {
{
ValidateDSProcess ds; ValidateDSProcess ds;
ds.ExecuteOnScene (this); ds.ExecuteOnScene (this);
if (!pimpl->mScene) { if (!pimpl->mScene) {
return NULL; return nullptr;
} }
} }
#endif // no validation #endif // no validation
@ -762,11 +777,9 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
BaseProcess* process = pimpl->mPostProcessingSteps[a]; BaseProcess* process = pimpl->mPostProcessingSteps[a];
pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) ); pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) );
if( process->IsActive( pFlags)) { if( process->IsActive( pFlags)) {
if (profiler) { if (profiler) {
profiler->BeginRegion("postprocess"); profiler->BeginRegion("postprocess");
} }
@ -803,24 +816,28 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
static_cast<int>(pimpl->mPostProcessingSteps.size()) ); static_cast<int>(pimpl->mPostProcessingSteps.size()) );
// update private scene flags // update private scene flags
if( pimpl->mScene ) if( pimpl->mScene ) {
ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags; ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags;
}
// clear any data allocated by post-process steps // clear any data allocated by post-process steps
pimpl->mPPShared->Clean(); pimpl->mPPShared->Clean();
ASSIMP_LOG_INFO("Leaving post processing pipeline"); ASSIMP_LOG_INFO("Leaving post processing pipeline");
ASSIMP_END_EXCEPTION_REGION(const aiScene*); ASSIMP_END_EXCEPTION_REGION(const aiScene*);
return pimpl->mScene; return pimpl->mScene;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) { const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) {
ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// Return immediately if no scene is active // Return immediately if no scene is active
if ( NULL == pimpl->mScene ) { if ( nullptr == pimpl->mScene ) {
return NULL; return nullptr;
} }
// If no flags are given, return the current scene with no further action // If no flags are given, return the current scene with no further action
@ -839,7 +856,7 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
ValidateDSProcess ds; ValidateDSProcess ds;
ds.ExecuteOnScene( this ); ds.ExecuteOnScene( this );
if ( !pimpl->mScene ) { if ( !pimpl->mScene ) {
return NULL; return nullptr;
} }
} }
#endif // no validation #endif // no validation
@ -890,46 +907,50 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Helper function to check whether an extension is supported by ASSIMP // Helper function to check whether an extension is supported by ASSIMP
bool Importer::IsExtensionSupported(const char* szExtension) const bool Importer::IsExtensionSupported(const char* szExtension) const {
{
return nullptr != GetImporter(szExtension); return nullptr != GetImporter(szExtension);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
size_t Importer::GetImporterCount() const size_t Importer::GetImporterCount() const {
{ ai_assert(nullptr != pimpl);
return pimpl->mImporter.size(); return pimpl->mImporter.size();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiImporterDesc* Importer::GetImporterInfo(size_t index) const const aiImporterDesc* Importer::GetImporterInfo(size_t index) const {
{ ai_assert(nullptr != pimpl);
if (index >= pimpl->mImporter.size()) { if (index >= pimpl->mImporter.size()) {
return NULL; return nullptr;
} }
return pimpl->mImporter[index]->GetInfo(); return pimpl->mImporter[index]->GetInfo();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
BaseImporter* Importer::GetImporter (size_t index) const BaseImporter* Importer::GetImporter (size_t index) const {
{ ai_assert(nullptr != pimpl);
if (index >= pimpl->mImporter.size()) { if (index >= pimpl->mImporter.size()) {
return NULL; return nullptr;
} }
return pimpl->mImporter[index]; return pimpl->mImporter[index];
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Find a loader plugin for a given file extension // Find a loader plugin for a given file extension
BaseImporter* Importer::GetImporter (const char* szExtension) const BaseImporter* Importer::GetImporter (const char* szExtension) const {
{ ai_assert(nullptr != pimpl);
return GetImporter(GetImporterIndex(szExtension)); return GetImporter(GetImporterIndex(szExtension));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Find a loader plugin for a given file extension // Find a loader plugin for a given file extension
size_t Importer::GetImporterIndex (const char* szExtension) const { size_t Importer::GetImporterIndex (const char* szExtension) const {
ai_assert(nullptr != pimpl);
ai_assert(nullptr != szExtension); ai_assert(nullptr != szExtension);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
@ -960,8 +981,9 @@ size_t Importer::GetImporterIndex (const char* szExtension) const {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Helper function to build a list of all file extensions supported by ASSIMP // Helper function to build a list of all file extensions supported by ASSIMP
void Importer::GetExtensionList(aiString& szOut) const void Importer::GetExtensionList(aiString& szOut) const {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
std::set<std::string> str; std::set<std::string> str;
for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) {
@ -985,8 +1007,9 @@ void Importer::GetExtensionList(aiString& szOut) const
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Set a configuration property // Set a configuration property
bool Importer::SetPropertyInteger(const char* szName, int iValue) bool Importer::SetPropertyInteger(const char* szName, int iValue) {
{ ai_assert(nullptr != pimpl);
bool existing; bool existing;
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue); existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue);
@ -996,8 +1019,9 @@ bool Importer::SetPropertyInteger(const char* szName, int iValue)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Set a configuration property // Set a configuration property
bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) {
{ ai_assert(nullptr != pimpl);
bool existing; bool existing;
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
existing = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue); existing = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue);
@ -1007,8 +1031,9 @@ bool Importer::SetPropertyFloat(const char* szName, ai_real iValue)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Set a configuration property // Set a configuration property
bool Importer::SetPropertyString(const char* szName, const std::string& value) bool Importer::SetPropertyString(const char* szName, const std::string& value) {
{ ai_assert(nullptr != pimpl);
bool existing; bool existing;
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
existing = SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value); existing = SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value);
@ -1018,8 +1043,9 @@ bool Importer::SetPropertyString(const char* szName, const std::string& value)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Set a configuration property // Set a configuration property
bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) {
{ ai_assert(nullptr != pimpl);
bool existing; bool existing;
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
existing = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value); existing = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value);
@ -1029,40 +1055,43 @@ bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a configuration property // Get a configuration property
int Importer::GetPropertyInteger(const char* szName, int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const {
int iErrorReturn /*= 0xffffffff*/) const ai_assert(nullptr != pimpl);
{
return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn); return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a configuration property // Get a configuration property
ai_real Importer::GetPropertyFloat(const char* szName, ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const {
ai_real iErrorReturn /*= 10e10*/) const ai_assert(nullptr != pimpl);
{
return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn); return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a configuration property // Get a configuration property
const std::string Importer::GetPropertyString(const char* szName, const std::string Importer::GetPropertyString(const char* szName, const std::string& iErrorReturn /*= ""*/) const {
const std::string& iErrorReturn /*= ""*/) const ai_assert(nullptr != pimpl);
{
return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn); return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a configuration property // Get a configuration property
const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const {
const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const ai_assert(nullptr != pimpl);
{
return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn); return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the memory requirements of a single node // Get the memory requirements of a single node
inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) inline
{ void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) {
if ( nullptr == pcNode ) {
return;
}
iScene += sizeof(aiNode); iScene += sizeof(aiNode);
iScene += sizeof(unsigned int) * pcNode->mNumMeshes; iScene += sizeof(unsigned int) * pcNode->mNumMeshes;
iScene += sizeof(void*) * pcNode->mNumChildren; iScene += sizeof(void*) * pcNode->mNumChildren;
@ -1074,8 +1103,9 @@ inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the memory requirements of the scene // Get the memory requirements of the scene
void Importer::GetMemoryRequirements(aiMemoryInfo& in) const void Importer::GetMemoryRequirements(aiMemoryInfo& in) const {
{ ai_assert(nullptr != pimpl);
in = aiMemoryInfo(); in = aiMemoryInfo();
aiScene* mScene = pimpl->mScene; aiScene* mScene = pimpl->mScene;
@ -1087,8 +1117,7 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
in.total = sizeof(aiScene); in.total = sizeof(aiScene);
// add all meshes // add all meshes
for (unsigned int i = 0; i < mScene->mNumMeshes;++i) for (unsigned int i = 0; i < mScene->mNumMeshes;++i) {
{
in.meshes += sizeof(aiMesh); in.meshes += sizeof(aiMesh);
if (mScene->mMeshes[i]->HasPositions()) { if (mScene->mMeshes[i]->HasPositions()) {
in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
@ -1105,14 +1134,16 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
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 (mScene->mMeshes[i]->HasVertexColors(a)) { if (mScene->mMeshes[i]->HasVertexColors(a)) {
in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices; in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices;
} else {
break;
} }
else break;
} }
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 (mScene->mMeshes[i]->HasTextureCoords(a)) { if (mScene->mMeshes[i]->HasTextureCoords(a)) {
in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
} else {
break;
} }
else break;
} }
if (mScene->mMeshes[i]->HasBones()) { if (mScene->mMeshes[i]->HasBones()) {
in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones; in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones;
@ -1131,8 +1162,9 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
in.textures += sizeof(aiTexture); in.textures += sizeof(aiTexture);
if (pc->mHeight) { if (pc->mHeight) {
in.textures += 4 * pc->mHeight * pc->mWidth; in.textures += 4 * pc->mHeight * pc->mWidth;
} else {
in.textures += pc->mWidth;
} }
else in.textures += pc->mWidth;
} }
in.total += in.textures; in.total += in.textures;

View File

@ -60,6 +60,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/CreateAnimMesh.h> #include <assimp/CreateAnimMesh.h>
#include <assimp/commonMetaData.h>
#include <assimp/StringUtils.h>
#include <tuple> #include <tuple>
#include <memory> #include <memory>
@ -1597,11 +1599,10 @@ namespace Assimp {
aiBone *bone = nullptr; aiBone *bone = nullptr;
if (bone_map.count(deformer_name)) { if (bone_map.count(deformer_name)) {
std::cout << "retrieved bone from lookup " << bone_name.C_Str() << ". Deformer: " << deformer_name ASSIMP_LOG_DEBUG_F("retrieved bone from lookup ", bone_name.C_Str(), ". Deformer:", deformer_name);
<< std::endl;
bone = bone_map[deformer_name]; bone = bone_map[deformer_name];
} else { } else {
std::cout << "created new bone " << bone_name.C_Str() << ". Deformer: " << deformer_name << std::endl; ASSIMP_LOG_DEBUG_F("created new bone ", bone_name.C_Str(), ". Deformer: ", deformer_name);
bone = new aiBone(); bone = new aiBone();
bone->mName = bone_name; bone->mName = bone_name;
@ -1648,7 +1649,7 @@ namespace Assimp {
bone_map.insert(std::pair<const std::string, aiBone *>(deformer_name, bone)); bone_map.insert(std::pair<const std::string, aiBone *>(deformer_name, bone));
} }
std::cout << "bone research: Indicies size: " << out_indices.size() << std::endl; ASSIMP_LOG_DEBUG_F("bone research: Indicies size: ", out_indices.size());
// lookup must be populated in case something goes wrong // lookup must be populated in case something goes wrong
// this also allocates bones to mesh instance outside // this also allocates bones to mesh instance outside
@ -2088,6 +2089,13 @@ namespace Assimp {
TrySetTextureProperties(out_mat, textures, "Maya|TEX_metallic_map|file", aiTextureType_METALNESS, mesh); TrySetTextureProperties(out_mat, textures, "Maya|TEX_metallic_map|file", aiTextureType_METALNESS, mesh);
TrySetTextureProperties(out_mat, textures, "Maya|TEX_roughness_map|file", aiTextureType_DIFFUSE_ROUGHNESS, mesh); TrySetTextureProperties(out_mat, textures, "Maya|TEX_roughness_map|file", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
TrySetTextureProperties(out_mat, textures, "Maya|TEX_ao_map|file", aiTextureType_AMBIENT_OCCLUSION, mesh); TrySetTextureProperties(out_mat, textures, "Maya|TEX_ao_map|file", aiTextureType_AMBIENT_OCCLUSION, mesh);
// 3DSMax PBR
TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|base_color_map", aiTextureType_BASE_COLOR, mesh);
TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|bump_map", aiTextureType_NORMAL_CAMERA, mesh);
TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|emission_map", aiTextureType_EMISSION_COLOR, mesh);
TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|metalness_map", aiTextureType_METALNESS, mesh);
TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|roughness_map", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
} }
void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh) void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh)
@ -3604,7 +3612,9 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
return; return;
} }
out->mMetaData = aiMetadata::Alloc(15); const bool hasGenerator = !doc.Creator().empty();
out->mMetaData = aiMetadata::Alloc(16 + (hasGenerator ? 1 : 0));
out->mMetaData->Set(0, "UpAxis", doc.GlobalSettings().UpAxis()); out->mMetaData->Set(0, "UpAxis", doc.GlobalSettings().UpAxis());
out->mMetaData->Set(1, "UpAxisSign", doc.GlobalSettings().UpAxisSign()); out->mMetaData->Set(1, "UpAxisSign", doc.GlobalSettings().UpAxisSign());
out->mMetaData->Set(2, "FrontAxis", doc.GlobalSettings().FrontAxis()); out->mMetaData->Set(2, "FrontAxis", doc.GlobalSettings().FrontAxis());
@ -3620,6 +3630,11 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
out->mMetaData->Set(12, "TimeSpanStart", doc.GlobalSettings().TimeSpanStart()); out->mMetaData->Set(12, "TimeSpanStart", doc.GlobalSettings().TimeSpanStart());
out->mMetaData->Set(13, "TimeSpanStop", doc.GlobalSettings().TimeSpanStop()); out->mMetaData->Set(13, "TimeSpanStop", doc.GlobalSettings().TimeSpanStop());
out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate()); out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
out->mMetaData->Set(15, AI_METADATA_SOURCE_FORMAT_VERSION, aiString(to_string(doc.FBXVersion())));
if (hasGenerator)
{
out->mMetaData->Set(16, AI_METADATA_SOURCE_GENERATOR, aiString(doc.Creator()));
}
} }
void FBXConverter::TransferDataToScene() void FBXConverter::TransferDataToScene()

View File

@ -421,6 +421,8 @@ private:
double& minTime, double& minTime,
Model::RotOrder order); Model::RotOrder order);
// ------------------------------------------------------------------------------------------------
// Copy global geometric data and some information about the source asset into scene metadata.
void ConvertGlobalSettings(); void ConvertGlobalSettings();
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -138,8 +138,9 @@ void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t m
} }
} }
} }
if (outer_polygon_it == end) {
ai_assert(outer_polygon_it != end); return;
}
const size_t outer_polygon_size = *outer_polygon_it; const size_t outer_polygon_size = *outer_polygon_it;
const IfcVector3& master_normal = normals[std::distance(begin, outer_polygon_it)]; const IfcVector3& master_normal = normals[std::distance(begin, outer_polygon_it)];

View File

@ -586,7 +586,6 @@ void LWOImporter::GenerateNodeGraph(std::map<uint16_t,aiNode*>& apcNodes)
root->mName.Set("<LWORoot>"); root->mName.Set("<LWORoot>");
//Set parent of all children, inserting pivots //Set parent of all children, inserting pivots
//std::cout << "Set parent of all children" << std::endl;
std::map<uint16_t, aiNode*> mapPivot; std::map<uint16_t, aiNode*> mapPivot;
for (auto itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) { for (auto itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) {
@ -618,7 +617,6 @@ void LWOImporter::GenerateNodeGraph(std::map<uint16_t,aiNode*>& apcNodes)
} }
//Merge pivot map into node map //Merge pivot map into node map
//std::cout << "Merge pivot map into node map" << std::endl;
for (auto itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end(); ++itMapPivot) { for (auto itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end(); ++itMapPivot) {
apcNodes[itMapPivot->first] = itMapPivot->second; apcNodes[itMapPivot->first] = itMapPivot->second;
} }

View File

@ -55,17 +55,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string> #include <string>
#include <vector> #include <vector>
#include <assimp/version.h> // aiGetVersion
#include <assimp/IOSystem.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/Exceptional.h> // DeadlyExportError #include <assimp/Exceptional.h> // DeadlyExportError
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/material.h> // aiTextureType #include <assimp/material.h> // aiTextureType
#include <assimp/scene.h>
#include <assimp/mesh.h> #include <assimp/mesh.h>
#include <assimp/scene.h>
#include <assimp/version.h> // aiGetVersion
#include <assimp/DefaultLogger.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/IOSystem.hpp>
#include "M3DExporter.h" #include "M3DExporter.h"
#include "M3DMaterials.h" #include "M3DMaterials.h"
#include "M3DWrapper.h"
// RESOURCES: // RESOURCES:
// https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md // https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md
@ -80,105 +82,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* - aiAnimation -> m3d_action (frame with timestamp and list of bone id, position, orientation * - aiAnimation -> m3d_action (frame with timestamp and list of bone id, position, orientation
* triplets, instead of per bone timestamp + lists) * triplets, instead of per bone timestamp + lists)
*/ */
using namespace Assimp;
namespace Assimp {
// ---------------------------------------------------------------------
// Worker function for exporting a scene to binary M3D.
// Prototyped and registered in Exporter.cpp
void ExportSceneM3D (
const char* pFile,
IOSystem* pIOSystem,
const aiScene* pScene,
const ExportProperties* pProperties
){
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform binary export
exporter.doExport(pFile, pIOSystem, false);
}
// ---------------------------------------------------------------------
// Worker function for exporting a scene to ASCII A3D.
// Prototyped and registered in Exporter.cpp
void ExportSceneA3D (
const char* pFile,
IOSystem* pIOSystem,
const aiScene* pScene,
const ExportProperties* pProperties
){
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform ascii export
exporter.doExport(pFile, pIOSystem, true);
}
} // end of namespace Assimp
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
M3DExporter::M3DExporter ( const aiScene* pScene, const ExportProperties* pProperties ) // Conversion functions
: mScene(pScene)
, mProperties(pProperties)
, outfile()
, m3d(nullptr) { }
// ------------------------------------------------------------------------------------------------
void M3DExporter::doExport (
const char* pFile,
IOSystem* pIOSystem,
bool toAscii
){
// TODO: convert mProperties into M3D_EXP_* flags
(void)mProperties;
// open the indicated file for writing (in binary / ASCII mode)
outfile.reset(pIOSystem->Open(pFile, toAscii ? "wt" : "wb"));
if (!outfile) {
throw DeadlyExportError( "could not open output .m3d file: " + std::string(pFile) );
}
// use malloc() here because m3d_free() will call free()
m3d = (m3d_t*)calloc(1, sizeof(m3d_t));
if(!m3d) {
throw DeadlyExportError( "memory allocation error" );
}
m3d->name = _m3d_safestr((char*)&mScene->mRootNode->mName.data, 2);
// Create a model from assimp structures
aiMatrix4x4 m;
NodeWalk(mScene->mRootNode, m);
// serialize the structures
unsigned int size;
unsigned char *output = m3d_save(m3d, M3D_EXP_FLOAT,
M3D_EXP_EXTRA | (toAscii ? M3D_EXP_ASCII : 0), &size);
m3d_free(m3d);
if(!output || size < 8) {
throw DeadlyExportError( "unable to serialize into Model 3D" );
}
// Write out serialized model
outfile->Write(output, size, 1);
// explicitly release file pointer,
// so we don't have to rely on class destruction.
outfile.reset();
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// helper to add a vertex (private to NodeWalk) // helper to add a vertex (private to NodeWalk)
m3dv_t *M3DExporter::AddVrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_t *idx) m3dv_t *AddVrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_t *idx) {
{ if (v->x == (M3D_FLOAT)-0.0) v->x = (M3D_FLOAT)0.0;
if(v->x == (M3D_FLOAT)-0.0) v->x = (M3D_FLOAT)0.0; if (v->y == (M3D_FLOAT)-0.0) v->y = (M3D_FLOAT)0.0;
if(v->y == (M3D_FLOAT)-0.0) v->y = (M3D_FLOAT)0.0; if (v->z == (M3D_FLOAT)-0.0) v->z = (M3D_FLOAT)0.0;
if(v->z == (M3D_FLOAT)-0.0) v->z = (M3D_FLOAT)0.0; if (v->w == (M3D_FLOAT)-0.0) v->w = (M3D_FLOAT)0.0;
if(v->w == (M3D_FLOAT)-0.0) v->w = (M3D_FLOAT)0.0; vrtx = (m3dv_t *)M3D_REALLOC(vrtx, ((*numvrtx) + 1) * sizeof(m3dv_t));
vrtx = (m3dv_t*)M3D_REALLOC(vrtx, ((*numvrtx) + 1) * sizeof(m3dv_t));
memcpy(&vrtx[*numvrtx], v, sizeof(m3dv_t)); memcpy(&vrtx[*numvrtx], v, sizeof(m3dv_t));
*idx = *numvrtx; *idx = *numvrtx;
(*numvrtx)++; (*numvrtx)++;
@ -187,9 +101,8 @@ m3dv_t *M3DExporter::AddVrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// helper to add a tmap (private to NodeWalk) // helper to add a tmap (private to NodeWalk)
m3dti_t *M3DExporter::AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uint32_t *idx) m3dti_t *AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uint32_t *idx) {
{ tmap = (m3dti_t *)M3D_REALLOC(tmap, ((*numtmap) + 1) * sizeof(m3dti_t));
tmap = (m3dti_t*)M3D_REALLOC(tmap, ((*numtmap) + 1) * sizeof(m3dti_t));
memcpy(&tmap[*numtmap], ti, sizeof(m3dti_t)); memcpy(&tmap[*numtmap], ti, sizeof(m3dti_t));
*idx = *numtmap; *idx = *numtmap;
(*numtmap)++; (*numtmap)++;
@ -197,154 +110,84 @@ m3dti_t *M3DExporter::AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uin
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// recursive node walker // convert aiColor4D into uint32_t
void M3DExporter::NodeWalk(const aiNode* pNode, aiMatrix4x4 m) uint32_t mkColor(aiColor4D *c) {
{ return ((uint8_t)(c->a * 255) << 24L) |
aiMatrix4x4 nm = m * pNode->mTransformation; ((uint8_t)(c->b * 255) << 16L) |
((uint8_t)(c->g * 255) << 8L) |
for(unsigned int i = 0; i < pNode->mNumMeshes; i++) { ((uint8_t)(c->r * 255) << 0L);
const aiMesh *mesh = mScene->mMeshes[pNode->mMeshes[i]];
unsigned int mi = (M3D_INDEX)-1U;
if(mScene->mMaterials) {
// get the material for this mesh
mi = addMaterial(mScene->mMaterials[mesh->mMaterialIndex]);
}
// iterate through the mesh faces
for(unsigned int j = 0; j < mesh->mNumFaces; j++) {
unsigned int n;
const aiFace* face = &(mesh->mFaces[j]);
// only triangle meshes supported for now
if(face->mNumIndices != 3) {
throw DeadlyExportError( "use aiProcess_Triangulate before export" );
}
// add triangle to the output
n = m3d->numface++;
m3d->face = (m3df_t*)M3D_REALLOC(m3d->face,
m3d->numface * sizeof(m3df_t));
if(!m3d->face) {
throw DeadlyExportError( "memory allocation error" );
}
/* set all index to -1 by default */
m3d->face[n].vertex[0] = m3d->face[n].vertex[1] = m3d->face[n].vertex[2] =
m3d->face[n].normal[0] = m3d->face[n].normal[1] = m3d->face[n].normal[2] =
m3d->face[n].texcoord[0] = m3d->face[n].texcoord[1] = m3d->face[n].texcoord[2] = -1U;
m3d->face[n].materialid = mi;
for(unsigned int k = 0; k < face->mNumIndices; k++) {
// get the vertex's index
unsigned int l = face->mIndices[k];
unsigned int idx;
m3dv_t vertex;
m3dti_t ti;
// multiply the position vector by the transformation matrix
aiVector3D v = mesh->mVertices[l];
v *= nm;
vertex.x = v.x;
vertex.y = v.y;
vertex.z = v.z;
vertex.w = 1.0;
vertex.color = 0;
vertex.skinid = -1U;
// add color if defined
if(mesh->HasVertexColors(0))
vertex.color = mkColor(&mesh->mColors[0][l]);
// save the vertex to the output
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex,
&vertex, &idx);
m3d->face[n].vertex[k] = (M3D_INDEX)idx;
// do we have texture coordinates?
if(mesh->HasTextureCoords(0)) {
ti.u = mesh->mTextureCoords[0][l].x;
ti.v = mesh->mTextureCoords[0][l].y;
m3d->tmap = AddTmap(m3d->tmap, &m3d->numtmap, &ti, &idx);
m3d->face[n].texcoord[k] = (M3D_INDEX)idx;
}
// do we have normal vectors?
if(mesh->HasNormals()) {
vertex.x = mesh->mNormals[l].x;
vertex.y = mesh->mNormals[l].y;
vertex.z = mesh->mNormals[l].z;
vertex.color = 0;
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex, &vertex, &idx);
m3d->face[n].normal[k] = (M3D_INDEX)idx;
}
}
}
}
// repeat for the children nodes
for (unsigned int i = 0; i < pNode->mNumChildren; i++) {
NodeWalk(pNode->mChildren[i], nm);
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// convert aiColor4D into uint32_t // add a material property to the output
uint32_t M3DExporter::mkColor(aiColor4D* c) void addProp(m3dm_t *m, uint8_t type, uint32_t value) {
{ unsigned int i;
return ((uint8_t)(c->a*255) << 24L) | i = m->numprop++;
((uint8_t)(c->b*255) << 16L) | m->prop = (m3dp_t *)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t));
((uint8_t)(c->g*255) << 8L) | if (!m->prop) {
((uint8_t)(c->r*255) << 0L); throw DeadlyExportError("memory allocation error");
}
m->prop[i].type = type;
m->prop[i].value.num = value;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// add a material to the output // add a material to the output
M3D_INDEX M3DExporter::addMaterial(const aiMaterial *mat) M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) {
{
unsigned int mi = -1U; unsigned int mi = -1U;
aiColor4D c; aiColor4D c;
aiString name; aiString name;
ai_real f; ai_real f;
char *fn; char *fn;
if(mat && mat->Get(AI_MATKEY_NAME, name) == AI_SUCCESS && name.length && if (mat && mat->Get(AI_MATKEY_NAME, name) == AI_SUCCESS && name.length &&
strcmp((char*)&name.data, AI_DEFAULT_MATERIAL_NAME)) { strcmp((char *)&name.data, AI_DEFAULT_MATERIAL_NAME)) {
// check if we have saved a material by this name. This has to be done // check if we have saved a material by this name. This has to be done
// because only the referenced materials should be added to the output // because only the referenced materials should be added to the output
for(unsigned int i = 0; i < m3d->nummaterial; i++) for (unsigned int i = 0; i < m3d->nummaterial; i++)
if(!strcmp((char*)&name.data, m3d->material[i].name)) { if (!strcmp((char *)&name.data, m3d->material[i].name)) {
mi = i; mi = i;
break; break;
} }
// if not found, add the material to the output // if not found, add the material to the output
if(mi == -1U) { if (mi == -1U) {
unsigned int k; unsigned int k;
mi = m3d->nummaterial++; mi = m3d->nummaterial++;
m3d->material = (m3dm_t*)M3D_REALLOC(m3d->material, m3d->nummaterial m3d->material = (m3dm_t *)M3D_REALLOC(m3d->material, m3d->nummaterial * sizeof(m3dm_t));
* sizeof(m3dm_t)); if (!m3d->material) {
if(!m3d->material) { throw DeadlyExportError("memory allocation error");
throw DeadlyExportError( "memory allocation error" );
} }
m3d->material[mi].name = _m3d_safestr((char*)&name.data, 0); m3d->material[mi].name = _m3d_safestr((char *)&name.data, 0);
m3d->material[mi].numprop = 0; m3d->material[mi].numprop = 0;
m3d->material[mi].prop = NULL; m3d->material[mi].prop = NULL;
// iterate through the material property table and see what we got // iterate through the material property table and see what we got
for(k = 0; k < 15; k++) { for (k = 0; k < 15; k++) {
unsigned int j; unsigned int j;
if(m3d_propertytypes[k].format == m3dpf_map) if (m3d_propertytypes[k].format == m3dpf_map)
continue; continue;
if(aiProps[k].pKey) { if (aiProps[k].pKey) {
switch(m3d_propertytypes[k].format) { switch (m3d_propertytypes[k].format) {
case m3dpf_color: case m3dpf_color:
if(mat->Get(aiProps[k].pKey, aiProps[k].type, if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, c) == AI_SUCCESS) aiProps[k].index, c) == AI_SUCCESS)
addProp(&m3d->material[mi], addProp(&m3d->material[mi],
m3d_propertytypes[k].id, mkColor(&c)); m3d_propertytypes[k].id, mkColor(&c));
break; break;
case m3dpf_float: case m3dpf_float:
if(mat->Get(aiProps[k].pKey, aiProps[k].type, if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, f) == AI_SUCCESS) aiProps[k].index, f) == AI_SUCCESS)
addProp(&m3d->material[mi], addProp(&m3d->material[mi],
m3d_propertytypes[k].id, m3d_propertytypes[k].id,
/* not (uint32_t)f, because we don't want to convert /* not (uint32_t)f, because we don't want to convert
* it, we want to see it as 32 bits of memory */ * it, we want to see it as 32 bits of memory */
*((uint32_t*)&f)); *((uint32_t *)&f));
break; break;
case m3dpf_uint8: case m3dpf_uint8:
if(mat->Get(aiProps[k].pKey, aiProps[k].type, if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS) { aiProps[k].index, j) == AI_SUCCESS) {
// special conversion for illumination model property // special conversion for illumination model property
if(m3d_propertytypes[k].id == m3dp_il) { if (m3d_propertytypes[k].id == m3dp_il) {
switch(j) { switch (j) {
case aiShadingMode_NoShading: j = 0; break; case aiShadingMode_NoShading: j = 0; break;
case aiShadingMode_Phong: j = 2; break; case aiShadingMode_Phong: j = 2; break;
default: j = 1; break; default: j = 1; break;
@ -355,39 +198,40 @@ M3D_INDEX M3DExporter::addMaterial(const aiMaterial *mat)
} }
break; break;
default: default:
if(mat->Get(aiProps[k].pKey, aiProps[k].type, if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS) aiProps[k].index, j) == AI_SUCCESS)
addProp(&m3d->material[mi], addProp(&m3d->material[mi],
m3d_propertytypes[k].id, j); m3d_propertytypes[k].id, j);
break; break;
} }
} }
if(aiTxProps[k].pKey && if (aiTxProps[k].pKey &&
mat->GetTexture((aiTextureType)aiTxProps[k].type, mat->GetTexture((aiTextureType)aiTxProps[k].type,
aiTxProps[k].index, &name, NULL, NULL, NULL, aiTxProps[k].index, &name, NULL, NULL, NULL,
NULL, NULL) == AI_SUCCESS) { NULL, NULL) == AI_SUCCESS) {
unsigned int i; unsigned int i;
for(j = name.length-1; j > 0 && name.data[j]!='.'; j++); for (j = name.length - 1; j > 0 && name.data[j] != '.'; j++)
if(j && name.data[j]=='.' && ;
(name.data[j+1]=='p' || name.data[j+1]=='P') && if (j && name.data[j] == '.' &&
(name.data[j+1]=='n' || name.data[j+1]=='N') && (name.data[j + 1] == 'p' || name.data[j + 1] == 'P') &&
(name.data[j+1]=='g' || name.data[j+1]=='G')) (name.data[j + 1] == 'n' || name.data[j + 1] == 'N') &&
name.data[j]=0; (name.data[j + 1] == 'g' || name.data[j + 1] == 'G'))
name.data[j] = 0;
// do we have this texture saved already? // do we have this texture saved already?
fn = _m3d_safestr((char*)&name.data, 0); fn = _m3d_safestr((char *)&name.data, 0);
for(j = 0, i = -1U; j < m3d->numtexture; j++) for (j = 0, i = -1U; j < m3d->numtexture; j++)
if(!strcmp(fn, m3d->texture[j].name)) { if (!strcmp(fn, m3d->texture[j].name)) {
i = j; i = j;
free(fn); free(fn);
break; break;
} }
if(i == -1U) { if (i == -1U) {
i = m3d->numtexture++; i = m3d->numtexture++;
m3d->texture = (m3dtx_t*)M3D_REALLOC( m3d->texture = (m3dtx_t *)M3D_REALLOC(
m3d->texture, m3d->texture,
m3d->numtexture * sizeof(m3dtx_t)); m3d->numtexture * sizeof(m3dtx_t));
if(!m3d->texture) { if (!m3d->texture) {
throw DeadlyExportError( "memory allocation error" ); throw DeadlyExportError("memory allocation error");
} }
// we don't need the texture itself, only its name // we don't need the texture itself, only its name
m3d->texture[i].name = fn; m3d->texture[i].name = fn;
@ -404,17 +248,165 @@ M3D_INDEX M3DExporter::addMaterial(const aiMaterial *mat)
return mi; return mi;
} }
// ------------------------------------------------------------------------------------------------ namespace Assimp {
// add a material property to the output
void M3DExporter::addProp(m3dm_t *m, uint8_t type, uint32_t value) // ---------------------------------------------------------------------
{ // Worker function for exporting a scene to binary M3D.
unsigned int i; // Prototyped and registered in Exporter.cpp
i = m->numprop++; void ExportSceneM3D(
m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t)); const char *pFile,
if(!m->prop) { throw DeadlyExportError( "memory allocation error" ); } IOSystem *pIOSystem,
m->prop[i].type = type; const aiScene *pScene,
m->prop[i].value.num = value; const ExportProperties *pProperties) {
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform binary export
exporter.doExport(pFile, pIOSystem, false);
} }
// ---------------------------------------------------------------------
// Worker function for exporting a scene to ASCII A3D.
// Prototyped and registered in Exporter.cpp
void ExportSceneA3D(
const char *pFile,
IOSystem *pIOSystem,
const aiScene *pScene,
const ExportProperties *pProperties
) {
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform ascii export
exporter.doExport(pFile, pIOSystem, true);
}
// ------------------------------------------------------------------------------------------------
M3DExporter::M3DExporter(const aiScene *pScene, const ExportProperties *pProperties) :
mScene(pScene),
mProperties(pProperties),
outfile() {}
// ------------------------------------------------------------------------------------------------
void M3DExporter::doExport(
const char *pFile,
IOSystem *pIOSystem,
bool toAscii) {
// TODO: convert mProperties into M3D_EXP_* flags
(void)mProperties;
// open the indicated file for writing (in binary / ASCII mode)
outfile.reset(pIOSystem->Open(pFile, toAscii ? "wt" : "wb"));
if (!outfile) {
throw DeadlyExportError("could not open output .m3d file: " + std::string(pFile));
}
M3DWrapper m3d;
if (!m3d) {
throw DeadlyExportError("memory allocation error");
}
m3d->name = _m3d_safestr((char *)&mScene->mRootNode->mName.data, 2);
// Create a model from assimp structures
aiMatrix4x4 m;
NodeWalk(m3d, mScene->mRootNode, m);
// serialize the structures
unsigned int size;
unsigned char *output = m3d.Save(M3D_EXP_FLOAT, M3D_EXP_EXTRA | (toAscii ? M3D_EXP_ASCII : 0), size);
if (!output || size < 8) {
throw DeadlyExportError("unable to serialize into Model 3D");
}
// Write out serialized model
outfile->Write(output, size, 1);
// explicitly release file pointer,
// so we don't have to rely on class destruction.
outfile.reset();
}
// ------------------------------------------------------------------------------------------------
// recursive node walker
void M3DExporter::NodeWalk(const M3DWrapper &m3d, const aiNode *pNode, aiMatrix4x4 m) {
aiMatrix4x4 nm = m * pNode->mTransformation;
for (unsigned int i = 0; i < pNode->mNumMeshes; i++) {
const aiMesh *mesh = mScene->mMeshes[pNode->mMeshes[i]];
unsigned int mi = (M3D_INDEX)-1U;
if (mScene->mMaterials) {
// get the material for this mesh
mi = addMaterial(m3d, mScene->mMaterials[mesh->mMaterialIndex]);
}
// iterate through the mesh faces
for (unsigned int j = 0; j < mesh->mNumFaces; j++) {
unsigned int n;
const aiFace *face = &(mesh->mFaces[j]);
// only triangle meshes supported for now
if (face->mNumIndices != 3) {
throw DeadlyExportError("use aiProcess_Triangulate before export");
}
// add triangle to the output
n = m3d->numface++;
m3d->face = (m3df_t *)M3D_REALLOC(m3d->face,
m3d->numface * sizeof(m3df_t));
if (!m3d->face) {
throw DeadlyExportError("memory allocation error");
}
/* set all index to -1 by default */
m3d->face[n].vertex[0] = m3d->face[n].vertex[1] = m3d->face[n].vertex[2] =
m3d->face[n].normal[0] = m3d->face[n].normal[1] = m3d->face[n].normal[2] =
m3d->face[n].texcoord[0] = m3d->face[n].texcoord[1] = m3d->face[n].texcoord[2] = -1U;
m3d->face[n].materialid = mi;
for (unsigned int k = 0; k < face->mNumIndices; k++) {
// get the vertex's index
unsigned int l = face->mIndices[k];
unsigned int idx;
m3dv_t vertex;
m3dti_t ti;
// multiply the position vector by the transformation matrix
aiVector3D v = mesh->mVertices[l];
v *= nm;
vertex.x = v.x;
vertex.y = v.y;
vertex.z = v.z;
vertex.w = 1.0;
vertex.color = 0;
vertex.skinid = -1U;
// add color if defined
if (mesh->HasVertexColors(0))
vertex.color = mkColor(&mesh->mColors[0][l]);
// save the vertex to the output
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex,
&vertex, &idx);
m3d->face[n].vertex[k] = (M3D_INDEX)idx;
// do we have texture coordinates?
if (mesh->HasTextureCoords(0)) {
ti.u = mesh->mTextureCoords[0][l].x;
ti.v = mesh->mTextureCoords[0][l].y;
m3d->tmap = AddTmap(m3d->tmap, &m3d->numtmap, &ti, &idx);
m3d->face[n].texcoord[k] = (M3D_INDEX)idx;
}
// do we have normal vectors?
if (mesh->HasNormals()) {
vertex.x = mesh->mNormals[l].x;
vertex.y = mesh->mNormals[l].y;
vertex.z = mesh->mNormals[l].z;
vertex.color = 0;
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex, &vertex, &idx);
m3d->face[n].normal[k] = (M3D_INDEX)idx;
}
}
}
}
// repeat for the children nodes
for (unsigned int i = 0; i < pNode->mNumChildren; i++) {
NodeWalk(m3d, pNode->mChildren[i], nm);
}
}
} // namespace Assimp
#endif // ASSIMP_BUILD_NO_M3D_EXPORTER #endif // ASSIMP_BUILD_NO_M3D_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT #endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -48,8 +48,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER #ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
#include "m3d.h"
#include <assimp/types.h> #include <assimp/types.h>
//#include <assimp/material.h> //#include <assimp/material.h>
#include <assimp/StreamWriter.h> // StreamWriterLE #include <assimp/StreamWriter.h> // StreamWriterLE
@ -68,6 +66,8 @@ namespace Assimp
class IOStream; class IOStream;
class ExportProperties; class ExportProperties;
class M3DWrapper;
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/** Helper class to export a given scene to an M3D file. */ /** Helper class to export a given scene to an M3D file. */
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -83,15 +83,9 @@ namespace Assimp
const aiScene* mScene; // the scene to export const aiScene* mScene; // the scene to export
const ExportProperties* mProperties; // currently unused const ExportProperties* mProperties; // currently unused
std::shared_ptr<IOStream> outfile; // file to write to std::shared_ptr<IOStream> outfile; // file to write to
m3d_t *m3d; // model for the C library to convert to
// helper to do the recursive walking // helper to do the recursive walking
void NodeWalk(const aiNode* pNode, aiMatrix4x4 m); void NodeWalk(const M3DWrapper &m3d, const aiNode* pNode, aiMatrix4x4 m);
m3dv_t *AddVrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_t *idx);
m3dti_t *AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uint32_t *idx);
uint32_t mkColor(aiColor4D* c);
M3D_INDEX addMaterial(const aiMaterial *mat);
void addProp(m3dm_t *m, uint8_t type, uint32_t value);
}; };
} }

View File

@ -48,16 +48,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define M3D_NOWEIGHTS #define M3D_NOWEIGHTS
#define M3D_NOANIMATION #define M3D_NOANIMATION
#include <assimp/IOStreamBuffer.h>
#include <memory>
#include <assimp/DefaultIOSystem.h> #include <assimp/DefaultIOSystem.h>
#include <assimp/Importer.hpp> #include <assimp/IOStreamBuffer.h>
#include <assimp/scene.h>
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <memory>
#include "M3DImporter.h" #include "M3DImporter.h"
#include "M3DMaterials.h" #include "M3DMaterials.h"
#include "M3DWrapper.h"
// RESOURCES: // RESOURCES:
// https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md // https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md
@ -89,7 +91,7 @@ static const aiImporterDesc desc = {
"", "",
"", "",
"", "",
aiImporterFlags_SupportBinaryFlavour, aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
0, 0,
0, 0,
0, 0,
@ -97,53 +99,18 @@ static const aiImporterDesc desc = {
"m3d a3d" "m3d a3d"
}; };
// workaround: the SDK expects a C callback, but we want to use Assimp::IOSystem to implement that
extern "C" {
void* m3dimporter_pIOHandler;
unsigned char *m3dimporter_readfile(char *fn, unsigned int *size) {
ai_assert( nullptr != fn );
ai_assert( nullptr != size );
std::string file(fn);
std::unique_ptr<Assimp::IOStream> pStream(
(reinterpret_cast<Assimp::IOSystem*>(m3dimporter_pIOHandler))->Open( file, "rb"));
size_t fileSize = 0;
unsigned char *data = NULL;
// sometimes pStream is nullptr for some reason (should be an empty object returning nothing I guess)
if(pStream) {
fileSize = pStream->FileSize();
// should be allocated with malloc(), because the library will call free() to deallocate
data = (unsigned char*)malloc(fileSize);
if( !data || !pStream.get() || !fileSize || fileSize != pStream->Read(data,1,fileSize)) {
pStream.reset();
*size = 0;
// don't throw a deadly exception, it's not fatal if we can't read an external asset
return nullptr;
}
pStream.reset();
}
*size = (int)fileSize;
return data;
}
}
namespace Assimp { namespace Assimp {
using namespace std; using namespace std;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Default constructor // Default constructor
M3DImporter::M3DImporter() M3DImporter::M3DImporter() :
: mScene(nullptr) mScene(nullptr) {}
, m3d(nullptr) { }
// ------------------------------------------------------------------------------------------------
// Destructor.
M3DImporter::~M3DImporter() {}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns true, if file is a binary or ASCII Model 3D file. // Returns true, if file is a binary or ASCII Model 3D file.
bool M3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler , bool checkSig) const { bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
const std::string extension = GetExtension(pFile); const std::string extension = GetExtension(pFile);
if (extension == "m3d" || extension == "a3d") if (extension == "m3d" || extension == "a3d")
@ -159,9 +126,9 @@ bool M3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler , bool
const char* tokens[] = {"3DMO", "3dmo"}; const char* tokens[] = {"3DMO", "3dmo"};
return CheckMagicToken(pIOHandler,pFile,tokens,2,0,4); return CheckMagicToken(pIOHandler,pFile,tokens,2,0,4);
*/ */
std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile, "rb")); std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile, "rb"));
unsigned char data[4]; unsigned char data[4];
if(4 != pStream->Read(data,1,4)) { if (4 != pStream->Read(data, 1, 4)) {
return false; return false;
} }
return !memcmp(data, "3DMO", 4) /* bin */ || !memcmp(data, "3dmo", 4) /* ASCII */; return !memcmp(data, "3DMO", 4) /* bin */ || !memcmp(data, "3dmo", 4) /* ASCII */;
@ -170,81 +137,75 @@ bool M3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler , bool
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiImporterDesc* M3DImporter::GetInfo() const { const aiImporterDesc *M3DImporter::GetInfo() const {
return &desc; return &desc;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Model 3D import implementation // Model 3D import implementation
void M3DImporter::InternReadFile( const std::string &file, aiScene* pScene, IOSystem* pIOHandler) { void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
// Read file into memory // Read file into memory
std::unique_ptr<IOStream> pStream( pIOHandler->Open( file, "rb")); std::unique_ptr<IOStream> pStream(pIOHandler->Open(file, "rb"));
if( !pStream.get() ) { if (!pStream.get()) {
throw DeadlyImportError( "Failed to open file " + file + "." ); throw DeadlyImportError("Failed to open file " + file + ".");
} }
// Get the file-size and validate it, throwing an exception when fails // Get the file-size and validate it, throwing an exception when fails
size_t fileSize = pStream->FileSize(); size_t fileSize = pStream->FileSize();
if( fileSize < 8 ) { if (fileSize < 8) {
throw DeadlyImportError( "M3D-file " + file + " is too small." ); throw DeadlyImportError("M3D-file " + file + " is too small.");
} }
std::unique_ptr<unsigned char[]> _buffer (new unsigned char[fileSize]); std::vector<unsigned char> buffer(fileSize);
unsigned char *data( _buffer.get() ); if (fileSize != pStream->Read(buffer.data(), 1, fileSize)) {
if(fileSize != pStream->Read(data,1,fileSize)) { throw DeadlyImportError("Failed to read the file " + file + ".");
throw DeadlyImportError( "Failed to read the file " + file + "." );
} }
// Get the path for external assets // Get the path for external assets
std::string folderName( "./" ); std::string folderName("./");
std::string::size_type pos = file.find_last_of( "\\/" ); std::string::size_type pos = file.find_last_of("\\/");
if ( pos != std::string::npos ) { if (pos != std::string::npos) {
folderName = file.substr( 0, pos ); folderName = file.substr(0, pos);
if ( !folderName.empty() ) { if (!folderName.empty()) {
pIOHandler->PushDirectory( folderName ); pIOHandler->PushDirectory(folderName);
} }
} }
// pass this IOHandler to the C callback
m3dimporter_pIOHandler = pIOHandler;
//DefaultLogger::create("/dev/stderr", Logger::VERBOSE); //DefaultLogger::create("/dev/stderr", Logger::VERBOSE);
ASSIMP_LOG_DEBUG_F("M3D: loading ", file); ASSIMP_LOG_DEBUG_F("M3D: loading ", file);
// let the C SDK do the hard work for us // let the C SDK do the hard work for us
m3d = m3d_load(&data[0], m3dimporter_readfile, free, nullptr); M3DWrapper m3d(pIOHandler, buffer);
m3dimporter_pIOHandler = nullptr;
if( !m3d ) {
throw DeadlyImportError( "Unable to parse " + file + " as M3D." ); if (!m3d) {
throw DeadlyImportError("Unable to parse " + file + " as M3D.");
} }
// create the root node // create the root node
pScene->mRootNode = new aiNode; pScene->mRootNode = new aiNode;
pScene->mRootNode->mName = aiString(std::string(std::string(m3d->name))); pScene->mRootNode->mName = aiString(m3d.Name());
pScene->mRootNode->mTransformation = aiMatrix4x4(); pScene->mRootNode->mTransformation = aiMatrix4x4();
pScene->mRootNode->mNumChildren = 0; pScene->mRootNode->mNumChildren = 0;
mScene = pScene; mScene = pScene;
ASSIMP_LOG_DEBUG("M3D: root node " + std::string(m3d->name)); ASSIMP_LOG_DEBUG("M3D: root node " + m3d.Name());
// now we just have to fill up the Assimp structures in pScene // now we just have to fill up the Assimp structures in pScene
importMaterials(); importMaterials(m3d);
importTextures(); importTextures(m3d);
importBones(-1U, pScene->mRootNode); importBones(m3d, -1U, pScene->mRootNode);
importMeshes(); importMeshes(m3d);
importAnimations(); importAnimations(m3d);
// we don't need the SDK's version any more
m3d_free(m3d);
// Pop directory stack // Pop directory stack
if ( pIOHandler->StackSize() > 0 ) { if (pIOHandler->StackSize() > 0) {
pIOHandler->PopDirectory(); pIOHandler->PopDirectory();
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// convert materials. properties are converted using a static table in M3DMaterials.h // convert materials. properties are converted using a static table in M3DMaterials.h
void M3DImporter::importMaterials() void M3DImporter::importMaterials(const M3DWrapper &m3d_wrap) {
{
unsigned int i, j, k, l, n; unsigned int i, j, k, l, n;
m3dm_t *m; m3dm_t *m;
aiString name = aiString(AI_DEFAULT_MATERIAL_NAME); aiString name = aiString(AI_DEFAULT_MATERIAL_NAME);
@ -252,42 +213,43 @@ void M3DImporter::importMaterials()
ai_real f; ai_real f;
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d_wrap);
mScene->mNumMaterials = m3d->nummaterial + 1; mScene->mNumMaterials = m3d_wrap->nummaterial + 1;
mScene->mMaterials = new aiMaterial*[ m3d->nummaterial + 1 ]; mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials];
ASSIMP_LOG_DEBUG_F("M3D: importMaterials ", mScene->mNumMaterials); ASSIMP_LOG_DEBUG_F("M3D: importMaterials ", mScene->mNumMaterials);
// add a default material as first // add a default material as first
aiMaterial* mat = new aiMaterial; aiMaterial *mat = new aiMaterial;
mat->AddProperty( &name, AI_MATKEY_NAME ); mat->AddProperty(&name, AI_MATKEY_NAME);
c.a = 1.0; c.b = c.g = c.r = 0.6; c.a = 1.0f;
mat->AddProperty( &c, 1, AI_MATKEY_COLOR_DIFFUSE); c.b = c.g = c.r = 0.6f;
mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE);
mScene->mMaterials[0] = mat; mScene->mMaterials[0] = mat;
for(i = 0; i < m3d->nummaterial; i++) { for (i = 0; i < m3d_wrap->nummaterial; i++) {
m = &m3d->material[i]; m = &m3d_wrap->material[i];
aiMaterial* mat = new aiMaterial; aiMaterial *mat = new aiMaterial;
name.Set(std::string(m->name)); name.Set(std::string(m->name));
mat->AddProperty( &name, AI_MATKEY_NAME ); mat->AddProperty(&name, AI_MATKEY_NAME);
for(j = 0; j < m->numprop; j++) { for (j = 0; j < m->numprop; j++) {
// look up property type // look up property type
// 0 - 127 scalar values, // 0 - 127 scalar values,
// 128 - 255 the same properties but for texture maps // 128 - 255 the same properties but for texture maps
k = 256; k = 256;
for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) for (l = 0; l < sizeof(m3d_propertytypes) / sizeof(m3d_propertytypes[0]); l++)
if(m->prop[j].type == m3d_propertytypes[l].id || if (m->prop[j].type == m3d_propertytypes[l].id ||
m->prop[j].type == m3d_propertytypes[l].id + 128) { m->prop[j].type == m3d_propertytypes[l].id + 128) {
k = l; k = l;
break; break;
} }
// should never happen, but be safe than sorry // should never happen, but be safe than sorry
if(k == 256) continue; if (k == 256) continue;
// scalar properties // scalar properties
if(m->prop[j].type < 128 && aiProps[k].pKey) { if (m->prop[j].type < 128 && aiProps[k].pKey) {
switch(m3d_propertytypes[k].format) { switch (m3d_propertytypes[k].format) {
case m3dpf_color: case m3dpf_color:
c = mkColor(m->prop[j].value.color); c = mkColor(m->prop[j].value.color);
mat->AddProperty(&c, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index); mat->AddProperty(&c, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index);
@ -298,8 +260,8 @@ void M3DImporter::importMaterials()
break; break;
default: default:
n = m->prop[j].value.num; n = m->prop[j].value.num;
if(m->prop[j].type == m3dp_il) { if (m->prop[j].type == m3dp_il) {
switch(n) { switch (n) {
case 0: n = aiShadingMode_NoShading; break; case 0: n = aiShadingMode_NoShading; break;
case 2: n = aiShadingMode_Phong; break; case 2: n = aiShadingMode_Phong; break;
default: n = aiShadingMode_Gouraud; break; default: n = aiShadingMode_Gouraud; break;
@ -310,11 +272,11 @@ void M3DImporter::importMaterials()
} }
} }
// texture map properties // texture map properties
if(m->prop[j].type >= 128 && aiTxProps[k].pKey && if (m->prop[j].type >= 128 && aiTxProps[k].pKey &&
// extra check, should never happen, do we have the refered texture? // extra check, should never happen, do we have the refered texture?
m->prop[j].value.textureid < m3d->numtexture && m->prop[j].value.textureid < m3d_wrap->numtexture &&
m3d->texture[m->prop[j].value.textureid].name) { m3d_wrap->texture[m->prop[j].value.textureid].name) {
name.Set(std::string(std::string(m3d->texture[m->prop[j].value.textureid].name) + ".png")); name.Set(std::string(std::string(m3d_wrap->texture[m->prop[j].value.textureid].name) + ".png"));
mat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index); mat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index);
n = 0; n = 0;
mat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index); mat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index);
@ -326,43 +288,49 @@ void M3DImporter::importMaterials()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// import textures, this is the simplest of all // import textures, this is the simplest of all
void M3DImporter::importTextures() void M3DImporter::importTextures(const M3DWrapper &m3d) {
{
unsigned int i; unsigned int i;
const char *formatHint[] = { "rgba0800", "rgba0808", "rgba8880", "rgba8888" }; const char *formatHint[] = { "rgba0800", "rgba0808", "rgba8880", "rgba8888" };
m3dtx_t *t; m3dtx_t *t;
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
mScene->mNumTextures = m3d->numtexture; mScene->mNumTextures = m3d->numtexture;
ASSIMP_LOG_DEBUG_F("M3D: importTextures ", mScene->mNumTextures); ASSIMP_LOG_DEBUG_F("M3D: importTextures ", mScene->mNumTextures);
if(!m3d->numtexture) if (!m3d->numtexture)
return; return;
mScene->mTextures = new aiTexture*[m3d->numtexture]; mScene->mTextures = new aiTexture *[m3d->numtexture];
for(i = 0; i < m3d->numtexture; i++) { for (i = 0; i < m3d->numtexture; i++) {
unsigned int j, k; unsigned int j, k;
t = &m3d->texture[i]; t = &m3d->texture[i];
if(!t->w || !t->h || !t->f || !t->d) continue; if (!t->w || !t->h || !t->f || !t->d) continue;
aiTexture *tx = new aiTexture; aiTexture *tx = new aiTexture;
strcpy(tx->achFormatHint, formatHint[t->f - 1]); strcpy(tx->achFormatHint, formatHint[t->f - 1]);
tx->mFilename = aiString(std::string(t->name) + ".png"); tx->mFilename = aiString(std::string(t->name) + ".png");
tx->mWidth = t->w; tx->mWidth = t->w;
tx->mHeight = t->h; tx->mHeight = t->h;
tx->pcData = new aiTexel[ tx->mWidth*tx->mHeight ]; tx->pcData = new aiTexel[tx->mWidth * tx->mHeight];
for(j = k = 0; j < tx->mWidth*tx->mHeight; j++) { for (j = k = 0; j < tx->mWidth * tx->mHeight; j++) {
switch(t->f) { switch (t->f) {
case 1: tx->pcData[j].g = t->d[k++]; break; case 1: tx->pcData[j].g = t->d[k++]; break;
case 2: tx->pcData[j].g = t->d[k++]; tx->pcData[j].a = t->d[k++]; break; case 2:
tx->pcData[j].g = t->d[k++];
tx->pcData[j].a = t->d[k++];
break;
case 3: case 3:
tx->pcData[j].r = t->d[k++]; tx->pcData[j].g = t->d[k++]; tx->pcData[j].r = t->d[k++];
tx->pcData[j].b = t->d[k++]; tx->pcData[j].a = 255; tx->pcData[j].g = t->d[k++];
tx->pcData[j].b = t->d[k++];
tx->pcData[j].a = 255;
break; break;
case 4: case 4:
tx->pcData[j].r = t->d[k++]; tx->pcData[j].g = t->d[k++]; tx->pcData[j].r = t->d[k++];
tx->pcData[j].b = t->d[k++]; tx->pcData[j].a = t->d[k++]; tx->pcData[j].g = t->d[k++];
tx->pcData[j].b = t->d[k++];
tx->pcData[j].a = t->d[k++];
break; break;
} }
} }
@ -374,10 +342,9 @@ void M3DImporter::importTextures()
// this is tricky. M3D has a global vertex and UV list, and faces are indexing them // this is tricky. M3D has a global vertex and UV list, and faces are indexing them
// individually. In assimp there're per mesh vertex and UV lists, and they must be // individually. In assimp there're per mesh vertex and UV lists, and they must be
// indexed simultaneously. // indexed simultaneously.
void M3DImporter::importMeshes() void M3DImporter::importMeshes(const M3DWrapper &m3d) {
{
unsigned int i, j, k, l, numpoly = 3, lastMat = -2U; unsigned int i, j, k, l, numpoly = 3, lastMat = -2U;
std::vector<aiMesh*> *meshes = new std::vector<aiMesh*>(); std::vector<aiMesh *> *meshes = new std::vector<aiMesh *>();
std::vector<aiFace> *faces = nullptr; std::vector<aiFace> *faces = nullptr;
std::vector<aiVector3D> *vertices = nullptr; std::vector<aiVector3D> *vertices = nullptr;
std::vector<aiVector3D> *normals = nullptr; std::vector<aiVector3D> *normals = nullptr;
@ -387,17 +354,17 @@ void M3DImporter::importMeshes()
aiMesh *pMesh = nullptr; aiMesh *pMesh = nullptr;
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
ai_assert(mScene->mRootNode != nullptr); ai_assert(mScene->mRootNode != nullptr);
ASSIMP_LOG_DEBUG_F("M3D: importMeshes ", m3d->numface); ASSIMP_LOG_DEBUG_F("M3D: importMeshes ", m3d->numface);
for(i = 0; i < m3d->numface; i++) { for (i = 0; i < m3d->numface; i++) {
// we must switch mesh if material changes // we must switch mesh if material changes
if(lastMat != m3d->face[i].materialid) { if (lastMat != m3d->face[i].materialid) {
lastMat = m3d->face[i].materialid; lastMat = m3d->face[i].materialid;
if(pMesh && vertices && vertices->size() && faces && faces->size()) { if (pMesh && vertices && vertices->size() && faces && faces->size()) {
populateMesh(pMesh, faces, vertices, normals, texcoords, colors, vertexids); populateMesh(m3d, pMesh, faces, vertices, normals, texcoords, colors, vertexids);
meshes->push_back(pMesh); meshes->push_back(pMesh);
delete faces; delete faces;
delete vertices; delete vertices;
@ -420,9 +387,9 @@ void M3DImporter::importMeshes()
aiFace *pFace = new aiFace; aiFace *pFace = new aiFace;
pFace->mNumIndices = numpoly; pFace->mNumIndices = numpoly;
pFace->mIndices = new unsigned int[numpoly]; pFace->mIndices = new unsigned int[numpoly];
for(j = 0; j < numpoly; j++) { for (j = 0; j < numpoly; j++) {
aiVector3D pos, uv, norm; aiVector3D pos, uv, norm;
k = vertices->size(); k = static_cast<unsigned int>(vertices->size());
pFace->mIndices[j] = k; pFace->mIndices[j] = k;
l = m3d->face[i].vertex[j]; l = m3d->face[i].vertex[j];
pos.x = m3d->vertex[l].x; pos.x = m3d->vertex[l].x;
@ -431,20 +398,20 @@ void M3DImporter::importMeshes()
vertices->push_back(pos); vertices->push_back(pos);
colors->push_back(mkColor(m3d->vertex[l].color)); colors->push_back(mkColor(m3d->vertex[l].color));
// add a bone to temporary vector // add a bone to temporary vector
if(m3d->vertex[l].skinid != -1U &&m3d->vertex[l].skinid != -2U && m3d->skin && m3d->bone) { if (m3d->vertex[l].skinid != -1U && m3d->vertex[l].skinid != -2U && m3d->skin && m3d->bone) {
// this is complicated, because M3D stores a list of bone id / weight pairs per // this is complicated, because M3D stores a list of bone id / weight pairs per
// vertex but assimp uses lists of local vertex id/weight pairs per local bone list // vertex but assimp uses lists of local vertex id/weight pairs per local bone list
vertexids->push_back(l); vertexids->push_back(l);
} }
l = m3d->face[i].texcoord[j]; l = m3d->face[i].texcoord[j];
if(l != -1U) { if (l != -1U) {
uv.x = m3d->tmap[l].u; uv.x = m3d->tmap[l].u;
uv.y = m3d->tmap[l].v; uv.y = m3d->tmap[l].v;
uv.z = 0.0; uv.z = 0.0;
texcoords->push_back(uv); texcoords->push_back(uv);
} }
l = m3d->face[i].normal[j]; l = m3d->face[i].normal[j];
if(l != -1U) { if (l != -1U) {
norm.x = m3d->vertex[l].x; norm.x = m3d->vertex[l].x;
norm.y = m3d->vertex[l].y; norm.y = m3d->vertex[l].y;
norm.z = m3d->vertex[l].z; norm.z = m3d->vertex[l].z;
@ -455,58 +422,57 @@ void M3DImporter::importMeshes()
delete pFace; delete pFace;
} }
// if there's data left in the temporary vectors, flush them // if there's data left in the temporary vectors, flush them
if(pMesh && vertices->size() && faces->size()) { if (pMesh && vertices->size() && faces->size()) {
populateMesh(pMesh, faces, vertices, normals, texcoords, colors, vertexids); populateMesh(m3d, pMesh, faces, vertices, normals, texcoords, colors, vertexids);
meshes->push_back(pMesh); meshes->push_back(pMesh);
} }
// create global mesh list in scene // create global mesh list in scene
mScene->mNumMeshes = meshes->size(); mScene->mNumMeshes = static_cast<unsigned int>(meshes->size());
mScene->mMeshes = new aiMesh*[mScene->mNumMeshes]; mScene->mMeshes = new aiMesh *[mScene->mNumMeshes];
std::copy(meshes->begin(), meshes->end(), mScene->mMeshes); std::copy(meshes->begin(), meshes->end(), mScene->mMeshes);
// create mesh indeces in root node // create mesh indeces in root node
mScene->mRootNode->mNumMeshes = meshes->size(); mScene->mRootNode->mNumMeshes = static_cast<unsigned int>(meshes->size());
mScene->mRootNode->mMeshes = new unsigned int[meshes->size()]; mScene->mRootNode->mMeshes = new unsigned int[meshes->size()];
for(i = 0; i < meshes->size(); i++) { for (i = 0; i < meshes->size(); i++) {
mScene->mRootNode->mMeshes[i] = i; mScene->mRootNode->mMeshes[i] = i;
} }
delete meshes; delete meshes;
if(faces) delete faces; if (faces) delete faces;
if(vertices) delete vertices; if (vertices) delete vertices;
if(normals) delete normals; if (normals) delete normals;
if(texcoords) delete texcoords; if (texcoords) delete texcoords;
if(colors) delete colors; if (colors) delete colors;
if(vertexids) delete vertexids; if (vertexids) delete vertexids;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// a reentrant node parser. Otherwise this is simple // a reentrant node parser. Otherwise this is simple
void M3DImporter::importBones(unsigned int parentid, aiNode *pParent) void M3DImporter::importBones(const M3DWrapper &m3d, unsigned int parentid, aiNode *pParent) {
{
unsigned int i, n; unsigned int i, n;
ai_assert(pParent != nullptr); ai_assert(pParent != nullptr);
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
ASSIMP_LOG_DEBUG_F("M3D: importBones ", m3d->numbone, " parentid ", (int)parentid); ASSIMP_LOG_DEBUG_F("M3D: importBones ", m3d->numbone, " parentid ", (int)parentid);
for(n = 0, i = parentid + 1; i < m3d->numbone; i++) for (n = 0, i = parentid + 1; i < m3d->numbone; i++)
if(m3d->bone[i].parent == parentid) n++; if (m3d->bone[i].parent == parentid) n++;
pParent->mChildren = new aiNode*[n]; pParent->mChildren = new aiNode *[n];
for(i = parentid + 1; i < m3d->numbone; i++) { for (i = parentid + 1; i < m3d->numbone; i++) {
if(m3d->bone[i].parent == parentid) { if (m3d->bone[i].parent == parentid) {
aiNode *pChild = new aiNode; aiNode *pChild = new aiNode;
pChild->mParent = pParent; pChild->mParent = pParent;
pChild->mName = aiString(std::string(m3d->bone[i].name)); pChild->mName = aiString(std::string(m3d->bone[i].name));
convertPose(&pChild->mTransformation, m3d->bone[i].pos, m3d->bone[i].ori); convertPose(m3d, &pChild->mTransformation, m3d->bone[i].pos, m3d->bone[i].ori);
pChild->mNumChildren = 0; pChild->mNumChildren = 0;
pParent->mChildren[pParent->mNumChildren] = pChild; pParent->mChildren[pParent->mNumChildren] = pChild;
pParent->mNumChildren++; pParent->mNumChildren++;
importBones(i, pChild); importBones(m3d, i, pChild);
} }
} }
} }
@ -515,24 +481,23 @@ void M3DImporter::importBones(unsigned int parentid, aiNode *pParent)
// this is another headache. M3D stores list of changed bone id/position/orientation triplets and // this is another headache. M3D stores list of changed bone id/position/orientation triplets and
// a timestamp per frame, but assimp needs timestamp and lists of position, orientation lists per // a timestamp per frame, but assimp needs timestamp and lists of position, orientation lists per
// bone, so we have to convert between the two conceptually different representation forms // bone, so we have to convert between the two conceptually different representation forms
void M3DImporter::importAnimations() void M3DImporter::importAnimations(const M3DWrapper &m3d) {
{
unsigned int i, j, k, l, pos, ori; unsigned int i, j, k, l, pos, ori;
double t; double t;
m3da_t *a; m3da_t *a;
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
mScene->mNumAnimations = m3d->numaction; mScene->mNumAnimations = m3d->numaction;
ASSIMP_LOG_DEBUG_F("M3D: importAnimations ", mScene->mNumAnimations); ASSIMP_LOG_DEBUG_F("M3D: importAnimations ", mScene->mNumAnimations);
if(!m3d->numaction || !m3d->numbone) if (!m3d->numaction || !m3d->numbone)
return; return;
mScene->mAnimations = new aiAnimation*[m3d->numaction]; mScene->mAnimations = new aiAnimation *[m3d->numaction];
for(i = 0; i < m3d->numaction; i++) { for (i = 0; i < m3d->numaction; i++) {
a = &m3d->action[i]; a = &m3d->action[i];
aiAnimation *pAnim = new aiAnimation; aiAnimation *pAnim = new aiAnimation;
pAnim->mName = aiString(std::string(a->name)); pAnim->mName = aiString(std::string(a->name));
@ -540,8 +505,8 @@ void M3DImporter::importAnimations()
pAnim->mTicksPerSecond = 100; pAnim->mTicksPerSecond = 100;
// now we know how many bones are referenced in this animation // now we know how many bones are referenced in this animation
pAnim->mNumChannels = m3d->numbone; pAnim->mNumChannels = m3d->numbone;
pAnim->mChannels = new aiNodeAnim*[pAnim->mNumChannels]; pAnim->mChannels = new aiNodeAnim *[pAnim->mNumChannels];
for(l = 0; l < m3d->numbone; l++) { for (l = 0; l < m3d->numbone; l++) {
unsigned int n; unsigned int n;
pAnim->mChannels[l] = new aiNodeAnim; pAnim->mChannels[l] = new aiNodeAnim;
pAnim->mChannels[l]->mNodeName = aiString(std::string(m3d->bone[l].name)); pAnim->mChannels[l]->mNodeName = aiString(std::string(m3d->bone[l].name));
@ -551,10 +516,10 @@ void M3DImporter::importAnimations()
pAnim->mChannels[l]->mRotationKeys = new aiQuatKey[a->numframe]; pAnim->mChannels[l]->mRotationKeys = new aiQuatKey[a->numframe];
pos = m3d->bone[l].pos; pos = m3d->bone[l].pos;
ori = m3d->bone[l].ori; ori = m3d->bone[l].ori;
for(j = n = 0; j < a->numframe; j++) { for (j = n = 0; j < a->numframe; j++) {
t = ((double)a->frame[j].msec) / 10; t = ((double)a->frame[j].msec) / 10;
for(k = 0; k < a->frame[j].numtransform; k++) { for (k = 0; k < a->frame[j].numtransform; k++) {
if(a->frame[j].transform[k].boneid == l) { if (a->frame[j].transform[k].boneid == l) {
pos = a->frame[j].transform[k].pos; pos = a->frame[j].transform[k].pos;
ori = a->frame[j].transform[k].ori; ori = a->frame[j].transform[k].ori;
} }
@ -570,8 +535,8 @@ void M3DImporter::importAnimations()
pAnim->mChannels[l]->mRotationKeys[j].mValue.x = q->x; pAnim->mChannels[l]->mRotationKeys[j].mValue.x = q->x;
pAnim->mChannels[l]->mRotationKeys[j].mValue.y = q->y; pAnim->mChannels[l]->mRotationKeys[j].mValue.y = q->y;
pAnim->mChannels[l]->mRotationKeys[j].mValue.z = q->z; pAnim->mChannels[l]->mRotationKeys[j].mValue.z = q->z;
}// foreach frame } // foreach frame
}// foreach bones } // foreach bones
mScene->mAnimations[i] = pAnim; mScene->mAnimations[i] = pAnim;
} }
} }
@ -580,72 +545,83 @@ void M3DImporter::importAnimations()
// convert uint32_t into aiColor4D // convert uint32_t into aiColor4D
aiColor4D M3DImporter::mkColor(uint32_t c) { aiColor4D M3DImporter::mkColor(uint32_t c) {
aiColor4D color; aiColor4D color;
color.a = ((float)((c >> 24)&0xff)) / 255; color.a = ((float)((c >> 24) & 0xff)) / 255;
color.b = ((float)((c >> 16)&0xff)) / 255; color.b = ((float)((c >> 16) & 0xff)) / 255;
color.g = ((float)((c >> 8)&0xff)) / 255; color.g = ((float)((c >> 8) & 0xff)) / 255;
color.r = ((float)((c >> 0)&0xff)) / 255; color.r = ((float)((c >> 0) & 0xff)) / 255;
return color; return color;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// convert a position id and orientation id into a 4 x 4 transformation matrix // convert a position id and orientation id into a 4 x 4 transformation matrix
void M3DImporter::convertPose(aiMatrix4x4 *m, unsigned int posid, unsigned int orientid) void M3DImporter::convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid) {
{
ai_assert(m != nullptr); ai_assert(m != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
ai_assert(posid != -1U && posid < m3d->numvertex); ai_assert(posid != -1U && posid < m3d->numvertex);
ai_assert(orientid != -1U && orientid < m3d->numvertex); ai_assert(orientid != -1U && orientid < m3d->numvertex);
m3dv_t *p = &m3d->vertex[posid]; m3dv_t *p = &m3d->vertex[posid];
m3dv_t *q = &m3d->vertex[orientid]; m3dv_t *q = &m3d->vertex[orientid];
/* quaternion to matrix. Do NOT use aiQuaternion to aiMatrix3x3, gives bad results */ /* quaternion to matrix. Do NOT use aiQuaternion to aiMatrix3x3, gives bad results */
if(q->x == 0.0 && q->y == 0.0 && q->z >= 0.7071065 && q->z <= 0.7071075 && q->w == 0.0) { if (q->x == 0.0 && q->y == 0.0 && q->z >= 0.7071065 && q->z <= 0.7071075 && q->w == 0.0) {
m->a2 = m->a3 = m->b1 = m->b3 = m->c1 = m->c2 = 0.0; m->a2 = m->a3 = m->b1 = m->b3 = m->c1 = m->c2 = 0.0;
m->a1 = m->b2 = m->c3 = -1.0; m->a1 = m->b2 = m->c3 = -1.0;
} else { } else {
m->a1 = 1 - 2 * (q->y * q->y + q->z * q->z); if(m->a1 > -M3D_EPSILON && m->a1 < M3D_EPSILON) m->a1 = 0.0; m->a1 = 1 - 2 * (q->y * q->y + q->z * q->z);
m->a2 = 2 * (q->x * q->y - q->z * q->w); if(m->a2 > -M3D_EPSILON && m->a2 < M3D_EPSILON) m->a2 = 0.0; if (m->a1 > -M3D_EPSILON && m->a1 < M3D_EPSILON) m->a1 = 0.0;
m->a3 = 2 * (q->x * q->z + q->y * q->w); if(m->a3 > -M3D_EPSILON && m->a3 < M3D_EPSILON) m->a3 = 0.0; m->a2 = 2 * (q->x * q->y - q->z * q->w);
m->b1 = 2 * (q->x * q->y + q->z * q->w); if(m->b1 > -M3D_EPSILON && m->b1 < M3D_EPSILON) m->b1 = 0.0; if (m->a2 > -M3D_EPSILON && m->a2 < M3D_EPSILON) m->a2 = 0.0;
m->b2 = 1 - 2 * (q->x * q->x + q->z * q->z); if(m->b2 > -M3D_EPSILON && m->b2 < M3D_EPSILON) m->b2 = 0.0; m->a3 = 2 * (q->x * q->z + q->y * q->w);
m->b3 = 2 * (q->y * q->z - q->x * q->w); if(m->b3 > -M3D_EPSILON && m->b3 < M3D_EPSILON) m->b3 = 0.0; if (m->a3 > -M3D_EPSILON && m->a3 < M3D_EPSILON) m->a3 = 0.0;
m->c1 = 2 * (q->x * q->z - q->y * q->w); if(m->c1 > -M3D_EPSILON && m->c1 < M3D_EPSILON) m->c1 = 0.0; m->b1 = 2 * (q->x * q->y + q->z * q->w);
m->c2 = 2 * (q->y * q->z + q->x * q->w); if(m->c2 > -M3D_EPSILON && m->c2 < M3D_EPSILON) m->c2 = 0.0; if (m->b1 > -M3D_EPSILON && m->b1 < M3D_EPSILON) m->b1 = 0.0;
m->c3 = 1 - 2 * (q->x * q->x + q->y * q->y); if(m->c3 > -M3D_EPSILON && m->c3 < M3D_EPSILON) m->c3 = 0.0; m->b2 = 1 - 2 * (q->x * q->x + q->z * q->z);
if (m->b2 > -M3D_EPSILON && m->b2 < M3D_EPSILON) m->b2 = 0.0;
m->b3 = 2 * (q->y * q->z - q->x * q->w);
if (m->b3 > -M3D_EPSILON && m->b3 < M3D_EPSILON) m->b3 = 0.0;
m->c1 = 2 * (q->x * q->z - q->y * q->w);
if (m->c1 > -M3D_EPSILON && m->c1 < M3D_EPSILON) m->c1 = 0.0;
m->c2 = 2 * (q->y * q->z + q->x * q->w);
if (m->c2 > -M3D_EPSILON && m->c2 < M3D_EPSILON) m->c2 = 0.0;
m->c3 = 1 - 2 * (q->x * q->x + q->y * q->y);
if (m->c3 > -M3D_EPSILON && m->c3 < M3D_EPSILON) m->c3 = 0.0;
} }
/* set translation */ /* set translation */
m->a4 = p->x; m->b4 = p->y; m->c4 = p->z; m->a4 = p->x;
m->b4 = p->y;
m->c4 = p->z;
m->d1 = 0; m->d2 = 0; m->d3 = 0; m->d4 = 1; m->d1 = 0;
m->d2 = 0;
m->d3 = 0;
m->d4 = 1;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// find a node by name // find a node by name
aiNode *M3DImporter::findNode(aiNode *pNode, aiString name) aiNode *M3DImporter::findNode(aiNode *pNode, aiString name) {
{
unsigned int i; unsigned int i;
ai_assert(pNode != nullptr); ai_assert(pNode != nullptr);
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
if(pNode->mName == name) if (pNode->mName == name)
return pNode; return pNode;
for(i = 0; i < pNode->mNumChildren; i++) { for (i = 0; i < pNode->mNumChildren; i++) {
aiNode *pChild = findNode(pNode->mChildren[i], name); aiNode *pChild = findNode(pNode->mChildren[i], name);
if(pChild) return pChild; if (pChild) return pChild;
} }
return nullptr; return nullptr;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// fills up offsetmatrix in mBones // fills up offsetmatrix in mBones
void M3DImporter::calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m) void M3DImporter::calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m) {
{
ai_assert(pNode != nullptr); ai_assert(pNode != nullptr);
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
if(pNode->mParent) { if (pNode->mParent) {
calculateOffsetMatrix(pNode->mParent, m); calculateOffsetMatrix(pNode->mParent, m);
*m *= pNode->mTransformation; *m *= pNode->mTransformation;
} else { } else {
@ -657,7 +633,7 @@ void M3DImporter::calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m)
// because M3D has a global mesh, global vertex ids and stores materialid on the face, we need // because M3D has a global mesh, global vertex ids and stores materialid on the face, we need
// temporary lists to collect data for an aiMesh, which requires local arrays and local indeces // temporary lists to collect data for an aiMesh, which requires local arrays and local indeces
// this function fills up an aiMesh with those temporary lists // this function fills up an aiMesh with those temporary lists
void M3DImporter::populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *vertices, void M3DImporter::populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *vertices,
std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors, std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors,
std::vector<unsigned int> *vertexids) { std::vector<unsigned int> *vertexids) {
@ -668,28 +644,28 @@ void M3DImporter::populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::v
ai_assert(texcoords != nullptr); ai_assert(texcoords != nullptr);
ai_assert(colors != nullptr); ai_assert(colors != nullptr);
ai_assert(vertexids != nullptr); ai_assert(vertexids != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
ASSIMP_LOG_DEBUG_F("M3D: populateMesh numvertices ", vertices->size(), " numfaces ", faces->size(), ASSIMP_LOG_DEBUG_F("M3D: populateMesh numvertices ", vertices->size(), " numfaces ", faces->size(),
" numnormals ", normals->size(), " numtexcoord ", texcoords->size(), " numbones ", m3d->numbone); " numnormals ", normals->size(), " numtexcoord ", texcoords->size(), " numbones ", m3d->numbone);
if(vertices->size() && faces->size()) { if (vertices->size() && faces->size()) {
pMesh->mNumFaces = faces->size(); pMesh->mNumFaces = static_cast<unsigned int>(faces->size());
pMesh->mFaces = new aiFace[pMesh->mNumFaces]; pMesh->mFaces = new aiFace[pMesh->mNumFaces];
std::copy(faces->begin(), faces->end(), pMesh->mFaces); std::copy(faces->begin(), faces->end(), pMesh->mFaces);
pMesh->mNumVertices = vertices->size(); pMesh->mNumVertices = static_cast<unsigned int>(vertices->size());
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
std::copy(vertices->begin(), vertices->end(), pMesh->mVertices); std::copy(vertices->begin(), vertices->end(), pMesh->mVertices);
if(normals->size() == vertices->size()) { if (normals->size() == vertices->size()) {
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
std::copy(normals->begin(), normals->end(), pMesh->mNormals); std::copy(normals->begin(), normals->end(), pMesh->mNormals);
} }
if(texcoords->size() == vertices->size()) { if (texcoords->size() == vertices->size()) {
pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices]; pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices];
std::copy(texcoords->begin(), texcoords->end(), pMesh->mTextureCoords[0]); std::copy(texcoords->begin(), texcoords->end(), pMesh->mTextureCoords[0]);
pMesh->mNumUVComponents[0] = 2; pMesh->mNumUVComponents[0] = 2;
} }
if(colors->size() == vertices->size()) { if (colors->size() == vertices->size()) {
pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices]; pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
std::copy(colors->begin(), colors->end(), pMesh->mColors[0]); std::copy(colors->begin(), colors->end(), pMesh->mColors[0]);
} }
@ -697,30 +673,30 @@ void M3DImporter::populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::v
// vertex but assimp uses lists of local vertex id/weight pairs per local bone list // vertex but assimp uses lists of local vertex id/weight pairs per local bone list
pMesh->mNumBones = m3d->numbone; pMesh->mNumBones = m3d->numbone;
/* we need aiBone with mOffsetMatrix for bones without weights as well */ /* we need aiBone with mOffsetMatrix for bones without weights as well */
if(pMesh->mNumBones) { if (pMesh->mNumBones) {
pMesh->mBones = new aiBone*[pMesh->mNumBones]; pMesh->mBones = new aiBone *[pMesh->mNumBones];
for(unsigned int i = 0; i < m3d->numbone; i++) { for (unsigned int i = 0; i < m3d->numbone; i++) {
aiNode *pNode; aiNode *pNode;
pMesh->mBones[i] = new aiBone; pMesh->mBones[i] = new aiBone;
pMesh->mBones[i]->mName = aiString(std::string(m3d->bone[i].name)); pMesh->mBones[i]->mName = aiString(std::string(m3d->bone[i].name));
pMesh->mBones[i]->mNumWeights = 0; pMesh->mBones[i]->mNumWeights = 0;
pNode = findNode(mScene->mRootNode, pMesh->mBones[i]->mName); pNode = findNode(mScene->mRootNode, pMesh->mBones[i]->mName);
if(pNode) { if (pNode) {
calculateOffsetMatrix(pNode, &pMesh->mBones[i]->mOffsetMatrix); calculateOffsetMatrix(pNode, &pMesh->mBones[i]->mOffsetMatrix);
pMesh->mBones[i]->mOffsetMatrix.Inverse(); pMesh->mBones[i]->mOffsetMatrix.Inverse();
} else } else
pMesh->mBones[i]->mOffsetMatrix = aiMatrix4x4(); pMesh->mBones[i]->mOffsetMatrix = aiMatrix4x4();
} }
if(vertexids->size()) { if (vertexids->size()) {
unsigned int i, j; unsigned int i, j;
// first count how many vertices we have per bone // first count how many vertices we have per bone
for(i = 0; i < vertexids->size(); i++) { for (i = 0; i < vertexids->size(); i++) {
unsigned int s = m3d->vertex[vertexids->at(i)].skinid; unsigned int s = m3d->vertex[vertexids->at(i)].skinid;
if(s != -1U && s!= -2U) { if (s != -1U && s != -2U) {
for(unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) { for (unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) {
aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name)); aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name));
for(j = 0; j < pMesh->mNumBones; j++) { for (j = 0; j < pMesh->mNumBones; j++) {
if(pMesh->mBones[j]->mName == name) { if (pMesh->mBones[j]->mName == name) {
pMesh->mBones[j]->mNumWeights++; pMesh->mBones[j]->mNumWeights++;
break; break;
} }
@ -729,21 +705,21 @@ void M3DImporter::populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::v
} }
} }
// allocate mWeights // allocate mWeights
for(j = 0; j < pMesh->mNumBones; j++) { for (j = 0; j < pMesh->mNumBones; j++) {
aiBone *pBone = pMesh->mBones[j]; aiBone *pBone = pMesh->mBones[j];
if(pBone->mNumWeights) { if (pBone->mNumWeights) {
pBone->mWeights = new aiVertexWeight[pBone->mNumWeights]; pBone->mWeights = new aiVertexWeight[pBone->mNumWeights];
pBone->mNumWeights = 0; pBone->mNumWeights = 0;
} }
} }
// fill up with data // fill up with data
for(i = 0; i < vertexids->size(); i++) { for (i = 0; i < vertexids->size(); i++) {
unsigned int s = m3d->vertex[vertexids->at(i)].skinid; unsigned int s = m3d->vertex[vertexids->at(i)].skinid;
if(s != -1U && s!= -2U) { if (s != -1U && s != -2U) {
for(unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) { for (unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) {
aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name)); aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name));
for(j = 0; j < pMesh->mNumBones; j++) { for (j = 0; j < pMesh->mNumBones; j++) {
if(pMesh->mBones[j]->mName == name) { if (pMesh->mBones[j]->mName == name) {
aiBone *pBone = pMesh->mBones[j]; aiBone *pBone = pMesh->mBones[j];
pBone->mWeights[pBone->mNumWeights].mVertexId = i; pBone->mWeights[pBone->mNumWeights].mVertexId = i;
pBone->mWeights[pBone->mNumWeights].mWeight = m3d->skin[s].weight[k]; pBone->mWeights[pBone->mNumWeights].mWeight = m3d->skin[s].weight[k];

View File

@ -48,7 +48,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
#include "m3d.h"
#include <assimp/BaseImporter.h> #include <assimp/BaseImporter.h>
#include <assimp/material.h> #include <assimp/material.h>
#include <vector> #include <vector>
@ -60,41 +59,39 @@ struct aiFace;
namespace Assimp { namespace Assimp {
class M3DWrapper;
class M3DImporter : public BaseImporter { class M3DImporter : public BaseImporter {
public: public:
/// \brief Default constructor /// \brief Default constructor
M3DImporter(); M3DImporter();
/// \brief Destructor
~M3DImporter();
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, bool checkSig) const; bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const;
private: private:
aiScene* mScene; // the scene to import to aiScene *mScene = nullptr; // the scene to import to
m3d_t *m3d; // model for the C library to convert from
//! \brief Appends the supported extension. //! \brief Appends the supported extension.
const aiImporterDesc* GetInfo () const; const aiImporterDesc *GetInfo() const;
//! \brief File import implementation. //! \brief File import implementation.
void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
void importMaterials(); void importMaterials(const M3DWrapper &m3d);
void importTextures(); void importTextures(const M3DWrapper &m3d);
void importMeshes(); void importMeshes(const M3DWrapper &m3d);
void importBones(unsigned int parentid, aiNode *pParent); void importBones(const M3DWrapper &m3d, unsigned int parentid, aiNode *pParent);
void importAnimations(); void importAnimations(const M3DWrapper &m3d);
// helper functions // helper functions
aiColor4D mkColor(uint32_t c); aiColor4D mkColor(uint32_t c);
void convertPose(aiMatrix4x4 *m, unsigned int posid, unsigned int orientid); void convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid);
aiNode *findNode(aiNode *pNode, aiString name); aiNode *findNode(aiNode *pNode, aiString name);
void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m); void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m);
void populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *verteces, void populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *verteces,
std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors, std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors,
std::vector<unsigned int> *vertexids); std::vector<unsigned int> *vertexids);
}; };

View File

@ -0,0 +1,142 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
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 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.
----------------------------------------------------------------------
*/
#if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER
#include "M3DWrapper.h"
#include <assimp/DefaultIOSystem.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/ai_assert.h>
#ifndef AI_M3D_USE_STDMUTEX
#if (__cplusplus >= 201103L) || (_MSC_VER >= 1900) // C++11 and MSVC 2015 onwards
#define AI_M3D_USE_STDMUTEX 1
#else
#define AI_M3D_USE_STDMUTEX 0
#endif
#endif
#if AI_M3D_USE_STDMUTEX
#include <mutex>
std::mutex file_mutex;
#endif
// workaround: the M3D SDK expects a C callback, but we want to use Assimp::IOSystem to implement that
// This makes it non-rentrant so lock a mutex (requires C++11)
extern "C" {
void *m3dimporter_pIOHandler;
unsigned char *m3dimporter_readfile(char *fn, unsigned int *size) {
ai_assert(nullptr != fn);
ai_assert(nullptr != size);
std::string file(fn);
std::unique_ptr<Assimp::IOStream> pStream(
(reinterpret_cast<Assimp::IOSystem *>(m3dimporter_pIOHandler))->Open(file, "rb"));
size_t fileSize = 0;
unsigned char *data = NULL;
// sometimes pStream is nullptr for some reason (should be an empty object returning nothing I guess)
if (pStream) {
fileSize = pStream->FileSize();
// should be allocated with malloc(), because the library will call free() to deallocate
data = (unsigned char *)malloc(fileSize);
if (!data || !pStream.get() || !fileSize || fileSize != pStream->Read(data, 1, fileSize)) {
pStream.reset();
*size = 0;
// don't throw a deadly exception, it's not fatal if we can't read an external asset
return nullptr;
}
pStream.reset();
}
*size = (int)fileSize;
return data;
}
}
namespace Assimp {
M3DWrapper::M3DWrapper() {
// use malloc() here because m3d_free() will call free()
m3d_ = (m3d_t *)calloc(1, sizeof(m3d_t));
}
M3DWrapper::M3DWrapper(IOSystem *pIOHandler, const std::vector<unsigned char> &buffer) {
#if AI_M3D_USE_STDMUTEX
// M3D is NOT thread-safe, so lock the global mutex
const std::lock_guard<std::mutex> lock(file_mutex);
#endif
// pass this IOHandler to the C callback
m3dimporter_pIOHandler = pIOHandler;
m3d_ = m3d_load(const_cast<unsigned char *>(buffer.data()), m3dimporter_readfile, free, nullptr);
// Clear the C callback
m3dimporter_pIOHandler = nullptr;
}
M3DWrapper::~M3DWrapper() {
reset();
}
void M3DWrapper::reset() {
ClearSave();
if (m3d_)
m3d_free(m3d_);
m3d_ = nullptr;
}
unsigned char *M3DWrapper::Save(int quality, int flags, unsigned int &size) {
#if (!(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER))
ClearSave();
saved_output_ = m3d_save(m3d_, quality, flags, &size);
return saved_output_;
#else
return nullptr;
#endif
}
void M3DWrapper::ClearSave() {
if (saved_output_)
M3D_FREE(saved_output_);
saved_output_ = nullptr;
}
} // namespace Assimp
#endif

View File

@ -0,0 +1,97 @@
#pragma once
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
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 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 M3DWrapper.h
* @brief Declares a class to wrap the C m3d SDK
*/
#ifndef AI_M3DWRAPPER_H_INC
#define AI_M3DWRAPPER_H_INC
#if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER
#include <memory>
#include <vector>
#include <string>
#include "m3d.h"
namespace Assimp {
class IOSystem;
class M3DWrapper {
m3d_t *m3d_ = nullptr;
unsigned char *saved_output_ = nullptr;
public:
// Construct an empty M3D model
explicit M3DWrapper();
// Construct an M3D model from provided buffer
// NOTE: The m3d.h SDK function does not mark the data as const. Have assumed it does not write.
// BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN
explicit M3DWrapper(IOSystem *pIOHandler, const std::vector<unsigned char> &buffer);
~M3DWrapper();
void reset();
// Name
inline std::string Name() const {
if (m3d_) return std::string(m3d_->name);
return std::string();
}
// Execute a save
unsigned char *Save(int quality, int flags, unsigned int &size);
void ClearSave();
inline explicit operator bool() const { return m3d_ != nullptr; }
// Allow direct access to M3D API
inline m3d_t *operator->() const { return m3d_; }
inline m3d_t *M3D() const { return m3d_; }
};
} // namespace Assimp
#endif
#endif // AI_M3DWRAPPER_H_INC

View File

@ -137,8 +137,9 @@ public:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
void Execute( aiScene* pScene); void Execute( aiScene* pScene);
protected: public:
void ProcessMesh( aiMesh* pMesh); /** Some other types of post-processing require winding order flips */
static void ProcessMesh( aiMesh* pMesh);
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -43,13 +43,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Implementation of the aiProcess_OptimizGraph step * @brief Implementation of the aiProcess_OptimizGraph step
*/ */
#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS #ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
#include "OptimizeGraph.h" #include "OptimizeGraph.h"
#include "ProcessHelper.h" #include "ProcessHelper.h"
#include <assimp/SceneCombiner.h> #include "ConvertToLHProcess.h"
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
#include <assimp/SceneCombiner.h>
#include <stdio.h> #include <stdio.h>
using namespace Assimp; using namespace Assimp;
@ -60,21 +60,21 @@ using namespace Assimp;
* The unhashed variant should be faster, except for *very* large data sets * The unhashed variant should be faster, except for *very* large data sets
*/ */
#ifdef AI_OG_USE_HASHING #ifdef AI_OG_USE_HASHING
// Use our standard hashing function to compute the hash // Use our standard hashing function to compute the hash
# define AI_OG_GETKEY(str) SuperFastHash(str.data,str.length) #define AI_OG_GETKEY(str) SuperFastHash(str.data, str.length)
#else #else
// Otherwise hope that std::string will utilize a static buffer // Otherwise hope that std::string will utilize a static buffer
// for shorter node names. This would avoid endless heap copying. // for shorter node names. This would avoid endless heap copying.
# define AI_OG_GETKEY(str) std::string(str.data) #define AI_OG_GETKEY(str) std::string(str.data)
#endif #endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
OptimizeGraphProcess::OptimizeGraphProcess() OptimizeGraphProcess::OptimizeGraphProcess() :
: mScene() mScene(),
, nodes_in() nodes_in(),
, nodes_out() nodes_out(),
, count_merged() { count_merged() {
// empty // empty
} }
@ -86,33 +86,33 @@ OptimizeGraphProcess::~OptimizeGraphProcess() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const { bool OptimizeGraphProcess::IsActive(unsigned int pFlags) const {
return (0 != (pFlags & aiProcess_OptimizeGraph)); return (0 != (pFlags & aiProcess_OptimizeGraph));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Setup properties for the post-processing step // Setup properties for the post-processing step
void OptimizeGraphProcess::SetupProperties(const Importer* pImp) { void OptimizeGraphProcess::SetupProperties(const Importer *pImp) {
// Get value of AI_CONFIG_PP_OG_EXCLUDE_LIST // Get value of AI_CONFIG_PP_OG_EXCLUDE_LIST
std::string tmp = pImp->GetPropertyString(AI_CONFIG_PP_OG_EXCLUDE_LIST,""); std::string tmp = pImp->GetPropertyString(AI_CONFIG_PP_OG_EXCLUDE_LIST, "");
AddLockedNodeList(tmp); AddLockedNodeList(tmp);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Collect new children // Collect new children
void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& nodes) { void OptimizeGraphProcess::CollectNewChildren(aiNode *nd, std::list<aiNode *> &nodes) {
nodes_in += nd->mNumChildren; nodes_in += nd->mNumChildren;
// Process children // Process children
std::list<aiNode*> child_nodes; std::list<aiNode *> child_nodes;
for (unsigned int i = 0; i < nd->mNumChildren; ++i) { for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
CollectNewChildren(nd->mChildren[i],child_nodes); CollectNewChildren(nd->mChildren[i], child_nodes);
nd->mChildren[i] = nullptr; nd->mChildren[i] = nullptr;
} }
// Check whether we need this node; if not we can replace it by our own children (warn, danger of incest). // Check whether we need this node; if not we can replace it by our own children (warn, danger of incest).
if (locked.find(AI_OG_GETKEY(nd->mName)) == locked.end() ) { if (locked.find(AI_OG_GETKEY(nd->mName)) == locked.end()) {
for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) { for (std::list<aiNode *>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
if (locked.find(AI_OG_GETKEY((*it)->mName)) == locked.end()) { if (locked.find(AI_OG_GETKEY((*it)->mName)) == locked.end()) {
(*it)->mTransformation = nd->mTransformation * (*it)->mTransformation; (*it)->mTransformation = nd->mTransformation * (*it)->mTransformation;
@ -136,19 +136,19 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& no
nodes.push_back(nd); nodes.push_back(nd);
// Now check for possible optimizations in our list of child nodes. join as many as possible // Now check for possible optimizations in our list of child nodes. join as many as possible
aiNode* join_master = NULL; aiNode *join_master = nullptr;
aiMatrix4x4 inv; aiMatrix4x4 inv;
const LockedSetType::const_iterator end = locked.end(); const LockedSetType::const_iterator end = locked.end();
std::list<aiNode*> join; std::list<aiNode *> join;
for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) { for (std::list<aiNode *>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
aiNode* child = *it; aiNode *child = *it;
if (child->mNumChildren == 0 && locked.find(AI_OG_GETKEY(child->mName)) == end) { if (child->mNumChildren == 0 && locked.find(AI_OG_GETKEY(child->mName)) == end) {
// There may be no instanced meshes // There may be no instanced meshes
unsigned int n = 0; unsigned int n = 0;
for (; n < child->mNumMeshes;++n) { for (; n < child->mNumMeshes; ++n) {
if (meshes[child->mMeshes[n]] > 1) { if (meshes[child->mMeshes[n]] > 1) {
break; break;
} }
@ -159,7 +159,7 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& no
inv = join_master->mTransformation; inv = join_master->mTransformation;
inv.Inverse(); inv.Inverse();
} else { } else {
child->mTransformation = inv * child->mTransformation ; child->mTransformation = inv * child->mTransformation;
join.push_back(child); join.push_back(child);
it = child_nodes.erase(it); it = child_nodes.erase(it);
@ -170,31 +170,40 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& no
++it; ++it;
} }
if (join_master && !join.empty()) { if (join_master && !join.empty()) {
join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i",count_merged++); join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i", count_merged++);
unsigned int out_meshes = 0; unsigned int out_meshes = 0;
for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) { for (std::list<aiNode *>::const_iterator it = join.cbegin(); it != join.cend(); ++it) {
out_meshes += (*it)->mNumMeshes; out_meshes += (*it)->mNumMeshes;
} }
// copy all mesh references in one array // copy all mesh references in one array
if (out_meshes) { if (out_meshes) {
unsigned int* meshes = new unsigned int[out_meshes+join_master->mNumMeshes], *tmp = meshes; unsigned int *meshes = new unsigned int[out_meshes + join_master->mNumMeshes], *tmp = meshes;
for (unsigned int n = 0; n < join_master->mNumMeshes;++n) { for (unsigned int n = 0; n < join_master->mNumMeshes; ++n) {
*tmp++ = join_master->mMeshes[n]; *tmp++ = join_master->mMeshes[n];
} }
for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) { for (const aiNode *join_node : join) {
for (unsigned int n = 0; n < (*it)->mNumMeshes; ++n) { for (unsigned int n = 0; n < join_node->mNumMeshes; ++n) {
*tmp = (*it)->mMeshes[n]; *tmp = join_node->mMeshes[n];
aiMesh* mesh = mScene->mMeshes[*tmp++]; aiMesh *mesh = mScene->mMeshes[*tmp++];
// Assume the transformation is affine
// manually move the mesh into the right coordinate system // manually move the mesh into the right coordinate system
const aiMatrix3x3 IT = aiMatrix3x3( (*it)->mTransformation ).Inverse().Transpose();
// Check for odd negative scale (mirror)
if (join_node->mTransformation.Determinant() < 0) {
// Reverse the mesh face winding order
FlipWindingOrderProcess::ProcessMesh(mesh);
}
// Update positions, normals and tangents
const aiMatrix3x3 IT = aiMatrix3x3(join_node->mTransformation).Inverse().Transpose();
for (unsigned int a = 0; a < mesh->mNumVertices; ++a) { for (unsigned int a = 0; a < mesh->mNumVertices; ++a) {
mesh->mVertices[a] *= (*it)->mTransformation; mesh->mVertices[a] *= join_node->mTransformation;
if (mesh->HasNormals()) if (mesh->HasNormals())
mesh->mNormals[a] *= IT; mesh->mNormals[a] *= IT;
@ -205,7 +214,7 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& no
} }
} }
} }
delete *it; // bye, node delete join_node; // bye, node
} }
delete[] join_master->mMeshes; delete[] join_master->mMeshes;
join_master->mMeshes = meshes; join_master->mMeshes = meshes;
@ -219,17 +228,17 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& no
delete[] nd->mChildren; delete[] nd->mChildren;
if (!child_nodes.empty()) { if (!child_nodes.empty()) {
nd->mChildren = new aiNode*[child_nodes.size()]; nd->mChildren = new aiNode *[child_nodes.size()];
} } else
else nd->mChildren = nullptr; nd->mChildren = nullptr;
} }
nd->mNumChildren = static_cast<unsigned int>(child_nodes.size()); nd->mNumChildren = static_cast<unsigned int>(child_nodes.size());
if (nd->mChildren) { if (nd->mChildren) {
aiNode** tmp = nd->mChildren; aiNode **tmp = nd->mChildren;
for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) { for (std::list<aiNode *>::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) {
aiNode* node = *tmp++ = *it; aiNode *node = *tmp++ = *it;
node->mParent = nd; node->mParent = nd;
} }
} }
@ -239,12 +248,12 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& no
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Execute the post-processing step on the given scene // Execute the post-processing step on the given scene
void OptimizeGraphProcess::Execute( aiScene* pScene) { void OptimizeGraphProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("OptimizeGraphProcess begin"); ASSIMP_LOG_DEBUG("OptimizeGraphProcess begin");
nodes_in = nodes_out = count_merged = 0; nodes_in = nodes_out = count_merged = 0;
mScene = pScene; mScene = pScene;
meshes.resize(pScene->mNumMeshes,0); meshes.resize(pScene->mNumMeshes, 0);
FindInstancedMeshes(pScene->mRootNode); FindInstancedMeshes(pScene->mRootNode);
// build a blacklist of identifiers. If the name of a node matches one of these, we won't touch it // build a blacklist of identifiers. If the name of a node matches one of these, we won't touch it
@ -259,7 +268,7 @@ void OptimizeGraphProcess::Execute( aiScene* pScene) {
for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) { for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) {
for (unsigned int a = 0; a < pScene->mAnimations[i]->mNumChannels; ++a) { for (unsigned int a = 0; a < pScene->mAnimations[i]->mNumChannels; ++a) {
aiNodeAnim* anim = pScene->mAnimations[i]->mChannels[a]; aiNodeAnim *anim = pScene->mAnimations[i]->mChannels[a];
locked.insert(AI_OG_GETKEY(anim->mNodeName)); locked.insert(AI_OG_GETKEY(anim->mNodeName));
} }
} }
@ -267,7 +276,7 @@ void OptimizeGraphProcess::Execute( aiScene* pScene) {
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
for (unsigned int a = 0; a < pScene->mMeshes[i]->mNumBones; ++a) { for (unsigned int a = 0; a < pScene->mMeshes[i]->mNumBones; ++a) {
aiBone* bone = pScene->mMeshes[i]->mBones[a]; aiBone *bone = pScene->mMeshes[i]->mBones[a];
locked.insert(AI_OG_GETKEY(bone->mName)); locked.insert(AI_OG_GETKEY(bone->mName));
// HACK: Meshes referencing bones may not be transformed; we need to look them. // HACK: Meshes referencing bones may not be transformed; we need to look them.
@ -277,35 +286,35 @@ void OptimizeGraphProcess::Execute( aiScene* pScene) {
} }
for (unsigned int i = 0; i < pScene->mNumCameras; ++i) { for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {
aiCamera* cam = pScene->mCameras[i]; aiCamera *cam = pScene->mCameras[i];
locked.insert(AI_OG_GETKEY(cam->mName)); locked.insert(AI_OG_GETKEY(cam->mName));
} }
for (unsigned int i = 0; i < pScene->mNumLights; ++i) { for (unsigned int i = 0; i < pScene->mNumLights; ++i) {
aiLight* lgh = pScene->mLights[i]; aiLight *lgh = pScene->mLights[i];
locked.insert(AI_OG_GETKEY(lgh->mName)); locked.insert(AI_OG_GETKEY(lgh->mName));
} }
// Insert a dummy master node and make it read-only // Insert a dummy master node and make it read-only
aiNode* dummy_root = new aiNode(AI_RESERVED_NODE_NAME); aiNode *dummy_root = new aiNode(AI_RESERVED_NODE_NAME);
locked.insert(AI_OG_GETKEY(dummy_root->mName)); locked.insert(AI_OG_GETKEY(dummy_root->mName));
const aiString prev = pScene->mRootNode->mName; const aiString prev = pScene->mRootNode->mName;
pScene->mRootNode->mParent = dummy_root; pScene->mRootNode->mParent = dummy_root;
dummy_root->mChildren = new aiNode*[dummy_root->mNumChildren = 1]; dummy_root->mChildren = new aiNode *[dummy_root->mNumChildren = 1];
dummy_root->mChildren[0] = pScene->mRootNode; dummy_root->mChildren[0] = pScene->mRootNode;
// Do our recursive processing of scenegraph nodes. For each node collect // Do our recursive processing of scenegraph nodes. For each node collect
// a fully new list of children and allow their children to place themselves // a fully new list of children and allow their children to place themselves
// on the same hierarchy layer as their parents. // on the same hierarchy layer as their parents.
std::list<aiNode*> nodes; std::list<aiNode *> nodes;
CollectNewChildren (dummy_root,nodes); CollectNewChildren(dummy_root, nodes);
ai_assert(nodes.size() == 1); ai_assert(nodes.size() == 1);
if (dummy_root->mNumChildren == 0) { if (dummy_root->mNumChildren == 0) {
pScene->mRootNode = NULL; pScene->mRootNode = nullptr;
throw DeadlyImportError("After optimizing the scene graph, no data remains"); throw DeadlyImportError("After optimizing the scene graph, no data remains");
} }
@ -314,19 +323,18 @@ void OptimizeGraphProcess::Execute( aiScene* pScene) {
// Keep the dummy node but assign the name of the old root node to it // Keep the dummy node but assign the name of the old root node to it
pScene->mRootNode->mName = prev; pScene->mRootNode->mName = prev;
} } else {
else {
// Remove the dummy root node again. // Remove the dummy root node again.
pScene->mRootNode = dummy_root->mChildren[0]; pScene->mRootNode = dummy_root->mChildren[0];
dummy_root->mChildren[0] = NULL; dummy_root->mChildren[0] = nullptr;
delete dummy_root; delete dummy_root;
} }
pScene->mRootNode->mParent = NULL; pScene->mRootNode->mParent = nullptr;
if (!DefaultLogger::isNullLogger()) { if (!DefaultLogger::isNullLogger()) {
if ( nodes_in != nodes_out) { if (nodes_in != nodes_out) {
ASSIMP_LOG_INFO_F("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out); ASSIMP_LOG_INFO_F("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out);
} else { } else {
ASSIMP_LOG_DEBUG("OptimizeGraphProcess finished"); ASSIMP_LOG_DEBUG("OptimizeGraphProcess finished");
@ -338,9 +346,8 @@ void OptimizeGraphProcess::Execute( aiScene* pScene) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Build a LUT of all instanced meshes // Build a LUT of all instanced meshes
void OptimizeGraphProcess::FindInstancedMeshes (aiNode* pNode) void OptimizeGraphProcess::FindInstancedMeshes(aiNode *pNode) {
{ for (unsigned int i = 0; i < pNode->mNumMeshes; ++i) {
for (unsigned int i = 0; i < pNode->mNumMeshes;++i) {
++meshes[pNode->mMeshes[i]]; ++meshes[pNode->mMeshes[i]];
} }

View File

@ -75,13 +75,13 @@ public:
~OptimizeGraphProcess(); ~OptimizeGraphProcess();
// ------------------------------------------------------------------- // -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const; bool IsActive( unsigned int pFlags) const override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
void Execute( aiScene* pScene); void Execute( aiScene* pScene) override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
void SetupProperties(const Importer* pImp); void SetupProperties(const Importer* pImp) override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** @brief Add a list of node names to be locked and not modified. /** @brief Add a list of node names to be locked and not modified.

View File

@ -45,11 +45,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Implementation of the "PretransformVertices" post processing step * @brief Implementation of the "PretransformVertices" post processing step
*/ */
#include "PretransformVertices.h" #include "PretransformVertices.h"
#include "ConvertToLHProcess.h"
#include "ProcessHelper.h" #include "ProcessHelper.h"
#include <assimp/SceneCombiner.h>
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
#include <assimp/SceneCombiner.h>
using namespace Assimp; using namespace Assimp;
@ -59,12 +59,12 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
PretransformVertices::PretransformVertices() PretransformVertices::PretransformVertices() :
: configKeepHierarchy (false) configKeepHierarchy(false),
, configNormalize(false) configNormalize(false),
, configTransform(false) configTransform(false),
, configTransformation() configTransformation(),
, mConfigPointCloud( false ) { mConfigPointCloud(false) {
// empty // empty
} }
@ -76,20 +76,18 @@ PretransformVertices::~PretransformVertices() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
bool PretransformVertices::IsActive( unsigned int pFlags) const bool PretransformVertices::IsActive(unsigned int pFlags) const {
{
return (pFlags & aiProcess_PreTransformVertices) != 0; return (pFlags & aiProcess_PreTransformVertices) != 0;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Setup import configuration // Setup import configuration
void PretransformVertices::SetupProperties(const Importer* pImp) void PretransformVertices::SetupProperties(const Importer *pImp) {
{
// Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE, // Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE,
// AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION // AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION
configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY,0)); configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, 0));
configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE,0)); configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, 0));
configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION,0)); configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION, 0));
configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4()); configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4());
@ -98,11 +96,9 @@ void PretransformVertices::SetupProperties(const Importer* pImp)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Count the number of nodes // Count the number of nodes
unsigned int PretransformVertices::CountNodes( aiNode* pcNode ) unsigned int PretransformVertices::CountNodes(const aiNode *pcNode) const {
{
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) {
{
iRet += CountNodes(pcNode->mChildren[i]); iRet += CountNodes(pcNode->mChildren[i]);
} }
return iRet; return iRet;
@ -110,8 +106,7 @@ unsigned int PretransformVertices::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 PretransformVertices::GetMeshVFormat( aiMesh* pcMesh ) unsigned int PretransformVertices::GetMeshVFormat(aiMesh *pcMesh) const {
{
// 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
// from scratch. The pointer is unused as animations are lost // from scratch. The pointer is unused as animations are lost
@ -119,56 +114,47 @@ unsigned int PretransformVertices::GetMeshVFormat( aiMesh* pcMesh )
if (pcMesh->mBones) if (pcMesh->mBones)
return (unsigned int)(uint64_t)pcMesh->mBones; return (unsigned int)(uint64_t)pcMesh->mBones;
const unsigned int iRet = GetMeshVFormatUnique(pcMesh); const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
// store the value for later use // store the value for later use
pcMesh->mBones = (aiBone**)(uint64_t)iRet; pcMesh->mBones = (aiBone **)(uint64_t)iRet;
return iRet; return iRet;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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 PretransformVertices::CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, void PretransformVertices::CountVerticesAndFaces(const aiScene *pcScene, const aiNode *pcNode, unsigned int iMat,
unsigned int iVFormat, unsigned int* piFaces, unsigned int* piVertices) unsigned int iVFormat, unsigned int *piFaces, unsigned int *piVertices) const {
{ 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]];
{ if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) {
aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
{
*piVertices += pcMesh->mNumVertices; *piVertices += pcMesh->mNumVertices;
*piFaces += pcMesh->mNumFaces; *piFaces += pcMesh->mNumFaces;
} }
} }
for (unsigned int i = 0;i < pcNode->mNumChildren;++i) for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
{ CountVerticesAndFaces(pcScene, pcNode->mChildren[i], iMat,
CountVerticesAndFaces(pcScene,pcNode->mChildren[i],iMat, iVFormat, piFaces, piVertices);
iVFormat,piFaces,piVertices);
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Collect vertex/face data // Collect vertex/face data
void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, void PretransformVertices::CollectData(const aiScene *pcScene, const aiNode *pcNode, unsigned int iMat,
unsigned int iVFormat, aiMesh* pcMeshOut, unsigned int iVFormat, aiMesh *pcMeshOut,
unsigned int aiCurrent[2], unsigned int* num_refs) unsigned int aiCurrent[2], unsigned int *num_refs) const {
{
// No need to multiply if there's no transformation // No need to multiply if there's no transformation
const bool identity = pcNode->mTransformation.IsIdentity(); 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))
{
// Decrement mesh reference counter // Decrement mesh reference counter
unsigned int& num_ref = num_refs[pcNode->mMeshes[i]]; unsigned int &num_ref = num_refs[pcNode->mMeshes[i]];
ai_assert(0 != num_ref); ai_assert(0 != num_ref);
--num_ref; --num_ref;
// Save the name of the last mesh // Save the name of the last mesh
if (num_ref==0) if (num_ref == 0) {
{
pcMeshOut->mName = pcMesh->mName; pcMeshOut->mName = pcMesh->mName;
} }
@ -184,8 +170,7 @@ void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsign
pcMesh->mNormals, pcMesh->mNormals,
pcMesh->mNumVertices * sizeof(aiVector3D)); pcMesh->mNumVertices * sizeof(aiVector3D));
} }
if (iVFormat & 0x4) if (iVFormat & 0x4) {
{
// copy tangents without modifying them // copy tangents without modifying them
::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX], ::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX],
pcMesh->mTangents, pcMesh->mTangents,
@ -195,12 +180,10 @@ void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsign
pcMesh->mBitangents, pcMesh->mBitangents,
pcMesh->mNumVertices * sizeof(aiVector3D)); pcMesh->mNumVertices * sizeof(aiVector3D));
} }
} } else {
else
{
// copy positions, 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->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n]; pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX] + n] = pcNode->mTransformation * pcMesh->mVertices[n];
} }
aiMatrix4x4 mWorldIT = pcNode->mTransformation; aiMatrix4x4 mWorldIT = pcNode->mTransformation;
mWorldIT.Inverse().Transpose(); mWorldIT.Inverse().Transpose();
@ -208,26 +191,23 @@ void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsign
// TODO: implement Inverse() for aiMatrix3x3 // TODO: implement Inverse() for aiMatrix3x3
aiMatrix3x3 m = aiMatrix3x3(mWorldIT); aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
if (iVFormat & 0x2) if (iVFormat & 0x2) {
{
// copy normals, transform them to worldspace // copy normals, transform them to worldspace
for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { for (unsigned int n = 0; n < pcMesh->mNumVertices; ++n) {
pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] = pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX] + n] =
(m * pcMesh->mNormals[n]).Normalize(); (m * pcMesh->mNormals[n]).Normalize();
} }
} }
if (iVFormat & 0x4) if (iVFormat & 0x4) {
{
// copy tangents and bitangents, transform them to worldspace // copy tangents and bitangents, 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]).Normalize(); pcMeshOut->mTangents[aiCurrent[AI_PTVS_VERTEX] + n] = (m * pcMesh->mTangents[n]).Normalize();
pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mBitangents[n]).Normalize(); pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX] + n] = (m * pcMesh->mBitangents[n]).Normalize();
} }
} }
} }
unsigned int p = 0; unsigned int p = 0;
while (iVFormat & (0x100 << p)) while (iVFormat & (0x100 << p)) {
{
// copy texture coordinates // copy texture coordinates
memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX], memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX],
pcMesh->mTextureCoords[p], pcMesh->mTextureCoords[p],
@ -235,8 +215,7 @@ void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsign
++p; ++p;
} }
p = 0; p = 0;
while (iVFormat & (0x1000000 << p)) while (iVFormat & (0x1000000 << p)) {
{
// copy vertex colors // copy vertex colors
memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX], memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX],
pcMesh->mColors[p], pcMesh->mColors[p],
@ -246,36 +225,33 @@ void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsign
// now we need to copy all faces. since we will delete the source mesh afterwards, // now we need to copy all faces. since we will delete the source mesh afterwards,
// we don't need to reallocate the array of indices except if this mesh is // we don't need to reallocate the array of indices except if this mesh is
// referenced multiple times. // referenced multiple times.
for (unsigned int planck = 0;planck < pcMesh->mNumFaces;++planck) for (unsigned int planck = 0; planck < pcMesh->mNumFaces; ++planck) {
{ aiFace &f_src = pcMesh->mFaces[planck];
aiFace& f_src = pcMesh->mFaces[planck]; aiFace &f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE] + planck];
aiFace& f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck];
const unsigned int num_idx = f_src.mNumIndices; const unsigned int num_idx = f_src.mNumIndices;
f_dst.mNumIndices = num_idx; f_dst.mNumIndices = num_idx;
unsigned int* pi; unsigned int *pi;
if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */ if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */
pi = f_dst.mIndices = f_src.mIndices; pi = f_dst.mIndices = f_src.mIndices;
// offset all vertex indices // offset all vertex indices
for (unsigned int hahn = 0; hahn < num_idx;++hahn){ for (unsigned int hahn = 0; hahn < num_idx; ++hahn) {
pi[hahn] += aiCurrent[AI_PTVS_VERTEX]; pi[hahn] += aiCurrent[AI_PTVS_VERTEX];
} }
} } else {
else {
pi = f_dst.mIndices = new unsigned int[num_idx]; pi = f_dst.mIndices = new unsigned int[num_idx];
// copy and offset all vertex indices // copy and offset all vertex indices
for (unsigned int hahn = 0; hahn < num_idx;++hahn){ for (unsigned int hahn = 0; hahn < num_idx; ++hahn) {
pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX]; 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) {
{
case 0x1: case 0x1:
pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT; pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT;
break; break;
@ -296,21 +272,19 @@ void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsign
} }
// append all children of us // append all children of us
for (unsigned int i = 0;i < pcNode->mNumChildren;++i) { for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
CollectData(pcScene,pcNode->mChildren[i],iMat, CollectData(pcScene, pcNode->mChildren[i], iMat,
iVFormat,pcMeshOut,aiCurrent,num_refs); 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 PretransformVertices::GetVFormatList( aiScene* pcScene, unsigned int iMat, void PretransformVertices::GetVFormatList(const aiScene *pcScene, unsigned int iMat,
std::list<unsigned int>& aiOut) std::list<unsigned int> &aiOut) const {
{ 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));
} }
@ -319,35 +293,38 @@ void PretransformVertices::GetVFormatList( aiScene* pcScene, unsigned int iMat,
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Compute the absolute transformation matrices of each node // Compute the absolute transformation matrices of each node
void PretransformVertices::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;
} }
for (unsigned int i = 0;i < pcNode->mNumChildren;++i) { for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
ComputeAbsoluteTransform(pcNode->mChildren[i]); ComputeAbsoluteTransform(pcNode->mChildren[i]);
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Apply the node transformation to a mesh // Apply the node transformation to a mesh
void PretransformVertices::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat) void PretransformVertices::ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat) const {
{
// Check whether we need to transform the coordinates at all // Check whether we need to transform the coordinates at all
if (!mat.IsIdentity()) { if (!mat.IsIdentity()) {
// Check for odd negative scale (mirror)
if (mesh->HasFaces() && mat.Determinant() < 0) {
// Reverse the mesh face winding order
FlipWindingOrderProcess::ProcessMesh(mesh);
}
// Update positions
if (mesh->HasPositions()) { if (mesh->HasPositions()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mVertices[i] = mat * mesh->mVertices[i]; mesh->mVertices[i] = mat * mesh->mVertices[i];
} }
} }
if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
aiMatrix4x4 mWorldIT = mat;
mWorldIT.Inverse().Transpose();
// TODO: implement Inverse() for aiMatrix3x3 // Update normals and tangents
aiMatrix3x3 m = aiMatrix3x3(mWorldIT); if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
const aiMatrix3x3 m = aiMatrix3x3(mat).Inverse().Transpose();
if (mesh->HasNormals()) { if (mesh->HasNormals()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
@ -366,29 +343,27 @@ void PretransformVertices::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Simple routine to build meshes in worldspace, no further optimization // Simple routine to build meshes in worldspace, no further optimization
void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in, void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **in,
unsigned int numIn, aiNode* node) unsigned int numIn, aiNode *node) const {
{
// NOTE: // NOTE:
// aiMesh::mNumBones store original source mesh, or UINT_MAX if not a copy // aiMesh::mNumBones store original source mesh, or UINT_MAX if not a copy
// aiMesh::mBones store reference to abs. transform we multiplied with // aiMesh::mBones store reference to abs. transform we multiplied with
// process meshes // process meshes
for (unsigned int i = 0; i < node->mNumMeshes;++i) { for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
aiMesh* mesh = in[node->mMeshes[i]]; aiMesh *mesh = in[node->mMeshes[i]];
// check whether we can operate on this mesh // check whether we can operate on this mesh
if (!mesh->mBones || *reinterpret_cast<aiMatrix4x4*>(mesh->mBones) == node->mTransformation) { if (!mesh->mBones || *reinterpret_cast<aiMatrix4x4 *>(mesh->mBones) == node->mTransformation) {
// yes, we can. // yes, we can.
mesh->mBones = reinterpret_cast<aiBone**> (&node->mTransformation); mesh->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
mesh->mNumBones = UINT_MAX; mesh->mNumBones = UINT_MAX;
} } else {
else {
// try to find us in the list of newly created meshes // try to find us in the list of newly created meshes
for (unsigned int n = 0; n < out.size(); ++n) { for (unsigned int n = 0; n < out.size(); ++n) {
aiMesh* ctz = out[n]; aiMesh *ctz = out[n];
if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4*>(ctz->mBones) == node->mTransformation) { if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4 *>(ctz->mBones) == node->mTransformation) {
// ok, use this one. Update node mesh index // ok, use this one. Update node mesh index
node->mMeshes[i] = numIn + n; node->mMeshes[i] = numIn + n;
@ -397,15 +372,15 @@ void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in
if (node->mMeshes[i] < numIn) { if (node->mMeshes[i] < numIn) {
// Worst case. Need to operate on a full copy of the mesh // Worst case. Need to operate on a full copy of the mesh
ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms"); ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms");
aiMesh* ntz; aiMesh *ntz;
const unsigned int tmp = mesh->mNumBones; // const unsigned int tmp = mesh->mNumBones; //
mesh->mNumBones = 0; mesh->mNumBones = 0;
SceneCombiner::Copy(&ntz,mesh); SceneCombiner::Copy(&ntz, mesh);
mesh->mNumBones = tmp; mesh->mNumBones = tmp;
ntz->mNumBones = node->mMeshes[i]; ntz->mNumBones = node->mMeshes[i];
ntz->mBones = reinterpret_cast<aiBone**> (&node->mTransformation); ntz->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
out.push_back(ntz); out.push_back(ntz);
@ -415,37 +390,34 @@ void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in
} }
// call children // call children
for (unsigned int i = 0; i < node->mNumChildren;++i) for (unsigned int i = 0; i < node->mNumChildren; ++i)
BuildWCSMeshes(out,in,numIn,node->mChildren[i]); BuildWCSMeshes(out, in, numIn, node->mChildren[i]);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reset transformation matrices to identity // Reset transformation matrices to identity
void PretransformVertices::MakeIdentityTransform(aiNode* nd) void PretransformVertices::MakeIdentityTransform(aiNode *nd) const {
{
nd->mTransformation = aiMatrix4x4(); nd->mTransformation = aiMatrix4x4();
// call children // call children
for (unsigned int i = 0; i < nd->mNumChildren;++i) for (unsigned int i = 0; i < nd->mNumChildren; ++i)
MakeIdentityTransform(nd->mChildren[i]); MakeIdentityTransform(nd->mChildren[i]);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Build reference counters for all meshes // Build reference counters for all meshes
void PretransformVertices::BuildMeshRefCountArray(aiNode* nd, unsigned int * refs) void PretransformVertices::BuildMeshRefCountArray(const aiNode *nd, unsigned int *refs) const {
{ for (unsigned int i = 0; i < nd->mNumMeshes; ++i)
for (unsigned int i = 0; i< nd->mNumMeshes;++i)
refs[nd->mMeshes[i]]++; refs[nd->mMeshes[i]]++;
// call children // call children
for (unsigned int i = 0; i < nd->mNumChildren;++i) for (unsigned int i = 0; i < nd->mNumChildren; ++i)
BuildMeshRefCountArray(nd->mChildren[i],refs); 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) {
{
ASSIMP_LOG_DEBUG("PretransformVerticesProcess begin"); ASSIMP_LOG_DEBUG("PretransformVerticesProcess begin");
// Return immediately if we have no meshes // Return immediately if we have no meshes
@ -456,7 +428,7 @@ void PretransformVertices::Execute( aiScene* pScene)
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);
if(configTransform) { if (configTransform) {
pScene->mRootNode->mTransformation = configTransformation; pScene->mRootNode->mTransformation = configTransformation;
} }
@ -466,10 +438,10 @@ void PretransformVertices::Execute( aiScene* pScene)
// 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)
delete mesh->mBones[a]; delete mesh->mBones[a];
delete[] mesh->mBones; delete[] mesh->mBones;
@ -477,74 +449,74 @@ 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;
// Keep scene hierarchy? It's an easy job in this case ... // Keep scene hierarchy? It's an easy job in this case ...
// we go on and transform all meshes, if one is referenced by nodes // we go on and transform all meshes, if one is referenced by nodes
// with different absolute transformations a depth copy of the mesh // with different absolute transformations a depth copy of the mesh
// is required. // is required.
if( configKeepHierarchy ) { if (configKeepHierarchy) {
// Hack: store the matrix we're transforming a mesh with in aiMesh::mBones // Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
BuildWCSMeshes(apcOutMeshes,pScene->mMeshes,pScene->mNumMeshes, pScene->mRootNode); BuildWCSMeshes(apcOutMeshes, pScene->mMeshes, pScene->mNumMeshes, pScene->mRootNode);
// ... if new meshes have been generated, append them to the end of the scene // ... if new meshes have been generated, append them to the end of the scene
if (apcOutMeshes.size() > 0) { if (apcOutMeshes.size() > 0) {
aiMesh** npp = new aiMesh*[pScene->mNumMeshes + apcOutMeshes.size()]; aiMesh **npp = new aiMesh *[pScene->mNumMeshes + apcOutMeshes.size()];
memcpy(npp,pScene->mMeshes,sizeof(aiMesh*)*pScene->mNumMeshes); memcpy(npp, pScene->mMeshes, sizeof(aiMesh *) * pScene->mNumMeshes);
memcpy(npp+pScene->mNumMeshes,&apcOutMeshes[0],sizeof(aiMesh*)*apcOutMeshes.size()); memcpy(npp + pScene->mNumMeshes, &apcOutMeshes[0], sizeof(aiMesh *) * apcOutMeshes.size());
pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size()); pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size());
delete[] pScene->mMeshes; pScene->mMeshes = npp; delete[] pScene->mMeshes;
pScene->mMeshes = npp;
} }
// now iterate through all meshes and transform them to worldspace // now iterate through all meshes and transform them to worldspace
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
ApplyTransform(pScene->mMeshes[i],*reinterpret_cast<aiMatrix4x4*>( pScene->mMeshes[i]->mBones )); ApplyTransform(pScene->mMeshes[i], *reinterpret_cast<aiMatrix4x4 *>(pScene->mMeshes[i]->mBones));
// prevent improper destruction // prevent improper destruction
pScene->mMeshes[i]->mBones = NULL; pScene->mMeshes[i]->mBones = NULL;
pScene->mMeshes[i]->mNumBones = 0; pScene->mMeshes[i]->mNumBones = 0;
} }
} else { } else {
apcOutMeshes.reserve(pScene->mNumMaterials<<1u); apcOutMeshes.reserve(pScene->mNumMaterials << 1u);
std::list<unsigned int> aiVFormats; std::list<unsigned int> aiVFormats;
std::vector<unsigned int> s(pScene->mNumMeshes,0); std::vector<unsigned int> s(pScene->mNumMeshes, 0);
BuildMeshRefCountArray(pScene->mRootNode,&s[0]); BuildMeshRefCountArray(pScene->mRootNode, &s[0]);
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) {
// get the list of all vertex formats for this material // get the list of all vertex formats for this material
aiVFormats.clear(); aiVFormats.clear();
GetVFormatList(pScene,i,aiVFormats); GetVFormatList(pScene, i, aiVFormats);
aiVFormats.sort(); aiVFormats.sort();
aiVFormats.unique(); aiVFormats.unique();
for (std::list<unsigned int>::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) { for (std::list<unsigned int>::const_iterator j = aiVFormats.begin(); j != aiVFormats.end(); ++j) {
unsigned int iVertices = 0; unsigned int iVertices = 0;
unsigned int iFaces = 0; unsigned int iFaces = 0;
CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices); CountVerticesAndFaces(pScene, pScene->mRootNode, i, *j, &iFaces, &iVertices);
if (0 != iFaces && 0 != iVertices) if (0 != iFaces && 0 != iVertices) {
{
apcOutMeshes.push_back(new aiMesh()); apcOutMeshes.push_back(new aiMesh());
aiMesh* pcMesh = apcOutMeshes.back(); aiMesh *pcMesh = apcOutMeshes.back();
pcMesh->mNumFaces = iFaces; pcMesh->mNumFaces = iFaces;
pcMesh->mNumVertices = iVertices; pcMesh->mNumVertices = iVertices;
pcMesh->mFaces = new aiFace[iFaces]; pcMesh->mFaces = new aiFace[iFaces];
pcMesh->mVertices = new aiVector3D[iVertices]; pcMesh->mVertices = new aiVector3D[iVertices];
pcMesh->mMaterialIndex = i; pcMesh->mMaterialIndex = i;
if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices]; if ((*j) & 0x2) pcMesh->mNormals = new aiVector3D[iVertices];
if ((*j) & 0x4) if ((*j) & 0x4) {
{
pcMesh->mTangents = new aiVector3D[iVertices]; pcMesh->mTangents = new aiVector3D[iVertices];
pcMesh->mBitangents = new aiVector3D[iVertices]; pcMesh->mBitangents = new aiVector3D[iVertices];
} }
iFaces = 0; iFaces = 0;
while ((*j) & (0x100 << iFaces)) while ((*j) & (0x100 << iFaces)) {
{
pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices]; pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3; if ((*j) & (0x10000 << iFaces))
else pcMesh->mNumUVComponents[iFaces] = 2; pcMesh->mNumUVComponents[iFaces] = 3;
else
pcMesh->mNumUVComponents[iFaces] = 2;
iFaces++; iFaces++;
} }
iFaces = 0; iFaces = 0;
@ -552,8 +524,8 @@ void PretransformVertices::Execute( aiScene* pScene)
pcMesh->mColors[iFaces++] = new aiColor4D[iVertices]; pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
// fill the mesh ... // fill the mesh ...
unsigned int aiTemp[2] = {0,0}; unsigned int aiTemp[2] = { 0, 0 };
CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp,&s[0]); CollectData(pScene, pScene->mRootNode, i, *j, pcMesh, aiTemp, &s[0]);
} }
} }
} }
@ -562,13 +534,10 @@ void PretransformVertices::Execute( aiScene* pScene)
if (apcOutMeshes.empty()) { if (apcOutMeshes.empty()) {
throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes"); throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes");
} } else {
else
{
// now delete all meshes in the scene and build a new mesh list // now delete all meshes in the scene and build a new mesh list
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];
mesh->mNumBones = 0; mesh->mNumBones = 0;
mesh->mBones = NULL; mesh->mBones = NULL;
@ -591,14 +560,14 @@ void PretransformVertices::Execute( aiScene* pScene)
// It is impossible that we have more output meshes than // It is impossible that we have more output meshes than
// input meshes, so we can easily reuse the old mesh array // input meshes, so we can easily reuse the old mesh array
pScene->mNumMeshes = (unsigned int)apcOutMeshes.size(); pScene->mNumMeshes = (unsigned int)apcOutMeshes.size();
for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
pScene->mMeshes[i] = apcOutMeshes[i]; pScene->mMeshes[i] = apcOutMeshes[i];
} }
} }
} }
// remove all animations from the scene // remove all animations from the scene
for (unsigned int i = 0; i < pScene->mNumAnimations;++i) for (unsigned int i = 0; i < pScene->mNumAnimations; ++i)
delete pScene->mAnimations[i]; delete pScene->mAnimations[i];
delete[] pScene->mAnimations; delete[] pScene->mAnimations;
@ -606,56 +575,50 @@ void PretransformVertices::Execute( aiScene* pScene)
pScene->mNumAnimations = 0; pScene->mNumAnimations = 0;
// --- 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) {
{ aiCamera *cam = pScene->mCameras[i];
aiCamera* cam = pScene->mCameras[i]; const aiNode *nd = pScene->mRootNode->FindNode(cam->mName);
const aiNode* nd = pScene->mRootNode->FindNode(cam->mName);
ai_assert(NULL != nd); ai_assert(NULL != nd);
// multiply all properties of the camera with the absolute // multiply all properties of the camera with the absolute
// transformation of the corresponding node // transformation of the corresponding node
cam->mPosition = nd->mTransformation * cam->mPosition; cam->mPosition = nd->mTransformation * cam->mPosition;
cam->mLookAt = aiMatrix3x3( nd->mTransformation ) * cam->mLookAt; cam->mLookAt = aiMatrix3x3(nd->mTransformation) * cam->mLookAt;
cam->mUp = aiMatrix3x3( nd->mTransformation ) * cam->mUp; cam->mUp = aiMatrix3x3(nd->mTransformation) * cam->mUp;
} }
for (unsigned int i = 0; i < pScene->mNumLights;++i) for (unsigned int i = 0; i < pScene->mNumLights; ++i) {
{ aiLight *l = pScene->mLights[i];
aiLight* l = pScene->mLights[i]; const aiNode *nd = pScene->mRootNode->FindNode(l->mName);
const aiNode* nd = pScene->mRootNode->FindNode(l->mName);
ai_assert(NULL != nd); ai_assert(NULL != nd);
// multiply all properties of the camera with the absolute // multiply all properties of the camera with the absolute
// transformation of the corresponding node // transformation of the corresponding node
l->mPosition = nd->mTransformation * l->mPosition; l->mPosition = nd->mTransformation * l->mPosition;
l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection; l->mDirection = aiMatrix3x3(nd->mTransformation) * l->mDirection;
l->mUp = aiMatrix3x3( nd->mTransformation ) * l->mUp; l->mUp = aiMatrix3x3(nd->mTransformation) * l->mUp;
} }
if( !configKeepHierarchy ) { if (!configKeepHierarchy) {
// now delete all nodes in the scene and build a new // now delete all nodes in the scene and build a new
// flat node graph with a root node and some level 1 children // flat node graph with a root node and some level 1 children
aiNode* newRoot = new aiNode(); aiNode *newRoot = new aiNode();
newRoot->mName = pScene->mRootNode->mName; newRoot->mName = pScene->mRootNode->mName;
delete pScene->mRootNode; delete pScene->mRootNode;
pScene->mRootNode = newRoot; pScene->mRootNode = newRoot;
if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras) if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras) {
{
pScene->mRootNode->mNumMeshes = 1; pScene->mRootNode->mNumMeshes = 1;
pScene->mRootNode->mMeshes = new unsigned int[1]; pScene->mRootNode->mMeshes = new unsigned int[1];
pScene->mRootNode->mMeshes[0] = 0; pScene->mRootNode->mMeshes[0] = 0;
} } else {
else pScene->mRootNode->mNumChildren = pScene->mNumMeshes + pScene->mNumLights + pScene->mNumCameras;
{ aiNode **nodes = pScene->mRootNode->mChildren = new aiNode *[pScene->mRootNode->mNumChildren];
pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras;
aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
// generate mesh nodes // generate mesh nodes
for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes) for (unsigned int i = 0; i < pScene->mNumMeshes; ++i, ++nodes) {
{ aiNode *pcNode = new aiNode();
aiNode* pcNode = new aiNode();
*nodes = pcNode; *nodes = pcNode;
pcNode->mParent = pScene->mRootNode; pcNode->mParent = pScene->mRootNode;
pcNode->mName = pScene->mMeshes[i]->mName; pcNode->mName = pScene->mMeshes[i]->mName;
@ -666,52 +629,49 @@ void PretransformVertices::Execute( aiScene* pScene)
pcNode->mMeshes[0] = i; pcNode->mMeshes[0] = i;
} }
// generate light nodes // generate light nodes
for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes) for (unsigned int i = 0; i < pScene->mNumLights; ++i, ++nodes) {
{ aiNode *pcNode = new aiNode();
aiNode* pcNode = new aiNode();
*nodes = pcNode; *nodes = pcNode;
pcNode->mParent = pScene->mRootNode; pcNode->mParent = pScene->mRootNode;
pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u",i); pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u", i);
pScene->mLights[i]->mName = pcNode->mName; pScene->mLights[i]->mName = pcNode->mName;
} }
// generate camera nodes // generate camera nodes
for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes) for (unsigned int i = 0; i < pScene->mNumCameras; ++i, ++nodes) {
{ aiNode *pcNode = new aiNode();
aiNode* pcNode = new aiNode();
*nodes = pcNode; *nodes = pcNode;
pcNode->mParent = pScene->mRootNode; pcNode->mParent = pScene->mRootNode;
pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"cam_%u",i); pcNode->mName.length = ::ai_snprintf(pcNode->mName.data, MAXLEN, "cam_%u", i);
pScene->mCameras[i]->mName = pcNode->mName; pScene->mCameras[i]->mName = pcNode->mName;
} }
} }
} } else {
else {
// ... and finally set the transformation matrix of all nodes to identity // ... and finally set the transformation matrix of all nodes to identity
MakeIdentityTransform(pScene->mRootNode); MakeIdentityTransform(pScene->mRootNode);
} }
if (configNormalize) { if (configNormalize) {
// compute the boundary of all meshes // compute the boundary of all meshes
aiVector3D min,max; aiVector3D min, max;
MinMaxChooser<aiVector3D> ()(min,max); MinMaxChooser<aiVector3D>()(min, max);
for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
aiMesh* m = pScene->mMeshes[a]; aiMesh *m = pScene->mMeshes[a];
for (unsigned int i = 0; i < m->mNumVertices;++i) { for (unsigned int i = 0; i < m->mNumVertices; ++i) {
min = std::min(m->mVertices[i],min); min = std::min(m->mVertices[i], min);
max = std::max(m->mVertices[i],max); max = std::max(m->mVertices[i], max);
} }
} }
// find the dominant axis // find the dominant axis
aiVector3D d = max-min; aiVector3D d = max - min;
const ai_real div = std::max(d.x,std::max(d.y,d.z))*ai_real( 0.5); const ai_real div = std::max(d.x, std::max(d.y, d.z)) * ai_real(0.5);
d = min + d * (ai_real)0.5; d = min + d * (ai_real)0.5;
for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
aiMesh* m = pScene->mMeshes[a]; aiMesh *m = pScene->mMeshes[a];
for (unsigned int i = 0; i < m->mNumVertices;++i) { for (unsigned int i = 0; i < m->mNumVertices; ++i) {
m->mVertices[i] = (m->mVertices[i]-d)/div; m->mVertices[i] = (m->mVertices[i] - d) / div;
} }
} }
} }
@ -721,8 +681,8 @@ void PretransformVertices::Execute( aiScene* pScene)
ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished"); ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished");
ASSIMP_LOG_INFO_F("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (", ASSIMP_LOG_INFO_F("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (",
CountNodes(pScene->mRootNode) ," output nodes)" ); CountNodes(pScene->mRootNode), " output nodes)");
ASSIMP_LOG_INFO_F("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras." ); ASSIMP_LOG_INFO_F("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras.");
ASSIMP_LOG_INFO_F("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")"); ASSIMP_LOG_INFO_F("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")");
} }
} }

View File

@ -68,20 +68,20 @@ namespace Assimp {
*/ */
class ASSIMP_API PretransformVertices : public BaseProcess { class ASSIMP_API PretransformVertices : public BaseProcess {
public: public:
PretransformVertices (); PretransformVertices();
~PretransformVertices (); ~PretransformVertices();
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Check whether step is active // Check whether step is active
bool IsActive( unsigned int pFlags) const; bool IsActive(unsigned int pFlags) const override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Execute step on a given scene // Execute step on a given scene
void Execute( aiScene* pScene); void Execute(aiScene *pScene) override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Setup import settings // Setup import settings
void SetupProperties(const Importer* pImp); void SetupProperties(const Importer *pImp) override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** @brief Toggle the 'keep hierarchy' option /** @brief Toggle the 'keep hierarchy' option
@ -102,56 +102,56 @@ public:
private: private:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Count the number of nodes // Count the number of nodes
unsigned int CountNodes( aiNode* pcNode ); unsigned int CountNodes(const aiNode *pcNode) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// 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 GetMeshVFormat(aiMesh *pcMesh) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// 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, void CountVerticesAndFaces(const aiScene *pcScene, const aiNode *pcNode,
unsigned int iMat, unsigned int iMat,
unsigned int iVFormat, unsigned int iVFormat,
unsigned int* piFaces, unsigned int *piFaces,
unsigned int* piVertices); unsigned int *piVertices) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Collect vertex/face data // Collect vertex/face data
void CollectData( aiScene* pcScene, aiNode* pcNode, void CollectData(const aiScene *pcScene, const aiNode *pcNode,
unsigned int iMat, unsigned int iMat,
unsigned int iVFormat, unsigned int iVFormat,
aiMesh* pcMeshOut, aiMesh *pcMeshOut,
unsigned int aiCurrent[2], unsigned int aiCurrent[2],
unsigned int* num_refs); unsigned int *num_refs) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Get a list of all vertex formats that occur for a given material // Get a list of all vertex formats that occur for a given material
// The output list contains duplicate elements // The output list contains duplicate elements
void GetVFormatList( aiScene* pcScene, unsigned int iMat, void GetVFormatList(const aiScene *pcScene, unsigned int iMat,
std::list<unsigned int>& aiOut); std::list<unsigned int> &aiOut) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Compute the absolute transformation matrices of each node // Compute the absolute transformation matrices of each node
void ComputeAbsoluteTransform( aiNode* pcNode ); void ComputeAbsoluteTransform(aiNode *pcNode);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Simple routine to build meshes in worldspace, no further optimization // Simple routine to build meshes in worldspace, no further optimization
void BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in, void BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **in,
unsigned int numIn, aiNode* node); unsigned int numIn, aiNode *node) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Apply the node transformation to a mesh // Apply the node transformation to a mesh
void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat); void ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Reset transformation matrices to identity // Reset transformation matrices to identity
void MakeIdentityTransform(aiNode* nd); void MakeIdentityTransform(aiNode *nd) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Build reference counters for all meshes // Build reference counters for all meshes
void BuildMeshRefCountArray(aiNode* nd, unsigned int * refs); void BuildMeshRefCountArray(const aiNode *nd, unsigned int *refs) const;
//! Configuration option: keep scene hierarchy as long as possible //! Configuration option: keep scene hierarchy as long as possible
bool configKeepHierarchy; bool configKeepHierarchy;

View File

@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
#include <assimp/commonMetaData.h>
#include <memory> #include <memory>
@ -697,6 +698,25 @@ void glTFImporter::ImportEmbeddedTextures(glTF::Asset& r)
} }
} }
void glTFImporter::ImportCommonMetadata(glTF::Asset& a)
{
ai_assert(mScene->mMetaData == nullptr);
const bool hasVersion = !a.asset.version.empty();
const bool hasGenerator = !a.asset.generator.empty();
if (hasVersion || hasGenerator)
{
mScene->mMetaData = new aiMetadata;
if (hasVersion)
{
mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version));
}
if (hasGenerator)
{
mScene->mMetaData->Add(AI_METADATA_SOURCE_GENERATOR, aiString(a.asset.generator));
}
}
}
void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{ {
// clean all member arrays // clean all member arrays
@ -723,7 +743,7 @@ void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOS
ImportLights(asset); ImportLights(asset);
ImportNodes(asset); ImportNodes(asset);
ImportCommonMetadata(asset);
if (pScene->mNumMeshes == 0) { if (pScene->mNumMeshes == 0) {
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;

View File

@ -83,7 +83,7 @@ private:
void ImportCameras(glTF::Asset& a); void ImportCameras(glTF::Asset& a);
void ImportLights(glTF::Asset& a); void ImportLights(glTF::Asset& a);
void ImportNodes(glTF::Asset& a); void ImportNodes(glTF::Asset& a);
void ImportCommonMetadata(glTF::Asset& a);
}; };
} // Namespace assimp } // Namespace assimp

View File

@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp> #include <assimp/Importer.hpp>
#include <assimp/commonMetaData.h>
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
@ -1302,6 +1303,24 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
} }
} }
void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) {
ai_assert(mScene->mMetaData == nullptr);
const bool hasVersion = !a.asset.version.empty();
const bool hasGenerator = !a.asset.generator.empty();
if (hasVersion || hasGenerator)
{
mScene->mMetaData = new aiMetadata;
if (hasVersion)
{
mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version));
}
if (hasGenerator)
{
mScene->mMetaData->Add(AI_METADATA_SOURCE_GENERATOR, aiString(a.asset.generator));
}
}
}
void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
// clean all member arrays // clean all member arrays
meshOffsets.clear(); meshOffsets.clear();
@ -1329,6 +1348,8 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
ImportAnimations(asset); ImportAnimations(asset);
ImportCommonMetadata(asset);
if (pScene->mNumMeshes == 0) { if (pScene->mNumMeshes == 0) {
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
} }

View File

@ -84,6 +84,7 @@ private:
void ImportLights(glTF2::Asset& a); void ImportLights(glTF2::Asset& a);
void ImportNodes(glTF2::Asset& a); void ImportNodes(glTF2::Asset& a);
void ImportAnimations(glTF2::Asset& a); void ImportAnimations(glTF2::Asset& a);
void ImportCommonMetadata(glTF2::Asset& a);
}; };
} // Namespace assimp } // Namespace assimp

View File

@ -72,7 +72,7 @@ for(LineSplitter splitter(stream);splitter;++splitter) {
if (strtol(splitter[2]) > 5) { .. } if (strtol(splitter[2]) > 5) { .. }
} }
std::cout << "Current line is: " << splitter.get_index() << std::endl; ASSIMP_LOG_DEBUG_F("Current line is: ", splitter.get_index());
} }
@endcode @endcode
*/ */

View File

@ -0,0 +1,63 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp 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 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 commonMetaData.h
* @brief Defines a set of common scene metadata keys.
*/
#pragma once
#ifndef AI_COMMONMETADATA_H_INC
#define AI_COMMONMETADATA_H_INC
/// Scene metadata holding the name of the importer which loaded the source asset.
/// This is always present if the scene was created from an imported asset.
#define AI_METADATA_SOURCE_FORMAT "SourceAsset_Format"
/// Scene metadata holding the version of the source asset as a string, if available.
/// Not all formats add this metadata.
#define AI_METADATA_SOURCE_FORMAT_VERSION "SourceAsset_FormatVersion"
/// Scene metadata holding the name of the software which generated the source asset, if available.
/// Not all formats add this metadata.
#define AI_METADATA_SOURCE_GENERATOR "SourceAsset_Generator"
#endif

View File

@ -286,8 +286,8 @@ struct aiMetadata {
new_values[i] = mValues[i]; new_values[i] = mValues[i];
} }
delete mKeys; delete[] mKeys;
delete mValues; delete[] mValues;
mKeys = new_keys; mKeys = new_keys;
mValues = new_values; mValues = new_values;
@ -377,6 +377,23 @@ struct aiMetadata {
return true; return true;
} }
/// Check whether there is a metadata entry for the given key.
/// \param [in] Key - the key value value to check for.
inline
bool HasKey(const char* key) {
if ( nullptr == key ) {
return false;
}
// Search for the given key
for (unsigned int i = 0; i < mNumProperties; ++i) {
if ( 0 == strncmp(mKeys[i].C_Str(), key, mKeys[i].length ) ) {
return true;
}
}
return false;
}
#endif // __cplusplus #endif // __cplusplus
}; };

View File

@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/material.h> #include <assimp/material.h>
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/types.h> #include <assimp/types.h>
#include <assimp/commonMetaData.h>
using namespace Assimp; using namespace Assimp;
@ -283,3 +284,29 @@ TEST_F(utFBXImporterExporter, importOrphantEmbeddedTextureTest) {
ASSERT_TRUE(scene->mTextures[0]->pcData); ASSERT_TRUE(scene->mTextures[0]->pcData);
ASSERT_EQ(9026u, scene->mTextures[0]->mWidth) << "FBX ASCII base64 compression used for a texture."; ASSERT_EQ(9026u, scene->mTextures[0]->mWidth) << "FBX ASCII base64 compression used for a texture.";
} }
TEST_F(utFBXImporterExporter, sceneMetadata) {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/global_settings.fbx",
aiProcess_ValidateDataStructure);
ASSERT_NE(scene, nullptr);
ASSERT_NE(scene->mMetaData, nullptr);
{
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT));
aiString format;
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, format));
ASSERT_EQ(strcmp(format.C_Str(), "Autodesk FBX Importer"), 0);
}
{
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT_VERSION));
aiString version;
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, version));
ASSERT_EQ(strcmp(version.C_Str(), "7400"), 0);
}
{
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_GENERATOR));
aiString generator;
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, generator));
ASSERT_EQ(strncmp(generator.C_Str(), "Blender", 7), 0);
}
}

View File

@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/Exporter.hpp> #include <assimp/Exporter.hpp>
#include <assimp/postprocess.h> #include <assimp/postprocess.h>
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/commonMetaData.h>
#include <array> #include <array>
#include <assimp/pbrmaterial.h> #include <assimp/pbrmaterial.h>
@ -439,6 +441,32 @@ TEST_F(utglTF2ImportExport, error_string_preserved) {
#endif // ASSIMP_BUILD_NO_EXPORT #endif // ASSIMP_BUILD_NO_EXPORT
TEST_F(utglTF2ImportExport, sceneMetadata) {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf",
aiProcess_ValidateDataStructure);
ASSERT_NE(scene, nullptr);
ASSERT_NE(scene->mMetaData, nullptr);
{
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT));
aiString format;
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, format));
ASSERT_EQ(strcmp(format.C_Str(), "glTF2 Importer"), 0);
}
{
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT_VERSION));
aiString version;
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, version));
ASSERT_EQ(strcmp(version.C_Str(), "2.0"), 0);
}
{
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_GENERATOR));
aiString generator;
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, generator));
ASSERT_EQ(strcmp(generator.C_Str(), "COLLADA2GLTF"), 0);
}
}
TEST_F(utglTF2ImportExport, texcoords) { TEST_F(utglTF2ImportExport, texcoords) {
Assimp::Importer importer; Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf",

View File

@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/postprocess.h> #include <assimp/postprocess.h>
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/commonMetaData.h>
using namespace Assimp; using namespace Assimp;
@ -85,3 +86,28 @@ TEST_F(utglTFImportExport, incorrect_vertex_arrays) {
EXPECT_EQ(scene->mMeshes[7]->mNumVertices, 35u); EXPECT_EQ(scene->mMeshes[7]->mNumVertices, 35u);
EXPECT_EQ(scene->mMeshes[7]->mNumFaces, 17u); EXPECT_EQ(scene->mMeshes[7]->mNumFaces, 17u);
} }
TEST_F(utglTFImportExport, sceneMetadata) {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF/TwoBoxes/TwoBoxes.gltf", aiProcess_ValidateDataStructure);
ASSERT_TRUE(scene);
ASSERT_TRUE(scene->mMetaData);
{
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT));
aiString format;
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, format));
ASSERT_EQ(strcmp(format.C_Str(), "glTF Importer"), 0);
}
{
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT_VERSION));
aiString version;
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, version));
ASSERT_EQ(strcmp(version.C_Str(), "1.0"), 0);
}
{
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_GENERATOR));
aiString generator;
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, generator));
ASSERT_EQ(strncmp(generator.C_Str(), "collada2gltf", 12), 0);
}
}