Просмотр исходного кода

Merge branch 'master' into kimkulling/fix_inno_setup_action

Kim Kulling 11 месяцев назад
Родитель
Сommit
96165f60be

+ 41 - 43
Build.md

@@ -1,36 +1,10 @@
 # Build / Install Instructions
 # 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
 ## 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
 ### Get the source
 Make sure you have a working git-installation. Open a command prompt and clone the Asset-Importer-Lib via:
 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
 git clone https://github.com/assimp/assimp.git
 ```
 ```
 ### Build from source:
 ### 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
 ```bash
 cd assimp
 cd assimp
-cmake CMakeLists.txt 
+cmake CMakeLists.txt -DASSIMP_BUILD_ASSIMP_TOOLS=ON
 cmake --build .
 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.
 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
 ### 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/
 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:
 To generate the build environment for your IDE open a command prompt, navigate to your repo and type:
 ```bash
 ```bash
@@ -57,17 +38,6 @@ This will generate the project files for the visual studio. All dependencies use
 ### Build instructions for Windows with UWP
 ### Build instructions for Windows with UWP
 See <https://stackoverflow.com/questions/40803170/cmake-uwp-using-cmake-to-build-universal-windows-app>
 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
 ### Build instructions for MinGW
  Older versions of MinGW's compiler (e.g. 5.1.0) do not support the -mbig_obj flag 
  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.
 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.
 - **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_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).
 - **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 (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 ###
 ### Current project status ###
 [![Financial Contributors on Open Collective](https://opencollective.com/assimp/all/badge.svg?label=financial+contributors)](https://opencollective.com/assimp) 
 [![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)
 ![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)
 [![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")
 [![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")
 [![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>
 <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.
 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 ###
 ### 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)
 - Find us on [https://discord.gg/s9KJfaem](https://discord.gg/kKazXMXDy2)
 - Ask [the Assimp community on Reddit](https://www.reddit.com/r/Assimp/).
 - 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). 
 - 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 ####
 #### Supported file formats ####
 See [the complete list of supported formats](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md).
 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)
 * [Javascript/Node.js Interface](https://github.com/kovacsv/assimpjs)
 * [Unity 3d Plugin](https://ricardoreis.net/trilib-2/)
 * [Unity 3d Plugin](https://ricardoreis.net/trilib-2/)
 * [Unreal Engine Plugin](https://github.com/irajsb/UE4_Assimp/)
 * [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.
 * [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port.
 * [Rust](https://github.com/jkvargas/russimp)
 * [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
 	/code		Source code
 	/contrib	Third-party libraries
 	/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
 	/fuzz           Contains the test code for the Google Fuzzer project
 	/include	Public header C and C++ header files
 	/include	Public header C and C++ header files
 	/scripts 	Scripts are used to generate the loading code for some formats
 	/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
 	code/AssetLib/<FormatName>	Implementation for import and export of the format
 
 
 ### Contributing ###
 ### 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.
 a pull request with your changes against the main repository's `master` branch.
 
 
 ## Contributors
 ## Contributors
@@ -101,7 +100,7 @@ Become a financial contributor and help us sustain our community. [[Contribute](
 
 
 #### Organizations
 #### 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>
 <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 -
 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.
 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.
 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
     // 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()];
     pcOut->mChildren = new aiNode *[pcIn->mChildren.size()];
 
 
     // Recursively process all children
     // Recursively process all children
-    const unsigned int size = static_cast<unsigned int>(pcIn->mChildren.size());
+    
     for (unsigned int i = 0; i < size; ++i) {
     for (unsigned int i = 0; i < size; ++i) {
         pcOut->mChildren[i] = new aiNode();
         pcOut->mChildren[i] = new aiNode();
         pcOut->mChildren[i]->mParent = pcOut;
         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
     // 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;
     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) {
     if (out->mNumMeshes == 0) {
         out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
         out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
     } else {
     } 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;
                 child->mParent = last_parent;
                 last_parent = child.mNode;
                 last_parent = child.mNode;
+
+                new_abs_transform *= child->mTransformation;
             }
             }
 
 
             // attach geometry
             // attach geometry
@@ -332,6 +336,8 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
 
 
                     postnode->mParent = last_parent;
                     postnode->mParent = last_parent;
                     last_parent = postnode.mNode;
                     last_parent = postnode.mNode;
+
+                    new_abs_transform *= postnode->mTransformation;
                 }
                 }
             } else {
             } else {
                 // free the nodes we allocated as we don't need them
                 // 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()) {
     if (nodes.empty()) {
         parent->mNumChildren = 0;
         parent->mNumChildren = 0;
         parent->mChildren = nullptr;
         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()) {
         for (const BlendShapeChannel *blendShapeChannel : blendShape->BlendShapeChannels()) {
             const auto& shapeGeometries = blendShapeChannel->GetShapeGeometries();
             const auto& shapeGeometries = blendShapeChannel->GetShapeGeometries();
             for (const ShapeGeometry *shapeGeometry : shapeGeometries) {
             for (const ShapeGeometry *shapeGeometry : shapeGeometries) {
-                aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh);
-                const auto &curVertices = shapeGeometry->GetVertices();
                 const auto &curNormals = shapeGeometry->GetNormals();
                 const auto &curNormals = shapeGeometry->GetNormals();
+                aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh, true, !curNormals.empty());
+                const auto &curVertices = shapeGeometry->GetVertices();
                 const auto &curIndices = shapeGeometry->GetIndices();
                 const auto &curIndices = shapeGeometry->GetIndices();
                 //losing channel name if using shapeGeometry->Name()
                 //losing channel name if using shapeGeometry->Name()
                 // if blendShapeChannel Name is empty or doesn't have a ".", add geoMetryName;
                 // 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++) {
                 for (size_t j = 0; j < curIndices.size(); j++) {
                     const unsigned int curIndex = curIndices.at(j);
                     const unsigned int curIndex = curIndices.at(j);
                     aiVector3D vertex = curVertices.at(j);
                     aiVector3D vertex = curVertices.at(j);
-                    aiVector3D normal = curNormals.at(j);
+                    aiVector3D normal = curNormals.empty() ? aiVector3D() : curNormals.at(j);
                     unsigned int count = 0;
                     unsigned int count = 0;
                     const unsigned int *outIndices = mesh.ToOutputVertexIndex(curIndex, count);
                     const unsigned int *outIndices = mesh.ToOutputVertexIndex(curIndex, count);
                     for (unsigned int k = 0; k < count; k++) {
                     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()) {
         for (const BlendShapeChannel *blendShapeChannel : blendShape->BlendShapeChannels()) {
             const auto& shapeGeometries = blendShapeChannel->GetShapeGeometries();
             const auto& shapeGeometries = blendShapeChannel->GetShapeGeometries();
             for (const ShapeGeometry *shapeGeometry : shapeGeometries) {
             for (const ShapeGeometry *shapeGeometry : shapeGeometries) {
-                aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh);
-                const auto& curVertices = shapeGeometry->GetVertices();
                 const auto& curNormals = shapeGeometry->GetNormals();
                 const auto& curNormals = shapeGeometry->GetNormals();
+                aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh, true, !curNormals.empty());
+                const auto& curVertices = shapeGeometry->GetVertices();
                 const auto& curIndices = shapeGeometry->GetIndices();
                 const auto& curIndices = shapeGeometry->GetIndices();
                 animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name()));
                 animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name()));
                 for (size_t j = 0; j < curIndices.size(); j++) {
                 for (size_t j = 0; j < curIndices.size(); j++) {
                     unsigned int curIndex = curIndices.at(j);
                     unsigned int curIndex = curIndices.at(j);
                     aiVector3D vertex = curVertices.at(j);
                     aiVector3D vertex = curVertices.at(j);
-                    aiVector3D normal = curNormals.at(j);
+                    aiVector3D normal = curNormals.empty() ? aiVector3D() : curNormals.at(j);
                     unsigned int count = 0;
                     unsigned int count = 0;
                     const unsigned int *outIndices = mesh.ToOutputVertexIndex(curIndex, count);
                     const unsigned int *outIndices = mesh.ToOutputVertexIndex(curIndex, count);
                     for (unsigned int k = 0; k < count; k++) {
                     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
     /** Set to true to perform a conversion from cm to meter after the import
     */
     */
     bool convertToMeters;
     bool convertToMeters;
+
+    // Set to true to ignore the axis configuration in the file
+    bool ignoreUpDirection = false;
 };
 };
 
 
 } // namespace FBX
 } // 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.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
     mSettings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
     mSettings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
     mSettings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
     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);
     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");
         DOMError("failed to read Geometry object (class: Shape), no data scope found");
     }
     }
     const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element);
     const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element);
-    const Element& Normals = GetRequiredElement(*sc, "Normals", &element);
     const Element& Vertices = GetRequiredElement(*sc, "Vertices", &element);
     const Element& Vertices = GetRequiredElement(*sc, "Vertices", &element);
     ParseVectorDataArray(m_indices, Indexes);
     ParseVectorDataArray(m_indices, Indexes);
     ParseVectorDataArray(m_vertices, Vertices);
     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
 // 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)) {
     for (; SkipSpacesAndLineEnd(&buffer, end); SkipLine(&buffer, end)) {
 
 
         // begin of a new element with children
         // 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
         // parse more elements recursively
         if (sub) {
         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;
     std::list<Element> children;
 
 
     //! Recursive parsing function
     //! 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)
 #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);
     std::vector<unsigned char> mBuffer2(fileSize);
     file->Read(&mBuffer2[0], 1, fileSize);
     file->Read(&mBuffer2[0], 1, fileSize);
     mBuffer = &mBuffer2[0];
     mBuffer = &mBuffer2[0];
+    const unsigned char* bufferEnd = mBuffer + fileSize;
 
 
     pcHeader = (BE_NCONST MD3::Header *)mBuffer;
     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
     // Navigate to the list of surfaces
     BE_NCONST MD3::Surface *pcSurfaces = (BE_NCONST MD3::Surface *)(mBuffer + pcHeader->OFS_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
     // Navigate to the list of tags
     BE_NCONST MD3::Tag *pcTags = (BE_NCONST MD3::Tag *)(mBuffer + pcHeader->OFS_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
     // Allocate output storage
     pScene->mNumMeshes = pcHeader->NUM_SURFACES;
     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) {
         for (unsigned int i = 0; i < pcHeader->NUM_TAGS; ++i, ++pcTags) {
             aiNode *nd = pScene->mRootNode->mChildren[i] = new aiNode();
             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->mName.Set((const char *)pcTags->NAME);
             nd->mParent = pScene->mRootNode;
             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/ByteSwapper.h>
 #include <assimp/fast_atof.h>
 #include <assimp/fast_atof.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/DefaultLogger.hpp>
+#include <unordered_set>
 #include <utility>
 #include <utility>
 
 
 namespace Assimp {
 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) {
 PLY::EDataType PLY::Property::ParseDataType(std::vector<char> &buffer) {
     ai_assert(!buffer.empty());
     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
         // if the exact semantic can't be determined, just store
         // the original string identifier
         // the original string identifier
         pOut->szName = std::string(&buffer[0], &buffer[0] + strlen(&buffer[0]));
         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))
     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) {
 bool PLY::DOM::ParseHeader(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, bool isBinary) {
     ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseHeader() begin");
     ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseHeader() begin");
 
 
+    std::unordered_set<std::string> definedAlElements;
     // parse all elements
     // parse all elements
     while (!buffer.empty()) {
     while (!buffer.empty()) {
         // skip all comments
         // skip all comments
@@ -421,6 +443,13 @@ bool PLY::DOM::ParseHeader(IOStreamBuffer<char> &streamBuffer, std::vector<char>
         PLY::Element out;
         PLY::Element out;
         if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) {
         if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) {
             // add the element to the list of elements
             // 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);
             alElements.push_back(out);
         } else if (TokenMatch(buffer, "end_header", 10)) {
         } else if (TokenMatch(buffer, "end_header", 10)) {
             // we have reached the end of the header
             // 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
     // now allocate the output array
-    pcNode->mChildren = new aiNode*[pcNode->mNumChildren];
+    pcNode->mChildren = new aiNode *[pcNode->mNumChildren];
 
 
     // and fill all subnodes
     // and fill all subnodes
     unsigned int qq( 0 );
     unsigned int qq( 0 );

+ 6 - 4
code/CMakeLists.txt

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

+ 9 - 4
code/Common/Assimp.cpp

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

+ 4 - 5
code/Common/Importer.cpp

@@ -848,11 +848,7 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) {
             break;
             break;
         }
         }
 #ifdef ASSIMP_BUILD_DEBUG
 #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 the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
         if (pimpl->bExtraVerbose)   {
         if (pimpl->bExtraVerbose)   {
             ASSIMP_LOG_DEBUG("Verbose Import: re-validating data structures");
             ASSIMP_LOG_DEBUG("Verbose Import: re-validating data structures");
@@ -864,6 +860,7 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) {
                 break;
                 break;
             }
             }
         }
         }
+#endif  // no validation
 #endif // ! DEBUG
 #endif // ! DEBUG
     }
     }
     pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()),
     pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()),
@@ -939,6 +936,7 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
         profiler->EndRegion( "postprocess" );
         profiler->EndRegion( "postprocess" );
     }
     }
 
 
+#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
     // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
     // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
     if ( pimpl->bExtraVerbose || requestValidation  ) {
     if ( pimpl->bExtraVerbose || requestValidation  ) {
         ASSIMP_LOG_DEBUG( "Verbose Import: revalidating data structures" );
         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" );
             ASSIMP_LOG_ERROR( "Verbose Import: failed to revalidate data structures" );
         }
         }
     }
     }
+#endif // no validation
 
 
     // clear any data allocated by post-process steps
     // clear any data allocated by post-process steps
     pimpl->mPPShared->Clean();
     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);
     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
 // Update changed meshes in all nodes
 void UpdateNodes(const std::vector<unsigned int> &replaceMeshIndex, aiNode *node) {
 void UpdateNodes(const std::vector<unsigned int> &replaceMeshIndex, aiNode *node) {
+    ai_assert(node != nullptr);
+    
     if (node->mNumMeshes) {
     if (node->mNumMeshes) {
         unsigned int newSize = 0;
         unsigned int newSize = 0;
         for (unsigned int m = 0; m < node->mNumMeshes; ++m) {
         for (unsigned int m = 0; m < node->mNumMeshes; ++m) {
             unsigned int add = node->mMeshes[m] << 2;
             unsigned int add = node->mMeshes[m] << 2;
             for (unsigned int i = 0; i < 4; ++i) {
             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
     // call all subnodes recursively
@@ -167,6 +177,10 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
         // with the largest number of primitives
         // with the largest number of primitives
         unsigned int aiNumPerPType[4] = { 0, 0, 0, 0 };
         unsigned int aiNumPerPType[4] = { 0, 0, 0, 0 };
         aiFace *pFirstFace = mesh->mFaces;
         aiFace *pFirstFace = mesh->mFaces;
+        if (pFirstFace == nullptr) {
+            continue;
+        }
+
         aiFace *const pLastFace = pFirstFace + mesh->mNumFaces;
         aiFace *const pLastFace = pFirstFace + mesh->mNumFaces;
 
 
         unsigned int numPolyVerts = 0;
         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
 // Update a node after some meshes have been split
 void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& avList) {
 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
     // for every index in out list build a new entry
     std::vector<unsigned int> aiEntries;
     std::vector<unsigned int> aiEntries;
     aiEntries.reserve(pcNode->mNumMeshes + 1);
     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);
                 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
 #pragma once
 
 
-#if defined(_WIN32)
+#if defined(_MSC_VER)
 #  pragma warning( disable: 4273)
 #  pragma warning( disable: 4273)
 #  define P2T_COMPILER_DLLEXPORT __declspec(dllexport)
 #  define P2T_COMPILER_DLLEXPORT __declspec(dllexport)
 #  define P2T_COMPILER_DLLIMPORT __declspec(dllimport)
 #  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;
 using namespace Assimp;
 
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) {
 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);
     aiAttachLogStream(&stream);
 
 
     Importer importer;
     Importer importer;

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

@@ -676,6 +676,19 @@ enum aiComponent
 #define AI_CONFIG_FBX_CONVERT_TO_M \
 #define AI_CONFIG_FBX_CONVERT_TO_M \
     "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.
 /** @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
 #define AI_DEFINES_H_INC
 
 
 #ifdef __GNUC__
 #ifdef __GNUC__
-#pragma GCC system_header
+#  pragma GCC system_header
 #endif
 #endif
 
 
 #include <assimp/config.h>
 #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
  * build in this case. 'XX' stands for the most common file
  * extension of the file format. E.g.:
  * extension of the file format. E.g.:
  * ASSIMP_BUILD_NO_X_IMPORTER disables the X loader.
  * 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
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_X
-#define ASSIMP_BUILD_NEED_Z_INFLATE
+#  define ASSIMP_BUILD_NEED_Z_INFLATE
 #endif
 #endif
 
 
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
-#define ASSIMP_BUILD_NEED_Z_INFLATE
+#  define ASSIMP_BUILD_NEED_Z_INFLATE
 #endif
 #endif
 
 
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
 #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
 #endif
 
 
 #ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
 #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
 #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)
 #if (!defined SIZE_MAX)
-#define SIZE_MAX (~((size_t)0))
+#  define SIZE_MAX (~((size_t)0))
 #endif
 #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'):
  * post processing step. This is the current list of process names ('XX'):
  * CALCTANGENTS
  * CALCTANGENTS
  * JOINVERTICES
  * JOINVERTICES
@@ -134,46 +135,50 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * OPTIMIZEGRAPH
  * OPTIMIZEGRAPH
  * GENENTITYMESHES
  * GENENTITYMESHES
  * FIXTEXTUREPATHS
  * 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
 #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
 #ifdef _MSC_VER
     #pragma warning(disable : 4521 4512 4714 4127 4351 4510)
     #pragma warning(disable : 4521 4512 4714 4127 4351 4510)
     #ifdef ASSIMP_BUILD_DLL_EXPORT
     #ifdef ASSIMP_BUILD_DLL_EXPORT
         #pragma warning(disable : 4251)
         #pragma warning(disable : 4251)
     #endif
     #endif
-    /* Force the compiler to inline a function, if possible */
     #define AI_FORCE_INLINE inline
     #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)
     #define AI_WONT_RETURN __declspec(noreturn)
 #elif defined(SWIG)
 #elif defined(SWIG)
   /* Do nothing, the relevant defines are all in AssimpSwigPort.i */
   /* 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. */
      * to typedef all structs/enums. */
 //////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////
 #if (defined ASSIMP_DOXYGEN_BUILD)
 #if (defined ASSIMP_DOXYGEN_BUILD)
-#define C_STRUCT
-#define C_ENUM
+#  define C_STRUCT
+#  define C_ENUM
 #else
 #else
-#define C_STRUCT struct
-#define C_ENUM enum
+#  define C_STRUCT struct
+#  define C_ENUM enum
 #endif
 #endif
 #endif
 #endif
 
 
 #if (defined(__BORLANDC__) || defined(__BCPLUSPLUS__))
 #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
 #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
 #ifndef ASSIMP_BUILD_SINGLETHREADED
-#define ASSIMP_BUILD_SINGLETHREADED
+#  define ASSIMP_BUILD_SINGLETHREADED
 #endif
 #endif
 
 
 #if defined(_DEBUG) || !defined(NDEBUG)
 #if defined(_DEBUG) || !defined(NDEBUG)
-#define ASSIMP_BUILD_DEBUG
+#  define ASSIMP_BUILD_DEBUG
 #endif
 #endif
 
 
 //////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////
@@ -291,55 +298,74 @@ typedef unsigned int ai_uint;
 #ifdef __cplusplus
 #ifdef __cplusplus
 constexpr ai_real ai_epsilon = (ai_real) 1e-6;
 constexpr ai_real ai_epsilon = (ai_real) 1e-6;
 #else
 #else
-#define ai_epsilon ((ai_real)1e-6)
+#  define ai_epsilon ((ai_real)1e-6)
 #endif
 #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 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
 #endif
 #if defined(__BIG_ENDIAN__)
 #if defined(__BIG_ENDIAN__)
-#define AI_BUILD_BIG_ENDIAN
+#  define AI_BUILD_BIG_ENDIAN
 #endif
 #endif
 
 
 /**
 /**
- *  To avoid running out of memory
+ *  @brief To avoid running out of memory
+ *
  *  This can be adjusted for specific use cases
  *  This can be adjusted for specific use cases
  *  It's NOT a total limit, just a limit for individual allocations
  *  It's NOT a total limit, just a limit for individual allocations
  */
  */
 #define AI_MAX_ALLOC(type) ((256U * 1024 * 1024) / sizeof(type))
 #define AI_MAX_ALLOC(type) ((256U * 1024 * 1024) / sizeof(type))
 
 
 #ifndef _MSC_VER
 #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
 #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
 #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)
 #if (defined ASSIMP_BUILD_DEBUG)
-#define AI_DEBUG_INVALIDATE_PTR(x) x = NULL;
+#  define AI_DEBUG_INVALIDATE_PTR(x) x = NULL;
 #else
 #else
-#define AI_DEBUG_INVALIDATE_PTR(x)
+#  define AI_DEBUG_INVALIDATE_PTR(x)
 #endif
 #endif
 
 
 #define AI_COUNT_OF(X) (sizeof(X) / sizeof((X)[0]))
 #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
 #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
 *  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
 *  pair is called a 'material property'. C++ users should use the provided
 *  member functions of aiMaterial to process material properties, C users
 *  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).
 *  The library defines a set of standard keys (AI_MATKEY_XXX).
 */
 */
 #ifdef __cplusplus
 #ifdef __cplusplus

+ 1 - 0
test/CMakeLists.txt

@@ -100,6 +100,7 @@ SET( COMMON
   unit/Common/utBase64.cpp
   unit/Common/utBase64.cpp
   unit/Common/utHash.cpp
   unit/Common/utHash.cpp
   unit/Common/utBaseProcess.cpp
   unit/Common/utBaseProcess.cpp
+  unit/Common/utLogger.cpp
 )
 )
 
 
 SET(Geometry 
 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) {
 TEST_F(utFBXImporterExporter, importCubesWithOutOfRangeFloat) {
     Assimp::Importer importer;
     Assimp::Importer importer;
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/cubes_with_outofrange_float.fbx", aiProcess_ValidateDataStructure);
     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
 #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) {
 TEST_F(utPLYImportExport, importerMultipleTest) {
     Assimp::Importer importer;
     Assimp::Importer importer;
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure);
     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);
     EXPECT_NE(nullptr, scene->mMeshes[0]);
     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(6u, scene->mMeshes[0]->mNumFaces);
     EXPECT_EQ(aiPrimitiveType_POLYGON, scene->mMeshes[0]->mPrimitiveTypes);
     EXPECT_EQ(aiPrimitiveType_POLYGON, scene->mMeshes[0]->mPrimitiveTypes);
     EXPECT_EQ(true, scene->mMeshes[0]->HasTextureCoords(0));
     EXPECT_EQ(true, scene->mMeshes[0]->HasTextureCoords(0));
@@ -121,7 +121,7 @@ TEST_F(utPLYImportExport, importBinaryPLY) {
 
 
     EXPECT_NE(nullptr, scene);
     EXPECT_NE(nullptr, scene);
     EXPECT_NE(nullptr, scene->mMeshes[0]);
     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);
     EXPECT_EQ(12u, scene->mMeshes[0]->mNumFaces);
 }
 }
 
 
@@ -160,7 +160,7 @@ TEST_F(utPLYImportExport, vertexColorTest) {
 TEST_F(utPLYImportExport, pointcloudTest) {
 TEST_F(utPLYImportExport, pointcloudTest) {
     Assimp::Importer importer;
     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);
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/issue623.ply", 0);
     EXPECT_NE(nullptr, scene);
     EXPECT_NE(nullptr, scene);
 
 
@@ -192,7 +192,7 @@ static const char *test_file =
 
 
 TEST_F(utPLYImportExport, parseErrorTest) {
 TEST_F(utPLYImportExport, parseErrorTest) {
     Assimp::Importer importer;
     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);
     const aiScene *scene = importer.ReadFileFromMemory(test_file, strlen(test_file), 0);
     EXPECT_NE(nullptr, scene);
     EXPECT_NE(nullptr, scene);
 }
 }
@@ -207,5 +207,57 @@ TEST_F(utPLYImportExport, parseInvalid) {
 TEST_F(utPLYImportExport, payload_JVN42386607) {
 TEST_F(utPLYImportExport, payload_JVN42386607) {
     Assimp::Importer importer;
     Assimp::Importer importer;
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/payload_JVN42386607", 0);
     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);
+}