Merge branch 'master' into feature/rust_bindings
This commit is contained in:
6
.github/workflows/ccpp.yml
vendored
6
.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
|
||||
@@ -188,7 +188,7 @@ jobs:
|
||||
- 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
|
||||
|
||||
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/
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) || *m_DataIt == '\v') {
|
||||
++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
|
||||
|
||||
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("");
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -462,9 +462,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 +766,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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -155,7 +155,7 @@ TEST_F(AssimpAPITest_aiMatrix3x3, aiMatrix3FromToTest) {
|
||||
const auto from = aiVector3D(1,2,1).Normalize(), to = aiVector3D(-1,1,1).Normalize();
|
||||
aiMatrix3x3::FromToMatrix(from, to, result_cpp);
|
||||
aiMatrix3FromTo(&result_c, &from, &to);
|
||||
EXPECT_EQ(result_cpp, result_c);
|
||||
EXPECT_TRUE(result_cpp.Equal(result_c, Epsilon));
|
||||
}
|
||||
|
||||
TEST_F(AssimpAPITest_aiMatrix3x3, operatorTest) {
|
||||
|
||||
@@ -261,7 +261,7 @@ TEST_F(AssimpAPITest_aiMatrix4x4, aiMatrix4FromToTest) {
|
||||
const auto from = aiVector3D(1,2,1).Normalize(), to = aiVector3D(-1,1,1).Normalize();
|
||||
aiMatrix4x4::FromToMatrix(from, to, result_cpp);
|
||||
aiMatrix4FromTo(&result_c, &from, &to);
|
||||
EXPECT_EQ(result_cpp, result_c);
|
||||
EXPECT_TRUE(result_cpp.Equal(result_c, Epsilon));
|
||||
}
|
||||
|
||||
TEST_F(AssimpAPITest_aiMatrix4x4, operatorTest) {
|
||||
|
||||
@@ -88,7 +88,10 @@ TEST_F(AssimpAPITest_aiQuaternion, aiQuaternionFromNormalizedQuaternionTest) {
|
||||
const auto qvec3 = random_unit_vec3();
|
||||
result_cpp = aiQuaternion(qvec3);
|
||||
aiQuaternionFromNormalizedQuaternion(&result_c, &qvec3);
|
||||
EXPECT_EQ(result_cpp, result_c);
|
||||
// Use a larger tolerance because FMA contraction differences in
|
||||
// 1.0 - x*x - y*y - z*z can flip the sign of a near-zero residual,
|
||||
// causing w = 0 vs w = sqrt(tiny) ≈ 1e-4.
|
||||
EXPECT_TRUE(result_cpp.Equal(result_c, 1e-4f));
|
||||
}
|
||||
|
||||
TEST_F(AssimpAPITest_aiQuaternion, aiQuaternionAreEqualTest) {
|
||||
|
||||
Reference in New Issue
Block a user