Merge branch 'master' into feature/rust_bindings

This commit is contained in:
Kim Kulling
2026-04-06 16:20:25 +02:00
committed by GitHub
23 changed files with 1693 additions and 1314 deletions

View File

@@ -109,7 +109,7 @@ jobs:
run: cd build/bin && ./unit ${{ steps.hunter_extra_test_args.outputs.args }}
shell: bash
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v7
if: matrix.name == 'windows-msvc'
with:
name: 'assimp-bins-${{ matrix.name }}'
@@ -141,7 +141,7 @@ jobs:
prerelease: true
- run: |
echo '${{steps.create-release.outputs.upload_url}}' > release_upload_url.txt
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v7
with:
name: create-release
path: release_upload_url.txt
@@ -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

View File

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

207
AGENTS.md Normal file
View File

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

172
AIToolPolicy.md Normal file
View File

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

View File

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

View File

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

View File

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

View File

@@ -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");
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@@ -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());
}

View File

@@ -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;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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) {