From 3402cd81c7c95f3c96db6b037023b94bd4c9ebc2 Mon Sep 17 00:00:00 2001 From: Vincent Fazio Date: Fri, 21 Sep 2018 10:31:21 +1000 Subject: [PATCH 1/6] Added interface to 'aiExportSceneToBlob()' for pyassimp --- port/PyAssimp/pyassimp/core.py | 29 ++++++++++++++++++++++++++++- port/PyAssimp/pyassimp/helper.py | 7 +++++-- port/PyAssimp/pyassimp/structs.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/port/PyAssimp/pyassimp/core.py b/port/PyAssimp/pyassimp/core.py index 50d2f9a1a..4668eeda2 100644 --- a/port/PyAssimp/pyassimp/core.py +++ b/port/PyAssimp/pyassimp/core.py @@ -35,7 +35,7 @@ class AssimpLib(object): """ Assimp-Singleton """ - load, load_mem, export, release, dll = helper.search_library() + load, load_mem, export, export_blob, release, dll = helper.search_library() _assimp_lib = AssimpLib() def make_tuple(ai_obj, type = None): @@ -352,6 +352,33 @@ def export(scene, if exportStatus != 0: raise AssimpError('Could not export scene!') +def export_blob(scene, + file_type = None, + processing = postprocess.aiProcess_Triangulate): + ''' + Export a scene and return a blob in the correct format. On failure throws AssimpError. + + Arguments + --------- + scene: scene to export. + file_type: string of file exporter to use. For example "collada". + processing: assimp postprocessing parameters. Verbose keywords are imported + from postprocessing, and the parameters can be combined bitwise to + generate the final processing value. Note that the default value will + triangulate quad faces. Example of generating other possible values: + processing = (pyassimp.postprocess.aiProcess_Triangulate | + pyassimp.postprocess.aiProcess_OptimizeMeshes) + Returns + --------- + ExportBlob + ''' + from ctypes import pointer + exportBlobPtr = _assimp_lib.export_blob(pointer(scene), file_type.encode("ascii"), processing) + + if exportBlobPtr == 0: + raise AssimpError('Could not export scene to blob!') + return exportBlobPtr + def release(scene): from ctypes import pointer _assimp_lib.release(pointer(scene)) diff --git a/port/PyAssimp/pyassimp/helper.py b/port/PyAssimp/pyassimp/helper.py index f9163de2a..55e8a9d2b 100644 --- a/port/PyAssimp/pyassimp/helper.py +++ b/port/PyAssimp/pyassimp/helper.py @@ -176,6 +176,7 @@ def try_load_functions(library_path, dll): load from filename function, load from memory function, export to filename function, + export to blob function, release function, ctypes handle to assimp library) ''' @@ -185,15 +186,17 @@ def try_load_functions(library_path, dll): release = dll.aiReleaseImport load_mem = dll.aiImportFileFromMemory export = dll.aiExportScene + export2blob = dll.aiExportSceneToBlob except AttributeError: #OK, this is a library, but it doesn't have the functions we need return None # library found! - from .structs import Scene + from .structs import Scene, ExportDataBlob load.restype = POINTER(Scene) load_mem.restype = POINTER(Scene) - return (library_path, load, load_mem, export, release, dll) + export2blob.restype = POINTER(ExportDataBlob) + return (library_path, load, load_mem, export, export2blob, release, dll) def search_library(): ''' diff --git a/port/PyAssimp/pyassimp/structs.py b/port/PyAssimp/pyassimp/structs.py index 15e50b14b..8a75c1e46 100644 --- a/port/PyAssimp/pyassimp/structs.py +++ b/port/PyAssimp/pyassimp/structs.py @@ -996,6 +996,37 @@ class Animation(Structure): ] +class ExportDataBlob(Structure): + """ + See 'cexport.h' for details. + """ + pass + +ExportDataBlob._fields_ = [ + # Size of the data in bytes + ("size", c_size_t), + + # The data. + ("data", c_void_p), + + # Name of the blob. An empty string always + # indicates the first (and primary) blob, + # which contains the actual file data. + # Any other blobs are auxiliary files produced + # by exporters (i.e. material files). Existence + # of such files depends on the file format. Most + # formats don't split assets across multiple files. + # + # If used, blob names usually contain the file + # extension that should be used when writing + # the data to disc. + ("name", String), + + # Pointer to the next blob in the chain or NULL if there is none. + ("next", POINTER(ExportDataBlob)), + ] + + class Scene(Structure): """ See 'aiScene.h' for details. From ef4e317625e8473077535055621b9779bb2109a4 Mon Sep 17 00:00:00 2001 From: Vincent Fazio Date: Fri, 21 Sep 2018 10:51:38 +1000 Subject: [PATCH 2/6] Improved some comments --- port/PyAssimp/pyassimp/core.py | 2 +- port/PyAssimp/pyassimp/structs.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/port/PyAssimp/pyassimp/core.py b/port/PyAssimp/pyassimp/core.py index 4668eeda2..64dd351a7 100644 --- a/port/PyAssimp/pyassimp/core.py +++ b/port/PyAssimp/pyassimp/core.py @@ -370,7 +370,7 @@ def export_blob(scene, pyassimp.postprocess.aiProcess_OptimizeMeshes) Returns --------- - ExportBlob + Pointer to structs.ExportDataBlob ''' from ctypes import pointer exportBlobPtr = _assimp_lib.export_blob(pointer(scene), file_type.encode("ascii"), processing) diff --git a/port/PyAssimp/pyassimp/structs.py b/port/PyAssimp/pyassimp/structs.py index 8a75c1e46..ddfd87f8a 100644 --- a/port/PyAssimp/pyassimp/structs.py +++ b/port/PyAssimp/pyassimp/structs.py @@ -999,6 +999,8 @@ class Animation(Structure): class ExportDataBlob(Structure): """ See 'cexport.h' for details. + + Note that the '_fields_' definition is outside the class to allow the 'next' field to be recursive """ pass From 8cb0b4ce2b5827fb0b847d749147c5b238e85f1f Mon Sep 17 00:00:00 2001 From: Vincent Fazio Date: Fri, 21 Sep 2018 11:02:14 +1000 Subject: [PATCH 3/6] Updated pyassimp function description --- port/PyAssimp/pyassimp/helper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/port/PyAssimp/pyassimp/helper.py b/port/PyAssimp/pyassimp/helper.py index 55e8a9d2b..4e9f10e94 100644 --- a/port/PyAssimp/pyassimp/helper.py +++ b/port/PyAssimp/pyassimp/helper.py @@ -206,6 +206,7 @@ def search_library(): Returns: tuple, (load from filename function, load from memory function, export to filename function, + export to blob function, release function, dll) ''' From b3e858956ab21a3a546b73dad8b27a803b07487e Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Fri, 21 Sep 2018 19:35:54 +0200 Subject: [PATCH 4/6] Fix #2149 Add flag to embed textures in assimp_cmd --- tools/assimp_cmd/Main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/assimp_cmd/Main.cpp b/tools/assimp_cmd/Main.cpp index 41412b93d..8a39540d1 100644 --- a/tools/assimp_cmd/Main.cpp +++ b/tools/assimp_cmd/Main.cpp @@ -466,6 +466,9 @@ int ProcessStandardArguments( else if (! strcmp( param, "-sbc") || ! strcmp( param, "--split-by-bone-count")) { fill.ppFlags |= aiProcess_SplitByBoneCount; } + else if (!strcmp(param, "-embtex") || ! strcmp(param, "--embed-textures")) { + fill.ppFlags |= aiProcess_EmbedTextures; + } else if (! strncmp( param, "-c",2) || ! strncmp( param, "--config=",9)) { const unsigned int ofs = (params[i][1] == '-' ? 9 : 2); From 673885a6d330787cc208aaf0ce81176961b89a62 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Fri, 21 Sep 2018 20:04:53 +0200 Subject: [PATCH 5/6] Add unit test for gltf2 export to obj --- test/unit/utglTF2ImportExport.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 9eb3ef5bd..ebf97a5eb 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -101,6 +101,24 @@ TEST_F( utglTF2ImportExport, importBinaryglTF2FromFileTest ) { EXPECT_TRUE( binaryImporterTest() ); } +#ifndef ASSIMP_BUILD_NO_EXPORT +TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured_out.obj")); +} + +TEST_F(utglTF2ImportExport, importglTF2EmbeddedAndExportToOBJ) { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf", aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured_out.obj")); +} +#endif // ASSIMP_BUILD_NO_EXPORT + TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePointsWithoutIndices) { Assimp::Importer importer; //Points without indices From 504e5fd5c556e450c0ea09365ca28952dffa12c9 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Fri, 21 Sep 2018 21:24:53 +0200 Subject: [PATCH 6/6] Use unique_ptr for gltf2 textures --- code/glTF2Asset.h | 4 ++-- code/glTF2Asset.inl | 20 ++++++++++---------- code/glTF2Importer.cpp | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index b4545baa2..d1a70ae0a 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -659,7 +659,7 @@ namespace glTF2 int width, height; private: - uint8_t* mData; + std::unique_ptr mData; size_t mDataLength; public: @@ -674,7 +674,7 @@ namespace glTF2 { return mDataLength; } inline const uint8_t* GetData() const - { return mData; } + { return mData.get(); } inline uint8_t* StealData(); diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 5a87715ce..96348d4f2 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -688,7 +688,6 @@ T Accessor::Indexer::GetValue(int i) inline Image::Image() : width(0) , height(0) - , mData(0) , mDataLength(0) { @@ -704,7 +703,9 @@ inline void Image::Read(Value& obj, Asset& r) if (ParseDataURI(uristr, uri->GetStringLength(), dataURI)) { mimeType = dataURI.mediaType; if (dataURI.base64) { - mDataLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, mData); + uint8_t *ptr = nullptr; + mDataLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, ptr); + mData.reset(ptr); } } else { @@ -717,8 +718,9 @@ inline void Image::Read(Value& obj, Asset& r) this->mDataLength = this->bufferView->byteLength; // maybe this memcpy could be avoided if aiTexture does not delete[] pcData at destruction. - this->mData = new uint8_t [this->mDataLength]; - memcpy(this->mData, buffer->GetPointer() + this->bufferView->byteOffset, this->mDataLength); + + this->mData.reset(new uint8_t[this->mDataLength]); + memcpy(this->mData.get(), buffer->GetPointer() + this->bufferView->byteOffset, this->mDataLength); if (Value* mtype = FindString(obj, "mimeType")) { this->mimeType = mtype->GetString(); @@ -729,10 +731,8 @@ inline void Image::Read(Value& obj, Asset& r) inline uint8_t* Image::StealData() { - uint8_t* data = mData; - mDataLength = 0; - mData = 0; - return data; + mDataLength = 0; + return mData.release(); } inline void Image::SetData(uint8_t* data, size_t length, Asset& r) @@ -747,8 +747,8 @@ inline void Image::SetData(uint8_t* data, size_t length, Asset& r) bufferView->byteOffset = b->AppendData(data, length); } else { // text file: will be stored as a data uri - this->mData = data; - this->mDataLength = length; + this->mData.reset(data); + this->mDataLength = length; } } diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 8581d25fe..740935601 100755 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -818,7 +818,7 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r) // Add the embedded textures for (size_t i = 0; i < r.images.Size(); ++i) { - Image img = r.images[i]; + Image &img = r.images[i]; if (!img.HasData()) continue; int idx = mScene->mNumTextures++;