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

Merge branch 'master' into kimkulling/mikktspace_issue-2363

Kim Kulling 1 неделя назад
Родитель
Сommit
f56a0b4592
100 измененных файлов с 2670 добавлено и 1984 удалено
  1. 1 1
      .clang-format
  2. 107 35
      .github/workflows/ccpp.yml
  3. 52 0
      .github/workflows/cd.yml
  4. 1 1
      .github/workflows/cifuzz.yml
  5. 51 0
      .github/workflows/inno_setup
  6. 6 0
      .gitignore
  7. 44 44
      Build.md
  8. 7 7
      CHANGES
  9. 220 122
      CMakeLists.txt
  10. 2 2
      CREDITS
  11. 8 12
      Dockerfile
  12. 4 4
      INSTALL
  13. 25 24
      Readme.md
  14. 2 2
      SECURITY.md
  15. 2 3
      cmake-modules/DebSourcePPA.cmake
  16. 5 5
      cmake-modules/FindDirectX.cmake
  17. 40 12
      cmake-modules/Findassimp.cmake
  18. 1 30
      cmake-modules/HunterGate.cmake
  19. 3 3
      cmake-modules/PrecompiledHeader.cmake
  20. 0 8
      code/.editorconfig
  21. 42 43
      code/AssetLib/3DS/3DSConverter.cpp
  22. 3 6
      code/AssetLib/3DS/3DSExporter.cpp
  23. 3 4
      code/AssetLib/3DS/3DSExporter.h
  24. 3 5
      code/AssetLib/3DS/3DSHelper.h
  25. 2 2
      code/AssetLib/3DS/3DSLoader.cpp
  26. 3 6
      code/AssetLib/3DS/3DSLoader.h
  27. 17 1
      code/AssetLib/3MF/3MFTypes.h
  28. 6 1
      code/AssetLib/3MF/3MFXmlTags.h
  29. 5 5
      code/AssetLib/3MF/D3MFExporter.cpp
  30. 1 1
      code/AssetLib/3MF/D3MFExporter.h
  31. 10 5
      code/AssetLib/3MF/D3MFImporter.cpp
  32. 1 1
      code/AssetLib/3MF/D3MFImporter.h
  33. 3 3
      code/AssetLib/3MF/D3MFOpcPackage.cpp
  34. 1 1
      code/AssetLib/3MF/D3MFOpcPackage.h
  35. 137 31
      code/AssetLib/3MF/XmlSerializer.cpp
  36. 7 4
      code/AssetLib/3MF/XmlSerializer.h
  37. 116 120
      code/AssetLib/AC/ACLoader.cpp
  38. 5 5
      code/AssetLib/AC/ACLoader.h
  39. 12 31
      code/AssetLib/AMF/AMFImporter.cpp
  40. 1 2
      code/AssetLib/AMF/AMFImporter.hpp
  41. 5 5
      code/AssetLib/AMF/AMFImporter_Geometry.cpp
  42. 7 8
      code/AssetLib/AMF/AMFImporter_Material.cpp
  43. 9 23
      code/AssetLib/AMF/AMFImporter_Node.hpp
  44. 6 5
      code/AssetLib/AMF/AMFImporter_Postprocess.cpp
  45. 20 15
      code/AssetLib/ASE/ASELoader.cpp
  46. 4 5
      code/AssetLib/ASE/ASELoader.h
  47. 214 190
      code/AssetLib/ASE/ASEParser.cpp
  48. 11 8
      code/AssetLib/ASE/ASEParser.h
  49. 1 1
      code/AssetLib/Assbin/AssbinExporter.cpp
  50. 1 1
      code/AssetLib/Assbin/AssbinExporter.h
  51. 1 1
      code/AssetLib/Assbin/AssbinFileWriter.cpp
  52. 1 1
      code/AssetLib/Assbin/AssbinFileWriter.h
  53. 11 4
      code/AssetLib/Assbin/AssbinLoader.cpp
  54. 2 3
      code/AssetLib/Assbin/AssbinLoader.h
  55. 2 3
      code/AssetLib/Assxml/AssxmlExporter.cpp
  56. 1 2
      code/AssetLib/Assxml/AssxmlExporter.h
  57. 2 4
      code/AssetLib/Assxml/AssxmlFileWriter.cpp
  58. 1 1
      code/AssetLib/Assxml/AssxmlFileWriter.h
  59. 2 2
      code/AssetLib/B3D/B3DImporter.cpp
  60. 1 1
      code/AssetLib/B3D/B3DImporter.h
  61. 4 8
      code/AssetLib/BVH/BVHLoader.cpp
  62. 8 14
      code/AssetLib/BVH/BVHLoader.h
  63. 1 1
      code/AssetLib/Blender/BlenderBMesh.cpp
  64. 1 1
      code/AssetLib/Blender/BlenderBMesh.h
  65. 1 2
      code/AssetLib/Blender/BlenderDNA.cpp
  66. 1 2
      code/AssetLib/Blender/BlenderDNA.h
  67. 4 2
      code/AssetLib/Blender/BlenderDNA.inl
  68. 3 4
      code/AssetLib/Blender/BlenderIntermediate.h
  69. 7 8
      code/AssetLib/Blender/BlenderLoader.cpp
  70. 1 1
      code/AssetLib/Blender/BlenderLoader.h
  71. 1 2
      code/AssetLib/Blender/BlenderModifier.cpp
  72. 1 2
      code/AssetLib/Blender/BlenderModifier.h
  73. 1 2
      code/AssetLib/Blender/BlenderScene.h
  74. 1 1
      code/AssetLib/Blender/BlenderSceneGen.h
  75. 1 1
      code/AssetLib/Blender/BlenderTessellator.cpp
  76. 1 1
      code/AssetLib/Blender/BlenderTessellator.h
  77. 10 22
      code/AssetLib/C4D/C4DImporter.cpp
  78. 3 1
      code/AssetLib/C4D/C4DImporter.h
  79. 30 24
      code/AssetLib/COB/COBLoader.cpp
  80. 2 3
      code/AssetLib/COB/COBLoader.h
  81. 1 1
      code/AssetLib/COB/COBScene.h
  82. 65 52
      code/AssetLib/CSM/CSMLoader.cpp
  83. 1 1
      code/AssetLib/CSM/CSMLoader.h
  84. 308 255
      code/AssetLib/Collada/ColladaExporter.cpp
  85. 2 3
      code/AssetLib/Collada/ColladaExporter.h
  86. 1 1
      code/AssetLib/Collada/ColladaHelper.cpp
  87. 1 1
      code/AssetLib/Collada/ColladaHelper.h
  88. 57 56
      code/AssetLib/Collada/ColladaLoader.cpp
  89. 25 35
      code/AssetLib/Collada/ColladaLoader.h
  90. 371 329
      code/AssetLib/Collada/ColladaParser.cpp
  91. 83 112
      code/AssetLib/Collada/ColladaParser.h
  92. 9 20
      code/AssetLib/DXF/DXFHelper.h
  93. 269 24
      code/AssetLib/DXF/DXFLoader.cpp
  94. 2 2
      code/AssetLib/DXF/DXFLoader.h
  95. 22 20
      code/AssetLib/FBX/FBXAnimation.cpp
  96. 10 53
      code/AssetLib/FBX/FBXBinaryTokenizer.cpp
  97. 3 2
      code/AssetLib/FBX/FBXCommon.h
  98. 1 1
      code/AssetLib/FBX/FBXCompileConfig.h
  99. 93 35
      code/AssetLib/FBX/FBXConverter.cpp
  100. 3 4
      code/AssetLib/FBX/FBXConverter.h

+ 1 - 1
.clang-format

@@ -70,7 +70,7 @@ IncludeCategories:
   - Regex:           '^<.*'
   - Regex:           '^<.*'
     Priority:        3
     Priority:        3
 # IncludeIsMainRegex: '(Test)?$'
 # IncludeIsMainRegex: '(Test)?$'
-IndentCaseLabels: false 
+IndentCaseLabels: false
 #IndentPPDirectives: AfterHash
 #IndentPPDirectives: AfterHash
 IndentWidth:     4
 IndentWidth:     4
 # IndentWrappedFunctionNames: false
 # IndentWrappedFunctionNames: false

+ 107 - 35
.github/workflows/ccpp.yml

@@ -7,22 +7,30 @@ on:
     branches: [ master ]
     branches: [ master ]
 
 
 permissions:
 permissions:
-  contents: read # to fetch code (actions/checkout)
+  contents: write # to fetch code (actions/checkout),and release
 
 
 jobs:
 jobs:
-  job:
+  build:
     name: ${{ matrix.name }}-build-and-test
     name: ${{ matrix.name }}-build-and-test
     runs-on: ${{ matrix.os }}
     runs-on: ${{ matrix.os }}
     strategy:
     strategy:
       fail-fast: false
       fail-fast: false
       matrix:
       matrix:
-        name: [ubuntu-latest-g++, macos-latest-clang++, windows-latest-cl.exe, ubuntu-latest-clang++, ubuntu-gcc-hunter, macos-clang-hunter, windows-msvc-hunter]
+        name: [ubuntu-latest-g++, macos-latest-clang++, windows-latest-cl.exe, windows-latest-clang.exe, ubuntu-latest-clang++]
         # For Windows msvc, for Linux and macOS let's use the clang compiler, use gcc for Linux.
         # For Windows msvc, for Linux and macOS let's use the clang compiler, use gcc for Linux.
         include:
         include:
+          - name: windows-latest-clang.exe
+            os: windows-latest
+            cxx: clang++.exe
+            cc: clang.exe
           - name: windows-latest-cl.exe
           - name: windows-latest-cl.exe
             os: windows-latest
             os: windows-latest
             cxx: cl.exe
             cxx: cl.exe
             cc: cl.exe
             cc: cl.exe
+          - name: windows-hunter-latest-cl.exe
+            os: windows-latest
+            cxx: cl.exe
+            cc: cl.exe
           - name: ubuntu-latest-clang++
           - name: ubuntu-latest-clang++
             os: ubuntu-latest
             os: ubuntu-latest
             cxx: clang++
             cxx: clang++
@@ -35,17 +43,11 @@ jobs:
             os: ubuntu-latest
             os: ubuntu-latest
             cxx: g++
             cxx: g++
             cc: gcc
             cc: gcc
-          - name: ubuntu-gcc-hunter
-            os: ubuntu-latest
-            toolchain: ninja-gcc-cxx17-fpic
-          - name: macos-clang-hunter
-            os: macos-latest
-            toolchain: ninja-clang-cxx17-fpic
-          - name: windows-msvc-hunter
-            os: windows-latest
-            toolchain: ninja-vs-win64-cxx17
 
 
     steps:
     steps:
+    - name: ccache
+      uses: hendrikmuhs/[email protected]
+      
     - uses: actions/checkout@v4
     - uses: actions/checkout@v4
       with:
       with:
           submodules: true
           submodules: true
@@ -55,29 +57,15 @@ jobs:
     - uses: ilammy/msvc-dev-cmd@v1
     - uses: ilammy/msvc-dev-cmd@v1
     
     
     - name: Set Compiler Environment
     - name: Set Compiler Environment
-      if: "!endsWith(matrix.name, 'hunter')"
       uses: lukka/set-shell-env@v1
       uses: lukka/set-shell-env@v1
       with:
       with:
         CXX: ${{ matrix.cxx }}
         CXX: ${{ matrix.cxx }}
         CC: ${{ matrix.cc }}
         CC: ${{ matrix.cc }}
 
 
-    - name: Set Compiler Environment for Hunter on Windows
-      if: startsWith(matrix.name, 'windows') && endsWith(matrix.name, 'hunter')
-      uses: lukka/set-shell-env@v1
-      with:
-        VS160COMNTOOLS: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools
-
-    - name: Checkout Hunter toolchains
-      if: endsWith(matrix.name, 'hunter')
-      uses: actions/checkout@v4
-      with:
-        repository: cpp-pm/polly
-        path: cmake/polly
-
     - name: Cache DX SDK
     - name: Cache DX SDK
       id: dxcache
       id: dxcache
       if: contains(matrix.name, 'windows')
       if: contains(matrix.name, 'windows')
-      uses: actions/cache@v3
+      uses: actions/cache@v4
       with:
       with:
         path: '${{ github.workspace }}/DX_SDK'
         path: '${{ github.workspace }}/DX_SDK'
         key: ${{ runner.os }}-DX_SDK
         key: ${{ runner.os }}-DX_SDK
@@ -85,20 +73,20 @@ jobs:
           ${{ runner.os }}-DX_SDK
           ${{ runner.os }}-DX_SDK
 
 
     - name: Download DXSetup
     - name: Download DXSetup
-      if: contains(matrix.name, 'windows') && steps.dxcache.outputs.cache-hit != 'true'
+      if: contains(matrix.name, 'windows-latest-cl.exe') && steps.dxcache.outputs.cache-hit != 'true'
       run: |
       run: |
         curl -s -o DXSDK_Jun10.exe --location https://download.microsoft.com/download/A/E/7/AE743F1F-632B-4809-87A9-AA1BB3458E31/DXSDK_Jun10.exe
         curl -s -o DXSDK_Jun10.exe --location https://download.microsoft.com/download/A/E/7/AE743F1F-632B-4809-87A9-AA1BB3458E31/DXSDK_Jun10.exe
         cmd.exe /c start /wait .\DXSDK_Jun10.exe /U /O /F /S /P "${{ github.workspace }}\DX_SDK"
         cmd.exe /c start /wait .\DXSDK_Jun10.exe /U /O /F /S /P "${{ github.workspace }}\DX_SDK"
 
 
     - name: Set Windows specific CMake arguments
     - name: Set Windows specific CMake arguments
-      if: contains(matrix.name, 'windows')
+      if: contains(matrix.name, 'windows-latest-cl.exe')
       id: windows_extra_cmake_args
       id: windows_extra_cmake_args
-      run: echo "::set-output name=args::-DASSIMP_BUILD_ASSIMP_TOOLS=1 -DASSIMP_BUILD_ASSIMP_VIEW=1 -DASSIMP_BUILD_ZLIB=1"
+      run: echo ":set-output name=args::=-DASSIMP_BUILD_ASSIMP_TOOLS=1 -DASSIMP_BUILD_ASSIMP_VIEW=1" >> $GITHUB_OUTPUT
     
     
     - name: Set Hunter specific CMake arguments
     - name: Set Hunter specific CMake arguments
       if: contains(matrix.name, 'hunter')
       if: contains(matrix.name, 'hunter')
       id: hunter_extra_cmake_args
       id: hunter_extra_cmake_args
-      run: echo "::set-output name=args::-DBUILD_SHARED_LIBS=OFF -DASSIMP_HUNTER_ENABLED=ON -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/cmake/polly/${{ matrix.toolchain }}.cmake"
+      run: echo "args=-DBUILD_SHARED_LIBS=OFF -DASSIMP_HUNTER_ENABLED=ON -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/cmake/polly/${{ matrix.toolchain }}.cmake" >> $GITHUB_OUTPUT
 
 
     - name: configure and build
     - name: configure and build
       uses: lukka/run-cmake@v3
       uses: lukka/run-cmake@v3
@@ -115,14 +103,98 @@ jobs:
     - name: Exclude certain tests in Hunter specific builds
     - name: Exclude certain tests in Hunter specific builds
       if: contains(matrix.name, 'hunter')
       if: contains(matrix.name, 'hunter')
       id: hunter_extra_test_args
       id: hunter_extra_test_args
-      run: echo "::set-output name=args::--gtest_filter=-utOpenGEXImportExport.Importissue1340_EmptyCameraObject:utColladaZaeImportExport.importBlenFromFileTest"
+      run: echo "args=--gtest_filter=-utOpenGEXImportExport.Importissue1340_EmptyCameraObject:utColladaZaeImportExport.importBlenFromFileTest" >> $GITHUB_OUTPUT
 
 
     - name: test
     - name: test
       run: cd build/bin && ./unit ${{ steps.hunter_extra_test_args.outputs.args }}
       run: cd build/bin && ./unit ${{ steps.hunter_extra_test_args.outputs.args }}
       shell: bash
       shell: bash
 
 
-    - uses: actions/upload-artifact@v3
+    - uses: actions/upload-artifact@v4
       if: matrix.name == 'windows-msvc'
       if: matrix.name == 'windows-msvc'
       with:
       with:
-        name: 'assimp-bins-${{ matrix.name }}-${{ github.sha }}'
-        path: build/bin
+        name: 'assimp-bins-${{ matrix.name }}'
+        path: build/bin/assimp*.exe
+        
+    - uses: marvinpinto/action-automatic-releases@latest
+      if: contains(matrix.name, 'windows-msvc-hunter')
+      with:
+        repo_token: "${{ secrets.GITHUB_TOKEN }}"
+        automatic_release_tag: "master"
+        prerelease: true
+        title: "AutoRelease"
+        files: |
+          build/bin/assimp*.exe
+
+  create-release:
+    needs: [build]
+    runs-on: ubuntu-latest
+    if: startsWith(github.ref, 'refs/tags/')
+    steps:
+      - id: create-release
+        uses: actions/create-release@v1
+        env:
+          GITHUB_TOKEN: '${{secrets.GITHUB_TOKEN}}'
+        with:
+          tag_name: '${{github.ref}}'
+          release_name: 'Release ${{github.ref}}'
+          draft: false
+          prerelease: true
+      - run: |
+          echo '${{steps.create-release.outputs.upload_url}}' > release_upload_url.txt
+      - uses: actions/upload-artifact@v4
+        with:
+          name: create-release
+          path: release_upload_url.txt
+
+  upload-release:
+    strategy:
+      matrix:
+        name: [ubuntu-latest-g++, macos-latest-clang++, windows-latest-cl.exe, ubuntu-latest-clang++, ubuntu-gcc-hunter, macos-clang-hunter, windows-msvc-hunter]
+        # For Windows msvc, for Linux and macOS let's use the clang compiler, use gcc for Linux.
+        include:
+          - name: windows-latest-cl.exe
+            os: windows-latest
+            cxx: cl.exe
+            cc: cl.exe
+          - name: ubuntu-latest-clang++
+            os: ubuntu-latest
+            cxx: clang++
+            cc: clang
+          - name: macos-latest-clang++
+            os: macos-latest
+            cxx: clang++
+            cc: clang
+          - name: ubuntu-latest-g++
+            os: ubuntu-latest
+            cxx: g++
+            cc: gcc
+          - name: ubuntu-gcc-hunter
+            os: ubuntu-latest
+            toolchain: ninja-gcc-cxx17-fpic
+          - name: macos-clang-hunter
+            os: macos-latest
+            toolchain: ninja-clang-cxx17-fpic
+          - name: windows-msvc-hunter
+            os: windows-latest
+            toolchain: ninja-vs-win64-cxx17
+            
+    needs: [create-release]
+    runs-on: ubuntu-latest
+    if: startsWith(github.ref, 'refs/tags/')
+    steps:
+      - uses: softprops/action-gh-release@v2
+        with:
+          name: create-release
+      - id: upload-url
+        run: |
+          echo "url=$(cat create-release/release_upload_url.txt)" >> $GITHUB_OUTPUT
+      - uses: actions/download-artifact@v4
+        with:
+          name: 'assimp-bins-${{ matrix.name }}-${{ github.sha }}'
+      - uses: actions/upload-release-asset@v1
+        env:
+          GITHUB_TOKEN: '${{secrets.GITHUB_TOKEN}}'
+        with:
+          files: |
+            *.zip
+ 

+ 52 - 0
.github/workflows/cd.yml

@@ -0,0 +1,52 @@
+name: Build and Publish Prebuilt Binaries
+
+on:
+  release:
+    types: [created]
+    
+jobs:
+  build:
+    name: ${{ matrix.name }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - name: windows-x64
+            os: windows-latest
+            arch: x64
+          - name: windows-x86
+            os: windows-latest
+            arch: x86
+            cmake_args: -A Win32
+          - name: macos-x64
+            os: macos-13
+          - name: macos-arm64
+            os: macos-latest
+          - name: linux-x64
+            os: ubuntu-latest
+
+    steps:
+      - uses: actions/[email protected]
+
+      - uses: lukka/get-cmake@latest
+
+      - uses: ilammy/msvc-dev-cmd@v1
+        with:
+          arch: ${{ matrix.arch }}
+      
+      - name: Build
+        run: |
+          cmake -B build -S . ${{ matrix.cmake_args }} -DCMAKE_BUILD_TYPE=Release -DASSIMP_BUILD_TESTS=OFF
+          cmake --build build --config Release
+
+      - uses: TheDoctor0/[email protected]
+        with:
+          filename: ${{ matrix.name }}-${{ github.event.release.tag_name }}.zip
+          directory: build/bin/
+
+      - uses: softprops/action-gh-release@v2
+        with:
+          files: build/bin/${{ matrix.name }}-${{ github.event.release.tag_name }}.zip
+          append_body: true
+          fail_on_unmatched_files: true

+ 1 - 1
.github/workflows/cifuzz.yml

@@ -19,7 +19,7 @@ jobs:
         dry-run: false
         dry-run: false
         language: c++
         language: c++
     - name: Upload Crash
     - name: Upload Crash
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       if: failure() && steps.build.outcome == 'success'
       if: failure() && steps.build.outcome == 'success'
       with:
       with:
         name: artifacts
         name: artifacts

+ 51 - 0
.github/workflows/inno_setup

@@ -0,0 +1,51 @@
+name: Build Windows Installer
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+jobs:
+  build:
+    name: Build the Inno Setup Installer
+    runs-on: windows-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: lukka/get-cmake@latest
+      - uses: ilammy/msvc-dev-cmd@v1
+
+
+      - name: Cache DX SDK
+        id: dxcache
+        uses: actions/cache@v4
+        with:
+          path: '${{ github.workspace }}/DX_SDK'
+          key: ${{ runner.os }}-DX_SDK
+          restore-keys: |
+            ${{ runner.os }}-DX_SDK
+
+      - name: Download DXSetup
+        run: |
+          curl -s -o DXSDK_Jun10.exe --location https://download.microsoft.com/download/A/E/7/AE743F1F-632B-4809-87A9-AA1BB3458E31/DXSDK_Jun10.exe
+          cmd.exe /c start /wait .\DXSDK_Jun10.exe /U /O /F /S /P "${{ github.workspace }}\DX_SDK"
+
+      - name: Set Windows specific CMake arguments
+        id: windows_extra_cmake_args
+        run: echo "::set-output name=args::-DASSIMP_BUILD_ASSIMP_TOOLS=1 -DASSIMP_BUILD_ASSIMP_VIEW=1 -DASSIMP_BUILD_ZLIB=1"
+
+      - name: configure and build
+        uses: lukka/run-cmake@v3
+        env:
+          DXSDK_DIR: '${{ github.workspace }}/DX_SDK'
+
+        with:
+          cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
+          cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
+          cmakeAppendedArgs: '-GNinja -DCMAKE_BUILD_TYPE=Release ${{ steps.windows_extra_cmake_args.outputs.args }} ${{ steps.hunter_extra_cmake_args.outputs.args }}'
+          buildWithCMakeArgs: '--parallel 24 -v'
+          buildDirectory: '${{ github.workspace }}/build/'
+
+      - name: Compile .ISS to .EXE Installer
+        uses: Minionguyjpro/[email protected]
+        with:
+          path: packaging/windows-innosetup/script_x64.iss
+          options: /O+

+ 6 - 0
.gitignore

@@ -120,3 +120,9 @@ tools/assimp_qt_viewer/ui_mainwindow.h
 
 
 #Generated directory
 #Generated directory
 generated/*
 generated/*
+
+# 3rd party cloned repos/tarballs etc
+# meshlab repo, automatically cloned via CMake (to gain 2 source files for VRML file format conversion)
+contrib/meshlab/autoclone
+# tinyusdz repo, automatically cloned via CMake
+contrib/tinyusdz/autoclone

+ 44 - 44
Build.md

@@ -1,36 +1,12 @@
 # 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 (must support C++17 and C99 at least)
+* For Windows
+  *  DX-SDK 9 if you want to use our 3D-Viewer
 
 
 ### 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 +14,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,19 +40,8 @@ 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.
 Version 7.3.0 of g++-mingw-w64 & gcc-mingw-w64 appears to work.
 Version 7.3.0 of g++-mingw-w64 & gcc-mingw-w64 appears to work.
 
 
@@ -111,3 +83,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 - 7
CHANGES

@@ -290,7 +290,7 @@ FEATURES:
 	- Added support for 64 bit version header introduced in FbxSdk2016
 	- Added support for 64 bit version header introduced in FbxSdk2016
 	- Travis: enable coverall support.
 	- Travis: enable coverall support.
 	- PyAssimp: New version of the pyASSIMP 3D viewer, with much improved 3D controls
 	- PyAssimp: New version of the pyASSIMP 3D viewer, with much improved 3D controls
-    - Morph animation support for collada	
+    - Morph animation support for collada
 	- Added support for parameters Ni and Tf in OBJ/MTL file format
 	- Added support for parameters Ni and Tf in OBJ/MTL file format
 	- aiScene: add method to add children
 	- aiScene: add method to add children
 	- Added new option to IFC importer to control tessellation angle + removed unused IFC option
 	- Added new option to IFC importer to control tessellation angle + removed unused IFC option
@@ -300,7 +300,7 @@ FEATURES:
 	- travis ci: enable sudo support.
 	- travis ci: enable sudo support.
 	- openddlparser: integrate release v0.4.0
 	- openddlparser: integrate release v0.4.0
 	- aiMetaData: Added support for metadata in assbin format
 	- aiMetaData: Added support for metadata in assbin format
-	
+
 FIXES/HOUSEKEEPING:
 FIXES/HOUSEKEEPING:
     - Introduce usage of #pragma statement
     - Introduce usage of #pragma statement
 	- Put cmake-scripts into their own folder
 	- Put cmake-scripts into their own folder
@@ -352,7 +352,7 @@ FIXES/HOUSEKEEPING:
 	- add vertex color export support ( issue 809 )
 	- add vertex color export support ( issue 809 )
 	- Fix memory leak in Collada importer ( issue 1169 )
 	- Fix memory leak in Collada importer ( issue 1169 )
 	- add stp to the list of supported extensions for step-files ( issue 1183 )
 	- add stp to the list of supported extensions for step-files ( issue 1183 )
-	- fix clang build ( Issue-1169 ) 
+	- fix clang build ( Issue-1169 )
 	- fix for FreeBSD
 	- fix for FreeBSD
 	- Import FindPkgMacros to main CMake Configuration
 	- Import FindPkgMacros to main CMake Configuration
 	- Extended support for tessellation parameter to more IFC shapes
 	- Extended support for tessellation parameter to more IFC shapes
@@ -375,7 +375,7 @@ FIXES/HOUSEKEEPING:
 	- Obj-Importer: do not break when detecting an overflow ( issue 1244 )
 	- Obj-Importer: do not break when detecting an overflow ( issue 1244 )
 	- Obj-Importer: fix parsing of multible line data definitions
 	- Obj-Importer: fix parsing of multible line data definitions
 	- Fixed bug where IFC models with multiple IFCSite only loaded 1 site instead of the complete model
 	- Fixed bug where IFC models with multiple IFCSite only loaded 1 site instead of the complete model
-	- PLYImporter: - optimize memory and speed on ply importer / change parser to use a file stream - manage texture path in ply 
+	- PLYImporter: - optimize memory and speed on ply importer / change parser to use a file stream - manage texture path in ply
 	  import - manage texture coords on faces in ply import - correction on point cloud faces generation
 	  import - manage texture coords on faces in ply import - correction on point cloud faces generation
 	- Utf8: integrate new lib ( issue 1158 )
 	- Utf8: integrate new lib ( issue 1158 )
 	- fixed CMAKE_MODULE_PATH overwriting previous values
 	- fixed CMAKE_MODULE_PATH overwriting previous values
@@ -400,7 +400,7 @@ FIXES/HOUSEKEEPING:
 	- Remove std functions deprecated by C++11.
 	- Remove std functions deprecated by C++11.
 	- X-Importer: make it deal with lines
 	- X-Importer: make it deal with lines
 	- use correct path for compilers ( issue 1335 )
 	- use correct path for compilers ( issue 1335 )
-	- Collada: add workaround to deal with polygon with holes 
+	- Collada: add workaround to deal with polygon with holes
 	- update python readme
 	- update python readme
 	- Use unique node names when loading Collada files
 	- Use unique node names when loading Collada files
 	- Fixed many FBX bugs
 	- Fixed many FBX bugs
@@ -429,7 +429,7 @@ FEATURES:
     - C4D: update to latest Melange-SDK
     - C4D: update to latest Melange-SDK
     - Add a gitter channel
     - Add a gitter channel
     - Coverity check enabled
     - Coverity check enabled
-    - Switch to <...> include brackets for public headers 
+    - Switch to <...> include brackets for public headers
     - Enable export by pyAssimp
     - Enable export by pyAssimp
     - CI: check windows build
     - CI: check windows build
     - Add functionality to perform a singlepost-processing step
     - Add functionality to perform a singlepost-processing step
@@ -564,7 +564,7 @@ API CHANGES:
      currently used, however ...)
      currently used, however ...)
    - Some Assimp::Importer methods are const now.
    - Some Assimp::Importer methods are const now.
 
 
-   
+
 1.1 (2010-04-17)
 1.1 (2010-04-17)
 This is the list of relevant changes from the 1.0 (r412) release to 1.1 (r700).
 This is the list of relevant changes from the 1.0 (r412) release to 1.1 (r700).
 
 

+ 220 - 122
CMakeLists.txt

@@ -1,6 +1,6 @@
 # Open Asset Import Library (assimp)
 # Open Asset Import Library (assimp)
 # ----------------------------------------------------------------------
 # ----------------------------------------------------------------------
-# Copyright (c) 2006-2023, assimp team
+# Copyright (c) 2006-2025, assimp team
 #
 #
 # All rights reserved.
 # All rights reserved.
 #
 #
@@ -38,24 +38,76 @@ SET(CMAKE_POLICY_DEFAULT_CMP0012 NEW)
 SET(CMAKE_POLICY_DEFAULT_CMP0074 NEW)
 SET(CMAKE_POLICY_DEFAULT_CMP0074 NEW)
 SET(CMAKE_POLICY_DEFAULT_CMP0092 NEW)
 SET(CMAKE_POLICY_DEFAULT_CMP0092 NEW)
 
 
-CMAKE_MINIMUM_REQUIRED( VERSION 3.10 )
+CMAKE_MINIMUM_REQUIRED( VERSION 3.22 )
+
+#================================================================================#
+#                    Model formats not enabled by default
+#
+#    3rd party projects may not adhere to strict standards enforced by assimp,
+#    in which case those formats must be opt-in; otherwise the 3rd party code
+#    would fail assimp CI checks
+#================================================================================#
+# M3D format import support (assimp integration no longer supported by M3D format author)
+# User may override these in their CMake script to provide M3D import/export support
+# (M3D importer/exporter was disabled for assimp release 5.1 or later)
+OPTION(ASSIMP_BUILD_M3D_IMPORTER "Enable M3D file import" off)
+OPTION(ASSIMP_BUILD_M3D_EXPORTER "Enable M3D file export" off)
+
+# Experimental USD importer: disabled, need to opt-in
+# Note: assimp github PR automatic checks will fail the PR due to compiler warnings in
+# the external, 3rd party tinyusdz code which isn't technically part of the PR since it's
+# auto-cloned during build; so MUST disable the feature or the PR will be rejected
+OPTION(ASSIMP_BUILD_USD_IMPORTER "Enable USD file import" off)
+OPTION(ASSIMP_BUILD_USD_VERBOSE_LOGS "Enable verbose USD import debug logging" off)
+
+# VRML (.wrl/.x3dv) file import support by leveraging X3D importer and 3rd party file
+# format converter to convert .wrl/.x3dv files to X3D-compatible .xml
+# (Need to make this opt-in because 3rd party code triggers lots of CI code quality warnings)
+OPTION(ASSIMP_BUILD_VRML_IMPORTER "Enable VRML (.wrl/.x3dv) file import" off)
+
+#--------------------------------------------------------------------------------#
+#                  Internal impl for optional model formats
+#--------------------------------------------------------------------------------#
+# Internal/private M3D logic
+if (NOT ASSIMP_BUILD_M3D_IMPORTER)
+    ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_IMPORTER)
+endif () # if (not ASSIMP_BUILD_M3D_IMPORTER)
+if (NOT ASSIMP_BUILD_M3D_EXPORTER)
+    ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_EXPORTER)
+endif () # if (not ASSIMP_BUILD_M3D_EXPORTER)
+
+# Internal/private VRML logic
+if (NOT ASSIMP_BUILD_VRML_IMPORTER)
+    ADD_DEFINITIONS( -DASSIMP_BUILD_NO_VRML_IMPORTER)
+endif () # if (not ASSIMP_BUILD_VRML_IMPORTER)
+#================================================================================#
+
+option(ASSIMP_BUILD_USE_CCACHE "Use ccache to speed up compilation." on)
+
+IF(ASSIMP_BUILD_USE_CCACHE)
+  find_program(CCACHE_PATH ccache)
+  IF (CCACHE_PATH)
+    set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_PATH})
+    set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_PATH})
+  ENDIF()
+ENDIF()
 
 
-# Disabled importers: m3d for 5.1
-ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_IMPORTER)
-ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_EXPORTER)
 # Toggles the use of the hunter package manager
 # Toggles the use of the hunter package manager
 option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
 option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
 
 
 IF(ASSIMP_HUNTER_ENABLED)
 IF(ASSIMP_HUNTER_ENABLED)
   include("cmake-modules/HunterGate.cmake")
   include("cmake-modules/HunterGate.cmake")
   HunterGate(
   HunterGate(
-    URL "https://github.com/cpp-pm/hunter/archive/v0.24.18.tar.gz"
-    SHA1 "1292e4d661e1770d6d6ca08c12c07cf34a0bf718"
+    URL "https://github.com/cpp-pm/hunter/archive/v0.25.8.tar.gz"
+    SHA1 "26c79d587883ec910bce168e25f6ac4595f97033"
   )
   )
-  add_definitions(-DASSIMP_USE_HUNTER)
+  ADD_DEFINITIONS(-DASSIMP_USE_HUNTER)
 ENDIF()
 ENDIF()
 
 
-PROJECT(Assimp VERSION 5.3.0)
+PROJECT(Assimp VERSION 6.0.2
+  LANGUAGES C CXX
+  DESCRIPTION "Open Asset Import Library (Assimp) is a library to import various well-known 3D model formats in a uniform manner."
+)
 
 
 # All supported options ###############################################
 # All supported options ###############################################
 
 
@@ -131,35 +183,35 @@ OPTION ( ASSIMP_IGNORE_GIT_HASH
 
 
 IF (WIN32)
 IF (WIN32)
   OPTION( ASSIMP_BUILD_ZLIB
   OPTION( ASSIMP_BUILD_ZLIB
-    "Build your own zlib"
+    "Build your zlib"
     ON
     ON
   )
   )
 ELSE()
 ELSE()
   OPTION( ASSIMP_BUILD_ZLIB
   OPTION( ASSIMP_BUILD_ZLIB
-    "Build your own zlib"
-    ON
+    "Build your zlib"
+    OFF
   )
   )
 ENDIF()
 ENDIF()
 
 
 IF (WIN32)
 IF (WIN32)
-  # Use subset of Windows.h
+  # Use a subset of Windows.h
   ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
   ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
 
 
   IF(MSVC)
   IF(MSVC)
     OPTION( ASSIMP_INSTALL_PDB
     OPTION( ASSIMP_INSTALL_PDB
-      "Install MSVC debug files."
+      "Create MSVC debug symbol files and add to Install target."
       ON )
       ON )
     IF(NOT (MSVC_VERSION LESS 1900))
     IF(NOT (MSVC_VERSION LESS 1900))
-      # Multibyte character set is deprecated since at least MSVC2015 (possibly earlier)
+      # Multibyte character set has been deprecated since at least MSVC2015 (possibly earlier)
       ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
       ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
     ENDIF()
     ENDIF()
 
 
-    # Link statically against c/c++ lib to avoid missing redistriburable such as
+    # Link statically against c/c++ lib to avoid missing redistributable such as
     # "VCRUNTIME140.dll not found. Try reinstalling the app.", but give users
     # "VCRUNTIME140.dll not found. Try reinstalling the app.", but give users
     # a choice to opt for the shared runtime if they want.
     # a choice to opt for the shared runtime if they want.
     option(USE_STATIC_CRT "Link against the static runtime libraries." OFF)
     option(USE_STATIC_CRT "Link against the static runtime libraries." OFF)
 
 
-    # The CMAKE_CXX_FLAGS vars can be overriden by some Visual Studio generators, so we use an alternative
+    # The CMAKE_CXX_FLAGS vars can be overridden by some Visual Studio generators, so we use an alternative
     # global method here:
     # global method here:
     if (${USE_STATIC_CRT})
     if (${USE_STATIC_CRT})
       add_compile_options(
       add_compile_options(
@@ -197,7 +249,7 @@ SET (ASSIMP_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
 SET (ASSIMP_VERSION_MINOR ${PROJECT_VERSION_MINOR})
 SET (ASSIMP_VERSION_MINOR ${PROJECT_VERSION_MINOR})
 SET (ASSIMP_VERSION_PATCH ${PROJECT_VERSION_PATCH})
 SET (ASSIMP_VERSION_PATCH ${PROJECT_VERSION_PATCH})
 SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH})
 SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH})
-SET (ASSIMP_SOVERSION 5)
+SET (ASSIMP_SOVERSION 6)
 
 
 SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" )
 SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" )
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD 17)
@@ -249,21 +301,32 @@ SET(ASSIMP_LIBRARY_SUFFIX "" CACHE STRING "Suffix to append to library names")
 IF( UNIX )
 IF( UNIX )
   # Use GNUInstallDirs for Unix predefined directories
   # Use GNUInstallDirs for Unix predefined directories
   INCLUDE(GNUInstallDirs)
   INCLUDE(GNUInstallDirs)
-  # Ensure that we do not run into issues like http://www.tcm.phy.cam.ac.uk/sw/inodes64.html on 32 bit linux
+  # Ensure that we do not run into issues like http://www.tcm.phy.cam.ac.uk/sw/inodes64.html on 32 bit Linux
   IF(NOT ${OPERATING_SYSTEM} MATCHES "Android")
   IF(NOT ${OPERATING_SYSTEM} MATCHES "Android")
-    IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux
+    IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit Linux
       ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 )
       ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 )
     ENDIF()
     ENDIF()
   ENDIF()
   ENDIF()
 ENDIF()
 ENDIF()
 
 
+IF(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND WIN32)
+  ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS )
+  ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS )
+ENDIF()
+
+IF( MSVC OR "${CMAKE_CXX_SIMULATE_ID}" MATCHES "MSVC") # clang with MSVC ABI
+  ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS )
+  ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS )
+endif ()
+
+
 # Grouped compiler settings ########################################
 # Grouped compiler settings ########################################
 IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT MINGW AND NOT HAIKU)
 IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT MINGW AND NOT HAIKU)
   IF(NOT ASSIMP_HUNTER_ENABLED)
   IF(NOT ASSIMP_HUNTER_ENABLED)
     SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
     SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
   ENDIF()
   ENDIF()
-  
-  IF(CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 13)
+
+  IF(CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 13 AND CMAKE_CXX_COMPILER_ID MATCHES "GNU")
     MESSAGE(STATUS "GCC13 detected disabling \"-Wdangling-reference\" in Cpp files as it appears to be a false positive")
     MESSAGE(STATUS "GCC13 detected disabling \"-Wdangling-reference\" in Cpp files as it appears to be a false positive")
     ADD_COMPILE_OPTIONS("$<$<COMPILE_LANGUAGE:CXX>:-Wno-dangling-reference>")
     ADD_COMPILE_OPTIONS("$<$<COMPILE_LANGUAGE:CXX>:-Wno-dangling-reference>")
   ENDIF()
   ENDIF()
@@ -284,22 +347,31 @@ ELSEIF(MSVC)
   ELSE() # msvc
   ELSE() # msvc
     ADD_COMPILE_OPTIONS(/MP /bigobj)
     ADD_COMPILE_OPTIONS(/MP /bigobj)
   ENDIF()
   ENDIF()
-  
+
   # disable "elements of array '' will be default initialized" warning on MSVC2013
   # disable "elements of array '' will be default initialized" warning on MSVC2013
   IF(MSVC12)
   IF(MSVC12)
-    ADD_COMPILE_OPTIONS(/wd4351)	
+    ADD_COMPILE_OPTIONS(/wd4351)
   ENDIF()
   ENDIF()
-  # supress warning for double to float conversion if Double precission is activated
-  ADD_COMPILE_OPTIONS(/wd4244) 
+  # supress warning for double to float conversion if Double precision is activated
+  ADD_COMPILE_OPTIONS(/wd4244)
   SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od")
   SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od")
-  SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
-  SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF")
+  # Allow user to disable PDBs
+  if(ASSIMP_INSTALL_PDB)
+    SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
+    SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF")
+  elseif((GENERATOR_IS_MULTI_CONFIG) OR (CMAKE_BUILD_TYPE MATCHES Release))
+    message("-- MSVC PDB generation disabled. Release binary will not be debuggable.")
+  endif()
+  if(NOT /utf-8 IN_LIST CMAKE_CXX_FLAGS)
+    # Source code is encoded in UTF-8
+    ADD_COMPILE_OPTIONS(/source-charset:utf-8)
+  endif()
 ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
 ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
   IF(NOT ASSIMP_HUNTER_ENABLED)
   IF(NOT ASSIMP_HUNTER_ENABLED)
     SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
     SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
   ENDIF()
   ENDIF()
-  SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long ${CMAKE_CXX_FLAGS}" )
-  SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
+  SET(CMAKE_CXX_FLAGS "-Wno-deprecated-non-prototype -fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long ${CMAKE_CXX_FLAGS}" )
+  SET(CMAKE_C_FLAGS "-Wno-deprecated-non-prototype -fno-strict-aliasing ${CMAKE_C_FLAGS}")
 ELSEIF( MINGW )
 ELSEIF( MINGW )
   IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
   IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
     message(FATAL_ERROR "MinGW is too old to be supported. Please update MinGW and try again.")
     message(FATAL_ERROR "MinGW is too old to be supported. Please update MinGW and try again.")
@@ -311,9 +383,9 @@ ELSEIF( MINGW )
     SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
     SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
   ENDIF()
   ENDIF()
     IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
     IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
-      SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long -Wa,-mbig-obj -g ${CMAKE_CXX_FLAGS}")
+      SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wno-dangling-reference -Wall -Wno-long-long -Wa,-mbig-obj -g ${CMAKE_CXX_FLAGS}")
     ELSE()
     ELSE()
-      SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long -Wa,-mbig-obj -O3 ${CMAKE_CXX_FLAGS}")
+      SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wno-dangling-reference -Wall -Wno-long-long -Wa,-mbig-obj -O3 ${CMAKE_CXX_FLAGS}")
     ENDIF()
     ENDIF()
     SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
     SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
 ENDIF()
 ENDIF()
@@ -330,7 +402,7 @@ ENDIF()
 
 
 IF (ASSIMP_COVERALLS)
 IF (ASSIMP_COVERALLS)
   MESSAGE(STATUS "Coveralls enabled")
   MESSAGE(STATUS "Coveralls enabled")
-  
+
   INCLUDE(Coveralls)
   INCLUDE(Coveralls)
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
@@ -338,7 +410,7 @@ ENDIF()
 
 
 IF (ASSIMP_ASAN)
 IF (ASSIMP_ASAN)
   MESSAGE(STATUS "AddressSanitizer enabled")
   MESSAGE(STATUS "AddressSanitizer enabled")
-  
+
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
   SET(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS} -fsanitize=address")
   SET(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS} -fsanitize=address")
 ENDIF()
 ENDIF()
@@ -422,20 +494,20 @@ ENDIF()
 set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
 set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
 
 
 IF(ASSIMP_HUNTER_ENABLED)
 IF(ASSIMP_HUNTER_ENABLED)
-  set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
-  set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-hunter-config.cmake.in")
-  set(NAMESPACE "${PROJECT_NAME}::")
-  set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
-  set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
-  set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake")
+  SET(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
+  SET(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-hunter-config.cmake.in")
+  SET(NAMESPACE "${PROJECT_NAME}::")
+  SET(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
+  SET(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
+  SET(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake")
 ELSE()
 ELSE()
-  set(CONFIG_INSTALL_DIR "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}")
-  set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-plain-config.cmake.in")
+  SET(CONFIG_INSTALL_DIR "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}")
+  SET(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-plain-config.cmake.in")
   string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE)
   string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE)
-  set(NAMESPACE "${PROJECT_NAME_LOWERCASE}::")
-  set(TARGETS_EXPORT_NAME "${PROJECT_NAME_LOWERCASE}Targets")
-  set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME_LOWERCASE}ConfigVersion.cmake")
-  set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME_LOWERCASE}Config.cmake")
+  SET(NAMESPACE "${PROJECT_NAME_LOWERCASE}::")
+  SET(TARGETS_EXPORT_NAME "${PROJECT_NAME_LOWERCASE}Targets")
+  SET(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME_LOWERCASE}ConfigVersion.cmake")
+  SET(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME_LOWERCASE}Config.cmake")
 ENDIF()
 ENDIF()
 
 
 set(INCLUDE_INSTALL_DIR "include")
 set(INCLUDE_INSTALL_DIR "include")
@@ -452,18 +524,20 @@ configure_package_config_file(
     INSTALL_DESTINATION "${CONFIG_INSTALL_DIR}"
     INSTALL_DESTINATION "${CONFIG_INSTALL_DIR}"
 )
 )
 
 
-install(
-    FILES "${PROJECT_CONFIG}" "${VERSION_CONFIG}"
-    DESTINATION "${CONFIG_INSTALL_DIR}"
-    COMPONENT ${LIBASSIMP-DEV_COMPONENT}
-)
+IF(ASSIMP_INSTALL)
+  INSTALL(
+      FILES "${PROJECT_CONFIG}" "${VERSION_CONFIG}"
+      DESTINATION "${CONFIG_INSTALL_DIR}"
+      COMPONENT ${LIBASSIMP-DEV_COMPONENT}
+  )
 
 
-install(
-    EXPORT "${TARGETS_EXPORT_NAME}"
-    NAMESPACE "${NAMESPACE}"
-    DESTINATION "${CONFIG_INSTALL_DIR}"
-    COMPONENT ${LIBASSIMP-DEV_COMPONENT}
-)
+  INSTALL(
+      EXPORT "${TARGETS_EXPORT_NAME}"
+      NAMESPACE "${NAMESPACE}"
+      DESTINATION "${CONFIG_INSTALL_DIR}"
+      COMPONENT ${LIBASSIMP-DEV_COMPONENT}
+  )
+ENDIF()
 
 
 IF( ASSIMP_BUILD_DOCS )
 IF( ASSIMP_BUILD_DOCS )
   ADD_SUBDIRECTORY(doc)
   ADD_SUBDIRECTORY(doc)
@@ -476,12 +550,12 @@ IF(ASSIMP_HUNTER_ENABLED)
   find_package(ZLIB CONFIG REQUIRED)
   find_package(ZLIB CONFIG REQUIRED)
 
 
   add_definitions(-DASSIMP_BUILD_NO_OWN_ZLIB)
   add_definitions(-DASSIMP_BUILD_NO_OWN_ZLIB)
-  set(ZLIB_FOUND TRUE)
-  set(ZLIB_LIBRARIES ZLIB::zlib)
-  set(ASSIMP_BUILD_MINIZIP TRUE)
+  SET(ZLIB_FOUND TRUE)
+  SET(ZLIB_LIBRARIES ZLIB::zlib)
+  SET(ASSIMP_BUILD_MINIZIP TRUE)
 ELSE()
 ELSE()
   # If the zlib is already found outside, add an export in case assimpTargets can't find it.
   # If the zlib is already found outside, add an export in case assimpTargets can't find it.
-  IF( ZLIB_FOUND )
+  IF( ZLIB_FOUND AND ASSIMP_INSTALL)
     INSTALL( TARGETS zlib zlibstatic
     INSTALL( TARGETS zlib zlibstatic
         EXPORT "${TARGETS_EXPORT_NAME}")
         EXPORT "${TARGETS_EXPORT_NAME}")
   ENDIF()
   ENDIF()
@@ -505,8 +579,8 @@ ELSE()
     #		https://github.com/madler/zlib/issues/41#issuecomment-125848075
     #		https://github.com/madler/zlib/issues/41#issuecomment-125848075
     # Also prevents these options from "polluting" the cmake options if assimp is being
     # Also prevents these options from "polluting" the cmake options if assimp is being
     # included as a submodule.
     # included as a submodule.
-    set( ASM686 FALSE CACHE INTERNAL "Override ZLIB flag to turn off assembly" FORCE )
-    set( AMD64 FALSE CACHE INTERNAL "Override ZLIB flag to turn off assembly" FORCE )
+    SET(ASM686 FALSE CACHE INTERNAL "Override ZLIB flag to turn off assembly" FORCE )
+    SET(AMD64 FALSE CACHE INTERNAL "Override ZLIB flag to turn off assembly" FORCE )
 
 
     # compile from sources
     # compile from sources
     ADD_SUBDIRECTORY(contrib/zlib)
     ADD_SUBDIRECTORY(contrib/zlib)
@@ -529,7 +603,7 @@ IF( NOT IOS )
 ELSE ()
 ELSE ()
   IF( NOT BUILD_SHARED_LIBS )
   IF( NOT BUILD_SHARED_LIBS )
     IF( NOT ASSIMP_BUILD_MINIZIP )
     IF( NOT ASSIMP_BUILD_MINIZIP )
-      use_pkgconfig(UNZIP minizip)
+      USE_PKGCONFIG(UNZIP minizip)
     ENDIF()
     ENDIF()
   ENDIF ()
   ENDIF ()
 ENDIF ()
 ENDIF ()
@@ -563,9 +637,9 @@ SET ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER OFF CACHE BOOL
 )
 )
 
 
 IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
 IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
-  IF ( MSVC )
-    SET(C4D_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Cineware/includes")
+  SET(C4D_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Cineware/includes")
 
 
+  IF (WIN32)
     # pick the correct prebuilt library
     # pick the correct prebuilt library
     IF(MSVC143)
     IF(MSVC143)
       SET(C4D_LIB_POSTFIX "_2022")
       SET(C4D_LIB_POSTFIX "_2022")
@@ -583,7 +657,7 @@ IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
       SET(C4D_LIB_POSTFIX "_2010")
       SET(C4D_LIB_POSTFIX "_2010")
     ELSE()
     ELSE()
       MESSAGE( FATAL_ERROR
       MESSAGE( FATAL_ERROR
-        "C4D is currently only supported with MSVC 10, 11, 12, 14, 14.2, 14.3"
+        "C4D for Windows is currently only supported with MSVC 10, 11, 12, 14, 14.2, 14.3"
       )
       )
     ENDIF()
     ENDIF()
 
 
@@ -601,68 +675,90 @@ IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
     # winsock and winmm are necessary (and undocumented) dependencies of Cineware SDK because
     # winsock and winmm are necessary (and undocumented) dependencies of Cineware SDK because
     # it can be used to communicate with a running Cinema 4D instance
     # it can be used to communicate with a running Cinema 4D instance
     SET(C4D_EXTRA_LIBRARIES WSock32.lib Winmm.lib)
     SET(C4D_EXTRA_LIBRARIES WSock32.lib Winmm.lib)
-  ELSE ()
+  ELSEIF (APPLE)
+    SET(C4D_LIB_BASE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Cineware/libraries/osx")
+
+    SET(C4D_DEBUG_LIBRARIES
+      "${C4D_LIB_BASE_PATH}/debug/libcinewarelib.a"
+      "${C4D_LIB_BASE_PATH}/debug/libjpeglib.a"
+    )
+    SET(C4D_RELEASE_LIBRARIES
+      "${C4D_LIB_BASE_PATH}/release/libcinewarelib.a"
+      "${C4D_LIB_BASE_PATH}/release/libjpeglib.a"
+    )
+  ELSE()
     MESSAGE( FATAL_ERROR
     MESSAGE( FATAL_ERROR
-      "C4D is currently only available on Windows with Cineware SDK installed in contrib/Cineware"
+      "C4D is currently only available on Windows and macOS with Cineware SDK installed in contrib/Cineware"
     )
     )
-  ENDIF ()
+  ENDIF()
 ELSE ()
 ELSE ()
   ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER )
   ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER )
 ENDIF ()
 ENDIF ()
 
 
+IF(ASSIMP_BUILD_DRACO_STATIC)
+  SET(ASSIMP_BUILD_DRACO ON)
+ENDIF()
+
 # Draco requires cmake 3.12
 # Draco requires cmake 3.12
 IF (DEFINED CMAKE_VERSION AND "${CMAKE_VERSION}" VERSION_LESS "3.12")
 IF (DEFINED CMAKE_VERSION AND "${CMAKE_VERSION}" VERSION_LESS "3.12")
-  message(NOTICE "draco requires cmake 3.12 or newer, cmake is ${CMAKE_VERSION} . Draco is disabled")
+  MESSAGE(NOTICE "draco requires cmake 3.12 or newer, cmake is ${CMAKE_VERSION} . Draco is disabled")
   SET ( ASSIMP_BUILD_DRACO OFF CACHE BOOL "Disabled: Draco requires newer cmake" FORCE )
   SET ( ASSIMP_BUILD_DRACO OFF CACHE BOOL "Disabled: Draco requires newer cmake" FORCE )
 ELSE()
 ELSE()
   OPTION ( ASSIMP_BUILD_DRACO "If the Draco libraries are to be built. Primarily for glTF" OFF )
   OPTION ( ASSIMP_BUILD_DRACO "If the Draco libraries are to be built. Primarily for glTF" OFF )
   IF ( ASSIMP_BUILD_DRACO )
   IF ( ASSIMP_BUILD_DRACO )
     # Primarily for glTF v2
     # Primarily for glTF v2
     # Enable Draco glTF feature set
     # Enable Draco glTF feature set
-    set(DRACO_GLTF ON CACHE BOOL "" FORCE)
+    SET(DRACO_GLTF_BITSTREAM ON CACHE BOOL "" FORCE)
     # Disable unnecessary or omitted components
     # Disable unnecessary or omitted components
-    set(DRACO_JS_GLUE OFF CACHE BOOL "" FORCE)
-    set(DRACO_WASM OFF CACHE BOOL "" FORCE)
-    set(DRACO_MAYA_PLUGIN OFF CACHE BOOL "" FORCE)
-    set(DRACO_UNITY_PLUGIN OFF CACHE BOOL "" FORCE)
-    set(DRACO_TESTS OFF CACHE BOOL "" FORCE)
+    SET(DRACO_JS_GLUE OFF CACHE BOOL "" FORCE)
+    SET(DRACO_WASM OFF CACHE BOOL "" FORCE)
+    SET(DRACO_MAYA_PLUGIN OFF CACHE BOOL "" FORCE)
+    SET(DRACO_UNITY_PLUGIN OFF CACHE BOOL "" FORCE)
+    SET(DRACO_TESTS OFF CACHE BOOL "" FORCE)
 
 
     IF(ASSIMP_HUNTER_ENABLED)
     IF(ASSIMP_HUNTER_ENABLED)
       hunter_add_package(draco)
       hunter_add_package(draco)
       find_package(draco CONFIG REQUIRED)
       find_package(draco CONFIG REQUIRED)
-      set(draco_LIBRARIES draco::draco)
+      SET(draco_LIBRARIES draco::draco)
     ELSE()
     ELSE()
       # Draco 1.4.1 has many warnings and will not build with /WX or -Werror
       # Draco 1.4.1 has many warnings and will not build with /WX or -Werror
       # See https://github.com/google/draco/issues/672
       # See https://github.com/google/draco/issues/672
       # and https://github.com/google/draco/issues/673
       # and https://github.com/google/draco/issues/673
       IF(MSVC)
       IF(MSVC)
-        set(DRACO_CXX_FLAGS "/W0")
+        SET(DRACO_CXX_FLAGS "/W0")
       ELSE()
       ELSE()
-        list(APPEND DRACO_CXX_FLAGS
+        LIST(APPEND DRACO_CXX_FLAGS
           "-Wno-bool-compare"
           "-Wno-bool-compare"
           "-Wno-comment"
           "-Wno-comment"
           "-Wno-maybe-uninitialized"
           "-Wno-maybe-uninitialized"
           "-Wno-sign-compare"
           "-Wno-sign-compare"
           "-Wno-unused-local-typedefs"
           "-Wno-unused-local-typedefs"
         )
         )
-        # Draco 1.4.1 does not explicitly export any symbols under GCC/clang
-        list(APPEND DRACO_CXX_FLAGS
-          "-fvisibility=default"
-        )
+
+        IF(NOT ASSIMP_BUILD_DRACO_STATIC)
+          # Draco 1.4.1 does not explicitly export any symbols under GCC/clang
+          LIST(APPEND DRACO_CXX_FLAGS
+            "-fvisibility=default"
+          )
+        ENDIF()
       ENDIF()
       ENDIF()
 
 
       # Don't build or install all of Draco by default
       # Don't build or install all of Draco by default
       ADD_SUBDIRECTORY( "contrib/draco" EXCLUDE_FROM_ALL )
       ADD_SUBDIRECTORY( "contrib/draco" EXCLUDE_FROM_ALL )
 
 
-      if(MSVC OR WIN32)
-        set(draco_LIBRARIES "draco")
-      else()
-        if(BUILD_SHARED_LIBS)
-          set(draco_LIBRARIES "draco_shared")
-        else()
-          set(draco_LIBRARIES "draco_static")
-        endif()
-      endif()
+      IF(ASSIMP_BUILD_DRACO_STATIC)
+        set_property(DIRECTORY "contrib/draco" PROPERTY BUILD_SHARED_LIBS OFF)
+      ENDIF()
+
+      IF(MSVC OR WIN32)
+        SET(draco_LIBRARIES "draco")
+      ELSE()
+        IF(ASSIMP_BUILD_DRACO_STATIC)
+          SET(draco_LIBRARIES "draco_static")
+        ELSE()
+          SET(draco_LIBRARIES "draco_shared")
+        ENDIF()
+      ENDIF()
 
 
       # Don't build the draco command-line tools by default
       # Don't build the draco command-line tools by default
       set_target_properties(draco_encoder draco_decoder PROPERTIES
       set_target_properties(draco_encoder draco_decoder PROPERTIES
@@ -680,18 +776,20 @@ ELSE()
       TARGET_USE_COMMON_OUTPUT_DIRECTORY(draco_encoder)
       TARGET_USE_COMMON_OUTPUT_DIRECTORY(draco_encoder)
       TARGET_USE_COMMON_OUTPUT_DIRECTORY(draco_decoder)
       TARGET_USE_COMMON_OUTPUT_DIRECTORY(draco_decoder)
 
 
-      set(draco_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/contrib/draco/src")
+      SET(draco_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/contrib/draco/src")
 
 
       # This is probably wrong
       # This is probably wrong
-      INSTALL( TARGETS ${draco_LIBRARIES}
-        EXPORT "${TARGETS_EXPORT_NAME}"
-        LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
-        ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
-        RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR}
-        FRAMEWORK DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
-        COMPONENT ${LIBASSIMP_COMPONENT}
-        INCLUDES DESTINATION include
-      )
+      IF (ASSIMP_INSTALL)
+        INSTALL( TARGETS ${draco_LIBRARIES}
+          EXPORT "${TARGETS_EXPORT_NAME}"
+          LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+          ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+          RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR}
+          FRAMEWORK DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+          COMPONENT ${LIBASSIMP_COMPONENT}
+          INCLUDES DESTINATION include
+        )
+      ENDIF()
     ENDIF()
     ENDIF()
   ENDIF()
   ENDIF()
 ENDIF()
 ENDIF()
@@ -735,8 +833,8 @@ IF ( ASSIMP_INSTALL )
 ENDIF()
 ENDIF()
 
 
 CONFIGURE_FILE(
 CONFIGURE_FILE(
-  ${CMAKE_CURRENT_LIST_DIR}/revision.h.in
-  ${CMAKE_CURRENT_BINARY_DIR}/revision.h
+  ${CMAKE_CURRENT_LIST_DIR}/include/assimp/revision.h.in
+  ${CMAKE_CURRENT_BINARY_DIR}/include/assimp/revision.h
 )
 )
 
 
 CONFIGURE_FILE(
 CONFIGURE_FILE(
@@ -824,24 +922,24 @@ if(WIN32)
   IF(MSVC12 OR MSVC14 OR MSVC15 )
   IF(MSVC12 OR MSVC14 OR MSVC15 )
     ADD_CUSTOM_TARGET(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM)
     ADD_CUSTOM_TARGET(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM)
     IF(CMAKE_GENERATOR MATCHES "^Visual Studio")
     IF(CMAKE_GENERATOR MATCHES "^Visual Studio")
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.dll	${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.dll VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.exp	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.exp VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.lib	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.lib VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.dll		${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.dll  VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.exp		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.exp VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.lib		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.lib VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.dll	${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.dll VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.exp	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.exp VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.lib	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.lib VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.dll		${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.dll  VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.exp		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.exp VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.lib		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.lib VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
     ELSE()
     ELSE()
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mt.dll	${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.dll VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mt.exp	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.exp VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mt.lib	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.lib VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.dll		${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.dll  VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.exp		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.exp VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.lib		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.lib VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
-      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mt.dll	${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.dll VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mt.exp	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.exp VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mt.lib	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.lib VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.dll		${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.dll  VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.exp		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.exp VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.lib		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.lib VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
     ENDIF()
     ENDIF()
   ENDIF()
   ENDIF()
 ENDIF ()
 ENDIF ()

+ 2 - 2
CREDITS

@@ -60,7 +60,7 @@ The GUY who performed some of the CSM mocaps.
 Contributed fixes for the documentation and the doxygen markup
 Contributed fixes for the documentation and the doxygen markup
 
 
 - Zhao Lei
 - Zhao Lei
-Contributed several bugfixes fixing memory leaks and improving float parsing 
+Contributed several bugfixes fixing memory leaks and improving float parsing
 
 
 - sueastside
 - sueastside
 Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date.
 Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date.
@@ -129,7 +129,7 @@ Contributed a patch to fix the VertexTriangleAdjacency postprocessing step.
 Contributed the Debian build fixes ( architecture macro ).
 Contributed the Debian build fixes ( architecture macro ).
 
 
 - gellule
 - gellule
-Several LWO and LWS fixes (pivoting). 
+Several LWO and LWS fixes (pivoting).
 
 
 - Marcel Metz
 - Marcel Metz
 GCC/Linux fixes for the SimpleOpenGL sample.
 GCC/Linux fixes for the SimpleOpenGL sample.

+ 8 - 12
Dockerfile

@@ -1,21 +1,17 @@
-FROM ubuntu:22.04
+FROM gcc:1.5.1.0
 
 
-RUN apt-get update && apt-get install -y ninja-build \
-    git cmake build-essential software-properties-common
+RUN apt-get update \
+    apt-get install --no-install-recommends -y ninja-build cmake zlib1g-dev
 
 
-RUN add-apt-repository ppa:ubuntu-toolchain-r/test && apt-get update 
+WORKDIR /app
 
 
-WORKDIR /opt
+COPY . .
 
 
-# Build Assimp
-RUN git clone https://github.com/assimp/assimp.git /opt/assimp
-
-WORKDIR /opt/assimp
-
-RUN git checkout master \
-    && mkdir build && cd build && \
+RUN mkdir build && cd build && \
     cmake -G 'Ninja' \
     cmake -G 'Ninja' \
     -DCMAKE_BUILD_TYPE=Release \
     -DCMAKE_BUILD_TYPE=Release \
     -DASSIMP_BUILD_ASSIMP_TOOLS=ON \
     -DASSIMP_BUILD_ASSIMP_TOOLS=ON \
     .. && \
     .. && \
     ninja -j4 && ninja install
     ninja -j4 && ninja install
+
+CMD ["/app/build/bin/unit"]

+ 4 - 4
INSTALL

@@ -1,17 +1,17 @@
-	
+
 ========================================================================
 ========================================================================
-Open Asset Import Library (assimp) INSTALL 
+Open Asset Import Library (assimp) INSTALL
 ========================================================================
 ========================================================================
 
 
 ------------------------------
 ------------------------------
 Getting the documentation
 Getting the documentation
 ------------------------------
 ------------------------------
 
 
-A regularly-updated copy is available at 
+A regularly-updated copy is available at
 https://assimp-docs.readthedocs.io/en/latest/
 https://assimp-docs.readthedocs.io/en/latest/
 
 
 ------------------------------
 ------------------------------
-Building Assimp 
+Building Assimp
 ------------------------------
 ------------------------------
 
 
 Just check the build-instructions which you can find here: https://github.com/assimp/assimp/blob/master/Build.md
 Just check the build-instructions which you can find here: https://github.com/assimp/assimp/blob/master/Build.md

+ 25 - 24
Readme.md

@@ -1,57 +1,61 @@
 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) 
 ![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)
+[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=assimp_assimp&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=assimp_assimp)
 [![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")
+[![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20assimp%20Guru-006BFF)](https://gurubase.io/g/assimp)
+[![Financial Contributors on Open Collective](https://opencollective.com/assimp/all/badge.svg?label=financial+contributors)](https://opencollective.com/assimp)
 <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.
 
 
+## Project activity ##
+![Alt](https://repobeats.axiom.co/api/embed/997f84e5f9fcf772da1e687f3a4f3a8afdbf4cf0.svg "Repobeats analytics image")
+
 ### Documentation ###
 ### Documentation ###
-Please check the latest documents at [Asset-Importer-Lib-Doc](https://assimp-docs.readthedocs.io/en/latest/). 
+Read [our latest documentation](https://the-asset-importer-lib-documentation.readthedocs.io/en/latest/).
 
 
 ### Pre-built binaries ###
 ### Pre-built binaries ###
-Please check our [Itchi Projectspace](https://kimkulling.itch.io/the-asset-importer-lib)
+Download binaries from [our Itchi Projectspace](https://kimkulling.itch.io/the-asset-importer-lib).
 
 
-If you want to check our Model-Database, use the following repo: https://github.com/assimp/assimp-mdb
+### Test data ###
+Clone [our model database](https://github.com/assimp/assimp-mdb).
 
 
 ### Communities ###
 ### Communities ###
-- Ask a question at [The Assimp-Discussion Board](https://github.com/assimp/assimp/discussions)
-- Ask on [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)
-
-And we also have a Gitter-channel:Gitter [![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)<br>
+- Ask questions at [the Assimp Discussion Board](https://github.com/assimp/assimp/discussions).
+- 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)
 
 
 #### Supported file formats ####
 #### Supported file formats ####
-You can find the complete list of supported file-formats [here](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).
 
 
 ### Building ###
 ### Building ###
-Take a look [here](https://github.com/assimp/assimp/blob/master/Build.md) to get started. We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
+Start by reading [our build instructions](https://github.com/assimp/assimp/blob/master/Build.md). We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
 
 
 ### Ports ###
 ### Ports ###
 * [Android](port/AndroidJNI/README.md)
 * [Android](port/AndroidJNI/README.md)
 * [Python](port/PyAssimp/README.md)
 * [Python](port/PyAssimp/README.md)
-* [.NET](https://bitbucket.org/Starnick/assimpnet/src/master/)
+* [.NET](https://github.com/Saalvage/AssimpNetter)
 * [Pascal](port/AssimpPascal/Readme.md)
 * [Pascal](port/AssimpPascal/Readme.md)
 * [Javascript (Alpha)](https://github.com/makc/assimp2json)
 * [Javascript (Alpha)](https://github.com/makc/assimp2json)
 * [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)
 
 
 ### Other tools ###
 ### Other tools ###
-[open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.
+[Qt5-ModelViewer](https://github.com/sharjith/ModelViewer-Qt5) is a powerful viewer based on Qt5 and Assimp's import and export abilities.<br>
 [Assimp-Viewer](https://github.com/assimp/assimp_view) is an experimental implementation for an Asset-Viewer based on ImGUI and Assimp (experimental).
 [Assimp-Viewer](https://github.com/assimp/assimp_view) is an experimental implementation for an Asset-Viewer based on ImGUI and Assimp (experimental).
 
 
 #### Repository structure ####
 #### Repository structure ####
@@ -59,7 +63,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 +83,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 +105,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 +115,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 ...

+ 2 - 2
SECURITY.md

@@ -7,10 +7,10 @@ currently being supported with security updates.
 
 
 | Version | Supported          |
 | Version | Supported          |
 | ------- | ------------------ |
 | ------- | ------------------ |
-| 5.2.4   | :white_check_mark: |
+| 6.0.2   | :white_check_mark: |
 
 
 ## Reporting a Vulnerability
 ## Reporting a Vulnerability
 
 
-If you have found any security vulnerability you can contact us via 
+If you have found any security vulnerability you can contact us via
 [email protected]
 [email protected]
 
 

+ 2 - 3
cmake-modules/DebSourcePPA.cmake

@@ -6,7 +6,7 @@
 # Creates source debian files and manages library dependencies
 # Creates source debian files and manages library dependencies
 #
 #
 # Features:
 # Features:
-# 
+#
 # - Automatically generates symbols and run-time dependencies from the build dependencies
 # - Automatically generates symbols and run-time dependencies from the build dependencies
 # - Custom copy of source directory via CPACK_DEBIAN_PACKAGE_SOURCE_COPY
 # - Custom copy of source directory via CPACK_DEBIAN_PACKAGE_SOURCE_COPY
 # - Simultaneous output of multiple debian source packages for each distribution
 # - Simultaneous output of multiple debian source packages for each distribution
@@ -114,7 +114,6 @@ foreach(RELEASE ${CPACK_DEBIAN_DISTRIBUTION_RELEASES})
     endif( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER} )
     endif( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER} )
   endif( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} )
   endif( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} )
 
 
-    
   file(APPEND ${DEBIAN_CONTROL} "\n"
   file(APPEND ${DEBIAN_CONTROL} "\n"
     "Standards-Version: 3.8.4\n"
     "Standards-Version: 3.8.4\n"
     "Homepage: ${CPACK_PACKAGE_VENDOR}\n"
     "Homepage: ${CPACK_PACKAGE_VENDOR}\n"
@@ -173,7 +172,7 @@ foreach(RELEASE ${CPACK_DEBIAN_DISTRIBUTION_RELEASES})
       endforeach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS})
       endforeach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS})
     endif( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER} )
     endif( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER} )
   endif( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} )
   endif( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} )
-  
+
   file(APPEND ${DEBIAN_CONTROL} "\n"
   file(APPEND ${DEBIAN_CONTROL} "\n"
     "Description: ${CPACK_PACKAGE_DISPLAY_NAME} ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}\n"
     "Description: ${CPACK_PACKAGE_DISPLAY_NAME} ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}\n"
     "${DEB_LONG_DESCRIPTION}"
     "${DEB_LONG_DESCRIPTION}"

+ 5 - 5
cmake-modules/FindDirectX.cmake

@@ -26,7 +26,7 @@ if(WIN32) # The only platform it makes sense to check for DirectX SDK
   getenv_path(DIRECTX_BASE)
   getenv_path(DIRECTX_BASE)
 
 
   # construct search paths
   # construct search paths
-  set(DirectX_PREFIX_PATH 
+  set(DirectX_PREFIX_PATH
     "${DXSDK_DIR}" "${ENV_DXSDK_DIR}"
     "${DXSDK_DIR}" "${ENV_DXSDK_DIR}"
     "${DIRECTX_HOME}" "${ENV_DIRECTX_HOME}"
     "${DIRECTX_HOME}" "${ENV_DIRECTX_HOME}"
     "${DIRECTX_ROOT}" "${ENV_DIRECTX_ROOT}"
     "${DIRECTX_ROOT}" "${ENV_DIRECTX_ROOT}"
@@ -66,7 +66,7 @@ if(WIN32) # The only platform it makes sense to check for DirectX SDK
   find_library(DirectX_D3DCOMPILER_LIBRARY NAMES d3dcompiler HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX})
   find_library(DirectX_D3DCOMPILER_LIBRARY NAMES d3dcompiler HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX})
 
 
   findpkg_finish(DirectX)
   findpkg_finish(DirectX)
-  set(DirectX_LIBRARIES ${DirectX_LIBRARIES} 
+  set(DirectX_LIBRARIES ${DirectX_LIBRARIES}
     ${DirectX_D3DX9_LIBRARY}
     ${DirectX_D3DX9_LIBRARY}
     ${DirectX_DXERR_LIBRARY}
     ${DirectX_DXERR_LIBRARY}
     ${DirectX_DXGUID_LIBRARY}
     ${DirectX_DXGUID_LIBRARY}
@@ -82,7 +82,7 @@ if(WIN32) # The only platform it makes sense to check for DirectX SDK
       get_filename_component(DirectX_LIBRARY_DIR "${DirectX_LIBRARY}" PATH)
       get_filename_component(DirectX_LIBRARY_DIR "${DirectX_LIBRARY}" PATH)
       message(STATUS "DX lib dir: ${DirectX_LIBRARY_DIR}")
       message(STATUS "DX lib dir: ${DirectX_LIBRARY_DIR}")
     find_library(DirectX_D3D11_LIBRARY NAMES d3d11 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX})
     find_library(DirectX_D3D11_LIBRARY NAMES d3d11 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX})
-    find_library(DirectX_D3DX11_LIBRARY NAMES d3dx11 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX})	
+    find_library(DirectX_D3DX11_LIBRARY NAMES d3dx11 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX})
     if (DirectX_D3D11_INCLUDE_DIR AND DirectX_D3D11_LIBRARY)
     if (DirectX_D3D11_INCLUDE_DIR AND DirectX_D3D11_LIBRARY)
       set(DirectX_D3D11_FOUND TRUE)
       set(DirectX_D3D11_FOUND TRUE)
       set(DirectX_D3D11_INCLUDE_DIR ${DirectX_D3D11_INCLUDE_DIR})
       set(DirectX_D3D11_INCLUDE_DIR ${DirectX_D3D11_INCLUDE_DIR})
@@ -92,8 +92,8 @@ if(WIN32) # The only platform it makes sense to check for DirectX SDK
 	${DirectX_DXGI_LIBRARY}
 	${DirectX_DXGI_LIBRARY}
 	${DirectX_DXERR_LIBRARY}
 	${DirectX_DXERR_LIBRARY}
 	${DirectX_DXGUID_LIBRARY}
 	${DirectX_DXGUID_LIBRARY}
-	${DirectX_D3DCOMPILER_LIBRARY}        	  
-      )	
+	${DirectX_D3DCOMPILER_LIBRARY}
+      )
     endif ()
     endif ()
     mark_as_advanced(DirectX_D3D11_INCLUDE_DIR DirectX_D3D11_LIBRARY DirectX_D3DX11_LIBRARY)
     mark_as_advanced(DirectX_D3D11_INCLUDE_DIR DirectX_D3D11_LIBRARY DirectX_D3DX11_LIBRARY)
   endif ()
   endif ()

+ 40 - 12
cmake-modules/Findassimp.cmake

@@ -3,7 +3,7 @@ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
 elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
 elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
 	set(ASSIMP_ARCHITECTURE "32")
 	set(ASSIMP_ARCHITECTURE "32")
 endif(CMAKE_SIZEOF_VOID_P EQUAL 8)
 endif(CMAKE_SIZEOF_VOID_P EQUAL 8)
-	
+
 if(WIN32)
 if(WIN32)
 	set(ASSIMP_ROOT_DIR CACHE PATH "ASSIMP root directory")
 	set(ASSIMP_ROOT_DIR CACHE PATH "ASSIMP root directory")
 
 
@@ -17,29 +17,29 @@ if(WIN32)
 
 
 	if(MSVC12)
 	if(MSVC12)
 		set(ASSIMP_MSVC_VERSION "vc120")
 		set(ASSIMP_MSVC_VERSION "vc120")
-	elseif(MSVC14)	
+	elseif(MSVC14)
 		set(ASSIMP_MSVC_VERSION "vc140")
 		set(ASSIMP_MSVC_VERSION "vc140")
 	endif(MSVC12)
 	endif(MSVC12)
-	
+
 	if(MSVC12 OR MSVC14)
 	if(MSVC12 OR MSVC14)
-	
+
 		find_path(ASSIMP_LIBRARY_DIR
 		find_path(ASSIMP_LIBRARY_DIR
 			NAMES
 			NAMES
 				assimp-${ASSIMP_MSVC_VERSION}-mt.lib
 				assimp-${ASSIMP_MSVC_VERSION}-mt.lib
 			HINTS
 			HINTS
 				${ASSIMP_ROOT_DIR}/lib${ASSIMP_ARCHITECTURE}
 				${ASSIMP_ROOT_DIR}/lib${ASSIMP_ARCHITECTURE}
 		)
 		)
-		
+
 		find_library(ASSIMP_LIBRARY_RELEASE				assimp-${ASSIMP_MSVC_VERSION}-mt.lib 			PATHS ${ASSIMP_LIBRARY_DIR})
 		find_library(ASSIMP_LIBRARY_RELEASE				assimp-${ASSIMP_MSVC_VERSION}-mt.lib 			PATHS ${ASSIMP_LIBRARY_DIR})
 		find_library(ASSIMP_LIBRARY_DEBUG				assimp-${ASSIMP_MSVC_VERSION}-mtd.lib			PATHS ${ASSIMP_LIBRARY_DIR})
 		find_library(ASSIMP_LIBRARY_DEBUG				assimp-${ASSIMP_MSVC_VERSION}-mtd.lib			PATHS ${ASSIMP_LIBRARY_DIR})
-		
-		set(ASSIMP_LIBRARY 
+
+		set(ASSIMP_LIBRARY
 			optimized 	${ASSIMP_LIBRARY_RELEASE}
 			optimized 	${ASSIMP_LIBRARY_RELEASE}
 			debug		${ASSIMP_LIBRARY_DEBUG}
 			debug		${ASSIMP_LIBRARY_DEBUG}
 		)
 		)
-		
+
 		set(ASSIMP_LIBRARIES "ASSIMP_LIBRARY_RELEASE" "ASSIMP_LIBRARY_DEBUG")
 		set(ASSIMP_LIBRARIES "ASSIMP_LIBRARY_RELEASE" "ASSIMP_LIBRARY_DEBUG")
-	
+
 		FUNCTION(ASSIMP_COPY_BINARIES TargetDirectory)
 		FUNCTION(ASSIMP_COPY_BINARIES TargetDirectory)
 			ADD_CUSTOM_TARGET(AssimpCopyBinaries
 			ADD_CUSTOM_TARGET(AssimpCopyBinaries
 				COMMAND ${CMAKE_COMMAND} -E copy ${ASSIMP_ROOT_DIR}/bin${ASSIMP_ARCHITECTURE}/assimp-${ASSIMP_MSVC_VERSION}-mtd.dll 	${TargetDirectory}/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.dll
 				COMMAND ${CMAKE_COMMAND} -E copy ${ASSIMP_ROOT_DIR}/bin${ASSIMP_ARCHITECTURE}/assimp-${ASSIMP_MSVC_VERSION}-mtd.dll 	${TargetDirectory}/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.dll
@@ -47,9 +47,37 @@ if(WIN32)
 			COMMENT "Copying Assimp binaries to '${TargetDirectory}'"
 			COMMENT "Copying Assimp binaries to '${TargetDirectory}'"
 			VERBATIM)
 			VERBATIM)
 		ENDFUNCTION(ASSIMP_COPY_BINARIES)
 		ENDFUNCTION(ASSIMP_COPY_BINARIES)
-	
+
+		if (NOT TARGET ASSIMP)
+			set(INCLUDE_DIRS ${ASSIMP_ROOT_DIR}/include)
+
+			find_library(ASSIMP_LIB_DEBUG
+				NAMES assimp-${ASSIMP_MSVC_VERSION}-mtd.lib
+				PATHS ${ASSIMP_LIBRARY_DIR})
+
+			find_file(ASSIMP_DLL_DEBUG
+				NAMES assimp-${ASSIMP_MSVC_VERSION}-mtd.dll
+				PATHS ${ASSIMP_ROOT_DIR}/bin${ASSIMP_ARCHITECTURE})
+
+			find_library(ASSIMP_LIB_RELEASE
+				NAMES assimp-${ASSIMP_MSVC_VERSION}-mt.lib
+				PATHS ${ASSIMP_LIBRARY_DIR})
+
+			find_file(ASSIMP_DLL_RELEASE
+				NAMES assimp-${ASSIMP_MSVC_VERSION}-mt.dll
+				PATHS ${ASSIMP_ROOT_DIR}/bin${ASSIMP_ARCHITECTURE})
+
+			add_library(ASSIMP SHARED IMPORTED)
+			set_target_properties(ASSIMP PROPERTIES
+				INTERFACE_INCLUDE_DIRECTORIES "${INCLUDE_DIRS}"
+				IMPORTED_IMPLIB_DEBUG ${ASSIMP_LIB_DEBUG}
+				IMPORTED_IMPLIB_RELEASE ${ASSIMP_LIB_RELEASE}
+				IMPORTED_LOCATION_DEBUG ${ASSIMP_DLL_DEBUG}
+				IMPORTED_LOCATION_RELEASE ${ASSIMP_DLL_RELEASE}
+			)
+		endif()
 	endif()
 	endif()
-	
+
 else(WIN32)
 else(WIN32)
 
 
 	find_path(
 	find_path(
@@ -81,5 +109,5 @@ else(WIN32)
 		message(FATAL_ERROR "Could not find asset importer library")
 		message(FATAL_ERROR "Could not find asset importer library")
 	  endif (assimp_FIND_REQUIRED)
 	  endif (assimp_FIND_REQUIRED)
 	endif (assimp_FOUND)
 	endif (assimp_FOUND)
-	
+
 endif(WIN32)
 endif(WIN32)

+ 1 - 30
cmake-modules/HunterGate.cmake

@@ -22,45 +22,16 @@
 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # 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.
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
-# This is a gate file to Hunter package manager.
-# Include this file using `include` command and add package you need, example:
-#
-#     cmake_minimum_required(VERSION 3.2)
-#
-#     include("cmake/HunterGate.cmake")
-#     HunterGate(
-#         URL "https://github.com/path/to/hunter/archive.tar.gz"
-#         SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d"
-#     )
-#
-#     project(MyProject)
-#
-#     hunter_add_package(Foo)
-#     hunter_add_package(Boo COMPONENTS Bar Baz)
-#
-# Projects:
-#     * https://github.com/hunter-packages/gate/
-#     * https://github.com/ruslo/hunter
 
 
 option(HUNTER_ENABLED "Enable Hunter package manager support" ON)
 option(HUNTER_ENABLED "Enable Hunter package manager support" ON)
 
 
-if(HUNTER_ENABLED)
-  if(CMAKE_VERSION VERSION_LESS "3.2")
-    message(
-        FATAL_ERROR
-        "At least CMake version 3.2 required for Hunter dependency management."
-        " Update CMake or set HUNTER_ENABLED to OFF."
-    )
-  endif()
-endif()
-
 include(CMakeParseArguments) # cmake_parse_arguments
 include(CMakeParseArguments) # cmake_parse_arguments
 
 
 option(HUNTER_STATUS_PRINT "Print working status" ON)
 option(HUNTER_STATUS_PRINT "Print working status" ON)
 option(HUNTER_STATUS_DEBUG "Print a lot info" OFF)
 option(HUNTER_STATUS_DEBUG "Print a lot info" OFF)
 option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON)
 option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON)
 
 
-set(HUNTER_ERROR_PAGE "https://docs.hunter.sh/en/latest/reference/errors")
+set(HUNTER_ERROR_PAGE "https://hunter.readthedocs.io/en/latest/reference/errors")
 
 
 function(hunter_gate_status_print)
 function(hunter_gate_status_print)
   if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG)
   if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG)

+ 3 - 3
cmake-modules/PrecompiledHeader.cmake

@@ -9,14 +9,14 @@ MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecompiledHeader PrecompiledSource SourcesVar
       OBJECT_OUTPUTS "${PrecompiledBinary}")
       OBJECT_OUTPUTS "${PrecompiledBinary}")
 
 
     # Do not consider .c files
     # Do not consider .c files
-    foreach(fname ${Sources}) 
+    foreach(fname ${Sources})
       GET_FILENAME_COMPONENT(fext ${fname} EXT)
       GET_FILENAME_COMPONENT(fext ${fname} EXT)
       if(fext STREQUAL ".cpp")
       if(fext STREQUAL ".cpp")
 	SET_SOURCE_FILES_PROPERTIES(${fname}
 	SET_SOURCE_FILES_PROPERTIES(${fname}
 	  PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledBinary}\" /FI\"${PrecompiledBinary}\" /Fp\"${PrecompiledBinary}\""
 	  PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledBinary}\" /FI\"${PrecompiledBinary}\" /Fp\"${PrecompiledBinary}\""
-          OBJECT_DEPENDS "${PrecompiledBinary}")     
+          OBJECT_DEPENDS "${PrecompiledBinary}")
       endif(fext STREQUAL ".cpp")
       endif(fext STREQUAL ".cpp")
-    endforeach(fname) 
+    endforeach(fname)
 
 
   ENDIF(MSVC)
   ENDIF(MSVC)
   # Add precompiled header to SourcesVar
   # Add precompiled header to SourcesVar

+ 0 - 8
code/.editorconfig

@@ -1,8 +0,0 @@
-# See <http://EditorConfig.org> for details
-
-[*.{h,hpp,c,cpp}]
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
-indent_size = 4
-indent_style = space

+ 42 - 43
code/AssetLib/3DS/3DSConverter.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -91,15 +91,12 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() {
     // now iterate through all meshes and through all faces and
     // now iterate through all meshes and through all faces and
     // find all faces that are using the default material
     // find all faces that are using the default material
     unsigned int cnt = 0;
     unsigned int cnt = 0;
-    for (std::vector<D3DS::Mesh>::iterator
-                    i = mScene->mMeshes.begin();
-            i != mScene->mMeshes.end(); ++i) {
-        for (std::vector<unsigned int>::iterator
-                        a = (*i).mFaceMaterials.begin();
-                a != (*i).mFaceMaterials.end(); ++a) {
+    for (auto i = mScene->mMeshes.begin(); i != mScene->mMeshes.end(); ++i) {
+        for (auto a = (*i).mFaceMaterials.begin(); a != (*i).mFaceMaterials.end(); ++a) {
             // NOTE: The additional check seems to be necessary,
             // NOTE: The additional check seems to be necessary,
             // some exporters seem to generate invalid data here
             // some exporters seem to generate invalid data here
-            if (0xcdcdcdcd == (*a)) {
+
+            if (NotSet == (*a)) {
                 (*a) = idx;
                 (*a) = idx;
                 ++cnt;
                 ++cnt;
             } else if ((*a) >= mScene->mMaterials.size()) {
             } else if ((*a) >= mScene->mMaterials.size()) {
@@ -111,7 +108,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() {
     }
     }
     if (cnt && idx == mScene->mMaterials.size()) {
     if (cnt && idx == mScene->mMaterials.size()) {
         // We need to create our own default material
         // We need to create our own default material
-        D3DS::Material sMat("%%%DEFAULT");
+        Material sMat("%%%DEFAULT");
         sMat.mDiffuse = aiColor3D(0.3f, 0.3f, 0.3f);
         sMat.mDiffuse = aiColor3D(0.3f, 0.3f, 0.3f);
         mScene->mMaterials.push_back(sMat);
         mScene->mMaterials.push_back(sMat);
 
 
@@ -121,17 +118,17 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Check whether all indices are valid. Otherwise we'd crash before the validation step is reached
 // Check whether all indices are valid. Otherwise we'd crash before the validation step is reached
-void Discreet3DSImporter::CheckIndices(D3DS::Mesh &sMesh) {
-    for (std::vector<D3DS::Face>::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end(); ++i) {
+void Discreet3DSImporter::CheckIndices(Mesh &sMesh) {
+    for (auto i = sMesh.mFaces.begin(); i != sMesh.mFaces.end(); ++i) {
         // check whether all indices are in range
         // check whether all indices are in range
         for (unsigned int a = 0; a < 3; ++a) {
         for (unsigned int a = 0; a < 3; ++a) {
             if ((*i).mIndices[a] >= sMesh.mPositions.size()) {
             if ((*i).mIndices[a] >= sMesh.mPositions.size()) {
                 ASSIMP_LOG_WARN("3DS: Vertex index overflow)");
                 ASSIMP_LOG_WARN("3DS: Vertex index overflow)");
-                (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size() - 1;
+                (*i).mIndices[a] = static_cast<uint32_t>(sMesh.mPositions.size() - 1);
             }
             }
             if (!sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size()) {
             if (!sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size()) {
                 ASSIMP_LOG_WARN("3DS: Texture coordinate index overflow)");
                 ASSIMP_LOG_WARN("3DS: Texture coordinate index overflow)");
-                (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size() - 1;
+                (*i).mIndices[a] = static_cast<uint32_t>(sMesh.mTexCoords.size() - 1);
             }
             }
         }
         }
     }
     }
@@ -139,7 +136,7 @@ void Discreet3DSImporter::CheckIndices(D3DS::Mesh &sMesh) {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Generate out unique verbose format representation
 // Generate out unique verbose format representation
-void Discreet3DSImporter::MakeUnique(D3DS::Mesh &sMesh) {
+void Discreet3DSImporter::MakeUnique(Mesh &sMesh) {
     // TODO: really necessary? I don't think. Just a waste of memory and time
     // TODO: really necessary? I don't think. Just a waste of memory and time
     // to do it now in a separate buffer.
     // to do it now in a separate buffer.
 
 
@@ -150,7 +147,7 @@ void Discreet3DSImporter::MakeUnique(D3DS::Mesh &sMesh) {
         vNew2.resize(sMesh.mFaces.size() * 3);
         vNew2.resize(sMesh.mFaces.size() * 3);
 
 
     for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size(); ++i) {
     for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size(); ++i) {
-        D3DS::Face &face = sMesh.mFaces[i];
+        Face &face = sMesh.mFaces[i];
 
 
         // Positions
         // Positions
         for (unsigned int a = 0; a < 3; ++a, ++base) {
         for (unsigned int a = 0; a < 3; ++a, ++base) {
@@ -167,10 +164,9 @@ void Discreet3DSImporter::MakeUnique(D3DS::Mesh &sMesh) {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Convert a 3DS texture to texture keys in an aiMaterial
 // Convert a 3DS texture to texture keys in an aiMaterial
-void CopyTexture(aiMaterial &mat, D3DS::Texture &texture, aiTextureType type) {
+void CopyTexture(aiMaterial &mat, Texture &texture, aiTextureType type) {
     // Setup the texture name
     // Setup the texture name
-    aiString tex;
-    tex.Set(texture.mMapName);
+    aiString tex(texture.mMapName);
     mat.AddProperty(&tex, AI_MATKEY_TEXTURE(type, 0));
     mat.AddProperty(&tex, AI_MATKEY_TEXTURE(type, 0));
 
 
     // Setup the texture blend factor
     // Setup the texture blend factor
@@ -197,13 +193,11 @@ void CopyTexture(aiMaterial &mat, D3DS::Texture &texture, aiTextureType type) {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Convert a 3DS material to an aiMaterial
 // Convert a 3DS material to an aiMaterial
-void Discreet3DSImporter::ConvertMaterial(D3DS::Material &oldMat,
-        aiMaterial &mat) {
+void Discreet3DSImporter::ConvertMaterial(Material &oldMat, aiMaterial &mat) {
     // NOTE: Pass the background image to the viewer by bypassing the
     // NOTE: Pass the background image to the viewer by bypassing the
     // material system. This is an evil hack, never do it again!
     // material system. This is an evil hack, never do it again!
-    if (0 != mBackgroundImage.length() && bHasBG) {
-        aiString tex;
-        tex.Set(mBackgroundImage);
+    if (mBackgroundImage.empty() && bHasBG) {
+        aiString tex(mBackgroundImage);
         mat.AddProperty(&tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
         mat.AddProperty(&tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
 
 
         // Be sure this is only done for the first material
         // Be sure this is only done for the first material
@@ -215,8 +209,7 @@ void Discreet3DSImporter::ConvertMaterial(D3DS::Material &oldMat,
     oldMat.mAmbient.g += mClrAmbient.g;
     oldMat.mAmbient.g += mClrAmbient.g;
     oldMat.mAmbient.b += mClrAmbient.b;
     oldMat.mAmbient.b += mClrAmbient.b;
 
 
-    aiString name;
-    name.Set(oldMat.mName);
+    aiString name(oldMat.mName);
     mat.AddProperty(&name, AI_MATKEY_NAME);
     mat.AddProperty(&name, AI_MATKEY_NAME);
 
 
     // Material colors
     // Material colors
@@ -226,10 +219,9 @@ void Discreet3DSImporter::ConvertMaterial(D3DS::Material &oldMat,
     mat.AddProperty(&oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
     mat.AddProperty(&oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
 
 
     // Phong shininess and shininess strength
     // Phong shininess and shininess strength
-    if (D3DS::Discreet3DS::Phong == oldMat.mShading ||
-            D3DS::Discreet3DS::Metal == oldMat.mShading) {
+    if (Discreet3DS::Phong == oldMat.mShading || Discreet3DS::Metal == oldMat.mShading) {
         if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength) {
         if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength) {
-            oldMat.mShading = D3DS::Discreet3DS::Gouraud;
+            oldMat.mShading = Discreet3DS::Gouraud;
         } else {
         } else {
             mat.AddProperty(&oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
             mat.AddProperty(&oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
             mat.AddProperty(&oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
             mat.AddProperty(&oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
@@ -251,40 +243,41 @@ void Discreet3DSImporter::ConvertMaterial(D3DS::Material &oldMat,
     // Shading mode
     // Shading mode
     aiShadingMode eShading = aiShadingMode_NoShading;
     aiShadingMode eShading = aiShadingMode_NoShading;
     switch (oldMat.mShading) {
     switch (oldMat.mShading) {
-    case D3DS::Discreet3DS::Flat:
+    case Discreet3DS::Flat:
         eShading = aiShadingMode_Flat;
         eShading = aiShadingMode_Flat;
         break;
         break;
 
 
     // I don't know what "Wire" shading should be,
     // I don't know what "Wire" shading should be,
     // assume it is simple lambertian diffuse shading
     // assume it is simple lambertian diffuse shading
-    case D3DS::Discreet3DS::Wire: {
+    case Discreet3DS::Wire: {
         // Set the wireframe flag
         // Set the wireframe flag
         unsigned int iWire = 1;
         unsigned int iWire = 1;
         mat.AddProperty<int>((int *)&iWire, 1, AI_MATKEY_ENABLE_WIREFRAME);
         mat.AddProperty<int>((int *)&iWire, 1, AI_MATKEY_ENABLE_WIREFRAME);
     }
     }
         [[fallthrough]];
         [[fallthrough]];
 
 
-    case D3DS::Discreet3DS::Gouraud:
+    case Discreet3DS::Gouraud:
         eShading = aiShadingMode_Gouraud;
         eShading = aiShadingMode_Gouraud;
         break;
         break;
 
 
     // assume cook-torrance shading for metals.
     // assume cook-torrance shading for metals.
-    case D3DS::Discreet3DS::Phong:
+    case Discreet3DS::Phong:
         eShading = aiShadingMode_Phong;
         eShading = aiShadingMode_Phong;
         break;
         break;
 
 
-    case D3DS::Discreet3DS::Metal:
+    case Discreet3DS::Metal:
         eShading = aiShadingMode_CookTorrance;
         eShading = aiShadingMode_CookTorrance;
         break;
         break;
 
 
         // FIX to workaround a warning with GCC 4 who complained
         // FIX to workaround a warning with GCC 4 who complained
         // about a missing case Blinn: here - Blinn isn't a valid
         // about a missing case Blinn: here - Blinn isn't a valid
         // value in the 3DS Loader, it is just needed for ASE
         // value in the 3DS Loader, it is just needed for ASE
-    case D3DS::Discreet3DS::Blinn:
+    case Discreet3DS::Blinn:
         eShading = aiShadingMode_Blinn;
         eShading = aiShadingMode_Blinn;
         break;
         break;
     }
     }
-    int eShading_ = static_cast<int>(eShading);
+
+    const int eShading_ = eShading;
     mat.AddProperty<int>(&eShading_, 1, AI_MATKEY_SHADING_MODEL);
     mat.AddProperty<int>(&eShading_, 1, AI_MATKEY_SHADING_MODEL);
 
 
     // DIFFUSE texture
     // DIFFUSE texture
@@ -643,11 +636,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;
@@ -709,7 +708,7 @@ void Discreet3DSImporter::GenerateNodeGraph(aiScene *pcOut) {
             pcNode->mNumMeshes = 1;
             pcNode->mNumMeshes = 1;
 
 
             // Build a name for the node
             // Build a name for the node
-            pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "3DSMesh_%u", i);
+            pcNode->mName.length = ai_snprintf(pcNode->mName.data, AI_MAXLEN, "3DSMesh_%u", i);
         }
         }
 
 
         // Build dummy nodes for all cameras
         // Build dummy nodes for all cameras
@@ -777,7 +776,7 @@ void Discreet3DSImporter::GenerateNodeGraph(aiScene *pcOut) {
 // Convert all meshes in the scene and generate the final output scene.
 // Convert all meshes in the scene and generate the final output scene.
 void Discreet3DSImporter::ConvertScene(aiScene *pcOut) {
 void Discreet3DSImporter::ConvertScene(aiScene *pcOut) {
     // Allocate enough storage for all output materials
     // Allocate enough storage for all output materials
-    pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size();
+    pcOut->mNumMaterials = static_cast<unsigned int>(mScene->mMaterials.size());
     pcOut->mMaterials = new aiMaterial *[pcOut->mNumMaterials];
     pcOut->mMaterials = new aiMaterial *[pcOut->mNumMaterials];
 
 
     //  ... and convert the 3DS materials to aiMaterial's
     //  ... and convert the 3DS materials to aiMaterial's
@@ -791,17 +790,17 @@ void Discreet3DSImporter::ConvertScene(aiScene *pcOut) {
     ConvertMeshes(pcOut);
     ConvertMeshes(pcOut);
 
 
     // Now copy all light sources to the output scene
     // Now copy all light sources to the output scene
-    pcOut->mNumLights = (unsigned int)mScene->mLights.size();
+    pcOut->mNumLights = static_cast<unsigned int>(mScene->mLights.size());
     if (pcOut->mNumLights) {
     if (pcOut->mNumLights) {
         pcOut->mLights = new aiLight *[pcOut->mNumLights];
         pcOut->mLights = new aiLight *[pcOut->mNumLights];
-        ::memcpy(pcOut->mLights, &mScene->mLights[0], sizeof(void *) * pcOut->mNumLights);
+        memcpy(pcOut->mLights, &mScene->mLights[0], sizeof(void *) * pcOut->mNumLights);
     }
     }
 
 
     // Now copy all cameras to the output scene
     // Now copy all cameras to the output scene
-    pcOut->mNumCameras = (unsigned int)mScene->mCameras.size();
+    pcOut->mNumCameras = static_cast<unsigned int>(mScene->mCameras.size());
     if (pcOut->mNumCameras) {
     if (pcOut->mNumCameras) {
         pcOut->mCameras = new aiCamera *[pcOut->mNumCameras];
         pcOut->mCameras = new aiCamera *[pcOut->mNumCameras];
-        ::memcpy(pcOut->mCameras, &mScene->mCameras[0], sizeof(void *) * pcOut->mNumCameras);
+        memcpy(pcOut->mCameras, &mScene->mCameras[0], sizeof(void *) * pcOut->mNumCameras);
     }
     }
 }
 }
 
 

+ 3 - 6
code/AssetLib/3DS/3DSExporter.cpp

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -52,6 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/StringComparison.h>
 #include <assimp/StringComparison.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/Exporter.hpp>
 #include <assimp/Exporter.hpp>
+#include <assimp/Exceptional.h>
 #include <assimp/IOSystem.hpp>
 #include <assimp/IOSystem.hpp>
 
 
 #include <memory>
 #include <memory>
@@ -102,7 +102,7 @@ private:
 // preserves the mesh's given name if it has one. |index| is the index
 // preserves the mesh's given name if it has one. |index| is the index
 // of the mesh in |aiScene::mMeshes|.
 // of the mesh in |aiScene::mMeshes|.
 std::string GetMeshName(const aiMesh &mesh, unsigned int index, const aiNode &node) {
 std::string GetMeshName(const aiMesh &mesh, unsigned int index, const aiNode &node) {
-    static const char underscore = '_';
+    static constexpr char underscore = '_';
     char postfix[10] = { 0 };
     char postfix[10] = { 0 };
     ASSIMP_itoa10(postfix, index);
     ASSIMP_itoa10(postfix, index);
 
 
@@ -208,9 +208,6 @@ Discreet3DSExporter::Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, con
     }
     }
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-Discreet3DSExporter::~Discreet3DSExporter() = default;
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 int Discreet3DSExporter::WriteHierarchy(const aiNode &node, int seq, int sibling_level) {
 int Discreet3DSExporter::WriteHierarchy(const aiNode &node, int seq, int sibling_level) {
     // 3DS scene hierarchy is serialized as in http://www.martinreddy.net/gfx/3d/3DS.spec
     // 3DS scene hierarchy is serialized as in http://www.martinreddy.net/gfx/3d/3DS.spec

+ 3 - 4
code/AssetLib/3DS/3DSExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -63,10 +63,10 @@ namespace Assimp {
  *  @brief  Helper class to export a given scene to a 3DS file.
  *  @brief  Helper class to export a given scene to a 3DS file.
  */
  */
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-class Discreet3DSExporter {
+class Discreet3DSExporter final {
 public:
 public:
     Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene* pScene);
     Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene* pScene);
-    ~Discreet3DSExporter();
+    ~Discreet3DSExporter() = default;
 
 
 private:
 private:
     void WriteMeshes();
     void WriteMeshes();
@@ -88,7 +88,6 @@ private:
 
 
     using MeshesByNodeMap = std::multimap<const aiNode*, unsigned int>;
     using MeshesByNodeMap = std::multimap<const aiNode*, unsigned int>;
     MeshesByNodeMap meshes;
     MeshesByNodeMap meshes;
-
 };
 };
 
 
 } // Namespace Assimp
 } // Namespace Assimp

+ 3 - 5
code/AssetLib/3DS/3DSHelper.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -365,14 +364,13 @@ struct Texture {
 #ifdef _MSC_VER
 #ifdef _MSC_VER
 #pragma warning(pop)
 #pragma warning(pop)
 #endif // _MSC_VER
 #endif // _MSC_VER
-
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Helper structure representing a 3ds material */
 /** Helper structure representing a 3ds material */
 struct Material {
 struct Material {
     //! Default constructor has been deleted
     //! Default constructor has been deleted
     Material() :
     Material() :
             mName(),
             mName(),
-            mDiffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)),
+            mDiffuse(0.6f, 0.6f, 0.6f),
             mSpecularExponent(ai_real(0.0)),
             mSpecularExponent(ai_real(0.0)),
             mShininessStrength(ai_real(1.0)),
             mShininessStrength(ai_real(1.0)),
             mShading(Discreet3DS::Gouraud),
             mShading(Discreet3DS::Gouraud),
@@ -385,7 +383,7 @@ struct Material {
     //! Constructor with explicit name
     //! Constructor with explicit name
     explicit Material(const std::string &name) :
     explicit Material(const std::string &name) :
             mName(name),
             mName(name),
-            mDiffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)),
+            mDiffuse(0.6f, 0.6f, 0.6f),
             mSpecularExponent(ai_real(0.0)),
             mSpecularExponent(ai_real(0.0)),
             mShininessStrength(ai_real(1.0)),
             mShininessStrength(ai_real(1.0)),
             mShading(Discreet3DS::Gouraud),
             mShading(Discreet3DS::Gouraud),

+ 2 - 2
code/AssetLib/3DS/3DSLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -106,7 +106,7 @@ Discreet3DSImporter::Discreet3DSImporter() :
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
 // Returns whether the class can handle the format of the given file.
 bool Discreet3DSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
 bool Discreet3DSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
-    static const uint16_t token[] = { 0x4d4d, 0x3dc2 /*, 0x3daa */ };
+    static constexpr uint16_t token[] = { 0x4d4d, 0x3dc2 /*, 0x3daa */ };
     return CheckMagicToken(pIOHandler, pFile, token, AI_COUNT_OF(token), 0, sizeof token[0]);
     return CheckMagicToken(pIOHandler, pFile, token, AI_COUNT_OF(token), 0, sizeof token[0]);
 }
 }
 
 

+ 3 - 6
code/AssetLib/3DS/3DSLoader.h

@@ -1,10 +1,8 @@
-
 /*
 /*
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -57,14 +55,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 struct aiNode;
 struct aiNode;
 
 
-namespace Assimp    {
+namespace Assimp {
 
 
 using namespace D3DS;
 using namespace D3DS;
 
 
 // ---------------------------------------------------------------------------------
 // ---------------------------------------------------------------------------------
 /** Importer class for 3D Studio r3 and r4 3DS files
 /** Importer class for 3D Studio r3 and r4 3DS files
  */
  */
-class Discreet3DSImporter : public BaseImporter {
+class Discreet3DSImporter final : public BaseImporter {
 public:
 public:
     Discreet3DSImporter();
     Discreet3DSImporter();
     ~Discreet3DSImporter() override = default;
     ~Discreet3DSImporter() override = default;
@@ -126,7 +124,6 @@ protected:
     void ParseColorChunk(aiColor3D* p_pcOut,
     void ParseColorChunk(aiColor3D* p_pcOut,
         bool p_bAcceptPercent = true);
         bool p_bAcceptPercent = true);
 
 
-
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     /** Skip a chunk in the file
     /** Skip a chunk in the file
     */
     */

+ 17 - 1
code/AssetLib/3MF/3MFTypes.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -57,6 +57,7 @@ enum class ResourceType {
     RT_BaseMaterials,
     RT_BaseMaterials,
     RT_EmbeddedTexture2D,
     RT_EmbeddedTexture2D,
     RT_Texture2DGroup,
     RT_Texture2DGroup,
+    RT_ColorGroup,
     RT_Unknown
     RT_Unknown
 }; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...)
 }; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...)
 
 
@@ -117,6 +118,21 @@ public:
     }
     }
 };
 };
 
 
+class ColorGroup : public Resource {
+public:
+    std::vector<aiColor4D> mColors;
+    ColorGroup(int id) :
+            Resource(id){
+        // empty
+    }
+
+    ~ColorGroup() override = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_ColorGroup;
+    }
+};
+
 class BaseMaterials : public Resource {
 class BaseMaterials : public Resource {
 public:
 public:
     std::vector<unsigned int> mMaterialIndex;
     std::vector<unsigned int> mMaterialIndex;

+ 6 - 1
code/AssetLib/3MF/3MFXmlTags.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -98,6 +98,11 @@ namespace XmlTag {
     const char *const texture_cuurd_u = "u";
     const char *const texture_cuurd_u = "u";
     const char *const texture_cuurd_v = "v";
     const char *const texture_cuurd_v = "v";
 
 
+    // vertex color definitions
+    const char *const colorgroup = "m:colorgroup";
+    const char *const color_item = "m:color";
+    const char *const color_vaule = "color";
+
     // Meta info tags
     // Meta info tags
     const char* const CONTENT_TYPES_ARCHIVE = "[Content_Types].xml";
     const char* const CONTENT_TYPES_ARCHIVE = "[Content_Types].xml";
     const char* const ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels";
     const char* const ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels";

+ 5 - 5
code/AssetLib/3MF/D3MFExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -249,10 +249,10 @@ void D3MFExporter::writeBaseMaterials() {
             if (color.r <= 1 && color.g <= 1 && color.b <= 1 && color.a <= 1) {
             if (color.r <= 1 && color.g <= 1 && color.b <= 1 && color.a <= 1) {
 
 
                 hexDiffuseColor = ai_rgba2hex(
                 hexDiffuseColor = ai_rgba2hex(
-                        (int)((ai_real)color.r) * 255,
-                        (int)((ai_real)color.g) * 255,
-                        (int)((ai_real)color.b) * 255,
-                        (int)((ai_real)color.a) * 255,
+                        (int)(((ai_real)color.r) * 255),
+                        (int)(((ai_real)color.g) * 255),
+                        (int)(((ai_real)color.b) * 255),
+                        (int)(((ai_real)color.a) * 255),
                         true);
                         true);
 
 
             } else {
             } else {

+ 1 - 1
code/AssetLib/3MF/D3MFExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 10 - 5
code/AssetLib/3MF/D3MFImporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -81,12 +81,17 @@ static constexpr aiImporterDesc desc = {
     "3mf"
     "3mf"
 };
 };
 
 
-bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool /*checkSig*/) const {
+bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool ) const {
     if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
     if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
         return false;
         return false;
     }
     }
-    D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
-    return opcPackage.validate();
+    static constexpr char ModelRef[] = "3D/3dmodel.model";
+    ZipArchiveIOSystem archive(pIOHandler, filename);
+    if (!archive.Exists(ModelRef)) {
+        return false;
+    }
+
+    return true;
 }
 }
 
 
 void D3MFImporter::SetupProperties(const Importer*) {
 void D3MFImporter::SetupProperties(const Importer*) {
@@ -102,7 +107,7 @@ void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene,
 
 
     XmlParser xmlParser;
     XmlParser xmlParser;
     if (xmlParser.parse(opcPackage.RootStream())) {
     if (xmlParser.parse(opcPackage.RootStream())) {
-        XmlSerializer xmlSerializer(&xmlParser);
+        XmlSerializer xmlSerializer(xmlParser);
         xmlSerializer.ImportXml(pScene);
         xmlSerializer.ImportXml(pScene);
 
 
         const std::vector<aiTexture*> &tex =  opcPackage.GetEmbeddedTextures();
         const std::vector<aiTexture*> &tex =  opcPackage.GetEmbeddedTextures();

+ 1 - 1
code/AssetLib/3MF/D3MFImporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 3 - 3
code/AssetLib/3MF/D3MFOpcPackage.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -119,9 +119,9 @@ public:
 
 
 static bool IsEmbeddedTexture( const std::string &filename ) {
 static bool IsEmbeddedTexture( const std::string &filename ) {
     const std::string extension = BaseImporter::GetExtension(filename);
     const std::string extension = BaseImporter::GetExtension(filename);
-    if (extension == "jpg" || extension == "png") {
+    if (extension == "jpg" || extension == "png" || extension == "jpeg") {
         std::string::size_type pos = filename.find("thumbnail");
         std::string::size_type pos = filename.find("thumbnail");
-        if (pos == std::string::npos) {
+        if (pos != std::string::npos) {
             return false;
             return false;
         }
         }
         return true;
         return true;

+ 1 - 1
code/AssetLib/3MF/D3MFOpcPackage.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 137 - 31
code/AssetLib/3MF/XmlSerializer.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -57,8 +57,8 @@ static constexpr size_t ColRGBA_Len = 9;
 static constexpr size_t ColRGB_Len = 7;
 static constexpr size_t ColRGB_Len = 7;
 
 
 // format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
 // format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
-bool validateColorString(const char *color) {
-    const size_t len = strlen(color);
+bool validateColorString(const std::string color) {
+    const size_t len = color.size();
     if (ColRGBA_Len != len && ColRGB_Len != len) {
     if (ColRGBA_Len != len && ColRGB_Len != len) {
         return false;
         return false;
     }
     }
@@ -75,7 +75,7 @@ aiFace ReadTriangle(XmlNode &node, int &texId0, int &texId1, int &texId2) {
     face.mIndices[1] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v2).as_string()));
     face.mIndices[1] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v2).as_string()));
     face.mIndices[2] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v3).as_string()));
     face.mIndices[2] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v3).as_string()));
 
 
-    texId0 = texId1 = texId2 = -1;
+    texId0 = texId1 = texId2 = IdNotSet;
     XmlParser::getIntAttribute(node, XmlTag::p1, texId0);
     XmlParser::getIntAttribute(node, XmlTag::p1, texId0);
     XmlParser::getIntAttribute(node, XmlTag::p2, texId1);
     XmlParser::getIntAttribute(node, XmlTag::p2, texId1);
     XmlParser::getIntAttribute(node, XmlTag::p3, texId2);
     XmlParser::getIntAttribute(node, XmlTag::p3, texId2);
@@ -157,8 +157,8 @@ aiMatrix4x4 parseTransformMatrix(const std::string& matrixStr) {
     return transformMatrix;
     return transformMatrix;
 }
 }
 
 
-bool parseColor(const char *color, aiColor4D &diffuse) {
-    if (nullptr == color) {
+bool parseColor(const std::string &color, aiColor4D &diffuse) {
+    if (color.empty()) {
         return false;
         return false;
     }
     }
 
 
@@ -178,7 +178,7 @@ bool parseColor(const char *color, aiColor4D &diffuse) {
 
 
     char b[3] = { color[5], color[6], '\0' };
     char b[3] = { color[5], color[6], '\0' };
     diffuse.b = static_cast<ai_real>(strtol(b, nullptr, 16)) / ai_real(255.0);
     diffuse.b = static_cast<ai_real>(strtol(b, nullptr, 16)) / ai_real(255.0);
-    const size_t len = strlen(color);
+    const size_t len = color.size();
     if (ColRGB_Len == len) {
     if (ColRGB_Len == len) {
         return true;
         return true;
     }
     }
@@ -199,11 +199,11 @@ void assignDiffuseColor(XmlNode &node, aiMaterial *mat) {
 
 
 } // namespace
 } // namespace
 
 
-XmlSerializer::XmlSerializer(XmlParser *xmlParser) :
+XmlSerializer::XmlSerializer(XmlParser &xmlParser) :
         mResourcesDictionnary(),
         mResourcesDictionnary(),
         mMeshCount(0),
         mMeshCount(0),
         mXmlParser(xmlParser) {
         mXmlParser(xmlParser) {
-    ai_assert(nullptr != xmlParser);
+    // empty
 }
 }
 
 
 XmlSerializer::~XmlSerializer() {
 XmlSerializer::~XmlSerializer() {
@@ -218,7 +218,7 @@ void XmlSerializer::ImportXml(aiScene *scene) {
     }
     }
 
 
     scene->mRootNode = new aiNode(XmlTag::RootTag);
     scene->mRootNode = new aiNode(XmlTag::RootTag);
-    XmlNode node = mXmlParser->getRootNode().child(XmlTag::model);
+    XmlNode node = mXmlParser.getRootNode().child(XmlTag::model);
     if (node.empty()) {
     if (node.empty()) {
         return;
         return;
     }
     }
@@ -236,6 +236,8 @@ void XmlSerializer::ImportXml(aiScene *scene) {
             ReadBaseMaterials(currentNode);
             ReadBaseMaterials(currentNode);
         } else if (currentNodeName == XmlTag::meta) {
         } else if (currentNodeName == XmlTag::meta) {
             ReadMetadata(currentNode);
             ReadMetadata(currentNode);
+        } else if (currentNodeName == XmlTag::colorgroup) {
+            ReadColorGroup(currentNode);
         }
         }
     }
     }
     StoreMaterialsInScene(scene);
     StoreMaterialsInScene(scene);
@@ -331,9 +333,49 @@ void XmlSerializer::ReadObject(XmlNode &node) {
 
 
             if (hasPid) {
             if (hasPid) {
                 auto it = mResourcesDictionnary.find(pid);
                 auto it = mResourcesDictionnary.find(pid);
-                if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) {
-                    BaseMaterials *materials = static_cast<BaseMaterials *>(it->second);
-                    mesh->mMaterialIndex = materials->mMaterialIndex[pindex];
+                if (hasPindex && it != mResourcesDictionnary.end()) {
+                    if (it->second->getType() == ResourceType::RT_BaseMaterials) {
+                        BaseMaterials *materials = static_cast<BaseMaterials *>(it->second);
+                        mesh->mMaterialIndex = materials->mMaterialIndex[pindex];
+                    } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) {
+                        Texture2DGroup *group = static_cast<Texture2DGroup *>(it->second);
+                        if (mesh->mTextureCoords[0] == nullptr) {
+                            mesh->mNumUVComponents[0] = 2;
+                            for (unsigned int i = 1; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+                                mesh->mNumUVComponents[i] = 0;
+                            }
+
+                            const std::string name = ai_to_string(group->mTexId);
+                            for (size_t i = 0; i < mMaterials.size(); ++i) {
+                                if (name == mMaterials[i]->GetName().C_Str()) {
+                                    mesh->mMaterialIndex = static_cast<unsigned int>(i);
+                                }
+                            }
+
+                            mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+                            for (unsigned int vertex_idx = 0; vertex_idx < mesh->mNumVertices; vertex_idx++) {
+                                mesh->mTextureCoords[0][vertex_idx] =
+                                        aiVector3D(group->mTex2dCoords[pindex].x, group->mTex2dCoords[pindex].y, 0.0f);
+                            }
+                        } else {
+                            for (unsigned int vertex_idx = 0; vertex_idx < mesh->mNumVertices; vertex_idx++) {
+                                if (mesh->mTextureCoords[0][vertex_idx].z < 0) {
+                                    // use default
+                                    mesh->mTextureCoords[0][vertex_idx] =
+                                            aiVector3D(group->mTex2dCoords[pindex].x, group->mTex2dCoords[pindex].y, 0.0f);
+                                }
+                            }
+                        }
+                    }else if (it->second->getType() == ResourceType::RT_ColorGroup) {
+                        if (mesh->mColors[0] == nullptr) {
+                            mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
+
+                            ColorGroup *group = static_cast<ColorGroup *>(it->second);
+                            for (unsigned int vertex_idx = 0; vertex_idx < mesh->mNumVertices; vertex_idx++) {
+                                mesh->mColors[0][vertex_idx] = group->mColors[pindex];
+                            }
+                        }
+                    }
                 }
                 }
             }
             }
 
 
@@ -415,27 +457,36 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) {
     for (XmlNode &currentNode : node.children()) {
     for (XmlNode &currentNode : node.children()) {
         const std::string currentName = currentNode.name();
         const std::string currentName = currentNode.name();
         if (currentName == XmlTag::triangle) {
         if (currentName == XmlTag::triangle) {
-            int pid = IdNotSet, p1 = IdNotSet;
+            int pid = IdNotSet;
             bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
             bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
-            bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1);
 
 
-            int texId[3];
-            Texture2DGroup *group = nullptr;
-            aiFace face = ReadTriangle(currentNode, texId[0], texId[1], texId[2]);
-            if (hasPid && hasP1) {
+            int pindex[3];
+            aiFace face = ReadTriangle(currentNode, pindex[0], pindex[1], pindex[2]);
+            if (hasPid && (pindex[0] != IdNotSet || pindex[1] != IdNotSet || pindex[2] != IdNotSet)) {
                 auto it = mResourcesDictionnary.find(pid);
                 auto it = mResourcesDictionnary.find(pid);
                 if (it != mResourcesDictionnary.end()) {
                 if (it != mResourcesDictionnary.end()) {
                     if (it->second->getType() == ResourceType::RT_BaseMaterials) {
                     if (it->second->getType() == ResourceType::RT_BaseMaterials) {
                         BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
                         BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
-                        mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1];
+
+                        auto update_material = [&](int idx) {
+                            if (pindex[idx] != IdNotSet) {
+                                mesh->mMaterialIndex = baseMaterials->mMaterialIndex[pindex[idx]];
+                            }
+                        };
+
+                        update_material(0);
+                        update_material(1);
+                        update_material(2);
+
                     } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) {
                     } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) {
+                        // Load texture coordinates into mesh, when any
+                        Texture2DGroup *group = static_cast<Texture2DGroup *>(it->second); // fix bug
                         if (mesh->mTextureCoords[0] == nullptr) {
                         if (mesh->mTextureCoords[0] == nullptr) {
                             mesh->mNumUVComponents[0] = 2;
                             mesh->mNumUVComponents[0] = 2;
                             for (unsigned int i = 1; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
                             for (unsigned int i = 1; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
                                 mesh->mNumUVComponents[i] = 0;
                                 mesh->mNumUVComponents[i] = 0;
                             }
                             }
 
 
-                            group = static_cast<Texture2DGroup *>(it->second);
                             const std::string name = ai_to_string(group->mTexId);
                             const std::string name = ai_to_string(group->mTexId);
                             for (size_t i = 0; i < mMaterials.size(); ++i) {
                             for (size_t i = 0; i < mMaterials.size(); ++i) {
                                 if (name == mMaterials[i]->GetName().C_Str()) {
                                 if (name == mMaterials[i]->GetName().C_Str()) {
@@ -443,21 +494,44 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) {
                                 }
                                 }
                             }
                             }
                             mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
                             mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+                            for (unsigned int vertex_index = 0; vertex_index < mesh->mNumVertices; vertex_index++) {
+                                mesh->mTextureCoords[0][vertex_index].z = IdNotSet;//mark not set
+                            }
                         }
                         }
+
+                        auto update_texture = [&](int idx) {
+                            if (pindex[idx] != IdNotSet) {
+                                size_t vertex_index = face.mIndices[idx];
+                                mesh->mTextureCoords[0][vertex_index] =
+                                        aiVector3D(group->mTex2dCoords[pindex[idx]].x, group->mTex2dCoords[pindex[idx]].y, 0.0f);
+                            }
+                        };
+
+                        update_texture(0);
+                        update_texture(1);
+                        update_texture(2);
+
+                    } else if (it->second->getType() == ResourceType::RT_ColorGroup) {
+                        // Load vertex color into mesh, when any
+                        ColorGroup *group = static_cast<ColorGroup *>(it->second);
+                        if (mesh->mColors[0] == nullptr) {
+                            mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
+                        }
+
+                        auto update_color = [&](int idx) {
+                            if (pindex[idx] != IdNotSet) {
+                                size_t vertex_index = face.mIndices[idx];
+                                mesh->mColors[0][vertex_index] = group->mColors[pindex[idx]];
+                            }
+                        };
+
+                        update_color(0);
+                        update_color(1);
+                        update_color(2);
                     }
                     }
                 }
                 }
             }
             }
 
 
-            // Load texture coordinates into mesh, when any
-            if (group != nullptr) {
-                size_t i0 = face.mIndices[0];
-                size_t i1 = face.mIndices[1];
-                size_t i2 = face.mIndices[2];
-                mesh->mTextureCoords[0][i0] = aiVector3D(group->mTex2dCoords[texId[0]].x, group->mTex2dCoords[texId[0]].y, 0.0f);
-                mesh->mTextureCoords[0][i1] = aiVector3D(group->mTex2dCoords[texId[1]].x, group->mTex2dCoords[texId[1]].y, 0.0f);
-                mesh->mTextureCoords[0][i2] = aiVector3D(group->mTex2dCoords[texId[2]].x, group->mTex2dCoords[texId[2]].y, 0.0f);
-            }
-
             faces.push_back(face);
             faces.push_back(face);
         }
         }
     }
     }
@@ -598,6 +672,38 @@ aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basemater
     return material;
     return material;
 }
 }
 
 
+void XmlSerializer::ReadColor(XmlNode &node, ColorGroup *colorGroup) {
+    if (node.empty() || nullptr == colorGroup) {
+        return;
+    }
+
+    for (XmlNode currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == XmlTag::color_item) {
+            const char *color = currentNode.attribute(XmlTag::color_vaule).as_string();
+            aiColor4D color_value;
+            if (parseColor(color, color_value)) {
+                colorGroup->mColors.push_back(color_value);
+            }
+        }
+    }
+}
+
+void XmlSerializer::ReadColorGroup(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    int id = IdNotSet;
+    if (!XmlParser::getIntAttribute(node, XmlTag::id, id)) {
+        return;
+    }
+
+    ColorGroup *group = new ColorGroup(id);
+    ReadColor(node, group);
+    mResourcesDictionnary.insert(std::make_pair(id, group));
+}
+
 void XmlSerializer::StoreMaterialsInScene(aiScene *scene) {
 void XmlSerializer::StoreMaterialsInScene(aiScene *scene) {
     if (nullptr == scene) {
     if (nullptr == scene) {
         return;
         return;

+ 7 - 4
code/AssetLib/3MF/XmlSerializer.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -57,10 +57,11 @@ class D3MFOpcPackage;
 class Object;
 class Object;
 class Texture2DGroup;
 class Texture2DGroup;
 class EmbeddedTexture;
 class EmbeddedTexture;
+class ColorGroup;
 
 
-class XmlSerializer {
+class XmlSerializer final {
 public:
 public:
-    XmlSerializer(XmlParser *xmlParser);
+    XmlSerializer(XmlParser &xmlParser);
     ~XmlSerializer();
     ~XmlSerializer();
     void ImportXml(aiScene *scene);
     void ImportXml(aiScene *scene);
 
 
@@ -78,6 +79,8 @@ private:
     void ReadTextureGroup(XmlNode &node);
     void ReadTextureGroup(XmlNode &node);
     aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId);
     aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId);
     void StoreMaterialsInScene(aiScene *scene);
     void StoreMaterialsInScene(aiScene *scene);
+    void ReadColorGroup(XmlNode &node);
+    void ReadColor(XmlNode &node, ColorGroup *colorGroup);
 
 
 private:
 private:
     struct MetaEntry {
     struct MetaEntry {
@@ -89,7 +92,7 @@ private:
     std::vector<aiMaterial *> mMaterials;
     std::vector<aiMaterial *> mMaterials;
     std::map<unsigned int, Resource *> mResourcesDictionnary;
     std::map<unsigned int, Resource *> mResourcesDictionnary;
     unsigned int mMeshCount;
     unsigned int mMeshCount;
-    XmlParser *mXmlParser;
+    XmlParser &mXmlParser;
 };
 };
 
 
 } // namespace D3MF
 } // namespace D3MF

+ 116 - 120
code/AssetLib/AC/ACLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -77,8 +77,8 @@ static constexpr aiImporterDesc desc = {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // skip to the next token
 // skip to the next token
-inline const char *AcSkipToNextToken(const char *buffer) {
-    if (!SkipSpaces(&buffer)) {
+inline const char *AcSkipToNextToken(const char *buffer, const char *end) {
+    if (!SkipSpaces(&buffer, end)) {
         ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL");
         ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL");
     }
     }
     return buffer;
     return buffer;
@@ -86,13 +86,13 @@ inline const char *AcSkipToNextToken(const char *buffer) {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // read a string (may be enclosed in double quotation marks). buffer must point to "
 // read a string (may be enclosed in double quotation marks). buffer must point to "
-inline const char *AcGetString(const char *buffer, std::string &out) {
+inline const char *AcGetString(const char *buffer, const char *end, std::string &out) {
     if (*buffer == '\0') {
     if (*buffer == '\0') {
         throw DeadlyImportError("AC3D: Unexpected EOF in string");
         throw DeadlyImportError("AC3D: Unexpected EOF in string");
     }
     }
     ++buffer;
     ++buffer;
     const char *sz = buffer;
     const char *sz = buffer;
-    while ('\"' != *buffer) {
+    while ('\"' != *buffer && buffer != end) {
         if (IsLineEnd(*buffer)) {
         if (IsLineEnd(*buffer)) {
             ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL in string");
             ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL in string");
             out = "ERROR";
             out = "ERROR";
@@ -112,8 +112,8 @@ inline const char *AcGetString(const char *buffer, std::string &out) {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // read 1 to n floats prefixed with an optional predefined identifier
 // read 1 to n floats prefixed with an optional predefined identifier
 template <class T>
 template <class T>
-inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *name, size_t name_length, size_t num, T *out) {
-    buffer = AcSkipToNextToken(buffer);
+inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) {
+    buffer = AcSkipToNextToken(buffer, end);
     if (0 != name_length) {
     if (0 != name_length) {
         if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) {
         if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) {
             ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected.");
             ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected.");
@@ -122,7 +122,7 @@ inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *name
         buffer += name_length + 1;
         buffer += name_length + 1;
     }
     }
     for (unsigned int _i = 0; _i < num; ++_i) {
     for (unsigned int _i = 0; _i < num; ++_i) {
-        buffer = AcSkipToNextToken(buffer);
+        buffer = AcSkipToNextToken(buffer, end);
         buffer = fast_atoreal_move<float>(buffer, ((float *)out)[_i]);
         buffer = fast_atoreal_move<float>(buffer, ((float *)out)[_i]);
     }
     }
 
 
@@ -132,7 +132,7 @@ inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *name
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 // Constructor to be privately used by Importer
 AC3DImporter::AC3DImporter() :
 AC3DImporter::AC3DImporter() :
-        buffer(),
+        mBuffer(),
         configSplitBFCull(),
         configSplitBFCull(),
         configEvalSubdivision(),
         configEvalSubdivision(),
         mNumMeshes(),
         mNumMeshes(),
@@ -144,14 +144,10 @@ AC3DImporter::AC3DImporter() :
     // nothing to be done here
     // nothing to be done here
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well
-AC3DImporter::~AC3DImporter() = default;
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
 // Returns whether the class can handle the format of the given file.
 bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
 bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
-    static const uint32_t tokens[] = { AI_MAKE_MAGIC("AC3D") };
+    static constexpr uint32_t tokens[] = { AI_MAKE_MAGIC("AC3D") };
     return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
     return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
 }
 }
 
 
@@ -164,17 +160,18 @@ const aiImporterDesc *AC3DImporter::GetInfo() const {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Get a pointer to the next line from the file
 // Get a pointer to the next line from the file
 bool AC3DImporter::GetNextLine() {
 bool AC3DImporter::GetNextLine() {
-    SkipLine(&buffer);
-    return SkipSpaces(&buffer);
+    SkipLine(&mBuffer.data, mBuffer.end);
+    return SkipSpaces(&mBuffer.data, mBuffer.end);
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Parse an object section in an AC file
 // Parse an object section in an AC file
-void AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
-    if (!TokenMatch(buffer, "OBJECT", 6))
-        return;
+bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
+    if (!TokenMatch(mBuffer.data, "OBJECT", 6)) {
+        return false;
+    }
 
 
-    SkipSpaces(&buffer);
+    SkipSpaces(&mBuffer.data, mBuffer.end);
 
 
     ++mNumMeshes;
     ++mNumMeshes;
 
 
@@ -182,7 +179,7 @@ void AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
     Object &obj = objects.back();
     Object &obj = objects.back();
 
 
     aiLight *light = nullptr;
     aiLight *light = nullptr;
-    if (!ASSIMP_strincmp(buffer, "light", 5)) {
+    if (!ASSIMP_strincmp(mBuffer.data, "light", 5)) {
         // This is a light source. Add it to the list
         // This is a light source. Add it to the list
         mLights->push_back(light = new aiLight());
         mLights->push_back(light = new aiLight());
 
 
@@ -192,68 +189,73 @@ void AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
         light->mAttenuationConstant = 1.f;
         light->mAttenuationConstant = 1.f;
 
 
         // Generate a default name for both the light source and the node
         // Generate a default name for both the light source and the node
-        // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version.
-        light->mName.length = ::ai_snprintf(light->mName.data, MAXLEN, "ACLight_%i", static_cast<unsigned int>(mLights->size()) - 1);
+        light->mName.length = ::ai_snprintf(light->mName.data, AI_MAXLEN, "ACLight_%i", static_cast<unsigned int>(mLights->size()) - 1);
         obj.name = std::string(light->mName.data);
         obj.name = std::string(light->mName.data);
 
 
         ASSIMP_LOG_VERBOSE_DEBUG("AC3D: Light source encountered");
         ASSIMP_LOG_VERBOSE_DEBUG("AC3D: Light source encountered");
         obj.type = Object::Light;
         obj.type = Object::Light;
-    } else if (!ASSIMP_strincmp(buffer, "group", 5)) {
+    } else if (!ASSIMP_strincmp(mBuffer.data, "group", 5)) {
         obj.type = Object::Group;
         obj.type = Object::Group;
-    } else if (!ASSIMP_strincmp(buffer, "world", 5)) {
+    } else if (!ASSIMP_strincmp(mBuffer.data, "world", 5)) {
         obj.type = Object::World;
         obj.type = Object::World;
-    } else
+    } else {
         obj.type = Object::Poly;
         obj.type = Object::Poly;
+    }
+
     while (GetNextLine()) {
     while (GetNextLine()) {
-        if (TokenMatch(buffer, "kids", 4)) {
-            SkipSpaces(&buffer);
-            unsigned int num = strtoul10(buffer, &buffer);
+        if (TokenMatch(mBuffer.data, "kids", 4)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            unsigned int num = strtoul10(mBuffer.data, &mBuffer.data);
             GetNextLine();
             GetNextLine();
             if (num) {
             if (num) {
                 // load the children of this object recursively
                 // load the children of this object recursively
                 obj.children.reserve(num);
                 obj.children.reserve(num);
-                for (unsigned int i = 0; i < num; ++i)
-                    LoadObjectSection(obj.children);
+                for (unsigned int i = 0; i < num; ++i) {
+                    if (!LoadObjectSection(obj.children)) {
+                        ASSIMP_LOG_WARN("AC3D: wrong number of kids");
+                        break;
+                    }
+                }
             }
             }
-            return;
-        } else if (TokenMatch(buffer, "name", 4)) {
-            SkipSpaces(&buffer);
-            buffer = AcGetString(buffer, obj.name);
+            return true;
+        } else if (TokenMatch(mBuffer.data, "name", 4)) {
+            SkipSpaces(&mBuffer.data, mBuffer.data);
+            mBuffer.data = AcGetString(mBuffer.data, mBuffer.end, obj.name);
 
 
             // If this is a light source, we'll also need to store
             // If this is a light source, we'll also need to store
             // the name of the node in it.
             // the name of the node in it.
             if (light) {
             if (light) {
                 light->mName.Set(obj.name);
                 light->mName.Set(obj.name);
             }
             }
-        } else if (TokenMatch(buffer, "texture", 7)) {
-            SkipSpaces(&buffer);
+        } else if (TokenMatch(mBuffer.data, "texture", 7)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
             std::string texture;
             std::string texture;
-            buffer = AcGetString(buffer, texture);
+            mBuffer.data = AcGetString(mBuffer.data, mBuffer.end, texture);
             obj.textures.push_back(texture);
             obj.textures.push_back(texture);
-        } else if (TokenMatch(buffer, "texrep", 6)) {
-            SkipSpaces(&buffer);
-            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &obj.texRepeat);
+        } else if (TokenMatch(mBuffer.data, "texrep", 6)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &obj.texRepeat);
             if (!obj.texRepeat.x || !obj.texRepeat.y)
             if (!obj.texRepeat.x || !obj.texRepeat.y)
                 obj.texRepeat = aiVector2D(1.f, 1.f);
                 obj.texRepeat = aiVector2D(1.f, 1.f);
-        } else if (TokenMatch(buffer, "texoff", 6)) {
-            SkipSpaces(&buffer);
-            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &obj.texOffset);
-        } else if (TokenMatch(buffer, "rot", 3)) {
-            SkipSpaces(&buffer);
-            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 9, &obj.rotation);
-        } else if (TokenMatch(buffer, "loc", 3)) {
-            SkipSpaces(&buffer);
-            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 3, &obj.translation);
-        } else if (TokenMatch(buffer, "subdiv", 6)) {
-            SkipSpaces(&buffer);
-            obj.subDiv = strtoul10(buffer, &buffer);
-        } else if (TokenMatch(buffer, "crease", 6)) {
-            SkipSpaces(&buffer);
-            obj.crease = fast_atof(buffer);
-        } else if (TokenMatch(buffer, "numvert", 7)) {
-            SkipSpaces(&buffer);
-
-            unsigned int t = strtoul10(buffer, &buffer);
+        } else if (TokenMatch(mBuffer.data, "texoff", 6)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &obj.texOffset);
+        } else if (TokenMatch(mBuffer.data, "rot", 3)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 9, &obj.rotation);
+        } else if (TokenMatch(mBuffer.data, "loc", 3)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 3, &obj.translation);
+        } else if (TokenMatch(mBuffer.data, "subdiv", 6)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            obj.subDiv = strtoul10(mBuffer.data, &mBuffer.data);
+        } else if (TokenMatch(mBuffer.data, "crease", 6)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            obj.crease = fast_atof(mBuffer.data);
+        } else if (TokenMatch(mBuffer.data, "numvert", 7)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+
+            unsigned int t = strtoul10(mBuffer.data, &mBuffer.data);
             if (t >= AI_MAX_ALLOC(aiVector3D)) {
             if (t >= AI_MAX_ALLOC(aiVector3D)) {
                 throw DeadlyImportError("AC3D: Too many vertices, would run out of memory");
                 throw DeadlyImportError("AC3D: Too many vertices, would run out of memory");
             }
             }
@@ -262,59 +264,59 @@ void AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
                 if (!GetNextLine()) {
                 if (!GetNextLine()) {
                     ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: not all vertices have been parsed yet");
                     ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: not all vertices have been parsed yet");
                     break;
                     break;
-                } else if (!IsNumeric(*buffer)) {
+                } else if (!IsNumeric(*mBuffer.data)) {
                     ASSIMP_LOG_ERROR("AC3D: Unexpected token: not all vertices have been parsed yet");
                     ASSIMP_LOG_ERROR("AC3D: Unexpected token: not all vertices have been parsed yet");
-                    --buffer; // make sure the line is processed a second time
+                    --mBuffer.data; // make sure the line is processed a second time
                     break;
                     break;
                 }
                 }
                 obj.vertices.emplace_back();
                 obj.vertices.emplace_back();
                 aiVector3D &v = obj.vertices.back();
                 aiVector3D &v = obj.vertices.back();
-                buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 3, &v.x);
+                mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 3, &v.x);
             }
             }
-        } else if (TokenMatch(buffer, "numsurf", 7)) {
-            SkipSpaces(&buffer);
+        } else if (TokenMatch(mBuffer.data, "numsurf", 7)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
 
 
             bool Q3DWorkAround = false;
             bool Q3DWorkAround = false;
 
 
-            const unsigned int t = strtoul10(buffer, &buffer);
+            const unsigned int t = strtoul10(mBuffer.data, &mBuffer.data);
             obj.surfaces.reserve(t);
             obj.surfaces.reserve(t);
             for (unsigned int i = 0; i < t; ++i) {
             for (unsigned int i = 0; i < t; ++i) {
                 GetNextLine();
                 GetNextLine();
-                if (!TokenMatch(buffer, "SURF", 4)) {
+                if (!TokenMatch(mBuffer.data, "SURF", 4)) {
                     // FIX: this can occur for some files - Quick 3D for
                     // FIX: this can occur for some files - Quick 3D for
                     // example writes no surf chunks
                     // example writes no surf chunks
                     if (!Q3DWorkAround) {
                     if (!Q3DWorkAround) {
                         ASSIMP_LOG_WARN("AC3D: SURF token was expected");
                         ASSIMP_LOG_WARN("AC3D: SURF token was expected");
                         ASSIMP_LOG_VERBOSE_DEBUG("Continuing with Quick3D Workaround enabled");
                         ASSIMP_LOG_VERBOSE_DEBUG("Continuing with Quick3D Workaround enabled");
                     }
                     }
-                    --buffer; // make sure the line is processed a second time
+                    --mBuffer.data; // make sure the line is processed a second time
                     // break; --- see fix notes above
                     // break; --- see fix notes above
 
 
                     Q3DWorkAround = true;
                     Q3DWorkAround = true;
                 }
                 }
-                SkipSpaces(&buffer);
+                SkipSpaces(&mBuffer.data, mBuffer.end);
                 obj.surfaces.emplace_back();
                 obj.surfaces.emplace_back();
                 Surface &surf = obj.surfaces.back();
                 Surface &surf = obj.surfaces.back();
-                surf.flags = strtoul_cppstyle(buffer);
+                surf.flags = strtoul_cppstyle(mBuffer.data);
 
 
                 while (true) {
                 while (true) {
                     if (!GetNextLine()) {
                     if (!GetNextLine()) {
                         throw DeadlyImportError("AC3D: Unexpected EOF: surface is incomplete");
                         throw DeadlyImportError("AC3D: Unexpected EOF: surface is incomplete");
                     }
                     }
-                    if (TokenMatch(buffer, "mat", 3)) {
-                        SkipSpaces(&buffer);
-                        surf.mat = strtoul10(buffer);
-                    } else if (TokenMatch(buffer, "refs", 4)) {
+                    if (TokenMatch(mBuffer.data, "mat", 3)) {
+                        SkipSpaces(&mBuffer.data, mBuffer.end);
+                        surf.mat = strtoul10(mBuffer.data);
+                    } else if (TokenMatch(mBuffer.data, "refs", 4)) {
                         // --- see fix notes above
                         // --- see fix notes above
                         if (Q3DWorkAround) {
                         if (Q3DWorkAround) {
                             if (!surf.entries.empty()) {
                             if (!surf.entries.empty()) {
-                                buffer -= 6;
+                                mBuffer.data -= 6;
                                 break;
                                 break;
                             }
                             }
                         }
                         }
 
 
-                        SkipSpaces(&buffer);
-                        const unsigned int m = strtoul10(buffer);
+                        SkipSpaces(&mBuffer.data, mBuffer.end);
+                        const unsigned int m = strtoul10(mBuffer.data);
                         surf.entries.reserve(m);
                         surf.entries.reserve(m);
 
 
                         obj.numRefs += m;
                         obj.numRefs += m;
@@ -327,12 +329,12 @@ void AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
                             surf.entries.emplace_back();
                             surf.entries.emplace_back();
                             Surface::SurfaceEntry &entry = surf.entries.back();
                             Surface::SurfaceEntry &entry = surf.entries.back();
 
 
-                            entry.first = strtoul10(buffer, &buffer);
-                            SkipSpaces(&buffer);
-                            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &entry.second);
+                            entry.first = strtoul10(mBuffer.data, &mBuffer.data);
+                            SkipSpaces(&mBuffer.data, mBuffer.end);
+                            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &entry.second);
                         }
                         }
                     } else {
                     } else {
-                        --buffer; // make sure the line is processed a second time
+                        --mBuffer.data; // make sure the line is processed a second time
                         break;
                         break;
                     }
                     }
                 }
                 }
@@ -340,6 +342,8 @@ void AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
         }
         }
     }
     }
     ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: \'kids\' line was expected");
     ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: \'kids\' line was expected");
+
+    return false;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -445,7 +449,7 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
                     idx = 0;
                     idx = 0;
                 }
                 }
                 if ((*it).entries.empty()) {
                 if ((*it).entries.empty()) {
-                    ASSIMP_LOG_WARN("AC3D: surface her zero vertex references");
+                    ASSIMP_LOG_WARN("AC3D: surface has zero vertex references");
                 }
                 }
 
 
                 // validate all vertex indices to make sure we won't crash here
                 // validate all vertex indices to make sure we won't crash here
@@ -463,16 +467,15 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
                 }
                 }
 
 
                 switch ((*it).GetType()) {
                 switch ((*it).GetType()) {
-                    // closed line
-                case Surface::ClosedLine:
-                    needMat[idx].first += (unsigned int)(*it).entries.size();
-                    needMat[idx].second += (unsigned int)(*it).entries.size() << 1u;
+                case Surface::ClosedLine: // closed line
+                    needMat[idx].first += static_cast<unsigned int>((*it).entries.size());
+                    needMat[idx].second += static_cast<unsigned int>((*it).entries.size() << 1u);
                     break;
                     break;
 
 
                     // unclosed line
                     // unclosed line
                 case Surface::OpenLine:
                 case Surface::OpenLine:
-                    needMat[idx].first += (unsigned int)(*it).entries.size() - 1;
-                    needMat[idx].second += ((unsigned int)(*it).entries.size() - 1) << 1u;
+                    needMat[idx].first += static_cast<unsigned int>((*it).entries.size() - 1);
+                    needMat[idx].second += static_cast<unsigned int>(((*it).entries.size() - 1) << 1u);
                     break;
                     break;
 
 
                     // triangle strip
                     // triangle strip
@@ -574,15 +577,6 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
                                 const Surface::SurfaceEntry &entry2 = src.entries[i + 1];
                                 const Surface::SurfaceEntry &entry2 = src.entries[i + 1];
                                 const Surface::SurfaceEntry &entry3 = src.entries[i + 2];
                                 const Surface::SurfaceEntry &entry3 = src.entries[i + 2];
 
 
-                                // skip degenerate triangles
-                                if (object.vertices[entry1.first] == object.vertices[entry2.first] ||
-                                        object.vertices[entry1.first] == object.vertices[entry3.first] ||
-                                        object.vertices[entry2.first] == object.vertices[entry3.first]) {
-                                    mesh->mNumFaces--;
-                                    mesh->mNumVertices -= 3;
-                                    continue;
-                                }
-
                                 aiFace &face = *faces++;
                                 aiFace &face = *faces++;
                                 face.mNumIndices = 3;
                                 face.mNumIndices = 3;
                                 face.mIndices = new unsigned int[face.mNumIndices];
                                 face.mIndices = new unsigned int[face.mNumIndices];
@@ -701,18 +695,18 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
         // generate a name depending on the type of the node
         // generate a name depending on the type of the node
         switch (object.type) {
         switch (object.type) {
         case Object::Group:
         case Object::Group:
-            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACGroup_%i", mGroupsCounter++);
+            node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACGroup_%i", mGroupsCounter++);
             break;
             break;
         case Object::Poly:
         case Object::Poly:
-            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACPoly_%i", mPolysCounter++);
+            node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACPoly_%i", mPolysCounter++);
             break;
             break;
         case Object::Light:
         case Object::Light:
-            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACLight_%i", mLightsCounter++);
+            node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACLight_%i", mLightsCounter++);
             break;
             break;
 
 
             // there shouldn't be more than one world, but we don't care
             // there shouldn't be more than one world, but we don't care
         case Object::World:
         case Object::World:
-            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACWorld_%i", mWorldsCounter++);
+            node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACWorld_%i", mWorldsCounter++);
             break;
             break;
         }
         }
     }
     }
@@ -760,17 +754,18 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
     std::vector<char> mBuffer2;
     std::vector<char> mBuffer2;
     TextFileToBuffer(file.get(), mBuffer2);
     TextFileToBuffer(file.get(), mBuffer2);
 
 
-    buffer = &mBuffer2[0];
+    mBuffer.data = &mBuffer2[0];
+    mBuffer.end = &mBuffer2[0] + mBuffer2.size();
     mNumMeshes = 0;
     mNumMeshes = 0;
 
 
     mLightsCounter = mPolysCounter = mWorldsCounter = mGroupsCounter = 0;
     mLightsCounter = mPolysCounter = mWorldsCounter = mGroupsCounter = 0;
 
 
-    if (::strncmp(buffer, "AC3D", 4)) {
+    if (::strncmp(mBuffer.data, "AC3D", 4)) {
         throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found");
         throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found");
     }
     }
 
 
     // print the file format version to the console
     // print the file format version to the console
-    unsigned int version = HexDigitToDecimal(buffer[4]);
+    unsigned int version = HexDigitToDecimal(mBuffer.data[4]);
     char msg[3];
     char msg[3];
     ASSIMP_itoa10(msg, 3, version);
     ASSIMP_itoa10(msg, 3, version);
     ASSIMP_LOG_INFO("AC3D file format version: ", msg);
     ASSIMP_LOG_INFO("AC3D file format version: ", msg);
@@ -785,30 +780,31 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
     mLights = &lights;
     mLights = &lights;
 
 
     while (GetNextLine()) {
     while (GetNextLine()) {
-        if (TokenMatch(buffer, "MATERIAL", 8)) {
+        if (TokenMatch(mBuffer.data, "MATERIAL", 8)) {
             materials.emplace_back();
             materials.emplace_back();
             Material &mat = materials.back();
             Material &mat = materials.back();
 
 
             // manually parse the material ... sscanf would use the buldin atof ...
             // manually parse the material ... sscanf would use the buldin atof ...
             // Format: (name) rgb %f %f %f  amb %f %f %f  emis %f %f %f  spec %f %f %f  shi %d  trans %f
             // Format: (name) rgb %f %f %f  amb %f %f %f  emis %f %f %f  spec %f %f %f  shi %d  trans %f
 
 
-            buffer = AcSkipToNextToken(buffer);
-            if ('\"' == *buffer) {
-                buffer = AcGetString(buffer, mat.name);
-                buffer = AcSkipToNextToken(buffer);
+            mBuffer.data = AcSkipToNextToken(mBuffer.data, mBuffer.end);
+            if ('\"' == *mBuffer.data) {
+                mBuffer.data = AcGetString(mBuffer.data, mBuffer.end, mat.name);
+                mBuffer.data = AcSkipToNextToken(mBuffer.data, mBuffer.end);
             }
             }
 
 
-            buffer = TAcCheckedLoadFloatArray(buffer, "rgb", 3, 3, &mat.rgb);
-            buffer = TAcCheckedLoadFloatArray(buffer, "amb", 3, 3, &mat.amb);
-            buffer = TAcCheckedLoadFloatArray(buffer, "emis", 4, 3, &mat.emis);
-            buffer = TAcCheckedLoadFloatArray(buffer, "spec", 4, 3, &mat.spec);
-            buffer = TAcCheckedLoadFloatArray(buffer, "shi", 3, 1, &mat.shin);
-            buffer = TAcCheckedLoadFloatArray(buffer, "trans", 5, 1, &mat.trans);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "rgb", 3, 3, &mat.rgb);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "amb", 3, 3, &mat.amb);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "emis", 4, 3, &mat.emis);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "spec", 4, 3, &mat.spec);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "shi", 3, 1, &mat.shin);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "trans", 5, 1, &mat.trans);
+        } else {
+            LoadObjectSection(rootObjects);
         }
         }
-        LoadObjectSection(rootObjects);
     }
     }
 
 
-    if (rootObjects.empty() || !mNumMeshes) {
+    if (rootObjects.empty() || mNumMeshes == 0u) {
         throw DeadlyImportError("AC3D: No meshes have been loaded");
         throw DeadlyImportError("AC3D: No meshes have been loaded");
     }
     }
     if (materials.empty()) {
     if (materials.empty()) {
@@ -824,7 +820,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
     materials.reserve(mNumMeshes);
     materials.reserve(mNumMeshes);
 
 
     // generate a dummy root if there are multiple objects on the top layer
     // generate a dummy root if there are multiple objects on the top layer
-    Object *root;
+    Object *root = nullptr;
     if (1 == rootObjects.size())
     if (1 == rootObjects.size())
         root = &rootObjects[0];
         root = &rootObjects[0];
     else {
     else {
@@ -837,7 +833,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
         delete root;
         delete root;
     }
     }
 
 
-    if (!::strncmp(pScene->mRootNode->mName.data, "Node", 4)) {
+    if (::strncmp(pScene->mRootNode->mName.data, "Node", 4) == 0) {
         pScene->mRootNode->mName.Set("<AC3DWorld>");
         pScene->mRootNode->mName.Set("<AC3DWorld>");
     }
     }
 
 
@@ -856,7 +852,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
 
 
     // copy lights
     // copy lights
     pScene->mNumLights = (unsigned int)lights.size();
     pScene->mNumLights = (unsigned int)lights.size();
-    if (lights.size()) {
+    if (!lights.empty()) {
         pScene->mLights = new aiLight *[lights.size()];
         pScene->mLights = new aiLight *[lights.size()];
         ::memcpy(pScene->mLights, &lights[0], lights.size() * sizeof(void *));
         ::memcpy(pScene->mLights, &lights[0], lights.size() * sizeof(void *));
     }
     }

+ 5 - 5
code/AssetLib/AC/ACLoader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -63,7 +63,7 @@ namespace Assimp {
 class AC3DImporter : public BaseImporter {
 class AC3DImporter : public BaseImporter {
 public:
 public:
     AC3DImporter();
     AC3DImporter();
-    ~AC3DImporter() override;
+    ~AC3DImporter() override = default;
 
 
     // Represents an AC3D material
     // Represents an AC3D material
     struct Material {
     struct Material {
@@ -103,7 +103,7 @@ public:
 
 
         unsigned int mat, flags;
         unsigned int mat, flags;
 
 
-        typedef std::pair<unsigned int, aiVector2D> SurfaceEntry;
+        using SurfaceEntry = std::pair<unsigned int, aiVector2D>;
         std::vector<SurfaceEntry> entries;
         std::vector<SurfaceEntry> entries;
 
 
         // Type is low nibble of flags
         // Type is low nibble of flags
@@ -216,7 +216,7 @@ private:
      *  load subobjects, the method returns after a 'kids 0' was
      *  load subobjects, the method returns after a 'kids 0' was
      *  encountered.
      *  encountered.
      *  @objects List of output objects*/
      *  @objects List of output objects*/
-    void LoadObjectSection(std::vector<Object> &objects);
+    bool LoadObjectSection(std::vector<Object> &objects);
 
 
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     /** Convert all objects into meshes and nodes.
     /** Convert all objects into meshes and nodes.
@@ -242,7 +242,7 @@ private:
 
 
 private:
 private:
     // points to the next data line
     // points to the next data line
-    const char *buffer;
+    aiBuffer mBuffer;
 
 
     // Configuration option: if enabled, up to two meshes
     // Configuration option: if enabled, up to two meshes
     // are generated per material: those faces who have
     // are generated per material: those faces who have

+ 12 - 31
code/AssetLib/AMF/AMFImporter.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -178,28 +178,6 @@ bool AMFImporter::XML_SearchNode(const std::string &nodeName) {
     return nullptr != mXmlParser->findNode(nodeName);
     return nullptr != mXmlParser->findNode(nodeName);
 }
 }
 
 
-void AMFImporter::ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString) {
-    size_t instr_len;
-
-    pOutString.clear();
-    instr_len = strlen(pInStr);
-    if (!instr_len) return;
-
-    pOutString.reserve(instr_len * 3 / 2);
-    // check and correct floats in format ".x". Must be "x.y".
-    if (pInStr[0] == '.') pOutString.push_back('0');
-
-    pOutString.push_back(pInStr[0]);
-    for (size_t ci = 1; ci < instr_len; ci++) {
-        if ((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t'))) {
-            pOutString.push_back('0');
-            pOutString.push_back('.');
-        } else {
-            pOutString.push_back(pInStr[ci]);
-        }
-    }
-}
-
 static bool ParseHelper_Decode_Base64_IsBase64(const char pChar) {
 static bool ParseHelper_Decode_Base64_IsBase64(const char pChar) {
     return (isalnum((unsigned char)pChar) || (pChar == '+') || (pChar == '/'));
     return (isalnum((unsigned char)pChar) || (pChar == '+') || (pChar == '/'));
 }
 }
@@ -213,7 +191,10 @@ void AMFImporter::ParseHelper_Decode_Base64(const std::string &pInputBase64, std
     uint8_t arr4[4], arr3[3];
     uint8_t arr4[4], arr3[3];
 
 
     // check input data
     // check input data
-    if (pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
+    if (pInputBase64.size() % 4) {
+        throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
+    }
+
     // prepare output place
     // prepare output place
     pOutputData.clear();
     pOutputData.clear();
     pOutputData.reserve(pInputBase64.size() / 4 * 3);
     pOutputData.reserve(pInputBase64.size() / 4 * 3);
@@ -403,17 +384,17 @@ void AMFImporter::ParseNode_Instance(XmlNode &node) {
         for (auto &currentNode : node.children()) {
         for (auto &currentNode : node.children()) {
             const std::string &currentName = currentNode.name();
             const std::string &currentName = currentNode.name();
             if (currentName == "deltax") {
             if (currentName == "deltax") {
-                XmlParser::getValueAsFloat(currentNode, als.Delta.x);
+                XmlParser::getValueAsReal(currentNode, als.Delta.x);
             } else if (currentName == "deltay") {
             } else if (currentName == "deltay") {
-                XmlParser::getValueAsFloat(currentNode, als.Delta.y);
+                XmlParser::getValueAsReal(currentNode, als.Delta.y);
             } else if (currentName == "deltaz") {
             } else if (currentName == "deltaz") {
-                XmlParser::getValueAsFloat(currentNode, als.Delta.z);
+                XmlParser::getValueAsReal(currentNode, als.Delta.z);
             } else if (currentName == "rx") {
             } else if (currentName == "rx") {
-                XmlParser::getValueAsFloat(currentNode, als.Delta.x);
+                XmlParser::getValueAsReal(currentNode, als.Delta.x);
             } else if (currentName == "ry") {
             } else if (currentName == "ry") {
-                XmlParser::getValueAsFloat(currentNode, als.Delta.y);
+                XmlParser::getValueAsReal(currentNode, als.Delta.y);
             } else if (currentName == "rz") {
             } else if (currentName == "rz") {
-                XmlParser::getValueAsFloat(currentNode, als.Delta.z);
+                XmlParser::getValueAsReal(currentNode, als.Delta.z);
             }
             }
         }
         }
         ParseHelper_Node_Exit();
         ParseHelper_Node_Exit();
@@ -493,7 +474,7 @@ void AMFImporter::ParseNode_Metadata(XmlNode &node) {
 
 
     // read attribute
     // read attribute
     ne = new AMFMetadata(mNodeElement_Cur);
     ne = new AMFMetadata(mNodeElement_Cur);
-    ((AMFMetadata *)ne)->Type = type;
+    ((AMFMetadata *)ne)->MetaType = type;
     ((AMFMetadata *)ne)->Value = value;
     ((AMFMetadata *)ne)->Value = value;
     mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
     mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
     mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
     mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.

+ 1 - 2
code/AssetLib/AMF/AMFImporter.hpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -168,7 +168,6 @@ public:
     AI_WONT_RETURN void Throw_ID_NotFound(const std::string &pID) const AI_WONT_RETURN_SUFFIX;
     AI_WONT_RETURN void Throw_ID_NotFound(const std::string &pID) const AI_WONT_RETURN_SUFFIX;
     void XML_CheckNode_MustHaveChildren(pugi::xml_node &node);
     void XML_CheckNode_MustHaveChildren(pugi::xml_node &node);
     bool XML_SearchNode(const std::string &nodeName);
     bool XML_SearchNode(const std::string &nodeName);
-    void ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString);
     AMFImporter(const AMFImporter &pScene) = delete;
     AMFImporter(const AMFImporter &pScene) = delete;
     AMFImporter &operator=(const AMFImporter &pScene) = delete;
     AMFImporter &operator=(const AMFImporter &pScene) = delete;
 
 

+ 5 - 5
code/AssetLib/AMF/AMFImporter_Geometry.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -167,11 +167,11 @@ void AMFImporter::ParseNode_Coordinates(XmlNode &node) {
             AMFCoordinates &als = *((AMFCoordinates *)ne); // alias for convenience
             AMFCoordinates &als = *((AMFCoordinates *)ne); // alias for convenience
             const std::string &currentName = ai_tolower(currentNode.name());
             const std::string &currentName = ai_tolower(currentNode.name());
             if (currentName == "x") {
             if (currentName == "x") {
-                XmlParser::getValueAsFloat(currentNode, als.Coordinate.x);
+                XmlParser::getValueAsReal(currentNode, als.Coordinate.x);
             } else if (currentName == "y") {
             } else if (currentName == "y") {
-                XmlParser::getValueAsFloat(currentNode, als.Coordinate.y);
+                XmlParser::getValueAsReal(currentNode, als.Coordinate.y);
             } else if (currentName == "z") {
             } else if (currentName == "z") {
-                XmlParser::getValueAsFloat(currentNode, als.Coordinate.z);
+                XmlParser::getValueAsReal(currentNode, als.Coordinate.z);
             }
             }
         }
         }
         ParseHelper_Node_Exit();
         ParseHelper_Node_Exit();
@@ -202,7 +202,7 @@ void AMFImporter::ParseNode_Volume(XmlNode &node) {
 
 
     ((AMFVolume *)ne)->MaterialID = node.attribute("materialid").as_string();
     ((AMFVolume *)ne)->MaterialID = node.attribute("materialid").as_string();
 
 
-    ((AMFVolume *)ne)->Type = type;
+    ((AMFVolume *)ne)->VolumeType = type;
     // Check for child nodes
     // Check for child nodes
     bool col_read = false;
     bool col_read = false;
     if (!node.empty()) {
     if (!node.empty()) {

+ 7 - 8
code/AssetLib/AMF/AMFImporter_Material.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -263,26 +263,25 @@ void AMFImporter::ParseNode_TexMap(XmlNode &node, const bool pUseOldName) {
             const std::string &name = currentNode.name();
             const std::string &name = currentNode.name();
             if (name == "utex1") {
             if (name == "utex1") {
 				read_flag[0] = true;
 				read_flag[0] = true;
-                XmlParser::getValueAsFloat(node, als.TextureCoordinate[0].x);
+                XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[0].x);
             } else if (name == "utex2") {
             } else if (name == "utex2") {
 				read_flag[1] = true;
 				read_flag[1] = true;
-                XmlParser::getValueAsFloat(node, als.TextureCoordinate[1].x);
+                XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[1].x);
             } else if (name == "utex3") {
             } else if (name == "utex3") {
 				read_flag[2] = true;
 				read_flag[2] = true;
-                XmlParser::getValueAsFloat(node, als.TextureCoordinate[2].x);
+                XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[2].x);
             } else if (name == "vtex1") {
             } else if (name == "vtex1") {
 				read_flag[3] = true;
 				read_flag[3] = true;
-                XmlParser::getValueAsFloat(node, als.TextureCoordinate[0].y);
+                XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[0].y);
             } else if (name == "vtex2") {
             } else if (name == "vtex2") {
 				read_flag[4] = true;
 				read_flag[4] = true;
-                XmlParser::getValueAsFloat(node, als.TextureCoordinate[1].y);
+                XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[1].y);
             } else if (name == "vtex3") {
             } else if (name == "vtex3") {
 				read_flag[5] = true;
 				read_flag[5] = true;
-                XmlParser::getValueAsFloat(node, als.TextureCoordinate[2].y);
+                XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[2].y);
 			}
 			}
 		}
 		}
         ParseHelper_Node_Exit();
         ParseHelper_Node_Exit();
-
 	} else {
 	} else {
 		for (pugi::xml_attribute &attr : node.attributes()) {
 		for (pugi::xml_attribute &attr : node.attributes()) {
             const std::string name = attr.name();
             const std::string name = attr.name();

+ 9 - 23
code/AssetLib/AMF/AMFImporter_Node.hpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -56,7 +56,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
 
 
-/// \class CAMFImporter_NodeElement
 /// Base class for elements of nodes.
 /// Base class for elements of nodes.
 class AMFNodeElementBase {
 class AMFNodeElementBase {
 public:
 public:
@@ -87,7 +86,8 @@ public:
 	AMFNodeElementBase *Parent; ///< Parent element. If nullptr then this node is root.
 	AMFNodeElementBase *Parent; ///< Parent element. If nullptr then this node is root.
 	std::list<AMFNodeElementBase *> Child; ///< Child elements.
 	std::list<AMFNodeElementBase *> Child; ///< Child elements.
 
 
-public: /// Destructor, virtual..
+public:
+	/// Destructor, virtual..
 	virtual ~AMFNodeElementBase() = default;
 	virtual ~AMFNodeElementBase() = default;
 
 
 	/// Disabled copy constructor and co.
 	/// Disabled copy constructor and co.
@@ -98,15 +98,14 @@ public: /// Destructor, virtual..
 
 
 protected:
 protected:
 	/// In constructor inheritor must set element type.
 	/// In constructor inheritor must set element type.
-	/// \param [in] pType - element type.
+	/// \param [in] type - element type.
 	/// \param [in] pParent - parent element.
 	/// \param [in] pParent - parent element.
-	AMFNodeElementBase(const EType pType, AMFNodeElementBase *pParent) :
-			Type(pType), Parent(pParent) {
+	AMFNodeElementBase(EType type, AMFNodeElementBase *pParent) :
+			Type(type), Parent(pParent) {
 		// empty
 		// empty
 	}
 	}
 }; // class IAMFImporter_NodeElement
 }; // class IAMFImporter_NodeElement
 
 
-/// \struct CAMFImporter_NodeElement_Constellation
 /// A collection of objects or constellations with specific relative locations.
 /// A collection of objects or constellations with specific relative locations.
 struct AMFConstellation : public AMFNodeElementBase {
 struct AMFConstellation : public AMFNodeElementBase {
 	/// Constructor.
 	/// Constructor.
@@ -116,7 +115,6 @@ struct AMFConstellation : public AMFNodeElementBase {
 
 
 }; // struct CAMFImporter_NodeElement_Constellation
 }; // struct CAMFImporter_NodeElement_Constellation
 
 
-/// \struct CAMFImporter_NodeElement_Instance
 /// Part of constellation.
 /// Part of constellation.
 struct AMFInstance : public AMFNodeElementBase {
 struct AMFInstance : public AMFNodeElementBase {
 
 
@@ -135,12 +133,11 @@ struct AMFInstance : public AMFNodeElementBase {
 			AMFNodeElementBase(ENET_Instance, pParent) {}
 			AMFNodeElementBase(ENET_Instance, pParent) {}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_Metadata
 /// Structure that define metadata node.
 /// Structure that define metadata node.
 struct AMFMetadata : public AMFNodeElementBase {
 struct AMFMetadata : public AMFNodeElementBase {
 
 
-	std::string Type; ///< Type of "Value".
-	std::string Value; ///< Value.
+	std::string MetaType; ///< Type of "Value".
+	std::string Value;    ///< Value.
 
 
 	/// Constructor.
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	/// \param [in] pParent - pointer to parent node.
@@ -148,7 +145,6 @@ struct AMFMetadata : public AMFNodeElementBase {
 			AMFNodeElementBase(ENET_Metadata, pParent) {}
 			AMFNodeElementBase(ENET_Metadata, pParent) {}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_Root
 /// Structure that define root node.
 /// Structure that define root node.
 struct AMFRoot : public AMFNodeElementBase {
 struct AMFRoot : public AMFNodeElementBase {
 
 
@@ -161,7 +157,6 @@ struct AMFRoot : public AMFNodeElementBase {
 			AMFNodeElementBase(ENET_Root, pParent) {}
 			AMFNodeElementBase(ENET_Root, pParent) {}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_Color
 /// Structure that define object node.
 /// Structure that define object node.
 struct AMFColor : public AMFNodeElementBase {
 struct AMFColor : public AMFNodeElementBase {
 	bool Composed; ///< Type of color stored: if true then look for formula in \ref Color_Composed[4], else - in \ref Color.
 	bool Composed; ///< Type of color stored: if true then look for formula in \ref Color_Composed[4], else - in \ref Color.
@@ -177,7 +172,6 @@ struct AMFColor : public AMFNodeElementBase {
 	}
 	}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_Material
 /// Structure that define material node.
 /// Structure that define material node.
 struct AMFMaterial : public AMFNodeElementBase {
 struct AMFMaterial : public AMFNodeElementBase {
 
 
@@ -187,7 +181,6 @@ struct AMFMaterial : public AMFNodeElementBase {
 			AMFNodeElementBase(ENET_Material, pParent) {}
 			AMFNodeElementBase(ENET_Material, pParent) {}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_Object
 /// Structure that define object node.
 /// Structure that define object node.
 struct AMFObject : public AMFNodeElementBase {
 struct AMFObject : public AMFNodeElementBase {
 
 
@@ -206,7 +199,6 @@ struct AMFMesh : public AMFNodeElementBase {
 			AMFNodeElementBase(ENET_Mesh, pParent) {}
 			AMFNodeElementBase(ENET_Mesh, pParent) {}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_Vertex
 /// Structure that define vertex node.
 /// Structure that define vertex node.
 struct AMFVertex : public AMFNodeElementBase {
 struct AMFVertex : public AMFNodeElementBase {
 	/// Constructor.
 	/// Constructor.
@@ -215,7 +207,6 @@ struct AMFVertex : public AMFNodeElementBase {
 			AMFNodeElementBase(ENET_Vertex, pParent) {}
 			AMFNodeElementBase(ENET_Vertex, pParent) {}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_Edge
 /// Structure that define edge node.
 /// Structure that define edge node.
 struct AMFEdge : public AMFNodeElementBase {
 struct AMFEdge : public AMFNodeElementBase {
 	/// Constructor.
 	/// Constructor.
@@ -224,7 +215,6 @@ struct AMFEdge : public AMFNodeElementBase {
 			AMFNodeElementBase(ENET_Edge, pParent) {}
 			AMFNodeElementBase(ENET_Edge, pParent) {}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_Vertices
 /// Structure that define vertices node.
 /// Structure that define vertices node.
 struct AMFVertices : public AMFNodeElementBase {
 struct AMFVertices : public AMFNodeElementBase {
 	/// Constructor.
 	/// Constructor.
@@ -233,11 +223,10 @@ struct AMFVertices : public AMFNodeElementBase {
 			AMFNodeElementBase(ENET_Vertices, pParent) {}
 			AMFNodeElementBase(ENET_Vertices, pParent) {}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_Volume
 /// Structure that define volume node.
 /// Structure that define volume node.
 struct AMFVolume : public AMFNodeElementBase {
 struct AMFVolume : public AMFNodeElementBase {
 	std::string MaterialID; ///< Which material to use.
 	std::string MaterialID; ///< Which material to use.
-	std::string Type; ///< What this volume describes can be "region" or "support". If none specified, "object" is assumed.
+	std::string VolumeType; ///< What this volume describes can be "region" or "support". If none specified, "object" is assumed.
 
 
 	/// Constructor.
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	/// \param [in] pParent - pointer to parent node.
@@ -245,7 +234,6 @@ struct AMFVolume : public AMFNodeElementBase {
 			AMFNodeElementBase(ENET_Volume, pParent) {}
 			AMFNodeElementBase(ENET_Volume, pParent) {}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_Coordinates
 /// Structure that define coordinates node.
 /// Structure that define coordinates node.
 struct AMFCoordinates : public AMFNodeElementBase {
 struct AMFCoordinates : public AMFNodeElementBase {
 	aiVector3D Coordinate; ///< Coordinate.
 	aiVector3D Coordinate; ///< Coordinate.
@@ -256,7 +244,6 @@ struct AMFCoordinates : public AMFNodeElementBase {
 			AMFNodeElementBase(ENET_Coordinates, pParent) {}
 			AMFNodeElementBase(ENET_Coordinates, pParent) {}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_TexMap
 /// Structure that define texture coordinates node.
 /// Structure that define texture coordinates node.
 struct AMFTexMap : public AMFNodeElementBase {
 struct AMFTexMap : public AMFNodeElementBase {
 	aiVector3D TextureCoordinate[3]; ///< Texture coordinates.
 	aiVector3D TextureCoordinate[3]; ///< Texture coordinates.
@@ -273,7 +260,6 @@ struct AMFTexMap : public AMFNodeElementBase {
 	}
 	}
 };
 };
 
 
-/// \struct CAMFImporter_NodeElement_Triangle
 /// Structure that define triangle node.
 /// Structure that define triangle node.
 struct AMFTriangle : public AMFNodeElementBase {
 struct AMFTriangle : public AMFNodeElementBase {
 	size_t V[3]; ///< Triangle vertices.
 	size_t V[3]; ///< Triangle vertices.

+ 6 - 5
code/AssetLib/AMF/AMFImporter_Postprocess.cpp

@@ -1,9 +1,9 @@
-/*
+/*
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -224,7 +224,8 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &
     }
     }
 
 
     // Create format hint.
     // Create format hint.
-    strcpy(converted_texture.FormatHint, "rgba0000"); // copy initial string.
+    constexpr char templateColor[] = "rgba0000";
+    memcpy(converted_texture.FormatHint, templateColor, 8);
     if (!r.empty()) converted_texture.FormatHint[4] = '8';
     if (!r.empty()) converted_texture.FormatHint[4] = '8';
     if (!g.empty()) converted_texture.FormatHint[5] = '8';
     if (!g.empty()) converted_texture.FormatHint[5] = '8';
     if (!b.empty()) converted_texture.FormatHint[6] = '8';
     if (!b.empty()) converted_texture.FormatHint[6] = '8';
@@ -332,7 +333,7 @@ void AMFImporter::Postprocess_AddMetadata(const AMFMetaDataArray &metadataList,
     size_t meta_idx(0);
     size_t meta_idx(0);
 
 
     for (const AMFMetadata *metadata : metadataList) {
     for (const AMFMetadata *metadata : metadataList) {
-        sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata->Type, aiString(metadata->Value));
+        sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata->MetaType, aiString(metadata->Value));
     }
     }
 }
 }
 
 
@@ -867,7 +868,7 @@ nl_clean_loop:
             pScene->mTextures[idx]->mHeight = static_cast<unsigned int>(tex_convd.Height);
             pScene->mTextures[idx]->mHeight = static_cast<unsigned int>(tex_convd.Height);
             pScene->mTextures[idx]->pcData = (aiTexel *)tex_convd.Data;
             pScene->mTextures[idx]->pcData = (aiTexel *)tex_convd.Data;
             // texture format description.
             // texture format description.
-            strcpy(pScene->mTextures[idx]->achFormatHint, tex_convd.FormatHint);
+            strncpy(pScene->mTextures[idx]->achFormatHint, tex_convd.FormatHint, HINTMAXTEXTURELEN);
             idx++;
             idx++;
         } // for(const SPP_Texture& tex_convd: mTexture_Converted)
         } // for(const SPP_Texture& tex_convd: mTexture_Converted)
 
 

+ 20 - 15
code/AssetLib/ASE/ASELoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -124,7 +124,7 @@ void ASEImporter::InternReadFile(const std::string &pFile,
     // Allocate storage and copy the contents of the file to a memory buffer
     // Allocate storage and copy the contents of the file to a memory buffer
     std::vector<char> mBuffer2;
     std::vector<char> mBuffer2;
     TextFileToBuffer(file.get(), mBuffer2);
     TextFileToBuffer(file.get(), mBuffer2);
-
+    const size_t fileSize = mBuffer2.size();
     this->mBuffer = &mBuffer2[0];
     this->mBuffer = &mBuffer2[0];
     this->pcScene = pScene;
     this->pcScene = pScene;
 
 
@@ -146,7 +146,7 @@ void ASEImporter::InternReadFile(const std::string &pFile,
     };
     };
 
 
     // Construct an ASE parser and parse the file
     // Construct an ASE parser and parse the file
-    ASE::Parser parser(mBuffer, defaultFormat);
+    ASE::Parser parser(mBuffer, fileSize, defaultFormat);
     mParser = &parser;
     mParser = &parser;
     mParser->Parse();
     mParser->Parse();
 
 
@@ -446,10 +446,9 @@ void ASEImporter::BuildLights() {
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void ASEImporter::AddNodes(const std::vector<BaseNode *> &nodes,
-        aiNode *pcParent, const char *szName) {
+void ASEImporter::AddNodes(const std::vector<BaseNode *> &nodes, aiNode *pcParent, const std::string &name) {
     aiMatrix4x4 m;
     aiMatrix4x4 m;
-    AddNodes(nodes, pcParent, szName, m);
+    AddNodes(nodes, pcParent, name, m);
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -506,10 +505,9 @@ void ASEImporter::AddMeshes(const ASE::BaseNode *snode, aiNode *node) {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Add child nodes to a given parent node
 // Add child nodes to a given parent node
-void ASEImporter::AddNodes(const std::vector<BaseNode *> &nodes,
-        aiNode *pcParent, const char *szName,
+void ASEImporter::AddNodes(const std::vector<BaseNode *> &nodes, aiNode *pcParent, const std::string &name,
         const aiMatrix4x4 &mat) {
         const aiMatrix4x4 &mat) {
-    const size_t len = szName ? ::strlen(szName) : 0;
+    const size_t len = name.size();
     ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS);
     ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS);
 
 
     // Receives child nodes for the pcParent node
     // Receives child nodes for the pcParent node
@@ -519,16 +517,18 @@ void ASEImporter::AddNodes(const std::vector<BaseNode *> &nodes,
     // which has *us* as parent.
     // which has *us* as parent.
     for (std::vector<BaseNode *>::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) {
     for (std::vector<BaseNode *>::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) {
         const BaseNode *snode = *it;
         const BaseNode *snode = *it;
-        if (szName) {
-            if (len != snode->mParent.length() || ::strcmp(szName, snode->mParent.c_str()))
+        if (!name.empty()) {
+            if (len != snode->mParent.length() || name != snode->mParent.c_str()) {
                 continue;
                 continue;
-        } else if (snode->mParent.length())
+            }
+        } else if (snode->mParent.length()) {
             continue;
             continue;
+        }
 
 
         (*it)->mProcessed = true;
         (*it)->mProcessed = true;
 
 
         // Allocate a new node and add it to the output data structure
         // Allocate a new node and add it to the output data structure
-        apcNodes.push_back(new aiNode());
+        apcNodes.push_back(new aiNode);
         aiNode *node = apcNodes.back();
         aiNode *node = apcNodes.back();
 
 
         node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node"));
         node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node"));
@@ -541,7 +541,7 @@ void ASEImporter::AddNodes(const std::vector<BaseNode *> &nodes,
 
 
         // Add sub nodes - prevent stack overflow due to recursive parenting
         // Add sub nodes - prevent stack overflow due to recursive parenting
         if (node->mName != node->mParent->mName && node->mName != node->mParent->mParent->mName) {
         if (node->mName != node->mParent->mName && node->mName != node->mParent->mParent->mName) {
-            AddNodes(nodes, node, node->mName.data, snode->mTransform);
+            AddNodes(nodes, node, node->mName.C_Str(), snode->mTransform);
         }
         }
 
 
         // Further processing depends on the type of the node
         // Further processing depends on the type of the node
@@ -619,7 +619,8 @@ void ASEImporter::BuildNodes(std::vector<BaseNode *> &nodes) {
     }
     }
 
 
     // add all nodes
     // add all nodes
-    AddNodes(nodes, ch, nullptr);
+    static const std::string none = "";
+    AddNodes(nodes, ch, none);
 
 
     // now iterate through al nodes and find those that have not yet
     // now iterate through al nodes and find those that have not yet
     // been added to the nodegraph (= their parent could not be recognized)
     // been added to the nodegraph (= their parent could not be recognized)
@@ -730,6 +731,10 @@ void ASEImporter::BuildUniqueRepresentation(ASE::Mesh &mesh) {
     unsigned int iCurrent = 0, fi = 0;
     unsigned int iCurrent = 0, fi = 0;
     for (std::vector<ASE::Face>::iterator i = mesh.mFaces.begin(); i != mesh.mFaces.end(); ++i, ++fi) {
     for (std::vector<ASE::Face>::iterator i = mesh.mFaces.begin(); i != mesh.mFaces.end(); ++i, ++fi) {
         for (unsigned int n = 0; n < 3; ++n, ++iCurrent) {
         for (unsigned int n = 0; n < 3; ++n, ++iCurrent) {
+            const uint32_t curIndex = (*i).mIndices[n];
+            if (curIndex >= mesh.mPositions.size()) {
+                throw DeadlyImportError("ASE: Invalid vertex index in face ", fi, ".");
+            }
             mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]];
             mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]];
 
 
             // add texture coordinates
             // add texture coordinates

+ 4 - 5
code/AssetLib/ASE/ASELoader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -153,13 +153,13 @@ private:
      *  \param matrix Current transform
      *  \param matrix Current transform
      */
      */
     void AddNodes(const std::vector<ASE::BaseNode*>& nodes,
     void AddNodes(const std::vector<ASE::BaseNode*>& nodes,
-        aiNode* pcParent,const char* szName);
+        aiNode* pcParent, const std::string &name);
 
 
     void AddNodes(const std::vector<ASE::BaseNode*>& nodes,
     void AddNodes(const std::vector<ASE::BaseNode*>& nodes,
-        aiNode* pcParent,const char* szName,
+        aiNode* pcParent, const std::string &name,
         const aiMatrix4x4& matrix);
         const aiMatrix4x4& matrix);
 
 
-    void AddMeshes(const ASE::BaseNode* snode,aiNode* node);
+    void AddMeshes(const ASE::BaseNode* snode, aiNode* node);
 
 
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     /** Generate a default material and add it to the parser's list
     /** Generate a default material and add it to the parser's list
@@ -188,5 +188,4 @@ protected:
 
 
 } // end of namespace Assimp
 } // end of namespace Assimp
 
 
-
 #endif // AI_3DSIMPORTER_H_INC
 #endif // AI_3DSIMPORTER_H_INC

Разница между файлами не показана из-за своего большого размера
+ 214 - 190
code/AssetLib/ASE/ASEParser.cpp


+ 11 - 8
code/AssetLib/ASE/ASEParser.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -391,11 +391,11 @@ public:
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     //! Construct a parser from a given input file which is
     //! Construct a parser from a given input file which is
     //! guaranteed to be terminated with zero.
     //! guaranteed to be terminated with zero.
-    //! @param szFile Input file
+    //! @param file              The name of the input file.
     //! @param fileFormatDefault Assumed file format version. If the
     //! @param fileFormatDefault Assumed file format version. If the
     //!   file format is specified in the file the new value replaces
     //!   file format is specified in the file the new value replaces
     //!   the default value.
     //!   the default value.
-    Parser(const char *szFile, unsigned int fileFormatDefault);
+    Parser(const char *file, size_t fileLen, unsigned int fileFormatDefault);
 
 
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     //! Parses the file into the parsers internal representation
     //! Parses the file into the parsers internal representation
@@ -553,13 +553,15 @@ private:
     //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL  ...)
     //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL  ...)
     //! \param apOut Output buffer (3 floats)
     //! \param apOut Output buffer (3 floats)
     //! \param rIndexOut Output index
     //! \param rIndexOut Output index
-    void ParseLV4MeshFloatTriple(ai_real *apOut, unsigned int &rIndexOut);
+    void ParseLV4MeshRealTriple(ai_real *apOut, unsigned int &rIndexOut);
+    void ParseLV4MeshFloatTriple(float *apOut, unsigned int &rIndexOut);
 
 
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     //! Parse a *MESH_VERT block in a file
     //! Parse a *MESH_VERT block in a file
     //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL  ...)
     //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL  ...)
     //! \param apOut Output buffer (3 floats)
     //! \param apOut Output buffer (3 floats)
-    void ParseLV4MeshFloatTriple(ai_real *apOut);
+    void ParseLV4MeshRealTriple(ai_real *apOut);
+    void ParseLV4MeshFloatTriple(float *apOut);
 
 
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     //! Parse a *MESH_TFACE block in a file
     //! Parse a *MESH_TFACE block in a file
@@ -577,7 +579,8 @@ private:
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     //! Parse a single float element
     //! Parse a single float element
     //! \param fOut Output float
     //! \param fOut Output float
-    void ParseLV4MeshFloat(ai_real &fOut);
+    void ParseLV4MeshReal(ai_real &fOut);
+    void ParseLV4MeshFloat(float &fOut);
 
 
     // -------------------------------------------------------------------
     // -------------------------------------------------------------------
     //! Parse a single int element
     //! Parse a single int element
@@ -617,8 +620,8 @@ private:
     bool ParseString(std::string &out, const char *szName);
     bool ParseString(std::string &out, const char *szName);
 
 
 public:
 public:
-    //! Pointer to current data
-    const char *filePtr;
+    const char *mFilePtr; ////< Pointer to current data
+    const char *mEnd;     ///< The end pointer of the file data
 
 
     //! background color to be passed to the viewer
     //! background color to be passed to the viewer
     //! QNAN if none was found
     //! QNAN if none was found

+ 1 - 1
code/AssetLib/Assbin/AssbinExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 1 - 1
code/AssetLib/Assbin/AssbinExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 1 - 1
code/AssetLib/Assbin/AssbinFileWriter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 1 - 1
code/AssetLib/Assbin/AssbinFileWriter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 11 - 4
code/AssetLib/Assbin/AssbinLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
 #ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
 
 
 // internal headers
 // internal headers
-#include "AssetLib/Assbin/AssbinLoader.h"
+#include "AssbinLoader.h"
 #include "Common/assbin_chunks.h"
 #include "Common/assbin_chunks.h"
 #include <assimp/MemoryIOWrapper.h>
 #include <assimp/MemoryIOWrapper.h>
 #include <assimp/anim.h>
 #include <assimp/anim.h>
@@ -91,10 +91,14 @@ bool AssbinImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, boo
     }
     }
 
 
     char s[32];
     char s[32];
-    in->Read(s, sizeof(char), 32);
+    const size_t read = in->Read(s, sizeof(char), 32);
 
 
     pIOHandler->Close(in);
     pIOHandler->Close(in);
 
 
+    if (read < 19) {
+      return false;
+    }
+
     return strncmp(s, "ASSIMP.binary-dump.", 19) == 0;
     return strncmp(s, "ASSIMP.binary-dump.", 19) == 0;
 }
 }
 
 
@@ -684,6 +688,7 @@ void AssbinImporter::InternReadFile(const std::string &pFile, aiScene *pScene, I
     unsigned int versionMajor = Read<unsigned int>(stream);
     unsigned int versionMajor = Read<unsigned int>(stream);
     unsigned int versionMinor = Read<unsigned int>(stream);
     unsigned int versionMinor = Read<unsigned int>(stream);
     if (versionMinor != ASSBIN_VERSION_MINOR || versionMajor != ASSBIN_VERSION_MAJOR) {
     if (versionMinor != ASSBIN_VERSION_MINOR || versionMajor != ASSBIN_VERSION_MAJOR) {
+        pIOHandler->Close(stream);
         throw DeadlyImportError("Invalid version, data format not compatible!");
         throw DeadlyImportError("Invalid version, data format not compatible!");
     }
     }
 
 
@@ -693,8 +698,10 @@ void AssbinImporter::InternReadFile(const std::string &pFile, aiScene *pScene, I
     shortened = Read<uint16_t>(stream) > 0;
     shortened = Read<uint16_t>(stream) > 0;
     compressed = Read<uint16_t>(stream) > 0;
     compressed = Read<uint16_t>(stream) > 0;
 
 
-    if (shortened)
+    if (shortened) {
+        pIOHandler->Close(stream);
         throw DeadlyImportError("Shortened binaries are not supported!");
         throw DeadlyImportError("Shortened binaries are not supported!");
+    }
 
 
     stream->Seek(256, aiOrigin_CUR); // original filename
     stream->Seek(256, aiOrigin_CUR); // original filename
     stream->Seek(128, aiOrigin_CUR); // options
     stream->Seek(128, aiOrigin_CUR); // options

+ 2 - 3
code/AssetLib/Assbin/AssbinLoader.h

@@ -1,9 +1,8 @@
-
 /*
 /*
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -61,7 +60,7 @@ struct aiCamera;
 
 
 #ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
 #ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
 
 
-namespace Assimp    {
+namespace Assimp {
 
 
 // ---------------------------------------------------------------------------------
 // ---------------------------------------------------------------------------------
 /** Importer class for 3D Studio r3 and r4 3DS files
 /** Importer class for 3D Studio r3 and r4 3DS files

+ 2 - 3
code/AssetLib/Assxml/AssxmlExporter.cpp

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -50,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/IOSystem.hpp>
 #include <assimp/IOSystem.hpp>
 #include <assimp/Exporter.hpp>
 #include <assimp/Exporter.hpp>
 
 
-namespace Assimp   {
+namespace Assimp {
 
 
 void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
 void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
 {
 {

+ 1 - 2
code/AssetLib/Assxml/AssxmlExporter.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 2 - 4
code/AssetLib/Assxml/AssxmlFileWriter.cpp

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -36,7 +35,6 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 */
 */
 
 
@@ -223,7 +221,7 @@ static void WriteDump(const char *pFile, const char *cmd, const aiScene *scene,
     const unsigned int majorVersion(aiGetVersionMajor());
     const unsigned int majorVersion(aiGetVersionMajor());
     const unsigned int minorVersion(aiGetVersionMinor());
     const unsigned int minorVersion(aiGetVersionMinor());
     const unsigned int rev(aiGetVersionRevision());
     const unsigned int rev(aiGetVersionRevision());
-    const char *curtime(asctime(p));
+    const char *curtime = asctime(p);
     ioprintf(io, header.c_str(), majorVersion, minorVersion, rev, pFile, c.c_str(), curtime, scene->mFlags, 0u);
     ioprintf(io, header.c_str(), majorVersion, minorVersion, rev, pFile, c.c_str(), curtime, scene->mFlags, 0u);
 
 
     // write the node graph
     // write the node graph

+ 1 - 1
code/AssetLib/Assxml/AssxmlFileWriter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 2 - 2
code/AssetLib/B3D/B3DImporter.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -46,7 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
 #ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
 
 
 // internal headers
 // internal headers
-#include "AssetLib/B3D/B3DImporter.h"
+#include "B3DImporter.h"
 #include "PostProcessing/ConvertToLHProcess.h"
 #include "PostProcessing/ConvertToLHProcess.h"
 #include "PostProcessing/TextureTransform.h"
 #include "PostProcessing/TextureTransform.h"
 
 

+ 1 - 1
code/AssetLib/B3D/B3DImporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 4 - 8
code/AssetLib/BVH/BVHLoader.cpp

@@ -4,9 +4,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -85,11 +83,9 @@ BVHLoader::BVHLoader() :
         mLine(),
         mLine(),
         mAnimTickDuration(),
         mAnimTickDuration(),
         mAnimNumFrames(),
         mAnimNumFrames(),
-        noSkeletonMesh() {}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well
-BVHLoader::~BVHLoader() = default;
+        noSkeletonMesh() {
+    // empty
+}
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
 // Returns whether the class can handle the format of the given file.

+ 8 - 14
code/AssetLib/BVH/BVHLoader.h

@@ -4,8 +4,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -80,32 +79,27 @@ class BVHLoader : public BaseImporter {
         std::vector<ChannelType> mChannels;
         std::vector<ChannelType> mChannels;
         std::vector<float> mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames
         std::vector<float> mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames
 
 
-        Node() :
-                mNode(nullptr) {}
-
-        explicit Node(const aiNode *pNode) :
-                mNode(pNode) {}
+        Node() : mNode(nullptr) {}
+        explicit Node(const aiNode *pNode) :mNode(pNode) {}
     };
     };
 
 
 public:
 public:
     BVHLoader();
     BVHLoader();
-    ~BVHLoader();
+    ~BVHLoader() override = default;
 
 
-public:
     /** Returns whether the class can handle the format of the given file.
     /** Returns whether the class can handle the format of the given file.
      * See BaseImporter::CanRead() for details. */
      * See BaseImporter::CanRead() for details. */
-    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const override;
 
 
-    void SetupProperties(const Importer *pImp);
-    const aiImporterDesc *GetInfo() const;
+    void SetupProperties(const Importer *pImp) override;
+    const aiImporterDesc *GetInfo() const override;
 
 
 protected:
 protected:
     /** Imports the given file into the given scene structure.
     /** Imports the given file into the given scene structure.
      * See BaseImporter::InternReadFile() for details
      * See BaseImporter::InternReadFile() for details
      */
      */
-    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
 
 
-protected:
     /** Reads the file */
     /** Reads the file */
     void ReadStructure(aiScene *pScene);
     void ReadStructure(aiScene *pScene);
 
 

+ 1 - 1
code/AssetLib/Blender/BlenderBMesh.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,

+ 1 - 1
code/AssetLib/Blender/BlenderBMesh.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,

+ 1 - 2
code/AssetLib/Blender/BlenderDNA.cpp

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 1 - 2
code/AssetLib/Blender/BlenderDNA.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 4 - 2
code/AssetLib/Blender/BlenderDNA.inl

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -841,5 +841,7 @@ template <template <typename> class TOUT> template <typename T> void ObjectCache
 #endif
 #endif
 }
 }
 
 
-}}
+}
+}
+
 #endif
 #endif

+ 3 - 4
code/AssetLib/Blender/BlenderIntermediate.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -117,7 +116,7 @@ namespace Blender {
         mywrap arr;
         mywrap arr;
     };
     };
 
 
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && _MSC_VER < 1900
 #   pragma warning(disable:4351)
 #   pragma warning(disable:4351)
 #endif
 #endif
 
 
@@ -172,7 +171,7 @@ namespace Blender {
         // original file data
         // original file data
         const FileDatabase& db;
         const FileDatabase& db;
     };
     };
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && _MSC_VER < 1900
 #   pragma warning(default:4351)
 #   pragma warning(default:4351)
 #endif
 #endif
 
 

+ 7 - 8
code/AssetLib/Blender/BlenderLoader.cpp

@@ -1,10 +1,8 @@
-
 /*
 /*
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -110,7 +108,7 @@ BlenderImporter::~BlenderImporter() {
     delete modifier_cache;
     delete modifier_cache;
 }
 }
 
 
-static const char Token[] = "BLENDER";
+static constexpr char Token[] = "BLENDER";
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
 // Returns whether the class can handle the format of the given file.
@@ -359,7 +357,7 @@ void BlenderImporter::ResolveImage(aiMaterial *out, const Material *mat, const M
     // check if the file contents are bundled with the BLEND file
     // check if the file contents are bundled with the BLEND file
     if (img->packedfile) {
     if (img->packedfile) {
         name.data[0] = '*';
         name.data[0] = '*';
-        name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast<unsigned int>(MAXLEN - 1), static_cast<int32_t>(conv_data.textures->size()));
+        name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast<unsigned int>(AI_MAXLEN - 1), static_cast<int32_t>(conv_data.textures->size()));
 
 
         conv_data.textures->push_back(new aiTexture());
         conv_data.textures->push_back(new aiTexture());
         aiTexture *curTex = conv_data.textures->back();
         aiTexture *curTex = conv_data.textures->back();
@@ -433,7 +431,7 @@ void BlenderImporter::AddSentinelTexture(aiMaterial *out, const Material *mat, c
     (void)conv_data;
     (void)conv_data;
 
 
     aiString name;
     aiString name;
-    name.length = ai_snprintf(name.data, MAXLEN, "Procedural,num=%i,type=%s", conv_data.sentinel_cnt++,
+    name.length = ai_snprintf(name.data, AI_MAXLEN, "Procedural,num=%i,type=%s", conv_data.sentinel_cnt++,
             GetTextureTypeDisplayString(tex->tex->type));
             GetTextureTypeDisplayString(tex->tex->type));
     out->AddProperty(&name, AI_MATKEY_TEXTURE_DIFFUSE(
     out->AddProperty(&name, AI_MATKEY_TEXTURE_DIFFUSE(
                                     conv_data.next_texture[aiTextureType_DIFFUSE]++));
                                     conv_data.next_texture[aiTextureType_DIFFUSE]++));
@@ -495,8 +493,9 @@ void BlenderImporter::BuildDefaultMaterial(Blender::ConversionData &conv_data) {
             if (index == static_cast<unsigned int>(-1)) {
             if (index == static_cast<unsigned int>(-1)) {
                 // Setup a default material.
                 // Setup a default material.
                 std::shared_ptr<Material> p(new Material());
                 std::shared_ptr<Material> p(new Material());
-                ai_assert(::strlen(AI_DEFAULT_MATERIAL_NAME) < sizeof(p->id.name) - 2);
-                strcpy(p->id.name + 2, AI_DEFAULT_MATERIAL_NAME);
+                const size_t len = ::strlen(AI_DEFAULT_MATERIAL_NAME);
+                ai_assert(len < sizeof(p->id.name) - 2);
+                memcpy(p->id.name + 2, AI_DEFAULT_MATERIAL_NAME, len);
 
 
                 // Note: MSVC11 does not zero-initialize Material here, although it should.
                 // Note: MSVC11 does not zero-initialize Material here, although it should.
                 // Thus all relevant fields should be explicitly initialized. We cannot add
                 // Thus all relevant fields should be explicitly initialized. We cannot add

+ 1 - 1
code/AssetLib/Blender/BlenderLoader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 1 - 2
code/AssetLib/Blender/BlenderModifier.cpp

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 1 - 2
code/AssetLib/Blender/BlenderModifier.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 1 - 2
code/AssetLib/Blender/BlenderScene.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 1 - 1
code/AssetLib/Blender/BlenderSceneGen.h

@@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "BlenderDNA.h"
 #include "BlenderDNA.h"
 #include "BlenderScene.h"
 #include "BlenderScene.h"
 
 
-namespace Assimp    {
+namespace Assimp {
 namespace Blender {
 namespace Blender {
 
 
 template <> void Structure :: Convert<Object> (
 template <> void Structure :: Convert<Object> (

+ 1 - 1
code/AssetLib/Blender/BlenderTessellator.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 1 - 1
code/AssetLib/Blender/BlenderTessellator.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 10 - 22
code/AssetLib/C4D/C4DImporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2025, assimp team
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -46,10 +46,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // no #ifdefing here, Cinema4D support is carried out in a branch of assimp
 // no #ifdefing here, Cinema4D support is carried out in a branch of assimp
 // where it is turned on in the CMake settings.
 // where it is turned on in the CMake settings.
 
 
-#ifndef _MSC_VER
-#   error C4D support is currently MSVC only
-#endif
-
 #include "C4DImporter.h"
 #include "C4DImporter.h"
 #include <memory>
 #include <memory>
 #include <assimp/IOSystem.hpp>
 #include <assimp/IOSystem.hpp>
@@ -68,7 +64,7 @@ namespace {
 
 
 aiString aiStringFrom(cineware::String const & cinestring) {
 aiString aiStringFrom(cineware::String const & cinestring) {
     aiString result;
     aiString result;
-    cinestring.GetCString(result.data, MAXLEN-1);
+    cinestring.GetCString(result.data, AI_MAXLEN - 1);
     result.length = static_cast<ai_uint32>(cinestring.GetLength());
     result.length = static_cast<ai_uint32>(cinestring.GetLength());
     return result;
     return result;
 }
 }
@@ -90,7 +86,7 @@ namespace Assimp {
     }
     }
 }
 }
 
 
-static const aiImporterDesc desc = {
+static constexpr aiImporterDesc desc = {
     "Cinema4D Importer",
     "Cinema4D Importer",
     "",
     "",
     "",
     "",
@@ -103,15 +99,8 @@ static const aiImporterDesc desc = {
     "c4d"
     "c4d"
 };
 };
 
 
-
-// ------------------------------------------------------------------------------------------------
-C4DImporter::C4DImporter() = default;
-
-// ------------------------------------------------------------------------------------------------
-C4DImporter::~C4DImporter() = default;
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-bool C4DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const {
+bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
     const std::string& extension = GetExtension(pFile);
     const std::string& extension = GetExtension(pFile);
     if (extension == "c4d") {
     if (extension == "c4d") {
         return true;
         return true;
@@ -200,7 +189,6 @@ void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
     std::copy(materials.begin(), materials.end(), pScene->mMaterials);
     std::copy(materials.begin(), materials.end(), pScene->mMaterials);
 }
 }
 
 
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 bool C4DImporter::ReadShader(aiMaterial* out, BaseShader* shader) {
 bool C4DImporter::ReadShader(aiMaterial* out, BaseShader* shader) {
     // based on Cineware sample code (C4DImportExport.cpp)
     // based on Cineware sample code (C4DImportExport.cpp)
@@ -305,7 +293,7 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent) {
 
 
     // based on Cineware sample code
     // based on Cineware sample code
     while (object) {
     while (object) {
-        const LONG type = object->GetType();
+        const Int32 type = object->GetType();
         const Matrix& ml = object->GetMl();
         const Matrix& ml = object->GetMl();
 
 
         aiNode* const nd = new aiNode();
         aiNode* const nd = new aiNode();
@@ -368,8 +356,8 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object) {
     PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
     PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
     ai_assert(polyObject != nullptr);
     ai_assert(polyObject != nullptr);
 
 
-    const LONG pointCount = polyObject->GetPointCount();
-    const LONG polyCount = polyObject->GetPolygonCount();
+    const Int32 pointCount = polyObject->GetPointCount();
+    const Int32 polyCount = polyObject->GetPolygonCount();
     if(!polyObject || !pointCount) {
     if(!polyObject || !pointCount) {
         LogWarn("ignoring mesh with zero vertices or faces");
         LogWarn("ignoring mesh with zero vertices or faces");
         return nullptr;
         return nullptr;
@@ -391,7 +379,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object) {
     unsigned int vcount = 0;
     unsigned int vcount = 0;
 
 
     // first count vertices
     // first count vertices
-    for (LONG i = 0; i < polyCount; i++)
+    for (Int32 i = 0; i < polyCount; i++)
     {
     {
         vcount += 3;
         vcount += 3;
 
 
@@ -434,7 +422,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object) {
     }
     }
 
 
     // copy vertices and extra channels over and populate faces
     // copy vertices and extra channels over and populate faces
-    for (LONG i = 0; i < polyCount; ++i, ++face) {
+    for (Int32 i = 0; i < polyCount; ++i, ++face) {
         ai_assert(polys[i].a < pointCount && polys[i].a >= 0);
         ai_assert(polys[i].a < pointCount && polys[i].a >= 0);
         const Vector& pointA = points[polys[i].a];
         const Vector& pointA = points[polys[i].a];
         verts->x = pointA.x;
         verts->x = pointA.x;
@@ -511,7 +499,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object) {
         if (tangents_src) {
         if (tangents_src) {
 
 
             for(unsigned int k = 0; k < face->mNumIndices; ++k) {
             for(unsigned int k = 0; k < face->mNumIndices; ++k) {
-                LONG l;
+                Int32 l;
                 switch(k) {
                 switch(k) {
                 case 0:
                 case 0:
                     l = polys[i].a;
                     l = polys[i].a;

+ 3 - 1
code/AssetLib/C4D/C4DImporter.h

@@ -63,7 +63,7 @@ namespace cineware {
     class BaseShader;
     class BaseShader;
 }
 }
 
 
-namespace Assimp  {
+namespace Assimp {
     // TinyFormatter.h
     // TinyFormatter.h
     namespace Formatter {
     namespace Formatter {
         template <typename T,typename TR, typename A> class basic_formatter;
         template <typename T,typename TR, typename A> class basic_formatter;
@@ -78,6 +78,8 @@ namespace Assimp  {
 // -------------------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------------------
 class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter> {
 class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter> {
 public:
 public:
+    C4DImporter() = default;
+    ~C4DImporter() override = default;
     bool CanRead( const std::string& pFile, IOSystem*, bool checkSig) const override;
     bool CanRead( const std::string& pFile, IOSystem*, bool checkSig) const override;
 
 
 protected:
 protected:

+ 30 - 24
code/AssetLib/COB/COBLoader.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -45,8 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #ifndef ASSIMP_BUILD_NO_COB_IMPORTER
 #ifndef ASSIMP_BUILD_NO_COB_IMPORTER
 
 
-#include "AssetLib/COB/COBLoader.h"
-#include "AssetLib/COB/COBScene.h"
+#include "COBLoader.h"
+#include "COBScene.h"
 #include "PostProcessing/ConvertToLHProcess.h"
 #include "PostProcessing/ConvertToLHProcess.h"
 
 
 #include <assimp/LineSplitter.h>
 #include <assimp/LineSplitter.h>
@@ -228,7 +228,7 @@ aiNode *COBImporter::BuildNodes(const Node &root, const Scene &scin, aiScene *fi
         const Mesh &ndmesh = (const Mesh &)(root);
         const Mesh &ndmesh = (const Mesh &)(root);
         if (ndmesh.vertex_positions.size() && ndmesh.texture_coords.size()) {
         if (ndmesh.vertex_positions.size() && ndmesh.texture_coords.size()) {
 
 
-            typedef std::pair<const unsigned int, Mesh::FaceRefList> Entry;
+            using Entry = std::pair<const unsigned int, Mesh::FaceRefList>;
             for (const Entry &reflist : ndmesh.temp_map) {
             for (const Entry &reflist : ndmesh.temp_map) {
                 { // create mesh
                 { // create mesh
                     size_t n = 0;
                     size_t n = 0;
@@ -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;
@@ -473,8 +475,9 @@ void COBImporter::ReadBasicNodeInfo_Ascii(Node &msh, LineSplitter &splitter, con
         } else if (splitter.match_start("Transform")) {
         } else if (splitter.match_start("Transform")) {
             for (unsigned int y = 0; y < 4 && ++splitter; ++y) {
             for (unsigned int y = 0; y < 4 && ++splitter; ++y) {
                 const char *s = splitter->c_str();
                 const char *s = splitter->c_str();
+                const char *end = s + splitter->size();
                 for (unsigned int x = 0; x < 4; ++x) {
                 for (unsigned int x = 0; x < 4; ++x) {
-                    SkipSpaces(&s);
+                    SkipSpaces(&s, end);
                     msh.transform[y][x] = fast_atof(&s);
                     msh.transform[y][x] = fast_atof(&s);
                 }
                 }
             }
             }
@@ -486,12 +489,12 @@ void COBImporter::ReadBasicNodeInfo_Ascii(Node &msh, LineSplitter &splitter, con
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 template <typename T>
 template <typename T>
-void COBImporter::ReadFloat3Tuple_Ascii(T &fill, const char **in) {
+void COBImporter::ReadFloat3Tuple_Ascii(T &fill, const char **in, const char *end) {
     const char *rgb = *in;
     const char *rgb = *in;
     for (unsigned int i = 0; i < 3; ++i) {
     for (unsigned int i = 0; i < 3; ++i) {
-        SkipSpaces(&rgb);
+        SkipSpaces(&rgb, end);
         if (*rgb == ',') ++rgb;
         if (*rgb == ',') ++rgb;
-        SkipSpaces(&rgb);
+        SkipSpaces(&rgb, end);
 
 
         fill[i] = fast_atof(&rgb);
         fill[i] = fast_atof(&rgb);
     }
     }
@@ -538,7 +541,7 @@ void COBImporter::ReadMat1_Ascii(Scene &out, LineSplitter &splitter, const Chunk
     }
     }
 
 
     const char *rgb = splitter[1];
     const char *rgb = splitter[1];
-    ReadFloat3Tuple_Ascii(mat.rgb, &rgb);
+    ReadFloat3Tuple_Ascii(mat.rgb, &rgb, splitter.getEnd());
 
 
     ++splitter;
     ++splitter;
     if (!splitter.match_start("alpha ")) {
     if (!splitter.match_start("alpha ")) {
@@ -617,20 +620,21 @@ void COBImporter::ReadLght_Ascii(Scene &out, LineSplitter &splitter, const Chunk
     }
     }
 
 
     const char *rgb = splitter[1];
     const char *rgb = splitter[1];
-    ReadFloat3Tuple_Ascii(msh.color, &rgb);
+    const char *end = splitter.getEnd();
+    ReadFloat3Tuple_Ascii(msh.color, &rgb, end);
 
 
-    SkipSpaces(&rgb);
+    SkipSpaces(&rgb, end);
     if (strncmp(rgb, "cone angle", 10) != 0) {
     if (strncmp(rgb, "cone angle", 10) != 0) {
         ASSIMP_LOG_WARN("Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id);
         ASSIMP_LOG_WARN("Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id);
     }
     }
-    SkipSpaces(rgb + 10, &rgb);
+    SkipSpaces(rgb + 10, &rgb, end);
     msh.angle = fast_atof(&rgb);
     msh.angle = fast_atof(&rgb);
 
 
-    SkipSpaces(&rgb);
+    SkipSpaces(&rgb, end);
     if (strncmp(rgb, "inner angle", 11) != 0) {
     if (strncmp(rgb, "inner angle", 11) != 0) {
         ASSIMP_LOG_WARN("Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id);
         ASSIMP_LOG_WARN("Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id);
     }
     }
-    SkipSpaces(rgb + 11, &rgb);
+    SkipSpaces(rgb + 11, &rgb, end);
     msh.inner_angle = fast_atof(&rgb);
     msh.inner_angle = fast_atof(&rgb);
 
 
     // skip the rest for we can't handle this kind of physically-based lighting information.
     // skip the rest for we can't handle this kind of physically-based lighting information.
@@ -703,14 +707,14 @@ void COBImporter::ReadPolH_Ascii(Scene &out, LineSplitter &splitter, const Chunk
 
 
             for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) {
             for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) {
                 const char *s = splitter->c_str();
                 const char *s = splitter->c_str();
-
+                const char *end = splitter.getEnd();
                 aiVector3D &v = msh.vertex_positions[cur];
                 aiVector3D &v = msh.vertex_positions[cur];
 
 
-                SkipSpaces(&s);
+                SkipSpaces(&s, end);
                 v.x = fast_atof(&s);
                 v.x = fast_atof(&s);
-                SkipSpaces(&s);
+                SkipSpaces(&s, end);
                 v.y = fast_atof(&s);
                 v.y = fast_atof(&s);
-                SkipSpaces(&s);
+                SkipSpaces(&s, end);
                 v.z = fast_atof(&s);
                 v.z = fast_atof(&s);
             }
             }
         } else if (splitter.match_start("Texture Vertices")) {
         } else if (splitter.match_start("Texture Vertices")) {
@@ -719,12 +723,13 @@ void COBImporter::ReadPolH_Ascii(Scene &out, LineSplitter &splitter, const Chunk
 
 
             for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) {
             for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) {
                 const char *s = splitter->c_str();
                 const char *s = splitter->c_str();
+                const char *end = splitter.getEnd();
 
 
                 aiVector2D &v = msh.texture_coords[cur];
                 aiVector2D &v = msh.texture_coords[cur];
 
 
-                SkipSpaces(&s);
+                SkipSpaces(&s, end);
                 v.x = fast_atof(&s);
                 v.x = fast_atof(&s);
-                SkipSpaces(&s);
+                SkipSpaces(&s, end);
                 v.y = fast_atof(&s);
                 v.y = fast_atof(&s);
             }
             }
         } else if (splitter.match_start("Faces")) {
         } else if (splitter.match_start("Faces")) {
@@ -749,8 +754,9 @@ void COBImporter::ReadPolH_Ascii(Scene &out, LineSplitter &splitter, const Chunk
                 face.material = strtoul10(splitter[6]);
                 face.material = strtoul10(splitter[6]);
 
 
                 const char *s = (++splitter)->c_str();
                 const char *s = (++splitter)->c_str();
+                const char *end = splitter.getEnd();
                 for (size_t i = 0; i < face.indices.size(); ++i) {
                 for (size_t i = 0; i < face.indices.size(); ++i) {
-                    if (!SkipSpaces(&s)) {
+                    if (!SkipSpaces(&s, end)) {
                         ThrowException("Expected EOL token in Face entry");
                         ThrowException("Expected EOL token in Face entry");
                     }
                     }
                     if ('<' != *s++) {
                     if ('<' != *s++) {

+ 2 - 3
code/AssetLib/COB/COBLoader.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -120,7 +119,7 @@ private:
     void ReadChunkInfo_Ascii(COB::ChunkInfo &out, const LineSplitter &splitter);
     void ReadChunkInfo_Ascii(COB::ChunkInfo &out, const LineSplitter &splitter);
     void ReadBasicNodeInfo_Ascii(COB::Node &msh, LineSplitter &splitter, const COB::ChunkInfo &nfo);
     void ReadBasicNodeInfo_Ascii(COB::Node &msh, LineSplitter &splitter, const COB::ChunkInfo &nfo);
     template <typename T>
     template <typename T>
-    void ReadFloat3Tuple_Ascii(T &fill, const char **in);
+    void ReadFloat3Tuple_Ascii(T &fill, const char **in, const char *end);
 
 
     void ReadPolH_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
     void ReadPolH_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
     void ReadBitM_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
     void ReadBitM_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);

+ 1 - 1
code/AssetLib/COB/COBScene.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 65 - 52
code/AssetLib/CSM/CSMLoader.cpp

@@ -3,9 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -73,40 +71,35 @@ static constexpr aiImporterDesc desc = {
     "csm"
     "csm"
 };
 };
 
 
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 // Constructor to be privately used by Importer
-CSMImporter::CSMImporter() : noSkeletonMesh(){
+CSMImporter::CSMImporter() : noSkeletonMesh() {
     // empty
     // empty
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
 // Returns whether the class can handle the format of the given file.
-bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const
-{
+bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const {
     static const char* tokens[] = {"$Filename"};
     static const char* tokens[] = {"$Filename"};
     return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens));
     return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens));
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Build a string of all file extensions supported
 // Build a string of all file extensions supported
-const aiImporterDesc* CSMImporter::GetInfo () const
-{
+const aiImporterDesc* CSMImporter::GetInfo () const {
     return &desc;
     return &desc;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Setup configuration properties for the loader
 // Setup configuration properties for the loader
-void CSMImporter::SetupProperties(const Importer* pImp)
-{
+void CSMImporter::SetupProperties(const Importer* pImp) {
     noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
     noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
 // Imports the given file into the given scene structure.
 void CSMImporter::InternReadFile( const std::string& pFile,
 void CSMImporter::InternReadFile( const std::string& pFile,
-    aiScene* pScene, IOSystem* pIOHandler)
-{
+        aiScene* pScene, IOSystem* pIOHandler) {
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
@@ -118,29 +111,30 @@ void CSMImporter::InternReadFile( const std::string& pFile,
     std::vector<char> mBuffer2;
     std::vector<char> mBuffer2;
     TextFileToBuffer(file.get(),mBuffer2);
     TextFileToBuffer(file.get(),mBuffer2);
     const char* buffer = &mBuffer2[0];
     const char* buffer = &mBuffer2[0];
-
+    const char *end = &mBuffer2[mBuffer2.size() - 1] + 1;
     std::unique_ptr<aiAnimation> anim(new aiAnimation());
     std::unique_ptr<aiAnimation> anim(new aiAnimation());
     int first = 0, last = 0x00ffffff;
     int first = 0, last = 0x00ffffff;
 
 
     // now process the file and look out for '$' sections
     // now process the file and look out for '$' sections
     while (true) {
     while (true) {
-        SkipSpaces(&buffer);
-        if ('\0' == *buffer)
+        SkipSpaces(&buffer, end);
+        if ('\0' == *buffer) {
             break;
             break;
+        }
 
 
-        if ('$'  == *buffer)    {
+        if ('$'  == *buffer) {
             ++buffer;
             ++buffer;
-            if (TokenMatchI(buffer,"firstframe",10))    {
-                SkipSpaces(&buffer);
+            if (TokenMatchI(buffer,"firstframe",10)) {
+                SkipSpaces(&buffer, end);
                 first = strtol10(buffer,&buffer);
                 first = strtol10(buffer,&buffer);
             }
             }
-            else if (TokenMatchI(buffer,"lastframe",9))     {
-                SkipSpaces(&buffer);
+            else if (TokenMatchI(buffer,"lastframe",9)) {
+                SkipSpaces(&buffer, end);
                 last = strtol10(buffer,&buffer);
                 last = strtol10(buffer,&buffer);
             }
             }
             else if (TokenMatchI(buffer,"rate",4))  {
             else if (TokenMatchI(buffer,"rate",4))  {
-                SkipSpaces(&buffer);
-                float d;
+                SkipSpaces(&buffer, end);
+                float d = { 0.0f };
                 buffer = fast_atoreal_move<float>(buffer,d);
                 buffer = fast_atoreal_move<float>(buffer,d);
                 anim->mTicksPerSecond = d;
                 anim->mTicksPerSecond = d;
             }
             }
@@ -148,50 +142,61 @@ void CSMImporter::InternReadFile( const std::string& pFile,
                 std::vector< aiNodeAnim* > anims_temp;
                 std::vector< aiNodeAnim* > anims_temp;
                 anims_temp.reserve(30);
                 anims_temp.reserve(30);
                 while (true) {
                 while (true) {
-                    SkipSpaces(&buffer);
-                    if (IsLineEnd(*buffer) && SkipSpacesAndLineEnd(&buffer) && *buffer == '$')
+                    SkipSpaces(&buffer, end);
+                    if (IsLineEnd(*buffer) && SkipSpacesAndLineEnd(&buffer, end) && *buffer == '$')
                         break; // next section
                         break; // next section
 
 
                     // Construct a new node animation channel and setup its name
                     // Construct a new node animation channel and setup its name
                     anims_temp.push_back(new aiNodeAnim());
                     anims_temp.push_back(new aiNodeAnim());
                     aiNodeAnim* nda = anims_temp.back();
                     aiNodeAnim* nda = anims_temp.back();
 
 
-                    char* ot = nda->mNodeName.data;
-                    while (!IsSpaceOrNewLine(*buffer))
+                    char *ot = nda->mNodeName.data;
+                    const char *ot_end = nda->mNodeName.data + AI_MAXLEN;
+                    while (!IsSpaceOrNewLine(*buffer) && buffer != end && ot != ot_end) {
                         *ot++ = *buffer++;
                         *ot++ = *buffer++;
+                    }
 
 
                     *ot = '\0';
                     *ot = '\0';
                     nda->mNodeName.length = static_cast<ai_uint32>(ot-nda->mNodeName.data);
                     nda->mNodeName.length = static_cast<ai_uint32>(ot-nda->mNodeName.data);
                 }
                 }
 
 
                 anim->mNumChannels = static_cast<unsigned int>(anims_temp.size());
                 anim->mNumChannels = static_cast<unsigned int>(anims_temp.size());
-                if (!anim->mNumChannels)
+                if (!anim->mNumChannels) {
                     throw DeadlyImportError("CSM: Empty $order section");
                     throw DeadlyImportError("CSM: Empty $order section");
+                }
 
 
                 // copy over to the output animation
                 // copy over to the output animation
                 anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
                 anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
                 ::memcpy(anim->mChannels,&anims_temp[0],sizeof(aiNodeAnim*)*anim->mNumChannels);
                 ::memcpy(anim->mChannels,&anims_temp[0],sizeof(aiNodeAnim*)*anim->mNumChannels);
-            }
-            else if (TokenMatchI(buffer,"points",6))    {
-                if (!anim->mNumChannels)
+            } else if (TokenMatchI(buffer,"points",6)) {
+                if (!anim->mNumChannels) {
                     throw DeadlyImportError("CSM: \'$order\' section is required to appear prior to \'$points\'");
                     throw DeadlyImportError("CSM: \'$order\' section is required to appear prior to \'$points\'");
+                }
 
 
                 // If we know how many frames we'll read, we can preallocate some storage
                 // If we know how many frames we'll read, we can preallocate some storage
                 unsigned int alloc = 100;
                 unsigned int alloc = 100;
-                if (last != 0x00ffffff)
-                {
+                if (last != 0x00ffffff) {
+                    // re-init if the file has last frame data
                     alloc = last-first;
                     alloc = last-first;
                     alloc += alloc>>2u; // + 25%
                     alloc += alloc>>2u; // + 25%
-                    for (unsigned int i = 0; i < anim->mNumChannels;++i)
+                    for (unsigned int i = 0; i < anim->mNumChannels; ++i) {
+                        if (anim->mChannels[i]->mPositionKeys != nullptr) delete[] anim->mChannels[i]->mPositionKeys;
+                        anim->mChannels[i]->mPositionKeys = new aiVectorKey[alloc];
+                    }
+                } else {
+                    // default init
+                    for (unsigned int i = 0; i < anim->mNumChannels; ++i) {
+                        if (anim->mChannels[i]->mPositionKeys != nullptr) continue;
                         anim->mChannels[i]->mPositionKeys = new aiVectorKey[alloc];
                         anim->mChannels[i]->mPositionKeys = new aiVectorKey[alloc];
+                    }
                 }
                 }
 
 
                 unsigned int filled = 0;
                 unsigned int filled = 0;
 
 
                 // Now read all point data.
                 // Now read all point data.
                 while (true) {
                 while (true) {
-                    SkipSpaces(&buffer);
-                    if (IsLineEnd(*buffer) && (!SkipSpacesAndLineEnd(&buffer) || *buffer == '$'))   {
+                    SkipSpaces(&buffer, end);
+                    if (IsLineEnd(*buffer) && (!SkipSpacesAndLineEnd(&buffer, end) || *buffer == '$'))   {
                         break; // next section
                         break; // next section
                     }
                     }
 
 
@@ -202,33 +207,35 @@ void CSMImporter::InternReadFile( const std::string& pFile,
                     for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
                     for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
 
 
                         aiNodeAnim* s = anim->mChannels[i];
                         aiNodeAnim* s = anim->mChannels[i];
-                        if (s->mNumPositionKeys == alloc)   { /* need to reallocate? */
-
+                        if (s->mNumPositionKeys == alloc)   {
+                            // need to reallocate?
                             aiVectorKey* old = s->mPositionKeys;
                             aiVectorKey* old = s->mPositionKeys;
-                            s->mPositionKeys = new aiVectorKey[s->mNumPositionKeys = alloc*2];
+                            s->mPositionKeys = new aiVectorKey[alloc*2];
                             ::memcpy(s->mPositionKeys,old,sizeof(aiVectorKey)*alloc);
                             ::memcpy(s->mPositionKeys,old,sizeof(aiVectorKey)*alloc);
                             delete[] old;
                             delete[] old;
                         }
                         }
 
 
                         // read x,y,z
                         // read x,y,z
-                        if(!SkipSpacesAndLineEnd(&buffer))
+                        if (!SkipSpacesAndLineEnd(&buffer, end)) {
                             throw DeadlyImportError("CSM: Unexpected EOF occurred reading sample x coord");
                             throw DeadlyImportError("CSM: Unexpected EOF occurred reading sample x coord");
+                        }
 
 
                         if (TokenMatchI(buffer, "DROPOUT", 7))  {
                         if (TokenMatchI(buffer, "DROPOUT", 7))  {
                             // seems this is invalid marker data; at least the doc says it's possible
                             // seems this is invalid marker data; at least the doc says it's possible
                             ASSIMP_LOG_WARN("CSM: Encountered invalid marker data (DROPOUT)");
                             ASSIMP_LOG_WARN("CSM: Encountered invalid marker data (DROPOUT)");
-                        }
-                        else    {
+                        } else {
                             aiVectorKey* sub = s->mPositionKeys + s->mNumPositionKeys;
                             aiVectorKey* sub = s->mPositionKeys + s->mNumPositionKeys;
                             sub->mTime = (double)frame;
                             sub->mTime = (double)frame;
                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.x);
                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.x);
 
 
-                            if(!SkipSpacesAndLineEnd(&buffer))
+                            if (!SkipSpacesAndLineEnd(&buffer, end)) {
                                 throw DeadlyImportError("CSM: Unexpected EOF occurred reading sample y coord");
                                 throw DeadlyImportError("CSM: Unexpected EOF occurred reading sample y coord");
+                            }
                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.y);
                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.y);
 
 
-                            if(!SkipSpacesAndLineEnd(&buffer))
+                            if (!SkipSpacesAndLineEnd(&buffer, end)) {
                                 throw DeadlyImportError("CSM: Unexpected EOF occurred reading sample z coord");
                                 throw DeadlyImportError("CSM: Unexpected EOF occurred reading sample z coord");
+                            }
                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.z);
                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.z);
 
 
                             ++s->mNumPositionKeys;
                             ++s->mNumPositionKeys;
@@ -236,22 +243,22 @@ void CSMImporter::InternReadFile( const std::string& pFile,
                     }
                     }
 
 
                     // update allocation granularity
                     // update allocation granularity
-                    if (filled == alloc)
+                    if (filled == alloc) {
                         alloc *= 2;
                         alloc *= 2;
+                    }
 
 
                     ++filled;
                     ++filled;
                 }
                 }
                 // all channels must be complete in order to continue safely.
                 // all channels must be complete in order to continue safely.
                 for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
                 for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
-
-                    if (!anim->mChannels[i]->mNumPositionKeys)
+                    if (!anim->mChannels[i]->mNumPositionKeys) {
                         throw DeadlyImportError("CSM: Invalid marker track");
                         throw DeadlyImportError("CSM: Invalid marker track");
+                    }
                 }
                 }
             }
             }
-        }
-        else    {
+        } else {
             // advance to the next line
             // advance to the next line
-            SkipLine(&buffer);
+            SkipLine(&buffer, end);
         }
         }
     }
     }
 
 
@@ -265,14 +272,20 @@ void CSMImporter::InternReadFile( const std::string& pFile,
     pScene->mRootNode->mNumChildren = anim->mNumChannels;
     pScene->mRootNode->mNumChildren = anim->mNumChannels;
     pScene->mRootNode->mChildren = new aiNode* [anim->mNumChannels];
     pScene->mRootNode->mChildren = new aiNode* [anim->mNumChannels];
 
 
-    for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
+    for (unsigned int i = 0; i < anim->mNumChannels;++i) {
         aiNodeAnim* na = anim->mChannels[i];
         aiNodeAnim* na = anim->mChannels[i];
 
 
         aiNode* nd  = pScene->mRootNode->mChildren[i] = new aiNode();
         aiNode* nd  = pScene->mRootNode->mChildren[i] = new aiNode();
         nd->mName   = anim->mChannels[i]->mNodeName;
         nd->mName   = anim->mChannels[i]->mNodeName;
         nd->mParent = pScene->mRootNode;
         nd->mParent = pScene->mRootNode;
 
 
-        aiMatrix4x4::Translation(na->mPositionKeys[0].mValue, nd->mTransformation);
+        if (na->mPositionKeys != nullptr && na->mNumPositionKeys > 0) {
+            aiMatrix4x4::Translation(na->mPositionKeys[0].mValue, nd->mTransformation);
+        } else {
+            // Use identity matrix if no valid position data is available
+            nd->mTransformation = aiMatrix4x4();
+            DefaultLogger::get()->warn("CSM: No position keys available for node - using identity transformation");
+        }
     }
     }
 
 
     // Store the one and only animation in the scene
     // Store the one and only animation in the scene

+ 1 - 1
code/AssetLib/CSM/CSMLoader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 308 - 255
code/AssetLib/Collada/ColladaExporter.cpp

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -36,7 +35,6 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 */
 */
 
 
@@ -64,6 +62,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 namespace Assimp {
 namespace Assimp {
 
 
+static const aiNode *findSkeletonRootNode(const aiScene *scene, const aiMesh *mesh) {
+    std::set<const aiNode *> topParentBoneNodes;
+    if (mesh && mesh->mNumBones > 0) {
+        for (unsigned int i = 0; i < mesh->mNumBones; ++i) {
+            aiBone *bone = mesh->mBones[i];
+
+            const aiNode *node = scene->mRootNode->findBoneNode(bone);
+            if (node) {
+                while (node->mParent && scene->findBone(node->mParent->mName) != nullptr) {
+                    node = node->mParent;
+                }
+                topParentBoneNodes.insert(node);
+            }
+        }
+    }
+
+    if (!topParentBoneNodes.empty()) {
+        const aiNode *parentBoneNode = *topParentBoneNodes.begin();
+        if (topParentBoneNodes.size() == 1) {
+            return parentBoneNode;
+        } else {
+            for (auto it : topParentBoneNodes) {
+                if (it->mParent) return it->mParent;
+            }
+            return parentBoneNode;
+        }
+    }
+
+    return nullptr;
+}
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
 void ExportSceneCollada(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
 void ExportSceneCollada(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
@@ -152,10 +181,6 @@ ColladaExporter::ColladaExporter(const aiScene *pScene, IOSystem *pIOSystem, con
     WriteFile();
     WriteFile();
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-// Destructor
-ColladaExporter::~ColladaExporter() = default;
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Starts writing the contents
 // Starts writing the contents
 void ColladaExporter::WriteFile() {
 void ColladaExporter::WriteFile() {
@@ -331,60 +356,68 @@ void ColladaExporter::WriteHeader() {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Write the embedded textures
 // Write the embedded textures
 void ColladaExporter::WriteTextures() {
 void ColladaExporter::WriteTextures() {
-    static const unsigned int buffer_size = 1024;
-    char str[buffer_size];
+    static constexpr unsigned int buffer_size = 1024;
+    char str[buffer_size] = {'\0'};
 
 
-    if (mScene->HasTextures()) {
-        for (unsigned int i = 0; i < mScene->mNumTextures; i++) {
-            // It would be great to be able to create a directory in portable standard C++, but it's not the case,
-            // so we just write the textures in the current directory.
-
-            aiTexture *texture = mScene->mTextures[i];
-            if (nullptr == texture) {
-                continue;
-            }
+    if (!mScene->HasTextures()) {
+        return;
+    }
 
 
-            ASSIMP_itoa10(str, buffer_size, i + 1);
+    for (unsigned int i = 0; i < mScene->mNumTextures; i++) {
+        // It would be great to be able to create a directory in portable standard C++, but it's not the case,
+        // so we just write the textures in the current directory.
 
 
-            std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char *)texture->achFormatHint);
+        aiTexture *texture = mScene->mTextures[i];
+        if (nullptr == texture) {
+            continue;
+        }
 
 
-            std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + mIOSystem->getOsSeparator() + name, "wb"));
-            if (outfile == nullptr) {
-                throw DeadlyExportError("could not open output texture file: " + mPath + name);
-            }
+        ASSIMP_itoa10(str, buffer_size, i + 1);
 
 
-            if (texture->mHeight == 0) {
-                outfile->Write((void *)texture->pcData, texture->mWidth, 1);
-            } else {
-                Bitmap::Save(texture, outfile.get());
-            }
+        std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char *)texture->achFormatHint);
 
 
-            outfile->Flush();
+        std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + mIOSystem->getOsSeparator() + name, "wb"));
+        if (outfile == nullptr) {
+            throw DeadlyExportError("could not open output texture file: " + mPath + name);
+        }
 
 
-            textures.insert(std::make_pair(i, name));
+        if (texture->mHeight == 0) {
+            outfile->Write((void *)texture->pcData, texture->mWidth, 1);
+        } else {
+            Bitmap::Save(texture, outfile.get());
         }
         }
+
+        outfile->Flush();
+
+        textures.insert(std::make_pair(i, name));
     }
     }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Write the embedded textures
 // Write the embedded textures
 void ColladaExporter::WriteCamerasLibrary() {
 void ColladaExporter::WriteCamerasLibrary() {
-    if (mScene->HasCameras()) {
-
-        mOutput << startstr << "<library_cameras>" << endstr;
-        PushTag();
+    if (!mScene->HasCameras()) {
+        return;
+    }
 
 
-        for (size_t a = 0; a < mScene->mNumCameras; ++a)
-            WriteCamera(a);
+    mOutput << startstr << "<library_cameras>" << endstr;
+    PushTag();
 
 
-        PopTag();
-        mOutput << startstr << "</library_cameras>" << endstr;
+    for (size_t a = 0; a < mScene->mNumCameras; ++a) {
+        WriteCamera(a);
     }
     }
+
+    PopTag();
+    mOutput << startstr << "</library_cameras>" << endstr;
 }
 }
 
 
 void ColladaExporter::WriteCamera(size_t pIndex) {
 void ColladaExporter::WriteCamera(size_t pIndex) {
 
 
     const aiCamera *cam = mScene->mCameras[pIndex];
     const aiCamera *cam = mScene->mCameras[pIndex];
+    if (cam == nullptr) {
+        return;
+    }
+
     const std::string cameraId = GetObjectUniqueId(AiObjectType::Camera, pIndex);
     const std::string cameraId = GetObjectUniqueId(AiObjectType::Camera, pIndex);
     const std::string cameraName = GetObjectName(AiObjectType::Camera, pIndex);
     const std::string cameraName = GetObjectName(AiObjectType::Camera, pIndex);
 
 
@@ -422,22 +455,27 @@ void ColladaExporter::WriteCamera(size_t pIndex) {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Write the embedded textures
 // Write the embedded textures
 void ColladaExporter::WriteLightsLibrary() {
 void ColladaExporter::WriteLightsLibrary() {
-    if (mScene->HasLights()) {
-
-        mOutput << startstr << "<library_lights>" << endstr;
-        PushTag();
+    if (!mScene->HasLights()) {
+        return;
+    }
 
 
-        for (size_t a = 0; a < mScene->mNumLights; ++a)
-            WriteLight(a);
+    mOutput << startstr << "<library_lights>" << endstr;
+    PushTag();
 
 
-        PopTag();
-        mOutput << startstr << "</library_lights>" << endstr;
+    for (size_t a = 0; a < mScene->mNumLights; ++a) {
+        WriteLight(a);
     }
     }
+
+    PopTag();
+    mOutput << startstr << "</library_lights>" << endstr;
 }
 }
 
 
 void ColladaExporter::WriteLight(size_t pIndex) {
 void ColladaExporter::WriteLight(size_t pIndex) {
 
 
     const aiLight *light = mScene->mLights[pIndex];
     const aiLight *light = mScene->mLights[pIndex];
+    if (light == nullptr) {
+        return;
+    }
     const std::string lightId = GetObjectUniqueId(AiObjectType::Light, pIndex);
     const std::string lightId = GetObjectUniqueId(AiObjectType::Light, pIndex);
     const std::string lightName = GetObjectName(AiObjectType::Light, pIndex);
     const std::string lightName = GetObjectName(AiObjectType::Light, pIndex);
 
 
@@ -462,6 +500,7 @@ void ColladaExporter::WriteLight(size_t pIndex) {
     case aiLightSource_AREA:
     case aiLightSource_AREA:
     case aiLightSource_UNDEFINED:
     case aiLightSource_UNDEFINED:
     case _aiLightSource_Force32Bit:
     case _aiLightSource_Force32Bit:
+    default:
         break;
         break;
     }
     }
     PopTag();
     PopTag();
@@ -521,10 +560,6 @@ void ColladaExporter::WriteSpotLight(const aiLight *const light) {
     mOutput << startstr << "<quadratic_attenuation>"
     mOutput << startstr << "<quadratic_attenuation>"
             << light->mAttenuationQuadratic
             << light->mAttenuationQuadratic
             << "</quadratic_attenuation>" << endstr;
             << "</quadratic_attenuation>" << endstr;
-    /*
-    out->mAngleOuterCone = AI_DEG_TO_RAD (std::acos(std::pow(0.1f,1.f/srcLight->mFalloffExponent))+
-                            srcLight->mFalloffAngle);
-    */
 
 
     const ai_real fallOffAngle = AI_RAD_TO_DEG(light->mAngleInnerCone);
     const ai_real fallOffAngle = AI_RAD_TO_DEG(light->mAngleInnerCone);
     mOutput << startstr << "<falloff_angle sid=\"fall_off_angle\">"
     mOutput << startstr << "<falloff_angle sid=\"fall_off_angle\">"
@@ -559,41 +594,43 @@ void ColladaExporter::WriteAmbientLight(const aiLight *const light) {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Reads a single surface entry from the given material keys
 // Reads a single surface entry from the given material keys
 bool ColladaExporter::ReadMaterialSurface(Surface &poSurface, const aiMaterial &pSrcMat, aiTextureType pTexture, const char *pKey, size_t pType, size_t pIndex) {
 bool ColladaExporter::ReadMaterialSurface(Surface &poSurface, const aiMaterial &pSrcMat, aiTextureType pTexture, const char *pKey, size_t pType, size_t pIndex) {
-    if (pSrcMat.GetTextureCount(pTexture) > 0) {
-        aiString texfile;
-        unsigned int uvChannel = 0;
-        pSrcMat.GetTexture(pTexture, 0, &texfile, nullptr, &uvChannel);
+    if (pSrcMat.GetTextureCount(pTexture) == 0) {
+        if (pKey)
+            poSurface.exist = pSrcMat.Get(pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
+        return poSurface.exist;
+    }
 
 
-        std::string index_str(texfile.C_Str());
+    aiString texfile;
+    unsigned int uvChannel = 0;
+    pSrcMat.GetTexture(pTexture, 0, &texfile, nullptr, &uvChannel);
 
 
-        if (index_str.size() != 0 && index_str[0] == '*') {
-            unsigned int index;
+    std::string index_str(texfile.C_Str());
 
 
-            index_str = index_str.substr(1, std::string::npos);
+    if (index_str.size() != 0 && index_str[0] == '*') {
+        unsigned int index;
 
 
-            try {
-                index = (unsigned int)strtoul10_64<DeadlyExportError>(index_str.c_str());
-            } catch (std::exception &error) {
-                throw DeadlyExportError(error.what());
-            }
+        index_str = index_str.substr(1, std::string::npos);
 
 
-            std::map<unsigned int, std::string>::const_iterator name = textures.find(index);
+        try {
+            index = (unsigned int)strtoul10_64<DeadlyExportError>(index_str.c_str());
+        } catch (std::exception &error) {
+            throw DeadlyExportError(error.what());
+        }
 
 
-            if (name != textures.end()) {
-                poSurface.texture = name->second;
-            } else {
-                throw DeadlyExportError("could not find embedded texture at index " + index_str);
-            }
+        std::map<unsigned int, std::string>::const_iterator name = textures.find(index);
+
+        if (name != textures.end()) {
+            poSurface.texture = name->second;
         } else {
         } else {
-            poSurface.texture = texfile.C_Str();
+            throw DeadlyExportError("could not find embedded texture at index " + index_str);
         }
         }
-
-        poSurface.channel = uvChannel;
-        poSurface.exist = true;
     } else {
     } else {
-        if (pKey)
-            poSurface.exist = pSrcMat.Get(pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
+        poSurface.texture = texfile.C_Str();
     }
     }
+
+    poSurface.channel = uvChannel;
+    poSurface.exist = true;
+
     return poSurface.exist;
     return poSurface.exist;
 }
 }
 
 
@@ -606,79 +643,87 @@ static bool isalnum_C(char c) {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Writes an image entry for the given surface
 // Writes an image entry for the given surface
 void ColladaExporter::WriteImageEntry(const Surface &pSurface, const std::string &imageId) {
 void ColladaExporter::WriteImageEntry(const Surface &pSurface, const std::string &imageId) {
-    if (!pSurface.texture.empty()) {
-        mOutput << startstr << "<image id=\"" << imageId << "\">" << endstr;
-        PushTag();
-        mOutput << startstr << "<init_from>";
+    if (pSurface.texture.empty()) {
+        return;
+    }
 
 
-        // URL encode image file name first, then XML encode on top
-        std::stringstream imageUrlEncoded;
-        for (std::string::const_iterator it = pSurface.texture.begin(); it != pSurface.texture.end(); ++it) {
-            if (isalnum_C((unsigned char)*it) || *it == ':' || *it == '_' || *it == '-' || *it == '.' || *it == '/' || *it == '\\')
-                imageUrlEncoded << *it;
-            else
-                imageUrlEncoded << '%' << std::hex << size_t((unsigned char)*it) << std::dec;
-        }
-        mOutput << XMLEscape(imageUrlEncoded.str());
-        mOutput << "</init_from>" << endstr;
-        PopTag();
-        mOutput << startstr << "</image>" << endstr;
+    mOutput << startstr << "<image id=\"" << imageId << "\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<init_from>";
+
+    // URL encode image file name first, then XML encode on top
+    std::stringstream imageUrlEncoded;
+    for (std::string::const_iterator it = pSurface.texture.begin(); it != pSurface.texture.end(); ++it) {
+        if (isalnum_C((unsigned char)*it) || *it == ':' || *it == '_' || *it == '-' || *it == '.' || *it == '/' || *it == '\\')
+            imageUrlEncoded << *it;
+        else
+            imageUrlEncoded << '%' << std::hex << size_t((unsigned char)*it) << std::dec;
     }
     }
+    mOutput << XMLEscape(imageUrlEncoded.str());
+    mOutput << "</init_from>" << endstr;
+    PopTag();
+    mOutput << startstr << "</image>" << endstr;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Writes a color-or-texture entry into an effect definition
 // Writes a color-or-texture entry into an effect definition
 void ColladaExporter::WriteTextureColorEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &imageId) {
 void ColladaExporter::WriteTextureColorEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &imageId) {
-    if (pSurface.exist) {
-        mOutput << startstr << "<" << pTypeName << ">" << endstr;
-        PushTag();
-        if (pSurface.texture.empty()) {
-            mOutput << startstr << "<color sid=\"" << pTypeName << "\">" << pSurface.color.r << "   " << pSurface.color.g << "   " << pSurface.color.b << "   " << pSurface.color.a << "</color>" << endstr;
-        } else {
-            mOutput << startstr << "<texture texture=\"" << imageId << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
-        }
-        PopTag();
-        mOutput << startstr << "</" << pTypeName << ">" << endstr;
+    if (!pSurface.exist) {
+        return;
     }
     }
+
+    mOutput << startstr << "<" << pTypeName << ">" << endstr;
+    PushTag();
+    if (pSurface.texture.empty()) {
+        mOutput << startstr << "<color sid=\"" << pTypeName << "\">" << pSurface.color.r << "   " << pSurface.color.g << "   " << pSurface.color.b << "   " << pSurface.color.a << "</color>" << endstr;
+    } else {
+        mOutput << startstr << "<texture texture=\"" << imageId << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
+    }
+    PopTag();
+    mOutput << startstr << "</" << pTypeName << ">" << endstr;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Writes the two parameters necessary for referencing a texture in an effect entry
 // Writes the two parameters necessary for referencing a texture in an effect entry
 void ColladaExporter::WriteTextureParamEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &materialId) {
 void ColladaExporter::WriteTextureParamEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &materialId) {
     // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture
     // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture
-    if (!pSurface.texture.empty()) {
-        mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-surface\">" << endstr;
-        PushTag();
-        mOutput << startstr << "<surface type=\"2D\">" << endstr;
-        PushTag();
-        mOutput << startstr << "<init_from>" << materialId << "-" << pTypeName << "-image</init_from>" << endstr;
-        PopTag();
-        mOutput << startstr << "</surface>" << endstr;
-        PopTag();
-        mOutput << startstr << "</newparam>" << endstr;
-
-        mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-sampler\">" << endstr;
-        PushTag();
-        mOutput << startstr << "<sampler2D>" << endstr;
-        PushTag();
-        mOutput << startstr << "<source>" << materialId << "-" << pTypeName << "-surface</source>" << endstr;
-        PopTag();
-        mOutput << startstr << "</sampler2D>" << endstr;
-        PopTag();
-        mOutput << startstr << "</newparam>" << endstr;
+    if (pSurface.texture.empty()) {
+        return;
     }
     }
+
+    mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-surface\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<surface type=\"2D\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<init_from>" << materialId << "-" << pTypeName << "-image</init_from>" << endstr;
+    PopTag();
+    mOutput << startstr << "</surface>" << endstr;
+    PopTag();
+    mOutput << startstr << "</newparam>" << endstr;
+
+    mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-sampler\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<sampler2D>" << endstr;
+    PushTag();
+    mOutput << startstr << "<source>" << materialId << "-" << pTypeName << "-surface</source>" << endstr;
+    PopTag();
+    mOutput << startstr << "</sampler2D>" << endstr;
+    PopTag();
+    mOutput << startstr << "</newparam>" << endstr;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Writes a scalar property
 // Writes a scalar property
 void ColladaExporter::WriteFloatEntry(const Property &pProperty, const std::string &pTypeName) {
 void ColladaExporter::WriteFloatEntry(const Property &pProperty, const std::string &pTypeName) {
-    if (pProperty.exist) {
-        mOutput << startstr << "<" << pTypeName << ">" << endstr;
-        PushTag();
-        mOutput << startstr << "<float sid=\"" << pTypeName << "\">" << pProperty.value << "</float>" << endstr;
-        PopTag();
-        mOutput << startstr << "</" << pTypeName << ">" << endstr;
+    if (!pProperty.exist) {
+        return;
     }
     }
+
+    mOutput << startstr << "<" << pTypeName << ">" << endstr;
+    PushTag();
+    mOutput << startstr << "<float sid=\"" << pTypeName << "\">" << pProperty.value << "</float>" << endstr;
+    PopTag();
+    mOutput << startstr << "</" << pTypeName << ">" << endstr;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -832,8 +877,9 @@ void ColladaExporter::WriteControllerLibrary() {
 void ColladaExporter::WriteController(size_t pIndex) {
 void ColladaExporter::WriteController(size_t pIndex) {
     const aiMesh *mesh = mScene->mMeshes[pIndex];
     const aiMesh *mesh = mScene->mMeshes[pIndex];
     // Is there a skin controller?
     // Is there a skin controller?
-    if (mesh->mNumBones == 0 || mesh->mNumFaces == 0 || mesh->mNumVertices == 0)
+    if (mesh->mNumBones == 0 || mesh->mNumFaces == 0 || mesh->mNumVertices == 0) {
         return;
         return;
+    }
 
 
     const std::string idstr = GetObjectUniqueId(AiObjectType::Mesh, pIndex);
     const std::string idstr = GetObjectUniqueId(AiObjectType::Mesh, pIndex);
     const std::string namestr = GetObjectName(AiObjectType::Mesh, pIndex);
     const std::string namestr = GetObjectName(AiObjectType::Mesh, pIndex);
@@ -864,8 +910,9 @@ void ColladaExporter::WriteController(size_t pIndex) {
 
 
     mOutput << startstr << "<Name_array id=\"" << idstr << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
     mOutput << startstr << "<Name_array id=\"" << idstr << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
 
 
-    for (size_t i = 0; i < mesh->mNumBones; ++i)
+    for (size_t i = 0; i < mesh->mNumBones; ++i) {
         mOutput << GetBoneUniqueId(mesh->mBones[i]) << ' ';
         mOutput << GetBoneUniqueId(mesh->mBones[i]) << ' ';
+    }
 
 
     mOutput << "</Name_array>" << endstr;
     mOutput << "</Name_array>" << endstr;
 
 
@@ -888,9 +935,11 @@ void ColladaExporter::WriteController(size_t pIndex) {
 
 
     std::vector<ai_real> bind_poses;
     std::vector<ai_real> bind_poses;
     bind_poses.reserve(mesh->mNumBones * 16);
     bind_poses.reserve(mesh->mNumBones * 16);
-    for (unsigned int i = 0; i < mesh->mNumBones; ++i)
-        for (unsigned int j = 0; j < 4; ++j)
+    for (unsigned int i = 0; i < mesh->mNumBones; ++i) {
+        for (unsigned int j = 0; j < 4; ++j) {
             bind_poses.insert(bind_poses.end(), mesh->mBones[i]->mOffsetMatrix[j], mesh->mBones[i]->mOffsetMatrix[j] + 4);
             bind_poses.insert(bind_poses.end(), mesh->mBones[i]->mOffsetMatrix[j], mesh->mBones[i]->mOffsetMatrix[j] + 4);
+        }
+    }
 
 
     WriteFloatArray(idstr + "-skin-bind_poses", FloatType_Mat4x4, (const ai_real *)bind_poses.data(), bind_poses.size() / 16);
     WriteFloatArray(idstr + "-skin-bind_poses", FloatType_Mat4x4, (const ai_real *)bind_poses.data(), bind_poses.size() / 16);
 
 
@@ -898,9 +947,11 @@ void ColladaExporter::WriteController(size_t pIndex) {
 
 
     std::vector<ai_real> skin_weights;
     std::vector<ai_real> skin_weights;
     skin_weights.reserve(mesh->mNumVertices * mesh->mNumBones);
     skin_weights.reserve(mesh->mNumVertices * mesh->mNumBones);
-    for (size_t i = 0; i < mesh->mNumBones; ++i)
-        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
+    for (size_t i = 0; i < mesh->mNumBones; ++i) {
+        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
             skin_weights.push_back(mesh->mBones[i]->mWeights[j].mWeight);
             skin_weights.push_back(mesh->mBones[i]->mWeights[j].mWeight);
+        }
+    }
 
 
     WriteFloatArray(idstr + "-skin-weights", FloatType_Weight, (const ai_real *)skin_weights.data(), skin_weights.size());
     WriteFloatArray(idstr + "-skin-weights", FloatType_Weight, (const ai_real *)skin_weights.data(), skin_weights.size());
 
 
@@ -924,12 +975,15 @@ void ColladaExporter::WriteController(size_t pIndex) {
     mOutput << startstr << "<vcount>";
     mOutput << startstr << "<vcount>";
 
 
     std::vector<ai_uint> num_influences(mesh->mNumVertices, (ai_uint)0);
     std::vector<ai_uint> num_influences(mesh->mNumVertices, (ai_uint)0);
-    for (size_t i = 0; i < mesh->mNumBones; ++i)
-        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
+    for (size_t i = 0; i < mesh->mNumBones; ++i) {
+        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
             ++num_influences[mesh->mBones[i]->mWeights[j].mVertexId];
             ++num_influences[mesh->mBones[i]->mWeights[j].mVertexId];
+        }
+    }
 
 
-    for (size_t i = 0; i < mesh->mNumVertices; ++i)
+    for (size_t i = 0; i < mesh->mNumVertices; ++i) {
         mOutput << num_influences[i] << " ";
         mOutput << num_influences[i] << " ";
+    }
 
 
     mOutput << "</vcount>" << endstr;
     mOutput << "</vcount>" << endstr;
 
 
@@ -945,7 +999,7 @@ void ColladaExporter::WriteController(size_t pIndex) {
 
 
     ai_uint weight_index = 0;
     ai_uint weight_index = 0;
     std::vector<ai_int> joint_weight_indices(2 * joint_weight_indices_length, (ai_int)-1);
     std::vector<ai_int> joint_weight_indices(2 * joint_weight_indices_length, (ai_int)-1);
-    for (unsigned int i = 0; i < mesh->mNumBones; ++i)
+    for (unsigned int i = 0; i < mesh->mNumBones; ++i) {
         for (unsigned j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
         for (unsigned j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
             unsigned int vId = mesh->mBones[i]->mWeights[j].mVertexId;
             unsigned int vId = mesh->mBones[i]->mWeights[j].mVertexId;
             for (ai_uint k = 0; k < num_influences[vId]; ++k) {
             for (ai_uint k = 0; k < num_influences[vId]; ++k) {
@@ -957,9 +1011,11 @@ void ColladaExporter::WriteController(size_t pIndex) {
             }
             }
             ++weight_index;
             ++weight_index;
         }
         }
+    }
 
 
-    for (size_t i = 0; i < joint_weight_indices.size(); ++i)
+    for (size_t i = 0; i < joint_weight_indices.size(); ++i) {
         mOutput << joint_weight_indices[i] << " ";
         mOutput << joint_weight_indices[i] << " ";
+    }
 
 
     num_influences.clear();
     num_influences.clear();
     accum_influences.clear();
     accum_influences.clear();
@@ -983,8 +1039,9 @@ void ColladaExporter::WriteGeometryLibrary() {
     mOutput << startstr << "<library_geometries>" << endstr;
     mOutput << startstr << "<library_geometries>" << endstr;
     PushTag();
     PushTag();
 
 
-    for (size_t a = 0; a < mScene->mNumMeshes; ++a)
+    for (size_t a = 0; a < mScene->mNumMeshes; ++a) {
         WriteGeometry(a);
         WriteGeometry(a);
+    }
 
 
     PopTag();
     PopTag();
     mOutput << startstr << "</library_geometries>" << endstr;
     mOutput << startstr << "</library_geometries>" << endstr;
@@ -997,8 +1054,9 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
     const std::string geometryId = GetObjectUniqueId(AiObjectType::Mesh, pIndex);
     const std::string geometryId = GetObjectUniqueId(AiObjectType::Mesh, pIndex);
     const std::string geometryName = GetObjectName(AiObjectType::Mesh, pIndex);
     const std::string geometryName = GetObjectName(AiObjectType::Mesh, pIndex);
 
 
-    if (mesh->mNumFaces == 0 || mesh->mNumVertices == 0)
+    if (mesh->mNumFaces == 0 || mesh->mNumVertices == 0) {
         return;
         return;
+    }
 
 
     // opening tag
     // opening tag
     mOutput << startstr << "<geometry id=\"" << geometryId << "\" name=\"" << geometryName << "\" >" << endstr;
     mOutput << startstr << "<geometry id=\"" << geometryId << "\" name=\"" << geometryName << "\" >" << endstr;
@@ -1010,8 +1068,9 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
     // Positions
     // Positions
     WriteFloatArray(geometryId + "-positions", FloatType_Vector, (ai_real *)mesh->mVertices, mesh->mNumVertices);
     WriteFloatArray(geometryId + "-positions", FloatType_Vector, (ai_real *)mesh->mVertices, mesh->mNumVertices);
     // Normals, if any
     // Normals, if any
-    if (mesh->HasNormals())
+    if (mesh->HasNormals()) {
         WriteFloatArray(geometryId + "-normals", FloatType_Vector, (ai_real *)mesh->mNormals, mesh->mNumVertices);
         WriteFloatArray(geometryId + "-normals", FloatType_Vector, (ai_real *)mesh->mNormals, mesh->mNumVertices);
+    }
 
 
     // texture coords
     // texture coords
     for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
     for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
@@ -1040,10 +1099,11 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
     int countLines = 0;
     int countLines = 0;
     int countPoly = 0;
     int countPoly = 0;
     for (size_t a = 0; a < mesh->mNumFaces; ++a) {
     for (size_t a = 0; a < mesh->mNumFaces; ++a) {
-        if (mesh->mFaces[a].mNumIndices == 2)
+        if (mesh->mFaces[a].mNumIndices == 2) {
             countLines++;
             countLines++;
-        else if (mesh->mFaces[a].mNumIndices >= 3)
+        } else if (mesh->mFaces[a].mNumIndices >= 3) {
             countPoly++;
             countPoly++;
+        }
     }
     }
 
 
     // lines
     // lines
@@ -1051,13 +1111,18 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
         mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr;
         mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr;
         PushTag();
         PushTag();
         mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
         mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
-        if (mesh->HasNormals())
+        if (mesh->HasNormals()) {
             mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
             mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
+        }
         for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
         for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
-            if (mesh->HasTextureCoords(static_cast<unsigned int>(a)))
-                mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" "
+            if (mesh->HasTextureCoords(static_cast<unsigned int>(a))) {
+                mOutput << startstr
+                        << "<input semantic=\"TEXCOORD\" source=\"#"
+                        << geometryId
+                        << "-tex" << a << "\" "
                         << "set=\"" << a << "\""
                         << "set=\"" << a << "\""
                         << " />" << endstr;
                         << " />" << endstr;
+            }
         }
         }
         for (size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
         for (size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
             if (mesh->HasVertexColors(static_cast<unsigned int>(a)))
             if (mesh->HasVertexColors(static_cast<unsigned int>(a)))
@@ -1070,8 +1135,9 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
         for (size_t a = 0; a < mesh->mNumFaces; ++a) {
         for (size_t a = 0; a < mesh->mNumFaces; ++a) {
             const aiFace &face = mesh->mFaces[a];
             const aiFace &face = mesh->mFaces[a];
             if (face.mNumIndices != 2) continue;
             if (face.mNumIndices != 2) continue;
-            for (size_t b = 0; b < face.mNumIndices; ++b)
+            for (size_t b = 0; b < face.mNumIndices; ++b) {
                 mOutput << face.mIndices[b] << " ";
                 mOutput << face.mIndices[b] << " ";
+            }
         }
         }
         mOutput << "</p>" << endstr;
         mOutput << "</p>" << endstr;
         PopTag();
         PopTag();
@@ -1085,8 +1151,9 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
         mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr;
         mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr;
         PushTag();
         PushTag();
         mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
         mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
-        if (mesh->HasNormals())
+        if (mesh->HasNormals()) {
             mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
             mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
+        }
         for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
         for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
             if (mesh->HasTextureCoords(static_cast<unsigned int>(a)))
             if (mesh->HasTextureCoords(static_cast<unsigned int>(a)))
                 mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" "
                 mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" "
@@ -1111,8 +1178,9 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
         for (size_t a = 0; a < mesh->mNumFaces; ++a) {
         for (size_t a = 0; a < mesh->mNumFaces; ++a) {
             const aiFace &face = mesh->mFaces[a];
             const aiFace &face = mesh->mFaces[a];
             if (face.mNumIndices < 3) continue;
             if (face.mNumIndices < 3) continue;
-            for (size_t b = 0; b < face.mNumIndices; ++b)
+            for (size_t b = 0; b < face.mNumIndices; ++b) {
                 mOutput << face.mIndices[b] << " ";
                 mOutput << face.mIndices[b] << " ";
+            }
         }
         }
         mOutput << "</p>" << endstr;
         mOutput << "</p>" << endstr;
         PopTag();
         PopTag();
@@ -1131,13 +1199,27 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
 void ColladaExporter::WriteFloatArray(const std::string &pIdString, FloatDataType pType, const ai_real *pData, size_t pElementCount) {
 void ColladaExporter::WriteFloatArray(const std::string &pIdString, FloatDataType pType, const ai_real *pData, size_t pElementCount) {
     size_t floatsPerElement = 0;
     size_t floatsPerElement = 0;
     switch (pType) {
     switch (pType) {
-    case FloatType_Vector: floatsPerElement = 3; break;
-    case FloatType_TexCoord2: floatsPerElement = 2; break;
-    case FloatType_TexCoord3: floatsPerElement = 3; break;
-    case FloatType_Color: floatsPerElement = 3; break;
-    case FloatType_Mat4x4: floatsPerElement = 16; break;
-    case FloatType_Weight: floatsPerElement = 1; break;
-    case FloatType_Time: floatsPerElement = 1; break;
+    case FloatType_Vector:
+        floatsPerElement = 3;
+        break;
+    case FloatType_TexCoord2:
+        floatsPerElement = 2;
+        break;
+    case FloatType_TexCoord3:
+        floatsPerElement = 3;
+        break;
+    case FloatType_Color:
+        floatsPerElement = 3;
+        break;
+    case FloatType_Mat4x4:
+        floatsPerElement = 16;
+        break;
+    case FloatType_Weight:
+        floatsPerElement = 1;
+        break;
+    case FloatType_Time:
+        floatsPerElement = 1;
+        break;
     default:
     default:
         return;
         return;
     }
     }
@@ -1163,8 +1245,9 @@ void ColladaExporter::WriteFloatArray(const std::string &pIdString, FloatDataTyp
             mOutput << pData[a * 4 + 2] << " ";
             mOutput << pData[a * 4 + 2] << " ";
         }
         }
     } else {
     } else {
-        for (size_t a = 0; a < pElementCount * floatsPerElement; ++a)
+        for (size_t a = 0; a < pElementCount * floatsPerElement; ++a) {
             mOutput << pData[a] << " ";
             mOutput << pData[a] << " ";
+        }
     }
     }
     mOutput << "</float_array>" << endstr;
     mOutput << "</float_array>" << endstr;
     PopTag();
     PopTag();
@@ -1256,9 +1339,13 @@ void ColladaExporter::WriteSceneLibrary() {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void ColladaExporter::WriteAnimationLibrary(size_t pIndex) {
 void ColladaExporter::WriteAnimationLibrary(size_t pIndex) {
     const aiAnimation *anim = mScene->mAnimations[pIndex];
     const aiAnimation *anim = mScene->mAnimations[pIndex];
+    if (anim == nullptr) {
+        return;
+    }
 
 
-    if (anim->mNumChannels == 0 && anim->mNumMeshChannels == 0 && anim->mNumMorphMeshChannels == 0)
+    if (anim->mNumChannels == 0 && anim->mNumMeshChannels == 0 && anim->mNumMorphMeshChannels == 0) {
         return;
         return;
+    }
 
 
     const std::string animationNameEscaped = GetObjectName(AiObjectType::Animation, pIndex);
     const std::string animationNameEscaped = GetObjectName(AiObjectType::Animation, pIndex);
     const std::string idstrEscaped = GetObjectUniqueId(AiObjectType::Animation, pIndex);
     const std::string idstrEscaped = GetObjectUniqueId(AiObjectType::Animation, pIndex);
@@ -1269,8 +1356,11 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) {
     std::string cur_node_idstr;
     std::string cur_node_idstr;
     for (size_t a = 0; a < anim->mNumChannels; ++a) {
     for (size_t a = 0; a < anim->mNumChannels; ++a) {
         const aiNodeAnim *nodeAnim = anim->mChannels[a];
         const aiNodeAnim *nodeAnim = anim->mChannels[a];
+        if (nodeAnim == nullptr) {
+            continue;
+        }
 
 
-        // sanity check
+        // sanity checks
         if (nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys || nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys) {
         if (nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys || nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys) {
             continue;
             continue;
         }
         }
@@ -1369,6 +1459,9 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) {
 
 
     for (size_t a = 0; a < anim->mNumChannels; ++a) {
     for (size_t a = 0; a < anim->mNumChannels; ++a) {
         const aiNodeAnim *nodeAnim = anim->mChannels[a];
         const aiNodeAnim *nodeAnim = anim->mChannels[a];
+        if (nodeAnim == nullptr) {
+            continue;
+        }
 
 
         {
         {
             // samplers
             // samplers
@@ -1387,97 +1480,42 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) {
 
 
     for (size_t a = 0; a < anim->mNumChannels; ++a) {
     for (size_t a = 0; a < anim->mNumChannels; ++a) {
         const aiNodeAnim *nodeAnim = anim->mChannels[a];
         const aiNodeAnim *nodeAnim = anim->mChannels[a];
+        if (nodeAnim == nullptr) {
+            continue;
+        }
 
 
         {
         {
             // channels
             // channels
-            mOutput << startstr << "<channel source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-sampler")) << "\" target=\"" << XMLIDEncode(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
+            mOutput << startstr
+                    << "<channel source=\"#"
+                    << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-sampler"))
+                    << "\" target=\""
+                    << XMLIDEncode(nodeAnim->mNodeName.data)
+                    << "/matrix\"/>"
+                    << endstr;
         }
         }
     }
     }
 
 
     PopTag();
     PopTag();
     mOutput << startstr << "</animation>" << endstr;
     mOutput << startstr << "</animation>" << endstr;
 }
 }
-// ------------------------------------------------------------------------------------------------
-void ColladaExporter::WriteAnimationsLibrary() {
-    if (mScene->mNumAnimations > 0) {
-        mOutput << startstr << "<library_animations>" << endstr;
-        PushTag();
-
-        // start recursive write at the root node
-        for (size_t a = 0; a < mScene->mNumAnimations; ++a)
-            WriteAnimationLibrary(a);
 
 
-        PopTag();
-        mOutput << startstr << "</library_animations>" << endstr;
-    }
-}
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-// Helper to find a bone by name in the scene
-aiBone *findBone(const aiScene *scene, const aiString &name) {
-    for (size_t m = 0; m < scene->mNumMeshes; m++) {
-        aiMesh *mesh = scene->mMeshes[m];
-        for (size_t b = 0; b < mesh->mNumBones; b++) {
-            aiBone *bone = mesh->mBones[b];
-            if (name == bone->mName) {
-                return bone;
-            }
-        }
-    }
-    return nullptr;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Helper to find the node associated with a bone in the scene
-const aiNode *findBoneNode(const aiNode *aNode, const aiBone *bone) {
-    if (aNode && bone && aNode->mName == bone->mName) {
-        return aNode;
-    }
-
-    if (aNode && bone) {
-        for (unsigned int i = 0; i < aNode->mNumChildren; ++i) {
-            aiNode *aChild = aNode->mChildren[i];
-            const aiNode *foundFromChild = nullptr;
-            if (aChild) {
-                foundFromChild = findBoneNode(aChild, bone);
-                if (foundFromChild) {
-                    return foundFromChild;
-                }
-            }
-        }
+void ColladaExporter::WriteAnimationsLibrary() {
+    if (mScene->mNumAnimations == 0) {
+        return;
     }
     }
 
 
-    return nullptr;
-}
-
-const aiNode *findSkeletonRootNode(const aiScene *scene, const aiMesh *mesh) {
-    std::set<const aiNode *> topParentBoneNodes;
-    if (mesh && mesh->mNumBones > 0) {
-        for (unsigned int i = 0; i < mesh->mNumBones; ++i) {
-            aiBone *bone = mesh->mBones[i];
-
-            const aiNode *node = findBoneNode(scene->mRootNode, bone);
-            if (node) {
-                while (node->mParent && findBone(scene, node->mParent->mName) != nullptr) {
-                    node = node->mParent;
-                }
-                topParentBoneNodes.insert(node);
-            }
-        }
-    }
+    mOutput << startstr << "<library_animations>" << endstr;
+    PushTag();
 
 
-    if (!topParentBoneNodes.empty()) {
-        const aiNode *parentBoneNode = *topParentBoneNodes.begin();
-        if (topParentBoneNodes.size() == 1) {
-            return parentBoneNode;
-        } else {
-            for (auto it : topParentBoneNodes) {
-                if (it->mParent) return it->mParent;
-            }
-            return parentBoneNode;
-        }
+    // start recursive write at the root node
+    for (size_t a = 0; a < mScene->mNumAnimations; ++a) {
+        WriteAnimationLibrary(a);
     }
     }
 
 
-    return nullptr;
+    PopTag();
+    mOutput << startstr << "</library_animations>" << endstr;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -1488,13 +1526,13 @@ void ColladaExporter::WriteNode(const aiNode *pNode) {
     // Assimp-specific: nodes with no name cannot be associated with bones
     // Assimp-specific: nodes with no name cannot be associated with bones
     const char *node_type;
     const char *node_type;
     bool is_joint, is_skeleton_root = false;
     bool is_joint, is_skeleton_root = false;
-    if (pNode->mName.length == 0 || nullptr == findBone(mScene, pNode->mName)) {
+    if (pNode->mName.length == 0 || nullptr == mScene->findBone(pNode->mName)) {
         node_type = "NODE";
         node_type = "NODE";
         is_joint = false;
         is_joint = false;
     } else {
     } else {
         node_type = "JOINT";
         node_type = "JOINT";
         is_joint = true;
         is_joint = true;
-        if (!pNode->mParent || nullptr == findBone(mScene, pNode->mParent->mName)) {
+        if (!pNode->mParent || nullptr == mScene->findBone(pNode->mParent->mName)) {
             is_skeleton_root = true;
             is_skeleton_root = true;
         }
         }
     }
     }
@@ -1532,7 +1570,6 @@ void ColladaExporter::WriteNode(const aiNode *pNode) {
     }
     }
 
 
     // customized, sid should be 'matrix' to match with loader code.
     // customized, sid should be 'matrix' to match with loader code.
-    //mOutput << startstr << "<matrix sid=\"transform\">";
     mOutput << startstr << "<matrix sid=\"matrix\">";
     mOutput << startstr << "<matrix sid=\"matrix\">";
 
 
     mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " ";
     mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " ";
@@ -1556,7 +1593,6 @@ void ColladaExporter::WriteNode(const aiNode *pNode) {
                 break;
                 break;
             }
             }
         }
         }
-
     } else
     } else
         // instance every geometry
         // instance every geometry
         for (size_t a = 0; a < pNode->mNumMeshes; ++a) {
         for (size_t a = 0; a < pNode->mNumMeshes; ++a) {
@@ -1612,8 +1648,9 @@ void ColladaExporter::WriteNode(const aiNode *pNode) {
         }
         }
 
 
     // recurse into subnodes
     // recurse into subnodes
-    for (size_t a = 0; a < pNode->mNumChildren; ++a)
+    for (size_t a = 0; a < pNode->mNumChildren; ++a) {
         WriteNode(pNode->mChildren[a]);
         WriteNode(pNode->mChildren[a]);
+    }
 
 
     PopTag();
     PopTag();
     mOutput << startstr << "</node>" << endstr;
     mOutput << startstr << "</node>" << endstr;
@@ -1628,8 +1665,9 @@ void ColladaExporter::CreateNodeIds(const aiNode *node) {
 std::string ColladaExporter::GetNodeUniqueId(const aiNode *node) {
 std::string ColladaExporter::GetNodeUniqueId(const aiNode *node) {
     // Use the pointer as the key. This is safe because the scene is immutable.
     // Use the pointer as the key. This is safe because the scene is immutable.
     auto idIt = mNodeIdMap.find(node);
     auto idIt = mNodeIdMap.find(node);
-    if (idIt != mNodeIdMap.cend())
+    if (idIt != mNodeIdMap.cend()) {
         return idIt->second;
         return idIt->second;
+    }
 
 
     // Prefer the requested Collada Id if extant
     // Prefer the requested Collada Id if extant
     std::string idStr;
     std::string idStr;
@@ -1640,36 +1678,42 @@ std::string ColladaExporter::GetNodeUniqueId(const aiNode *node) {
         idStr = node->mName.C_Str();
         idStr = node->mName.C_Str();
     }
     }
     // Make sure the requested id is valid
     // Make sure the requested id is valid
-    if (idStr.empty())
+    if (idStr.empty()) {
         idStr = "node";
         idStr = "node";
-    else
+    } else {
         idStr = XMLIDEncode(idStr);
         idStr = XMLIDEncode(idStr);
+    }
 
 
     // Ensure it's unique
     // Ensure it's unique
     idStr = MakeUniqueId(mUniqueIds, idStr, std::string());
     idStr = MakeUniqueId(mUniqueIds, idStr, std::string());
     mUniqueIds.insert(idStr);
     mUniqueIds.insert(idStr);
     mNodeIdMap.insert(std::make_pair(node, idStr));
     mNodeIdMap.insert(std::make_pair(node, idStr));
+
     return idStr;
     return idStr;
 }
 }
 
 
 std::string ColladaExporter::GetNodeName(const aiNode *node) {
 std::string ColladaExporter::GetNodeName(const aiNode *node) {
-
+    if (node == nullptr) {
+        return std::string();
+    }
     return XMLEscape(node->mName.C_Str());
     return XMLEscape(node->mName.C_Str());
 }
 }
 
 
 std::string ColladaExporter::GetBoneUniqueId(const aiBone *bone) {
 std::string ColladaExporter::GetBoneUniqueId(const aiBone *bone) {
     // Find the Node that is this Bone
     // Find the Node that is this Bone
-    const aiNode *boneNode = findBoneNode(mScene->mRootNode, bone);
-    if (boneNode == nullptr)
+    const aiNode *boneNode = mScene->mRootNode->findBoneNode(bone);
+    if (boneNode == nullptr) {
         return std::string();
         return std::string();
+    }
 
 
     return GetNodeUniqueId(boneNode);
     return GetNodeUniqueId(boneNode);
 }
 }
 
 
 std::string ColladaExporter::GetObjectUniqueId(AiObjectType type, size_t pIndex) {
 std::string ColladaExporter::GetObjectUniqueId(AiObjectType type, size_t pIndex) {
     auto idIt = GetObjectIdMap(type).find(pIndex);
     auto idIt = GetObjectIdMap(type).find(pIndex);
-    if (idIt != GetObjectIdMap(type).cend())
+    if (idIt != GetObjectIdMap(type).cend()) {
         return idIt->second;
         return idIt->second;
+    }
 
 
     // Not seen this object before, create and add
     // Not seen this object before, create and add
     NameIdPair result = AddObjectIndexToMaps(type, pIndex);
     NameIdPair result = AddObjectIndexToMaps(type, pIndex);
@@ -1678,8 +1722,9 @@ std::string ColladaExporter::GetObjectUniqueId(AiObjectType type, size_t pIndex)
 
 
 std::string ColladaExporter::GetObjectName(AiObjectType type, size_t pIndex) {
 std::string ColladaExporter::GetObjectName(AiObjectType type, size_t pIndex) {
     auto objectName = GetObjectNameMap(type).find(pIndex);
     auto objectName = GetObjectNameMap(type).find(pIndex);
-    if (objectName != GetObjectNameMap(type).cend())
+    if (objectName != GetObjectNameMap(type).cend()) {
         return objectName->second;
         return objectName->second;
+    }
 
 
     // Not seen this object before, create and add
     // Not seen this object before, create and add
     NameIdPair result = AddObjectIndexToMaps(type, pIndex);
     NameIdPair result = AddObjectIndexToMaps(type, pIndex);
@@ -1699,9 +1744,15 @@ ColladaExporter::NameIdPair ColladaExporter::AddObjectIndexToMaps(AiObjectType t
 
 
     // Get the name and id postfix
     // Get the name and id postfix
     switch (type) {
     switch (type) {
-    case AiObjectType::Mesh: name = mScene->mMeshes[index]->mName.C_Str(); break;
-    case AiObjectType::Material: name = mScene->mMaterials[index]->GetName().C_Str(); break;
-    case AiObjectType::Animation: name = mScene->mAnimations[index]->mName.C_Str(); break;
+    case AiObjectType::Mesh:
+        name = mScene->mMeshes[index]->mName.C_Str();
+        break;
+    case AiObjectType::Material:
+        name = mScene->mMaterials[index]->GetName().C_Str();
+        break;
+    case AiObjectType::Animation:
+        name = mScene->mAnimations[index]->mName.C_Str();
+        break;
     case AiObjectType::Light:
     case AiObjectType::Light:
         name = mScene->mLights[index]->mName.C_Str();
         name = mScene->mLights[index]->mName.C_Str();
         idPostfix = "-light";
         idPostfix = "-light";
@@ -1710,7 +1761,8 @@ ColladaExporter::NameIdPair ColladaExporter::AddObjectIndexToMaps(AiObjectType t
         name = mScene->mCameras[index]->mName.C_Str();
         name = mScene->mCameras[index]->mName.C_Str();
         idPostfix = "-camera";
         idPostfix = "-camera";
         break;
         break;
-    case AiObjectType::Count: throw std::logic_error("ColladaExporter::AiObjectType::Count is not an object type");
+    case AiObjectType::Count:
+        throw std::logic_error("ColladaExporter::AiObjectType::Count is not an object type");
     }
     }
 
 
     if (name.empty()) {
     if (name.empty()) {
@@ -1728,8 +1780,9 @@ ColladaExporter::NameIdPair ColladaExporter::AddObjectIndexToMaps(AiObjectType t
         idStr = XMLIDEncode(name);
         idStr = XMLIDEncode(name);
     }
     }
 
 
-    if (!name.empty())
+    if (!name.empty()) {
         name = XMLEscape(name);
         name = XMLEscape(name);
+    }
 
 
     idStr = MakeUniqueId(mUniqueIds, idStr, idPostfix);
     idStr = MakeUniqueId(mUniqueIds, idStr, idPostfix);
 
 
@@ -1743,5 +1796,5 @@ ColladaExporter::NameIdPair ColladaExporter::AddObjectIndexToMaps(AiObjectType t
 
 
 } // end of namespace Assimp
 } // end of namespace Assimp
 
 
-#endif
-#endif
+#endif // ASSIMP_BUILD_NO_COLLADA_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 2 - 3
code/AssetLib/Collada/ColladaExporter.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -72,7 +71,7 @@ public:
     ColladaExporter(const aiScene *pScene, IOSystem *pIOSystem, const std::string &path, const std::string &file);
     ColladaExporter(const aiScene *pScene, IOSystem *pIOSystem, const std::string &path, const std::string &file);
 
 
     /// Destructor
     /// Destructor
-    virtual ~ColladaExporter();
+    virtual ~ColladaExporter() = default;
 
 
 protected:
 protected:
     /// Starts writing the contents
     /// Starts writing the contents

+ 1 - 1
code/AssetLib/Collada/ColladaHelper.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 1 - 1
code/AssetLib/Collada/ColladaHelper.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 57 - 56
code/AssetLib/Collada/ColladaLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -89,6 +89,14 @@ inline void AddNodeMetaData(aiNode *node, const std::string &key, const T &value
     node->mMetaData->Add(key, value);
     node->mMetaData->Add(key, value);
 }
 }
 
 
+// ------------------------------------------------------------------------------------------------
+// Reads a float value from an accessor and its data array.
+static ai_real ReadFloat(const Accessor &pAccessor, const Data &pData, size_t pIndex, size_t pOffset) {
+    size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset;
+    ai_assert(pos < pData.mValues.size());
+    return pData.mValues[pos];
+}
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 // Constructor to be privately used by Importer
 ColladaLoader::ColladaLoader() :
 ColladaLoader::ColladaLoader() :
@@ -152,7 +160,7 @@ void ColladaLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IO
         throw DeadlyImportError("Collada: File came out empty. Something is wrong here.");
         throw DeadlyImportError("Collada: File came out empty. Something is wrong here.");
     }
     }
 
 
-    // reserve some storage to avoid unnecessary reallocs
+    // reserve some storage to avoid unnecessary reallocates
     newMats.reserve(parser.mMaterialLibrary.size() * 2u);
     newMats.reserve(parser.mMaterialLibrary.size() * 2u);
     mMeshes.reserve(parser.mMeshLibrary.size() * 2u);
     mMeshes.reserve(parser.mMeshLibrary.size() * 2u);
 
 
@@ -176,7 +184,7 @@ void ColladaLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IO
                 0, 0, parser.mUnitSize, 0,
                 0, 0, parser.mUnitSize, 0,
                 0, 0, 0, 1);
                 0, 0, 0, 1);
     }
     }
-    
+
     if (!ignoreUpDirection) {
     if (!ignoreUpDirection) {
         // Convert to Y_UP, if different orientation
         // Convert to Y_UP, if different orientation
         if (parser.mUpDirection == ColladaParser::UP_X) {
         if (parser.mUpDirection == ColladaParser::UP_X) {
@@ -224,7 +232,7 @@ void ColladaLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IO
 // Recursively constructs a scene node for the given parser node and returns it.
 // Recursively constructs a scene node for the given parser node and returns it.
 aiNode *ColladaLoader::BuildHierarchy(const ColladaParser &pParser, const Collada::Node *pNode) {
 aiNode *ColladaLoader::BuildHierarchy(const ColladaParser &pParser, const Collada::Node *pNode) {
     // create a node for it
     // create a node for it
-    aiNode *node = new aiNode();
+    auto *node = new aiNode();
 
 
     // find a name for the new node. It's more complicated than you might think
     // find a name for the new node. It's more complicated than you might think
     node->mName.Set(FindNameForNode(pNode));
     node->mName.Set(FindNameForNode(pNode));
@@ -247,7 +255,9 @@ aiNode *ColladaLoader::BuildHierarchy(const ColladaParser &pParser, const Collad
 
 
     // add children. first the *real* ones
     // add children. first the *real* ones
     node->mNumChildren = static_cast<unsigned int>(pNode->mChildren.size() + instances.size());
     node->mNumChildren = static_cast<unsigned int>(pNode->mChildren.size() + instances.size());
-    node->mChildren = new aiNode *[node->mNumChildren];
+    if (node->mNumChildren != 0) {
+        node->mChildren = new aiNode * [node->mNumChildren];
+    }
 
 
     for (size_t a = 0; a < pNode->mChildren.size(); ++a) {
     for (size_t a = 0; a < pNode->mChildren.size(); ++a) {
         node->mChildren[a] = BuildHierarchy(pParser, pNode->mChildren[a]);
         node->mChildren[a] = BuildHierarchy(pParser, pNode->mChildren[a]);
@@ -270,24 +280,24 @@ aiNode *ColladaLoader::BuildHierarchy(const ColladaParser &pParser, const Collad
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Resolve node instances
 // Resolve node instances
 void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Node *pNode,
 void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Node *pNode,
-        std::vector<const Node*> &resolved) {
+        std::vector<const Node*> &resolved) const {
     // reserve enough storage
     // reserve enough storage
     resolved.reserve(pNode->mNodeInstances.size());
     resolved.reserve(pNode->mNodeInstances.size());
 
 
     // ... and iterate through all nodes to be instanced as children of pNode
     // ... and iterate through all nodes to be instanced as children of pNode
-    for (const auto &nodeInst : pNode->mNodeInstances) {
+    for (const auto &[mNode] : pNode->mNodeInstances) {
         // find the corresponding node in the library
         // find the corresponding node in the library
-        const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find(nodeInst.mNode);
+        const auto itt = pParser.mNodeLibrary.find(mNode);
         const Node *nd = itt == pParser.mNodeLibrary.end() ? nullptr : (*itt).second;
         const Node *nd = itt == pParser.mNodeLibrary.end() ? nullptr : (*itt).second;
 
 
         // FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632
         // FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632
         // need to check for both name and ID to catch all. To avoid breaking valid files,
         // need to check for both name and ID to catch all. To avoid breaking valid files,
         // the workaround is only enabled when the first attempt to resolve the node has failed.
         // the workaround is only enabled when the first attempt to resolve the node has failed.
         if (nullptr == nd) {
         if (nullptr == nd) {
-            nd = FindNode(pParser.mRootNode, nodeInst.mNode);
+            nd = FindNode(pParser.mRootNode, mNode);
         }
         }
         if (nullptr == nd) {
         if (nullptr == nd) {
-            ASSIMP_LOG_ERROR("Collada: Unable to resolve reference to instanced node ", nodeInst.mNode);
+            ASSIMP_LOG_ERROR("Collada: Unable to resolve reference to instanced node ", mNode);
         } else {
         } else {
             //  attach this node to the list of children
             //  attach this node to the list of children
             resolved.push_back(nd);
             resolved.push_back(nd);
@@ -297,8 +307,8 @@ void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Nod
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Resolve UV channels
 // Resolve UV channels
-void ColladaLoader::ApplyVertexToEffectSemanticMapping(Sampler &sampler, const SemanticMappingTable &table) {
-    SemanticMappingTable::InputSemanticMap::const_iterator it = table.mMap.find(sampler.mUVChannel);
+static void ApplyVertexToEffectSemanticMapping(Sampler &sampler, const SemanticMappingTable &table) {
+    const auto it = table.mMap.find(sampler.mUVChannel);
     if (it == table.mMap.end()) {
     if (it == table.mMap.end()) {
         return;
         return;
     }
     }
@@ -315,7 +325,7 @@ void ColladaLoader::ApplyVertexToEffectSemanticMapping(Sampler &sampler, const S
 void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) {
 void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) {
     for (const LightInstance &lid : pNode->mLights) {
     for (const LightInstance &lid : pNode->mLights) {
         // find the referred light
         // find the referred light
-        ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find(lid.mLight);
+        auto srcLightIt = pParser.mLightLibrary.find(lid.mLight);
         if (srcLightIt == pParser.mLightLibrary.end()) {
         if (srcLightIt == pParser.mLightLibrary.end()) {
             ASSIMP_LOG_WARN("Collada: Unable to find light for ID \"", lid.mLight, "\". Skipping.");
             ASSIMP_LOG_WARN("Collada: Unable to find light for ID \"", lid.mLight, "\". Skipping.");
             continue;
             continue;
@@ -323,7 +333,7 @@ void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node
         const Collada::Light *srcLight = &srcLightIt->second;
         const Collada::Light *srcLight = &srcLightIt->second;
 
 
         // now fill our ai data structure
         // now fill our ai data structure
-        aiLight *out = new aiLight();
+        auto out = new aiLight();
         out->mName = pTarget->mName;
         out->mName = pTarget->mName;
         out->mType = (aiLightSourceType)srcLight->mType;
         out->mType = (aiLightSourceType)srcLight->mType;
 
 
@@ -380,7 +390,7 @@ void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node
 void ColladaLoader::BuildCamerasForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) {
 void ColladaLoader::BuildCamerasForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) {
     for (const CameraInstance &cid : pNode->mCameras) {
     for (const CameraInstance &cid : pNode->mCameras) {
         // find the referred light
         // find the referred light
-        ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find(cid.mCamera);
+        auto srcCameraIt = pParser.mCameraLibrary.find(cid.mCamera);
         if (srcCameraIt == pParser.mCameraLibrary.end()) {
         if (srcCameraIt == pParser.mCameraLibrary.end()) {
             ASSIMP_LOG_WARN("Collada: Unable to find camera for ID \"", cid.mCamera, "\". Skipping.");
             ASSIMP_LOG_WARN("Collada: Unable to find camera for ID \"", cid.mCamera, "\". Skipping.");
             continue;
             continue;
@@ -393,7 +403,7 @@ void ColladaLoader::BuildCamerasForNode(const ColladaParser &pParser, const Node
         }
         }
 
 
         // now fill our ai data structure
         // now fill our ai data structure
-        aiCamera *out = new aiCamera();
+        auto *out = new aiCamera();
         out->mName = pTarget->mName;
         out->mName = pTarget->mName;
 
 
         // collada cameras point in -Z by default, rest is specified in node transform
         // collada cameras point in -Z by default, rest is specified in node transform
@@ -443,10 +453,10 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
         const Controller *srcController = nullptr;
         const Controller *srcController = nullptr;
 
 
         // find the referred mesh
         // find the referred mesh
-        ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find(mid.mMeshOrController);
+        auto srcMeshIt = pParser.mMeshLibrary.find(mid.mMeshOrController);
         if (srcMeshIt == pParser.mMeshLibrary.end()) {
         if (srcMeshIt == pParser.mMeshLibrary.end()) {
             // if not found in the mesh-library, it might also be a controller referring to a mesh
             // if not found in the mesh-library, it might also be a controller referring to a mesh
-            ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find(mid.mMeshOrController);
+            auto srcContrIt = pParser.mControllerLibrary.find(mid.mMeshOrController);
             if (srcContrIt != pParser.mControllerLibrary.end()) {
             if (srcContrIt != pParser.mControllerLibrary.end()) {
                 srcController = &srcContrIt->second;
                 srcController = &srcContrIt->second;
                 srcMeshIt = pParser.mMeshLibrary.find(srcController->mMeshId);
                 srcMeshIt = pParser.mMeshLibrary.find(srcController->mMeshId);
@@ -460,7 +470,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
                 continue;
                 continue;
             }
             }
         } else {
         } else {
-            // ID found in the mesh library -> direct reference to an unskinned mesh
+            // ID found in the mesh library -> direct reference to a not skinned mesh
             srcMesh = srcMeshIt->second;
             srcMesh = srcMeshIt->second;
         }
         }
 
 
@@ -474,7 +484,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
 
 
             // find material assigned to this submesh
             // find material assigned to this submesh
             std::string meshMaterial;
             std::string meshMaterial;
-            std::map<std::string, SemanticMappingTable>::const_iterator meshMatIt = mid.mMaterials.find(submesh.mMaterial);
+            auto meshMatIt = mid.mMaterials.find(submesh.mMaterial);
 
 
             const Collada::SemanticMappingTable *table = nullptr;
             const Collada::SemanticMappingTable *table = nullptr;
             if (meshMatIt != mid.mMaterials.end()) {
             if (meshMatIt != mid.mMaterials.end()) {
@@ -490,7 +500,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
 
 
             // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table
             // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table
             // given. The only mapping stuff which we do actually support is the UV channel.
             // given. The only mapping stuff which we do actually support is the UV channel.
-            std::map<std::string, size_t>::const_iterator matIt = mMaterialIndexByName.find(meshMaterial);
+            auto matIt = mMaterialIndexByName.find(meshMaterial);
             unsigned int matIdx = 0;
             unsigned int matIdx = 0;
             if (matIt != mMaterialIndexByName.end()) {
             if (matIt != mMaterialIndexByName.end()) {
                 matIdx = static_cast<unsigned int>(matIt->second);
                 matIdx = static_cast<unsigned int>(matIt->second);
@@ -513,7 +523,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
             ColladaMeshIndex index(mid.mMeshOrController, sm, meshMaterial);
             ColladaMeshIndex index(mid.mMeshOrController, sm, meshMaterial);
 
 
             // if we already have the mesh at the library, just add its index to the node's array
             // if we already have the mesh at the library, just add its index to the node's array
-            std::map<ColladaMeshIndex, size_t>::const_iterator dstMeshIt = mMeshIndexByID.find(index);
+            auto dstMeshIt = mMeshIndexByID.find(index);
             if (dstMeshIt != mMeshIndexByID.end()) {
             if (dstMeshIt != mMeshIndexByID.end()) {
                 newMeshRefs.push_back(dstMeshIt->second);
                 newMeshRefs.push_back(dstMeshIt->second);
             } else {
             } else {
@@ -528,7 +538,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
                 faceStart += submesh.mNumFaces;
                 faceStart += submesh.mNumFaces;
 
 
                 // assign the material index
                 // assign the material index
-                std::map<std::string, size_t>::const_iterator subMatIt = mMaterialIndexByName.find(submesh.mMaterial);
+                auto subMatIt = mMaterialIndexByName.find(submesh.mMaterial);
                 if (subMatIt != mMaterialIndexByName.end()) {
                 if (subMatIt != mMaterialIndexByName.end()) {
                     dstMesh->mMaterialIndex = static_cast<unsigned int>(subMatIt->second);
                     dstMesh->mMaterialIndex = static_cast<unsigned int>(subMatIt->second);
                 } else {
                 } else {
@@ -616,23 +626,21 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
         std::copy(pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() + pStartVertex + numVertices, dstMesh->mTangents);
         std::copy(pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() + pStartVertex + numVertices, dstMesh->mTangents);
     }
     }
 
 
-    // bitangents, if given.
+    // bi-tangents, if given.
     if (pSrcMesh->mBitangents.size() >= pStartVertex + numVertices) {
     if (pSrcMesh->mBitangents.size() >= pStartVertex + numVertices) {
         dstMesh->mBitangents = new aiVector3D[numVertices];
         dstMesh->mBitangents = new aiVector3D[numVertices];
         std::copy(pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() + pStartVertex + numVertices, dstMesh->mBitangents);
         std::copy(pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() + pStartVertex + numVertices, dstMesh->mBitangents);
     }
     }
 
 
     // same for texture coords, as many as we have
     // same for texture coords, as many as we have
-    // empty slots are not allowed, need to pack and adjust UV indexes accordingly
-    for (size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+    for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
         if (pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices) {
         if (pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices) {
-            dstMesh->mTextureCoords[real] = new aiVector3D[numVertices];
+            dstMesh->mTextureCoords[a] = new aiVector3D[numVertices];
             for (size_t b = 0; b < numVertices; ++b) {
             for (size_t b = 0; b < numVertices; ++b) {
-                dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex + b];
+                dstMesh->mTextureCoords[a][b] = pSrcMesh->mTexCoords[a][pStartVertex + b];
             }
             }
 
 
-            dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a];
-            ++real;
+            dstMesh->mNumUVComponents[a] = pSrcMesh->mNumUVComponents[a];
         }
         }
     }
     }
 
 
@@ -664,7 +672,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
     std::vector<float> targetWeights;
     std::vector<float> targetWeights;
     Collada::MorphMethod method = Normalized;
     Collada::MorphMethod method = Normalized;
 
 
-    for (std::map<std::string, Controller>::const_iterator it = pParser.mControllerLibrary.begin();
+    for (auto it = pParser.mControllerLibrary.begin();
             it != pParser.mControllerLibrary.end(); ++it) {
             it != pParser.mControllerLibrary.end(); ++it) {
         const Controller &c = it->second;
         const Controller &c = it->second;
         const Collada::Mesh *baseMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, c.mMeshId);
         const Collada::Mesh *baseMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, c.mMeshId);
@@ -754,7 +762,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
         std::vector<IndexPairVector::const_iterator> weightStartPerVertex;
         std::vector<IndexPairVector::const_iterator> weightStartPerVertex;
         weightStartPerVertex.resize(pSrcController->mWeightCounts.size(), pSrcController->mWeights.end());
         weightStartPerVertex.resize(pSrcController->mWeightCounts.size(), pSrcController->mWeights.end());
 
 
-        IndexPairVector::const_iterator pit = pSrcController->mWeights.begin();
+        auto pit = pSrcController->mWeights.begin();
         for (size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a) {
         for (size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a) {
             weightStartPerVertex[a] = pit;
             weightStartPerVertex[a] = pit;
             pit += pSrcController->mWeightCounts[a];
             pit += pSrcController->mWeightCounts[a];
@@ -766,7 +774,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
             // the controller assigns the vertex weights
             // the controller assigns the vertex weights
             size_t orgIndex = pSrcMesh->mFacePosIndices[a];
             size_t orgIndex = pSrcMesh->mFacePosIndices[a];
             // find the vertex weights for this vertex
             // find the vertex weights for this vertex
-            IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex];
+            auto iit = weightStartPerVertex[orgIndex];
             size_t pairCount = pSrcController->mWeightCounts[orgIndex];
             size_t pairCount = pSrcController->mWeightCounts[orgIndex];
 
 
             for (size_t b = 0; b < pairCount; ++b, ++iit) {
             for (size_t b = 0; b < pairCount; ++b, ++iit) {
@@ -807,7 +815,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
             }
             }
 
 
             // create bone with its weights
             // create bone with its weights
-            aiBone *bone = new aiBone;
+            auto bone = new aiBone;
             bone->mName = ReadString(jointNamesAcc, jointNames, a);
             bone->mName = ReadString(jointNamesAcc, jointNames, a);
             bone->mOffsetMatrix.a1 = ReadFloat(jointMatrixAcc, jointMatrices, a, 0);
             bone->mOffsetMatrix.a1 = ReadFloat(jointMatrixAcc, jointMatrices, a, 0);
             bone->mOffsetMatrix.a2 = ReadFloat(jointMatrixAcc, jointMatrices, a, 1);
             bone->mOffsetMatrix.a2 = ReadFloat(jointMatrixAcc, jointMatrices, a, 1);
@@ -973,7 +981,7 @@ void ColladaLoader::StoreAnimations(aiScene *pScene, const ColladaParser &pParse
 
 
             // if there are other animations which fit the template anim, combine all channels into a single anim
             // if there are other animations which fit the template anim, combine all channels into a single anim
             if (!collectedAnimIndices.empty()) {
             if (!collectedAnimIndices.empty()) {
-                aiAnimation *combinedAnim = new aiAnimation();
+                auto *combinedAnim = new aiAnimation();
                 combinedAnim->mName = aiString(std::string("combinedAnim_") + char('0' + a));
                 combinedAnim->mName = aiString(std::string("combinedAnim_") + char('0' + a));
                 combinedAnim->mDuration = templateAnim->mDuration;
                 combinedAnim->mDuration = templateAnim->mDuration;
                 combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond;
                 combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond;
@@ -1040,7 +1048,7 @@ struct MorphTimeValues {
 };
 };
 
 
 void insertMorphTimeValue(std::vector<MorphTimeValues> &values, float time, float weight, unsigned int value) {
 void insertMorphTimeValue(std::vector<MorphTimeValues> &values, float time, float weight, unsigned int value) {
-    MorphTimeValues::key k;
+    MorphTimeValues::key k{};
     k.mValue = value;
     k.mValue = value;
     k.mWeight = weight;
     k.mWeight = weight;
     if (values.empty() || time < values[0].mTime) {
     if (values.empty() || time < values[0].mTime) {
@@ -1077,6 +1085,7 @@ static float getWeightAtKey(const std::vector<MorphTimeValues> &values, int key,
             return mKey.mWeight;
             return mKey.mWeight;
         }
         }
     }
     }
+
     // no value at key found, try to interpolate if present at other keys. if not, return zero
     // no value at key found, try to interpolate if present at other keys. if not, return zero
     // TODO: interpolation
     // TODO: interpolation
     return 0.0f;
     return 0.0f;
@@ -1105,7 +1114,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
 
 
         // now check all channels if they affect the current node
         // now check all channels if they affect the current node
         std::string targetID, subElement;
         std::string targetID, subElement;
-        for (std::vector<AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin();
+        for (auto cit = pSrcAnim->mChannels.begin();
                 cit != pSrcAnim->mChannels.end(); ++cit) {
                 cit != pSrcAnim->mChannels.end(); ++cit) {
             const AnimationChannel &srcChannel = *cit;
             const AnimationChannel &srcChannel = *cit;
             ChannelEntry entry;
             ChannelEntry entry;
@@ -1348,7 +1357,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
 
 
         // build an animation channel for the given node out of these trafo keys
         // build an animation channel for the given node out of these trafo keys
         if (!resultTrafos.empty()) {
         if (!resultTrafos.empty()) {
-            aiNodeAnim *dstAnim = new aiNodeAnim;
+            auto *dstAnim = new aiNodeAnim;
             dstAnim->mNodeName = nodeName;
             dstAnim->mNodeName = nodeName;
             dstAnim->mNumPositionKeys = static_cast<unsigned int>(resultTrafos.size());
             dstAnim->mNumPositionKeys = static_cast<unsigned int>(resultTrafos.size());
             dstAnim->mNumRotationKeys = static_cast<unsigned int>(resultTrafos.size());
             dstAnim->mNumRotationKeys = static_cast<unsigned int>(resultTrafos.size());
@@ -1390,7 +1399,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
                 // or     2) one channel with morph target count arrays
                 // or     2) one channel with morph target count arrays
                 // assume first
                 // assume first
 
 
-                aiMeshMorphAnim *morphAnim = new aiMeshMorphAnim;
+                auto *morphAnim = new aiMeshMorphAnim;
                 morphAnim->mName.Set(nodeName);
                 morphAnim->mName.Set(nodeName);
 
 
                 std::vector<MorphTimeValues> morphTimeValues;
                 std::vector<MorphTimeValues> morphTimeValues;
@@ -1433,7 +1442,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
     }
     }
 
 
     if (!anims.empty() || !morphAnims.empty()) {
     if (!anims.empty() || !morphAnims.empty()) {
-        aiAnimation *anim = new aiAnimation;
+        auto anim = new aiAnimation;
         anim->mName.Set(pName);
         anim->mName.Set(pName);
         anim->mNumChannels = static_cast<unsigned int>(anims.size());
         anim->mNumChannels = static_cast<unsigned int>(anims.size());
         if (anim->mNumChannels > 0) {
         if (anim->mNumChannels > 0) {
@@ -1513,7 +1522,7 @@ void ColladaLoader::AddTexture(aiMaterial &mat,
         map = sampler.mUVId;
         map = sampler.mUVId;
     } else {
     } else {
         map = -1;
         map = -1;
-        for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) {
+        for (auto it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) {
             if (IsNumeric(*it)) {
             if (IsNumeric(*it)) {
                 map = strtoul10(&(*it));
                 map = strtoul10(&(*it));
                 break;
                 break;
@@ -1531,7 +1540,7 @@ void ColladaLoader::AddTexture(aiMaterial &mat,
 // Fills materials from the collada material definitions
 // Fills materials from the collada material definitions
 void ColladaLoader::FillMaterials(const ColladaParser &pParser, aiScene * /*pScene*/) {
 void ColladaLoader::FillMaterials(const ColladaParser &pParser, aiScene * /*pScene*/) {
     for (auto &elem : newMats) {
     for (auto &elem : newMats) {
-        aiMaterial &mat = (aiMaterial &)*elem.second;
+        auto &mat = (aiMaterial &)*elem.second;
         Collada::Effect &effect = *elem.first;
         Collada::Effect &effect = *elem.first;
 
 
         // resolve shading mode
         // resolve shading mode
@@ -1641,17 +1650,17 @@ void ColladaLoader::FillMaterials(const ColladaParser &pParser, aiScene * /*pSce
 void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) {
 void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) {
     newMats.reserve(pParser.mMaterialLibrary.size());
     newMats.reserve(pParser.mMaterialLibrary.size());
 
 
-    for (ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin();
+    for (auto matIt = pParser.mMaterialLibrary.begin();
             matIt != pParser.mMaterialLibrary.end(); ++matIt) {
             matIt != pParser.mMaterialLibrary.end(); ++matIt) {
         const Material &material = matIt->second;
         const Material &material = matIt->second;
         // a material is only a reference to an effect
         // a material is only a reference to an effect
-        ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find(material.mEffect);
+        auto effIt = pParser.mEffectLibrary.find(material.mEffect);
         if (effIt == pParser.mEffectLibrary.end())
         if (effIt == pParser.mEffectLibrary.end())
             continue;
             continue;
         Effect &effect = effIt->second;
         Effect &effect = effIt->second;
 
 
         // create material
         // create material
-        aiMaterial *mat = new aiMaterial;
+        auto *mat = new aiMaterial;
         aiString name(material.mName.empty() ? matIt->first : material.mName);
         aiString name(material.mName.empty() ? matIt->first : material.mName);
         mat->AddProperty(&name, AI_MATKEY_NAME);
         mat->AddProperty(&name, AI_MATKEY_NAME);
 
 
@@ -1674,7 +1683,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse
     std::string name = pName;
     std::string name = pName;
     while (true) {
     while (true) {
         // the given string is a param entry. Find it
         // the given string is a param entry. Find it
-        Effect::ParamLibrary::const_iterator it = pEffect.mParams.find(name);
+        auto it = pEffect.mParams.find(name);
         // if not found, we're at the end of the recursion. The resulting string should be the image ID
         // if not found, we're at the end of the recursion. The resulting string should be the image ID
         if (it == pEffect.mParams.end())
         if (it == pEffect.mParams.end())
             break;
             break;
@@ -1684,7 +1693,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse
     }
     }
 
 
     // find the image referred by this name in the image library of the scene
     // find the image referred by this name in the image library of the scene
-    ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find(name);
+    auto imIt = pParser.mImageLibrary.find(name);
     if (imIt == pParser.mImageLibrary.end()) {
     if (imIt == pParser.mImageLibrary.end()) {
         ASSIMP_LOG_WARN("Collada: Unable to resolve effect texture entry \"", pName, "\", ended up at ID \"", name, "\".");
         ASSIMP_LOG_WARN("Collada: Unable to resolve effect texture entry \"", pName, "\", ended up at ID \"", name, "\".");
 
 
@@ -1696,7 +1705,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse
 
 
     // if this is an embedded texture image setup an aiTexture for it
     // if this is an embedded texture image setup an aiTexture for it
     if (!imIt->second.mImageData.empty()) {
     if (!imIt->second.mImageData.empty()) {
-        aiTexture *tex = new aiTexture();
+        auto *tex = new aiTexture();
 
 
         // Store embedded texture name reference
         // Store embedded texture name reference
         tex->mFilename.Set(imIt->second.mFileName.c_str());
         tex->mFilename.Set(imIt->second.mFileName.c_str());
@@ -1728,14 +1737,6 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse
     return result;
     return result;
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-// Reads a float value from an accessor and its data array.
-ai_real ColladaLoader::ReadFloat(const Accessor &pAccessor, const Data &pData, size_t pIndex, size_t pOffset) const {
-    size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset;
-    ai_assert(pos < pData.mValues.size());
-    return pData.mValues[pos];
-}
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Reads a string value from an accessor and its data array.
 // Reads a string value from an accessor and its data array.
 const std::string &ColladaLoader::ReadString(const Accessor &pAccessor, const Data &pData, size_t pIndex) const {
 const std::string &ColladaLoader::ReadString(const Accessor &pAccessor, const Data &pData, size_t pIndex) const {

+ 25 - 35
code/AssetLib/Collada/ColladaLoader.h

@@ -4,8 +4,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -77,8 +76,11 @@ struct ColladaMeshIndex {
     }
     }
 };
 };
 
 
-/** Loader class to read Collada scenes. Collada is over-engineered to death, with every new iteration bringing
- * more useless stuff, so I limited the data to what I think is useful for games.
+/**
+ * @brief Loader class to read Collada scenes.
+ *
+ * Collada is over-engineered to death, with every new iteration bringing  more useless stuff,
+ * so I limited the data to what I think is useful for games.
 */
 */
 class ColladaLoader : public BaseImporter {
 class ColladaLoader : public BaseImporter {
 public:
 public:
@@ -102,50 +104,51 @@ protected:
     /// See #BaseImporter::InternReadFile for the details
     /// See #BaseImporter::InternReadFile for the details
     void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
     void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
 
 
-    /** Recursively constructs a scene node for the given parser node and returns it. */
+    /// Recursively constructs a scene node for the given parser node and returns it.
     aiNode *BuildHierarchy(const ColladaParser &pParser, const Collada::Node *pNode);
     aiNode *BuildHierarchy(const ColladaParser &pParser, const Collada::Node *pNode);
 
 
-    /** Resolve node instances */
+    /// Resolve node instances
     void ResolveNodeInstances(const ColladaParser &pParser, const Collada::Node *pNode,
     void ResolveNodeInstances(const ColladaParser &pParser, const Collada::Node *pNode,
-            std::vector<const Collada::Node *> &resolved);
+            std::vector<const Collada::Node *> &resolved) const;
 
 
-    /** Builds meshes for the given node and references them */
+    /// Builds meshes for the given node and references them
     void BuildMeshesForNode(const ColladaParser &pParser, const Collada::Node *pNode,
     void BuildMeshesForNode(const ColladaParser &pParser, const Collada::Node *pNode,
             aiNode *pTarget);
             aiNode *pTarget);
 
 
+    /// Lookup for meshes by their name
     aiMesh *findMesh(const std::string &meshid);
     aiMesh *findMesh(const std::string &meshid);
 
 
-    /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */
+    /// Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh
     aiMesh *CreateMesh(const ColladaParser &pParser, const Collada::Mesh *pSrcMesh, const Collada::SubMesh &pSubMesh,
     aiMesh *CreateMesh(const ColladaParser &pParser, const Collada::Mesh *pSrcMesh, const Collada::SubMesh &pSubMesh,
             const Collada::Controller *pSrcController, size_t pStartVertex, size_t pStartFace);
             const Collada::Controller *pSrcController, size_t pStartVertex, size_t pStartFace);
 
 
-    /** Builds cameras for the given node and references them */
+    /// Builds cameras for the given node and references them
     void BuildCamerasForNode(const ColladaParser &pParser, const Collada::Node *pNode,
     void BuildCamerasForNode(const ColladaParser &pParser, const Collada::Node *pNode,
             aiNode *pTarget);
             aiNode *pTarget);
 
 
-    /** Builds lights for the given node and references them */
+    /// Builds lights for the given node and references them
     void BuildLightsForNode(const ColladaParser &pParser, const Collada::Node *pNode,
     void BuildLightsForNode(const ColladaParser &pParser, const Collada::Node *pNode,
             aiNode *pTarget);
             aiNode *pTarget);
 
 
-    /** Stores all meshes in the given scene */
+    /// Stores all meshes in the given scene
     void StoreSceneMeshes(aiScene *pScene);
     void StoreSceneMeshes(aiScene *pScene);
 
 
-    /** Stores all materials in the given scene */
+    /// Stores all materials in the given scene
     void StoreSceneMaterials(aiScene *pScene);
     void StoreSceneMaterials(aiScene *pScene);
 
 
-    /** Stores all lights in the given scene */
+    /// Stores all lights in the given scene
     void StoreSceneLights(aiScene *pScene);
     void StoreSceneLights(aiScene *pScene);
 
 
-    /** Stores all cameras in the given scene */
+    /// Stores all cameras in the given scene
     void StoreSceneCameras(aiScene *pScene);
     void StoreSceneCameras(aiScene *pScene);
 
 
-    /** Stores all textures in the given scene */
+    /// Stores all textures in the given scene
     void StoreSceneTextures(aiScene *pScene);
     void StoreSceneTextures(aiScene *pScene);
 
 
-    /** Stores all animations
-     * @param pScene target scene to store the anims
-     */
-    void StoreAnimations(aiScene *pScene, const ColladaParser &pParser);
+    /// Stores all animations
+    /// @param pScene   Target scene to store the anims
+    /// @param parser   The collada parser
+    void StoreAnimations(aiScene *pScene, const ColladaParser &parser);
 
 
     /** Stores all animations for the given source anim and its nested child animations
     /** Stores all animations for the given source anim and its nested child animations
      * @param pScene target scene to store the anims
      * @param pScene target scene to store the anims
@@ -163,10 +166,6 @@ protected:
     /** Fill materials from the collada material definitions */
     /** Fill materials from the collada material definitions */
     void FillMaterials(const ColladaParser &pParser, aiScene *pScene);
     void FillMaterials(const ColladaParser &pParser, aiScene *pScene);
 
 
-    /** Resolve UV channel mappings*/
-    void ApplyVertexToEffectSemanticMapping(Collada::Sampler &sampler,
-            const Collada::SemanticMappingTable &table);
-
     /** Add a texture and all of its sampling properties to a material*/
     /** Add a texture and all of its sampling properties to a material*/
     void AddTexture(aiMaterial &mat, const ColladaParser &pParser,
     void AddTexture(aiMaterial &mat, const ColladaParser &pParser,
             const Collada::Effect &effect,
             const Collada::Effect &effect,
@@ -177,22 +176,13 @@ protected:
     aiString FindFilenameForEffectTexture(const ColladaParser &pParser,
     aiString FindFilenameForEffectTexture(const ColladaParser &pParser,
             const Collada::Effect &pEffect, const std::string &pName);
             const Collada::Effect &pEffect, const std::string &pName);
 
 
-    /** Reads a float value from an accessor and its data array.
-     * @param pAccessor The accessor to use for reading
-     * @param pData The data array to read from
-     * @param pIndex The index of the element to retrieve
-     * @param pOffset Offset into the element, for multipart elements such as vectors or matrices
-     * @return the specified value
-     */
-    ai_real ReadFloat(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex, size_t pOffset) const;
-
     /** Reads a string value from an accessor and its data array.
     /** Reads a string value from an accessor and its data array.
      * @param pAccessor The accessor to use for reading
      * @param pAccessor The accessor to use for reading
      * @param pData The data array to read from
      * @param pData The data array to read from
      * @param pIndex The index of the element to retrieve
      * @param pIndex The index of the element to retrieve
      * @return the specified value
      * @return the specified value
      */
      */
-    const std::string &ReadString(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex) const;
+    [[nodiscard]] const std::string &ReadString(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex) const;
 
 
     /** Recursively collects all nodes into the given array */
     /** Recursively collects all nodes into the given array */
     void CollectNodes(const aiNode *pNode, std::vector<const aiNode *> &poNodes) const;
     void CollectNodes(const aiNode *pNode, std::vector<const aiNode *> &poNodes) const;
@@ -205,7 +195,7 @@ protected:
     /** Finds a proper name for a node derived from the collada-node's properties */
     /** Finds a proper name for a node derived from the collada-node's properties */
     std::string FindNameForNode(const Collada::Node *pNode);
     std::string FindNameForNode(const Collada::Node *pNode);
 
 
-protected:
+private:
     /** Filename, for a verbose error message */
     /** Filename, for a verbose error message */
     std::string mFileName;
     std::string mFileName;
 
 

Разница между файлами не показана из-за своего большого размера
+ 371 - 329
code/AssetLib/Collada/ColladaParser.cpp


+ 83 - 112
code/AssetLib/Collada/ColladaParser.h

@@ -2,7 +2,7 @@
  Open Asset Import Library (assimp)
  Open Asset Import Library (assimp)
  ----------------------------------------------------------------------
  ----------------------------------------------------------------------
 
 
- Copyright (c) 2006-2022, assimp team
+ Copyright (c) 2006-2025, assimp team
 
 
  All rights reserved.
  All rights reserved.
 
 
@@ -48,7 +48,6 @@
 #define AI_COLLADAPARSER_H_INC
 #define AI_COLLADAPARSER_H_INC
 
 
 #include "ColladaHelper.h"
 #include "ColladaHelper.h"
-#include <assimp/TinyFormatter.h>
 #include <assimp/ai_assert.h>
 #include <assimp/ai_assert.h>
 #include <assimp/XmlParser.h>
 #include <assimp/XmlParser.h>
 
 
@@ -67,268 +66,240 @@ class ZipArchiveIOSystem;
 class ColladaParser {
 class ColladaParser {
     friend class ColladaLoader;
     friend class ColladaLoader;
 
 
-    /** Converts a path read from a collada file to the usual representation */
-    static void UriDecodePath(aiString &ss);
-
-protected:
-    /** Map for generic metadata as aiString */
-    typedef std::map<std::string, aiString> StringMetaData;
+public:
+    /// Map for generic metadata as aiString.
+    using StringMetaData = std::map<std::string, aiString>;
 
 
-    /** Constructor from XML file */
+    /// Constructor from XML file.
     ColladaParser(IOSystem *pIOHandler, const std::string &pFile);
     ColladaParser(IOSystem *pIOHandler, const std::string &pFile);
 
 
-    /** Destructor */
+    /// Destructor
     ~ColladaParser();
     ~ColladaParser();
 
 
-    /** Attempts to read the ZAE manifest and returns the DAE to open */
+    /// Attempts to read the ZAE manifest and returns the DAE to open
     static std::string ReadZaeManifest(ZipArchiveIOSystem &zip_archive);
     static std::string ReadZaeManifest(ZipArchiveIOSystem &zip_archive);
 
 
-    /** Reads the contents of the file */
+    /// Reads the contents of the file
     void ReadContents(XmlNode &node);
     void ReadContents(XmlNode &node);
 
 
-    /** Reads the structure of the file */
+    /// Reads the structure of the file
     void ReadStructure(XmlNode &node);
     void ReadStructure(XmlNode &node);
 
 
-    /** Reads asset information such as coordinate system information and legal blah */
+    /// Reads asset information such as coordinate system information and legal blah
     void ReadAssetInfo(XmlNode &node);
     void ReadAssetInfo(XmlNode &node);
 
 
-    /** Reads contributor information such as author and legal blah */
+    /// Reads contributor information such as author and legal blah
     void ReadContributorInfo(XmlNode &node);
     void ReadContributorInfo(XmlNode &node);
 
 
-    /** Reads generic metadata into provided map and renames keys for Assimp */
-    void ReadMetaDataItem(XmlNode &node, StringMetaData &metadata);
-
-    /** Reads the animation library */
+    /// Reads the animation library
     void ReadAnimationLibrary(XmlNode &node);
     void ReadAnimationLibrary(XmlNode &node);
 
 
-    /** Reads the animation clip library */
+    /// Reads the animation clip library
     void ReadAnimationClipLibrary(XmlNode &node);
     void ReadAnimationClipLibrary(XmlNode &node);
 
 
-    /** Unwrap controllers dependency hierarchy */
+    /// Unwrap controllers dependency hierarchy
     void PostProcessControllers();
     void PostProcessControllers();
 
 
-    /** Re-build animations from animation clip library, if present, otherwise combine single-channel animations */
+    /// Re-build animations from animation clip library, if present, otherwise combine single-channel animations
     void PostProcessRootAnimations();
     void PostProcessRootAnimations();
 
 
-    /** Reads an animation into the given parent structure */
+    /// Reads an animation into the given parent structure
     void ReadAnimation(XmlNode &node, Collada::Animation *pParent);
     void ReadAnimation(XmlNode &node, Collada::Animation *pParent);
 
 
-    /** Reads an animation sampler into the given anim channel */
-    void ReadAnimationSampler(XmlNode &node, Collada::AnimationChannel &pChannel);
-
-    /** Reads the skeleton controller library */
+    /// Reads the skeleton controller library
     void ReadControllerLibrary(XmlNode &node);
     void ReadControllerLibrary(XmlNode &node);
 
 
-    /** Reads a controller into the given mesh structure */
+    /// Reads a controller into the given mesh structure
     void ReadController(XmlNode &node, Collada::Controller &pController);
     void ReadController(XmlNode &node, Collada::Controller &pController);
 
 
-    /** Reads the joint definitions for the given controller */
-    void ReadControllerJoints(XmlNode &node, Collada::Controller &pController);
-
-    /** Reads the joint weights for the given controller */
-    void ReadControllerWeights(XmlNode &node, Collada::Controller &pController);
+    /// Reads the image library contents
+    void ReadImageLibrary(const XmlNode &node);
 
 
-    /** Reads the image library contents */
-    void ReadImageLibrary(XmlNode &node);
+    /// Reads an image entry into the given image
+    void ReadImage(const XmlNode &node, Collada::Image &pImage) const;
 
 
-    /** Reads an image entry into the given image */
-    void ReadImage(XmlNode &node, Collada::Image &pImage);
-
-    /** Reads the material library */
+    /// Reads the material library
     void ReadMaterialLibrary(XmlNode &node);
     void ReadMaterialLibrary(XmlNode &node);
 
 
-    /** Reads a material entry into the given material */
-    void ReadMaterial(XmlNode &node, Collada::Material &pMaterial);
-
-    /** Reads the camera library */
+    /// Reads the camera library
     void ReadCameraLibrary(XmlNode &node);
     void ReadCameraLibrary(XmlNode &node);
 
 
-    /** Reads a camera entry into the given camera */
-    void ReadCamera(XmlNode &node, Collada::Camera &pCamera);
-
-    /** Reads the light library */
+    /// Reads the light library
     void ReadLightLibrary(XmlNode &node);
     void ReadLightLibrary(XmlNode &node);
 
 
-    /** Reads a light entry into the given light */
-    void ReadLight(XmlNode &node, Collada::Light &pLight);
-
-    /** Reads the effect library */
+    /// Reads the effect library
     void ReadEffectLibrary(XmlNode &node);
     void ReadEffectLibrary(XmlNode &node);
 
 
-    /** Reads an effect entry into the given effect*/
+    /// Reads an effect entry into the given effect
     void ReadEffect(XmlNode &node, Collada::Effect &pEffect);
     void ReadEffect(XmlNode &node, Collada::Effect &pEffect);
 
 
-    /** Reads an COMMON effect profile */
+    /// Reads an COMMON effect profile
     void ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEffect);
     void ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEffect);
 
 
-    /** Read sampler properties */
+    /// Read sampler properties
     void ReadSamplerProperties(XmlNode &node, Collada::Sampler &pSampler);
     void ReadSamplerProperties(XmlNode &node, Collada::Sampler &pSampler);
 
 
-    /** Reads an effect entry containing a color or a texture defining that color */
+    /// Reads an effect entry containing a color or a texture defining that color
     void ReadEffectColor(XmlNode &node, aiColor4D &pColor, Collada::Sampler &pSampler);
     void ReadEffectColor(XmlNode &node, aiColor4D &pColor, Collada::Sampler &pSampler);
 
 
-    /** Reads an effect entry containing a float */
+    /// Reads an effect entry containing a float
     void ReadEffectFloat(XmlNode &node, ai_real &pFloat);
     void ReadEffectFloat(XmlNode &node, ai_real &pFloat);
 
 
-    /** Reads an effect parameter specification of any kind */
+    /// Reads an effect parameter specification of any kind
     void ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam);
     void ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam);
 
 
-    /** Reads the geometry library contents */
+    /// Reads the geometry library contents
     void ReadGeometryLibrary(XmlNode &node);
     void ReadGeometryLibrary(XmlNode &node);
 
 
-    /** Reads a geometry from the geometry library. */
+    /// Reads a geometry from the geometry library.
     void ReadGeometry(XmlNode &node, Collada::Mesh &pMesh);
     void ReadGeometry(XmlNode &node, Collada::Mesh &pMesh);
 
 
-    /** Reads a mesh from the geometry library */
+    /// Reads a mesh from the geometry library
     void ReadMesh(XmlNode &node, Collada::Mesh &pMesh);
     void ReadMesh(XmlNode &node, Collada::Mesh &pMesh);
 
 
-    /** Reads a source element - a combination of raw data and an accessor defining
-         * things that should not be redefinable. Yes, that's another rant.
-         */
+    /// Reads a source element - a combination of raw data and an accessor defining
+    ///things that should not be definable. Yes, that's another rant.
     void ReadSource(XmlNode &node);
     void ReadSource(XmlNode &node);
 
 
-    /** Reads a data array holding a number of elements, and stores it in the global library.
-         * Currently supported are array of floats and arrays of strings.
-         */
+    /// Reads a data array holding a number of elements, and stores it in the global library.
+    /// Currently supported are array of floats and arrays of strings.
     void ReadDataArray(XmlNode &node);
     void ReadDataArray(XmlNode &node);
 
 
-    /** Reads an accessor and stores it in the global library under the given ID -
-         * accessors use the ID of the parent <source> element
-         */
+    /// Reads an accessor and stores it in the global library under the given ID -
+    /// accessors use the ID of the parent <source> element
     void ReadAccessor(XmlNode &node, const std::string &pID);
     void ReadAccessor(XmlNode &node, const std::string &pID);
 
 
-    /** Reads input declarations of per-vertex mesh data into the given mesh */
+    /// Reads input declarations of per-vertex mesh data into the given mesh
     void ReadVertexData(XmlNode &node, Collada::Mesh &pMesh);
     void ReadVertexData(XmlNode &node, Collada::Mesh &pMesh);
 
 
-    /** Reads input declarations of per-index mesh data into the given mesh */
+    /// Reads input declarations of per-index mesh data into the given mesh
     void ReadIndexData(XmlNode &node, Collada::Mesh &pMesh);
     void ReadIndexData(XmlNode &node, Collada::Mesh &pMesh);
 
 
-    /** Reads a single input channel element and stores it in the given array, if valid */
+    /// Reads a single input channel element and stores it in the given array, if valid
     void ReadInputChannel(XmlNode &node, std::vector<Collada::InputChannel> &poChannels);
     void ReadInputChannel(XmlNode &node, std::vector<Collada::InputChannel> &poChannels);
 
 
-    /** Reads a <p> primitive index list and assembles the mesh data into the given mesh */
+    /// Reads a <p> primitive index list and assembles the mesh data into the given mesh
     size_t ReadPrimitives(XmlNode &node, Collada::Mesh &pMesh, std::vector<Collada::InputChannel> &pPerIndexChannels,
     size_t ReadPrimitives(XmlNode &node, Collada::Mesh &pMesh, std::vector<Collada::InputChannel> &pPerIndexChannels,
             size_t pNumPrimitives, const std::vector<size_t> &pVCount, Collada::PrimitiveType pPrimType);
             size_t pNumPrimitives, const std::vector<size_t> &pVCount, Collada::PrimitiveType pPrimType);
 
 
-    /** Copies the data for a single primitive into the mesh, based on the InputChannels */
+    /// Copies the data for a single primitive into the mesh, based on the InputChannels
     void CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset,
     void CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset,
             Collada::Mesh &pMesh, std::vector<Collada::InputChannel> &pPerIndexChannels,
             Collada::Mesh &pMesh, std::vector<Collada::InputChannel> &pPerIndexChannels,
             size_t currentPrimitive, const std::vector<size_t> &indices);
             size_t currentPrimitive, const std::vector<size_t> &indices);
 
 
-    /** Reads one triangle of a tristrip into the mesh */
+    /// Reads one triangle of a tristrip into the mesh
     void ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Collada::Mesh &pMesh,
     void ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Collada::Mesh &pMesh,
             std::vector<Collada::InputChannel> &pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t> &indices);
             std::vector<Collada::InputChannel> &pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t> &indices);
 
 
-    /** Extracts a single object from an input channel and stores it in the appropriate mesh data array */
+    /// Extracts a single object from an input channel and stores it in the appropriate mesh data array
     void ExtractDataObjectFromChannel(const Collada::InputChannel &pInput, size_t pLocalIndex, Collada::Mesh &pMesh);
     void ExtractDataObjectFromChannel(const Collada::InputChannel &pInput, size_t pLocalIndex, Collada::Mesh &pMesh);
 
 
-    /** Reads the library of node hierarchies and scene parts */
+    /// Reads the library of node hierarchies and scene parts
     void ReadSceneLibrary(XmlNode &node);
     void ReadSceneLibrary(XmlNode &node);
 
 
-    /** Reads a scene node's contents including children and stores it in the given node */
+    /// Reads a scene node's contents including children and stores it in the given node
     void ReadSceneNode(XmlNode &node, Collada::Node *pNode);
     void ReadSceneNode(XmlNode &node, Collada::Node *pNode);
-
-    /** Reads a node transformation entry of the given type and adds it to the given node's transformation list. */
-    void ReadNodeTransformation(XmlNode &node, Collada::Node *pNode, Collada::TransformType pType);
-
-    /** Reads a mesh reference in a node and adds it to the node's mesh list */
+    
+    /// Reads a mesh reference in a node and adds it to the node's mesh list
     void ReadNodeGeometry(XmlNode &node, Collada::Node *pNode);
     void ReadNodeGeometry(XmlNode &node, Collada::Node *pNode);
 
 
-    /** Reads the collada scene */
+    /// Reads the collada scene
     void ReadScene(XmlNode &node);
     void ReadScene(XmlNode &node);
 
 
-    // Processes bind_vertex_input and bind elements
+    /// Processes bind_vertex_input and bind elements
     void ReadMaterialVertexInputBinding(XmlNode &node, Collada::SemanticMappingTable &tbl);
     void ReadMaterialVertexInputBinding(XmlNode &node, Collada::SemanticMappingTable &tbl);
 
 
-    /** Reads embedded textures from a ZAE archive*/
+    /// Reads embedded textures from a ZAE archive
     void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive);
     void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive);
 
 
 protected:
 protected:
-    /** Calculates the resulting transformation from all the given transform steps */
+    /// Converts a path read from a collada file to the usual representation
+    static void UriDecodePath(aiString &ss);
+
+    /// Calculates the resulting transformation from all the given transform steps
     aiMatrix4x4 CalculateResultTransform(const std::vector<Collada::Transform> &pTransforms) const;
     aiMatrix4x4 CalculateResultTransform(const std::vector<Collada::Transform> &pTransforms) const;
 
 
-    /** Determines the input data type for the given semantic string */
+    /// Determines the input data type for the given semantic string
     Collada::InputType GetTypeForSemantic(const std::string &pSemantic);
     Collada::InputType GetTypeForSemantic(const std::string &pSemantic);
 
 
-    /** Finds the item in the given library by its reference, throws if not found */
+    /// Finds the item in the given library by its reference, throws if not found
     template <typename Type>
     template <typename Type>
     const Type &ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const;
     const Type &ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const;
 
 
-protected:
-    // Filename, for a verbose error message
+private:
+    /// Filename, for a verbose error message
     std::string mFileName;
     std::string mFileName;
 
 
-    // XML reader, member for everyday use
+    /// XML reader, member for everyday use
     XmlParser mXmlParser;
     XmlParser mXmlParser;
 
 
-    /** All data arrays found in the file by ID. Might be referred to by actually
-         everyone. Collada, you are a steaming pile of indirection. */
+    /// All data arrays found in the file by ID. Might be referred to by actually
+    ///     everyone. Collada, you are a steaming pile of indirection.
     using DataLibrary = std::map<std::string, Collada::Data> ;
     using DataLibrary = std::map<std::string, Collada::Data> ;
     DataLibrary mDataLibrary;
     DataLibrary mDataLibrary;
 
 
-    /** Same for accessors which define how the data in a data array is accessed. */
+    /// Same for accessors which define how the data in a data array is accessed.
     using AccessorLibrary = std::map<std::string, Collada::Accessor> ;
     using AccessorLibrary = std::map<std::string, Collada::Accessor> ;
     AccessorLibrary mAccessorLibrary;
     AccessorLibrary mAccessorLibrary;
 
 
-    /** Mesh library: mesh by ID */
+    /// Mesh library: mesh by ID
     using MeshLibrary = std::map<std::string, Collada::Mesh *>;
     using MeshLibrary = std::map<std::string, Collada::Mesh *>;
     MeshLibrary mMeshLibrary;
     MeshLibrary mMeshLibrary;
 
 
-    /** node library: root node of the hierarchy part by ID */
+    /// node library: root node of the hierarchy part by ID
     using NodeLibrary = std::map<std::string, Collada::Node *>;
     using NodeLibrary = std::map<std::string, Collada::Node *>;
     NodeLibrary mNodeLibrary;
     NodeLibrary mNodeLibrary;
 
 
-    /** Image library: stores texture properties by ID */
+    /// Image library: stores texture properties by ID
     using ImageLibrary = std::map<std::string, Collada::Image> ;
     using ImageLibrary = std::map<std::string, Collada::Image> ;
     ImageLibrary mImageLibrary;
     ImageLibrary mImageLibrary;
 
 
-    /** Effect library: surface attributes by ID */
+    /// Effect library: surface attributes by ID
     using EffectLibrary = std::map<std::string, Collada::Effect> ;
     using EffectLibrary = std::map<std::string, Collada::Effect> ;
     EffectLibrary mEffectLibrary;
     EffectLibrary mEffectLibrary;
 
 
-    /** Material library: surface material by ID */
+    /// Material library: surface material by ID
     using MaterialLibrary = std::map<std::string, Collada::Material> ;
     using MaterialLibrary = std::map<std::string, Collada::Material> ;
     MaterialLibrary mMaterialLibrary;
     MaterialLibrary mMaterialLibrary;
 
 
-    /** Light library: surface light by ID */
+    /// Light library: surface light by ID
     using LightLibrary = std::map<std::string, Collada::Light> ;
     using LightLibrary = std::map<std::string, Collada::Light> ;
     LightLibrary mLightLibrary;
     LightLibrary mLightLibrary;
 
 
-    /** Camera library: surface material by ID */
+    /// Camera library: surface material by ID
     using CameraLibrary = std::map<std::string, Collada::Camera> ;
     using CameraLibrary = std::map<std::string, Collada::Camera> ;
     CameraLibrary mCameraLibrary;
     CameraLibrary mCameraLibrary;
 
 
-    /** Controller library: joint controllers by ID */
+    /// Controller library: joint controllers by ID
     using ControllerLibrary = std::map<std::string, Collada::Controller> ;
     using ControllerLibrary = std::map<std::string, Collada::Controller> ;
     ControllerLibrary mControllerLibrary;
     ControllerLibrary mControllerLibrary;
 
 
-    /** Animation library: animation references by ID */
+    /// Animation library: animation references by ID
     using AnimationLibrary = std::map<std::string, Collada::Animation *> ;
     using AnimationLibrary = std::map<std::string, Collada::Animation *> ;
     AnimationLibrary mAnimationLibrary;
     AnimationLibrary mAnimationLibrary;
 
 
-    /** Animation clip library: clip animation references by ID */
+    /// Animation clip library: clip animation references by ID
     using AnimationClipLibrary = std::vector<std::pair<std::string, std::vector<std::string>>> ;
     using AnimationClipLibrary = std::vector<std::pair<std::string, std::vector<std::string>>> ;
     AnimationClipLibrary mAnimationClipLibrary;
     AnimationClipLibrary mAnimationClipLibrary;
 
 
-    /** Pointer to the root node. Don't delete, it just points to one of
-         the nodes in the node library. */
+    /// Pointer to the root node. Don't delete, it just points to one of the nodes in the node library.
     Collada::Node *mRootNode;
     Collada::Node *mRootNode;
 
 
-    /** Root animation container */
+    /// Root animation container
     Collada::Animation mAnims;
     Collada::Animation mAnims;
 
 
-    /** Size unit: how large compared to a meter */
+    /// Size unit: how large compared to a meter
     ai_real mUnitSize;
     ai_real mUnitSize;
 
 
-    /** Which is the up vector */
+    /// Which is the up vector
     enum { UP_X,
     enum { UP_X,
         UP_Y,
         UP_Y,
         UP_Z } mUpDirection;
         UP_Z } mUpDirection;
 
 
-    /** Asset metadata (global for scene) */
+    /// Asset metadata (global for scene)
     StringMetaData mAssetMetaData;
     StringMetaData mAssetMetaData;
 
 
-    /** Collada file format version */
+    /// Collada file format version
     Collada::FormatVersion mFormat;
     Collada::FormatVersion mFormat;
 };
 };
 
 

+ 9 - 20
code/AssetLib/DXF/DXFHelper.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -62,10 +61,7 @@ namespace DXF {
 // do NOT skip empty lines. In DXF files, they count as valid data.
 // do NOT skip empty lines. In DXF files, they count as valid data.
 class LineReader {
 class LineReader {
 public:
 public:
-    LineReader(StreamReaderLE& reader)
-    : splitter(reader,false,true)
-    , groupcode( 0 )
-    , end() {
+    LineReader(StreamReaderLE& reader) : splitter(reader,false,true), groupcode( 0 ), end() {
         // empty
         // empty
     }
     }
 
 
@@ -165,8 +161,7 @@ private:
 
 
 // represents a POLYLINE or a LWPOLYLINE. or even a 3DFACE The data is converted as needed.
 // represents a POLYLINE or a LWPOLYLINE. or even a 3DFACE The data is converted as needed.
 struct PolyLine {
 struct PolyLine {
-    PolyLine()
-    : flags() {
+    PolyLine() : flags() {
         // empty
         // empty
     }
     }
 
 
@@ -182,10 +177,7 @@ struct PolyLine {
 
 
 // reference to a BLOCK. Specifies its own coordinate system.
 // reference to a BLOCK. Specifies its own coordinate system.
 struct InsertBlock {
 struct InsertBlock {
-    InsertBlock()
-    : pos()
-    , scale(1.f,1.f,1.f)
-    , angle() {
+    InsertBlock() : pos(0.f, 0.f, 0.f), scale(1.f,1.f,1.f), angle(0.0f) {
         // empty
         // empty
     }
     }
 
 
@@ -198,8 +190,7 @@ struct InsertBlock {
 
 
 
 
 // keeps track of all geometry in a single BLOCK.
 // keeps track of all geometry in a single BLOCK.
-struct Block
-{
+struct Block {
     std::vector< std::shared_ptr<PolyLine> > lines;
     std::vector< std::shared_ptr<PolyLine> > lines;
     std::vector<InsertBlock> insertions;
     std::vector<InsertBlock> insertions;
 
 
@@ -207,14 +198,12 @@ struct Block
     aiVector3D base;
     aiVector3D base;
 };
 };
 
 
-
-struct FileData
-{
+struct FileData {
     // note: the LAST block always contains the stuff from ENTITIES.
     // note: the LAST block always contains the stuff from ENTITIES.
     std::vector<Block> blocks;
     std::vector<Block> blocks;
 };
 };
 
 
-}
-} // Namespace Assimp
+} // namespace DXF
+} // namespace Assimp
 
 
-#endif
+#endif // INCLUDED_DXFHELPER_H

+ 269 - 24
code/AssetLib/DXF/DXFLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -43,11 +43,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Implementation of the DXF importer class
  *  @brief Implementation of the DXF importer class
  */
  */
 
 
-
 #ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
 #ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
 
 
-#include "AssetLib/DXF/DXFLoader.h"
-#include "AssetLib/DXF/DXFHelper.h"
+#include "DXFLoader.h"
+#include "DXFHelper.h"
 #include "PostProcessing/ConvertToLHProcess.h"
 #include "PostProcessing/ConvertToLHProcess.h"
 
 
 #include <assimp/ParsingUtils.h>
 #include <assimp/ParsingUtils.h>
@@ -68,25 +67,267 @@ static constexpr size_t AI_DXF_BINARY_IDENT_LEN = sizeof AI_DXF_BINARY_IDENT;
 // default vertex color that all uncolored vertices will receive
 // default vertex color that all uncolored vertices will receive
 static const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f));
 static const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f));
 
 
-// color indices for DXF - 16 are supported, the table is
-// taken directly from the DXF spec.
-static const aiColor4D g_aclrDxfIndexColors[] = {
-    aiColor4D(0.6f, 0.6f, 0.6f, 1.0f),
-    aiColor4D (1.0f, 0.0f, 0.0f, 1.0f), // red
-    aiColor4D (0.0f, 1.0f, 0.0f, 1.0f), // green
-    aiColor4D (0.0f, 0.0f, 1.0f, 1.0f), // blue
-    aiColor4D (0.3f, 1.0f, 0.3f, 1.0f), // light green
-    aiColor4D (0.3f, 0.3f, 1.0f, 1.0f), // light blue
-    aiColor4D (1.0f, 0.3f, 0.3f, 1.0f), // light red
-    aiColor4D (1.0f, 0.0f, 1.0f, 1.0f), // pink
-    aiColor4D (1.0f, 0.6f, 0.0f, 1.0f), // orange
-    aiColor4D (0.6f, 0.3f, 0.0f, 1.0f), // dark orange
-    aiColor4D (1.0f, 1.0f, 0.0f, 1.0f), // yellow
-    aiColor4D (0.3f, 0.3f, 0.3f, 1.0f), // dark gray
-    aiColor4D (0.8f, 0.8f, 0.8f, 1.0f), // light gray
-    aiColor4D (0.0f, 00.f, 0.0f, 1.0f), // black
-    aiColor4D (1.0f, 1.0f, 1.0f, 1.0f), // white
-    aiColor4D (0.6f, 0.0f, 1.0f, 1.0f)  // violet
+// color indices for DXF - 256 are supported, the table is
+// taken directly from the AutoCad Index (ACI) table
+// https://gohtx.com/acadcolors.php
+//STH 2024-0126
+static const aiColor4D g_aclrDxfIndexColors[256] = {
+    aiColor4D (0.0f, 0.0f ,0.0f, 1.0f), //dxf color code 0
+    aiColor4D (1.0f, 0.0f ,0.0f, 1.0f), //dxf color code 1
+    aiColor4D (1.0f, 1.0f ,0.0f, 1.0f), //dxf color code 2
+    aiColor4D (0.0f, 1.0f ,0.0f, 1.0f), //dxf color code 3
+    aiColor4D (0.0f, 1.0f ,1.0f, 1.0f), //dxf color code 4
+    aiColor4D (0.0f, 0.0f ,1.0f, 1.0f), //dxf color code 5
+    aiColor4D (1.0f, 0.0f ,1.0f, 1.0f), //dxf color code 6
+    aiColor4D (1.0f, 1.0f ,1.0f, 1.0f), //dxf color code 7
+    aiColor4D (0.3f, 0.3f ,0.3f, 1.0f), //dxf color code 8
+    aiColor4D (0.5f, 0.5f ,0.5f, 1.0f), //dxf color code 9
+    aiColor4D (1.0f, 0.0f ,0.0f, 1.0f), //dxf color code 10
+    aiColor4D (1.0f, 0.7f ,0.7f, 1.0f), //dxf color code 11
+    aiColor4D (0.7f, 0.0f ,0.0f, 1.0f), //dxf color code 12
+    aiColor4D (0.7f, 0.5f ,0.5f, 1.0f), //dxf color code 13
+    aiColor4D (0.5f, 0.0f ,0.0f, 1.0f), //dxf color code 14
+    aiColor4D (0.5f, 0.3f ,0.3f, 1.0f), //dxf color code 15
+    aiColor4D (0.4f, 0.0f ,0.0f, 1.0f), //dxf color code 16
+    aiColor4D (0.4f, 0.3f ,0.3f, 1.0f), //dxf color code 17
+    aiColor4D (0.3f, 0.0f ,0.0f, 1.0f), //dxf color code 18
+    aiColor4D (0.3f, 0.2f ,0.2f, 1.0f), //dxf color code 19
+    aiColor4D (1.0f, 0.2f ,0.0f, 1.0f), //dxf color code 20
+    aiColor4D (1.0f, 0.7f ,0.7f, 1.0f), //dxf color code 21
+    aiColor4D (0.7f, 0.2f ,0.0f, 1.0f), //dxf color code 22
+    aiColor4D (0.7f, 0.6f ,0.5f, 1.0f), //dxf color code 23
+    aiColor4D (0.5f, 0.1f ,0.0f, 1.0f), //dxf color code 24
+    aiColor4D (0.5f, 0.4f ,0.3f, 1.0f), //dxf color code 25
+    aiColor4D (0.4f, 0.1f ,0.0f, 1.0f), //dxf color code 26
+    aiColor4D (0.4f, 0.3f ,0.3f, 1.0f), //dxf color code 27
+    aiColor4D (0.3f, 0.1f ,0.0f, 1.0f), //dxf color code 28
+    aiColor4D (0.3f, 0.2f ,0.2f, 1.0f), //dxf color code 29
+    aiColor4D (1.0f, 0.5f ,0.0f, 1.0f), //dxf color code 30
+    aiColor4D (1.0f, 0.8f ,0.7f, 1.0f), //dxf color code 31
+    aiColor4D (0.7f, 0.4f ,0.0f, 1.0f), //dxf color code 32
+    aiColor4D (0.7f, 0.6f ,0.5f, 1.0f), //dxf color code 33
+    aiColor4D (0.5f, 0.3f ,0.0f, 1.0f), //dxf color code 34
+    aiColor4D (0.5f, 0.4f ,0.3f, 1.0f), //dxf color code 35
+    aiColor4D (0.4f, 0.2f ,0.0f, 1.0f), //dxf color code 36
+    aiColor4D (0.4f, 0.3f ,0.3f, 1.0f), //dxf color code 37
+    aiColor4D (0.3f, 0.2f ,0.0f, 1.0f), //dxf color code 38
+    aiColor4D (0.3f, 0.3f ,0.2f, 1.0f), //dxf color code 39
+    aiColor4D (1.0f, 0.7f ,0.0f, 1.0f), //dxf color code 40
+    aiColor4D (1.0f, 0.9f ,0.7f, 1.0f), //dxf color code 41
+    aiColor4D (0.7f, 0.6f ,0.0f, 1.0f), //dxf color code 42
+    aiColor4D (0.7f, 0.7f ,0.5f, 1.0f), //dxf color code 43
+    aiColor4D (0.5f, 0.4f ,0.0f, 1.0f), //dxf color code 44
+    aiColor4D (0.5f, 0.5f ,0.3f, 1.0f), //dxf color code 45
+    aiColor4D (0.4f, 0.3f ,0.0f, 1.0f), //dxf color code 46
+    aiColor4D (0.4f, 0.4f ,0.3f, 1.0f), //dxf color code 47
+    aiColor4D (0.3f, 0.2f ,0.0f, 1.0f), //dxf color code 48
+    aiColor4D (0.3f, 0.3f ,0.2f, 1.0f), //dxf color code 49
+    aiColor4D (1.0f, 1.0f ,0.0f, 1.0f), //dxf color code 50
+    aiColor4D (1.0f, 1.0f ,0.7f, 1.0f), //dxf color code 51
+    aiColor4D (0.7f, 0.7f ,0.0f, 1.0f), //dxf color code 52
+    aiColor4D (0.7f, 0.7f ,0.5f, 1.0f), //dxf color code 53
+    aiColor4D (0.5f, 0.5f ,0.0f, 1.0f), //dxf color code 54
+    aiColor4D (0.5f, 0.5f ,0.3f, 1.0f), //dxf color code 55
+    aiColor4D (0.4f, 0.4f ,0.0f, 1.0f), //dxf color code 56
+    aiColor4D (0.4f, 0.4f ,0.3f, 1.0f), //dxf color code 57
+    aiColor4D (0.3f, 0.3f ,0.0f, 1.0f), //dxf color code 58
+    aiColor4D (0.3f, 0.3f ,0.2f, 1.0f), //dxf color code 59
+    aiColor4D (0.7f, 1.0f ,0.0f, 1.0f), //dxf color code 60
+    aiColor4D (0.9f, 1.0f ,0.7f, 1.0f), //dxf color code 61
+    aiColor4D (0.6f, 0.7f ,0.0f, 1.0f), //dxf color code 62
+    aiColor4D (0.7f, 0.7f ,0.5f, 1.0f), //dxf color code 63
+    aiColor4D (0.4f, 0.5f ,0.0f, 1.0f), //dxf color code 64
+    aiColor4D (0.5f, 0.5f ,0.3f, 1.0f), //dxf color code 65
+    aiColor4D (0.3f, 0.4f ,0.0f, 1.0f), //dxf color code 66
+    aiColor4D (0.4f, 0.4f ,0.3f, 1.0f), //dxf color code 67
+    aiColor4D (0.2f, 0.3f ,0.0f, 1.0f), //dxf color code 68
+    aiColor4D (0.3f, 0.3f ,0.2f, 1.0f), //dxf color code 69
+    aiColor4D (0.5f, 1.0f ,0.0f, 1.0f), //dxf color code 70
+    aiColor4D (0.8f, 1.0f ,0.7f, 1.0f), //dxf color code 71
+    aiColor4D (0.4f, 0.7f ,0.0f, 1.0f), //dxf color code 72
+    aiColor4D (0.6f, 0.7f ,0.5f, 1.0f), //dxf color code 73
+    aiColor4D (0.3f, 0.5f ,0.0f, 1.0f), //dxf color code 74
+    aiColor4D (0.4f, 0.5f ,0.3f, 1.0f), //dxf color code 75
+    aiColor4D (0.2f, 0.4f ,0.0f, 1.0f), //dxf color code 76
+    aiColor4D (0.3f, 0.4f ,0.3f, 1.0f), //dxf color code 77
+    aiColor4D (0.2f, 0.3f ,0.0f, 1.0f), //dxf color code 78
+    aiColor4D (0.3f, 0.3f ,0.2f, 1.0f), //dxf color code 79
+    aiColor4D (0.2f, 1.0f ,0.0f, 1.0f), //dxf color code 80
+    aiColor4D (0.7f, 1.0f ,0.7f, 1.0f), //dxf color code 81
+    aiColor4D (0.2f, 0.7f ,0.0f, 1.0f), //dxf color code 82
+    aiColor4D (0.6f, 0.7f ,0.5f, 1.0f), //dxf color code 83
+    aiColor4D (0.1f, 0.5f ,0.0f, 1.0f), //dxf color code 84
+    aiColor4D (0.4f, 0.5f ,0.3f, 1.0f), //dxf color code 85
+    aiColor4D (0.1f, 0.4f ,0.0f, 1.0f), //dxf color code 86
+    aiColor4D (0.3f, 0.4f ,0.3f, 1.0f), //dxf color code 87
+    aiColor4D (0.1f, 0.3f ,0.0f, 1.0f), //dxf color code 88
+    aiColor4D (0.2f, 0.3f ,0.2f, 1.0f), //dxf color code 89
+    aiColor4D (0.0f, 1.0f ,0.0f, 1.0f), //dxf color code 90
+    aiColor4D (0.7f, 1.0f ,0.7f, 1.0f), //dxf color code 91
+    aiColor4D (0.0f, 0.7f ,0.0f, 1.0f), //dxf color code 92
+    aiColor4D (0.5f, 0.7f ,0.5f, 1.0f), //dxf color code 93
+    aiColor4D (0.0f, 0.5f ,0.0f, 1.0f), //dxf color code 94
+    aiColor4D (0.3f, 0.5f ,0.3f, 1.0f), //dxf color code 95
+    aiColor4D (0.0f, 0.4f ,0.0f, 1.0f), //dxf color code 96
+    aiColor4D (0.3f, 0.4f ,0.3f, 1.0f), //dxf color code 97
+    aiColor4D (0.0f, 0.3f ,0.0f, 1.0f), //dxf color code 98
+    aiColor4D (0.2f, 0.3f ,0.2f, 1.0f), //dxf color code 99
+    aiColor4D (0.0f, 1.0f ,0.2f, 1.0f), //dxf color code 100
+    aiColor4D (0.7f, 1.0f ,0.7f, 1.0f), //dxf color code 101
+    aiColor4D (0.0f, 0.7f ,0.2f, 1.0f), //dxf color code 102
+    aiColor4D (0.5f, 0.7f ,0.6f, 1.0f), //dxf color code 103
+    aiColor4D (0.0f, 0.5f ,0.1f, 1.0f), //dxf color code 104
+    aiColor4D (0.3f, 0.5f ,0.4f, 1.0f), //dxf color code 105
+    aiColor4D (0.0f, 0.4f ,0.1f, 1.0f), //dxf color code 106
+    aiColor4D (0.3f, 0.4f ,0.3f, 1.0f), //dxf color code 107
+    aiColor4D (0.0f, 0.3f ,0.1f, 1.0f), //dxf color code 108
+    aiColor4D (0.2f, 0.3f ,0.2f, 1.0f), //dxf color code 109
+    aiColor4D (0.0f, 1.0f ,0.5f, 1.0f), //dxf color code 110
+    aiColor4D (0.7f, 1.0f ,0.8f, 1.0f), //dxf color code 111
+    aiColor4D (0.0f, 0.7f ,0.4f, 1.0f), //dxf color code 112
+    aiColor4D (0.5f, 0.7f ,0.6f, 1.0f), //dxf color code 113
+    aiColor4D (0.0f, 0.5f ,0.3f, 1.0f), //dxf color code 114
+    aiColor4D (0.3f, 0.5f ,0.4f, 1.0f), //dxf color code 115
+    aiColor4D (0.0f, 0.4f ,0.2f, 1.0f), //dxf color code 116
+    aiColor4D (0.3f, 0.4f ,0.3f, 1.0f), //dxf color code 117
+    aiColor4D (0.0f, 0.3f ,0.2f, 1.0f), //dxf color code 118
+    aiColor4D (0.2f, 0.3f ,0.3f, 1.0f), //dxf color code 119
+    aiColor4D (0.0f, 1.0f ,0.7f, 1.0f), //dxf color code 120
+    aiColor4D (0.7f, 1.0f ,0.9f, 1.0f), //dxf color code 121
+    aiColor4D (0.0f, 0.7f ,0.6f, 1.0f), //dxf color code 122
+    aiColor4D (0.5f, 0.7f ,0.7f, 1.0f), //dxf color code 123
+    aiColor4D (0.0f, 0.5f ,0.4f, 1.0f), //dxf color code 124
+    aiColor4D (0.3f, 0.5f ,0.5f, 1.0f), //dxf color code 125
+    aiColor4D (0.0f, 0.4f ,0.3f, 1.0f), //dxf color code 126
+    aiColor4D (0.3f, 0.4f ,0.4f, 1.0f), //dxf color code 127
+    aiColor4D (0.0f, 0.3f ,0.2f, 1.0f), //dxf color code 128
+    aiColor4D (0.2f, 0.3f ,0.3f, 1.0f), //dxf color code 129
+    aiColor4D (0.0f, 1.0f ,1.0f, 1.0f), //dxf color code 130
+    aiColor4D (0.7f, 1.0f ,1.0f, 1.0f), //dxf color code 131
+    aiColor4D (0.0f, 0.7f ,0.7f, 1.0f), //dxf color code 132
+    aiColor4D (0.5f, 0.7f ,0.7f, 1.0f), //dxf color code 133
+    aiColor4D (0.0f, 0.5f ,0.5f, 1.0f), //dxf color code 134
+    aiColor4D (0.3f, 0.5f ,0.5f, 1.0f), //dxf color code 135
+    aiColor4D (0.0f, 0.4f ,0.4f, 1.0f), //dxf color code 136
+    aiColor4D (0.3f, 0.4f ,0.4f, 1.0f), //dxf color code 137
+    aiColor4D (0.0f, 0.3f ,0.3f, 1.0f), //dxf color code 138
+    aiColor4D (0.2f, 0.3f ,0.3f, 1.0f), //dxf color code 139
+    aiColor4D (0.0f, 0.7f ,1.0f, 1.0f), //dxf color code 140
+    aiColor4D (0.7f, 0.9f ,1.0f, 1.0f), //dxf color code 141
+    aiColor4D (0.0f, 0.6f ,0.7f, 1.0f), //dxf color code 142
+    aiColor4D (0.5f, 0.7f ,0.7f, 1.0f), //dxf color code 143
+    aiColor4D (0.0f, 0.4f ,0.5f, 1.0f), //dxf color code 144
+    aiColor4D (0.3f, 0.5f ,0.5f, 1.0f), //dxf color code 145
+    aiColor4D (0.0f, 0.3f ,0.4f, 1.0f), //dxf color code 146
+    aiColor4D (0.3f, 0.4f ,0.4f, 1.0f), //dxf color code 147
+    aiColor4D (0.0f, 0.2f ,0.3f, 1.0f), //dxf color code 148
+    aiColor4D (0.2f, 0.3f ,0.3f, 1.0f), //dxf color code 149
+    aiColor4D (0.0f, 0.5f ,1.0f, 1.0f), //dxf color code 150
+    aiColor4D (0.7f, 0.8f ,1.0f, 1.0f), //dxf color code 151
+    aiColor4D (0.0f, 0.4f ,0.7f, 1.0f), //dxf color code 152
+    aiColor4D (0.5f, 0.6f ,0.7f, 1.0f), //dxf color code 153
+    aiColor4D (0.0f, 0.3f ,0.5f, 1.0f), //dxf color code 154
+    aiColor4D (0.3f, 0.4f ,0.5f, 1.0f), //dxf color code 155
+    aiColor4D (0.0f, 0.2f ,0.4f, 1.0f), //dxf color code 156
+    aiColor4D (0.3f, 0.3f ,0.4f, 1.0f), //dxf color code 157
+    aiColor4D (0.0f, 0.2f ,0.3f, 1.0f), //dxf color code 158
+    aiColor4D (0.2f, 0.3f ,0.3f, 1.0f), //dxf color code 159
+    aiColor4D (0.0f, 0.2f ,1.0f, 1.0f), //dxf color code 160
+    aiColor4D (0.7f, 0.7f ,1.0f, 1.0f), //dxf color code 161
+    aiColor4D (0.0f, 0.2f ,0.7f, 1.0f), //dxf color code 162
+    aiColor4D (0.5f, 0.6f ,0.7f, 1.0f), //dxf color code 163
+    aiColor4D (0.0f, 0.1f ,0.5f, 1.0f), //dxf color code 164
+    aiColor4D (0.3f, 0.4f ,0.5f, 1.0f), //dxf color code 165
+    aiColor4D (0.0f, 0.1f ,0.4f, 1.0f), //dxf color code 166
+    aiColor4D (0.3f, 0.3f ,0.4f, 1.0f), //dxf color code 167
+    aiColor4D (0.0f, 0.1f ,0.3f, 1.0f), //dxf color code 168
+    aiColor4D (0.2f, 0.2f ,0.3f, 1.0f), //dxf color code 169
+    aiColor4D (0.0f, 0.0f ,1.0f, 1.0f), //dxf color code 170
+    aiColor4D (0.7f, 0.7f ,1.0f, 1.0f), //dxf color code 171
+    aiColor4D (0.0f, 0.0f ,0.7f, 1.0f), //dxf color code 172
+    aiColor4D (0.5f, 0.5f ,0.7f, 1.0f), //dxf color code 173
+    aiColor4D (0.0f, 0.0f ,0.5f, 1.0f), //dxf color code 174
+    aiColor4D (0.3f, 0.3f ,0.5f, 1.0f), //dxf color code 175
+    aiColor4D (0.0f, 0.0f ,0.4f, 1.0f), //dxf color code 176
+    aiColor4D (0.3f, 0.3f ,0.4f, 1.0f), //dxf color code 177
+    aiColor4D (0.0f, 0.0f ,0.3f, 1.0f), //dxf color code 178
+    aiColor4D (0.2f, 0.2f ,0.3f, 1.0f), //dxf color code 179
+    aiColor4D (0.2f, 0.0f ,1.0f, 1.0f), //dxf color code 180
+    aiColor4D (0.7f, 0.7f ,1.0f, 1.0f), //dxf color code 181
+    aiColor4D (0.2f, 0.0f ,0.7f, 1.0f), //dxf color code 182
+    aiColor4D (0.6f, 0.5f ,0.7f, 1.0f), //dxf color code 183
+    aiColor4D (0.1f, 0.0f ,0.5f, 1.0f), //dxf color code 184
+    aiColor4D (0.4f, 0.3f ,0.5f, 1.0f), //dxf color code 185
+    aiColor4D (0.1f, 0.0f ,0.4f, 1.0f), //dxf color code 186
+    aiColor4D (0.3f, 0.3f ,0.4f, 1.0f), //dxf color code 187
+    aiColor4D (0.1f, 0.0f ,0.3f, 1.0f), //dxf color code 188
+    aiColor4D (0.2f, 0.2f ,0.3f, 1.0f), //dxf color code 189
+    aiColor4D (0.5f, 0.0f ,1.0f, 1.0f), //dxf color code 190
+    aiColor4D (0.8f, 0.7f ,1.0f, 1.0f), //dxf color code 191
+    aiColor4D (0.4f, 0.0f ,0.7f, 1.0f), //dxf color code 192
+    aiColor4D (0.6f, 0.5f ,0.7f, 1.0f), //dxf color code 193
+    aiColor4D (0.3f, 0.0f ,0.5f, 1.0f), //dxf color code 194
+    aiColor4D (0.4f, 0.3f ,0.5f, 1.0f), //dxf color code 195
+    aiColor4D (0.2f, 0.0f ,0.4f, 1.0f), //dxf color code 196
+    aiColor4D (0.3f, 0.3f ,0.4f, 1.0f), //dxf color code 197
+    aiColor4D (0.2f, 0.0f ,0.3f, 1.0f), //dxf color code 198
+    aiColor4D (0.3f, 0.2f ,0.3f, 1.0f), //dxf color code 199
+    aiColor4D (0.7f, 0.0f ,1.0f, 1.0f), //dxf color code 200
+    aiColor4D (0.9f, 0.7f ,1.0f, 1.0f), //dxf color code 201
+    aiColor4D (0.6f, 0.0f ,0.7f, 1.0f), //dxf color code 202
+    aiColor4D (0.7f, 0.5f ,0.7f, 1.0f), //dxf color code 203
+    aiColor4D (0.4f, 0.0f ,0.5f, 1.0f), //dxf color code 204
+    aiColor4D (0.5f, 0.3f ,0.5f, 1.0f), //dxf color code 205
+    aiColor4D (0.3f, 0.0f ,0.4f, 1.0f), //dxf color code 206
+    aiColor4D (0.4f, 0.3f ,0.4f, 1.0f), //dxf color code 207
+    aiColor4D (0.2f, 0.0f ,0.3f, 1.0f), //dxf color code 208
+    aiColor4D (0.3f, 0.2f ,0.3f, 1.0f), //dxf color code 209
+    aiColor4D (1.0f, 0.0f ,1.0f, 1.0f), //dxf color code 210
+    aiColor4D (1.0f, 0.7f ,1.0f, 1.0f), //dxf color code 211
+    aiColor4D (0.7f, 0.0f ,0.7f, 1.0f), //dxf color code 212
+    aiColor4D (0.7f, 0.5f ,0.7f, 1.0f), //dxf color code 213
+    aiColor4D (0.5f, 0.0f ,0.5f, 1.0f), //dxf color code 214
+    aiColor4D (0.5f, 0.3f ,0.5f, 1.0f), //dxf color code 215
+    aiColor4D (0.4f, 0.0f ,0.4f, 1.0f), //dxf color code 216
+    aiColor4D (0.4f, 0.3f ,0.4f, 1.0f), //dxf color code 217
+    aiColor4D (0.3f, 0.0f ,0.3f, 1.0f), //dxf color code 218
+    aiColor4D (0.3f, 0.2f ,0.3f, 1.0f), //dxf color code 219
+    aiColor4D (1.0f, 0.0f ,0.7f, 1.0f), //dxf color code 220
+    aiColor4D (1.0f, 0.7f ,0.9f, 1.0f), //dxf color code 221
+    aiColor4D (0.7f, 0.0f ,0.6f, 1.0f), //dxf color code 222
+    aiColor4D (0.7f, 0.5f ,0.7f, 1.0f), //dxf color code 223
+    aiColor4D (0.5f, 0.0f ,0.4f, 1.0f), //dxf color code 224
+    aiColor4D (0.5f, 0.3f ,0.5f, 1.0f), //dxf color code 225
+    aiColor4D (0.4f, 0.0f ,0.3f, 1.0f), //dxf color code 226
+    aiColor4D (0.4f, 0.3f ,0.4f, 1.0f), //dxf color code 227
+    aiColor4D (0.3f, 0.0f ,0.2f, 1.0f), //dxf color code 228
+    aiColor4D (0.3f, 0.2f ,0.3f, 1.0f), //dxf color code 229
+    aiColor4D (1.0f, 0.0f ,0.5f, 1.0f), //dxf color code 230
+    aiColor4D (1.0f, 0.7f ,0.8f, 1.0f), //dxf color code 231
+    aiColor4D (0.7f, 0.0f ,0.4f, 1.0f), //dxf color code 232
+    aiColor4D (0.7f, 0.5f ,0.6f, 1.0f), //dxf color code 233
+    aiColor4D (0.5f, 0.0f ,0.3f, 1.0f), //dxf color code 234
+    aiColor4D (0.5f, 0.3f ,0.4f, 1.0f), //dxf color code 235
+    aiColor4D (0.4f, 0.0f ,0.2f, 1.0f), //dxf color code 236
+    aiColor4D (0.4f, 0.3f ,0.3f, 1.0f), //dxf color code 237
+    aiColor4D (0.3f, 0.0f ,0.2f, 1.0f), //dxf color code 238
+    aiColor4D (0.3f, 0.2f ,0.3f, 1.0f), //dxf color code 239
+    aiColor4D (1.0f, 0.0f ,0.2f, 1.0f), //dxf color code 240
+    aiColor4D (1.0f, 0.7f ,0.7f, 1.0f), //dxf color code 241
+    aiColor4D (0.7f, 0.0f ,0.2f, 1.0f), //dxf color code 242
+    aiColor4D (0.7f, 0.5f ,0.6f, 1.0f), //dxf color code 243
+    aiColor4D (0.5f, 0.0f ,0.1f, 1.0f), //dxf color code 244
+    aiColor4D (0.5f, 0.3f ,0.4f, 1.0f), //dxf color code 245
+    aiColor4D (0.4f, 0.0f ,0.1f, 1.0f), //dxf color code 246
+    aiColor4D (0.4f, 0.3f ,0.3f, 1.0f), //dxf color code 247
+    aiColor4D (0.3f, 0.0f ,0.1f, 1.0f), //dxf color code 248
+    aiColor4D (0.3f, 0.2f ,0.2f, 1.0f), //dxf color code 249
+    aiColor4D (0.2f, 0.2f ,0.2f, 1.0f), //dxf color code 250
+    aiColor4D (0.3f, 0.3f ,0.3f, 1.0f), //dxf color code 251
+    aiColor4D (0.4f, 0.4f ,0.4f, 1.0f), //dxf color code 252
+    aiColor4D (0.5f, 0.5f ,0.5f, 1.0f), //dxf color code 253
+    aiColor4D (0.7f, 0.7f ,0.7f, 1.0f), //dxf color code 254
+    aiColor4D (1.0f, 1.0f ,1.0f, 1.0f) //dxf color code 255
 };
 };
 
 
 #define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0]))
 #define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0]))
@@ -372,8 +613,12 @@ void DXFImporter::ExpandBlockReferences(DXF::Block& bl,const DXF::BlockMap& bloc
                 // XXX order
                 // XXX order
                 aiMatrix4x4 trafo, tmp;
                 aiMatrix4x4 trafo, tmp;
                 aiMatrix4x4::Translation(-bl_src.base,trafo);
                 aiMatrix4x4::Translation(-bl_src.base,trafo);
-                trafo *= aiMatrix4x4::Scaling(insert.scale,tmp);
+                //Need to translate position before scaling the insert
+                //otherwise the position ends up being the position*scaling
+                //STH 2024.01.17
                 trafo *= aiMatrix4x4::Translation(insert.pos,tmp);
                 trafo *= aiMatrix4x4::Translation(insert.pos,tmp);
+                trafo *= aiMatrix4x4::Scaling(insert.scale,tmp);
+                //trafo *= aiMatrix4x4::Translation(insert.pos,tmp);
 
 
                 // XXX rotation currently ignored - I didn't find an appropriate sample model.
                 // XXX rotation currently ignored - I didn't find an appropriate sample model.
                 if (insert.angle != 0.f) {
                 if (insert.angle != 0.f) {

+ 2 - 2
code/AssetLib/DXF/DXFLoader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -49,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/BaseImporter.h>
 #include <assimp/BaseImporter.h>
 #include <map>
 #include <map>
 
 
-namespace Assimp    {
+namespace Assimp {
 
 
 // Forward declarations
 // Forward declarations
 namespace DXF {
 namespace DXF {

+ 22 - 20
code/AssetLib/FBX/FBXAnimation.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -143,31 +143,33 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element &element, cons
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 const AnimationCurveMap &AnimationCurveNode::Curves() const {
 const AnimationCurveMap &AnimationCurveNode::Curves() const {
-    if (curves.empty()) {
-        // resolve attached animation curves
-        const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve");
+    if (!curves.empty()) {
+        return curves;
+    }
 
 
-        for (const Connection *con : conns) {
+    // resolve attached animation curves
+    const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve");
 
 
-            // link should go for a property
-            if (!con->PropertyName().length()) {
-                continue;
-            }
+    for (const Connection *con : conns) {
 
 
-            const Object *const ob = con->SourceObject();
-            if (nullptr == ob) {
-                DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring", &element);
-                continue;
-            }
+        // link should go for a property
+        if (!con->PropertyName().length()) {
+            continue;
+        }
 
 
-            const AnimationCurve *const anim = dynamic_cast<const AnimationCurve *>(ob);
-            if (nullptr == anim) {
-                DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve", &element);
-                continue;
-            }
+        const Object *const ob = con->SourceObject();
+        if (nullptr == ob) {
+            DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring", &element);
+            continue;
+        }
 
 
-            curves[con->PropertyName()] = anim;
+        const AnimationCurve *const anim = dynamic_cast<const AnimationCurve *>(ob);
+        if (nullptr == anim) {
+            DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve", &element);
+            continue;
         }
         }
+
+        curves[con->PropertyName()] = anim;
     }
     }
 
 
     return curves;
     return curves;

+ 10 - 53
code/AssetLib/FBX/FBXBinaryTokenizer.cpp

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -60,58 +59,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp {
 namespace Assimp {
 namespace FBX {
 namespace FBX {
 
 
-//enum Flag
-//{
-//   e_unknown_0 = 1 << 0,
-//   e_unknown_1 = 1 << 1,
-//   e_unknown_2 = 1 << 2,
-//   e_unknown_3 = 1 << 3,
-//   e_unknown_4 = 1 << 4,
-//   e_unknown_5 = 1 << 5,
-//   e_unknown_6 = 1 << 6,
-//   e_unknown_7 = 1 << 7,
-//   e_unknown_8 = 1 << 8,
-//   e_unknown_9 = 1 << 9,
-//   e_unknown_10 = 1 << 10,
-//   e_unknown_11 = 1 << 11,
-//   e_unknown_12 = 1 << 12,
-//   e_unknown_13 = 1 << 13,
-//   e_unknown_14 = 1 << 14,
-//   e_unknown_15 = 1 << 15,
-//   e_unknown_16 = 1 << 16,
-//   e_unknown_17 = 1 << 17,
-//   e_unknown_18 = 1 << 18,
-//   e_unknown_19 = 1 << 19,
-//   e_unknown_20 = 1 << 20,
-//   e_unknown_21 = 1 << 21,
-//   e_unknown_22 = 1 << 22,
-//   e_unknown_23 = 1 << 23,
-//   e_flag_field_size_64_bit = 1 << 24, // Not sure what is
-//   e_unknown_25 = 1 << 25,
-//   e_unknown_26 = 1 << 26,
-//   e_unknown_27 = 1 << 27,
-//   e_unknown_28 = 1 << 28,
-//   e_unknown_29 = 1 << 29,
-//   e_unknown_30 = 1 << 30,
-//   e_unknown_31 = 1 << 31
-//};
-//
-//bool check_flag(uint32_t flags, Flag to_check)
-//{
-//	return (flags & to_check) != 0;
-//}
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-Token::Token(const char* sbegin, const char* send, TokenType type, size_t offset)
-    :
-    #ifdef DEBUG
-    contents(sbegin, static_cast<size_t>(send-sbegin)),
-    #endif
-    sbegin(sbegin)
-    , send(send)
-    , type(type)
-    , line(offset)
-    , column(BINARY_MARKER)
-{
+Token::Token(const char* sbegin, const char* send, TokenType type, size_t offset) :
+        #ifdef DEBUG
+        contents(sbegin, static_cast<size_t>(send-sbegin)),
+        #endif
+        sbegin(sbegin),
+        send(send),
+        type(type),
+        line(offset),
+        column(BINARY_MARKER) {
     ai_assert(sbegin);
     ai_assert(sbegin);
     ai_assert(send);
     ai_assert(send);
 
 

+ 3 - 2
code/AssetLib/FBX/FBXCommon.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -51,7 +51,8 @@ namespace Assimp {
 namespace FBX {
 namespace FBX {
 
 
 static constexpr size_t NumNullRecords = 25;
 static constexpr size_t NumNullRecords = 25;
-const char NULL_RECORD[NumNullRecords] = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit
+
+constexpr char NULL_RECORD[NumNullRecords] = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit
     '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
     '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
     '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
     '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
 }; // who knows why, it looks like two integers 32/64 bit (compressed and uncompressed sizes?) + 1 byte (might be compression type?)
 }; // who knows why, it looks like two integers 32/64 bit (compressed and uncompressed sizes?) + 1 byte (might be compression type?)

+ 1 - 1
code/AssetLib/FBX/FBXCompileConfig.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 

+ 93 - 35
code/AssetLib/FBX/FBXConverter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -76,6 +76,53 @@ using namespace Util;
 
 
 #define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
 #define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
 
 
+static void correctRootTransform(const aiScene *scene) {
+    if (scene == nullptr) {
+        return;
+    }
+
+    if (scene->mMetaData == nullptr) {
+        return;
+    }
+
+    int32_t UpAxis = 1, UpAxisSign = 1, FrontAxis = 2, FrontAxisSign = 1, CoordAxis = 0, CoordAxisSign = 1;
+    double UnitScaleFactor = 1.0;
+    for (unsigned MetadataIndex = 0; MetadataIndex < scene->mMetaData->mNumProperties; ++MetadataIndex) {
+        if (strcmp(scene->mMetaData->mKeys[MetadataIndex].C_Str(), "UpAxis") == 0) {
+            scene->mMetaData->Get<int32_t>(MetadataIndex, UpAxis);
+        }
+        if (strcmp(scene->mMetaData->mKeys[MetadataIndex].C_Str(), "UpAxisSign") == 0) {
+            scene->mMetaData->Get<int32_t>(MetadataIndex, UpAxisSign);
+        }
+        if (strcmp(scene->mMetaData->mKeys[MetadataIndex].C_Str(), "FrontAxis") == 0) {
+            scene->mMetaData->Get<int32_t>(MetadataIndex, FrontAxis);
+        }
+        if (strcmp(scene->mMetaData->mKeys[MetadataIndex].C_Str(), "FrontAxisSign") == 0) {
+            scene->mMetaData->Get<int32_t>(MetadataIndex, FrontAxisSign);
+        }
+        if (strcmp(scene->mMetaData->mKeys[MetadataIndex].C_Str(), "CoordAxis") == 0) {
+            scene->mMetaData->Get<int32_t>(MetadataIndex, CoordAxis);
+        }
+        if (strcmp(scene->mMetaData->mKeys[MetadataIndex].C_Str(), "CoordAxisSign") == 0) {
+            scene->mMetaData->Get<int32_t>(MetadataIndex, CoordAxisSign);
+        }
+        if (strcmp(scene->mMetaData->mKeys[MetadataIndex].C_Str(), "UnitScaleFactor") == 0) {
+            scene->mMetaData->Get<double>(MetadataIndex, UnitScaleFactor);
+        }
+    }
+
+    aiVector3D upVec, forwardVec, rightVec;
+    upVec[UpAxis] = UpAxisSign * static_cast<float>(UnitScaleFactor);
+    forwardVec[FrontAxis] = FrontAxisSign * static_cast<float>(UnitScaleFactor);
+    rightVec[CoordAxis] = CoordAxisSign * (float)UnitScaleFactor;
+
+    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);
+    scene->mRootNode->mTransformation *= mat;
+}
+
 FBXConverter::FBXConverter(aiScene *out, const Document &doc, bool removeEmptyBones) :
 FBXConverter::FBXConverter(aiScene *out, const Document &doc, bool removeEmptyBones) :
         defaultMaterialIndex(),
         defaultMaterialIndex(),
         mMeshes(),
         mMeshes(),
@@ -133,6 +180,10 @@ FBXConverter::FBXConverter(aiScene *out, const Document &doc, bool removeEmptyBo
     // need not contain geometry (i.e. camera animations, raw armatures).
     // need not contain geometry (i.e. camera animations, raw armatures).
     if (out->mNumMeshes == 0) {
     if (out->mNumMeshes == 0) {
         out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
         out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+    } else {
+        // Apply the FBX axis metadata unless requested not to
+        if (!doc.Settings().ignoreUpDirection)
+            correctRootTransform(mSceneOut);
     }
     }
 }
 }
 
 
@@ -196,7 +247,7 @@ struct FBXConverter::PotentialNode {
 /// todo: get bone from stack
 /// todo: get bone from stack
 /// todo: make map of aiBone* to aiNode*
 /// todo: make map of aiBone* to aiNode*
 /// then update convert clusters to the new format
 /// then update convert clusters to the new format
-void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) {
+void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node, const aiMatrix4x4& parent_transform) {
     const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
     const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
 
 
     std::vector<PotentialNode> nodes;
     std::vector<PotentialNode> nodes;
@@ -227,7 +278,7 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
         if (nullptr != model) {
         if (nullptr != model) {
             nodes_chain.clear();
             nodes_chain.clear();
             post_nodes_chain.clear();
             post_nodes_chain.clear();
-            aiMatrix4x4 new_abs_transform = parent->mTransformation;
+            aiMatrix4x4 new_abs_transform = parent_transform;
             std::string node_name = FixNodeName(model->Name());
             std::string node_name = FixNodeName(model->Name());
             // even though there is only a single input node, the design of
             // even though there is only a single input node, the design of
             // assimp (or rather: the complicated transformation chain that
             // assimp (or rather: the complicated transformation chain that
@@ -261,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
@@ -283,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
@@ -290,7 +345,7 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
             }
             }
 
 
             // recursion call - child nodes
             // recursion call - child nodes
-            ConvertNodes(model->ID(), last_parent, root_node);
+            ConvertNodes(model->ID(), last_parent, root_node, new_abs_transform);
 
 
             if (doc.Settings().readLights) {
             if (doc.Settings().readLights) {
                 ConvertLights(*model, node_name);
                 ConvertLights(*model, node_name);
@@ -308,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();
+        }
     }
     }
 }
 }
 
 
@@ -383,7 +438,8 @@ void FBXConverter::ConvertLight(const Light &light, const std::string &orig_name
             out_light->mType = aiLightSource_UNDEFINED;
             out_light->mType = aiLightSource_UNDEFINED;
             break;
             break;
         default:
         default:
-            ai_assert(false);
+            FBXImporter::LogError("Not handled light type: ", light.LightType());
+            break;
     }
     }
 
 
     float decay = light.DecayStart();
     float decay = light.DecayStart();
@@ -408,7 +464,7 @@ void FBXConverter::ConvertLight(const Light &light, const std::string &orig_name
             out_light->mAttenuationQuadratic = 1.0f;
             out_light->mAttenuationQuadratic = 1.0f;
             break;
             break;
         default:
         default:
-            ai_assert(false);
+            FBXImporter::LogError("Not handled light decay type: ", light.DecayType());
             break;
             break;
     }
     }
 }
 }
@@ -546,7 +602,7 @@ const char *FBXConverter::NameTransformationCompProperty(TransformationComp comp
             return "GeometricRotationInverse";
             return "GeometricRotationInverse";
         case TransformationComp_GeometricTranslationInverse:
         case TransformationComp_GeometricTranslationInverse:
             return "GeometricTranslationInverse";
             return "GeometricTranslationInverse";
-        case TransformationComp_MAXIMUM: // this is to silence compiler warnings
+        case TransformationComp_MAXIMUM:
             break;
             break;
     }
     }
 
 
@@ -662,8 +718,7 @@ bool FBXConverter::NeedsComplexTransformationChain(const Model &model) {
     for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
     for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
         const TransformationComp comp = static_cast<TransformationComp>(i);
         const TransformationComp comp = static_cast<TransformationComp>(i);
 
 
-        if (comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation ||
-            comp == TransformationComp_PreRotation || comp == TransformationComp_PostRotation) {
+        if (comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation) {
             continue;
             continue;
         }
         }
 
 
@@ -1199,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;
@@ -1217,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++) {
@@ -1437,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++) {
@@ -1621,14 +1676,14 @@ void FBXConverter::ConvertCluster(std::vector<aiBone*> &local_mesh_bones, const
 
 
         //bone->mOffsetMatrix = cluster->Transform();
         //bone->mOffsetMatrix = cluster->Transform();
         // store local transform link for post processing
         // store local transform link for post processing
-        
+
         bone->mOffsetMatrix = cluster->TransformLink();
         bone->mOffsetMatrix = cluster->TransformLink();
         bone->mOffsetMatrix.Inverse();
         bone->mOffsetMatrix.Inverse();
 
 
         const aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
         const aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
 
 
         bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
         bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
-        
+
         //
         //
         // Now calculate the aiVertexWeights
         // Now calculate the aiVertexWeights
         //
         //
@@ -1811,7 +1866,7 @@ aiString FBXConverter::GetTexturePath(const Texture *tex) {
                 // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it
                 // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it
                 // This may occur on this case too, it has to be studied
                 // This may occur on this case too, it has to be studied
                 path.data[0] = '*';
                 path.data[0] = '*';
-                path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index);
+                path.length = 1 + ASSIMP_itoa10(path.data + 1, AI_MAXLEN - 1, index);
             }
             }
         }
         }
     }
     }
@@ -2079,6 +2134,10 @@ void FBXConverter::SetTextureProperties(aiMaterial *out_mat, const TextureMap &_
     TrySetTextureProperties(out_mat, _textures, "Maya|emissionColor", aiTextureType_EMISSION_COLOR, mesh);
     TrySetTextureProperties(out_mat, _textures, "Maya|emissionColor", aiTextureType_EMISSION_COLOR, mesh);
     TrySetTextureProperties(out_mat, _textures, "Maya|metalness", aiTextureType_METALNESS, mesh);
     TrySetTextureProperties(out_mat, _textures, "Maya|metalness", aiTextureType_METALNESS, mesh);
     TrySetTextureProperties(out_mat, _textures, "Maya|diffuseRoughness", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
     TrySetTextureProperties(out_mat, _textures, "Maya|diffuseRoughness", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|base", aiTextureType_MAYA_BASE, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|specular", aiTextureType_MAYA_SPECULAR, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|specularColor", aiTextureType_MAYA_SPECULAR_COLOR, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|specularRoughness", aiTextureType_MAYA_SPECULAR_ROUGHNESS, mesh);
 
 
     // Maya stingray
     // Maya stingray
     TrySetTextureProperties(out_mat, _textures, "Maya|TEX_color_map", aiTextureType_BASE_COLOR, mesh);
     TrySetTextureProperties(out_mat, _textures, "Maya|TEX_color_map", aiTextureType_BASE_COLOR, mesh);
@@ -2391,7 +2450,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTa
 
 
                 // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
                 // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
                 path.data[0] = '*';
                 path.data[0] = '*';
-                path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index);
+                path.length = 1 + ASSIMP_itoa10(path.data + 1, AI_MAXLEN - 1, index);
             }
             }
 
 
             out_mat->AddProperty(&path, (name + "|file").c_str(), aiTextureType_UNKNOWN, 0);
             out_mat->AddProperty(&path, (name + "|file").c_str(), aiTextureType_UNKNOWN, 0);
@@ -2757,7 +2816,7 @@ void FBXConverter::ProcessMorphAnimDatas(std::map<std::string, morphAnimData *>
                                 auto geoIt = std::find(model->GetGeometry().begin(), model->GetGeometry().end(), geo);
                                 auto geoIt = std::find(model->GetGeometry().begin(), model->GetGeometry().end(), geo);
                                 auto geoIndex = static_cast<unsigned int>(std::distance(model->GetGeometry().begin(), geoIt));
                                 auto geoIndex = static_cast<unsigned int>(std::distance(model->GetGeometry().begin(), geoIt));
                                 auto name = aiString(FixNodeName(model->Name() + "*"));
                                 auto name = aiString(FixNodeName(model->Name() + "*"));
-                                name.length = 1 + ASSIMP_itoa10(name.data + name.length, MAXLEN - 1, geoIndex);
+                                name.length = 1 + ASSIMP_itoa10(name.data + name.length, AI_MAXLEN - 1, geoIndex);
                                 morphAnimData *animData;
                                 morphAnimData *animData;
                                 auto animIt = morphAnimDatas->find(name.C_Str());
                                 auto animIt = morphAnimDatas->find(name.C_Str());
                                 if (animIt == morphAnimDatas->end()) {
                                 if (animIt == morphAnimDatas->end()) {
@@ -2899,7 +2958,7 @@ void FBXConverter::GenerateNodeAnimations(std::vector<aiNodeAnim *> &node_anims,
     // be invoked _later_ (animations come first). If this node has only rotation,
     // be invoked _later_ (animations come first). If this node has only rotation,
     // scaling and translation _and_ there are no animated other components either,
     // scaling and translation _and_ there are no animated other components either,
     // we can use a single node and also a single node animation channel.
     // we can use a single node and also a single node animation channel.
-    if( !has_complex && !NeedsComplexTransformationChain(target)) {
+    if (!doc.Settings().preservePivots || (!has_complex && !NeedsComplexTransformationChain(target))) {
         aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain,
         aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain,
                 node_property_map.end(),
                 node_property_map.end(),
                 start, stop,
                 start, stop,
@@ -3239,9 +3298,9 @@ aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name,
     }
     }
 
 
     if (keyframeLists[TransformationComp_Rotation].size() > 0) {
     if (keyframeLists[TransformationComp_Rotation].size() > 0) {
-        InterpolateKeys(outRotations, keytimes, keyframeLists[TransformationComp_Rotation], AI_DEG_TO_RAD(defRotation), maxTime, minTime, rotOrder);
+        InterpolateKeys(outRotations, keytimes, keyframeLists[TransformationComp_Rotation], defRotation, maxTime, minTime, rotOrder);
     } else {
     } else {
-        aiQuaternion defQuat = EulerToQuaternion(AI_DEG_TO_RAD(defRotation), rotOrder);
+        aiQuaternion defQuat = EulerToQuaternion(defRotation, rotOrder);
         for (size_t i = 0; i < keyCount; ++i) {
         for (size_t i = 0; i < keyCount; ++i) {
             outRotations[i].mTime = CONVERT_FBX_TIME(keytimes[i]) * anim_fps;
             outRotations[i].mTime = CONVERT_FBX_TIME(keytimes[i]) * anim_fps;
             outRotations[i].mValue = defQuat;
             outRotations[i].mValue = defQuat;
@@ -3263,7 +3322,7 @@ aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name,
 
 
     const aiVector3D& preRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
     const aiVector3D& preRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
     if (ok && preRotation.SquareLength() > zero_epsilon) {
     if (ok && preRotation.SquareLength() > zero_epsilon) {
-        const aiQuaternion preQuat = EulerToQuaternion(AI_DEG_TO_RAD(preRotation), Model::RotOrder_EulerXYZ);
+        const aiQuaternion preQuat = EulerToQuaternion(preRotation, Model::RotOrder_EulerXYZ);
         for (size_t i = 0; i < keyCount; ++i) {
         for (size_t i = 0; i < keyCount; ++i) {
             outRotations[i].mValue = preQuat * outRotations[i].mValue;
             outRotations[i].mValue = preQuat * outRotations[i].mValue;
         }
         }
@@ -3271,7 +3330,7 @@ aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name,
 
 
     const aiVector3D& postRotation = PropertyGet<aiVector3D>(props, "PostRotation", ok);
     const aiVector3D& postRotation = PropertyGet<aiVector3D>(props, "PostRotation", ok);
     if (ok && postRotation.SquareLength() > zero_epsilon) {
     if (ok && postRotation.SquareLength() > zero_epsilon) {
-        const aiQuaternion postQuat = EulerToQuaternion(AI_DEG_TO_RAD(postRotation), Model::RotOrder_EulerXYZ);
+        const aiQuaternion postQuat = EulerToQuaternion(postRotation, Model::RotOrder_EulerXYZ);
         for (size_t i = 0; i < keyCount; ++i) {
         for (size_t i = 0; i < keyCount; ++i) {
             outRotations[i].mValue = outRotations[i].mValue * postQuat;
             outRotations[i].mValue = outRotations[i].mValue * postQuat;
         }
         }
@@ -3357,7 +3416,7 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::
     KeyFrameListList inputs;
     KeyFrameListList inputs;
     inputs.reserve(nodes.size() * 3);
     inputs.reserve(nodes.size() * 3);
 
 
-    //give some breathing room for rounding errors
+    // give some breathing room for rounding errors
     const int64_t adj_start = start - 10000;
     const int64_t adj_start = start - 10000;
     const int64_t adj_stop = stop + 10000;
     const int64_t adj_stop = stop + 10000;
 
 
@@ -3383,7 +3442,7 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::
             ai_assert(curve->GetKeys().size() == curve->GetValues().size());
             ai_assert(curve->GetKeys().size() == curve->GetValues().size());
             ai_assert(curve->GetKeys().size());
             ai_assert(curve->GetKeys().size());
 
 
-            //get values within the start/stop time window
+            // get values within the start/stop time window
             std::shared_ptr<KeyTimeList> Keys(new KeyTimeList());
             std::shared_ptr<KeyTimeList> Keys(new KeyTimeList());
             std::shared_ptr<KeyValueList> Values(new KeyValueList());
             std::shared_ptr<KeyValueList> Values(new KeyValueList());
             const size_t count = curve->GetKeys().size();
             const size_t count = curve->GetKeys().size();
@@ -3403,8 +3462,7 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::
                         if (tnew >= adj_start && tnew <= adj_stop) {
                         if (tnew >= adj_start && tnew <= adj_stop) {
                             Keys->push_back(tnew);
                             Keys->push_back(tnew);
                             Values->push_back(vnew);
                             Values->push_back(vnew);
-                        }
-                        else {
+                        } else {
                             // Something broke
                             // Something broke
                             break;
                             break;
                         }
                         }

+ 3 - 4
code/AssetLib/FBX/FBXConverter.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2022, assimp team
-
+Copyright (c) 2006-2025, assimp team
 
 
 All rights reserved.
 All rights reserved.
 
 
@@ -70,7 +69,7 @@ struct morphKeyData {
     std::vector<unsigned int> values;
     std::vector<unsigned int> values;
     std::vector<float> weights;
     std::vector<float> weights;
 };
 };
-typedef std::map<int64_t, morphKeyData*> morphAnimData;
+using morphAnimData = std::map<int64_t, morphKeyData*> ;
 
 
 namespace Assimp {
 namespace Assimp {
 namespace FBX {
 namespace FBX {
@@ -134,7 +133,7 @@ private:
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     // collect and assign child nodes
     // collect and assign child nodes
-    void ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node);
+    void ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node, const aiMatrix4x4& parent_transform = aiMatrix4x4());
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     void ConvertLights(const Model& model, const std::string &orig_name );
     void ConvertLights(const Model& model, const std::string &orig_name );

Некоторые файлы не были показаны из-за большого количества измененных файлов