Compare commits

..

40 Commits

Author SHA1 Message Date
Kim Kulling
392a658f9c Bugfix/sparky kitty studios (#6623)
* Fixed regression that scrambled FBX blendshape order.

* Merge master to this branch

---------

Co-authored-by: Lux <lxw404@gmail.com>
Co-authored-by: Lux <52231149+lxw404@users.noreply.github.com>
Co-authored-by: Kim Kulling <kim.kulling@draeger.com>
2026-04-30 11:15:09 +02:00
uwezkhan
11a5d1b8ef Fix out-of-bounds read in StreamReader::IncPtr (#6600)
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-04-29 13:24:29 +02:00
uwezkhan
9e56e52252 Fix integer truncation in StreamReader size calculations (#6601)
* Fix integer truncation in StreamReader size calculations
* improves type clarity and avoids implicit conversions by replacing C-style casts with static_cast

---------

Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-04-29 08:27:50 +02:00
uwezkhan
86ae4876fb Hardening aiString deserialization in AssbinLoader to prevent stack b… (#6606)
* Hardening aiString deserialization in AssbinLoader to prevent stack buffer overflow

* Simplify string read error handling in AssbinLoader

---------

Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-04-28 14:44:07 +02:00
Kim Kulling
fb9a58735d M3D: fix overflow (#6610)
* M3D: fix overflow

---------

Co-authored-by: Kazuki Y <6259214+kazu0617@users.noreply.github.com>
Co-authored-by: kazu0617 <kazu0617@protonmail.com>
2026-04-27 11:35:28 +02:00
Matthew Pohlmann
25a1318534 Fix glTF2 occlusion strength import/export and normal scale export (#6602)
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-04-23 14:20:06 +02:00
Matthew Pohlmann
ede83d7fe2 glTF2: Fix extracting interleaved buffer data (#6605)
* Fix extracting interleaved buffers

* Fix some issues noted by coderabbit:
- Remove narrowing cast from size_t to unsigned int
- Validate accessor count is <= space in buffer (maxCount)
- Ensure remapped indices only access data valid for this accessor by checking count and not maxCount
2026-04-23 14:02:06 +02:00
dependabot[bot]
17c12da558 Bump softprops/action-gh-release from 2 to 3 (#6597)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-14 14:04:53 +02:00
James Sharam
de77101866 Fix for CanRead function for FBXImporter (#6158)
* Fixed CanRead function for FBXImporter, which is important when reading from memory

* Update FBXImporter.cpp

---------

Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-04-07 10:52:24 +02:00
Bill Wendling
158da57515 Fixed a container-overflow vulnerability in Assimp::ColladaLoader::BuildMeshesForNode (#6575)
The root cause was an unchecked access to the `newMats` vector using `matIdx`.
When a material name (`meshMaterial`) is not found in `mMaterialIndexByName`,
`matIdx` defaults to 0.  If `newMats` is empty (which happens if the material
library is empty or failed to load), accessing `newMats[0]` results in a
container-overflow.

The fix involves adding a bounds check `matIdx < newMats.size()` to the
condition guarding the access to `newMats`. Additionally, based on maintainer
feedback, I added a warning log `ASSIMP_LOG_WARN` when the index is out of
bounds, to inform the user about the missing material definition or broken
reference.

I verified the fix using the provided reproduction command. The
container-overflow is no longer triggered. I also built and ran the unit tests.
Since `bin/unit` was missing, I manually built the unit tests using
`cmake.real` and `ninja` in `/src/assimp/build_tests` with
`ASSIMP_BUILD_TESTS=ON`, `ASSIMP_BUILD_ZLIB=ON`, and
`ASSIMP_WARNINGS_AS_ERRORS=OFF` (to bypass a gtest compilation warning), and
updated `run_tests.sh` to point to the built binary. All 584 tests passed.

Fixes: https://issues.oss-fuzz.com/issues/483102958

Signed-off-by: Bill Wendling <morbo@google.com>
Co-authored-by: CodeMender <codemender-patching@google.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-04-06 17:31:59 +02:00
Kim Kulling
021bbff3e5 Feature/rust bindings: Exterimental bindings (#6467)
* Add files for assimp rust wrapper

* Fix bindgen usage.

* Add missing includes for the rust bindings.

* Apply suggestion from @coderabbitai[bot]

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update build.rs

* Fix formatting in Cargo.toml

* Remove generated file

---------

Co-authored-by: Kim Kulling <kullingk@LDED5178.corp.draeger.global>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-04-06 16:34:15 +02:00
Chris de Claverie
9c10e7d2b5 test: use epsilon comparison for FMA-sensitive API tests (#6591)
When compiling with -march=znver4 (or any arch with FMA), GCC's default
-ffp-contract=fast contracts a*b+c into FMA opportunistically. The same
inline math function compiled in the shared library and in the test
binary can get different FMA contraction decisions due to different
optimization contexts, producing bit-different FP results.

Three API tests compare C++ direct calls (inlined into test TU) against
C API wrapper calls (through libassimp.so) using EXPECT_EQ (bit-exact),
which fails when the compiler contracts differently across TUs.

Verified via disassembly: the library uses vfnmadd FMA instructions
(3 roundings) while the test binary uses separate vmulss+vsubss
(6 roundings) for the same computation.

Replace EXPECT_EQ with Equal(epsilon) for the three affected tests:
- aiMatrix3FromToTest: use machine epsilon (~1.19e-7)
- aiMatrix4FromToTest: use machine epsilon (~1.19e-7)
- aiQuaternionFromNormalizedQuaternionTest: use 1e-4 because FMA
  differences in 1.0-x*x-y*y-z*z can flip a near-zero residual's sign,
  causing w=0 vs w=sqrt(tiny)≈1e-4

Fixes #6246

Co-authored-by: Chris de Claverie <declaverie@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-04-01 19:16:58 +02:00
dimonp
048becec92 Fix linkage issue for ai_epsilon in C++20 modules (#6592)
* Add inline to ai_epsilon to avoid ODR violations
---------

Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-04-01 09:36:11 +02:00
Kim Kulling
8ff005570a Bugfix/fix unittest for cla7aye15 i4nd fix skipspaces (#6590)
* Fix conditional check in SkipSpaces function to prevent out-of-bound access.

The `SkipSpaces` function's condition was updated to ensure that the pointer check `in != end` is evaluated before dereferencing the pointer. This change prevents potential out-of-bound access when the input pointer reaches the end.

* Avoid input is wrong

* Fix all in != end

* Fix some minor bugs

* Change size to capacity

* Obj: Fix unittest
2026-03-24 21:23:38 +01:00
copycd
fcb1702162 + An error occurs when it is 0 (#6544)
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-03-17 00:33:49 +01:00
EasyT_T
b5f567bd3a Fix animations for B3DImporter (#6547)
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-03-16 19:00:36 +01:00
Shutd0wns
1569f6a9e0 Fixed logic for finding min/max for glTF2Exporter (#6552)
Fixes bug introduced in d1b73df, where min was always set to DBL_MIN

Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-03-16 16:43:30 +01:00
Pichas
29df28d7d3 Fix memory leak for USD importer (#6549)
* initial commit

* remove unused function
2026-03-14 23:57:22 +01:00
dependabot[bot]
b10b8ab99a Bump actions/upload-artifact from 6 to 7 (#6551)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-03-12 16:00:25 +01:00
Matthew Suozzo
b2852bc49b glTF2: Fix heap-buffer-overflow in GetVertexColorsForType (#6558)
The `GetVertexColorsForType` function previously used `input->count`
(the total number of elements in the accessor) to allocate the output
array and bound the conversion loop. However, when a
`vertexRemappingTable` is provided, `ExtractData` extracts a subset of
elements matching the size of the remapping table rather than the full
accessor count.

In cases where the remapping table was smaller than the accessor count
(including empty tables), the subsequent loop would perform
out-of-bounds reads on the `colors` buffer allocated by `ExtractData`.

This fix captures the actual number of elements extracted by
`ExtractData` and uses this value for the output allocation and loop
iteration, ensuring memory safety when vertex remapping is active.

Verified with ASan and existing unit tests.
2026-03-11 15:53:34 +01:00
Kim Kulling
e70199446d Refactor material system (#6563)
* Refactor material system

* Fix sonarqube finding
2026-03-08 23:40:09 +01:00
Kim Kulling
dc54c9fdea [Draft] Create AI Tool Use Policy for contributors (#6553)
Added a comprehensive AI Tool Use Policy outlining guidelines for contributors on using AI tools, ensuring human oversight and accountability in contributions.
- closes https://github.com/assimp/assimp/issues/6538
2026-03-08 21:27:08 +01:00
Kim Kulling
38f3e8d98b Add agents file. (#6562) 2026-03-07 00:42:53 +01:00
Steve M
8a073f7cb4 Apply codeRabbit suggested fix(es) from assimp PR #6548 (#6557)
Co-authored-by: tellypresence <info@tellypresence.com>
2026-03-06 12:44:08 +01:00
Kim Kulling
553fbc1fdb LWO: Fix negative iterator access, ptr was before begin (#6555) 2026-03-05 23:48:33 +01:00
dependabot[bot]
e13e0b5b7d Bump actions/download-artifact from 7 to 8 (#6550)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-02 20:16:21 +01:00
Kim Kulling
929bff1830 Ply: Fix ply triangle-strip parsing. (#6548) 2026-02-28 21:56:02 +01:00
EarendelArc
8ef1461cb8 glTF2: Preserve interpolation type and CubicSpline tangent data in animation keys (#6543)
fix(gltf2): preserve interpolation type and CubicSpline tangents

The glTF2 importer previously ignored the mInterpolation field and discarded
CubicSpline tangent data, leading to incorrect animation playback.

- Implement MapInterpolation() to map glTF2 interpolation types to Assimp.
- Set mInterpolation for all aiVectorKey and aiQuatKey instances.
- For CUBICSPLINE samplers, store [in-tangent, value, out-tangent] triplets
  (N x 3 keys) instead of discarding tangents.
- Fixes rendering consistency for InterpolationTest.glb where STEP, LINEAR,
  and CUBICSPLINE rows previously rendered identically.

Affected channels: mPositionKeys, mRotationKeys, mScalingKeys.

Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-02-26 15:33:03 +01:00
Kim Kulling
10be273aea Update supported version in SECURITY.md (#6459)
* Update supported version in SECURITY.md

- Preparation for bugfix release v6.0.5

* Update CMakeLists.txt

* Update aiGetVersionPatch test to expect version 5
2026-02-25 11:13:44 +01:00
Carter Rennick
3e672ff856 Fixed compiler error on platforms where unsigned int is not 32 bit (#6541)
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-02-23 21:26:18 +01:00
Oliver Chang
3e188e6768 HL1MDLLoader: fix segfault in destructors (#6537)
In `HL1MDLLoader::read_animations`, the arrays for `scene_->mAnimations`
and `scene_animation->mChannels` were allocated using `new T*[count]`.
This performs default initialization, which leaves the pointer elements
with indeterminate (garbage) values.

If an exception (such as a `DeadlyImportError` from malformed input) is
thrown during the loop populating these arrays, the `aiScene` or
`aiAnimation` destructors are invoked during stack unwinding. These
destructors iterate through the allocated arrays and call `delete` on
each element. Because the arrays contained garbage values for indices
not yet reached by the loader, the destructor attempted to delete
invalid memory addresses, leading to a segmentation fault.

This patch changes the allocations to use value-initialization (`new
T*[count]()`), ensuring all pointers are initialized to `nullptr`. Since
`delete nullptr` is a safe no-op, the destructors can now safely clean
up partially initialized objects during an exception.

Fixes: https://issues.oss-fuzz.com/issues/483188619

Co-authored-by: CodeMender <codemender-patching@google.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-02-22 21:27:05 +01:00
Bill Wendling
21607dfb75 Obj: Fix heap-buffer-overflow in getFace via vertical tabs (#6540)
The `ObjFileParser::getFace` method failed to recognize the vertical tab
character (`\v`, 0x0b) as a separator. While the `IsSpaceOrNewLine`
utility handles most whitespace (space, tab, CR, LF, FF), it excludes
`\v`.

When encountering a vertical tab, the parser fell through to an `else`
block that calls `::atoi(&(*m_DataIt))`. Because `atoi` treats `\v` as
whitespace per the C standard, it skips the character and continues
reading. If `\v` is located at the end of the buffer (e.g., followed by
a newline at the buffer boundary), `atoi` can read past the allocated
memory, triggering a heap-buffer-overflow.

This fix explicitly checks for `\v` and treats it as a separator,
resetting the position counter and preventing the invalid `atoi` call.

Verified with AddressSanitizer and confirmed that all 584 existing unit
tests pass.

Fixes: https://issues.oss-fuzz.com/issues/476180586

Signed-off-by: Bill Wendling <morbo@google.com>
Co-authored-by: Meder Kydyraliev <meder@google.com>
Co-authored-by: CodeMender <codemender-patching@google.com>
2026-02-21 19:19:02 +01:00
Oliver Chang
970f8691dd FBXExporter: Fix stack-use-after-scope in WriteObjects (#6472)
The FBX exporter was unconditionally dereferencing a map iterator (`tp_elem`)
even when the key was not found (i.e., `tp_elem == tpath_by_image.end()`).
This resulted in a stack-use-after-scope error when accessing `tp_elem->second`
to populate "FileName" and "RelativeFilename" nodes, as dereferencing the
end iterator of the map accessed invalid stack memory (the map's sentinel).

The code already correctly initialized a local `tfile_path` variable based
on whether the iterator was valid. This patch updates the `AddChild` calls
to use `tfile_path` instead of dereferencing the potentially invalid iterator.


Fixes: https://issues.oss-fuzz.com/issues/465494996

Co-authored-by: CodeMender <codemender-patching@google.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-02-17 19:16:53 +01:00
Oliver Chang
8acd2c964e glTF2: Fix heap-buffer-overflow in Accessor validation and size calculation (#6473)
* glTF2: Fix heap-buffer-overflow in Accessor validation and size calculation

This patch fixes a heap-buffer-overflow in
`glTF2::Accessor::ExtractData` caused by incorrect bounds validation and
available size reporting.

The vulnerability stemmed from two issues in `glTF2Asset.inl`:

1.  **Underestimated validation in `Accessor::Read`**: The logic used
    `GetBytesPerComponent() * count` to validate the required buffer
    size. This failed to account for the actual `stride`, allowing
    accessors to pass validation even if their total footprint
    (including stride) exceeded the buffer view.
2.  **Incorrect size reporting in `Accessor::GetMaxByteSize`**: The
    function returned the total `bufferView->byteLength` while ignoring
    the `byteOffset`. Since the accessor data starts at `byteOffset`,
    the actual available space is `byteLength - byteOffset`. This led
    `ExtractData` to permit reads that extended beyond the end of the
    allocated buffer.

Changes:
*   Modified `Accessor::Read` to use `GetStride() * count` for length
    validation.
*   Updated `Accessor::GetMaxByteSize` to correctly return
    `bufferView->byteLength - byteOffset` for standard accessors and
    `sparse->data.size()` for sparse accessors.

Co-authored-by: CodeMender <codemender-patching@google.com>
Fixes: https://issues.oss-fuzz.com/issues/483102963

* address comment

---------

Co-authored-by: CodeMender <codemender-patching@google.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-02-17 10:57:40 +01:00
ZhangJY
b4c7912d21 Fix heap-buffer-overflow in OpenGEXImporter::handleIndexArrayNode() (#6470)
The mColors[0] array was allocated with m_numColors elements but
indexed up to mNumVertices (= numFaces * 3), causing an out-of-bounds
write when the color count is less than the vertex count.

Allocate mColors[0] with mNumVertices (matching mNormals and
mTextureCoords), and add a bounds check on idx before reading
from the source color array.

Fixes #6468

Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-02-10 23:19:12 +01:00
Kim Kulling
e38451ff6c Add developer quickstart section to Readme (#6474)
* Add developer quickstart section to Readme

-Added a quickstart guide for developers to clone and build the project.
- closes https://github.com/assimp/assimp/issues/6469

* Change headings from H3 to H2 in Readme.md
2026-02-10 20:26:07 +01:00
Kyungjoon Ko
36c3a19aa8 Fix invalid verifying in OpenDDLParser::parseStringLiteral (#6314)
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-02-06 13:34:23 +01:00
peng
5c5ae38934 LWO: Fix heap buffer overflow in LWOImporter::GetS0 (#6451)
* LWO: Fix heap buffer overflow in LWOImporter::GetS0
* Add strict buffer boundary checks to prevent out-of-bounds reads on malformed or unterminated strings.
Fixes #6169 (CVE-2025-5167)
2026-02-03 19:21:06 +01:00
peng
18798f150d MDL: Fix heap buffer overflow in MDLImporter frame parsing (#6456)
Add buffer boundary checks before reading frame data to prevent out-of-bounds reads on malformed MDL files.
Fixes #6172 (CVE-2025-5200)

Signed-off-by: mapengyuan <mapengyuan@xfusion.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-01-29 21:10:18 +01:00
peng
ae6633ef8a Fix AC3DImporter heap-buffer-overflow by validating mesh vertex bounds (#6458)
Add validations check in AC3DImporter::ConvertObjectSection to ensure that writing TriangleStrip vertex data does not exceed mesh->mNumVertices allocation.
Fixes #6015 (CVE-2025-2754)
Fixes #6018 (CVE-2025-2756)

Signed-off-by: mapengyuan <mapengyuan@xfusion.com>
2026-01-28 21:33:55 +01:00
110 changed files with 2208 additions and 2009 deletions

View File

@@ -109,7 +109,7 @@ jobs:
run: cd build/bin && ./unit ${{ steps.hunter_extra_test_args.outputs.args }}
shell: bash
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v7
if: matrix.name == 'windows-msvc'
with:
name: 'assimp-bins-${{ matrix.name }}'
@@ -141,7 +141,7 @@ jobs:
prerelease: true
- run: |
echo '${{steps.create-release.outputs.upload_url}}' > release_upload_url.txt
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v7
with:
name: create-release
path: release_upload_url.txt
@@ -182,13 +182,13 @@ jobs:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: softprops/action-gh-release@v2
- uses: softprops/action-gh-release@v3
with:
name: create-release
- id: upload-url
run: |
echo "url=$(cat create-release/release_upload_url.txt)" >> $GITHUB_OUTPUT
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: 'assimp-bins-${{ matrix.name }}-${{ github.sha }}'
- uses: actions/upload-release-asset@v1
@@ -197,4 +197,4 @@ jobs:
with:
files: |
*.zip

View File

@@ -45,7 +45,7 @@ jobs:
filename: ${{ matrix.name }}-${{ github.event.release.tag_name }}.zip
directory: build/bin/
- uses: softprops/action-gh-release@v2
- uses: softprops/action-gh-release@v3
with:
files: build/bin/${{ matrix.name }}-${{ github.event.release.tag_name }}.zip
append_body: true

View File

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

207
AGENTS.md Normal file
View File

@@ -0,0 +1,207 @@
# AGENTS.md - Agent Guidelines for Assimp
This document provides guidelines for AI agents working on the Assimp codebase.
## Project Overview
Assimp (Open Asset Import Library) is a C++ library that loads various 3D file formats into a shared, in-memory format. It supports 40+ import formats and several export formats.
## Build Commands
### Basic Build (CMake + Ninja recommended)
```bash
# Configure with CMake
cmake -G Ninja -DASSIMP_BUILD_TESTS=ON -DASSIMP_WARNINGS_AS_ERRORS=ON -S . -B build
# Build
cmake --build build
```
### Key CMake Options
- `-DASSIMP_BUILD_TESTS=ON` - Build unit tests (default ON)
- `-DASSIMP_WARNINGS_AS_ERRORS=ON` - Treat warnings as errors (default ON)
- `-DASSIMP_BUILD_ASSIMP_TOOLS=ON` - Build command-line tools
- `-DASSIMP_BUILD_SAMPLES=ON` - Build sample applications
- `-DASSIMP_DOUBLE_PRECISION=ON` - Use double precision for calculations
- `-DASSIMP_NO_EXPORT=ON` - Disable export functionality
- `-DBUILD_SHARED_LIBS=OFF` - Build static library
### Running Tests
#### Run All Tests
```bash
# Using ctest
cd build && ctest
# Or run unit directly
./build/unit
```
#### Run Single Test
```bash
# Using ctest with filter
cd build && ctest -R "TestName"
# Or run unit with filter
./build/unit --gtest_filter="TestSuiteName.TestName"
```
For example:
```bash
./build/unit --gtest_filter="utObjImportExport.*"
./build/unit --gtest_filter="utMaterialSystem.*"
```
#### Test Directory
Tests are located in `test/unit/` and use Google Test. Test files are named `ut<Feature>.cpp`.
## Code Style
### Formatting
- **Use clang-format** before committing. Run: `clang-format -i <file>`
- The project uses a `.clang-format` file at the root with LLVM-based style
- Key settings:
- Indent width: 4 spaces
- Tab width: 4, UseTab: Never
- ColumnLimit: 0 (no line length limit)
- BreakConstructorInitializers: AfterColon
- AccessModifierOffset: -4
### Header File Organization
```cpp
// Order of includes (use clang-format to enforce):
// 1. Module's own header
// 2. Other assimp headers (assimp/*)
// 3. External headers (contrib/*)
// 4. Standard library headers
// 5. System headers
#include "Common/Importer.h"
#include <assimp/version.h>
#include <assimp/config.h>
#include "../contrib/some_lib/some.h"
#include <vector>
#include <string>
```
### Naming Conventions
- **Classes/Types**: PascalCase (e.g., `Importer`, `aiScene`)
- **Functions**: PascalCase (e.g., `ReadFile`, `GetExtension`)
- **Variables**: camelCase (e.g., `scene`, `importStep`)
- **Constants**: kCamelCase or UPPER_SNAKE_CASE (e.g., `kMaxVertices`)
- **Member variables**: Often prefixed with `m_` (e.g., `mScene`)
- **Static variables**: Often prefixed with `s_`
### File Naming
- **Header files**: PascalCase (e.g., `Importer.h`, `ScenePrivate.h`)
- **Source files**: PascalCase (e.g., `Importer.cpp`)
- **Test files**: Prefixed with `ut` (e.g., `utObjImportExport.cpp`)
### C++ Guidelines
#### Language Standard
- Minimum: C++17
- Use modern C++ features (smart pointers, constexpr, etc.)
#### Error Handling
- Use exceptions for recoverable errors (derived from `std::exception`)
- Use `ai_assert` for debugging assertions in code
- Return error codes from C API functions
#### Classes
- Use `ai_enable_erasing` pattern for optional features
- Use PImpl idiom for ABI stability where appropriate
#### Memory Management
- Use smart pointers (`std::unique_ptr`, `std::shared_ptr`)
- Prefer RAII patterns
- Document ownership semantics in function comments
### Importers/Exporters
#### Structure
Each importer typically has:
1. Header in `code/AssetLib/<Format>/`
2. Implementation in `code/AssetLib/<Format>/`
3. Registration in `code/Common/ImporterRegistry.cpp`
4. Unit tests in `test/unit/ImportExport/`
#### Registration
```cpp
void GetImporterInstanceList(std::vector<BaseImporter*>& out);
// Add to registry
out.push_back(new MyFormatImporter());
```
### Post-Processing
- Located in `code/PostProcessing/`
- Each process inherits from `BaseProcess`
- Implement `ExecuteOnScene` method
### Documentation
- Use Doxygen-style comments for public APIs
- Example:
```cpp
/// <summary>
/// Loads a file from disk.
/// </summary>
/// <param name="pFile">Path to the file.</param>
/// <returns>Pointer to the imported scene.</returns>
aiScene* Importer::ReadFile(const char* pFile);
```
### Contributing
1. Create a fork of assimp
2. Create a branch for your feature/fix
3. Run `clang-format` on modified files
4. Ensure tests pass
5. Open a PR against `master` branch
## Directory Structure
```
code/
AssetLib/ - Importers and exporters
CApi/ - C API wrapper
Common/ - Shared utilities
Geometry/ - Geometry processing
Material/ - Material handling
PostProcessing/ - Mesh post-processing
include/ - Public headers (assimp/)
test/
unit/ - Unit tests
models/ - Test 3D models
test/ - Test data (non-BSD licensed)
contrib/ - Third-party libraries
```
## CI/CD
The project runs CI on GitHub Actions:
- Builds on Linux and Windows
- Runs tests including memory leak detection
- Checks compiler warnings
## Common Tasks
### Adding a New Importer
1. Create `code/AssetLib/<Format>/<Format>Importer.h`
2. Create `code/AssetLib/<Format>/<Format>Importer.cpp`
3. Register in `code/Common/ImporterRegistry.cpp`
4. Add tests in `test/unit/ImportExport/`
5. Add to CMakeLists.txt if needed
### Running Specific Test Suite
```bash
# Test specific importer
./build/unit --gtest_filter="utglTF2ImportExport.*"
# Test post-processing
./build/unit --gtest_filter="utTriangulate.*"
# Test math operations
./build/unit --gtest_filter="utMatrix4x4.*"
```

172
AIToolPolicy.md Normal file
View File

@@ -0,0 +1,172 @@
# Asset-Importer-Lib AI Tool Use Policy
## Policy
Assimp's policy is that contributors can use whatever tools they would like to
craft their contributions, but there must be a **human in the loop**.
**Contributors must read and review all LLM-generated code or text before they
ask other project members to review it.** The contributor is always the author
and is fully accountable for their contributions. Contributors should be
sufficiently confident that the contribution is high enough quality that asking
for a review is a good use of scarce maintainer time, and they should be **able
to answer questions about their work** during review.
We expect that new contributors will be less confident in their contributions,
and our guidance to them is to **start with small contributions** that they can
fully understand to build confidence. We aspire to be a welcoming community
that helps new contributors grow their expertise, but learning involves taking
small steps, getting feedback, and iterating. Passing maintainer feedback to an
LLM doesn't help anyone grow, and does not sustain our community.
Contributors are expected to **be transparent and label contributions that
contain substantial amounts of tool-generated content**. Our policy on
labelling is intended to facilitate reviews, and not to track which parts of
Assimp are generated. Contributors should note tool usage in their pull request
description, commit message, or wherever authorship is normally indicated for
the work. For instance, use a commit message trailer like Assisted-by: <name of
code assistant>. This transparency helps the community develop best practices
and understand the role of these new tools.
This policy includes, but is not limited to, the following kinds of
contributions:
- Code, usually in the form of a pull request
- RFCs or design proposals
- Issues or security vulnerabilities
- Comments and feedback on pull requests
## Details
To ensure sufficient self review and understanding of the work, it is strongly
recommended that contributors write PR descriptions themselves (if needed,
using tools for translation or copy-editing). The description should explain
the motivation, implementation approach, expected impact, and any open
questions or uncertainties to the same extent as a contribution made without
tool assistance.
AI tools must not be used to fix GitHub issues labelled [`good first
issue`][good-first-issue]. These issues are generally not urgent, and are
intended to be learning opportunities for new contributors to get familiar with
the codebase. Whether you are a newcomer or not, fully automating the process
of fixing this issue squanders the learning opportunity and doesn't add much
value to the project. **Using AI tools to fix issues labelled as "good first
issues" is forbidden**.
[good-first-issue]: https://github.com/llvm/llvm-project/issues/?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22
## Extractive Contributions
The reason for our "human-in-the-loop" contribution policy is that processing
patches, PRs, RFCs, and comments to Assimp is not free -- it takes a lot of
maintainer time and energy to review those contributions! Sending the
unreviewed output of an Assimp to open source project maintainers *extracts* work
from them in the form of design and code review, so we call this kind of
contribution an "extractive contribution".
Our **golden rule** is that a contribution should be worth more to the project
than the time it takes to review it. These ideas are captured by this quote
from the book [Working in Public][public] by Nadia Eghbal:
[public]: https://press.stripe.com/working-in-public
> \"When attention is being appropriated, producers need to weigh the costs and
> benefits of the transaction. To assess whether the appropriation of attention
> is net-positive, it's useful to distinguish between *extractive* and
> *non-extractive* contributions. Extractive contributions are those where the
> marginal cost of reviewing and merging that contribution is greater than the
> marginal benefit to the project's producers. In the case of a code
> contribution, it might be a pull request that's too complex or unwieldy to
> review, given the potential upside.\" \-- Nadia Eghbal
Prior to the advent of LLMs, open source project maintainers would often review
any and all changes sent to the project simply because posting a change for
review was a sign of interest from a potential long-term contributor. While new
tools enable more development, it shifts effort from the implementor to the
reviewer, and our policy exists to ensure that we value and do not squander
maintainer time.
Reviewing changes from new contributors is part of growing the next generation
of contributors and sustaining the project. We want the LLVM project to be
welcoming and open to aspiring compiler engineers who are willing to invest
time and effort to learn and grow, because growing our contributor base and
recruiting new maintainers helps sustain the project over the long term. Being
open to contributions and [liberally granting commit access][commit-access]
is a big part of how LLVM has grown and successfully been adopted all across
the industry. We therefore automatically post a greeting comment to pull
requests from new contributors and encourage maintainers to spend their time to
help new contributors learn.
[commit-access]: https://llvm.org/docs/DeveloperPolicy.html#obtaining-commit-access
## Handling Violations
If a maintainer judges that a contribution doesn't comply with this policy,
they should paste the following response to request changes:
This PR doesn't appear to comply with our policy on tool-generated content,
and requires additional justification for why it is valuable enough to the
project for us to review it. Please see our developer policy on
AI-generated contributions: http://llvm.org/docs/AIToolPolicy.html
The best ways to make a change less extractive and more valuable are to reduce
its size or complexity or to increase its usefulness to the community. These
factors are impossible to weigh objectively, and our project policy leaves this
determination up to the maintainers of the project, i.e. those who are doing
the work of sustaining the project.
If or when it becomes clear that a GitHub issue or PR is off-track and not
moving in the right direction, maintainers should apply the `extractive` label
to help other reviewers prioritize their review time.
If a contributor fails to make their change meaningfully less extractive,
maintainers should escalate to the relevant moderation or admin team for the
space (GitHub, Discourse, Discord, etc) to lock the conversation.
## Copyright
Artificial intelligence systems raise many questions around copyright that have
yet to be answered. Our policy on AI tools is similar to our copyright policy:
Contributors are responsible for ensuring that they have the right to
contribute code under the terms of our license, typically meaning that either
they, their employer, or their collaborators hold the copyright. Using AI tools
to regenerate copyrighted material does not remove the copyright, and
contributors are responsible for ensuring that such material does not appear in
their contributions. Contributions found to violate this policy will be removed
just like any other offending contribution.
## Examples
Here are some examples of contributions that demonstrate how to apply
the principles of this policy:
- [This PR][alive-pr] contains a proof from Alive2, which is a strong signal of
value and correctness.
- This [generated documentation][gsym-docs] was reviewed for correctness by a
human before being posted.
[alive-pr]: https://github.com/llvm/llvm-project/pull/142869
[gsym-docs]: https://discourse.llvm.org/t/searching-for-gsym-documentation/85185/2
## References
Our policy was informed by experiences in other communities:
- [Fedora Council Policy Proposal: Policy on AI-Assisted Contributions (fetched
2025-10-01)][fedora]: Some of the text above was copied from the Fedora
project policy proposal, which is licensed under the [Creative Commons
Attribution 4.0 International License][cca]. This link serves as attribution.
- [Rust draft policy on burdensome PRs][rust-burdensome]
- [Seth Larson's post][security-slop]
on slop security reports in the Python ecosystem
- The METR paper [Measuring the Impact of Early-2025 AI on Experienced
Open-Source Developer Productivity][metr-paper].
- [QEMU bans use of AI content generators][qemu-ban]
- [Slop is the new name for unwanted AI-generated content][ai-slop]
[fedora]: https://communityblog.fedoraproject.org/council-policy-proposal-policy-on-ai-assisted-contributions/
[cca]: https://creativecommons.org/licenses/by/4.0/
[rust-burdensome]: https://github.com/rust-lang/compiler-team/issues/893
[security-slop]: https://sethmlarson.dev/slop-security-reports
[metr-paper]: https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/
[qemu-ban]: https://www.qemu.org/docs/master/devel/code-provenance.html#use-of-ai-content-generators
[ai-slop]: https://simonwillison.net/2024/May/8/slop/

View File

@@ -104,7 +104,7 @@ IF(ASSIMP_HUNTER_ENABLED)
ADD_DEFINITIONS(-DASSIMP_USE_HUNTER)
ENDIF()
PROJECT(Assimp VERSION 6.0.4
PROJECT(Assimp VERSION 6.0.5
LANGUAGES C CXX
DESCRIPTION "Open Asset Import Library (Assimp) is a library to import various well-known 3D model formats in a uniform manner."
)

View File

@@ -3,7 +3,7 @@ Open Asset Import Library (assimp)
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
![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)
[![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)
@@ -19,29 +19,38 @@ Additionally, assimp features various __mesh post-processing tools__: normals an
## Project activity
![Alt](https://repobeats.axiom.co/api/embed/997f84e5f9fcf772da1e687f3a4f3a8afdbf4cf0.svg "Repobeats analytics image")
### Documentation
## Developer quickstart
```bash
git clone https://github.com/assimp/assimp
cd assimp
cmake -G Ninja -DASSIMP_BUILD_TESTS=off -DASSIMP_INSTALL=off -S . -B build
cd build
ninja
```
## Documentation
Read [our latest documentation](https://the-asset-importer-lib-documentation.readthedocs.io/en/latest/).
### Pre-built binaries
## Pre-built binaries
Download binaries from [our Itch Projectspace](https://kimkulling.itch.io/the-asset-importer-lib).
### Test data
## Test data
Clone [our model database for testing purposes](https://github.com/assimp/assimp-mdb).
### Communities
## Communities
- Ask questions at [the Assimp Discussion Board](https://github.com/assimp/assimp/discussions).
- Find us on [discord](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
See [the complete list of supported formats](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md).
### Building
## Building
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)
* [Python](port/PyAssimp/README.md)
* [.NET](https://github.com/Saalvage/AssimpNetter)
@@ -54,11 +63,11 @@ Start by reading [our build instructions](https://github.com/assimp/assimp/blob/
* [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port.
* [Rust](https://github.com/jkvargas/russimp)
### Other tools
## Other tools
[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).
#### Repository structure ####
### Repository structure
Open Asset Import Library is implemented in C++. The directory structure looks like this:
```txt
@@ -85,7 +94,7 @@ test Unit- and regression tests, test suite of models
tools Tools (old assimp viewer, command line `assimp`)
samples Small number of samples to illustrate possible use cases for Assimp
```
### Contributing
## Contributing
We would greatly appreciate for you to contribute to assimp. The easiest way to get involved is to submit
a pull request with your changes against the main repository's `master` branch.
@@ -113,7 +122,7 @@ You can support the project with your organization. Your logo will show up here
<a href="https://opencollective.com/assimp/organization/0/website"><img src="https://opencollective.com/assimp/organization/0/avatar.svg"></a>
### License ###
## License ###
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 -

View File

@@ -6,7 +6,7 @@ The current version of Assimp that's being supported with security updates:
| Version | Supported |
| ------- | ------------------ |
| 6.0.2 | :white_check_mark: |
| 6.0.5 | :white_check_mark: |
## Reporting a Vulnerability

View File

@@ -607,6 +607,10 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
const Surface::SurfaceEntry &entry1 = src.entries[i];
const Surface::SurfaceEntry &entry2 = src.entries[i + 1];
const Surface::SurfaceEntry &entry3 = src.entries[i + 2];
const unsigned int verticesNeeded = isDoubleSided ? 6 : 3;
if (static_cast<unsigned>(vertices - mesh->mVertices) + verticesNeeded > mesh->mNumVertices) {
throw DeadlyImportError("AC3D: Invalid number of vertices");
}
aiFace &face = *faces++;
face.mNumIndices = 3;
@@ -661,6 +665,10 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
unsigned int tmp = (unsigned int)(*it).entries.size();
if (Surface::OpenLine == type) --tmp;
for (unsigned int m = 0; m < tmp; ++m) {
if (static_cast<unsigned>(vertices - mesh->mVertices) + 2 > mesh->mNumVertices) {
throw DeadlyImportError("AC3D: Invalid number of vertices");
}
aiFace &face = *faces++;
face.mNumIndices = 2;

View File

@@ -149,11 +149,18 @@ aiQuaternion Read<aiQuaternion>(IOStream *stream) {
template <>
aiString Read<aiString>(IOStream *stream) {
aiString s;
stream->Read(&s.length, 4, 1);
if (s.length) {
stream->Read(s.data, s.length, 1);
uint32_t len;
if (stream->Read(&len, 4, 1) != 1) {
throw DeadlyImportError("ASSBIN: Unexpected EOF reading string length");
}
s.data[s.length] = 0;
if (len >= AI_MAXLEN) {
throw DeadlyImportError("ASSBIN: String length too large, potential buffer overflow attempt");
}
s.length = len;
if ((s.length > 0) && (stream->Read(s.data, s.length, 1) != 1)) {
throw DeadlyImportError("ASSBIN: Unexpected EOF reading string data");
}
s.data[s.length] = '\0';
return s;
}

View File

@@ -469,9 +469,11 @@ void B3DImporter::ReadBONE(int id) {
}
// ------------------------------------------------------------------------------------------------
void B3DImporter::ReadKEYS(aiNodeAnim *nodeAnim) {
vector<aiVectorKey> trans, scale;
vector<aiQuatKey> rot;
void B3DImporter::ReadKEYS(AnimKeys& keys) {
vector<aiVectorKey>& trans = keys.positionKeys;
vector<aiVectorKey>& scale = keys.scalingKeys;
vector<aiQuatKey>& rot = keys.rotationKeys;
int flags = ReadInt();
while (ChunkSize()) {
int frame = ReadInt();
@@ -485,21 +487,6 @@ void B3DImporter::ReadKEYS(aiNodeAnim *nodeAnim) {
rot.emplace_back(frame, ReadQuat());
}
}
if (flags & 1) {
nodeAnim->mNumPositionKeys = static_cast<unsigned int>(trans.size());
nodeAnim->mPositionKeys = to_array(trans);
}
if (flags & 2) {
nodeAnim->mNumScalingKeys = static_cast<unsigned int>(scale.size());
nodeAnim->mScalingKeys = to_array(scale);
}
if (flags & 4) {
nodeAnim->mNumRotationKeys = static_cast<unsigned int>(rot.size());
nodeAnim->mRotationKeys = to_array(rot);
}
}
// ------------------------------------------------------------------------------------------------
@@ -542,6 +529,7 @@ aiNode *B3DImporter::ReadNODE(aiNode *parent) {
std::unique_ptr<aiNodeAnim> nodeAnim;
vector<unsigned> meshes;
vector<aiNode *> children;
AnimKeys keys;
while (ChunkSize()) {
const string chunk = ReadChunk();
@@ -560,7 +548,7 @@ aiNode *B3DImporter::ReadNODE(aiNode *parent) {
nodeAnim.reset(new aiNodeAnim);
nodeAnim->mNodeName = node->mName;
}
ReadKEYS(nodeAnim.get());
ReadKEYS(keys);
} else if (chunk == "NODE") {
aiNode *child = ReadNODE(node);
children.push_back(child);
@@ -569,6 +557,21 @@ aiNode *B3DImporter::ReadNODE(aiNode *parent) {
}
if (nodeAnim) {
if (!keys.positionKeys.empty()) {
nodeAnim->mNumPositionKeys = static_cast<unsigned int>(keys.positionKeys.size());
nodeAnim->mPositionKeys = to_array(keys.positionKeys);
}
if (!keys.scalingKeys.empty()) {
nodeAnim->mNumScalingKeys = static_cast<unsigned int>(keys.scalingKeys.size());
nodeAnim->mScalingKeys = to_array(keys.scalingKeys);
}
if (!keys.rotationKeys.empty()) {
nodeAnim->mNumRotationKeys = static_cast<unsigned int>(keys.rotationKeys.size());
nodeAnim->mRotationKeys = to_array(keys.rotationKeys);
}
_nodeAnims.emplace_back(std::move(nodeAnim));
}

View File

@@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/types.h>
#include <assimp/mesh.h>
#include <assimp/material.h>
#include <assimp/anim.h>
#include <assimp/BaseImporter.h>
#include <memory>
@@ -94,6 +95,12 @@ private:
float weights[4];
};
struct AnimKeys {
std::vector<aiVectorKey> positionKeys;
std::vector<aiVectorKey> scalingKeys;
std::vector<aiQuatKey> rotationKeys;
};
AI_WONT_RETURN void Oops() AI_WONT_RETURN_SUFFIX;
AI_WONT_RETURN void Fail(const std::string &str) AI_WONT_RETURN_SUFFIX;
@@ -104,7 +111,7 @@ private:
void ReadTRIS( int v0 );
void ReadMESH();
void ReadBONE( int id );
void ReadKEYS( aiNodeAnim *nodeAnim );
void ReadKEYS( AnimKeys& keys );
void ReadANIM();
aiNode *ReadNODE( aiNode *parent );

View File

@@ -507,16 +507,20 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
}
if (table && !table->mMap.empty()) {
std::pair<Collada::Effect *, aiMaterial *> &mat = newMats[matIdx];
if (matIdx < newMats.size()) {
std::pair<Collada::Effect *, aiMaterial *> &mat = newMats[matIdx];
// Iterate through all texture channels assigned to the effect and
// check whether we have mapping information for it.
ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table);
// Iterate through all texture channels assigned to the effect and
// check whether we have mapping information for it.
ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table);
} else {
ASSIMP_LOG_WARN("Collada: Ignoring material mapping for mesh \"", mid.mMeshOrController, "\". Material index ", matIdx, " is out of bounds (newMats.size()=", newMats.size(), ").");
}
}
// built lookup index of the Mesh-Submesh-Material combination

View File

@@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include <algorithm>
#include "FBXParser.h"
#include "FBXDocument.h"
#include "FBXMeshGeometry.h"
@@ -144,8 +146,10 @@ BlendShape::BlendShape(uint64_t id, const Element& element, const Document& doc,
for (const Connection* con : conns) {
const BlendShapeChannel* const bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element);
if (bspc) {
auto pr = blendShapeChannels.insert(bspc);
if (!pr.second) {
// Only add a channel if it doesn't exist already
if (std::find(blendShapeChannels.begin(), blendShapeChannels.end(), bspc) == blendShapeChannels.end()) {
blendShapeChannels.push_back(bspc);
} else {
FBXImporter::LogWarn("there is the same blendShapeChannel id ", bspc->ID());
}
}
@@ -170,8 +174,10 @@ BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const
for (const Connection* con : conns) {
const ShapeGeometry* const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element);
if (sg) {
auto pr = shapeGeometries.insert(sg);
if (!pr.second) {
// Only add a geometry if it doesn't exist already
if (std::find(shapeGeometries.begin(), shapeGeometries.end(), sg) == shapeGeometries.end()) {
shapeGeometries.push_back(sg);
} else {
FBXImporter::LogWarn("there is the same shapeGeometrie id ", sg->ID());
}
}

View File

@@ -865,14 +865,14 @@ public:
return fullWeights;
}
const std::unordered_set<const ShapeGeometry*>& GetShapeGeometries() const {
const std::vector<const ShapeGeometry*>& GetShapeGeometries() const {
return shapeGeometries;
}
private:
float percent;
WeightArray fullWeights;
std::unordered_set<const ShapeGeometry*> shapeGeometries;
std::vector<const ShapeGeometry*> shapeGeometries;
};
/** DOM class for BlendShape deformers */
@@ -882,12 +882,12 @@ public:
virtual ~BlendShape() = default;
const std::unordered_set<const BlendShapeChannel*>& BlendShapeChannels() const {
const std::vector<const BlendShapeChannel*>& BlendShapeChannels() const {
return blendShapeChannels;
}
private:
std::unordered_set<const BlendShapeChannel*> blendShapeChannels;
std::vector<const BlendShapeChannel*> blendShapeChannels;
};
/** DOM class for skin deformer clusters (aka sub-deformers) */

View File

@@ -1766,8 +1766,8 @@ void FBXExporter::WriteObjects () {
// can't easily determine which texture path will be correct,
// so just store what we have in every field.
// these being incorrect is a common problem with FBX anyway.
tnode.AddChild("FileName", tp_elem->second);
tnode.AddChild("RelativeFilename", tp_elem->second);
tnode.AddChild("FileName", tfile_path);
tnode.AddChild("RelativeFilename", tfile_path);
tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0));
tnode.AddChild("ModelUVScaling", double(1.0), double(1.0));
tnode.AddChild("Texture_Alpha_Source", "None");

View File

@@ -90,7 +90,7 @@ namespace {
// Returns whether the class can handle the format of the given file.
bool FBXImporter::CanRead(const std::string & pFile, IOSystem * pIOHandler, bool /*checkSig*/) const {
// at least ASCII-FBX files usually have a 'FBX' somewhere in their head
static const char *tokens[] = { " \n\r\n " };
static const char *tokens[] = { " \n\r\n ", "fbx" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
}

View File

@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include <algorithm>
#include <functional>
#include "FBXMeshGeometry.h"
@@ -69,8 +70,10 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name,
}
const BlendShape* const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element);
if (bsp) {
auto pr = blendShapes.insert(bsp);
if (!pr.second) {
// Only add a blendshape if it doesn't exist already
if (std::find(blendShapes.begin(), blendShapes.end(), bsp) == blendShapes.end()) {
blendShapes.push_back(bsp);
} else {
FBXImporter::LogWarn("there is the same blendShape id ", bsp->ID());
}
}
@@ -78,7 +81,7 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name,
}
// ------------------------------------------------------------------------------------------------
const std::unordered_set<const BlendShape*>& Geometry::GetBlendShapes() const {
const std::vector<const BlendShape*>& Geometry::GetBlendShapes() const {
return blendShapes;
}

View File

@@ -72,11 +72,11 @@ public:
/// @brief Get the BlendShape attached to this geometry or nullptr
/// @return The blendshape arrays.
const std::unordered_set<const BlendShape*>& GetBlendShapes() const;
const std::vector<const BlendShape*>& GetBlendShapes() const;
private:
const Skin* skin;
std::unordered_set<const BlendShape*> blendShapes;
std::vector<const BlendShape*> blendShapes;
};

View File

@@ -179,7 +179,8 @@ void AnimResolver::UpdateAnimRangeSetup() {
case LWO::PrePostBehaviour_Oscillate: {
const double start_time = delta - std::fmod(my_first - first, delta);
std::vector<LWO::Key>::iterator n = std::find_if((*it).keys.begin(), (*it).keys.end(),
[start_time](double t) { return start_time > t; }), m;
[start_time](double t) { return start_time > t; });
std::vector<LWO::Key>::iterator m;
size_t ofs = 0;
if (n != (*it).keys.end()) {
@@ -211,13 +212,21 @@ void AnimResolver::UpdateAnimRangeSetup() {
double cur_minus = delta;
unsigned int tt = 1;
for (const double tmp = delta * (num + 1); cur_minus <= tmp; cur_minus += delta, ++tt) {
m = (delta == tmp ? (*it).keys.begin() : n - (old_size + 1));
for (; m < n; --n) {
(*n).time -= cur_minus;
// offset repeat? add delta offset to key value
if (delta == tmp) {
m = it->keys.begin();
} else {
ptrdiff_t dist = std::distance((*it).keys.begin(), n);
if (dist <= static_cast<ptrdiff_t>(old_size)) {
// clamp to begin to avoid seeking before begin
m = (*it).keys.begin();
} else {
m = n - (old_size + 1);
}
}
for (auto it2 = m; it2 != n; ++it2) {
it2->time -= cur_minus;
if ((*it).pre == LWO::PrePostBehaviour_OffsetRepeat) {
(*n).value += tt * value_delta;
it2->value += tt * value_delta;
}
}
}

View File

@@ -64,7 +64,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
static const aiImporterDesc desc = {
static constexpr aiImporterDesc desc = {
"LightWave/Modo Object Importer",
"",
"",
@@ -77,30 +77,6 @@ static const aiImporterDesc desc = {
"lwo lxo"
};
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
LWOImporter::LWOImporter() :
mIsLWO2(),
mIsLXOB(),
mIsLWO3(),
mLayers(),
mCurLayer(),
mTags(),
mMapping(),
mSurfaces(),
mFileBuffer(),
fileSize(),
mScene(nullptr),
configSpeedFlag(),
configLayerIndex(),
hasNamedLayer() {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
LWOImporter::~LWOImporter() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool LWOImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool /*checkSig*/) const {
@@ -155,6 +131,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
}
mFileBuffer = &mBuffer[0] + 12;
mFileBufferEnd = &mBuffer[0] + fileSize;
fileSize -= 12;
// Initialize some members with their default values

View File

@@ -56,6 +56,7 @@ struct aiNode;
struct aiMaterial;
namespace Assimp {
using namespace LWO;
// ---------------------------------------------------------------------------
@@ -68,10 +69,17 @@ using namespace LWO;
* they aren't specific to one format version
*/
// ---------------------------------------------------------------------------
class LWOImporter : public BaseImporter {
class LWOImporter final : public BaseImporter {
public:
LWOImporter();
~LWOImporter() override;
/**
* @brief The class constructor.
*/
LWOImporter() = default;
/**
* @brief The class destructor.
*/
~LWOImporter() override = default;
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
@@ -113,13 +121,13 @@ private:
// -------------------------------------------------------------------
/** Parsing functions used for all file format versions
*/
inline void GetS0(std::string &out, unsigned int max);
inline float GetF4();
inline float GetF8();
inline uint64_t GetU8();
inline uint32_t GetU4();
inline uint16_t GetU2();
inline uint8_t GetU1();
void GetS0(std::string &out, unsigned int max);
float GetF4();
float GetF8();
uint64_t GetU8();
uint32_t GetU4();
uint16_t GetU2();
uint8_t GetU1();
// -------------------------------------------------------------------
/** Loads a surface chunk from an LWOB file
@@ -353,57 +361,44 @@ private:
LWO::Texture *SetupNewTextureLWOB(LWO::TextureList &list,
unsigned int size);
protected:
/** true if the file is a LWO2 file*/
bool mIsLWO2;
/** true if the file is a LXOB file*/
bool mIsLXOB;
bool mIsLWO3;
/** Temporary list of layers from the file */
LayerList *mLayers;
/** Pointer to the current layer */
LWO::Layer *mCurLayer;
/** Temporary tag list from the file */
TagList *mTags;
/** Mapping table to convert from tag to surface indices.
UINT_MAX indicates that a no corresponding surface is available */
TagMappingTable *mMapping;
/** Temporary surface list from the file */
SurfaceList *mSurfaces;
/** Temporary clip list from the file */
ClipList mClips;
/** Temporary envelope list from the file */
EnvelopeList mEnvelopes;
/** file buffer */
uint8_t *mFileBuffer;
/** Size of the file, in bytes */
unsigned int fileSize;
/** Output scene */
aiScene *mScene;
/** Configuration option: speed flag set? */
bool configSpeedFlag;
/** Configuration option: index of layer to be loaded */
unsigned int configLayerIndex;
/** Configuration option: name of layer to be loaded */
std::string configLayerName;
/** True if we have a named layer */
bool hasNamedLayer;
private:
/// true if the file is a LWO2 file
bool mIsLWO2{false};
/// true if the file is a LXOB file
bool mIsLXOB{false};
/// true if the file is a LWO3 file
bool mIsLWO3{false};
/// Temporary list of layers from the file
LayerList *mLayers{nullptr};
/// Pointer to the current layer
LWO::Layer *mCurLayer{nullptr};
/// Temporary tag list from the file
TagList *mTags{nullptr};
/// Mapping table to convert from tag to surface indices.
// UINT_MAX indicates that a no corresponding surface is available
TagMappingTable *mMapping{nullptr};
/// Temporary surface list from the file
SurfaceList *mSurfaces{nullptr};
/// Temporary clip list from the file
ClipList mClips{};
/// Temporary envelope list from the file
EnvelopeList mEnvelopes{};
/// Pointer to the file buffer
uint8_t *mFileBuffer{nullptr};
/// Size of the file, in bytes
unsigned int fileSize{0u};
/// End of the file buffer (for bounds checking)
uint8_t *mFileBufferEnd{nullptr};
/// Output scene
aiScene *mScene{nullptr};
/// Configuration option: speed flag set?
bool configSpeedFlag{false};
/// Configuration option: index of layer to be loaded
unsigned int configLayerIndex{0};
/// Configuration option: name of layer to be loaded */
std::string configLayerName{};
/// True if we have a named layer
bool hasNamedLayer{false};
};
// ------------------------------------------------------------------------------------------------
@@ -415,6 +410,7 @@ inline float LWOImporter::GetF4() {
return f;
}
// ------------------------------------------------------------------------------------------------
inline float LWOImporter::GetF8() {
double f;
::memcpy(&f, mFileBuffer, 8);
@@ -423,6 +419,7 @@ inline float LWOImporter::GetF8() {
return (float)f;
}
// ------------------------------------------------------------------------------------------------
inline uint64_t LWOImporter::GetU8() {
uint64_t f;
::memcpy(&f, mFileBuffer, 8);
@@ -482,16 +479,23 @@ inline int LWOImporter::ReadVSizedIntLWO2(uint8_t *&inout) {
inline void LWOImporter::GetS0(std::string &out, unsigned int max) {
unsigned int iCursor = 0;
const char *sz = (const char *)mFileBuffer;
while (*mFileBuffer) {
while (mFileBuffer < mFileBufferEnd && *mFileBuffer) {
if (++iCursor > max) {
ASSIMP_LOG_WARN("LWO: Invalid file, string is is too long");
ASSIMP_LOG_WARN("LWO: Invalid file, string is too long");
break;
}
++mFileBuffer;
}
size_t len = (size_t)((const char *)mFileBuffer - sz);
out = std::string(sz, len);
mFileBuffer += (len & 0x1 ? 1 : 2);
const size_t skip = (len & 0x1 ? 1u : 2u);
const size_t remaining = static_cast<size_t>(mFileBufferEnd - mFileBuffer);
if (remaining < skip) {
mFileBuffer = mFileBufferEnd;
} else {
mFileBuffer += skip;
}
}
} // end of namespace Assimp

View File

@@ -36,7 +36,6 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#if !defined ASSIMP_BUILD_NO_M3D_IMPORTER || !(defined ASSIMP_BUILD_NO_EXPORT || defined ASSIMP_BUILD_NO_M3D_EXPORTER)
@@ -103,15 +102,68 @@ M3DWrapper::M3DWrapper(IOSystem *pIOHandler, const std::vector<unsigned char> &b
ai_assert(nullptr != pIOHandler);
}
// Security fix: Validate buffer size and content before processing
if (buffer.empty()) {
ASSIMP_LOG_ERROR("M3D: Empty buffer provided");
m3d_ = nullptr;
return;
}
// Check for minimum valid M3D file size (header + basic structure)
const size_t MIN_VALID_M3D_SIZE = 100; // Minimum reasonable size for a valid M3D file
if (buffer.size() < MIN_VALID_M3D_SIZE) {
ASSIMP_LOG_ERROR("M3D: Buffer too small to be a valid M3D file");
m3d_ = nullptr;
return;
}
// Validate M3D magic signature to ensure this is actually an M3D file
// M3D files should start with "3DMO" or "3dmo" magic signature
if (buffer.size() >= 4) {
bool valid_signature = false;
// Check for "3DMO" (uppercase)
if (buffer[0] == '3' && buffer[1] == 'D' && buffer[2] == 'M' && buffer[3] == 'O') {
valid_signature = true;
}
// Check for "3dmo" (lowercase)
else if (buffer[0] == '3' && buffer[1] == 'd' && buffer[2] == 'm' && buffer[3] == 'o') {
valid_signature = true;
}
if (!valid_signature) {
ASSIMP_LOG_ERROR("M3D: Invalid file signature - not a valid M3D file");
m3d_ = nullptr;
return;
}
}
// Create a safe copy of the buffer with guard pages/validation
// This helps prevent buffer overflows from affecting the main process
std::vector<unsigned char> safe_buffer = buffer;
// Add a guard page at the end to catch any overflow attempts
// Note: This is a defensive measure, not a complete fix for the underlying issue
safe_buffer.resize(safe_buffer.size() + 1);
safe_buffer.back() = 0xFF; // Guard byte
#ifdef ASSIMP_USE_M3D_READFILECB
// pass this IOHandler to the C callback in a thread-local pointer
m3dimporter_pIOHandler = pIOHandler;
m3d_ = m3d_load(const_cast<unsigned char *>(buffer.data()), m3dimporter_readfile, free, nullptr);
m3d_ = m3d_load(const_cast<unsigned char *>(safe_buffer.data()), m3dimporter_readfile, free, nullptr);
// Clear the C callback
m3dimporter_pIOHandler = nullptr;
#else
m3d_ = m3d_load(const_cast<unsigned char *>(buffer.data()), nullptr, nullptr, nullptr);
m3d_ = m3d_load(const_cast<unsigned char *>(safe_buffer.data()), nullptr, nullptr, nullptr);
#endif
// Verify the guard byte wasn't overwritten (indicating potential buffer overflow)
if (safe_buffer.empty() || safe_buffer.back() != 0xFF) {
ASSIMP_LOG_ERROR("M3D: Potential buffer overflow detected during loading");
if (m3d_) {
m3d_free(m3d_);
m3d_ = nullptr;
}
}
}
M3DWrapper::~M3DWrapper() {
@@ -147,4 +199,4 @@ void M3DWrapper::ClearSave() {
}
} // namespace Assimp
#endif
#endif

View File

@@ -72,7 +72,7 @@ public:
/// Construct an M3D model from provided buffer
/// @note The m3d.h SDK function does not mark the data as const. Have assumed it does not write.
/// BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN
/// SECURITY: Buffer size validation and guard pages added to mitigate potential overflows
explicit M3DWrapper(IOSystem *pIOHandler, const std::vector<unsigned char> &buffer);
/// Theclasss destructor.

View File

@@ -971,7 +971,7 @@ void HL1MDLLoader::read_animations() {
pseqdesc = get_buffer_data<SequenceDesc_HL1>(header_->seqindex, header_->numseq);
aiAnimation **scene_animations_ptr = scene_->mAnimations = new aiAnimation *[scene_->mNumAnimations];
aiAnimation **scene_animations_ptr = scene_->mAnimations = new aiAnimation *[scene_->mNumAnimations]();
for (int sequence = 0; sequence < header_->numseq; ++sequence, ++pseqdesc) {
pseqgroup = get_buffer_data<SequenceGroup_HL1>(header_->seqgroupindex + pseqdesc->seqgroup * sizeof(SequenceGroup_HL1), 1);
@@ -992,7 +992,7 @@ void HL1MDLLoader::read_animations() {
scene_animation->mTicksPerSecond = pseqdesc->fps;
scene_animation->mDuration = static_cast<double>(pseqdesc->fps) * pseqdesc->numframes;
scene_animation->mNumChannels = static_cast<unsigned int>(header_->numbones);
scene_animation->mChannels = new aiNodeAnim *[scene_animation->mNumChannels];
scene_animation->mChannels = new aiNodeAnim *[scene_animation->mNumChannels]();
for (int bone = 0; bone < header_->numbones; bone++, ++pbone, ++panim) {
aiNodeAnim *node_anim = scene_animation->mChannels[bone] = new aiNodeAnim();

View File

@@ -450,12 +450,14 @@ void MDLImporter::InternReadFile_Quake1() {
BE_NCONST MDL::Frame *pcFrames = (BE_NCONST MDL::Frame *)szCurrent;
MDL::SimpleFrame *pcFirstFrame;
VALIDATE_FILE_SIZE((const unsigned char *)(pcFrames + 1));
if (0 == pcFrames->type) {
// get address of single frame
pcFirstFrame = (MDL::SimpleFrame *)&pcFrames->frame;
} else {
// get the first frame in the group
BE_NCONST MDL::GroupFrame *pcFrames2 = (BE_NCONST MDL::GroupFrame *)szCurrent;
VALIDATE_FILE_SIZE((const unsigned char *)(pcFrames2 + 1));
pcFirstFrame = (MDL::SimpleFrame *)( szCurrent + sizeof(MDL::GroupFrame::type) + sizeof(MDL::GroupFrame::numframes)
+ sizeof(MDL::GroupFrame::min) + sizeof(MDL::GroupFrame::max) + sizeof(*MDL::GroupFrame::times) * pcFrames2->numframes );
}
@@ -703,6 +705,7 @@ void MDLImporter::InternReadFile_3DGS_MDL345() {
// now get a pointer to the first frame in the file
BE_NCONST MDL::Frame *pcFrames = (BE_NCONST MDL::Frame *)szCurrent;
VALIDATE_FILE_SIZE((const unsigned char *)(pcFrames + 1));
AI_SWAP4(pcFrames->type);
// byte packed vertices
@@ -1173,6 +1176,7 @@ bool MDLImporter::ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7 &groupInf
for (unsigned int iFrame = 0; iFrame < (unsigned int)groupInfo.pcGroup->numframes; ++iFrame) {
MDL::IntFrameInfo_MDL7 frame((BE_NCONST MDL::Frame_MDL7 *)szCurrent, iFrame);
VALIDATE_FILE_SIZE((const unsigned char *)(frame.pcFrame + 1));
AI_SWAP4(frame.pcFrame->vertices_count);
AI_SWAP4(frame.pcFrame->transmatrix_count);

View File

@@ -57,58 +57,73 @@ namespace Assimp {
constexpr const char ObjFileParser::DEFAULT_MATERIAL[];
ObjFileParser::ObjFileParser() :
m_DataIt(),
m_DataItEnd(),
m_pModel(nullptr),
m_uiLine(0),
m_buffer(),
mEnd(&m_buffer[Buffersize]),
m_pIO(nullptr),
m_progress(nullptr),
m_originalObjFileName() {
std::fill_n(m_buffer, Buffersize, '\0');
// -------------------------------------------------------------------
static bool isDataDefinitionEnd(const char *tmp) {
ai_assert(tmp != nullptr);
if (*tmp == '\\') {
++tmp;
if (IsLineEnd(*tmp)) {
return true;
}
}
return false;
}
// -------------------------------------------------------------------
static bool isNanOrInf(const char *in) {
ai_assert(in != nullptr);
// Look for "nan" or "inf", case insensitive
return ((in[0] == 'N' || in[0] == 'n') && ASSIMP_strincmp(in, "nan", 3) == 0) ||
((in[0] == 'I' || in[0] == 'i') && ASSIMP_strincmp(in, "inf", 3) == 0);
}
// -------------------------------------------------------------------
ObjFileParser::ObjFileParser() : mEnd(&mBuffer[Buffersize-1]+1) {
std::fill_n(mBuffer, Buffersize, '\0');
}
// -------------------------------------------------------------------
ObjFileParser::ObjFileParser(IOStreamBuffer<char> &streamBuffer, const std::string &modelName,
IOSystem *io, ProgressHandler *progress,
const std::string &originalObjFileName) :
m_DataIt(),
m_DataItEnd(),
m_pModel(nullptr),
m_uiLine(0),
m_buffer(),
m_pIO(io),
m_progress(progress),
m_originalObjFileName(originalObjFileName) {
std::fill_n(m_buffer, Buffersize, '\0');
IOSystem *io, ProgressHandler *progress, const std::string &originalObjFileName) :
mIO(io),
mProgress(progress),
mOriginalObjFileName(originalObjFileName) {
std::fill_n(mBuffer, Buffersize, '\0');
// Create the model instance to store all the data
m_pModel.reset(new ObjFile::Model());
m_pModel->mModelName = modelName;
mModel.reset(new ObjFile::Model());
mModel->mModelName = modelName;
// create default material and store it
m_pModel->mDefaultMaterial = new ObjFile::Material;
m_pModel->mDefaultMaterial->MaterialName.Set(DEFAULT_MATERIAL);
m_pModel->mMaterialLib.emplace_back(DEFAULT_MATERIAL);
m_pModel->mMaterialMap[DEFAULT_MATERIAL] = m_pModel->mDefaultMaterial;
mModel->mDefaultMaterial = new ObjFile::Material;
mModel->mDefaultMaterial->MaterialName.Set(DEFAULT_MATERIAL);
mModel->mMaterialLib.emplace_back(DEFAULT_MATERIAL);
mModel->mMaterialMap[DEFAULT_MATERIAL] = mModel->mDefaultMaterial;
// Start parsing the file
parseFile(streamBuffer);
}
void ObjFileParser::setBuffer(std::vector<char> &buffer) {
m_DataIt = buffer.begin();
m_DataItEnd = buffer.end();
mDataIt = buffer.begin();
mDataItEnd = buffer.end();
ai_assert(mDataIt < mDataItEnd);
if (!buffer.empty()) {
mEnd = &buffer[buffer.size() - 1] + 1;
}
}
// -------------------------------------------------------------------
ObjFile::Model *ObjFileParser::GetModel() const {
return m_pModel.get();
return mModel.get();
}
// -------------------------------------------------------------------
void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
// only update every 100KB or it'll be too slow
//const unsigned int updateProgressEveryBytes = 100 * 1024;
// const unsigned int updateProgressEveryBytes = 100 * 1024;
const unsigned int bytesToProcess = static_cast<unsigned int>(streamBuffer.size());
const unsigned int progressTotal = bytesToProcess;
unsigned int processed = 0u;
@@ -117,90 +132,93 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
bool insideCstype = false;
std::vector<char> buffer;
while (streamBuffer.getNextDataLine(buffer, '\\')) {
m_DataIt = buffer.begin();
m_DataItEnd = buffer.end();
mDataIt = buffer.begin();
mDataItEnd = buffer.end();
mEnd = &buffer[buffer.size() - 1] + 1;
if (processed == 0 && std::distance(m_DataIt, m_DataItEnd) >= 3 &&
static_cast<unsigned char>(*m_DataIt) == 0xEF &&
static_cast<unsigned char>(*(m_DataIt + 1)) == 0xBB &&
static_cast<unsigned char>(*(m_DataIt + 2)) == 0xBF) {
m_DataIt += 3; // skip BOM
if (processed == 0 && std::distance(mDataIt, mDataItEnd) >= 3 &&
static_cast<unsigned char>(*mDataIt) == 0xEF &&
static_cast<unsigned char>(*(mDataIt + 1)) == 0xBB &&
static_cast<unsigned char>(*(mDataIt + 2)) == 0xBF) {
mDataIt += 3; // skip BOM
}
// Handle progress reporting
const size_t filePos(streamBuffer.getFilePos());
const size_t filePos = streamBuffer.getFilePos();
if (lastFilePos < filePos) {
processed = static_cast<unsigned int>(filePos);
lastFilePos = filePos;
m_progress->UpdateFileRead(processed, progressTotal);
if (mProgress != nullptr) {
mProgress->UpdateFileRead(processed, progressTotal);
}
}
// handle c-stype section end (http://paulbourke.net/dataformats/obj/)
if (insideCstype) {
switch (*m_DataIt) {
case 'e': {
std::string name;
getNameNoSpace(m_DataIt, m_DataItEnd, name);
insideCstype = name != "end";
} break;
switch (*mDataIt) {
case 'e': {
std::string name;
getNameNoSpace(mDataIt, mDataItEnd, name);
insideCstype = name != "end";
} break;
default:
break;
}
goto pf_skip_line;
}
// parse line
switch (*m_DataIt) {
switch (*mDataIt) {
case 'v': // Parse a vertex texture coordinate
{
++m_DataIt;
if (*m_DataIt == ' ' || *m_DataIt == '\t') {
++mDataIt;
if (*mDataIt == ' ' || *mDataIt == '\t') {
size_t numComponents = getNumComponentsInDataDefinition();
if (numComponents == 3) {
// read in vertex definition
getVector3(m_pModel->mVertices);
getVector3(mModel->mVertices);
} else if (numComponents == 4) {
// read in vertex definition (homogeneous coords)
getHomogeneousVector3(m_pModel->mVertices);
getHomogeneousVector3(mModel->mVertices);
} else if (numComponents == 6) {
// fill previous omitted vertex-colors by default
if (m_pModel->mVertexColors.size() < m_pModel->mVertices.size()) {
m_pModel->mVertexColors.resize(m_pModel->mVertices.size(), aiVector3D(0, 0, 0));
if (mModel->mVertexColors.size() < mModel->mVertices.size()) {
mModel->mVertexColors.resize(mModel->mVertices.size(), aiVector3D(0, 0, 0));
}
// read vertex and vertex-color
getTwoVectors3(m_pModel->mVertices, m_pModel->mVertexColors);
getTwoVectors3(mModel->mVertices, mModel->mVertexColors);
}
// append omitted vertex-colors as default for the end if any vertex-color exists
if (!m_pModel->mVertexColors.empty() && m_pModel->mVertexColors.size() < m_pModel->mVertices.size()) {
m_pModel->mVertexColors.resize(m_pModel->mVertices.size(), aiVector3D(0, 0, 0));
if (!mModel->mVertexColors.empty() && mModel->mVertexColors.size() < mModel->mVertices.size()) {
mModel->mVertexColors.resize(mModel->mVertices.size(), aiVector3D(0, 0, 0));
}
} else if (*m_DataIt == 't') {
} else if (*mDataIt == 't') {
// read in texture coordinate ( 2D or 3D )
++m_DataIt;
size_t dim = getTexCoordVector(m_pModel->mTextureCoord);
m_pModel->mTextureCoordDim = std::max(m_pModel->mTextureCoordDim, (unsigned int)dim);
} else if (*m_DataIt == 'n') {
++mDataIt;
size_t dim = getTexCoordVector(mModel->mTextureCoord);
mModel->mTextureCoordDim = std::max(mModel->mTextureCoordDim, (unsigned int)dim);
} else if (*mDataIt == 'n') {
// Read in normal vector definition
++m_DataIt;
getVector3(m_pModel->mNormals);
++mDataIt;
getVector3(mModel->mNormals);
}
} break;
case 'p': // Parse a face, line or point statement
case 'l':
case 'f': {
getFace(*m_DataIt == 'f' ? aiPrimitiveType_POLYGON : (*m_DataIt == 'l' ? aiPrimitiveType_LINE : aiPrimitiveType_POINT));
getFace(*mDataIt == 'f' ? aiPrimitiveType_POLYGON : (*mDataIt == 'l' ? aiPrimitiveType_LINE : aiPrimitiveType_POINT));
} break;
case '#': // Parse a comment
{
getComment();
skipComment();
} break;
case 'u': // Parse a material desc. setter
{
std::string name;
getNameNoSpace(m_DataIt, m_DataItEnd, name);
getNameNoSpace(mDataIt, mDataItEnd, name);
size_t nextSpace = name.find(' ');
if (nextSpace != std::string::npos)
@@ -215,14 +233,14 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
{
std::string name;
getNameNoSpace(m_DataIt, m_DataItEnd, name);
getNameNoSpace(mDataIt, mDataItEnd, name);
size_t nextSpace = name.find(' ');
if (nextSpace != std::string::npos)
name = name.substr(0, nextSpace);
if (name == "mg")
getGroupNumberAndResolution();
skipGroupNumberAndResolution();
else if (name == "mtllib")
getMaterialLib();
else
@@ -236,7 +254,7 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
case 's': // Parse group number
{
getGroupNumber();
skipGroupNumber();
} break;
case 'o': // Parse object name
@@ -247,59 +265,45 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
case 'c': // handle cstype section start
{
std::string name;
getNameNoSpace(m_DataIt, m_DataItEnd, name);
getNameNoSpace(mDataIt, mDataItEnd, name);
insideCstype = name == "cstype";
goto pf_skip_line;
}
default: {
pf_skip_line:
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
} break;
}
}
}
// -------------------------------------------------------------------
void ObjFileParser::copyNextWord(char *pBuffer, size_t length) {
size_t index = 0;
m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
if (*m_DataIt == '\\') {
++m_DataIt;
++m_DataIt;
m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
mDataIt = getNextWord<DataArrayIt>(mDataIt, mDataItEnd);
if (*mDataIt == '\\') {
++mDataIt;
++mDataIt;
mDataIt = getNextWord<DataArrayIt>(mDataIt, mDataItEnd);
}
while (m_DataIt != m_DataItEnd && !IsSpaceOrNewLine(*m_DataIt)) {
pBuffer[index] = *m_DataIt;
while (mDataIt != mDataItEnd && !IsSpaceOrNewLine(*mDataIt)) {
pBuffer[index] = *mDataIt;
index++;
if (index == length - 1) {
break;
}
++m_DataIt;
++mDataIt;
}
ai_assert(index < length);
pBuffer[index] = '\0';
}
static bool isDataDefinitionEnd(const char *tmp) {
if (*tmp == '\\') {
tmp++;
if (IsLineEnd(*tmp)) {
return true;
}
}
return false;
}
static bool isNanOrInf(const char *in) {
// Look for "nan" or "inf", case insensitive
return ((in[0] == 'N' || in[0] == 'n') && ASSIMP_strincmp(in, "nan", 3) == 0) ||
((in[0] == 'I' || in[0] == 'i') && ASSIMP_strincmp(in, "inf", 3) == 0);
}
// -------------------------------------------------------------------
size_t ObjFileParser::getNumComponentsInDataDefinition() {
size_t numComponents(0);
const char *tmp(&m_DataIt[0]);
const char *tmp = &mDataIt[0];
bool end_of_definition = false;
while (!end_of_definition) {
if (isDataDefinitionEnd(tmp)) {
@@ -323,25 +327,26 @@ size_t ObjFileParser::getNumComponentsInDataDefinition() {
return numComponents;
}
// -------------------------------------------------------------------
size_t ObjFileParser::getTexCoordVector(std::vector<aiVector3D> &point3d_array) {
size_t numComponents = getNumComponentsInDataDefinition();
ai_real x, y, z;
if (2 == numComponents) {
copyNextWord(m_buffer, Buffersize);
x = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
x = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
y = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
y = fast_atof(mBuffer);
z = 0.0;
} else if (3 == numComponents) {
copyNextWord(m_buffer, Buffersize);
x = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
x = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
y = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
y = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
z = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
z = fast_atof(mBuffer);
} else {
throw DeadlyImportError("OBJ: Invalid number of components");
}
@@ -357,121 +362,126 @@ size_t ObjFileParser::getTexCoordVector(std::vector<aiVector3D> &point3d_array)
z = 0;
point3d_array.emplace_back(x, y, z);
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
return numComponents;
}
// -------------------------------------------------------------------
void ObjFileParser::getVector3(std::vector<aiVector3D> &point3d_array) {
ai_real x, y, z;
copyNextWord(m_buffer, Buffersize);
x = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
x = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
y = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
y = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
z = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
z = fast_atof(mBuffer);
point3d_array.emplace_back(x, y, z);
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
// -------------------------------------------------------------------
void ObjFileParser::getHomogeneousVector3(std::vector<aiVector3D> &point3d_array) {
ai_real x, y, z, w;
copyNextWord(m_buffer, Buffersize);
x = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
x = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
y = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
y = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
z = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
z = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
w = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
w = fast_atof(mBuffer);
if (w == 0)
throw DeadlyImportError("OBJ: Invalid component in homogeneous vector (Division by zero)");
point3d_array.emplace_back(x / w, y / w, z / w);
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
// -------------------------------------------------------------------
void ObjFileParser::getTwoVectors3(std::vector<aiVector3D> &point3d_array_a, std::vector<aiVector3D> &point3d_array_b) {
ai_real x, y, z;
copyNextWord(m_buffer, Buffersize);
x = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
x = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
y = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
y = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
z = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
z = fast_atof(mBuffer);
point3d_array_a.emplace_back(x, y, z);
copyNextWord(m_buffer, Buffersize);
x = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
x = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
y = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
y = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
z = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
z = fast_atof(mBuffer);
point3d_array_b.emplace_back(x, y, z);
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
// -------------------------------------------------------------------
void ObjFileParser::getVector2(std::vector<aiVector2D> &point2d_array) {
ai_real x, y;
copyNextWord(m_buffer, Buffersize);
x = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
x = fast_atof(mBuffer);
copyNextWord(m_buffer, Buffersize);
y = (ai_real)fast_atof(m_buffer);
copyNextWord(mBuffer, Buffersize);
y = fast_atof(mBuffer);
point2d_array.emplace_back(x, y);
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
static constexpr char DefaultObjName[] = "defaultobject";
// -------------------------------------------------------------------
void ObjFileParser::getFace(aiPrimitiveType type) {
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
if (m_DataIt == m_DataItEnd || *m_DataIt == '\0') {
mDataIt = getNextToken<DataArrayIt>(mDataIt, mDataItEnd);
if (mDataIt == mDataItEnd || *mDataIt == '\0') {
return;
}
ObjFile::Face *face = new ObjFile::Face(type);
bool hasNormal = false;
const int vSize = static_cast<unsigned int>(m_pModel->mVertices.size());
const int vtSize = static_cast<unsigned int>(m_pModel->mTextureCoord.size());
const int vnSize = static_cast<unsigned int>(m_pModel->mNormals.size());
const int vSize = static_cast<unsigned int>(mModel->mVertices.size());
const int vtSize = static_cast<unsigned int>(mModel->mTextureCoord.size());
const int vnSize = static_cast<unsigned int>(mModel->mNormals.size());
const bool vt = (!m_pModel->mTextureCoord.empty());
const bool vn = (!m_pModel->mNormals.empty());
const bool vt = (!mModel->mTextureCoord.empty());
const bool vn = (!mModel->mNormals.empty());
int iPos = 0;
while (m_DataIt < m_DataItEnd) {
while (mDataIt < mDataItEnd) {
int iStep = 1;
if (IsLineEnd(*m_DataIt) || *m_DataIt == '#') {
if (IsLineEnd(*mDataIt) || *mDataIt == '#') {
break;
}
if (*m_DataIt == '/') {
if (*mDataIt == '/') {
if (type == aiPrimitiveType_POINT) {
ASSIMP_LOG_ERROR("Obj: Separator unexpected in point statement");
}
iPos++;
} else if (IsSpaceOrNewLine(*m_DataIt)) {
++iPos;
} else if (IsSpaceOrNewLine(*mDataIt) || *mDataIt == '\v') {
iPos = 0;
} else {
//OBJ USES 1 Base ARRAYS!!!!
const int iVal = ::atoi(&(*m_DataIt));
const int iVal = ::atoi(&(*mDataIt));
// increment iStep position based off of the sign and # of digits
int tmp = iVal;
@@ -516,62 +526,63 @@ void ObjFileParser::getFace(aiPrimitiveType type) {
throw DeadlyImportError("OBJ: Invalid face index.");
}
}
m_DataIt += iStep;
mDataIt += iStep;
}
if (face->m_vertices.empty()) {
ASSIMP_LOG_ERROR("Obj: Ignoring empty face");
// skip line and clean up
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
delete face;
return;
}
// Set active material, if one set
if (nullptr != m_pModel->mCurrentMaterial) {
face->m_pMaterial = m_pModel->mCurrentMaterial;
if (nullptr != mModel->mCurrentMaterial) {
face->m_pMaterial = mModel->mCurrentMaterial;
} else {
face->m_pMaterial = m_pModel->mDefaultMaterial;
face->m_pMaterial = mModel->mDefaultMaterial;
}
// Create a default object, if nothing is there
if (nullptr == m_pModel->mCurrentObject) {
if (nullptr == mModel->mCurrentObject) {
createObject(DefaultObjName);
}
// Assign face to mesh
if (nullptr == m_pModel->mCurrentMesh) {
if (nullptr == mModel->mCurrentMesh) {
createMesh(DefaultObjName);
}
// Store the face
m_pModel->mCurrentMesh->m_Faces.emplace_back(face);
m_pModel->mCurrentMesh->m_uiNumIndices += static_cast<unsigned int>(face->m_vertices.size());
m_pModel->mCurrentMesh->m_uiUVCoordinates[0] += static_cast<unsigned int>(face->m_texturCoords.size());
if (!m_pModel->mCurrentMesh->m_hasNormals && hasNormal) {
m_pModel->mCurrentMesh->m_hasNormals = true;
mModel->mCurrentMesh->m_Faces.emplace_back(face);
mModel->mCurrentMesh->m_uiNumIndices += static_cast<unsigned int>(face->m_vertices.size());
mModel->mCurrentMesh->m_uiUVCoordinates[0] += static_cast<unsigned int>(face->m_texturCoords.size());
if (!mModel->mCurrentMesh->m_hasNormals && hasNormal) {
mModel->mCurrentMesh->m_hasNormals = true;
}
// Skip the rest of the line
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
// -------------------------------------------------------------------
void ObjFileParser::getMaterialDesc() {
// Get next data for material data
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
if (m_DataIt == m_DataItEnd) {
mDataIt = getNextToken<DataArrayIt>(mDataIt, mDataItEnd);
if (mDataIt == mDataItEnd) {
return;
}
char *pStart = &(*m_DataIt);
while (m_DataIt != m_DataItEnd && !IsLineEnd(*m_DataIt)) {
++m_DataIt;
char *pStart = &(*mDataIt);
while (mDataIt != mDataItEnd && !IsLineEnd(*mDataIt)) {
++mDataIt;
}
// In some cases we should ignore this 'usemtl' command, this variable helps us to do so
bool skip = false;
// Get name
std::string strName(pStart, &(*m_DataIt));
std::string strName(pStart, &(*mDataIt));
strName = ai_trim(strName);
if (strName.empty()) {
skip = true;
@@ -580,63 +591,62 @@ void ObjFileParser::getMaterialDesc() {
// If the current mesh has the same material, we will ignore that 'usemtl' command
// There is no need to create another object or even mesh here
if (!skip) {
if (m_pModel->mCurrentMaterial && m_pModel->mCurrentMaterial->MaterialName == aiString(strName)) {
if (mModel->mCurrentMaterial && mModel->mCurrentMaterial->MaterialName == aiString(strName)) {
skip = true;
}
}
if (!skip) {
// Search for material
std::map<std::string, ObjFile::Material *>::iterator it = m_pModel->mMaterialMap.find(strName);
if (it == m_pModel->mMaterialMap.end()) {
std::map<std::string, ObjFile::Material *>::iterator it = mModel->mMaterialMap.find(strName);
if (it == mModel->mMaterialMap.end()) {
// Not found, so we don't know anything about the material except for its name.
// This may be the case if the material library is missing. We don't want to lose all
// materials if that happens, so create a new named material instead of discarding it
// completely.
ASSIMP_LOG_ERROR("OBJ: failed to locate material ", strName, ", creating new material");
m_pModel->mCurrentMaterial = new ObjFile::Material();
m_pModel->mCurrentMaterial->MaterialName.Set(strName);
m_pModel->mMaterialLib.push_back(strName);
m_pModel->mMaterialMap[strName] = m_pModel->mCurrentMaterial;
mModel->mCurrentMaterial = new ObjFile::Material();
mModel->mCurrentMaterial->MaterialName.Set(strName);
mModel->mMaterialLib.push_back(strName);
mModel->mMaterialMap[strName] = mModel->mCurrentMaterial;
} else {
// Found, using detected material
m_pModel->mCurrentMaterial = (*it).second;
mModel->mCurrentMaterial = it->second;
}
if (needsNewMesh(strName)) {
auto newMeshName = m_pModel->mActiveGroup.empty() ? strName : m_pModel->mActiveGroup;
auto newMeshName = mModel->mActiveGroup.empty() ? strName : mModel->mActiveGroup;
createMesh(newMeshName);
}
m_pModel->mCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strName);
mModel->mCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strName);
}
// Skip rest of line
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
// -------------------------------------------------------------------
// Get a comment, values will be skipped
void ObjFileParser::getComment() {
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
void ObjFileParser::skipComment() {
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
// -------------------------------------------------------------------
// Get material library from file.
void ObjFileParser::getMaterialLib() {
// Translate tuple
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
if (m_DataIt == m_DataItEnd) {
mDataIt = getNextToken<DataArrayIt>(mDataIt, mDataItEnd);
if (mDataIt == mDataItEnd) {
return;
}
char *pStart = &(*m_DataIt);
while (m_DataIt != m_DataItEnd && !IsLineEnd(*m_DataIt)) {
++m_DataIt;
char *pStart = &(*mDataIt);
while (mDataIt != mDataItEnd && !IsLineEnd(*mDataIt)) {
++mDataIt;
}
// Check for existence
const std::string strMatName(pStart, &(*m_DataIt));
const std::string strMatName(pStart, &(*mDataIt));
std::string absName;
// Check if directive is valid.
@@ -645,8 +655,8 @@ void ObjFileParser::getMaterialLib() {
return;
}
if (m_pIO->StackSize() > 0) {
std::string path = m_pIO->CurrentDirectory();
if (mIO->StackSize() > 0) {
std::string path = mIO->CurrentDirectory();
if ('/' != *path.rbegin()) {
path += '/';
}
@@ -656,15 +666,15 @@ void ObjFileParser::getMaterialLib() {
absName = strMatName;
}
std::unique_ptr<IOStream> pFile(m_pIO->Open(absName));
std::unique_ptr<IOStream> pFile(mIO->Open(absName));
if (nullptr == pFile) {
ASSIMP_LOG_ERROR("OBJ: Unable to locate material file ", strMatName);
std::string strMatFallbackName = m_originalObjFileName.substr(0, m_originalObjFileName.length() - 3) + "mtl";
std::string strMatFallbackName = mOriginalObjFileName.substr(0, mOriginalObjFileName.length() - 3) + "mtl";
ASSIMP_LOG_INFO("OBJ: Opening fallback material file ", strMatFallbackName);
pFile.reset(m_pIO->Open(strMatFallbackName));
pFile.reset(mIO->Open(strMatFallbackName));
if (!pFile) {
ASSIMP_LOG_ERROR("OBJ: Unable to locate fallback material file ", strMatFallbackName);
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
return;
}
}
@@ -678,47 +688,48 @@ void ObjFileParser::getMaterialLib() {
//m_pIO->Close(pFile);
// Importing the material library
ObjFileMtlImporter mtlImporter(buffer, strMatName, m_pModel.get());
ObjFileMtlImporter mtlImporter(buffer, strMatName, mModel.get());
}
// -------------------------------------------------------------------
// Set a new material definition as the current material.
void ObjFileParser::getNewMaterial() {
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
if (m_DataIt == m_DataItEnd) {
mDataIt = getNextToken<DataArrayIt>(mDataIt, mDataItEnd);
mDataIt = getNextWord<DataArrayIt>(mDataIt, mDataItEnd);
if (mDataIt == mDataItEnd) {
return;
}
char *pStart = &(*m_DataIt);
std::string strMat(pStart, *m_DataIt);
while (m_DataIt != m_DataItEnd && IsSpaceOrNewLine(*m_DataIt)) {
++m_DataIt;
char *pStart = &(*mDataIt);
std::string strMat(pStart, *mDataIt);
while (mDataIt != mDataItEnd && IsSpaceOrNewLine(*mDataIt)) {
++mDataIt;
}
std::map<std::string, ObjFile::Material *>::iterator it = m_pModel->mMaterialMap.find(strMat);
if (it == m_pModel->mMaterialMap.end()) {
auto it = mModel->mMaterialMap.find(strMat);
if (it == mModel->mMaterialMap.end()) {
// Show a warning, if material was not found
ASSIMP_LOG_WARN("OBJ: Unsupported material requested: ", strMat);
m_pModel->mCurrentMaterial = m_pModel->mDefaultMaterial;
mModel->mCurrentMaterial = mModel->mDefaultMaterial;
} else {
// Set new material
if (needsNewMesh(strMat)) {
createMesh(strMat);
}
m_pModel->mCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strMat);
mModel->mCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strMat);
}
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
static constexpr int InvalidMaterialIndex = -1;
// -------------------------------------------------------------------
int ObjFileParser::getMaterialIndex(const std::string &strMaterialName) {
int mat_index = -1;
int mat_index = InvalidMaterialIndex;
if (strMaterialName.empty()) {
return mat_index;
}
for (size_t index = 0; index < m_pModel->mMaterialLib.size(); ++index) {
if (strMaterialName == m_pModel->mMaterialLib[index]) {
for (size_t index = 0; index < mModel->mMaterialLib.size(); ++index) {
if (strMaterialName == mModel->mMaterialLib[index]) {
mat_index = (int)index;
break;
}
@@ -732,111 +743,108 @@ void ObjFileParser::getGroupName() {
std::string groupName;
// here we skip 'g ' from line
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
m_DataIt = getName<DataArrayIt>(m_DataIt, m_DataItEnd, groupName);
if (isEndOfBuffer(m_DataIt, m_DataItEnd)) {
mDataIt = getNextToken<DataArrayIt>(mDataIt, mDataItEnd);
mDataIt = getName<DataArrayIt>(mDataIt, mDataItEnd, groupName);
if (isEndOfBuffer(mDataIt, mDataItEnd)) {
return;
}
// Change active group, if necessary
if (m_pModel->mActiveGroup != groupName) {
if (mModel->mActiveGroup != groupName) {
// Search for already existing entry
ObjFile::Model::ConstGroupMapIt it = m_pModel->mGroups.find(groupName);
ObjFile::Model::ConstGroupMapIt it = mModel->mGroups.find(groupName);
// We are mapping groups into the object structure
createObject(groupName);
// New group name, creating a new entry
if (it == m_pModel->mGroups.end()) {
if (it == mModel->mGroups.end()) {
std::vector<unsigned int> *pFaceIDArray = new std::vector<unsigned int>;
m_pModel->mGroups[groupName] = pFaceIDArray;
m_pModel->mGroupFaceIDs = (pFaceIDArray);
mModel->mGroups[groupName] = pFaceIDArray;
mModel->mGroupFaceIDs = (pFaceIDArray);
} else {
m_pModel->mGroupFaceIDs = (*it).second;
mModel->mGroupFaceIDs = (*it).second;
}
m_pModel->mActiveGroup = groupName;
mModel->mActiveGroup = groupName;
}
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
// -------------------------------------------------------------------
// Not supported
void ObjFileParser::getGroupNumber() {
void ObjFileParser::skipGroupNumber() {
// Not used
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
// -------------------------------------------------------------------
// Not supported
void ObjFileParser::getGroupNumberAndResolution() {
void ObjFileParser::skipGroupNumberAndResolution() {
// Not used
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
// -------------------------------------------------------------------
// Stores values for a new object instance, name will be used to
// identify it.
void ObjFileParser::getObjectName() {
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
if (m_DataIt == m_DataItEnd) {
mDataIt = getNextToken<DataArrayIt>(mDataIt, mDataItEnd);
if (mDataIt == mDataItEnd) {
return;
}
char *pStart = &(*m_DataIt);
while (m_DataIt != m_DataItEnd && !IsSpaceOrNewLine(*m_DataIt)) {
++m_DataIt;
char *pStart = &(*mDataIt);
while (mDataIt != mDataItEnd && !IsSpaceOrNewLine(*mDataIt)) {
++mDataIt;
}
std::string strObjectName(pStart, &(*m_DataIt));
std::string strObjectName(pStart, &(*mDataIt));
if (!strObjectName.empty()) {
// Reset current object
m_pModel->mCurrentObject = nullptr;
mModel->mCurrentObject = nullptr;
// Search for actual object
for (std::vector<ObjFile::Object *>::const_iterator it = m_pModel->mObjects.begin();
it != m_pModel->mObjects.end();
++it) {
for (auto it = mModel->mObjects.begin(); it != mModel->mObjects.end(); ++it) {
if ((*it)->m_strObjName == strObjectName) {
m_pModel->mCurrentObject = *it;
mModel->mCurrentObject = *it;
break;
}
}
// Allocate a new object, if current one was not found before
if (nullptr == m_pModel->mCurrentObject) {
if (mModel->mCurrentObject == nullptr) {
createObject(strObjectName);
}
}
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
}
// -------------------------------------------------------------------
// Creates a new object instance
void ObjFileParser::createObject(const std::string &objName) {
ai_assert(nullptr != m_pModel);
ai_assert(nullptr != mModel);
m_pModel->mCurrentObject = new ObjFile::Object;
m_pModel->mCurrentObject->m_strObjName = objName;
m_pModel->mObjects.push_back(m_pModel->mCurrentObject);
mModel->mCurrentObject = new ObjFile::Object;
mModel->mCurrentObject->m_strObjName = objName;
mModel->mObjects.push_back(mModel->mCurrentObject);
createMesh(objName);
if (m_pModel->mCurrentMaterial) {
m_pModel->mCurrentMesh->m_uiMaterialIndex =
getMaterialIndex(m_pModel->mCurrentMaterial->MaterialName.data);
m_pModel->mCurrentMesh->m_pMaterial = m_pModel->mCurrentMaterial;
if (mModel->mCurrentMaterial) {
mModel->mCurrentMesh->m_uiMaterialIndex =
getMaterialIndex(mModel->mCurrentMaterial->MaterialName.data);
mModel->mCurrentMesh->m_pMaterial = mModel->mCurrentMaterial;
}
}
// -------------------------------------------------------------------
// Creates a new mesh
void ObjFileParser::createMesh(const std::string &meshName) {
ai_assert(nullptr != m_pModel);
ai_assert(nullptr != mModel);
m_pModel->mCurrentMesh = new ObjFile::Mesh(meshName);
m_pModel->mMeshes.push_back(m_pModel->mCurrentMesh);
unsigned int meshId = static_cast<unsigned int>(m_pModel->mMeshes.size() - 1);
if (nullptr != m_pModel->mCurrentObject) {
m_pModel->mCurrentObject->m_Meshes.push_back(meshId);
mModel->mCurrentMesh = new ObjFile::Mesh(meshName);
mModel->mMeshes.push_back(mModel->mCurrentMesh);
auto meshId = static_cast<unsigned int>(mModel->mMeshes.size() - 1);
if (mModel->mCurrentObject != nullptr) {
mModel->mCurrentObject->m_Meshes.push_back(meshId);
} else {
ASSIMP_LOG_ERROR("OBJ: No object detected to attach a new mesh instance.");
}
@@ -846,16 +854,16 @@ void ObjFileParser::createMesh(const std::string &meshName) {
// Returns true, if a new mesh must be created.
bool ObjFileParser::needsNewMesh(const std::string &materialName) {
// If no mesh data yet
if (m_pModel->mCurrentMesh == nullptr) {
if (mModel->mCurrentMesh == nullptr) {
return true;
}
bool newMat = false;
int matIdx = getMaterialIndex(materialName);
int curMatIdx = m_pModel->mCurrentMesh->m_uiMaterialIndex;
int curMatIdx = mModel->mCurrentMesh->m_uiMaterialIndex;
if (curMatIdx != int(ObjFile::Mesh::NoMaterial) && curMatIdx != matIdx
// no need create a new mesh if no faces in current
// lets say 'usemtl' goes straight after 'g'
&& !m_pModel->mCurrentMesh->m_Faces.empty()) {
&& !mModel->mCurrentMesh->m_Faces.empty()) {
// New material -> only one material per mesh, so we need to create a new
// material
newMat = true;
@@ -866,7 +874,7 @@ bool ObjFileParser::needsNewMesh(const std::string &materialName) {
// -------------------------------------------------------------------
// Shows an error in parsing process.
void ObjFileParser::reportErrorTokenInFace() {
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
mDataIt = skipLine<DataArrayIt>(mDataIt, mDataItEnd, mLine);
ASSIMP_LOG_ERROR("OBJ: Not supported token in face description detected");
}

View File

@@ -106,8 +106,8 @@ protected:
void getFace(aiPrimitiveType type);
/// Reads the material description.
void getMaterialDesc();
/// Gets a comment.
void getComment();
/// Skip a comment.
void skipComment();
/// Gets a a material library.
void getMaterialLib();
/// Creates a new material.
@@ -115,9 +115,9 @@ protected:
/// Gets the group name from file.
void getGroupName();
/// Gets the group number from file.
void getGroupNumber();
void skipGroupNumber();
/// Gets the group number and resolution from file.
void getGroupNumberAndResolution();
void skipGroupNumberAndResolution();
/// Returns the index of the material. Is -1 if not material was found.
int getMaterialIndex(const std::string &strMaterialName);
/// Parse object name
@@ -135,23 +135,23 @@ private:
/// Default material name
static constexpr const char DEFAULT_MATERIAL[] = AI_DEFAULT_MATERIAL_NAME;
//! Iterator to current position in buffer
DataArrayIt m_DataIt;
DataArrayIt mDataIt{};
//! Iterator to end position of buffer
DataArrayIt m_DataItEnd;
DataArrayIt mDataItEnd{};
//! Pointer to model instance
std::unique_ptr<ObjFile::Model> m_pModel;
std::unique_ptr<ObjFile::Model> mModel{};
//! Current line (for debugging)
unsigned int m_uiLine;
unsigned int mLine{ 0 };
//! Helper buffer
char m_buffer[Buffersize];
char mBuffer[Buffersize];
/// End of buffer
const char *mEnd;
const char *mEnd{ nullptr };
/// Pointer to IO system instance.
IOSystem *m_pIO;
IOSystem *mIO{ nullptr };
//! Pointer to progress handler
ProgressHandler *m_progress;
ProgressHandler *mProgress{ nullptr };
/// Path to the current model, name of the obj file where the buffer comes from
const std::string m_originalObjFileName;
const std::string mOriginalObjFileName{};
};
} // Namespace Assimp

View File

@@ -892,7 +892,7 @@ void OpenGEXImporter::handleIndexArrayNode(ODDLParser::DDLNode *node, aiScene *
m_currentMesh->mVertices = new aiVector3D[m_currentMesh->mNumVertices];
bool hasColors(false);
if (m_currentVertices.m_numColors > 0) {
m_currentMesh->mColors[0] = new aiColor4D[m_currentVertices.m_numColors];
m_currentMesh->mColors[0] = new aiColor4D[m_currentMesh->mNumVertices];
hasColors = true;
}
bool hasNormalCoords(false);
@@ -924,7 +924,7 @@ void OpenGEXImporter::handleIndexArrayNode(ODDLParser::DDLNode *node, aiScene *
ai_assert(index < m_currentMesh->mNumVertices);
aiVector3D &pos = (m_currentVertices.m_vertices[idx]);
m_currentMesh->mVertices[index].Set(pos.x, pos.y, pos.z);
if (hasColors) {
if (hasColors && static_cast<size_t>(idx) < m_currentVertices.m_numColors) {
aiColor4D &col = m_currentVertices.m_colors[idx];
m_currentMesh->mColors[0][index] = col;
}

File diff suppressed because it is too large Load Diff

View File

@@ -60,70 +60,60 @@ namespace Assimp {
using namespace PLY;
// ---------------------------------------------------------------------------
/** Importer class to load the stanford PLY file format
*/
/// @brief Importer class to load the stanford PLY file format
// ---------------------------------------------------------------------------
class PLYImporter final : public BaseImporter {
public:
PLYImporter();
/// @brief Default constructor
PLYImporter() = default;
/// @brief Destructor
~PLYImporter() override;
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details.
*/
bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
bool checkSig) const override;
/// Returns whether the class can handle the format of the given file.
/// @see BaseImporter::CanRead() for details.
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
// -------------------------------------------------------------------
/** Extract a vertex from the DOM
*/
/// Extract a vertex from the DOM
void LoadVertex(const PLY::Element *pcElement, const PLY::ElementInstance *instElement, unsigned int pos);
// -------------------------------------------------------------------
/** Extract a face from the DOM
*/
/// @brief Extract a face from the DOM
/// The function will also take care of the correct winding order of the triangles.
/// @param pcElement The element containing the face data
/// @param instElement The element instance containing the face data
/// @param pos The position of the face in the element instance. This is needed to correctly assign the face to the mesh.
void LoadFace(const PLY::Element *pcElement, const PLY::ElementInstance *instElement, unsigned int pos);
/// @brief Will create a triangle from a triangle strip.
/// The function will use the first three vertices of the strip to create the first triangle, then the second,
/// third and fourth vertex to create the second triangle and so on. The function will also take care of the correct
/// winding order of the triangles.
/// @param instElement The element instance containing the triangle strip data
/// @param iProperty The index of the property containing the triangle strip data
/// @param eType The data type of the triangle strip data
void createFromeTriStrip(const Assimp::PLY::ElementInstance *instElement, unsigned int iProperty, Assimp::PLY::EDataType eType);
protected:
// -------------------------------------------------------------------
/** Return importer meta information.
* See #BaseImporter::GetInfo for the details
*/
const aiImporterDesc *GetInfo() const override;
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile(const std::string &pFile, aiScene *pScene,
IOSystem *pIOHandler) override;
void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
// -------------------------------------------------------------------
/** Extract a material list from the DOM
*/
/// @brief Extract a material list from the DOM
/// @param pvOut The output material list. The function will fill the list with the extracted materials.
/// @param defaultTexture The default texture to use for the materials. This is needed to correctly assign the texture to the materials.
/// @param pointsOnly Whether the file contains only points. This is needed to correctly assign the material properties.
void LoadMaterial(std::vector<aiMaterial *> *pvOut, std::string &defaultTexture, const bool pointsOnly);
// -------------------------------------------------------------------
/** Static helper to parse a color from four single channels in
*/
static void GetMaterialColor(
const std::vector<PLY::PropertyInstance> &avList,
unsigned int aiPositions[4],
PLY::EDataType aiTypes[4],
aiColor4D *clrOut);
// -------------------------------------------------------------------
/** Static helper to parse a color channel value. The input value
* is normalized to 0-1.
*/
static ai_real NormalizeColorValue(
PLY::PropertyInstance::ValueUnion val,
PLY::EDataType eType);
private:
unsigned char *mBuffer;
PLY::DOM *pcDOM;
aiMesh *mGeneratedMesh;
unsigned char *mBuffer{nullptr};
PLY::DOM *pcDOM{nullptr};
aiMesh *mGeneratedMesh{nullptr};
};
} // end of namespace Assimp

View File

@@ -52,53 +52,53 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
std::string to_string(EElementSemantic e) {
// ------------------------------------------------------------------------------------------------
static std::string to_string(EElementSemantic e) {
switch (e) {
case EEST_Vertex:
return std::string{ "vertex" };
case EEST_TriStrip:
return std::string{ "tristrips" };
case EEST_Edge:
return std::string{ "edge" };
case EEST_Material:
return std::string{ "material" };
case EEST_TextureFile:
return std::string{ "TextureFile" };
default:
return std::string{ "invalid" };
case EEST_Vertex:
return std::string{ "vertex" };
case EEST_TriStrip:
return std::string{ "tristrips" };
case EEST_Edge:
return std::string{ "edge" };
case EEST_Material:
return std::string{ "material" };
case EEST_TextureFile:
return std::string{ "TextureFile" };
default:
return std::string{ "invalid" };
}
}
// ------------------------------------------------------------------------------------------------
PLY::EDataType PLY::Property::ParseDataType(std::vector<char> &buffer) {
EDataType Property::ParseDataType(std::vector<char> &buffer) {
ai_assert(!buffer.empty());
PLY::EDataType eOut = PLY::EDT_INVALID;
EDataType eOut = EDT_INVALID;
if (PLY::DOM::TokenMatch(buffer, "char", 4) ||
PLY::DOM::TokenMatch(buffer, "int8", 4)) {
eOut = PLY::EDT_Char;
} else if (PLY::DOM::TokenMatch(buffer, "uchar", 5) ||
PLY::DOM::TokenMatch(buffer, "uint8", 5)) {
eOut = PLY::EDT_UChar;
} else if (PLY::DOM::TokenMatch(buffer, "short", 5) ||
PLY::DOM::TokenMatch(buffer, "int16", 5)) {
eOut = PLY::EDT_Short;
} else if (PLY::DOM::TokenMatch(buffer, "ushort", 6) ||
PLY::DOM::TokenMatch(buffer, "uint16", 6)) {
eOut = PLY::EDT_UShort;
} else if (PLY::DOM::TokenMatch(buffer, "int32", 5) || PLY::DOM::TokenMatch(buffer, "int", 3)) {
eOut = PLY::EDT_Int;
} else if (PLY::DOM::TokenMatch(buffer, "uint32", 6) || PLY::DOM::TokenMatch(buffer, "uint", 4)) {
eOut = PLY::EDT_UInt;
} else if (PLY::DOM::TokenMatch(buffer, "float", 5) || PLY::DOM::TokenMatch(buffer, "float32", 7)) {
eOut = PLY::EDT_Float;
} else if (PLY::DOM::TokenMatch(buffer, "double64", 8) || PLY::DOM::TokenMatch(buffer, "double", 6) ||
PLY::DOM::TokenMatch(buffer, "float64", 7)) {
eOut = PLY::EDT_Double;
if (DOM::TokenMatch(buffer, "char", 4) ||
DOM::TokenMatch(buffer, "int8", 4)) {
eOut = EDT_Char;
} else if (DOM::TokenMatch(buffer, "uchar", 5) ||
DOM::TokenMatch(buffer, "uint8", 5)) {
eOut = EDT_UChar;
} else if (DOM::TokenMatch(buffer, "short", 5) ||
DOM::TokenMatch(buffer, "int16", 5)) {
eOut = EDT_Short;
} else if (DOM::TokenMatch(buffer, "ushort", 6) ||
DOM::TokenMatch(buffer, "uint16", 6)) {
eOut = EDT_UShort;
} else if (DOM::TokenMatch(buffer, "int32", 5) || DOM::TokenMatch(buffer, "int", 3)) {
eOut = EDT_Int;
} else if (DOM::TokenMatch(buffer, "uint32", 6) || DOM::TokenMatch(buffer, "uint", 4)) {
eOut = EDT_UInt;
} else if (DOM::TokenMatch(buffer, "float", 5) || DOM::TokenMatch(buffer, "float32", 7)) {
eOut = EDT_Float;
} else if (DOM::TokenMatch(buffer, "double64", 8) || DOM::TokenMatch(buffer, "double", 6) ||
DOM::TokenMatch(buffer, "float64", 7)) {
eOut = EDT_Double;
}
if (PLY::EDT_INVALID == eOut) {
if (EDT_INVALID == eOut) {
ASSIMP_LOG_INFO("Found unknown data type in PLY file. This is OK");
}
@@ -106,81 +106,81 @@ PLY::EDataType PLY::Property::ParseDataType(std::vector<char> &buffer) {
}
// ------------------------------------------------------------------------------------------------
PLY::ESemantic PLY::Property::ParseSemantic(std::vector<char> &buffer) {
ESemantic Property::ParseSemantic(std::vector<char> &buffer) {
ai_assert(!buffer.empty());
PLY::ESemantic eOut = PLY::EST_INVALID;
if (PLY::DOM::TokenMatch(buffer, "red", 3)) {
eOut = PLY::EST_Red;
} else if (PLY::DOM::TokenMatch(buffer, "green", 5)) {
eOut = PLY::EST_Green;
} else if (PLY::DOM::TokenMatch(buffer, "blue", 4)) {
eOut = PLY::EST_Blue;
} else if (PLY::DOM::TokenMatch(buffer, "alpha", 5)) {
eOut = PLY::EST_Alpha;
} else if (PLY::DOM::TokenMatch(buffer, "vertex_index", 12) || PLY::DOM::TokenMatch(buffer, "vertex_indices", 14)) {
eOut = PLY::EST_VertexIndex;
} else if (PLY::DOM::TokenMatch(buffer, "texcoord", 8)) // Manage uv coords on faces
ESemantic eOut = PLY::EST_INVALID;
if (DOM::TokenMatch(buffer, "red", 3)) {
eOut = EST_Red;
} else if (DOM::TokenMatch(buffer, "green", 5)) {
eOut = EST_Green;
} else if (DOM::TokenMatch(buffer, "blue", 4)) {
eOut = EST_Blue;
} else if (DOM::TokenMatch(buffer, "alpha", 5)) {
eOut = EST_Alpha;
} else if (DOM::TokenMatch(buffer, "vertex_index", 12) || PLY::DOM::TokenMatch(buffer, "vertex_indices", 14)) {
eOut = EST_VertexIndex;
} else if (DOM::TokenMatch(buffer, "texcoord", 8)) // Manage uv coords on faces
{
eOut = PLY::EST_TextureCoordinates;
} else if (PLY::DOM::TokenMatch(buffer, "material_index", 14)) {
eOut = PLY::EST_MaterialIndex;
} else if (PLY::DOM::TokenMatch(buffer, "ambient_red", 11)) {
eOut = PLY::EST_AmbientRed;
} else if (PLY::DOM::TokenMatch(buffer, "ambient_green", 13)) {
eOut = PLY::EST_AmbientGreen;
} else if (PLY::DOM::TokenMatch(buffer, "ambient_blue", 12)) {
eOut = PLY::EST_AmbientBlue;
} else if (PLY::DOM::TokenMatch(buffer, "ambient_alpha", 13)) {
eOut = PLY::EST_AmbientAlpha;
} else if (PLY::DOM::TokenMatch(buffer, "diffuse_red", 11)) {
eOut = PLY::EST_DiffuseRed;
} else if (PLY::DOM::TokenMatch(buffer, "diffuse_green", 13)) {
eOut = PLY::EST_DiffuseGreen;
} else if (PLY::DOM::TokenMatch(buffer, "diffuse_blue", 12)) {
eOut = PLY::EST_DiffuseBlue;
} else if (PLY::DOM::TokenMatch(buffer, "diffuse_alpha", 13)) {
eOut = PLY::EST_DiffuseAlpha;
} else if (PLY::DOM::TokenMatch(buffer, "specular_red", 12)) {
eOut = PLY::EST_SpecularRed;
} else if (PLY::DOM::TokenMatch(buffer, "specular_green", 14)) {
eOut = PLY::EST_SpecularGreen;
} else if (PLY::DOM::TokenMatch(buffer, "specular_blue", 13)) {
eOut = PLY::EST_SpecularBlue;
} else if (PLY::DOM::TokenMatch(buffer, "specular_alpha", 14)) {
eOut = PLY::EST_SpecularAlpha;
} else if (PLY::DOM::TokenMatch(buffer, "opacity", 7)) {
eOut = PLY::EST_Opacity;
} else if (PLY::DOM::TokenMatch(buffer, "specular_power", 14)) {
eOut = PLY::EST_PhongPower;
} else if (PLY::DOM::TokenMatch(buffer, "r", 1)) {
eOut = PLY::EST_Red;
} else if (PLY::DOM::TokenMatch(buffer, "g", 1)) {
eOut = PLY::EST_Green;
} else if (PLY::DOM::TokenMatch(buffer, "b", 1)) {
eOut = PLY::EST_Blue;
eOut = EST_TextureCoordinates;
} else if (DOM::TokenMatch(buffer, "material_index", 14)) {
eOut = EST_MaterialIndex;
} else if (DOM::TokenMatch(buffer, "ambient_red", 11)) {
eOut = EST_AmbientRed;
} else if (DOM::TokenMatch(buffer, "ambient_green", 13)) {
eOut = EST_AmbientGreen;
} else if (DOM::TokenMatch(buffer, "ambient_blue", 12)) {
eOut = EST_AmbientBlue;
} else if (DOM::TokenMatch(buffer, "ambient_alpha", 13)) {
eOut = EST_AmbientAlpha;
} else if (DOM::TokenMatch(buffer, "diffuse_red", 11)) {
eOut = EST_DiffuseRed;
} else if (DOM::TokenMatch(buffer, "diffuse_green", 13)) {
eOut = EST_DiffuseGreen;
} else if (DOM::TokenMatch(buffer, "diffuse_blue", 12)) {
eOut = EST_DiffuseBlue;
} else if (DOM::TokenMatch(buffer, "diffuse_alpha", 13)) {
eOut = EST_DiffuseAlpha;
} else if (DOM::TokenMatch(buffer, "specular_red", 12)) {
eOut = EST_SpecularRed;
} else if (DOM::TokenMatch(buffer, "specular_green", 14)) {
eOut = EST_SpecularGreen;
} else if (DOM::TokenMatch(buffer, "specular_blue", 13)) {
eOut = EST_SpecularBlue;
} else if (DOM::TokenMatch(buffer, "specular_alpha", 14)) {
eOut = EST_SpecularAlpha;
} else if (DOM::TokenMatch(buffer, "opacity", 7)) {
eOut = EST_Opacity;
} else if (DOM::TokenMatch(buffer, "specular_power", 14)) {
eOut = EST_PhongPower;
} else if (DOM::TokenMatch(buffer, "r", 1)) {
eOut = EST_Red;
} else if (DOM::TokenMatch(buffer, "g", 1)) {
eOut = EST_Green;
} else if (DOM::TokenMatch(buffer, "b", 1)) {
eOut = EST_Blue;
}
// NOTE: Blender3D exports texture coordinates as s,t tuples
else if (PLY::DOM::TokenMatch(buffer, "u", 1) || PLY::DOM::TokenMatch(buffer, "s", 1) || PLY::DOM::TokenMatch(buffer, "tx", 2) || PLY::DOM::TokenMatch(buffer, "texture_u", 9)) {
eOut = PLY::EST_UTextureCoord;
} else if (PLY::DOM::TokenMatch(buffer, "v", 1) || PLY::DOM::TokenMatch(buffer, "t", 1) || PLY::DOM::TokenMatch(buffer, "ty", 2) || PLY::DOM::TokenMatch(buffer, "texture_v", 9)) {
eOut = PLY::EST_VTextureCoord;
} else if (PLY::DOM::TokenMatch(buffer, "x", 1)) {
eOut = PLY::EST_XCoord;
} else if (PLY::DOM::TokenMatch(buffer, "y", 1)) {
eOut = PLY::EST_YCoord;
} else if (PLY::DOM::TokenMatch(buffer, "z", 1)) {
eOut = PLY::EST_ZCoord;
} else if (PLY::DOM::TokenMatch(buffer, "nx", 2)) {
eOut = PLY::EST_XNormal;
} else if (PLY::DOM::TokenMatch(buffer, "ny", 2)) {
eOut = PLY::EST_YNormal;
} else if (PLY::DOM::TokenMatch(buffer, "nz", 2)) {
eOut = PLY::EST_ZNormal;
else if (DOM::TokenMatch(buffer, "u", 1) || PLY::DOM::TokenMatch(buffer, "s", 1) || PLY::DOM::TokenMatch(buffer, "tx", 2) || PLY::DOM::TokenMatch(buffer, "texture_u", 9)) {
eOut = EST_UTextureCoord;
} else if (DOM::TokenMatch(buffer, "v", 1) || PLY::DOM::TokenMatch(buffer, "t", 1) || PLY::DOM::TokenMatch(buffer, "ty", 2) || PLY::DOM::TokenMatch(buffer, "texture_v", 9)) {
eOut = EST_VTextureCoord;
} else if (DOM::TokenMatch(buffer, "x", 1)) {
eOut = EST_XCoord;
} else if (DOM::TokenMatch(buffer, "y", 1)) {
eOut = EST_YCoord;
} else if (DOM::TokenMatch(buffer, "z", 1)) {
eOut = EST_ZCoord;
} else if (DOM::TokenMatch(buffer, "nx", 2)) {
eOut = EST_XNormal;
} else if (DOM::TokenMatch(buffer, "ny", 2)) {
eOut = EST_YNormal;
} else if (DOM::TokenMatch(buffer, "nz", 2)) {
eOut = EST_ZNormal;
} else {
ASSIMP_LOG_INFO("Found unknown property semantic in file. This is ok");
PLY::DOM::SkipLine(buffer);
DOM::SkipLine(buffer);
}
return eOut;
}
@@ -194,12 +194,12 @@ bool PLY::Property::ParseProperty(std::vector<char> &buffer, PLY::Property *pOut
// "property list uchar int vertex_index"
// skip leading spaces
if (!PLY::DOM::SkipSpaces(buffer)) {
if (!DOM::SkipSpaces(buffer)) {
return false;
}
// skip the "property" string at the beginning
if (!PLY::DOM::TokenMatch(buffer, "property", 8)) {
if (!DOM::TokenMatch(buffer, "property", 8)) {
// seems not to be a valid property entry
return false;
}
@@ -343,6 +343,7 @@ bool PLY::Element::ParseElement(IOStreamBuffer<char> &streamBuffer, std::vector<
return true;
}
// ------------------------------------------------------------------------------------------------
bool PLY::DOM::SkipSpaces(std::vector<char> &buffer) {
const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
const char *end = pCur + buffer.size();
@@ -359,6 +360,7 @@ bool PLY::DOM::SkipSpaces(std::vector<char> &buffer) {
return ret;
}
// ------------------------------------------------------------------------------------------------
bool PLY::DOM::SkipLine(std::vector<char> &buffer) {
const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
const char *end = pCur + buffer.size();
@@ -375,6 +377,7 @@ bool PLY::DOM::SkipLine(std::vector<char> &buffer) {
return ret;
}
// ------------------------------------------------------------------------------------------------
bool PLY::DOM::TokenMatch(std::vector<char> &buffer, const char *token, unsigned int len) {
const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
bool ret = false;
@@ -390,6 +393,7 @@ bool PLY::DOM::TokenMatch(std::vector<char> &buffer, const char *token, unsigned
return ret;
}
// ------------------------------------------------------------------------------------------------
bool PLY::DOM::SkipSpacesAndLineEnd(std::vector<char> &buffer) {
const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
const char *end = pCur + buffer.size();
@@ -406,6 +410,7 @@ bool PLY::DOM::SkipSpacesAndLineEnd(std::vector<char> &buffer) {
return ret;
}
// ------------------------------------------------------------------------------------------------
bool PLY::DOM::SkipComments(std::vector<char> buffer) {
ai_assert(!buffer.empty());
@@ -835,7 +840,7 @@ bool PLY::PropertyInstance::ParseValue(const char *&pCur,
case EDT_Double:
double d;
pCur = fast_atoreal_move(pCur, d);
out->fDouble = (double)d;
out->fDouble = d;
break;
case EDT_INVALID:
@@ -848,12 +853,12 @@ bool PLY::PropertyInstance::ParseValue(const char *&pCur,
}
// ------------------------------------------------------------------------------------------------
bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer<char> &streamBuffer,
bool PropertyInstance::ParseValueBinary(IOStreamBuffer<char> &streamBuffer,
std::vector<char> &buffer,
const char *&pCur,
unsigned int &bufferSize,
PLY::EDataType eType,
PLY::PropertyInstance::ValueUnion *out,
EDataType eType,
PropertyInstance::ValueUnion *out,
bool p_bBE) {
ai_assert(nullptr != out);
@@ -991,6 +996,8 @@ bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer<char> &streamBuffer,
return ret;
}
// ------------------------------------------------------------------------------------------------
} // namespace Assimp
#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER

View File

@@ -291,16 +291,13 @@ public:
// ---------------------------------------------------------------------------------
/** \brief Instance of a property in a PLY file
*/
class PropertyInstance
{
class PropertyInstance {
public:
//! Default constructor
PropertyInstance() AI_NO_EXCEPT = default;
union ValueUnion
{
//! uInt32 representation of the property. All
// uint types are automatically converted to uint32
uint32_t iUInt;
@@ -351,9 +348,11 @@ public:
static TYPE ConvertTo(ValueUnion v, EDataType eType);
};
using PropertyInstVec = std::vector<PropertyInstance::ValueUnion>;
// ---------------------------------------------------------------------------------
/// @brief Class for an element instance in a PLY file
// ---------------------------------------------------------------------------------
/** \brief Class for an element instance in a PLY file
*/
class ElementInstance {
public:
//! Default constructor
@@ -374,20 +373,18 @@ public:
};
// ---------------------------------------------------------------------------------
/** \brief Class for an element instance list in a PLY file
*/
class ElementInstanceList
{
/// @brief Class for an element instance list in a PLY file
// ---------------------------------------------------------------------------------
class ElementInstanceList {
public:
//! Default constructor
/// Default constructor
ElementInstanceList() AI_NO_EXCEPT = default;
//! List of all element instances
/// List of all element instances
std::vector< ElementInstance > alInstances;
// -------------------------------------------------------------------
//! Parse an element instance list
/// Parse an element instance list
static bool ParseInstanceList(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer,
const Element* pcElement, ElementInstanceList* p_pcOut, PLYImporter* loader);
@@ -400,17 +397,15 @@ public:
/** \brief Class to represent the document object model of an ASCII or binary
* (both little and big-endian) PLY file
*/
class DOM
{
class DOM {
public:
//! Default constructor
/// Default constructor
DOM() AI_NO_EXCEPT = default;
//! Contains all elements of the file format
/// Contains all elements of the file format
std::vector<Element> alElements;
//! Contains the real data of each element's instance list
/// Contains the real data of each element's instance list
std::vector<ElementInstanceList> alElementData;
//! Parse the DOM for a PLY file. The input string is assumed
@@ -446,11 +441,8 @@ private:
// ---------------------------------------------------------------------------------
template <typename TYPE>
inline TYPE PLY::PropertyInstance::ConvertTo(
PLY::PropertyInstance::ValueUnion v, PLY::EDataType eType)
{
switch (eType)
{
inline TYPE PLY::PropertyInstance::ConvertTo(PLY::PropertyInstance::ValueUnion v, PLY::EDataType eType) {
switch (eType) {
case EDT_Float:
return (TYPE)v.fFloat;
case EDT_Double:

View File

@@ -511,14 +511,6 @@ void USDImporterImplTinyusdz::uvsForMesh(
}
}
static aiColor3D *ownedColorPtrFor(const std::array<float, 3> &color) {
aiColor3D *colorPtr = new aiColor3D();
colorPtr->r = color[0];
colorPtr->g = color[1];
colorPtr->b = color[2];
return colorPtr;
}
static std::string nameForTextureWithId(const RenderScene &render_scene, const int targetId) {
std::stringstream ss;
std::string texName;
@@ -544,14 +536,14 @@ static void assignTexture(
const int textureId,
const int aiTextureType) {
std::string name = nameForTextureWithId(render_scene, textureId);
aiString *texName = new aiString();
texName->Set(name);
aiString texName;
texName.Set(name);
std::stringstream ss;
ss.str("");
ss << "assignTexture(): name: " << name;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
// TODO: verify hard-coded '0' index is correct
mat->AddProperty(texName, _AI_MATKEY_TEXTURE_BASE, aiTextureType, 0);
mat->AddProperty(&texName, _AI_MATKEY_TEXTURE_BASE, aiTextureType, 0);
}
void USDImporterImplTinyusdz::materials(
@@ -575,18 +567,18 @@ void USDImporterImplTinyusdz::materials(
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
aiMaterial *mat = new aiMaterial;
aiString *materialName = new aiString();
materialName->Set(material.name);
aiString materialName;
materialName.Set(material.name);
mat->AddProperty(materialName, AI_MATKEY_NAME);
mat->AddProperty(
ownedColorPtrFor(material.surfaceShader.diffuseColor.value),
reinterpret_cast<const aiColor3D *>(material.surfaceShader.diffuseColor.value.data()),
1, AI_MATKEY_COLOR_DIFFUSE);
mat->AddProperty(
ownedColorPtrFor(material.surfaceShader.specularColor.value),
reinterpret_cast<const aiColor3D *>(material.surfaceShader.specularColor.value.data()),
1, AI_MATKEY_COLOR_SPECULAR);
mat->AddProperty(
ownedColorPtrFor(material.surfaceShader.emissiveColor.value),
reinterpret_cast<const aiColor3D *>(material.surfaceShader.emissiveColor.value.data()),
1, AI_MATKEY_COLOR_EMISSIVE);
ss.str("");

View File

@@ -888,7 +888,9 @@ inline void Accessor::Read(Value &obj, Asset &r) {
if (bufferView) {
// Check length
unsigned long long byteLength = (unsigned long long)GetBytesPerComponent() * (unsigned long long)count;
unsigned long long byteLength = count > 0
? (unsigned long long)GetStride() * (unsigned long long)(count - 1) + (unsigned long long)GetElementSize()
: 0;
// handle integer overflow
if (byteLength < count) {
@@ -1004,7 +1006,15 @@ inline size_t Accessor::GetMaxByteSize() {
if (decodedBuffer)
return decodedBuffer->byteLength;
return (bufferView ? bufferView->byteLength : sparse->data.size());
if (sparse) {
return sparse->data.size();
}
if (bufferView) {
return byteOffset <= bufferView->byteLength ? bufferView->byteLength - byteOffset : 0;
}
return 0;
}
template <class T>
@@ -1028,21 +1038,27 @@ size_t Accessor::ExtractData(T *&outData, const std::vector<unsigned int> *remap
const size_t maxSize = GetMaxByteSize();
if (elemSize > maxSize) {
throw DeadlyImportError("GLTF: elemSize ", elemSize, " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
}
const size_t maxCount = (maxSize - elemSize) / stride + 1;
if (count > maxCount) {
throw DeadlyImportError("GLTF: count ", count, " > maxCount ", maxCount, " in ", getContextForErrorMessages(id, name));
}
outData = new T[usedCount];
if (remappingIndices != nullptr) {
const unsigned int maxIndexCount = static_cast<unsigned int>(maxSize / stride);
for (size_t i = 0; i < usedCount; ++i) {
size_t srcIdx = (*remappingIndices)[i];
if (srcIdx >= maxIndexCount) {
throw DeadlyImportError("GLTF: index*stride ", (srcIdx * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
if (srcIdx >= count) {
throw DeadlyImportError("GLTF: index ", srcIdx, " >= count ", count, " in ", getContextForErrorMessages(id, name));
}
memcpy(outData + i, data + srcIdx * stride, elemSize);
}
} else { // non-indexed cases
if (usedCount * stride > maxSize) {
throw DeadlyImportError("GLTF: count*stride ", (usedCount * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
}
if (stride == elemSize && targetElemSize == elemSize) {
memcpy(outData, data, totalSize);
} else {

View File

@@ -190,7 +190,7 @@ void SetAccessorRange(Ref<Accessor> acc, void *data, size_t count,
// Allocate and initialize with large values.
for (unsigned int i = 0; i < numCompsOut; i++) {
acc->min.push_back(std::numeric_limits<double>::min());
acc->min.push_back(std::numeric_limits<double>::max());
acc->max.push_back(-std::numeric_limits<double>::max());
}
@@ -556,12 +556,6 @@ void glTF2Exporter::GetMatTexProp(const aiMaterial &mat, unsigned int &prop, con
mat.Get(textureKey.c_str(), tt, slot, prop);
}
void glTF2Exporter::GetMatTexProp(const aiMaterial &mat, float &prop, const char *propName, aiTextureType tt, unsigned int slot) {
std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName;
mat.Get(textureKey.c_str(), tt, slot, prop);
}
void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot = 0) {
if (mat.GetTextureCount(tt) == 0) {
return;
@@ -652,7 +646,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, NormalTextureInfo &prop, ai
if (texture) {
// GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
GetMatTexProp(mat, prop.scale, "scale", tt, slot);
mat.Get(AI_MATKEY_GLTF_TEXTURE_SCALE(tt, slot), prop.scale);
}
}
@@ -663,7 +657,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, OcclusionTextureInfo &prop,
if (texture) {
// GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
GetMatTexProp(mat, prop.strength, "strength", tt, slot);
mat.Get(AI_MATKEY_GLTF_TEXTURE_STRENGTH(tt, slot), prop.strength);
}
}

View File

@@ -110,7 +110,6 @@ protected:
void WriteBinaryData(IOStream *outfile, std::size_t sceneLength);
void GetTexSampler(const aiMaterial &mat, glTFCommon::Ref<glTF2::Texture> texture, aiTextureType tt, unsigned int slot);
void GetMatTexProp(const aiMaterial &mat, unsigned int &prop, const char *propName, aiTextureType tt, unsigned int idx);
void GetMatTexProp(const aiMaterial &mat, float &prop, const char *propName, aiTextureType tt, unsigned int idx);
void GetMatTex(const aiMaterial &mat, glTFCommon::Ref<glTF2::Texture> &texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot);
void GetMatTex(const aiMaterial &mat, glTF2::TextureInfo &prop, aiTextureType tt, unsigned int slot);
void GetMatTex(const aiMaterial &mat, glTF2::NormalTextureInfo &prop, aiTextureType tt, unsigned int slot);

View File

@@ -237,8 +237,7 @@ static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
SetMaterialTextureProperty(embeddedTexIdxs, r, static_cast<TextureInfo>(prop), mat, texType, texSlot);
if (prop.texture && prop.texture->source) {
std::string textureStrengthKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + "strength";
mat->AddProperty(&prop.strength, 1, textureStrengthKey.c_str(), texType, texSlot);
mat->AddProperty(&prop.strength, 1, AI_MATKEY_GLTF_TEXTURE_STRENGTH(texType, texSlot));
}
}
@@ -462,9 +461,9 @@ template <typename T>
aiColor4D *GetVertexColorsForType(Ref<Accessor> input, std::vector<unsigned int> *vertexRemappingTable) {
constexpr float max = std::numeric_limits<T>::max();
aiColor4t<T> *colors;
input->ExtractData(colors, vertexRemappingTable);
auto output = new aiColor4D[input->count];
for (size_t i = 0; i < input->count; i++) {
size_t count = input->ExtractData(colors, vertexRemappingTable);
auto output = new aiColor4D[count];
for (size_t i = 0; i < count; i++) {
output[i] = aiColor4D(
colors[i].r / max, colors[i].g / max,
colors[i].b / max, colors[i].a / max);
@@ -766,9 +765,12 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
count = nFaces * 3;
}
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]);
// copycd:: than > 0
if (nFaces > 0) {
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]);
}
}
break;
}
@@ -1316,6 +1318,15 @@ struct AnimationSamplers {
struct vec4f {
float x, y, z, w;
};
static aiAnimInterpolation MapInterpolation(Interpolation interp) {
switch (interp) {
case Interpolation_STEP: return aiAnimInterpolation_Step;
case Interpolation_CUBICSPLINE: return aiAnimInterpolation_Cubic_Spline;
default: return aiAnimInterpolation_Linear;
}
}
aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &samplers) {
aiNodeAnim *anim = new aiNodeAnim();
@@ -1327,7 +1338,6 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &sample
if (samplers.translation && samplers.translation->input && samplers.translation->output) {
float *times = nullptr;
samplers.translation->input->ExtractData(times);
//aiVector3D *values = nullptr;
vec4f *tmp_values = nullptr;
size_t numItems = samplers.translation->output->ExtractData(tmp_values);
aiVector3D *values = new aiVector3D[numItems];
@@ -1338,13 +1348,31 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &sample
}
delete[] tmp_values;
anim->mNumPositionKeys = static_cast<unsigned int>(samplers.translation->input->count);
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
unsigned int ii = (samplers.translation->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
anim->mPositionKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mPositionKeys[i].mValue = values[ii];
ii += (samplers.translation->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
const bool isCubic = (samplers.translation->interpolation == Interpolation_CUBICSPLINE);
const aiAnimInterpolation interpType = MapInterpolation(samplers.translation->interpolation);
const unsigned int numLogicalKeys = static_cast<unsigned int>(samplers.translation->input->count);
if (isCubic) {
// Store as triplets: in-tangent, value, out-tangent per logical key
anim->mNumPositionKeys = numLogicalKeys * 3;
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
for (unsigned int i = 0; i < numLogicalKeys; ++i) {
unsigned int srcBase = i * 3;
unsigned int dstBase = i * 3;
for (unsigned int t = 0; t < 3; ++t) {
anim->mPositionKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
anim->mPositionKeys[dstBase + t].mValue = values[srcBase + t];
anim->mPositionKeys[dstBase + t].mInterpolation = interpType;
}
}
} else {
anim->mNumPositionKeys = numLogicalKeys;
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
anim->mPositionKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mPositionKeys[i].mValue = values[i];
anim->mPositionKeys[i].mInterpolation = interpType;
}
}
delete[] times;
delete[] values;
@@ -1362,16 +1390,36 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &sample
samplers.rotation->input->ExtractData(times);
aiQuaternion *values = nullptr;
samplers.rotation->output->ExtractData(values);
anim->mNumRotationKeys = static_cast<unsigned int>(samplers.rotation->input->count);
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
unsigned int ii = (samplers.rotation->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
anim->mRotationKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mRotationKeys[i].mValue.x = values[ii].w;
anim->mRotationKeys[i].mValue.y = values[ii].x;
anim->mRotationKeys[i].mValue.z = values[ii].y;
anim->mRotationKeys[i].mValue.w = values[ii].z;
ii += (samplers.rotation->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
const bool isCubic = (samplers.rotation->interpolation == Interpolation_CUBICSPLINE);
const aiAnimInterpolation interpType = MapInterpolation(samplers.rotation->interpolation);
const unsigned int numLogicalKeys = static_cast<unsigned int>(samplers.rotation->input->count);
if (isCubic) {
anim->mNumRotationKeys = numLogicalKeys * 3;
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
for (unsigned int i = 0; i < numLogicalKeys; ++i) {
unsigned int srcBase = i * 3;
unsigned int dstBase = i * 3;
for (unsigned int t = 0; t < 3; ++t) {
anim->mRotationKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
anim->mRotationKeys[dstBase + t].mValue.x = values[srcBase + t].w;
anim->mRotationKeys[dstBase + t].mValue.y = values[srcBase + t].x;
anim->mRotationKeys[dstBase + t].mValue.z = values[srcBase + t].y;
anim->mRotationKeys[dstBase + t].mValue.w = values[srcBase + t].z;
anim->mRotationKeys[dstBase + t].mInterpolation = interpType;
}
}
} else {
anim->mNumRotationKeys = numLogicalKeys;
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
anim->mRotationKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mRotationKeys[i].mValue.x = values[i].w;
anim->mRotationKeys[i].mValue.y = values[i].x;
anim->mRotationKeys[i].mValue.z = values[i].y;
anim->mRotationKeys[i].mValue.w = values[i].z;
anim->mRotationKeys[i].mInterpolation = interpType;
}
}
delete[] times;
delete[] values;
@@ -1390,13 +1438,30 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &sample
samplers.scale->input->ExtractData(times);
aiVector3D *values = nullptr;
samplers.scale->output->ExtractData(values);
anim->mNumScalingKeys = static_cast<unsigned int>(samplers.scale->input->count);
anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
unsigned int ii = (samplers.scale->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
for (unsigned int i = 0; i < anim->mNumScalingKeys; ++i) {
anim->mScalingKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mScalingKeys[i].mValue = values[ii];
ii += (samplers.scale->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
const bool isCubic = (samplers.scale->interpolation == Interpolation_CUBICSPLINE);
const aiAnimInterpolation interpType = MapInterpolation(samplers.scale->interpolation);
const unsigned int numLogicalKeys = static_cast<unsigned int>(samplers.scale->input->count);
if (isCubic) {
anim->mNumScalingKeys = numLogicalKeys * 3;
anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
for (unsigned int i = 0; i < numLogicalKeys; ++i) {
unsigned int srcBase = i * 3;
unsigned int dstBase = i * 3;
for (unsigned int t = 0; t < 3; ++t) {
anim->mScalingKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
anim->mScalingKeys[dstBase + t].mValue = values[srcBase + t];
anim->mScalingKeys[dstBase + t].mInterpolation = interpType;
}
}
} else {
anim->mNumScalingKeys = numLogicalKeys;
anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
for (unsigned int i = 0; i < anim->mNumScalingKeys; ++i) {
anim->mScalingKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mScalingKeys[i].mValue = values[i];
anim->mScalingKeys[i].mInterpolation = interpType;
}
}
delete[] times;
delete[] values;

View File

@@ -80,11 +80,11 @@ aiReturn aiGetMaterialProperty(const aiMaterial *pMat,
}
}
*pPropOut = nullptr;
return AI_FAILURE;
}
namespace
{
namespace {
// ------------------------------------------------------------------------------------------------
// Implementation of functions "aiGetMaterialFloatArray" and "aiGetMaterialFloatFloatArray".
@@ -98,8 +98,8 @@ aiReturn GetMaterialFloatArray(const aiMaterial *pMat,
ai_assert(pOut != nullptr);
ai_assert(pMat != nullptr);
const aiMaterialProperty *prop;
aiGetMaterialProperty(pMat, pKey, type, index, (const aiMaterialProperty **)&prop);
const aiMaterialProperty *prop{nullptr};
aiGetMaterialProperty(pMat, pKey, type, index, &prop);
if (nullptr == prop) {
return AI_FAILURE;
}
@@ -110,7 +110,6 @@ aiReturn GetMaterialFloatArray(const aiMaterial *pMat,
iWrite = prop->mDataLength / sizeof(float);
if (pMax) {
iWrite = std::min(*pMax, iWrite);
;
}
for (unsigned int a = 0; a < iWrite; ++a) {
@@ -120,9 +119,7 @@ aiReturn GetMaterialFloatArray(const aiMaterial *pMat,
if (pMax) {
*pMax = iWrite;
}
}
// data is given in doubles, convert to TReal
else if (aiPTI_Double == prop->mType) {
} else if (aiPTI_Double == prop->mType) { // data is given in doubles, convert to TReal
iWrite = prop->mDataLength / sizeof(double);
if (pMax) {
iWrite = std::min(*pMax, iWrite);
@@ -134,9 +131,7 @@ aiReturn GetMaterialFloatArray(const aiMaterial *pMat,
if (pMax) {
*pMax = iWrite;
}
}
// data is given in ints, convert to TReal
else if (aiPTI_Integer == prop->mType) {
} else if (aiPTI_Integer == prop->mType) { // data is given in ints, convert to TReal
iWrite = prop->mDataLength / sizeof(int32_t);
if (pMax) {
iWrite = std::min(*pMax, iWrite);
@@ -148,9 +143,7 @@ aiReturn GetMaterialFloatArray(const aiMaterial *pMat,
if (pMax) {
*pMax = iWrite;
}
}
// a string ... read floats separated by spaces
else {
} else { // a string ... read floats separated by spaces
if (pMax) {
iWrite = *pMax;
}
@@ -174,6 +167,7 @@ aiReturn GetMaterialFloatArray(const aiMaterial *pMat,
*pMax = iWrite;
}
}
return AI_SUCCESS;
}
@@ -185,7 +179,7 @@ aiReturn aiGetMaterialFloatFloatArray(const aiMaterial *pMat,
unsigned int index,
float *pOut,
unsigned int *pMax) {
return ::GetMaterialFloatArray(pMat, pKey, type, index, pOut, pMax);
return GetMaterialFloatArray(pMat, pKey, type, index, pOut, pMax);
}
} // namespace
@@ -198,7 +192,7 @@ aiReturn aiGetMaterialFloatArray(const aiMaterial *pMat,
unsigned int index,
ai_real *pOut,
unsigned int *pMax) {
return ::GetMaterialFloatArray(pMat, pKey, type, index, pOut, pMax);
return GetMaterialFloatArray(pMat, pKey, type, index, pOut, pMax);
}
// ------------------------------------------------------------------------------------------------
@@ -213,7 +207,7 @@ aiReturn aiGetMaterialIntegerArray(const aiMaterial *pMat,
ai_assert(pMat != nullptr);
const aiMaterialProperty *prop;
aiGetMaterialProperty(pMat, pKey, type, index, (const aiMaterialProperty **)&prop);
aiGetMaterialProperty(pMat, pKey, type, index, &prop);
if (!prop) {
return AI_FAILURE;
}
@@ -305,7 +299,7 @@ aiReturn aiGetMaterialUVTransform(const aiMaterial *pMat,
unsigned int index,
aiUVTransform *pOut) {
unsigned int iMax = 5;
return aiGetMaterialFloatArray(pMat, pKey, type, index, (ai_real *)pOut, &iMax);
return aiGetMaterialFloatArray(pMat, pKey, type, index, reinterpret_cast<ai_real *>(pOut), &iMax);
}
// ------------------------------------------------------------------------------------------------
@@ -317,8 +311,8 @@ aiReturn aiGetMaterialString(const aiMaterial *pMat,
aiString *pOut) {
ai_assert(pOut != nullptr);
const aiMaterialProperty *prop;
aiGetMaterialProperty(pMat, pKey, type, index, (const aiMaterialProperty **)&prop);
const aiMaterialProperty *prop{nullptr};
aiGetMaterialProperty(pMat, pKey, type, index, &prop);
if (!prop) {
return AI_FAILURE;
}
@@ -386,7 +380,7 @@ aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial *mat,
}
// Determine mapping type
int mapping_ = static_cast<int>(aiTextureMapping_UV);
int mapping_ = aiTextureMapping_UV;
aiGetMaterialInteger(mat, AI_MATKEY_MAPPING(type, index), &mapping_);
aiTextureMapping mapping = static_cast<aiTextureMapping>(mapping_);
if (_mapping)
@@ -417,12 +411,11 @@ aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial *mat,
return AI_SUCCESS;
}
static const unsigned int DefaultNumAllocated = 5;
static constexpr unsigned int DefaultNumAllocated = 5;
// ------------------------------------------------------------------------------------------------
// Construction. Actually the one and only way to get an aiMaterial instance
aiMaterial::aiMaterial() :
mProperties(nullptr), mNumProperties(0), mNumAllocated(DefaultNumAllocated) {
aiMaterial::aiMaterial() : mProperties(nullptr), mNumProperties(0), mNumAllocated(DefaultNumAllocated) {
// Allocate 5 entries by default
mProperties = new aiMaterialProperty *[DefaultNumAllocated];
}
@@ -436,7 +429,7 @@ aiMaterial::~aiMaterial() {
// ------------------------------------------------------------------------------------------------
aiString aiMaterial::GetName() const {
aiString name;
aiString name{};
Get(AI_MATKEY_NAME, name);
return name;
@@ -507,7 +500,7 @@ aiReturn aiMaterial::AddBinaryProperty(const void *pInput,
}
// Allocate a new material property
std::unique_ptr<aiMaterialProperty> pcNew(new aiMaterialProperty());
auto pcNew = std::make_unique<aiMaterialProperty>();
// .. and fill it
pcNew->mType = pType;
@@ -569,11 +562,9 @@ aiReturn aiMaterial::AddProperty(const aiString *pInput,
uint32_t Assimp::ComputeMaterialHash(const aiMaterial *mat, bool includeMatName /*= false*/) {
uint32_t hash = 1503; // magic start value, chosen to be my birthday :-)
for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
aiMaterialProperty *prop;
// Exclude all properties whose first character is '?' from the hash
// See doc for aiMaterialProperty.
prop = mat->mProperties[i];
const aiMaterialProperty *prop = mat->mProperties[i];
if (nullptr != prop && (includeMatName || prop->mKey.data[0] != '?')) {
hash = SuperFastHash(prop->mKey.data, (unsigned int)prop->mKey.length, hash);
@@ -617,7 +608,7 @@ void aiMaterial::CopyPropertyList(aiMaterial *const pcDest,
}
for (unsigned int i = iOldNum; i < pcDest->mNumProperties; ++i) {
aiMaterialProperty *propSrc = pcSrc->mProperties[i];
const aiMaterialProperty *propSrc = pcSrc->mProperties[i];
// search whether we have already a property with this name -> if yes, overwrite it
aiMaterialProperty *prop;

View File

@@ -58,11 +58,55 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stack>
namespace Assimp {
namespace {
ai_real calculateInputACMR(aiMesh *pMesh, const aiFace *const pcEnd,
unsigned int configCacheDepth, unsigned int meshNum) {
ai_real fACMR = 0.0f;
unsigned int *piFIFOStack = new unsigned int[configCacheDepth];
memset(piFIFOStack, 0xff, configCacheDepth * sizeof(unsigned int));
unsigned int *piCur = piFIFOStack;
const unsigned int *const piCurEnd = piFIFOStack + configCacheDepth;
// count the number of cache misses
unsigned int iCacheMisses = 0;
for (const aiFace *pcFace = pMesh->mFaces; pcFace != pcEnd; ++pcFace) {
for (unsigned int qq = 0; qq < 3; ++qq) {
bool bInCache = false;
for (unsigned int *pp = piFIFOStack; pp < piCurEnd; ++pp) {
if (*pp == pcFace->mIndices[qq]) {
// the vertex is in cache
bInCache = true;
break;
}
}
if (!bInCache) {
++iCacheMisses;
if (piCurEnd == piCur) {
piCur = piFIFOStack;
}
*piCur++ = pcFace->mIndices[qq];
}
}
}
delete[] piFIFOStack;
fACMR = (ai_real)iCacheMisses / pMesh->mNumFaces;
if (3.0 == fACMR) {
char szBuff[128]; // should be sufficiently large in every case
// the JoinIdenticalVertices process has not been executed on this
// mesh, otherwise this value would normally be at least minimally
// smaller than 3.0 ...
ai_snprintf(szBuff, 128, "Mesh %u: Not suitable for vcache optimization", meshNum);
ASSIMP_LOG_WARN(szBuff);
return static_cast<ai_real>(0.f);
}
return fACMR;
}
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() :
mConfigCacheDepth(PP_ICL_PTCACHE_SIZE) {
ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() : mConfigCacheDepth(PP_ICL_PTCACHE_SIZE) {
// empty
}
@@ -107,51 +151,6 @@ void ImproveCacheLocalityProcess::Execute(aiScene *pScene) {
}
}
// ------------------------------------------------------------------------------------------------
static ai_real calculateInputACMR(aiMesh *pMesh, const aiFace *const pcEnd,
unsigned int configCacheDepth, unsigned int meshNum) {
ai_real fACMR = 0.0f;
unsigned int *piFIFOStack = new unsigned int[configCacheDepth];
memset(piFIFOStack, 0xff, configCacheDepth * sizeof(unsigned int));
unsigned int *piCur = piFIFOStack;
const unsigned int *const piCurEnd = piFIFOStack + configCacheDepth;
// count the number of cache misses
unsigned int iCacheMisses = 0;
for (const aiFace *pcFace = pMesh->mFaces; pcFace != pcEnd; ++pcFace) {
for (unsigned int qq = 0; qq < 3; ++qq) {
bool bInCache = false;
for (unsigned int *pp = piFIFOStack; pp < piCurEnd; ++pp) {
if (*pp == pcFace->mIndices[qq]) {
// the vertex is in cache
bInCache = true;
break;
}
}
if (!bInCache) {
++iCacheMisses;
if (piCurEnd == piCur) {
piCur = piFIFOStack;
}
*piCur++ = pcFace->mIndices[qq];
}
}
}
delete[] piFIFOStack;
fACMR = (ai_real)iCacheMisses / pMesh->mNumFaces;
if (3.0 == fACMR) {
char szBuff[128]; // should be sufficiently large in every case
// the JoinIdenticalVertices process has not been executed on this
// mesh, otherwise this value would normally be at least minimally
// smaller than 3.0 ...
ai_snprintf(szBuff, 128, "Mesh %u: Not suitable for vcache optimization", meshNum);
ASSIMP_LOG_WARN(szBuff);
return static_cast<ai_real>(0.f);
}
return fACMR;
}
// ------------------------------------------------------------------------------------------------
// Improves the cache coherency of a specific mesh
ai_real ImproveCacheLocalityProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshNum) {

View File

@@ -234,7 +234,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
++numOut;
} else {
numOut += face.mNumIndices-2;
max_out = std::max(max_out,face.mNumIndices);
max_out = std::max<uint32_t>(max_out,face.mNumIndices);
}
}

View File

@@ -796,10 +796,13 @@ char *OpenDDLParser::parseStringLiteral(char *in, char *end, Value **stringData)
if (*start == '\"') {
++start;
++in;
while (*in != '\"' && in != end) {
while (in != end && *in != '\"') {
++in;
++len;
}
if (in == end) {
return in;
}
*stringData = ValueAllocator::allocPrimData(Value::ValueType::ddl_string, len);
::strncpy((char *)(*stringData)->m_data, start, len);

View File

@@ -166,7 +166,7 @@ AI_FORCE_INLINE LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_emp
mSkip_empty_lines(skip_empty_lines),
mTrim(trim) {
mCur.reserve(1024);
mEnd = mCur.c_str() + 1024;
mEnd = mCur.c_str() + mCur.capacity();
operator++();
mIdx = 0;
}
@@ -183,6 +183,7 @@ AI_FORCE_INLINE LineSplitter& LineSplitter::operator++() {
char s;
mCur.clear();
mEnd = mCur.c_str() + mCur.capacity();
while (mStream.GetRemainingSize() && (s = mStream.GetI1(), 1)) {
if (s == '\n' || s == '\r') {
if (mSkip_empty_lines) {
@@ -205,6 +206,7 @@ AI_FORCE_INLINE LineSplitter& LineSplitter::operator++() {
break;
}
mCur += s;
mEnd = mCur.c_str() + mCur.capacity();
}
++mIdx;

View File

@@ -124,11 +124,11 @@ AI_FORCE_INLINE bool IsSpaceOrNewLine(char_t in) {
/// @return true if valid.
template <class char_t>
AI_FORCE_INLINE bool SkipSpaces(const char_t *in, const char_t **out, const char_t *end) {
while ((*in == (char_t)' ' || *in == (char_t)'\t') && in != end) {
while (in < end && (*in == (char_t)' ' || *in == (char_t)'\t')) {
++in;
}
*out = in;
return !IsLineEnd<char_t>(*in);
return ((in < end) && !IsLineEnd<char_t>(*in));
}
// ---------------------------------------------------------------------------------
@@ -154,11 +154,11 @@ AI_FORCE_INLINE bool SkipLine(const char_t *in, const char_t **out, const char_t
}
// files are opened in binary mode. Ergo there are both NL and CR
while ((*in == (char_t)'\r' || *in == (char_t)'\n') && in != end) {
while (in < end && (*in == (char_t)'\r' || *in == (char_t)'\n')) {
++in;
}
*out = in;
return *in != (char_t)'\0';
return in < end && *in != (char_t)'\0';
}
// ---------------------------------------------------------------------------------
@@ -178,11 +178,11 @@ AI_FORCE_INLINE bool SkipLine(const char_t **inout, const char_t *end) {
/// @return true if valid.
template <class char_t>
AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t *in, const char_t **out, const char_t *end) {
while ((*in == (char_t)' ' || *in == (char_t)'\t' || *in == (char_t)'\r' || *in == (char_t)'\n') && in != end) {
while (in < end && (*in == (char_t)' ' || *in == (char_t)'\t' || *in == (char_t)'\r' || *in == (char_t)'\n')) {
++in;
}
*out = in;
return *in != '\0';
return in < end && *in != '\0';
}
// ---------------------------------------------------------------------------------

View File

@@ -182,7 +182,7 @@ public:
// ---------------------------------------------------------------------
/// Get the remaining stream size (to the end of the stream)
size_t GetRemainingSize() const {
return (unsigned int)(mEnd - mCurrent);
return static_cast<size_t>(mEnd - mCurrent);
}
// ---------------------------------------------------------------------
@@ -190,16 +190,29 @@ public:
* return value is the remaining size of the stream if no custom
* read limit has been set. */
size_t GetRemainingSizeToLimit() const {
return (unsigned int)(mLimit - mCurrent);
return static_cast<size_t>(mLimit - mCurrent);
}
// ---------------------------------------------------------------------
/** Increase the file pointer (relative seeking) */
void IncPtr(intptr_t plus) {
mCurrent += plus;
if (mCurrent > mLimit) {
throw DeadlyImportError("End of file or read limit was reached");
// Ensure internal pointer invariants hold
if (mCurrent < mBuffer || mCurrent > mLimit) {
throw DeadlyImportError("StreamReader: Invalid internal pointer state");
}
if (plus < 0) {
const size_t absPlus = static_cast<size_t>(-(plus + 1)) + 1;
if (absPlus > static_cast<size_t>(mCurrent - mBuffer)) {
throw DeadlyImportError("StreamReader: Attempted to seek outside buffer bounds");
}
} else if (plus > 0) {
if (static_cast<size_t>(plus) > static_cast<size_t>(mLimit - mCurrent)) {
throw DeadlyImportError("StreamReader: Attempted to seek outside buffer bounds");
}
}
mCurrent += plus;
}
// ---------------------------------------------------------------------
@@ -233,8 +246,9 @@ public:
}
/// @brief Get the current offset from the beginning of the file
/// @return The current offset from the beginning of the file.
int GetCurrentPos() const {
return (unsigned int)(mCurrent - mBuffer);
return static_cast<int>(mCurrent - mBuffer);
}
void SetCurrentPos(size_t pos) {
@@ -244,10 +258,10 @@ public:
// ---------------------------------------------------------------------
/** Setup a temporary read limit
*
* @param limit Maximum number of bytes to be read from
* @param _limit Maximum number of bytes to be read from
* the beginning of the file. Specifying UINT_MAX
* resets the limit to the original end of the stream.
* Returns the previously set limit. */
* @return The previously set limit. */
unsigned int SetReadLimit(unsigned int _limit) {
unsigned int prev = GetReadLimit();
if (UINT_MAX == _limit) {
@@ -264,9 +278,10 @@ public:
// ---------------------------------------------------------------------
/** Get the current read limit in bytes. Reading over this limit
* accidentally raises an exception. */
* accidentally raises an exception.
* @return The current limit. */
unsigned int GetReadLimit() const {
return (unsigned int)(mLimit - mBuffer);
return static_cast<unsigned int>(mLimit - mBuffer);
}
// ---------------------------------------------------------------------

View File

@@ -299,7 +299,7 @@ typedef unsigned int ai_uint;
/* Numerical limits */
#ifdef __cplusplus
constexpr ai_real ai_epsilon = (ai_real) 1e-6;
inline constexpr ai_real ai_epsilon = (ai_real) 1e-6;
#else
# define ai_epsilon ((ai_real)1e-6)
#endif

View File

@@ -1,6 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "assimp_rs"
version = "0.1.0"

View File

@@ -6,4 +6,5 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[build-dependencies]
bindgen = "*"

40
port/assimp_rs/build.rs Normal file
View File

@@ -0,0 +1,40 @@
extern crate bindgen;
use std::env;
use std::path::PathBuf;
fn main() {
// Rerun if wrapper header changes
println!("cargo:rerun-if-changed=./src/assimp_wrapper.h");
// Tell cargo to look for shared libraries in the specified directory
println!("cargo:rustc-link-search=../../bin/");
// Tell cargo to tell rustc to link the assimp shared library.
println!("cargo:rustc-link-lib=assimp");
// The bindgen::Builder is the main entry point
// to bindgen, and lets you build up options for
// the resulting bindings.
let bindings = bindgen::Builder::default()
// The input header we would like to generate
// bindings for.
.header("./src/assimp_wrapper.h").clang_arg("-I../../include/")
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.blocklist_item("FP_ZERO")
.blocklist_item("FP_SUBNORMAL")
.blocklist_item("FP_NORMAL")
.blocklist_item("FP_INFINITE")
.blocklist_item("FP_NAN")
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");
// Write the bindings to the $OUT_DIR/bindings.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}

View File

@@ -0,0 +1,48 @@
#ifndef ASSIMP_WRAPPER_H_INC
#define ASSIMP_WRAPPER_H_INC
#include <assimp/defs.h>
#include <assimp/camera.h>
#include <assimp/scene.h>
#include <assimp/types.h>
#include <assimp/vector3.h>
#include <assimp/matrix4x4.h>
#include <assimp/quaternion.h>
#include <assimp/color4.h>
#include <assimp/mesh.h>
#include <assimp/anim.h>
#include <assimp/metadata.h>
#include <assimp/light.h>
#include <assimp/material.h>
#include <assimp/texture.h>
#include <assimp/postprocess.h>
#include <assimp/version.h>
#include <assimp/importerdesc.h>
#include <assimp/aabb.h>
#include <assimp/anim.h>
#include <assimp/camera.h>
#include <assimp/cexport.h>
#include <assimp/cfileio.h>
#include <assimp/cimport.h>
#include <assimp/color4.h>
#include <assimp/commonMetaData.h>
#include <assimp/defs.h>
#include <assimp/importerdesc.h>
#include <assimp/light.h>
#include <assimp/material.h>
#include <assimp/matrix3x3.h>
#include <assimp/matrix4x4.h>
#include <assimp/mesh.h>
#include <assimp/metadata.h>
#include <assimp/pbrmaterial.h>
#include <assimp/postprocess.h>
#include <assimp/quaternion.h>
#include <assimp/revision.h>
#include <assimp/scene.h>
#include <assimp/texture.h>
#include <assimp/types.h>
#include <assimp/vector2.h>
#include <assimp/vector3.h>
#include <assimp/version.h>
#endif // ASSIMP_WRAPPER_H_INC

View File

@@ -1 +0,0 @@
pub use self::structs::{Camera};

View File

@@ -1,17 +1,21 @@
pub mod camera;
pub mod core;
pub mod errors;
pub mod formats;
pub mod material;
pub mod postprocess;
pub mod shims;
pub mod socket;
pub mod structs;
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(true, true);
fn import_test() {
unsafe {
use crate::aiImportFile;
let mut file: *mut dyn const i8 = std::ptr::null_mut();
//let file = String::from("test.obj");
//let (ptr, len, cap) = file.into_raw_parts();
//let raw_file = unsafe{String::from_raw_parts}
let asset = aiImportFile(file, 0);
}
}
}

View File

@@ -1,44 +0,0 @@
pub struct Animation<'mA, 'mMA, 'nA> {
/* The name of the animation. If the modeling package this data was
* exported from does support only a single animation channel, this
* name is usually empty (length is zero).
*/
m_name: Option<String>,
// Duration of the animation in ticks
m_duration: f64,
// Ticks per second. Zero (0.000... ticks/second) if not
// specified in the imported file
m_ticks_per_second: Option<f64>,
/* Number of bone animation channels.
Each channel affects a single node.
*/
m_num_channels: u64,
/* Node animation channels. Each channel
affects a single node.
?? -> The array is m_num_channels in size.
(maybe refine to a derivative type of usize?)
*/
m_channels: &'nA NodeAnim,
/* Number of mesh animation channels. Each
channel affects a single mesh and defines
vertex-based animation.
*/
m_num_mesh_channels: u64,
/* The mesh animation channels. Each channel
affects a single mesh.
The array is m_num_mesh_channels in size
(maybe refine to a derivative of usize?)
*/
m_mesh_channels: &'mA MeshAnim,
/* The number of mesh animation channels. Each channel
affects a single mesh and defines some morphing animation.
*/
m_num_morph_mesh_channels: u64,
/* The morph mesh animation channels. Each channel affects a single mesh.
The array is mNumMorphMeshChannels in size.
*/
m_morph_mesh_channels: &'mMA MeshMorphAnim
}
pub struct NodeAnim {}
pub struct MeshAnim {}
pub struct MeshMorphAnim {}

View File

@@ -1,6 +0,0 @@
mod anim;
pub use self::anim::{
Animation,
NodeAnim,
MeshAnim,
MeshMorphAnim};

View File

@@ -1,2 +0,0 @@
mod blob;

View File

@@ -1,2 +0,0 @@
mod bone;

View File

@@ -1,2 +0,0 @@
mod camera;

View File

@@ -1,27 +0,0 @@
#[derive(Clone, Debug, Copy)]
struct Color3D {
r: f32,
g: f32,
b: f32
}
impl Color3D {
pub fn new(r_f32: f32, g_f32: f32, b_f32: f32) -> Color3D {
Color3D {r: r_f32, g: g_f32, b: b_f32 }
}
}
#[derive(Clone, Debug, Copy)]
struct Color4D {
r: f32,
g: f32,
b: f32,
a: f32
}
impl Color4D {
pub fn new(r_f32: f32, g_f32: f32, b_f32: f32, a_f32: f32) -> Color4D {
Color4D {r: r_f32, g: g_f32, b: b_f32, a: a_f32 }
}
}

View File

@@ -1,5 +0,0 @@
mod color;
pub use self::color::{
Color3D,
Color4D
};

View File

@@ -1,2 +0,0 @@
mod face;

View File

@@ -1,2 +0,0 @@
mod key;

View File

@@ -1,2 +0,0 @@
mod light;

View File

@@ -1,2 +0,0 @@
mod material;

View File

@@ -1,64 +0,0 @@
#[derive(Clone, Debug, Copy)]
struct Matrix3x3 {
a1: f32,
a2: f32,
a3: f32,
b1: f32,
b2: f32,
b3: f32,
c1: f32,
c2: f32,
c3: f32
}
#[derive(Clone, Debug, Copy)]
struct Matrix4x4 {
a1: f32,
a2: f32,
a3: f32,
a4: f32,
b1: f32,
b2: f32,
b3: f32,
b4: f32,
c1: f32,
c2: f32,
c3: f32,
c4: f32,
d1: f32,
d2: f32,
d3: f32,
d4: f32
}
impl Matrix3x3 {
pub fn new(
a1_f32: f32, a2_f32: f32, a3_f32: f32,
b1_f32: f32, b2_f32: f32, b3_f32: f32,
c1_f32: f32, c2_f32: f32, c3_f32: f32
) -> Matrix3x3 {
Matrix3x3 {
a1: a1_f32, a2: a2_f32, a3: a3_f32,
b1: b1_f32, b2: b2_f32, b3: b3_f32,
c1: c1_f32, c2: c2_f32, c3: c3_f32
}
}
}
impl Matrix4x4 {
pub fn new(
a1_f32: f32, a2_f32: f32, a3_f32: f32, a4_f32: f32,
b1_f32: f32, b2_f32: f32, b3_f32: f32, b4_f32: f32,
c1_f32: f32, c2_f32: f32, c3_f32: f32, c4_f32: f32,
d1_f32: f32, d2_f32: f32, d3_f32: f32, d4_f32: f32
) -> Matrix4x4 {
Matrix4x4 {
a1: a1_f32, a2: a2_f32, a3: a3_f32, a4: a4_f32,
b1: b1_f32, b2: b2_f32, b3: b3_f32, b4: b4_f32,
c1: c1_f32, c2: c2_f32, c3: c3_f32, c4: c4_f32,
d1: d1_f32, d2: d2_f32, d3: d3_f32, d4: d4_f32
}
}
}

View File

@@ -1,4 +0,0 @@
mod matrix;
pub use self::matrix::{
Matrix3x3,
Matrix4x4};

View File

@@ -1,35 +0,0 @@
#[derive(Clone, Debug, Copy)]
struct MemoryInfo {
textures: u32,
materials: u32,
meshes: u32,
nodes: u32,
animations: u32,
cameras: u32,
lights: u32,
total: u32
}
impl MemoryInfo {
pub fn new(
textures_uint: u32,
materials_uint: u32,
meshes_uint: u32,
nodes_uint: u32,
animations_uint: u32,
cameras_uint: u32,
lights_uint: u32,
total_uint: u32) -> MemoryInfo {
MemoryInfo {
textures: textures_uint,
materials: materials_uint,
meshes: meshes_uint,
nodes: nodes_uint,
animations: animations_uint,
cameras: cameras_uint,
lights: lights_uint,
total: total_uint
}
}
}

View File

@@ -1,2 +0,0 @@
mod memory;
pub use self::memory::MemoryInfo;

View File

@@ -1,3 +0,0 @@
mod mesh;

View File

@@ -1,2 +0,0 @@
mod meta;

View File

@@ -1,61 +0,0 @@
mod anim;
/* Animation
* NodeAnim
* MeshAnim
* MeshMorphAnim
*/
mod blob;
/* ExportDataBlob
*/
mod vec;
/* Vector2d
* Vector3d
* */
mod matrix;
/* Matrix3by3
* Matrix4by4
*/
mod camera;
/* Camera */
mod color;
/* Color3d
* Color4d
*/
mod key;
/* MeshKey
* MeshMorphKey
* QuatKey
* VectorKey
*/
mod texel;
mod plane;
mod string;
/* String
*/
mod material;
/* Material
* MaterialPropery
* MaterialPropertyString
*/
mod mem;
mod quaternion;
mod face;
mod vertex_weight;
mod mesh;
/* Mesh
*/
mod meta;
/* Metadata
* MetadataEntry
*/
mod node;
/* Node
* */
mod light;
mod texture;
mod ray;
mod transform;
/* UVTransform */
mod bone;
mod scene;
/* Scene */

View File

@@ -1,2 +0,0 @@
mod node;

View File

@@ -1,2 +0,0 @@
mod plane;

View File

@@ -1,23 +0,0 @@
#[derive(Clone, Debug, Copy)]
struct Plane {
a: f32,
b: f32,
c: f32,
d: f32
}
impl Plane {
pub fn new(
a_f32: f32,
b_f32: f32,
c_f32: f32,
d_f32: f32
) -> Plane {
Plane {
a: a_f32,
b: b_f32,
c: b_f32,
d: d_f32
}
}
}

View File

@@ -1,3 +0,0 @@
mod quaternion;
pub use self::quaternion::Quaternion;

View File

@@ -1,7 +0,0 @@
use crate::vec;
#[derive(Clone, Debug, Copy)]
pub struct Quaternion {
_coordinates: vec::Vector4d
}

View File

@@ -1,2 +0,0 @@
mod ray;

View File

@@ -1,2 +0,0 @@
mod scene;

View File

@@ -1,3 +0,0 @@
mod string;
pub use self::string::MAXLEN;
pub use self::string::Str;

View File

@@ -1,41 +0,0 @@
pub const MAXLEN: usize = 1024;
/// Want to consider replacing `Vec<char>`
/// with a comparable definition at
/// https://doc.rust-lang.org/src/alloc/string.rs.html#415-417
#[derive(Clone, Debug)]
struct Str {
length: usize,
data: Vec<char>
}
impl Str {
pub fn new(len_u32: usize, data_string: String) -> Str {
Str {
length: len_u32,
data: data_string.chars().collect()
}
}
}
/// MaterialPropertyStr
/// The size of length is truncated to 4 bytes on a 64-bit platform when used as a
/// material property (see MaterialSystem.cpp, as aiMaterial::AddProperty() ).
#[derive(Clone, Debug)]
struct MaterialPropertyStr {
length: usize,
data: Vec<char>
}
impl MaterialPropertyStr {
pub fn new(len_u32: usize, data_string: String) -> MaterialPropertyStr {
MaterialPropertyStr {
length: len_u32,
data: data_string.chars().collect()
}
}
}

View File

@@ -1,3 +0,0 @@
mod texture;
pub use self::texture::Texel;

View File

@@ -1,19 +0,0 @@
#[derive(Clone, Debug, Copy)]
struct Texel {
b: u32,
g: u32,
r: u32,
a: u32
}
impl Texel {
pub fn new(b_u32: u32, g_u32: u32,
r_u32: u32, a_u32: u32) -> Texel {
Texel {
b: b_u32,
g: g_u32,
r: r_u32,
a: a_u32
}
}
}

Some files were not shown because too many files have changed in this diff Show More