diff --git a/.travis.yml b/.travis.yml index 9265dfb38..0b8d2f328 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,9 +33,10 @@ env: matrix: include: - - os: linux - compiler: clang - env: ANALYZE=ON + # disabled until clang 5.0 analyzer issues are fixed + # - os: linux + # compiler: clang + # env: ANALYZE=ON - os: linux compiler: clang env: ASAN=ON diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index bc888fb66..aa87609d4 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -93,7 +93,6 @@ void DeleteAllBarePointers(std::vector& x) B3DImporter::~B3DImporter() { - DeleteAllBarePointers(_animations); } // ------------------------------------------------------------------------------------------------ @@ -267,6 +266,21 @@ T *B3DImporter::to_array( const vector &v ){ return p; } + +// ------------------------------------------------------------------------------------------------ +template +T **unique_to_array( vector > &v ){ + if( v.empty() ) { + return 0; + } + T **p = new T*[ v.size() ]; + for( size_t i = 0; i < v.size(); ++i ){ + p[i] = v[i].release(); + } + return p; +} + + // ------------------------------------------------------------------------------------------------ void B3DImporter::ReadTEXS(){ while( ChunkSize() ){ @@ -295,8 +309,7 @@ void B3DImporter::ReadBRUS(){ /*int blend=**/ReadInt(); int fx=ReadInt(); - aiMaterial *mat=new aiMaterial; - _materials.push_back( mat ); + std::unique_ptr mat(new aiMaterial); // Name aiString ainame( name ); @@ -333,6 +346,7 @@ void B3DImporter::ReadBRUS(){ mat->AddProperty( &texname,AI_MATKEY_TEXTURE_DIFFUSE(0) ); } } + _materials.emplace_back( std::move(mat) ); } } @@ -386,8 +400,7 @@ void B3DImporter::ReadTRIS( int v0 ){ Fail( "Bad material id" ); } - aiMesh *mesh=new aiMesh; - _meshes.push_back( mesh ); + std::unique_ptr mesh(new aiMesh); mesh->mMaterialIndex=matid; mesh->mNumFaces=0; @@ -415,6 +428,8 @@ void B3DImporter::ReadTRIS( int v0 ){ ++mesh->mNumFaces; ++face; } + + _meshes.emplace_back( std::move(mesh) ); } // ------------------------------------------------------------------------------------------------ @@ -500,11 +515,11 @@ void B3DImporter::ReadANIM(){ int frames=ReadInt(); float fps=ReadFloat(); - aiAnimation *anim=new aiAnimation; - _animations.push_back( anim ); + std::unique_ptr anim(new aiAnimation); anim->mDuration=frames; anim->mTicksPerSecond=fps; + _animations.emplace_back( std::move(anim) ); } // ------------------------------------------------------------------------------------------------ @@ -531,7 +546,7 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){ node->mParent=parent; node->mTransformation=tform; - aiNodeAnim *nodeAnim=0; + std::unique_ptr nodeAnim; vector meshes; vector children; @@ -549,11 +564,10 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){ ReadANIM(); }else if( t=="KEYS" ){ if( !nodeAnim ){ - nodeAnim=new aiNodeAnim; - _nodeAnims.push_back( nodeAnim ); + nodeAnim.reset(new aiNodeAnim); nodeAnim->mNodeName=node->mName; } - ReadKEYS( nodeAnim ); + ReadKEYS( nodeAnim.get() ); }else if( t=="NODE" ){ aiNode *child=ReadNODE( node ); children.push_back( child ); @@ -561,6 +575,10 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){ ExitChunk(); } + if (nodeAnim) { + _nodeAnims.emplace_back( std::move(nodeAnim) ); + } + node->mNumMeshes= static_cast(meshes.size()); node->mMeshes=to_array( meshes ); @@ -586,7 +604,6 @@ void B3DImporter::ReadBB3D( aiScene *scene ){ _nodeAnims.clear(); - DeleteAllBarePointers(_animations); _animations.clear(); string t=ReadChunk(); @@ -622,7 +639,7 @@ void B3DImporter::ReadBB3D( aiScene *scene ){ aiNode *node=_nodes[i]; for( size_t j=0;jmNumMeshes;++j ){ - aiMesh *mesh=_meshes[node->mMeshes[j]]; + aiMesh *mesh = _meshes[node->mMeshes[j]].get(); int n_tris=mesh->mNumFaces; int n_verts=mesh->mNumVertices=n_tris * 3; @@ -685,27 +702,28 @@ void B3DImporter::ReadBB3D( aiScene *scene ){ //nodes scene->mRootNode=_nodes[0]; + _nodes.clear(); // node ownership now belongs to scene //material if( !_materials.size() ){ - _materials.push_back( new aiMaterial ); + _materials.emplace_back( std::unique_ptr(new aiMaterial) ); } scene->mNumMaterials= static_cast(_materials.size()); - scene->mMaterials=to_array( _materials ); + scene->mMaterials = unique_to_array( _materials ); //meshes scene->mNumMeshes= static_cast(_meshes.size()); - scene->mMeshes=to_array( _meshes ); + scene->mMeshes = unique_to_array( _meshes ); //animations if( _animations.size()==1 && _nodeAnims.size() ){ - aiAnimation *anim=_animations.back(); + aiAnimation *anim = _animations.back().get(); anim->mNumChannels=static_cast(_nodeAnims.size()); - anim->mChannels=to_array( _nodeAnims ); + anim->mChannels = unique_to_array( _nodeAnims ); scene->mNumAnimations=static_cast(_animations.size()); - scene->mAnimations=to_array( _animations ); + scene->mAnimations=unique_to_array( _animations ); } // convert to RH diff --git a/code/B3DImporter.h b/code/B3DImporter.h index 94644edd4..342b88a28 100644 --- a/code/B3DImporter.h +++ b/code/B3DImporter.h @@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "BaseImporter.h" +#include #include struct aiNodeAnim; @@ -116,15 +117,15 @@ private: std::vector _stack; std::vector _textures; - std::vector _materials; + std::vector > _materials; int _vflags,_tcsets,_tcsize; std::vector _vertices; std::vector _nodes; - std::vector _meshes; - std::vector _nodeAnims; - std::vector _animations; + std::vector > _meshes; + std::vector > _nodeAnims; + std::vector > _animations; }; } diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 0281e4e99..eebecd859 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -92,6 +92,7 @@ void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperti void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); @@ -151,6 +152,8 @@ Exporter::ExportFormatEntry gExporters[] = aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf2", &ExportSceneGLTF2, aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), + Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb2", &ExportSceneGLB2, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), #endif #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER @@ -420,7 +423,7 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId; ASSIMP_END_EXCEPTION_REGION(aiReturn); - + return AI_FAILURE; } diff --git a/code/FBXBinaryTokenizer.cpp b/code/FBXBinaryTokenizer.cpp index 519f2e176..cc2e734fc 100644 --- a/code/FBXBinaryTokenizer.cpp +++ b/code/FBXBinaryTokenizer.cpp @@ -434,6 +434,14 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int le TokenizeError("file is too short",0); } + //uint32_t offset = 0x15; +/* const char* cursor = input + 0x15; + + const uint32_t flags = ReadWord(input, cursor, input + length); + + const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused + const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused*/ + if (strncmp(input,"Kaydara FBX Binary",18)) { TokenizeError("magic bytes not found",0); } diff --git a/code/MD5Loader.h b/code/MD5Loader.h index afb07a62d..13a01451d 100644 --- a/code/MD5Loader.h +++ b/code/MD5Loader.h @@ -145,7 +145,7 @@ protected: // ------------------------------------------------------------------- /** Load the contents of a specific file into memory and - * alocates a buffer to keep it. + * allocates a buffer to keep it. * * mBuffer is modified to point to this buffer. * @param pFile File stream to be read diff --git a/code/MDLLoader.cpp b/code/MDLLoader.cpp index 3a9f2c344..643b3882f 100644 --- a/code/MDLLoader.cpp +++ b/code/MDLLoader.cpp @@ -415,9 +415,15 @@ void MDLImporter::InternReadFile_Quake1( ) else { // get the first frame in the group - //BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames; - //pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type); - ai_assert( false && "Unaligned pointer" ); + +#if 1 + // FIXME: the cast is wrong and causea a warning on clang 5.0 + // disable thi code for now, fix it later + ai_assert(false && "Bad pointer cast"); +#else + BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames; + pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type); +#endif } BE_NCONST MDL::Vertex* pcVertices = (BE_NCONST MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name)); VALIDATE_FILE_SIZE((const unsigned char*)(pcVertices + pcHeader->num_verts)); diff --git a/code/PlyLoader.cpp b/code/PlyLoader.cpp index c6e862bf1..7c4614474 100644 --- a/code/PlyLoader.cpp +++ b/code/PlyLoader.cpp @@ -91,9 +91,9 @@ namespace // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer PLYImporter::PLYImporter() - : mBuffer() - , pcDOM() - , mGeneratedMesh(NULL){ + : mBuffer(nullptr) + , pcDOM(nullptr) + , mGeneratedMesh(nullptr){ // empty } @@ -196,7 +196,10 @@ void PLYImporter::InternReadFile(const std::string& pFile, if (!PLY::DOM::ParseInstance(streamedBuffer, &sPlyDom, this)) { if (mGeneratedMesh != NULL) + { delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } streamedBuffer.close(); throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#1)"); @@ -211,7 +214,10 @@ void PLYImporter::InternReadFile(const std::string& pFile, if (!PLY::DOM::ParseInstanceBinary(streamedBuffer, &sPlyDom, this, bIsBE)) { if (mGeneratedMesh != NULL) + { delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } streamedBuffer.close(); throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#2)"); @@ -220,7 +226,10 @@ void PLYImporter::InternReadFile(const std::string& pFile, else { if (mGeneratedMesh != NULL) + { delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } streamedBuffer.close(); throw DeadlyImportError("Invalid .ply file: Unknown file format"); @@ -230,7 +239,10 @@ void PLYImporter::InternReadFile(const std::string& pFile, { AI_DEBUG_INVALIDATE_PTR(this->mBuffer); if (mGeneratedMesh != NULL) + { delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } streamedBuffer.close(); throw DeadlyImportError("Invalid .ply file: Missing format specification"); @@ -252,7 +264,10 @@ void PLYImporter::InternReadFile(const std::string& pFile, if (mGeneratedMesh->mNumVertices < 3) { if (mGeneratedMesh != NULL) + { delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } streamedBuffer.close(); throw DeadlyImportError("Invalid .ply file: Not enough " @@ -289,6 +304,7 @@ void PLYImporter::InternReadFile(const std::string& pFile, pScene->mNumMeshes = 1; pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; pScene->mMeshes[0] = mGeneratedMesh; + mGeneratedMesh = nullptr; // generate a simple node structure pScene->mRootNode = new aiNode(); diff --git a/code/StreamReader.h b/code/StreamReader.h index 6220de9a8..b70ee7eca 100644 --- a/code/StreamReader.h +++ b/code/StreamReader.h @@ -192,7 +192,7 @@ public: // --------------------------------------------------------------------- /** Increase the file pointer (relative seeking) */ - void IncPtr(size_t plus) { + void IncPtr(intptr_t plus) { current += plus; if (current > limit) { throw DeadlyImportError("End of file or read limit was reached"); diff --git a/code/glTF2AssetWriter.h b/code/glTF2AssetWriter.h index bce2b1bd1..b4e7ffc2e 100644 --- a/code/glTF2AssetWriter.h +++ b/code/glTF2AssetWriter.h @@ -81,6 +81,7 @@ public: AssetWriter(Asset& asset); void WriteFile(const char* path); + void WriteGLBFile(const char* path); }; } diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 8b2769a37..6b1a50887 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -561,6 +561,97 @@ namespace glTF2 { } } + inline void AssetWriter::WriteGLBFile(const char* path) + { + std::unique_ptr outfile(mAsset.OpenFile(path, "wb", true)); + + if (outfile == 0) { + throw DeadlyExportError("Could not open output file: " + std::string(path)); + } + + // Padding with spaces as required by the spec + uint32_t padding = 0x20202020; + + // Adapt JSON so that it is not pointing to an external file, + // as this is required by the GLB spec'. + mDoc["buffers"][0].RemoveMember("uri"); + + // + // JSON chunk + // + + StringBuffer docBuffer; + Writer writer(docBuffer); + mDoc.Accept(writer); + + uint32_t jsonChunkLength = (docBuffer.GetSize() + 3) & ~3; // Round up to next multiple of 4 + auto paddingLength = jsonChunkLength - docBuffer.GetSize(); + + GLB_Chunk jsonChunk; + jsonChunk.chunkLength = jsonChunkLength; + jsonChunk.chunkType = ChunkType_JSON; + AI_SWAP4(jsonChunk.chunkLength); + + outfile->Seek(sizeof(GLB_Header), aiOrigin_SET); + if (outfile->Write(&jsonChunk, 1, sizeof(GLB_Chunk)) != sizeof(GLB_Chunk)) { + throw DeadlyExportError("Failed to write scene data header!"); + } + if (outfile->Write(docBuffer.GetString(), 1, docBuffer.GetSize()) != docBuffer.GetSize()) { + throw DeadlyExportError("Failed to write scene data!"); + } + if (paddingLength && outfile->Write(&padding, 1, paddingLength) != paddingLength) { + throw DeadlyExportError("Failed to write scene data padding!"); + } + + // + // Binary chunk + // + + uint32_t binaryChunkLength = 0; + if (mAsset.buffers.Size() > 0) { + Ref b = mAsset.buffers.Get(0u); + if (b->byteLength > 0) { + binaryChunkLength = (b->byteLength + 3) & ~3; // Round up to next multiple of 4 + auto paddingLength = binaryChunkLength - b->byteLength; + + GLB_Chunk binaryChunk; + binaryChunk.chunkLength = binaryChunkLength; + binaryChunk.chunkType = ChunkType_BIN; + AI_SWAP4(binaryChunk.chunkLength); + + size_t bodyOffset = sizeof(GLB_Header) + sizeof(GLB_Chunk) + jsonChunk.chunkLength; + outfile->Seek(bodyOffset, aiOrigin_SET); + if (outfile->Write(&binaryChunk, 1, sizeof(GLB_Chunk)) != sizeof(GLB_Chunk)) { + throw DeadlyExportError("Failed to write body data header!"); + } + if (outfile->Write(b->GetPointer(), 1, b->byteLength) != b->byteLength) { + throw DeadlyExportError("Failed to write body data!"); + } + if (paddingLength && outfile->Write(&padding, 1, paddingLength) != paddingLength) { + throw DeadlyExportError("Failed to write body data padding!"); + } + } + } + + // + // Header + // + + GLB_Header header; + memcpy(header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)); + + header.version = 2; + AI_SWAP4(header.version); + + header.length = uint32_t(sizeof(GLB_Header) + 2 * sizeof(GLB_Chunk) + jsonChunkLength + binaryChunkLength); + AI_SWAP4(header.length); + + outfile->Seek(0, aiOrigin_SET); + if (outfile->Write(&header, 1, sizeof(GLB_Header)) != sizeof(GLB_Header)) { + throw DeadlyExportError("Failed to write the header!"); + } + } + inline void AssetWriter::WriteMetadata() { Value asset; diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index c1a803c1f..22ceb57d2 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -77,10 +77,18 @@ namespace Assimp { glTF2Exporter exporter(pFile, pIOSystem, pScene, pProperties, false); } + // ------------------------------------------------------------------------------------------------ + // Worker function for exporting a scene to GLB. Prototyped and registered in Exporter.cpp + void ExportSceneGLB2(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) + { + // invoke the exporter + glTF2Exporter exporter(pFile, pIOSystem, pScene, pProperties, true); + } + } // end of namespace Assimp glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene, - const ExportProperties* pProperties, bool /*isBinary*/) + const ExportProperties* pProperties, bool isBinary) : mFilename(filename) , mIOSystem(pIOSystem) , mProperties(pProperties) @@ -118,7 +126,11 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai AssetWriter writer(*mAsset); - writer.WriteFile(filename); + if (isBinary) { + writer.WriteGLBFile(filename); + } else { + writer.WriteFile(filename); + } } /* diff --git a/code/glTFExporter.h b/code/glTFExporter.h index c813fff44..752072604 100644 --- a/code/glTFExporter.h +++ b/code/glTFExporter.h @@ -45,7 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_GLTFEXPORTER_H_INC #define AI_GLTFEXPORTER_H_INC -#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER +#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER #include #include @@ -113,6 +113,6 @@ namespace Assimp } -#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER +#endif // ASSIMP_BUILD_NO_GLTF_EXPORTER #endif // AI_GLTFEXPORTER_H_INC diff --git a/include/assimp/material.h b/include/assimp/material.h index a12e7d076..502b89746 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -491,7 +491,7 @@ struct aiUVTransform } #endif -} PACK_STRUCT; +}; #include "./Compiler/poppack1.h" diff --git a/include/assimp/vector2.h b/include/assimp/vector2.h index df3c41024..73f765359 100644 --- a/include/assimp/vector2.h +++ b/include/assimp/vector2.h @@ -98,7 +98,7 @@ public: operator aiVector2t () const; TReal x, y; -} PACK_STRUCT; +}; typedef aiVector2t aiVector2D; diff --git a/include/assimp/vector2.inl b/include/assimp/vector2.inl index d6171955f..43694a8ff 100644 --- a/include/assimp/vector2.inl +++ b/include/assimp/vector2.inl @@ -130,6 +130,7 @@ TReal aiVector2t::operator[](unsigned int i) const { return y; default: break; + } return x; } diff --git a/test/unit/utPLYImportExport.cpp b/test/unit/utPLYImportExport.cpp index 82cc54bdb..dbb7f4292 100644 --- a/test/unit/utPLYImportExport.cpp +++ b/test/unit/utPLYImportExport.cpp @@ -85,6 +85,18 @@ TEST_F(utPLYImportExport, exportTest_Success ) { #endif // ASSIMP_BUILD_NO_EXPORT +//Test issue 1623, crash when loading two PLY files in a row +TEST_F(utPLYImportExport, importerMultipleTest) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0); + + EXPECT_NE(nullptr, scene); + + scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0); + + EXPECT_NE(nullptr, scene); +} + TEST_F( utPLYImportExport, vertexColorTest ) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/float-color.ply", 0 );