Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
392a658f9c | ||
|
|
11a5d1b8ef | ||
|
|
9e56e52252 | ||
|
|
86ae4876fb | ||
|
|
fb9a58735d | ||
|
|
25a1318534 | ||
|
|
ede83d7fe2 | ||
|
|
17c12da558 | ||
|
|
de77101866 | ||
|
|
158da57515 | ||
|
|
021bbff3e5 | ||
|
|
9c10e7d2b5 | ||
|
|
048becec92 | ||
|
|
8ff005570a | ||
|
|
fcb1702162 | ||
|
|
b5f567bd3a | ||
|
|
1569f6a9e0 | ||
|
|
29df28d7d3 | ||
|
|
b10b8ab99a | ||
|
|
b2852bc49b | ||
|
|
e70199446d | ||
|
|
dc54c9fdea | ||
|
|
38f3e8d98b | ||
|
|
8a073f7cb4 | ||
|
|
553fbc1fdb | ||
|
|
e13e0b5b7d | ||
|
|
929bff1830 | ||
|
|
8ef1461cb8 | ||
|
|
10be273aea | ||
|
|
3e672ff856 | ||
|
|
3e188e6768 | ||
|
|
21607dfb75 | ||
|
|
970f8691dd | ||
|
|
8acd2c964e | ||
|
|
b4c7912d21 | ||
|
|
e38451ff6c | ||
|
|
36c3a19aa8 | ||
|
|
5c5ae38934 | ||
|
|
18798f150d | ||
|
|
ae6633ef8a |
10
.github/workflows/ccpp.yml
vendored
10
.github/workflows/ccpp.yml
vendored
@@ -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
|
||||
|
||||
|
||||
2
.github/workflows/cd.yml
vendored
2
.github/workflows/cd.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/cifuzz.yml
vendored
2
.github/workflows/cifuzz.yml
vendored
@@ -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
207
AGENTS.md
Normal 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
172
AIToolPolicy.md
Normal 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/
|
||||
@@ -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."
|
||||
)
|
||||
|
||||
33
Readme.md
33
Readme.md
@@ -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
|
||||

|
||||
[](https://www.codacy.com/gh/assimp/assimp/dashboard?utm_source=github.com&utm_medium=referral&utm_content=assimp/assimp&utm_campaign=Badge_Grade)
|
||||
[](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
|
||||

|
||||
|
||||
### 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 -
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) */
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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("");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
6
port/assimp_rs/Cargo.lock
generated
6
port/assimp_rs/Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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
40
port/assimp_rs/build.rs
Normal 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!");
|
||||
}
|
||||
48
port/assimp_rs/src/assimp_wrapper.h
Normal file
48
port/assimp_rs/src/assimp_wrapper.h
Normal 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
|
||||
@@ -1 +0,0 @@
|
||||
pub use self::structs::{Camera};
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
@@ -1,6 +0,0 @@
|
||||
mod anim;
|
||||
pub use self::anim::{
|
||||
Animation,
|
||||
NodeAnim,
|
||||
MeshAnim,
|
||||
MeshMorphAnim};
|
||||
@@ -1,2 +0,0 @@
|
||||
mod blob;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
mod bone;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
mod camera;
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
mod color;
|
||||
pub use self::color::{
|
||||
Color3D,
|
||||
Color4D
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
mod face;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
mod key;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
mod light;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
mod material;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
mod matrix;
|
||||
pub use self::matrix::{
|
||||
Matrix3x3,
|
||||
Matrix4x4};
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
mod memory;
|
||||
pub use self::memory::MemoryInfo;
|
||||
@@ -1,3 +0,0 @@
|
||||
mod mesh;
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
mod meta;
|
||||
|
||||
@@ -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 */
|
||||
@@ -1,2 +0,0 @@
|
||||
mod node;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
mod plane;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
mod quaternion;
|
||||
|
||||
pub use self::quaternion::Quaternion;
|
||||
@@ -1,7 +0,0 @@
|
||||
use crate::vec;
|
||||
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
pub struct Quaternion {
|
||||
_coordinates: vec::Vector4d
|
||||
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
mod ray;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
mod scene;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
mod string;
|
||||
pub use self::string::MAXLEN;
|
||||
pub use self::string::Str;
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
mod texture;
|
||||
pub use self::texture::Texel;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user