瀏覽代碼

Merge branch 'master' into kimkulling/fix_inno_setup_action

Kim Kulling 11 月之前
父節點
當前提交
96165f60be

+ 41 - 43
Build.md

@@ -1,36 +1,10 @@
 # Build / Install Instructions
 
-## Install on all platforms using vcpkg
-You can download and install assimp using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
-```bash
-    git clone https://github.com/Microsoft/vcpkg.git
-    cd vcpkg
-    ./bootstrap-vcpkg.sh
-    ./vcpkg integrate install
-    ./vcpkg install assimp
-```
-The assimp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
-
-## Install on Ubuntu
-You can install the Asset-Importer-Lib via apt:
-```
-sudo apt-get update
-sudo apt-get install libassimp-dev
-```
-
-## Install pyassimp
-You need to have pip installed:
-```
-pip install pyassimp
-```
-
 ## Manual build instructions
-
-### Install CMake
-Asset-Importer-Lib can be built for a lot of different platforms. We are using cmake to generate the build environment for these via cmake. So you have to make sure that you have a working cmake-installation on your system. You can download it at https://cmake.org/ or for linux install it via
-```bash
-sudo apt-get install cmake
-```
+### Install prerequisites
+You need to install
+* cmake
+* Your compiler
 
 ### Get the source
 Make sure you have a working git-installation. Open a command prompt and clone the Asset-Importer-Lib via:
@@ -38,15 +12,22 @@ Make sure you have a working git-installation. Open a command prompt and clone t
 git clone https://github.com/assimp/assimp.git
 ```
 ### Build from source:
+* For *assimp.lib* without any tools:
+```bash
+cd assimp
+cmake CMakeLists.txt
+cmake --build .
+```
+
+* For assimp with the common tools like *assimp-cmd*
 ```bash
 cd assimp
-cmake CMakeLists.txt 
+cmake CMakeLists.txt -DASSIMP_BUILD_ASSIMP_TOOLS=ON
 cmake --build .
 ```
 Note that by default this builds a shared library into the `bin` directory. If you want to build it as a static library see the build options at the bottom of this file.
 
 ### Build instructions for Windows with Visual-Studio
-
 First, you have to install Visual-Studio on your windows-system. You can get the Community-Version for free here: https://visualstudio.microsoft.com/de/downloads/
 To generate the build environment for your IDE open a command prompt, navigate to your repo and type:
 ```bash
@@ -57,17 +38,6 @@ This will generate the project files for the visual studio. All dependencies use
 ### Build instructions for Windows with UWP
 See <https://stackoverflow.com/questions/40803170/cmake-uwp-using-cmake-to-build-universal-windows-app>
 
-### Build instructions for Linux / Unix
-Open a terminal and got to your repository. You can generate the makefiles and build the library via:
-
-```bash
-cmake CMakeLists.txt
-make -j4
-```
-The option -j describes the number of parallel processes for the build. In this case make will try to use 4 cores for the build.
-
-If you want to use an IDE for linux you can try QTCreator for instance. 
-
 ### Build instructions for MinGW
  Older versions of MinGW's compiler (e.g. 5.1.0) do not support the -mbig_obj flag 
 required to compile some of assimp's files, especially for debug builds.
@@ -111,3 +81,31 @@ The cmake-build-environment provides options to configure the build. The followi
 - **USE_STATIC_CRT (default OFF)**: Link against the static MSVC runtime libraries.
 - **ASSIMP_BUILD_DRACO (default OFF)**: Build Draco libraries. Primarily for glTF.
 - **ASSIMP_BUILD_ASSIMP_VIEW (default ON, if DirectX found, OFF otherwise)**: Build Assimp view tool (requires DirectX).
+
+### Install prebuild binaries
+## Install on all platforms using vcpkg
+You can download and install assimp using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
+```bash
+    git clone https://github.com/Microsoft/vcpkg.git
+    cd vcpkg
+    ./bootstrap-vcpkg.sh
+    ./vcpkg integrate install
+    ./vcpkg install assimp
+```
+The assimp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
+
+### Install on Ubuntu
+You can install the Asset-Importer-Lib via apt:
+```
+sudo apt-get update
+sudo apt-get install libassimp-dev
+```
+
+### Install pyassimp
+You need to have pip installed:
+```
+pip install pyassimp
+```
+
+### Get the SDK from itchi.io
+Just check [itchi.io](https://kimkulling.itch.io/the-asset-importer-lib)

+ 7 - 11
Readme.md

@@ -1,18 +1,17 @@
 Open Asset Import Library (assimp)
 ==================================
 
-Open Asset Import Library is a library to load various 3d file formats into a shared, in-memory format. It supports more than __40 file formats__ for import and a growing selection of file formats for export.
+Open Asset Import Library is a library that loads various 3D file formats into a shared, in-memory format. It supports more than __40 file formats__ for import and a growing selection of file formats for export.
 
 ### Current project status ###
 [![Financial Contributors on Open Collective](https://opencollective.com/assimp/all/badge.svg?label=financial+contributors)](https://opencollective.com/assimp) 
 ![C/C++ CI](https://github.com/assimp/assimp/workflows/C/C++%20CI/badge.svg)
 [![Codacy Badge](https://app.codacy.com/project/badge/Grade/9973693b7bdd4543b07084d5d9cf4745)](https://www.codacy.com/gh/assimp/assimp/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=assimp/assimp&amp;utm_campaign=Badge_Grade)
-[![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Average time to resolve an issue")
 [![Percentage of issues still open](http://isitmaintained.com/badge/open/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Percentage of issues still open")
 <br>
 
-APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
+APIs are provided for C and C++. Various bindings exist to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
 Additionally, assimp features various __mesh post-processing tools__: normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials and many more.
 
 ### Documentation ###
@@ -29,7 +28,7 @@ Clone [our model database](https://github.com/assimp/assimp-mdb).
 - Find us on [https://discord.gg/s9KJfaem](https://discord.gg/kKazXMXDy2)
 - Ask [the Assimp community on Reddit](https://www.reddit.com/r/Assimp/).
 - Ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest). 
-- Nothing has worked? File a question or an issue-report at [The Assimp-Issue Tracker](https://github.com/assimp/assimp/issues)
+- Nothing has worked? File a question or an issue report at [The Assimp-Issue Tracker](https://github.com/assimp/assimp/issues)
 
 #### Supported file formats ####
 See [the complete list of supported formats](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md).
@@ -46,7 +45,7 @@ Start by reading [our build instructions](https://github.com/assimp/assimp/blob/
 * [Javascript/Node.js Interface](https://github.com/kovacsv/assimpjs)
 * [Unity 3d Plugin](https://ricardoreis.net/trilib-2/)
 * [Unreal Engine Plugin](https://github.com/irajsb/UE4_Assimp/)
-* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status))
+* [JVM](https://github.com/kotlin-graphics/assimp) Full JVM port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status))
 * [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port.
 * [Rust](https://github.com/jkvargas/russimp)
 
@@ -59,7 +58,7 @@ Open Asset Import Library is implemented in C++. The directory structure looks l
 
 	/code		Source code
 	/contrib	Third-party libraries
-	/doc		Documentation (doxysource and pre-compiled docs)
+	/doc		Documentation (Doxygen source and pre-compiled docs)
 	/fuzz           Contains the test code for the Google Fuzzer project
 	/include	Public header C and C++ header files
 	/scripts 	Scripts are used to generate the loading code for some formats
@@ -79,7 +78,7 @@ The source code is organized in the following way:
 	code/AssetLib/<FormatName>	Implementation for import and export of the format
 
 ### Contributing ###
-Contributions to assimp are highly appreciated. The easiest way to get involved is to submit
+I would greatly appreciate contributing to assimp. The easiest way to get involved is to submit
 a pull request with your changes against the main repository's `master` branch.
 
 ## Contributors
@@ -101,7 +100,7 @@ Become a financial contributor and help us sustain our community. [[Contribute](
 
 #### Organizations
 
-Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/assimp/contribute)]
+You can support the project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/assimp/contribute)]
 
 <a href="https://opencollective.com/assimp/organization/0/website"><img src="https://opencollective.com/assimp/organization/0/avatar.svg"></a>
 
@@ -111,6 +110,3 @@ Our license is based on the modified, __3-clause BSD__-License.
 An _informal_ summary is: do whatever you want, but include Assimp's license text with your product -
 and don't sue us if our code doesn't work. Note that, unlike LGPLed code, you may link statically to Assimp.
 For the legal details, see the `LICENSE` file.
-
-### Why this name ###
-Sorry, we're germans :-), no English native speakers ...

+ 8 - 2
code/AssetLib/3DS/3DSConverter.cpp

@@ -643,11 +643,17 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene *pcSOut, aiNode *pcOut,
     }
 
     // Allocate storage for children
-    pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size();
+    const unsigned int size = static_cast<unsigned int>(pcIn->mChildren.size());
+
+    pcOut->mNumChildren = size;
+    if (size == 0) {
+        return;
+    }
+
     pcOut->mChildren = new aiNode *[pcIn->mChildren.size()];
 
     // Recursively process all children
-    const unsigned int size = static_cast<unsigned int>(pcIn->mChildren.size());
+    
     for (unsigned int i = 0; i < size; ++i) {
         pcOut->mChildren[i] = new aiNode();
         pcOut->mChildren[i]->mParent = pcOut;

+ 5 - 3
code/AssetLib/COB/COBLoader.cpp

@@ -372,9 +372,11 @@ aiNode *COBImporter::BuildNodes(const Node &root, const Scene &scin, aiScene *fi
     }
 
     // add children recursively
-    nd->mChildren = new aiNode *[root.temp_children.size()]();
-    for (const Node *n : root.temp_children) {
-        (nd->mChildren[nd->mNumChildren++] = BuildNodes(*n, scin, fill))->mParent = nd;
+    if (!root.temp_children.empty()) {
+        nd->mChildren = new aiNode *[root.temp_children.size()]();
+        for (const Node *n : root.temp_children) {
+            (nd->mChildren[nd->mNumChildren++] = BuildNodes(*n, scin, fill))->mParent = nd;
+        }
     }
 
     return nd;

+ 19 - 13
code/AssetLib/FBX/FBXConverter.cpp

@@ -181,7 +181,9 @@ FBXConverter::FBXConverter(aiScene *out, const Document &doc, bool removeEmptyBo
     if (out->mNumMeshes == 0) {
         out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
     } else {
-        correctRootTransform(mSceneOut);
+        // Apply the FBX axis metadata unless requested not to
+        if (!doc.Settings().ignoreUpDirection)
+            correctRootTransform(mSceneOut);
     }
 }
 
@@ -310,6 +312,8 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
 
                 child->mParent = last_parent;
                 last_parent = child.mNode;
+
+                new_abs_transform *= child->mTransformation;
             }
 
             // attach geometry
@@ -332,6 +336,8 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
 
                     postnode->mParent = last_parent;
                     last_parent = postnode.mNode;
+
+                    new_abs_transform *= postnode->mTransformation;
                 }
             } else {
                 // free the nodes we allocated as we don't need them
@@ -357,12 +363,12 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
     if (nodes.empty()) {
         parent->mNumChildren = 0;
         parent->mChildren = nullptr;
-    }
-
-    parent->mChildren = new aiNode *[nodes.size()]();
-    parent->mNumChildren = static_cast<unsigned int>(nodes.size());
-    for (unsigned int i = 0; i < nodes.size(); ++i) {
-        parent->mChildren[i] = nodes[i].mOwnership.release();
+    } else {
+        parent->mChildren = new aiNode *[nodes.size()]();
+        parent->mNumChildren = static_cast<unsigned int>(nodes.size());
+        for (unsigned int i = 0; i < nodes.size(); ++i) {
+            parent->mChildren[i] = nodes[i].mOwnership.release();
+        }
     }
 }
 
@@ -1248,9 +1254,9 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c
         for (const BlendShapeChannel *blendShapeChannel : blendShape->BlendShapeChannels()) {
             const auto& shapeGeometries = blendShapeChannel->GetShapeGeometries();
             for (const ShapeGeometry *shapeGeometry : shapeGeometries) {
-                aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh);
-                const auto &curVertices = shapeGeometry->GetVertices();
                 const auto &curNormals = shapeGeometry->GetNormals();
+                aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh, true, !curNormals.empty());
+                const auto &curVertices = shapeGeometry->GetVertices();
                 const auto &curIndices = shapeGeometry->GetIndices();
                 //losing channel name if using shapeGeometry->Name()
                 // if blendShapeChannel Name is empty or doesn't have a ".", add geoMetryName;
@@ -1266,7 +1272,7 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c
                 for (size_t j = 0; j < curIndices.size(); j++) {
                     const unsigned int curIndex = curIndices.at(j);
                     aiVector3D vertex = curVertices.at(j);
-                    aiVector3D normal = curNormals.at(j);
+                    aiVector3D normal = curNormals.empty() ? aiVector3D() : curNormals.at(j);
                     unsigned int count = 0;
                     const unsigned int *outIndices = mesh.ToOutputVertexIndex(curIndex, count);
                     for (unsigned int k = 0; k < count; k++) {
@@ -1486,15 +1492,15 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co
         for (const BlendShapeChannel *blendShapeChannel : blendShape->BlendShapeChannels()) {
             const auto& shapeGeometries = blendShapeChannel->GetShapeGeometries();
             for (const ShapeGeometry *shapeGeometry : shapeGeometries) {
-                aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh);
-                const auto& curVertices = shapeGeometry->GetVertices();
                 const auto& curNormals = shapeGeometry->GetNormals();
+                aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh, true, !curNormals.empty());
+                const auto& curVertices = shapeGeometry->GetVertices();
                 const auto& curIndices = shapeGeometry->GetIndices();
                 animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name()));
                 for (size_t j = 0; j < curIndices.size(); j++) {
                     unsigned int curIndex = curIndices.at(j);
                     aiVector3D vertex = curVertices.at(j);
-                    aiVector3D normal = curNormals.at(j);
+                    aiVector3D normal = curNormals.empty() ? aiVector3D() : curNormals.at(j);
                     unsigned int count = 0;
                     const unsigned int *outIndices = mesh.ToOutputVertexIndex(curIndex, count);
                     for (unsigned int k = 0; k < count; k++) {

+ 3 - 0
code/AssetLib/FBX/FBXImportSettings.h

@@ -156,6 +156,9 @@ struct ImportSettings {
     /** Set to true to perform a conversion from cm to meter after the import
     */
     bool convertToMeters;
+
+    // Set to true to ignore the axis configuration in the file
+    bool ignoreUpDirection = false;
 };
 
 } // namespace FBX

+ 1 - 0
code/AssetLib/FBX/FBXImporter.cpp

@@ -117,6 +117,7 @@ void FBXImporter::SetupProperties(const Importer *pImp) {
     mSettings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
     mSettings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
     mSettings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
+    mSettings.ignoreUpDirection = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_IGNORE_UP_DIRECTION, false);
     mSettings.useSkeleton = pImp->GetPropertyBool(AI_CONFIG_FBX_USE_SKELETON_BONE_CONTAINER, false);
 }
 

+ 5 - 2
code/AssetLib/FBX/FBXMeshGeometry.cpp

@@ -685,11 +685,14 @@ ShapeGeometry::ShapeGeometry(uint64_t id, const Element& element, const std::str
         DOMError("failed to read Geometry object (class: Shape), no data scope found");
     }
     const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element);
-    const Element& Normals = GetRequiredElement(*sc, "Normals", &element);
     const Element& Vertices = GetRequiredElement(*sc, "Vertices", &element);
     ParseVectorDataArray(m_indices, Indexes);
     ParseVectorDataArray(m_vertices, Vertices);
-    ParseVectorDataArray(m_normals, Normals);
+
+    if ((*sc)["Normals"]) {
+        const Element& Normals = GetRequiredElement(*sc, "Normals", &element);
+        ParseVectorDataArray(m_normals, Normals);
+    }
 }
 
 // ------------------------------------------------------------------------------------------------

+ 10 - 2
code/AssetLib/LWS/LWSLoader.cpp

@@ -78,7 +78,15 @@ static constexpr aiImporterDesc desc = {
 
 // ------------------------------------------------------------------------------------------------
 // Recursive parsing of LWS files
-void LWS::Element::Parse(const char *&buffer, const char *end) {
+namespace {
+    constexpr int MAX_DEPTH = 1000; // Define the maximum depth allowed
+}
+
+void LWS::Element::Parse(const char *&buffer, const char *end, int depth) {
+    if (depth > MAX_DEPTH) {
+        throw std::runtime_error("Maximum recursion depth exceeded in LWS::Element::Parse");
+    }
+
     for (; SkipSpacesAndLineEnd(&buffer, end); SkipLine(&buffer, end)) {
 
         // begin of a new element with children
@@ -121,7 +129,7 @@ void LWS::Element::Parse(const char *&buffer, const char *end) {
 
         // parse more elements recursively
         if (sub) {
-            children.back().Parse(buffer, end);
+            children.back().Parse(buffer, end, depth + 1);
         }
     }
 }

+ 1 - 1
code/AssetLib/LWS/LWSLoader.h

@@ -76,7 +76,7 @@ public:
     std::list<Element> children;
 
     //! Recursive parsing function
-    void Parse(const char *&buffer, const char *end);
+    void Parse(const char *&buffer, const char *end, int depth = 0);
 };
 
 #define AI_LWS_MASK (0xffffffff >> 4u)

+ 11 - 0
code/AssetLib/MD3/MD3Loader.cpp

@@ -724,6 +724,7 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
     std::vector<unsigned char> mBuffer2(fileSize);
     file->Read(&mBuffer2[0], 1, fileSize);
     mBuffer = &mBuffer2[0];
+    const unsigned char* bufferEnd = mBuffer + fileSize;
 
     pcHeader = (BE_NCONST MD3::Header *)mBuffer;
 
@@ -749,9 +750,15 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
     // Navigate to the list of surfaces
     BE_NCONST MD3::Surface *pcSurfaces = (BE_NCONST MD3::Surface *)(mBuffer + pcHeader->OFS_SURFACES);
+    if ((const unsigned char*)pcSurfaces + sizeof(MD3::Surface) * pcHeader->NUM_SURFACES > bufferEnd) {
+        throw DeadlyImportError("MD3 surface headers are outside the file");
+    }
 
     // Navigate to the list of tags
     BE_NCONST MD3::Tag *pcTags = (BE_NCONST MD3::Tag *)(mBuffer + pcHeader->OFS_TAGS);
+    if ((const unsigned char*)pcTags + sizeof(MD3::Tag) * pcHeader->NUM_TAGS > bufferEnd) {
+        throw DeadlyImportError("MD3 tags are outside the file");
+    }
 
     // Allocate output storage
     pScene->mNumMeshes = pcHeader->NUM_SURFACES;
@@ -1026,6 +1033,10 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
         for (unsigned int i = 0; i < pcHeader->NUM_TAGS; ++i, ++pcTags) {
             aiNode *nd = pScene->mRootNode->mChildren[i] = new aiNode();
+            if ((const unsigned char*)pcTags + sizeof(MD3::Tag) > bufferEnd) {
+                throw DeadlyImportError("MD3 tag is outside the file");
+            }
+
             nd->mName.Set((const char *)pcTags->NAME);
             nd->mParent = pScene->mRootNode;
 

+ 29 - 0
code/AssetLib/Ply/PlyParser.cpp

@@ -48,10 +48,29 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/ByteSwapper.h>
 #include <assimp/fast_atof.h>
 #include <assimp/DefaultLogger.hpp>
+#include <unordered_set>
 #include <utility>
 
 namespace Assimp {
 
+std::string to_string(EElementSemantic e) {
+
+    switch (e) {
+    case EEST_Vertex:
+        return std::string{ "vertex" };
+    case EEST_TriStrip:
+        return std::string{ "tristrips" };
+    case EEST_Edge:
+        return std::string{ "edge" };
+    case EEST_Material:
+        return std::string{ "material" };
+    case EEST_TextureFile:
+        return std::string{ "TextureFile" };
+    default:
+        return std::string{ "invalid" };
+    }
+}
+
 // ------------------------------------------------------------------------------------------------
 PLY::EDataType PLY::Property::ParseDataType(std::vector<char> &buffer) {
     ai_assert(!buffer.empty());
@@ -281,6 +300,8 @@ bool PLY::Element::ParseElement(IOStreamBuffer<char> &streamBuffer, std::vector<
         // if the exact semantic can't be determined, just store
         // the original string identifier
         pOut->szName = std::string(&buffer[0], &buffer[0] + strlen(&buffer[0]));
+        auto pos = pOut->szName.find_last_of(' ');
+        pOut->szName.erase(pos, pOut->szName.size());
     }
 
     if (!PLY::DOM::SkipSpaces(buffer))
@@ -413,6 +434,7 @@ bool PLY::DOM::SkipComments(std::vector<char> buffer) {
 bool PLY::DOM::ParseHeader(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, bool isBinary) {
     ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseHeader() begin");
 
+    std::unordered_set<std::string> definedAlElements;
     // parse all elements
     while (!buffer.empty()) {
         // skip all comments
@@ -421,6 +443,13 @@ bool PLY::DOM::ParseHeader(IOStreamBuffer<char> &streamBuffer, std::vector<char>
         PLY::Element out;
         if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) {
             // add the element to the list of elements
+
+            const auto propertyName = (out.szName.empty()) ? to_string(out.eSemantic) : out.szName;
+            auto alreadyDefined = definedAlElements.find(propertyName);
+            if (alreadyDefined != definedAlElements.end()) {
+                throw DeadlyImportError("Property '" + propertyName + "' in header already defined ");
+            }
+            definedAlElements.insert(propertyName);
             alElements.push_back(out);
         } else if (TokenMatch(buffer, "end_header", 10)) {
             // we have reached the end of the header

+ 5 - 1
code/AssetLib/SMD/SMDLoader.cpp

@@ -400,8 +400,12 @@ void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent) {
         }
     }
 
+    // nothing to do
+    if (pcNode->mNumChildren == 0)
+        return;
+
     // now allocate the output array
-    pcNode->mChildren = new aiNode*[pcNode->mNumChildren];
+    pcNode->mChildren = new aiNode *[pcNode->mNumChildren];
 
     // and fill all subnodes
     unsigned int qq( 0 );

+ 6 - 4
code/CMakeLists.txt

@@ -271,7 +271,6 @@ MACRO(ADD_ASSIMP_IMPORTER name)
 ENDMACRO()
 
 if (NOT ASSIMP_NO_EXPORT)
-
   # if this variable is set to TRUE, the user can manually disable exporters by setting
   # ASSIMP_BUILD_XXX_EXPORTER to FALSE for each exporter
   # if this variable is set to FALSE, the user can manually enable exporters by setting
@@ -1053,7 +1052,7 @@ ENDIF() # IF (ASSIMP_BUILD_USD_IMPORTER)
 IF(ASSIMP_HUNTER_ENABLED)
   hunter_add_package(pugixml)
   find_package(pugixml CONFIG REQUIRED)
-ELSE()
+ELSEIF(NOT TARGET pugixml::pugixml)
   SET( Pugixml_SRCS
     ../contrib/pugixml/src/pugiconfig.hpp
     ../contrib/pugixml/src/pugixml.hpp
@@ -1396,6 +1395,8 @@ IF (ASSIMP_WARNINGS_AS_ERRORS)
         -Wno-unused-template
         -Wno-undefined-func-template
         -Wno-declaration-after-statement
+        -Wno-deprecated-declarations
+	-Wno-deprecated-non-prototype
       )
     ELSE()
       TARGET_COMPILE_OPTIONS(assimp PRIVATE /W4 /WX)
@@ -1417,9 +1418,7 @@ TARGET_INCLUDE_DIRECTORIES ( assimp PUBLIC
 IF(ASSIMP_HUNTER_ENABLED)
   TARGET_LINK_LIBRARIES(assimp
       PUBLIC
-      #polyclipping::polyclipping
       openddlparser::openddl_parser
-      #poly2tri::poly2tri
       minizip::minizip
       ZLIB::zlib
       RapidJSON::rapidjson
@@ -1439,6 +1438,9 @@ ELSE()
   if (ASSIMP_BUILD_DRACO)
     target_link_libraries(assimp ${draco_LIBRARIES})
   endif()
+  if(TARGET pugixml::pugixml)
+    target_link_libraries(assimp pugixml::pugixml)
+  endif()
 ENDIF()
 
 if(ASSIMP_ANDROID_JNIIOSYSTEM)

+ 9 - 4
code/Common/Assimp.cpp

@@ -359,20 +359,25 @@ void CallbackToLogRedirector(const char *msg, char *dt) {
     s->write(msg);
 }
 
+static LogStream *DefaultStream = nullptr;
+
 // ------------------------------------------------------------------------------------------------
 ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream, const char *file) {
     aiLogStream sout;
 
     ASSIMP_BEGIN_EXCEPTION_REGION();
-    LogStream *stream = LogStream::createDefaultStream(pStream, file);
-    if (!stream) {
+    if (DefaultStream == nullptr) {
+        DefaultStream = LogStream::createDefaultStream(pStream, file);
+    }
+    
+    if (!DefaultStream) {
         sout.callback = nullptr;
         sout.user = nullptr;
     } else {
         sout.callback = &CallbackToLogRedirector;
-        sout.user = (char *)stream;
+        sout.user = (char *)DefaultStream;
     }
-    gPredefinedStreams.push_back(stream);
+    gPredefinedStreams.push_back(DefaultStream);
     ASSIMP_END_EXCEPTION_REGION(aiLogStream);
     return sout;
 }

+ 4 - 5
code/Common/Importer.cpp

@@ -848,11 +848,7 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) {
             break;
         }
 #ifdef ASSIMP_BUILD_DEBUG
-
-#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-        continue;
-#endif  // no validation
-
+#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
         // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
         if (pimpl->bExtraVerbose)   {
             ASSIMP_LOG_DEBUG("Verbose Import: re-validating data structures");
@@ -864,6 +860,7 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) {
                 break;
             }
         }
+#endif  // no validation
 #endif // ! DEBUG
     }
     pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()),
@@ -939,6 +936,7 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
         profiler->EndRegion( "postprocess" );
     }
 
+#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
     // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
     if ( pimpl->bExtraVerbose || requestValidation  ) {
         ASSIMP_LOG_DEBUG( "Verbose Import: revalidating data structures" );
@@ -949,6 +947,7 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
             ASSIMP_LOG_ERROR( "Verbose Import: failed to revalidate data structures" );
         }
     }
+#endif // no validation
 
     // clear any data allocated by post-process steps
     pimpl->mPPShared->Clean();

+ 32 - 18
code/PostProcessing/SortByPTypeProcess.cpp

@@ -65,37 +65,47 @@ void SortByPTypeProcess::SetupProperties(const Importer *pImp) {
     mConfigRemoveMeshes = pImp->GetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, 0);
 }
 
+// ------------------------------------------------------------------------------------------------
+static void clearMeshesInNode(aiNode *node) {
+    delete[] node->mMeshes;
+    node->mNumMeshes = 0;
+    node->mMeshes = nullptr;
+}
+
 // ------------------------------------------------------------------------------------------------
 // Update changed meshes in all nodes
 void UpdateNodes(const std::vector<unsigned int> &replaceMeshIndex, aiNode *node) {
+    ai_assert(node != nullptr);
+    
     if (node->mNumMeshes) {
         unsigned int newSize = 0;
         for (unsigned int m = 0; m < node->mNumMeshes; ++m) {
             unsigned int add = node->mMeshes[m] << 2;
             for (unsigned int i = 0; i < 4; ++i) {
-                if (UINT_MAX != replaceMeshIndex[add + i]) ++newSize;
+                if (UINT_MAX != replaceMeshIndex[add + i]) {
+                    ++newSize;
+                }
             }
         }
-        if (!newSize) {
-            delete[] node->mMeshes;
-            node->mNumMeshes = 0;
-            node->mMeshes = nullptr;
-        } else {
-            // Try to reuse the old array if possible
-            unsigned int *newMeshes = (newSize > node->mNumMeshes ? new unsigned int[newSize] : node->mMeshes);
-
-            for (unsigned int m = 0; m < node->mNumMeshes; ++m) {
-                unsigned int add = node->mMeshes[m] << 2;
-                for (unsigned int i = 0; i < 4; ++i) {
-                    if (UINT_MAX != replaceMeshIndex[add + i])
-                        *newMeshes++ = replaceMeshIndex[add + i];
+        if (newSize == 0) {
+            clearMeshesInNode(node);
+            return;
+        }
+        
+        // Try to reuse the old array if possible
+        unsigned int *newMeshes = (newSize > node->mNumMeshes ? new unsigned int[newSize] : node->mMeshes);
+        for (unsigned int m = 0; m < node->mNumMeshes; ++m) {
+            unsigned int add = node->mMeshes[m] << 2;
+            for (unsigned int i = 0; i < 4; ++i) {
+                if (UINT_MAX != replaceMeshIndex[add + i]) {
+                    *newMeshes++ = replaceMeshIndex[add + i];
                 }
             }
-            if (newSize > node->mNumMeshes)
-                delete[] node->mMeshes;
-
-            node->mMeshes = newMeshes - (node->mNumMeshes = newSize);
         }
+        if (newSize > node->mNumMeshes) {
+            clearMeshesInNode(node);
+        }
+        node->mMeshes = newMeshes - (node->mNumMeshes = newSize);
     }
 
     // call all subnodes recursively
@@ -167,6 +177,10 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
         // with the largest number of primitives
         unsigned int aiNumPerPType[4] = { 0, 0, 0, 0 };
         aiFace *pFirstFace = mesh->mFaces;
+        if (pFirstFace == nullptr) {
+            continue;
+        }
+
         aiFace *const pLastFace = pFirstFace + mesh->mNumFaces;
 
         unsigned int numPolyVerts = 0;

+ 5 - 0
code/PostProcessing/SplitLargeMeshes.cpp

@@ -100,6 +100,11 @@ void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp) {
 // ------------------------------------------------------------------------------------------------
 // Update a node after some meshes have been split
 void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& avList) {
+    if (pcNode == nullptr) {
+        ASSIMP_LOG_WARN("UpdateNode skipped, nullptr detected.");
+        return; 
+    }
+    
     // for every index in out list build a new entry
     std::vector<unsigned int> aiEntries;
     aiEntries.reserve(pcNode->mNumMeshes + 1);

+ 3 - 0
code/PostProcessing/ValidateDataStructure.cpp

@@ -891,6 +891,9 @@ void ValidateDSProcess::Validate(const aiNode *pNode) {
                 ReportError("aiNode \"%s\" child %i \"%s\" parent is someone else: \"%s\"", pNode->mName.C_Str(), i, pChild->mName.C_Str(), parentName);
             }
         }
+    } else if (pNode->mChildren) {
+        ReportError("aiNode::mChildren is not nullptr for empty node %s (aiNode::mNumChildren is %i)",
+                nodeName, pNode->mNumChildren);
     }
 }
 

+ 1 - 1
contrib/poly2tri/poly2tri/common/dll_symbol.h

@@ -31,7 +31,7 @@
 
 #pragma once
 
-#if defined(_WIN32)
+#if defined(_MSC_VER)
 #  pragma warning( disable: 4273)
 #  define P2T_COMPILER_DLLEXPORT __declspec(dllexport)
 #  define P2T_COMPILER_DLLIMPORT __declspec(dllimport)

+ 1 - 1
fuzz/assimp_fuzzer.cc

@@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) {
-    aiLogStream stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT,NULL);
+    aiLogStream stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, nullptr);
     aiAttachLogStream(&stream);
 
     Importer importer;

+ 13 - 0
include/assimp/config.h.in

@@ -676,6 +676,19 @@ enum aiComponent
 #define AI_CONFIG_FBX_CONVERT_TO_M \
     "AI_CONFIG_FBX_CONVERT_TO_M"
 
+// ---------------------------------------------------------------------------
+/** @brief  Set whether the FBX importer shall ignore the provided axis configuration
+ *
+ * If this property is set to true, the axis directions provided in the FBX file
+ * will be ignored and the file will be loaded as is.
+ *
+ * Set to true for Assimp 5.3.x and earlier behavior
+ * Equivalent to AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION
+ * Property type: Bool. Default value: false.
+ */
+#define AI_CONFIG_IMPORT_FBX_IGNORE_UP_DIRECTION \
+    "AI_CONFIG_IMPORT_FBX_IGNORE_UP_DIRECTION"
+
 // ---------------------------------------------------------------------------
 /** @brief  Will enable the skeleton struct to store bone data.
  *

+ 107 - 81
include/assimp/defs.h

@@ -49,14 +49,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define AI_DEFINES_H_INC
 
 #ifdef __GNUC__
-#pragma GCC system_header
+#  pragma GCC system_header
 #endif
 
 #include <assimp/config.h>
 
 //////////////////////////////////////////////////////////////////////////
-/* Define ASSIMP_BUILD_NO_XX_IMPORTER to disable a specific
- * file format loader. The loader is be excluded from the
+/** 
+ * @brief Define ASSIMP_BUILD_NO_XX_IMPORTER to disable a specific file format loader. 
+ *
+ * The loader is be excluded from the
  * build in this case. 'XX' stands for the most common file
  * extension of the file format. E.g.:
  * ASSIMP_BUILD_NO_X_IMPORTER disables the X loader.
@@ -76,34 +78,33 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //////////////////////////////////////////////////////////////////////////
 
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_X
-#define ASSIMP_BUILD_NEED_Z_INFLATE
+#  define ASSIMP_BUILD_NEED_Z_INFLATE
 #endif
 
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
-#define ASSIMP_BUILD_NEED_Z_INFLATE
+#  define ASSIMP_BUILD_NEED_Z_INFLATE
 #endif
 
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
-#define ASSIMP_BUILD_NEED_Z_INFLATE
-#define ASSIMP_BUILD_NEED_UNZIP
+#  define ASSIMP_BUILD_NEED_Z_INFLATE
+#  define ASSIMP_BUILD_NEED_UNZIP
 #endif
 
 #ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
-#define ASSIMP_BUILD_NEED_Z_INFLATE
-#define ASSIMP_BUILD_NEED_UNZIP
+#  define ASSIMP_BUILD_NEED_Z_INFLATE
+#  define ASSIMP_BUILD_NEED_UNZIP
 #endif
 
-// We need those constants, workaround for any platforms where nobody defined them yet
+/**
+ * @brief We need those constants, workaround for any platforms where nobody defined them yet.
+ */
 #if (!defined SIZE_MAX)
-#define SIZE_MAX (~((size_t)0))
+#  define SIZE_MAX (~((size_t)0))
 #endif
 
-/*#if (!defined UINT_MAX)
-#define UINT_MAX (~((unsigned int)0))
-#endif*/
-
 //////////////////////////////////////////////////////////////////////////
-/* Define ASSIMP_BUILD_NO_XX_PROCESS to disable a specific
+/** @brief Define ASSIMP_BUILD_NO_XX_PROCESS to disable a specific
+ *
  * post processing step. This is the current list of process names ('XX'):
  * CALCTANGENTS
  * JOINVERTICES
@@ -134,46 +135,50 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * OPTIMIZEGRAPH
  * GENENTITYMESHES
  * FIXTEXTUREPATHS
- * GENBOUNDINGBOXES */
-//////////////////////////////////////////////////////////////////////////
-
-#ifdef _WIN32
-#undef ASSIMP_API
-//////////////////////////////////////////////////////////////////////////
-/* Define 'ASSIMP_BUILD_DLL_EXPORT' to build a DLL of the library */
-//////////////////////////////////////////////////////////////////////////
-#ifdef ASSIMP_BUILD_DLL_EXPORT
-#define ASSIMP_API __declspec(dllexport)
-#define ASSIMP_API_WINONLY __declspec(dllexport)
+ * GENBOUNDINGBOXES 
+ */
 
 //////////////////////////////////////////////////////////////////////////
-/* Define 'ASSIMP_DLL' before including Assimp to link to ASSIMP in
-     * an external DLL under Windows. Default is static linkage. */
+/** @brief Define 'ASSIMP_BUILD_DLL_EXPORT' to build a DLL of the library 
+ *
+ * Define 'ASSIMP_DLL' before including Assimp to link to ASSIMP in
+ * an external DLL under Windows. Default is static linkage. 
+ */
 //////////////////////////////////////////////////////////////////////////
-#elif (defined ASSIMP_DLL)
-#define ASSIMP_API __declspec(dllimport)
-#define ASSIMP_API_WINONLY __declspec(dllimport)
-#else
-#define ASSIMP_API
-#define ASSIMP_API_WINONLY
-#endif
-#elif defined(SWIG)
-/* Do nothing, the relevant defines are all in AssimpSwigPort.i */
-#else
-#define ASSIMP_API __attribute__((visibility("default")))
-#define ASSIMP_API_WINONLY
+#ifdef _WIN32
+#  undef ASSIMP_API
+#  ifdef ASSIMP_BUILD_DLL_EXPORT
+#    define ASSIMP_API __declspec(dllexport)
+#    define ASSIMP_API_WINONLY __declspec(dllexport)
+#  elif (defined ASSIMP_DLL)
+#    define ASSIMP_API __declspec(dllimport)
+#    define ASSIMP_API_WINONLY __declspec(dllimport)
+#  else
+#    define ASSIMP_API
+#    define ASSIMP_API_WINONLY
+#  endif
+#else 
+#  define ASSIMP_API __attribute__((visibility("default")))
+#  define ASSIMP_API_WINONLY
 #endif // _WIN32
 
+/**
+ * @brief Helper macros
+ *
+ * @def   AI_FORCE_INLINE
+ * @brief Force the compiler to inline a function, if possible
+ *
+ * @def   AI_WONT_RETURN
+ * @brief Tells the compiler that a function never returns. 
+ *
+ * Used in code analysis to skip dead paths (e.g. after an assertion evaluated to false).
+ */
 #ifdef _MSC_VER
     #pragma warning(disable : 4521 4512 4714 4127 4351 4510)
     #ifdef ASSIMP_BUILD_DLL_EXPORT
         #pragma warning(disable : 4251)
     #endif
-    /* Force the compiler to inline a function, if possible */
     #define AI_FORCE_INLINE inline
-
-    /* Tells the compiler that a function never returns. Used in code analysis
-    * to skip dead paths (e.g. after an assertion evaluated to false). */
     #define AI_WONT_RETURN __declspec(noreturn)
 #elif defined(SWIG)
   /* Do nothing, the relevant defines are all in AssimpSwigPort.i */
@@ -223,29 +228,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      * to typedef all structs/enums. */
 //////////////////////////////////////////////////////////////////////////
 #if (defined ASSIMP_DOXYGEN_BUILD)
-#define C_STRUCT
-#define C_ENUM
+#  define C_STRUCT
+#  define C_ENUM
 #else
-#define C_STRUCT struct
-#define C_ENUM enum
+#  define C_STRUCT struct
+#  define C_ENUM enum
 #endif
 #endif
 
 #if (defined(__BORLANDC__) || defined(__BCPLUSPLUS__))
-#error Currently, Borland is unsupported. Feel free to port Assimp.
+#  error Currently, Borland is unsupported. Feel free to port Assimp.
 #endif
 
 //////////////////////////////////////////////////////////////////////////
-/* Define ASSIMP_BUILD_SINGLETHREADED to compile assimp
-     * without threading support. The library doesn't utilize
-     * threads then and is itself not threadsafe. */
+/**
+ * Define ASSIMP_BUILD_SINGLETHREADED to compile assimp
+ * without threading support. The library doesn't utilize
+ * threads then and is itself not threadsafe. 
+ */
 //////////////////////////////////////////////////////////////////////////
 #ifndef ASSIMP_BUILD_SINGLETHREADED
-#define ASSIMP_BUILD_SINGLETHREADED
+#  define ASSIMP_BUILD_SINGLETHREADED
 #endif
 
 #if defined(_DEBUG) || !defined(NDEBUG)
-#define ASSIMP_BUILD_DEBUG
+#  define ASSIMP_BUILD_DEBUG
 #endif
 
 //////////////////////////////////////////////////////////////////////////
@@ -291,55 +298,74 @@ typedef unsigned int ai_uint;
 #ifdef __cplusplus
 constexpr ai_real ai_epsilon = (ai_real) 1e-6;
 #else
-#define ai_epsilon ((ai_real)1e-6)
+#  define ai_epsilon ((ai_real)1e-6)
 #endif
 
-/* Support for big-endian builds */
+/**
+ * @brief Support for big-endian builds
+ * 
+ * This will check which byte ordering is used on the target architecture.
+ */
 #if defined(__BYTE_ORDER__)
-#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
-#if !defined(__BIG_ENDIAN__)
-#define __BIG_ENDIAN__
-#endif
-#else /* little endian */
-#if defined(__BIG_ENDIAN__)
-#undef __BIG_ENDIAN__
-#endif
-#endif
+#  if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#    if !defined(__BIG_ENDIAN__)
+#      define __BIG_ENDIAN__
+#    endif
+#  else /* little endian */
+#    if defined(__BIG_ENDIAN__)
+#      undef __BIG_ENDIAN__
+#    endif
+#  endif
 #endif
 #if defined(__BIG_ENDIAN__)
-#define AI_BUILD_BIG_ENDIAN
+#  define AI_BUILD_BIG_ENDIAN
 #endif
 
 /**
- *  To avoid running out of memory
+ *  @brief To avoid running out of memory
+ *
  *  This can be adjusted for specific use cases
  *  It's NOT a total limit, just a limit for individual allocations
  */
 #define AI_MAX_ALLOC(type) ((256U * 1024 * 1024) / sizeof(type))
 
 #ifndef _MSC_VER
-#if __cplusplus >= 201103L // C++11
-#define AI_NO_EXCEPT noexcept
-#else
-#define AI_NO_EXCEPT
-#endif
+#  if __cplusplus >= 201103L // C++11
+#    define AI_NO_EXCEPT noexcept
+#  else
+#    define AI_NO_EXCEPT
+#  endif
 #else
-#if (_MSC_VER >= 1915)
-#define AI_NO_EXCEPT noexcept
-#else
-#define AI_NO_EXCEPT
-#endif
+#  if (_MSC_VER >= 1915)
+#    define AI_NO_EXCEPT noexcept
+#  else
+#    define AI_NO_EXCEPT
+#  endif
 #endif // _MSC_VER
 
 /**
- *  Helper macro to set a pointer to NULL in debug builds
+ *  @brief Helper macro to set a pointer to NULL in debug builds
  */
 #if (defined ASSIMP_BUILD_DEBUG)
-#define AI_DEBUG_INVALIDATE_PTR(x) x = NULL;
+#  define AI_DEBUG_INVALIDATE_PTR(x) x = NULL;
 #else
-#define AI_DEBUG_INVALIDATE_PTR(x)
+#  define AI_DEBUG_INVALIDATE_PTR(x)
 #endif
 
 #define AI_COUNT_OF(X) (sizeof(X) / sizeof((X)[0]))
 
+/**
+ * @brief Will mark functions or classes as deprecated.
+ *
+ * Deprecation means that we will remove this function, class or methods in the next m
+ */
+#if defined(__GNUC__) || defined(__clang__)
+#  define AI_DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+#  define AI_DEPRECATED __declspec(deprecated)
+#else
+#  pragma message("WARNING: You need to implement DEPRECATED for this compiler")
+#  define AI_DEPRECATED
+#endif
+
 #endif // !! AI_DEFINES_H_INC

+ 1 - 1
include/assimp/material.h

@@ -701,7 +701,7 @@ struct aiMaterialProperty {
 *  Material data is stored using a key-value structure. A single key-value
 *  pair is called a 'material property'. C++ users should use the provided
 *  member functions of aiMaterial to process material properties, C users
-*  have to stick with the aiMaterialGetXXX family of unbound functions.
+*  have to stick with the aiGetMaterialXXX family of unbound functions.
 *  The library defines a set of standard keys (AI_MATKEY_XXX).
 */
 #ifdef __cplusplus

+ 1 - 0
test/CMakeLists.txt

@@ -100,6 +100,7 @@ SET( COMMON
   unit/Common/utBase64.cpp
   unit/Common/utHash.cpp
   unit/Common/utBaseProcess.cpp
+  unit/Common/utLogger.cpp
 )
 
 SET(Geometry 

+ 52 - 0
test/unit/Common/utLogger.cpp

@@ -0,0 +1,52 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2024, 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.
+---------------------------------------------------------------------------
+*/
+
+#include "UnitTestPCH.h"
+#include <assimp/Importer.hpp>
+
+using namespace Assimp;
+class utLogger : public ::testing::Test {};
+
+TEST_F(utLogger, aiGetPredefinedLogStream_leak_test) {
+    aiLogStream stream1 = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, nullptr);
+    aiLogStream stream2 = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, nullptr);
+    ASSERT_EQ(stream1.callback, stream2.callback);
+}

+ 31 - 0
test/unit/utFBXImporterExporter.cpp

@@ -311,6 +311,37 @@ TEST_F(utFBXImporterExporter, sceneMetadata) {
     }
 }
 
+TEST_F(utFBXImporterExporter, importCustomAxes) {
+    // see https://github.com/assimp/assimp/issues/5494
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/embedded_ascii/box.FBX", aiProcess_ValidateDataStructure);
+    EXPECT_NE(nullptr, scene);
+
+    // The ASCII box has customised the Up and Forward axes, verify that the RootNode transform has applied it
+    ASSERT_FALSE(scene->mRootNode->mTransformation.IsIdentity()) << "Did not apply the custom axis transform";
+
+    aiVector3D upVec{ 0, 0, 1 }; // Up is +Z
+    aiVector3D forwardVec{ 0, -1, 0 }; // Forward is -Y
+    aiVector3D rightVec{ 1, 0, 0 }; // Right is +X
+    aiMatrix4x4 mat(rightVec.x, rightVec.y, rightVec.z, 0.0f,
+            upVec.x, upVec.y, upVec.z, 0.0f,
+            forwardVec.x, forwardVec.y, forwardVec.z, 0.0f,
+            0.0f, 0.0f, 0.0f, 1.0f);
+
+    EXPECT_EQ(mat, scene->mRootNode->mTransformation);
+}
+
+TEST_F(utFBXImporterExporter, importIgnoreCustomAxes) {
+    // see https://github.com/assimp/assimp/issues/5494
+    Assimp::Importer importer;
+    importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_IGNORE_UP_DIRECTION, true);
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/embedded_ascii/box.FBX", aiProcess_ValidateDataStructure);
+    EXPECT_NE(nullptr, scene);
+
+    // Verify that the RootNode transform has NOT applied the custom axes
+    EXPECT_TRUE(scene->mRootNode->mTransformation.IsIdentity());
+}
+
 TEST_F(utFBXImporterExporter, importCubesWithOutOfRangeFloat) {
     Assimp::Importer importer;
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/cubes_with_outofrange_float.fbx", aiProcess_ValidateDataStructure);

+ 59 - 7
test/unit/utPLYImportExport.cpp

@@ -89,7 +89,7 @@ TEST_F(utPLYImportExport, exportTest_Success) {
 
 #endif // ASSIMP_BUILD_NO_EXPORT
 
-//Test issue 1623, crash when loading two PLY files in a row
+// 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", aiProcess_ValidateDataStructure);
@@ -109,7 +109,7 @@ TEST_F(utPLYImportExport, importPLYwithUV) {
 
     EXPECT_NE(nullptr, scene);
     EXPECT_NE(nullptr, scene->mMeshes[0]);
-    //This test model is using n-gons, so 6 faces instead of 12 tris
+    // This test model is using n-gons, so 6 faces instead of 12 tris
     EXPECT_EQ(6u, scene->mMeshes[0]->mNumFaces);
     EXPECT_EQ(aiPrimitiveType_POLYGON, scene->mMeshes[0]->mPrimitiveTypes);
     EXPECT_EQ(true, scene->mMeshes[0]->HasTextureCoords(0));
@@ -121,7 +121,7 @@ TEST_F(utPLYImportExport, importBinaryPLY) {
 
     EXPECT_NE(nullptr, scene);
     EXPECT_NE(nullptr, scene->mMeshes[0]);
-    //This test model is double sided, so 12 faces instead of 6
+    // This test model is double sided, so 12 faces instead of 6
     EXPECT_EQ(12u, scene->mMeshes[0]->mNumFaces);
 }
 
@@ -160,7 +160,7 @@ TEST_F(utPLYImportExport, vertexColorTest) {
 TEST_F(utPLYImportExport, pointcloudTest) {
     Assimp::Importer importer;
 
-    //Could not use aiProcess_ValidateDataStructure since it's missing faces.
+    // Could not use aiProcess_ValidateDataStructure since it's missing faces.
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/issue623.ply", 0);
     EXPECT_NE(nullptr, scene);
 
@@ -192,7 +192,7 @@ static const char *test_file =
 
 TEST_F(utPLYImportExport, parseErrorTest) {
     Assimp::Importer importer;
-    //Could not use aiProcess_ValidateDataStructure since it's missing faces.
+    // Could not use aiProcess_ValidateDataStructure since it's missing faces.
     const aiScene *scene = importer.ReadFileFromMemory(test_file, strlen(test_file), 0);
     EXPECT_NE(nullptr, scene);
 }
@@ -207,5 +207,57 @@ TEST_F(utPLYImportExport, parseInvalid) {
 TEST_F(utPLYImportExport, payload_JVN42386607) {
     Assimp::Importer importer;
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/payload_JVN42386607", 0);
-   EXPECT_EQ(nullptr, scene);
-}
+    EXPECT_EQ(nullptr, scene);
+}
+
+// Tests Issue #5729. Test, if properties defined multiple times. Unclear what to do, better to abort than to crash entirely
+TEST_F(utPLYImportExport, parseInvalidDoubleProperty) {
+    const char data[] = "ply\n"
+                        "format ascii 1.0\n"
+                        "element vertex 4\n"
+                        "property float x\n"
+                        "property float y\n"
+                        "property float z\n"
+                        "element vertex 8\n"
+                        "property float x\n"
+                        "property float y\n"
+                        "property float z\n"
+                        "end_header\n"
+                        "0.0 0.0 0.0 0.0 0.0 0.0\n"
+                        "0.0 0.0 1.0 0.0 0.0 1.0\n"
+                        "0.0 1.0 0.0 0.0 1.0 0.0\n"
+                        "0.0 0.0 1.0\n"
+                        "0.0 1.0 0.0 0.0 0.0 1.0\n"
+                        "0.0 1.0 1.0 0.0 1.0 1.0\n";
+
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFileFromMemory(data, sizeof(data), 0);
+    EXPECT_EQ(nullptr, scene);
+}
+
+// Tests Issue #5729. Test, if properties defined multiple times. Unclear what to do, better to abort than to crash entirely
+TEST_F(utPLYImportExport, parseInvalidDoubleCustomProperty) {
+    const char data[] = "ply\n"
+                        "format ascii 1.0\n"
+                        "element vertex 4\n"
+                        "property float x\n"
+                        "property float y\n"
+                        "property float z\n"
+                        "element name 8\n"
+                        "property float x\n"
+                        "element name 5\n"
+                        "property float x\n"
+                        "end_header\n"
+                        "0.0 0.0 0.0 100.0 10.0\n"
+                        "0.0 0.0 1.0 200.0 20.0\n"
+                        "0.0 1.0 0.0 300.0 30.0\n"
+                        "0.0 1.0 1.0 400.0 40.0\n"
+                        "0.0 0.0 0.0 500.0 50.0\n"
+                        "0.0 0.0 1.0 600.0 60.0\n"
+                        "0.0 1.0 0.0 700.0 70.0\n"
+                        "0.0 1.0 1.0 800.0 80.0\n";
+
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFileFromMemory(data, sizeof(data), 0);
+    EXPECT_EQ(nullptr, scene);
+}