diff --git a/CMakeLists.txt b/CMakeLists.txt
index b603b2ef4..a61cd4d9f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -104,7 +104,7 @@ IF(ASSIMP_HUNTER_ENABLED)
ADD_DEFINITIONS(-DASSIMP_USE_HUNTER)
ENDIF()
-PROJECT(Assimp VERSION 6.0.4
+PROJECT(Assimp VERSION 6.0.5
LANGUAGES C CXX
DESCRIPTION "Open Asset Import Library (Assimp) is a library to import various well-known 3D model formats in a uniform manner."
)
diff --git a/Readme.md b/Readme.md
index 34f9caf21..928698f82 100644
--- a/Readme.md
+++ b/Readme.md
@@ -3,7 +3,7 @@ Open Asset Import Library (assimp)
Open Asset Import Library is a library that loads various 3D file formats into a shared, in-memory format. It supports more than __40 file formats__ for import and a growing selection of file formats for export.
-### Current project status
+## Current project status

[](https://www.codacy.com/gh/assimp/assimp/dashboard?utm_source=github.com&utm_medium=referral&utm_content=assimp/assimp&utm_campaign=Badge_Grade)
[](https://sonarcloud.io/summary/new_code?id=assimp_assimp)
@@ -19,29 +19,38 @@ Additionally, assimp features various __mesh post-processing tools__: normals an
## Project activity

-### Documentation
+## Developer quickstart
+```bash
+git clone https://github.com/assimp/assimp
+cd assimp
+cmake -G Ninja -DASSIMP_BUILD_TESTS=off -DASSIMP_INSTALL=off -S . -B build
+cd build
+ninja
+```
+
+## Documentation
Read [our latest documentation](https://the-asset-importer-lib-documentation.readthedocs.io/en/latest/).
-### Pre-built binaries
+## Pre-built binaries
Download binaries from [our Itch Projectspace](https://kimkulling.itch.io/the-asset-importer-lib).
-### Test data
+## Test data
Clone [our model database for testing purposes](https://github.com/assimp/assimp-mdb).
-### Communities
+## Communities
- Ask questions at [the Assimp Discussion Board](https://github.com/assimp/assimp/discussions).
- Find us on [discord](https://discord.gg/kKazXMXDy2)
- Ask [the Assimp community on Reddit](https://www.reddit.com/r/Assimp/).
- Ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest).
- Nothing has worked? File a question or an issue report at [The Assimp-Issue Tracker](https://github.com/assimp/assimp/issues)
-#### Supported file formats
+## Supported file formats
See [the complete list of supported formats](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md).
-### Building
+## Building
Start by reading [our build instructions](https://github.com/assimp/assimp/blob/master/Build.md). We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
-### Ports
+## Ports
* [Android](port/AndroidJNI/README.md)
* [Python](port/PyAssimp/README.md)
* [.NET](https://github.com/Saalvage/AssimpNetter)
@@ -54,11 +63,11 @@ Start by reading [our build instructions](https://github.com/assimp/assimp/blob/
* [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port.
* [Rust](https://github.com/jkvargas/russimp)
-### Other tools
+## Other tools
[Qt5-ModelViewer](https://github.com/sharjith/ModelViewer-Qt5) is a powerful viewer based on Qt5 and Assimp's import and export abilities.
[Assimp-Viewer](https://github.com/assimp/assimp_view) is an experimental implementation for an Asset-Viewer based on ImGUI and Assimp (experimental).
-#### Repository structure ####
+### Repository structure
Open Asset Import Library is implemented in C++. The directory structure looks like this:
```txt
@@ -85,7 +94,7 @@ test Unit- and regression tests, test suite of models
tools Tools (old assimp viewer, command line `assimp`)
samples Small number of samples to illustrate possible use cases for Assimp
```
-### Contributing
+## Contributing
We would greatly appreciate for you to contribute to assimp. The easiest way to get involved is to submit
a pull request with your changes against the main repository's `master` branch.
@@ -113,7 +122,7 @@ You can support the project with your organization. Your logo will show up here
-### License ###
+## License ###
Our license is based on the modified, __3-clause BSD__-License.
An _informal_ summary is: do whatever you want, but include Assimp's license text with your product -
diff --git a/SECURITY.md b/SECURITY.md
index b5cd16831..987e899ef 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -6,7 +6,7 @@ The current version of Assimp that's being supported with security updates:
| Version | Supported |
| ------- | ------------------ |
-| 6.0.2 | :white_check_mark: |
+| 6.0.5 | :white_check_mark: |
## Reporting a Vulnerability
diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp
index edb78a764..90ce22a75 100644
--- a/code/AssetLib/FBX/FBXExporter.cpp
+++ b/code/AssetLib/FBX/FBXExporter.cpp
@@ -1766,8 +1766,8 @@ void FBXExporter::WriteObjects () {
// can't easily determine which texture path will be correct,
// so just store what we have in every field.
// these being incorrect is a common problem with FBX anyway.
- tnode.AddChild("FileName", tp_elem->second);
- tnode.AddChild("RelativeFilename", tp_elem->second);
+ tnode.AddChild("FileName", tfile_path);
+ tnode.AddChild("RelativeFilename", tfile_path);
tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0));
tnode.AddChild("ModelUVScaling", double(1.0), double(1.0));
tnode.AddChild("Texture_Alpha_Source", "None");
diff --git a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp
index 3949f76a2..e2dce6cf9 100644
--- a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp
+++ b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp
@@ -971,7 +971,7 @@ void HL1MDLLoader::read_animations() {
pseqdesc = get_buffer_data(header_->seqindex, header_->numseq);
- aiAnimation **scene_animations_ptr = scene_->mAnimations = new aiAnimation *[scene_->mNumAnimations];
+ aiAnimation **scene_animations_ptr = scene_->mAnimations = new aiAnimation *[scene_->mNumAnimations]();
for (int sequence = 0; sequence < header_->numseq; ++sequence, ++pseqdesc) {
pseqgroup = get_buffer_data(header_->seqgroupindex + pseqdesc->seqgroup * sizeof(SequenceGroup_HL1), 1);
@@ -992,7 +992,7 @@ void HL1MDLLoader::read_animations() {
scene_animation->mTicksPerSecond = pseqdesc->fps;
scene_animation->mDuration = static_cast(pseqdesc->fps) * pseqdesc->numframes;
scene_animation->mNumChannels = static_cast(header_->numbones);
- scene_animation->mChannels = new aiNodeAnim *[scene_animation->mNumChannels];
+ scene_animation->mChannels = new aiNodeAnim *[scene_animation->mNumChannels]();
for (int bone = 0; bone < header_->numbones; bone++, ++pbone, ++panim) {
aiNodeAnim *node_anim = scene_animation->mChannels[bone] = new aiNodeAnim();
diff --git a/code/AssetLib/Obj/ObjFileParser.cpp b/code/AssetLib/Obj/ObjFileParser.cpp
index 921ae6c3d..cc843468d 100644
--- a/code/AssetLib/Obj/ObjFileParser.cpp
+++ b/code/AssetLib/Obj/ObjFileParser.cpp
@@ -467,7 +467,7 @@ void ObjFileParser::getFace(aiPrimitiveType type) {
ASSIMP_LOG_ERROR("Obj: Separator unexpected in point statement");
}
iPos++;
- } else if (IsSpaceOrNewLine(*m_DataIt)) {
+ } else if (IsSpaceOrNewLine(*m_DataIt) || *m_DataIt == '\v') {
iPos = 0;
} else {
//OBJ USES 1 Base ARRAYS!!!!
diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp
index 4edbcc9ed..b377a191e 100644
--- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp
+++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp
@@ -892,7 +892,7 @@ void OpenGEXImporter::handleIndexArrayNode(ODDLParser::DDLNode *node, aiScene *
m_currentMesh->mVertices = new aiVector3D[m_currentMesh->mNumVertices];
bool hasColors(false);
if (m_currentVertices.m_numColors > 0) {
- m_currentMesh->mColors[0] = new aiColor4D[m_currentVertices.m_numColors];
+ m_currentMesh->mColors[0] = new aiColor4D[m_currentMesh->mNumVertices];
hasColors = true;
}
bool hasNormalCoords(false);
@@ -924,7 +924,7 @@ void OpenGEXImporter::handleIndexArrayNode(ODDLParser::DDLNode *node, aiScene *
ai_assert(index < m_currentMesh->mNumVertices);
aiVector3D &pos = (m_currentVertices.m_vertices[idx]);
m_currentMesh->mVertices[index].Set(pos.x, pos.y, pos.z);
- if (hasColors) {
+ if (hasColors && static_cast(idx) < m_currentVertices.m_numColors) {
aiColor4D &col = m_currentVertices.m_colors[idx];
m_currentMesh->mColors[0][index] = col;
}
diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl
index 569ce9972..9a67dbf66 100644
--- a/code/AssetLib/glTF2/glTF2Asset.inl
+++ b/code/AssetLib/glTF2/glTF2Asset.inl
@@ -888,7 +888,9 @@ inline void Accessor::Read(Value &obj, Asset &r) {
if (bufferView) {
// Check length
- unsigned long long byteLength = (unsigned long long)GetBytesPerComponent() * (unsigned long long)count;
+ unsigned long long byteLength = count > 0
+ ? (unsigned long long)GetStride() * (unsigned long long)(count - 1) + (unsigned long long)GetElementSize()
+ : 0;
// handle integer overflow
if (byteLength < count) {
@@ -1004,7 +1006,15 @@ inline size_t Accessor::GetMaxByteSize() {
if (decodedBuffer)
return decodedBuffer->byteLength;
- return (bufferView ? bufferView->byteLength : sparse->data.size());
+ if (sparse) {
+ return sparse->data.size();
+ }
+
+ if (bufferView) {
+ return byteOffset <= bufferView->byteLength ? bufferView->byteLength - byteOffset : 0;
+ }
+
+ return 0;
}
template
diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp
index 13c8bf090..a83526a27 100644
--- a/code/AssetLib/glTF2/glTF2Importer.cpp
+++ b/code/AssetLib/glTF2/glTF2Importer.cpp
@@ -1316,6 +1316,15 @@ struct AnimationSamplers {
struct vec4f {
float x, y, z, w;
};
+
+static aiAnimInterpolation MapInterpolation(Interpolation interp) {
+ switch (interp) {
+ case Interpolation_STEP: return aiAnimInterpolation_Step;
+ case Interpolation_CUBICSPLINE: return aiAnimInterpolation_Cubic_Spline;
+ default: return aiAnimInterpolation_Linear;
+ }
+}
+
aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &samplers) {
aiNodeAnim *anim = new aiNodeAnim();
@@ -1327,7 +1336,6 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &sample
if (samplers.translation && samplers.translation->input && samplers.translation->output) {
float *times = nullptr;
samplers.translation->input->ExtractData(times);
- //aiVector3D *values = nullptr;
vec4f *tmp_values = nullptr;
size_t numItems = samplers.translation->output->ExtractData(tmp_values);
aiVector3D *values = new aiVector3D[numItems];
@@ -1338,13 +1346,31 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &sample
}
delete[] tmp_values;
- anim->mNumPositionKeys = static_cast(samplers.translation->input->count);
- anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
- unsigned int ii = (samplers.translation->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
- for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
- anim->mPositionKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
- anim->mPositionKeys[i].mValue = values[ii];
- ii += (samplers.translation->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
+ const bool isCubic = (samplers.translation->interpolation == Interpolation_CUBICSPLINE);
+ const aiAnimInterpolation interpType = MapInterpolation(samplers.translation->interpolation);
+ const unsigned int numLogicalKeys = static_cast(samplers.translation->input->count);
+
+ if (isCubic) {
+ // Store as triplets: in-tangent, value, out-tangent per logical key
+ anim->mNumPositionKeys = numLogicalKeys * 3;
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+ for (unsigned int i = 0; i < numLogicalKeys; ++i) {
+ unsigned int srcBase = i * 3;
+ unsigned int dstBase = i * 3;
+ for (unsigned int t = 0; t < 3; ++t) {
+ anim->mPositionKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
+ anim->mPositionKeys[dstBase + t].mValue = values[srcBase + t];
+ anim->mPositionKeys[dstBase + t].mInterpolation = interpType;
+ }
+ }
+ } else {
+ anim->mNumPositionKeys = numLogicalKeys;
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+ for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
+ anim->mPositionKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
+ anim->mPositionKeys[i].mValue = values[i];
+ anim->mPositionKeys[i].mInterpolation = interpType;
+ }
}
delete[] times;
delete[] values;
@@ -1362,16 +1388,36 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &sample
samplers.rotation->input->ExtractData(times);
aiQuaternion *values = nullptr;
samplers.rotation->output->ExtractData(values);
- anim->mNumRotationKeys = static_cast(samplers.rotation->input->count);
- anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
- unsigned int ii = (samplers.rotation->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
- for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
- anim->mRotationKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
- anim->mRotationKeys[i].mValue.x = values[ii].w;
- anim->mRotationKeys[i].mValue.y = values[ii].x;
- anim->mRotationKeys[i].mValue.z = values[ii].y;
- anim->mRotationKeys[i].mValue.w = values[ii].z;
- ii += (samplers.rotation->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
+ const bool isCubic = (samplers.rotation->interpolation == Interpolation_CUBICSPLINE);
+ const aiAnimInterpolation interpType = MapInterpolation(samplers.rotation->interpolation);
+ const unsigned int numLogicalKeys = static_cast(samplers.rotation->input->count);
+
+ if (isCubic) {
+ anim->mNumRotationKeys = numLogicalKeys * 3;
+ anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
+ for (unsigned int i = 0; i < numLogicalKeys; ++i) {
+ unsigned int srcBase = i * 3;
+ unsigned int dstBase = i * 3;
+ for (unsigned int t = 0; t < 3; ++t) {
+ anim->mRotationKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
+ anim->mRotationKeys[dstBase + t].mValue.x = values[srcBase + t].w;
+ anim->mRotationKeys[dstBase + t].mValue.y = values[srcBase + t].x;
+ anim->mRotationKeys[dstBase + t].mValue.z = values[srcBase + t].y;
+ anim->mRotationKeys[dstBase + t].mValue.w = values[srcBase + t].z;
+ anim->mRotationKeys[dstBase + t].mInterpolation = interpType;
+ }
+ }
+ } else {
+ anim->mNumRotationKeys = numLogicalKeys;
+ anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
+ for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
+ anim->mRotationKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
+ anim->mRotationKeys[i].mValue.x = values[i].w;
+ anim->mRotationKeys[i].mValue.y = values[i].x;
+ anim->mRotationKeys[i].mValue.z = values[i].y;
+ anim->mRotationKeys[i].mValue.w = values[i].z;
+ anim->mRotationKeys[i].mInterpolation = interpType;
+ }
}
delete[] times;
delete[] values;
@@ -1390,13 +1436,30 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &sample
samplers.scale->input->ExtractData(times);
aiVector3D *values = nullptr;
samplers.scale->output->ExtractData(values);
- anim->mNumScalingKeys = static_cast(samplers.scale->input->count);
- anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
- unsigned int ii = (samplers.scale->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
- for (unsigned int i = 0; i < anim->mNumScalingKeys; ++i) {
- anim->mScalingKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
- anim->mScalingKeys[i].mValue = values[ii];
- ii += (samplers.scale->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
+ const bool isCubic = (samplers.scale->interpolation == Interpolation_CUBICSPLINE);
+ const aiAnimInterpolation interpType = MapInterpolation(samplers.scale->interpolation);
+ const unsigned int numLogicalKeys = static_cast(samplers.scale->input->count);
+
+ if (isCubic) {
+ anim->mNumScalingKeys = numLogicalKeys * 3;
+ anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
+ for (unsigned int i = 0; i < numLogicalKeys; ++i) {
+ unsigned int srcBase = i * 3;
+ unsigned int dstBase = i * 3;
+ for (unsigned int t = 0; t < 3; ++t) {
+ anim->mScalingKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
+ anim->mScalingKeys[dstBase + t].mValue = values[srcBase + t];
+ anim->mScalingKeys[dstBase + t].mInterpolation = interpType;
+ }
+ }
+ } else {
+ anim->mNumScalingKeys = numLogicalKeys;
+ anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
+ for (unsigned int i = 0; i < anim->mNumScalingKeys; ++i) {
+ anim->mScalingKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
+ anim->mScalingKeys[i].mValue = values[i];
+ anim->mScalingKeys[i].mInterpolation = interpType;
+ }
}
delete[] times;
delete[] values;
diff --git a/code/PostProcessing/TriangulateProcess.cpp b/code/PostProcessing/TriangulateProcess.cpp
index cc92b441d..6c9bc033c 100644
--- a/code/PostProcessing/TriangulateProcess.cpp
+++ b/code/PostProcessing/TriangulateProcess.cpp
@@ -234,7 +234,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
++numOut;
} else {
numOut += face.mNumIndices-2;
- max_out = std::max(max_out,face.mNumIndices);
+ max_out = std::max(max_out,face.mNumIndices);
}
}
diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp
index 1e14f5fd0..e065f9410 100644
--- a/contrib/openddlparser/code/OpenDDLParser.cpp
+++ b/contrib/openddlparser/code/OpenDDLParser.cpp
@@ -796,10 +796,13 @@ char *OpenDDLParser::parseStringLiteral(char *in, char *end, Value **stringData)
if (*start == '\"') {
++start;
++in;
- while (*in != '\"' && in != end) {
+ while (in != end && *in != '\"') {
++in;
++len;
}
+ if (in == end) {
+ return in;
+ }
*stringData = ValueAllocator::allocPrimData(Value::ValueType::ddl_string, len);
::strncpy((char *)(*stringData)->m_data, start, len);
diff --git a/test/unit/utVersion.cpp b/test/unit/utVersion.cpp
index 7f0a373e3..4eb133e29 100644
--- a/test/unit/utVersion.cpp
+++ b/test/unit/utVersion.cpp
@@ -61,7 +61,7 @@ TEST_F( utVersion, aiGetVersionMinorTest ) {
}
TEST_F( utVersion, aiGetVersionPatchTest ) {
- EXPECT_EQ(aiGetVersionPatch(), 4U );
+ EXPECT_EQ(aiGetVersionPatch(), 5U );
}
TEST_F( utVersion, aiGetCompileFlagsTest ) {