diff --git a/code/Assimp.cpp b/code/Assimp.cpp index 2a9b896e7..b682d257b 100644 --- a/code/Assimp.cpp +++ b/code/Assimp.cpp @@ -272,8 +272,6 @@ void aiReleaseImport( const aiScene* pScene) ASSIMP_BEGIN_EXCEPTION_REGION(); - aiReleaseDefaultMaterial(); - // find the importer associated with this data const ScenePrivateData* priv = ScenePriv(pScene); if( !priv || !priv->mOrigImporter) { diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index e02c5f41d..e1a0d92d0 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -297,8 +297,9 @@ private: return false; } + //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) const size_t len( strlen( color ) ); - if ( 9 != len ) { + if ( 9 != len && 7 != len) { return false; } @@ -313,26 +314,28 @@ private: ++buf; comp[ 1 ] = *buf; ++buf; - diffuse.r = static_cast( strtol( comp, NULL, 16 ) ); + diffuse.r = static_cast( strtol( comp, NULL, 16 ) ) / 255.0; comp[ 0 ] = *buf; ++buf; comp[ 1 ] = *buf; ++buf; - diffuse.g = static_cast< ai_real >( strtol( comp, NULL, 16 ) ); + diffuse.g = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / 255.0; comp[ 0 ] = *buf; ++buf; comp[ 1 ] = *buf; ++buf; - diffuse.b = static_cast< ai_real >( strtol( comp, NULL, 16 ) ); + diffuse.b = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / 255.0; + if(7 == len) + return true; comp[ 0 ] = *buf; ++buf; comp[ 1 ] = *buf; ++buf; - diffuse.a = static_cast< ai_real >( strtol( comp, NULL, 16 ) ); + diffuse.a = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / 255.0; return true; } diff --git a/code/Importer.cpp b/code/Importer.cpp index 7aabe0a5f..32bec9414 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -178,7 +178,6 @@ Importer::~Importer() { // Delete all import plugins DeleteImporterInstanceList(pimpl->mImporter); - aiReleaseDefaultMaterial(); // Delete all post-processing plug-ins for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) @@ -385,7 +384,6 @@ void Importer::FreeScene( ) { ASSIMP_BEGIN_EXCEPTION_REGION(); - aiReleaseDefaultMaterial(); delete pimpl->mScene; pimpl->mScene = NULL; diff --git a/code/MaterialSystem.cpp b/code/MaterialSystem.cpp index 3679c93c7..fa8fb819c 100644 --- a/code/MaterialSystem.cpp +++ b/code/MaterialSystem.cpp @@ -387,26 +387,6 @@ aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat, return AI_SUCCESS; } -static aiMaterial *DefaultMaterial = nullptr; - -// ------------------------------------------------------------------------------------------------ -// Will return the default material. -aiMaterial *aiCreateAndRegisterDefaultMaterial() { - if (nullptr == DefaultMaterial) { - DefaultMaterial = new aiMaterial; - aiString s; - s.Set(AI_DEFAULT_MATERIAL_NAME); - DefaultMaterial->AddProperty(&s, AI_MATKEY_NAME); - } - - return DefaultMaterial; -} - -// ------------------------------------------------------------------------------------------------ -// Will return the default material. -void aiReleaseDefaultMaterial() { - DefaultMaterial = nullptr; -} static const unsigned int DefaultNumAllocated = 5; diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index 2b6730e4e..93d48ba16 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -211,12 +211,29 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene } if (pModel->m_Objects.size() > 0) { + + unsigned int meshCount = 0; + unsigned int childCount = 0; + + for(size_t index = 0; index < pModel->m_Objects.size(); ++index) { + if(pModel->m_Objects[index]) { + ++childCount; + meshCount += (unsigned int)pModel->m_Objects[index]->m_Meshes.size(); + } + } + + // Allocate space for the child nodes on the root node + pScene->mRootNode->mChildren = new aiNode*[ childCount ]; + // Create nodes for the whole scene std::vector MeshArray; + MeshArray.reserve(meshCount); for (size_t index = 0; index < pModel->m_Objects.size(); ++index) { createNodes(pModel, pModel->m_Objects[index], pScene->mRootNode, pScene, MeshArray); } + ai_assert(pScene->mRootNode->mNumChildren == childCount); + // Create mesh pointer buffer for this scene if (pScene->mNumMeshes > 0) { pScene->mMeshes = new aiMesh*[MeshArray.size()]; @@ -287,9 +304,8 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile pNode->mName = pObject->m_strObjName; // If we have a parent node, store it - if( pParent != NULL ) { - appendChildToParentNode( pParent, pNode ); - } + ai_assert( NULL != pParent ); + appendChildToParentNode( pParent, pNode ); for ( size_t i=0; i< pObject->m_Meshes.size(); ++i ) { unsigned int meshId = pObject->m_Meshes[ i ]; @@ -442,8 +458,8 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel, pMesh->mNumVertices = numIndices; if (pMesh->mNumVertices == 0) { throw DeadlyImportError( "OBJ: no vertices" ); - } else if (pMesh->mNumVertices > AI_MAX_ALLOC(aiVector3D)) { - throw DeadlyImportError( "OBJ: Too many vertices, would run out of memory" ); + } else if (pMesh->mNumVertices > AI_MAX_VERTICES) { + throw DeadlyImportError( "OBJ: Too many vertices" ); } pMesh->mVertices = new aiVector3D[ pMesh->mNumVertices ]; @@ -770,25 +786,8 @@ void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild) // Assign parent to child pChild->mParent = pParent; - // If already children was assigned to the parent node, store them in a - std::vector temp; - if (pParent->mChildren != NULL) - { - ai_assert( 0 != pParent->mNumChildren ); - for (size_t index = 0; index < pParent->mNumChildren; index++) - { - temp.push_back(pParent->mChildren [ index ] ); - } - delete [] pParent->mChildren; - } - // Copy node instances into parent node pParent->mNumChildren++; - pParent->mChildren = new aiNode*[ pParent->mNumChildren ]; - for (size_t index = 0; index < pParent->mNumChildren-1; index++) - { - pParent->mChildren[ index ] = temp [ index ]; - } pParent->mChildren[ pParent->mNumChildren-1 ] = pChild; } diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 3f8c810fa..fb866bb7a 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -214,11 +214,10 @@ void STLImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS // create a single default material, using a white diffuse color for consistency with // other geometric types (e.g., PLY). - aiMaterial* pcMat = aiCreateAndRegisterDefaultMaterial(); - /*aiMaterial* pcMat = new aiMaterial(); + aiMaterial* pcMat = new aiMaterial(); aiString s; s.Set(AI_DEFAULT_MATERIAL_NAME); - pcMat->AddProperty(&s, AI_MATKEY_NAME);*/ + pcMat->AddProperty(&s, AI_MATKEY_NAME); aiColor4D clrDiffuse(ai_real(1.0),ai_real(1.0),ai_real(1.0),ai_real(1.0)); if (bMatClr) { diff --git a/include/assimp/material.h b/include/assimp/material.h index 6564e047b..911853b4b 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -1565,26 +1565,6 @@ C_ENUM aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat, unsigned int* flags /*= NULL*/); #endif // !#ifdef __cplusplus -// --------------------------------------------------------------------------- -/** @brief Helper function to get all values pertaining to a particular -* texture slot from a material structure. -* -* @return Pointer showing to the default material. -*/ -// --------------------------------------------------------------------------- -#ifdef __cplusplus -ASSIMP_API aiMaterial *aiCreateAndRegisterDefaultMaterial(void); -#else -C_STRUCT aiMaterial *aiCreateAndRegisterDefaultMaterial(void); -#endif // !#ifdef __cplusplus - -// --------------------------------------------------------------------------- -/** - * @brief Helper function to release the default material instance, the - * instance will not be destroyed. - */ -// --------------------------------------------------------------------------- -ASSIMP_API void aiReleaseDefaultMaterial(); #ifdef __cplusplus } diff --git a/port/PyAssimp/pyassimp/core.py b/port/PyAssimp/pyassimp/core.py index 64d6d69c6..50d2f9a1a 100644 --- a/port/PyAssimp/pyassimp/core.py +++ b/port/PyAssimp/pyassimp/core.py @@ -66,6 +66,13 @@ def make_tuple(ai_obj, type = None): return res +# Returns unicode object for Python 2, and str object for Python 3. +def _convert_assimp_string(assimp_string): + try: + return unicode(assimp_string.data, errors='ignore') + except: + return str(assimp_string.data, errors='ignore') + # It is faster and more correct to have an init function for each assimp class def _init_face(aiFace): aiFace.indices = [aiFace.mIndices[i] for i in range(aiFace.mNumIndices)] @@ -118,14 +125,9 @@ def _init(self, target = None, parent = None): continue if m == 'mName': - obj = self.mName - try: - uni = unicode(obj.data, errors='ignore') - except: - uni = str(obj.data, errors='ignore') - target.name = str( uni ) - target.__class__.__repr__ = lambda x: str(x.__class__) + "(" + getattr(x, 'name','') + ")" - target.__class__.__str__ = lambda x: getattr(x, 'name', '') + target.name = str(_convert_assimp_string(self.mName)) + target.__class__.__repr__ = lambda x: str(x.__class__) + "(" + getattr(x, 'name','') + ")" + target.__class__.__str__ = lambda x: getattr(x, 'name', '') continue name = m[1:].lower() @@ -220,6 +222,9 @@ def _init(self, target = None, parent = None): if isinstance(self, structs.Texture): _finalize_texture(self, target) + if isinstance(self, structs.Metadata): + _finalize_metadata(self, target) + return self @@ -412,6 +417,43 @@ def _finalize_mesh(mesh, target): faces = [f.indices for f in target.faces] setattr(target, 'faces', faces) +def _init_metadata_entry(entry): + from ctypes import POINTER, c_bool, c_int32, c_uint64, c_float, c_double, cast + + entry.type = entry.mType + if entry.type == structs.MetadataEntry.AI_BOOL: + entry.data = cast(entry.mData, POINTER(c_bool)).contents.value + elif entry.type == structs.MetadataEntry.AI_INT32: + entry.data = cast(entry.mData, POINTER(c_int32)).contents.value + elif entry.type == structs.MetadataEntry.AI_UINT64: + entry.data = cast(entry.mData, POINTER(c_uint64)).contents.value + elif entry.type == structs.MetadataEntry.AI_FLOAT: + entry.data = cast(entry.mData, POINTER(c_float)).contents.value + elif entry.type == structs.MetadataEntry.AI_DOUBLE: + entry.data = cast(entry.mData, POINTER(c_double)).contents.value + elif entry.type == structs.MetadataEntry.AI_AISTRING: + assimp_string = cast(entry.mData, POINTER(structs.String)).contents + entry.data = _convert_assimp_string(assimp_string) + elif entry.type == structs.MetadataEntry.AI_AIVECTOR3D: + assimp_vector = cast(entry.mData, POINTER(structs.Vector3D)).contents + entry.data = make_tuple(assimp_vector) + + return entry + +def _finalize_metadata(metadata, target): + """ Building the metadata object is a bit specific. + + Firstly, there are two separate arrays: one with metadata keys and one + with metadata values, and there are no corresponding mNum* attributes, + so the C arrays are not converted to Python arrays using the generic + code in the _init function. + + Secondly, a metadata entry value has to be cast according to declared + metadata entry type. + """ + length = metadata.mNumProperties + setattr(target, 'keys', [str(_convert_assimp_string(metadata.mKeys[i])) for i in range(length)]) + setattr(target, 'values', [_init_metadata_entry(metadata.mValues[i]) for i in range(length)]) class PropertyGetter(dict): def __getitem__(self, key): @@ -443,11 +485,8 @@ def _get_properties(properties, length): for p in [properties[i] for i in range(length)]: #the name p = p.contents - try: - uni = unicode(p.mKey.data, errors='ignore') - except: - uni = str(p.mKey.data, errors='ignore') - key = (str(uni).split('.')[1], p.mSemantic) + key = str(_convert_assimp_string(p.mKey)) + key = (key.split('.')[1], p.mSemantic) #the data from ctypes import POINTER, cast, c_int, c_float, sizeof @@ -455,11 +494,7 @@ def _get_properties(properties, length): arr = cast(p.mData, POINTER(c_float * int(p.mDataLength/sizeof(c_float)) )).contents value = [x for x in arr] elif p.mType == 3: #string can't be an array - try: - uni = unicode(cast(p.mData, POINTER(structs.MaterialPropertyString)).contents.data, errors='ignore') - except: - uni = str(cast(p.mData, POINTER(structs.MaterialPropertyString)).contents.data, errors='ignore') - value = uni + value = _convert_assimp_string(cast(p.mData, POINTER(structs.MaterialPropertyString)).contents) elif p.mType == 4: arr = cast(p.mData, POINTER(c_int * int(p.mDataLength/sizeof(c_int)) )).contents diff --git a/port/PyAssimp/pyassimp/structs.py b/port/PyAssimp/pyassimp/structs.py index 0a7742705..7cd8e634c 100644 --- a/port/PyAssimp/pyassimp/structs.py +++ b/port/PyAssimp/pyassimp/structs.py @@ -291,7 +291,7 @@ Node._fields_ = [ # Metadata associated with this node or NULL if there is no metadata. # Whether any metadata is generated depends on the source file format. - ("mMetadata", POINTER(POINTER(Metadata))), + ("mMetadata", POINTER(Metadata)), ] class Light(Structure): @@ -939,7 +939,7 @@ class Scene(Structure): # This data contains global metadata which belongs to the scene like # unit-conversions, versions, vendors or other model-specific data. This # can be used to store format-specific metadata as well. - ("mMetadata", POINTER(POINTER(Metadata))), + ("mMetadata", POINTER(Metadata)), ] assimp_structs_as_tuple = (Matrix4x4, diff --git a/test/unit/utMaterialSystem.cpp b/test/unit/utMaterialSystem.cpp index 550dc63da..55ee6e2a0 100644 --- a/test/unit/utMaterialSystem.cpp +++ b/test/unit/utMaterialSystem.cpp @@ -129,18 +129,10 @@ TEST_F(MaterialSystemTest, testStringProperty) { EXPECT_STREQ("Hello, this is a small test", s.data); } -// ------------------------------------------------------------------------------------------------ -TEST_F(MaterialSystemTest, testDefaultMaterialAccess) { - aiMaterial *mat = aiCreateAndRegisterDefaultMaterial(); - EXPECT_NE(nullptr, mat); - aiReleaseDefaultMaterial(); - - delete mat; -} // ------------------------------------------------------------------------------------------------ TEST_F(MaterialSystemTest, testMaterialNameAccess) { - aiMaterial *mat = aiCreateAndRegisterDefaultMaterial(); + aiMaterial *mat = new aiMaterial(); EXPECT_NE(nullptr, mat); aiString name = mat->GetName(); diff --git a/test/unit/utSTLImportExport.cpp b/test/unit/utSTLImportExport.cpp index 9ebfb0f05..ee62253fd 100644 --- a/test/unit/utSTLImportExport.cpp +++ b/test/unit/utSTLImportExport.cpp @@ -67,6 +67,20 @@ TEST_F( utSTLImporterExporter, importSTLFromFileTest ) { EXPECT_TRUE( importerTest() ); } + +TEST_F(utSTLImporterExporter, test_multiple) { + // import same file twice, each with its own importer + // must work both times and not crash + Assimp::Importer importer1; + const aiScene *scene1 = importer1.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/Spider_ascii.stl", aiProcess_ValidateDataStructure ); + EXPECT_NE(nullptr, scene1); + + Assimp::Importer importer2; + const aiScene *scene2 = importer2.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/Spider_ascii.stl", aiProcess_ValidateDataStructure ); + EXPECT_NE(nullptr, scene2); +} + + TEST_F( utSTLImporterExporter, test_with_two_solids ) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/triangle_with_two_solids.stl", aiProcess_ValidateDataStructure );