Merge branch 'master' into feature/rust_bindings

This commit is contained in:
Kim Kulling
2026-02-26 15:33:51 +01:00
committed by GitHub
12 changed files with 136 additions and 51 deletions
+1 -1
View File
@@ -104,7 +104,7 @@ IF(ASSIMP_HUNTER_ENABLED)
ADD_DEFINITIONS(-DASSIMP_USE_HUNTER)
ENDIF()
PROJECT(Assimp VERSION 6.0.4
PROJECT(Assimp VERSION 6.0.5
LANGUAGES C CXX
DESCRIPTION "Open Asset Import Library (Assimp) is a library to import various well-known 3D model formats in a uniform manner."
)
+21 -12
View File
@@ -3,7 +3,7 @@ Open Asset Import Library (assimp)
Open Asset Import Library is a library that loads various 3D file formats into a shared, in-memory format. It supports more than __40 file formats__ for import and a growing selection of file formats for export.
### Current project status
## Current project status
![C/C++ CI](https://github.com/assimp/assimp/workflows/C/C++%20CI/badge.svg)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9973693b7bdd4543b07084d5d9cf4745)](https://www.codacy.com/gh/assimp/assimp/dashboard?utm_source=github.com&utm_medium=referral&utm_content=assimp/assimp&utm_campaign=Badge_Grade)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=assimp_assimp&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=assimp_assimp)
@@ -19,29 +19,38 @@ Additionally, assimp features various __mesh post-processing tools__: normals an
## Project activity
![Alt](https://repobeats.axiom.co/api/embed/997f84e5f9fcf772da1e687f3a4f3a8afdbf4cf0.svg "Repobeats analytics image")
### Documentation
## Developer quickstart
```bash
git clone https://github.com/assimp/assimp
cd assimp
cmake -G Ninja -DASSIMP_BUILD_TESTS=off -DASSIMP_INSTALL=off -S . -B build
cd build
ninja
```
## Documentation
Read [our latest documentation](https://the-asset-importer-lib-documentation.readthedocs.io/en/latest/).
### Pre-built binaries
## Pre-built binaries
Download binaries from [our Itch Projectspace](https://kimkulling.itch.io/the-asset-importer-lib).
### Test data
## Test data
Clone [our model database for testing purposes](https://github.com/assimp/assimp-mdb).
### Communities
## Communities
- Ask questions at [the Assimp Discussion Board](https://github.com/assimp/assimp/discussions).
- Find us on [discord](https://discord.gg/kKazXMXDy2)
- Ask [the Assimp community on Reddit](https://www.reddit.com/r/Assimp/).
- Ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest).
- Nothing has worked? File a question or an issue report at [The Assimp-Issue Tracker](https://github.com/assimp/assimp/issues)
#### Supported file formats
## Supported file formats
See [the complete list of supported formats](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md).
### Building
## Building
Start by reading [our build instructions](https://github.com/assimp/assimp/blob/master/Build.md). We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
### Ports
## Ports
* [Android](port/AndroidJNI/README.md)
* [Python](port/PyAssimp/README.md)
* [.NET](https://github.com/Saalvage/AssimpNetter)
@@ -54,11 +63,11 @@ Start by reading [our build instructions](https://github.com/assimp/assimp/blob/
* [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port.
* [Rust](https://github.com/jkvargas/russimp)
### Other tools
## Other tools
[Qt5-ModelViewer](https://github.com/sharjith/ModelViewer-Qt5) is a powerful viewer based on Qt5 and Assimp's import and export abilities.<br>
[Assimp-Viewer](https://github.com/assimp/assimp_view) is an experimental implementation for an Asset-Viewer based on ImGUI and Assimp (experimental).
#### Repository structure ####
### Repository structure
Open Asset Import Library is implemented in C++. The directory structure looks like this:
```txt
@@ -85,7 +94,7 @@ test Unit- and regression tests, test suite of models
tools Tools (old assimp viewer, command line `assimp`)
samples Small number of samples to illustrate possible use cases for Assimp
```
### Contributing
## Contributing
We would greatly appreciate for you to contribute to assimp. The easiest way to get involved is to submit
a pull request with your changes against the main repository's `master` branch.
@@ -113,7 +122,7 @@ You can support the project with your organization. Your logo will show up here
<a href="https://opencollective.com/assimp/organization/0/website"><img src="https://opencollective.com/assimp/organization/0/avatar.svg"></a>
### License ###
## License ###
Our license is based on the modified, __3-clause BSD__-License.
An _informal_ summary is: do whatever you want, but include Assimp's license text with your product -
+1 -1
View File
@@ -6,7 +6,7 @@ The current version of Assimp that's being supported with security updates:
| Version | Supported |
| ------- | ------------------ |
| 6.0.2 | :white_check_mark: |
| 6.0.5 | :white_check_mark: |
## Reporting a Vulnerability
+2 -2
View File
@@ -1766,8 +1766,8 @@ void FBXExporter::WriteObjects () {
// can't easily determine which texture path will be correct,
// so just store what we have in every field.
// these being incorrect is a common problem with FBX anyway.
tnode.AddChild("FileName", tp_elem->second);
tnode.AddChild("RelativeFilename", tp_elem->second);
tnode.AddChild("FileName", tfile_path);
tnode.AddChild("RelativeFilename", tfile_path);
tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0));
tnode.AddChild("ModelUVScaling", double(1.0), double(1.0));
tnode.AddChild("Texture_Alpha_Source", "None");
+2 -2
View File
@@ -971,7 +971,7 @@ void HL1MDLLoader::read_animations() {
pseqdesc = get_buffer_data<SequenceDesc_HL1>(header_->seqindex, header_->numseq);
aiAnimation **scene_animations_ptr = scene_->mAnimations = new aiAnimation *[scene_->mNumAnimations];
aiAnimation **scene_animations_ptr = scene_->mAnimations = new aiAnimation *[scene_->mNumAnimations]();
for (int sequence = 0; sequence < header_->numseq; ++sequence, ++pseqdesc) {
pseqgroup = get_buffer_data<SequenceGroup_HL1>(header_->seqgroupindex + pseqdesc->seqgroup * sizeof(SequenceGroup_HL1), 1);
@@ -992,7 +992,7 @@ void HL1MDLLoader::read_animations() {
scene_animation->mTicksPerSecond = pseqdesc->fps;
scene_animation->mDuration = static_cast<double>(pseqdesc->fps) * pseqdesc->numframes;
scene_animation->mNumChannels = static_cast<unsigned int>(header_->numbones);
scene_animation->mChannels = new aiNodeAnim *[scene_animation->mNumChannels];
scene_animation->mChannels = new aiNodeAnim *[scene_animation->mNumChannels]();
for (int bone = 0; bone < header_->numbones; bone++, ++pbone, ++panim) {
aiNodeAnim *node_anim = scene_animation->mChannels[bone] = new aiNodeAnim();
+1 -1
View File
@@ -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!!!!
+2 -2
View File
@@ -892,7 +892,7 @@ void OpenGEXImporter::handleIndexArrayNode(ODDLParser::DDLNode *node, aiScene *
m_currentMesh->mVertices = new aiVector3D[m_currentMesh->mNumVertices];
bool hasColors(false);
if (m_currentVertices.m_numColors > 0) {
m_currentMesh->mColors[0] = new aiColor4D[m_currentVertices.m_numColors];
m_currentMesh->mColors[0] = new aiColor4D[m_currentMesh->mNumVertices];
hasColors = true;
}
bool hasNormalCoords(false);
@@ -924,7 +924,7 @@ void OpenGEXImporter::handleIndexArrayNode(ODDLParser::DDLNode *node, aiScene *
ai_assert(index < m_currentMesh->mNumVertices);
aiVector3D &pos = (m_currentVertices.m_vertices[idx]);
m_currentMesh->mVertices[index].Set(pos.x, pos.y, pos.z);
if (hasColors) {
if (hasColors && static_cast<size_t>(idx) < m_currentVertices.m_numColors) {
aiColor4D &col = m_currentVertices.m_colors[idx];
m_currentMesh->mColors[0][index] = col;
}
+12 -2
View File
@@ -888,7 +888,9 @@ inline void Accessor::Read(Value &obj, Asset &r) {
if (bufferView) {
// Check length
unsigned long long byteLength = (unsigned long long)GetBytesPerComponent() * (unsigned long long)count;
unsigned long long byteLength = count > 0
? (unsigned long long)GetStride() * (unsigned long long)(count - 1) + (unsigned long long)GetElementSize()
: 0;
// handle integer overflow
if (byteLength < count) {
@@ -1004,7 +1006,15 @@ inline size_t Accessor::GetMaxByteSize() {
if (decodedBuffer)
return decodedBuffer->byteLength;
return (bufferView ? bufferView->byteLength : sparse->data.size());
if (sparse) {
return sparse->data.size();
}
if (bufferView) {
return byteOffset <= bufferView->byteLength ? bufferView->byteLength - byteOffset : 0;
}
return 0;
}
template <class T>
+88 -25
View File
@@ -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<unsigned int>(samplers.translation->input->count);
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
unsigned int ii = (samplers.translation->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
anim->mPositionKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mPositionKeys[i].mValue = values[ii];
ii += (samplers.translation->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
const bool isCubic = (samplers.translation->interpolation == Interpolation_CUBICSPLINE);
const aiAnimInterpolation interpType = MapInterpolation(samplers.translation->interpolation);
const unsigned int numLogicalKeys = static_cast<unsigned int>(samplers.translation->input->count);
if (isCubic) {
// Store as triplets: in-tangent, value, out-tangent per logical key
anim->mNumPositionKeys = numLogicalKeys * 3;
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
for (unsigned int i = 0; i < numLogicalKeys; ++i) {
unsigned int srcBase = i * 3;
unsigned int dstBase = i * 3;
for (unsigned int t = 0; t < 3; ++t) {
anim->mPositionKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
anim->mPositionKeys[dstBase + t].mValue = values[srcBase + t];
anim->mPositionKeys[dstBase + t].mInterpolation = interpType;
}
}
} else {
anim->mNumPositionKeys = numLogicalKeys;
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
anim->mPositionKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mPositionKeys[i].mValue = values[i];
anim->mPositionKeys[i].mInterpolation = interpType;
}
}
delete[] times;
delete[] values;
@@ -1362,16 +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<unsigned int>(samplers.rotation->input->count);
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
unsigned int ii = (samplers.rotation->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
anim->mRotationKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mRotationKeys[i].mValue.x = values[ii].w;
anim->mRotationKeys[i].mValue.y = values[ii].x;
anim->mRotationKeys[i].mValue.z = values[ii].y;
anim->mRotationKeys[i].mValue.w = values[ii].z;
ii += (samplers.rotation->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
const bool isCubic = (samplers.rotation->interpolation == Interpolation_CUBICSPLINE);
const aiAnimInterpolation interpType = MapInterpolation(samplers.rotation->interpolation);
const unsigned int numLogicalKeys = static_cast<unsigned int>(samplers.rotation->input->count);
if (isCubic) {
anim->mNumRotationKeys = numLogicalKeys * 3;
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
for (unsigned int i = 0; i < numLogicalKeys; ++i) {
unsigned int srcBase = i * 3;
unsigned int dstBase = i * 3;
for (unsigned int t = 0; t < 3; ++t) {
anim->mRotationKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
anim->mRotationKeys[dstBase + t].mValue.x = values[srcBase + t].w;
anim->mRotationKeys[dstBase + t].mValue.y = values[srcBase + t].x;
anim->mRotationKeys[dstBase + t].mValue.z = values[srcBase + t].y;
anim->mRotationKeys[dstBase + t].mValue.w = values[srcBase + t].z;
anim->mRotationKeys[dstBase + t].mInterpolation = interpType;
}
}
} else {
anim->mNumRotationKeys = numLogicalKeys;
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
anim->mRotationKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mRotationKeys[i].mValue.x = values[i].w;
anim->mRotationKeys[i].mValue.y = values[i].x;
anim->mRotationKeys[i].mValue.z = values[i].y;
anim->mRotationKeys[i].mValue.w = values[i].z;
anim->mRotationKeys[i].mInterpolation = interpType;
}
}
delete[] times;
delete[] values;
@@ -1390,13 +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<unsigned int>(samplers.scale->input->count);
anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
unsigned int ii = (samplers.scale->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
for (unsigned int i = 0; i < anim->mNumScalingKeys; ++i) {
anim->mScalingKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mScalingKeys[i].mValue = values[ii];
ii += (samplers.scale->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
const bool isCubic = (samplers.scale->interpolation == Interpolation_CUBICSPLINE);
const aiAnimInterpolation interpType = MapInterpolation(samplers.scale->interpolation);
const unsigned int numLogicalKeys = static_cast<unsigned int>(samplers.scale->input->count);
if (isCubic) {
anim->mNumScalingKeys = numLogicalKeys * 3;
anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
for (unsigned int i = 0; i < numLogicalKeys; ++i) {
unsigned int srcBase = i * 3;
unsigned int dstBase = i * 3;
for (unsigned int t = 0; t < 3; ++t) {
anim->mScalingKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
anim->mScalingKeys[dstBase + t].mValue = values[srcBase + t];
anim->mScalingKeys[dstBase + t].mInterpolation = interpType;
}
}
} else {
anim->mNumScalingKeys = numLogicalKeys;
anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
for (unsigned int i = 0; i < anim->mNumScalingKeys; ++i) {
anim->mScalingKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mScalingKeys[i].mValue = values[i];
anim->mScalingKeys[i].mInterpolation = interpType;
}
}
delete[] times;
delete[] values;
+1 -1
View File
@@ -234,7 +234,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
++numOut;
} else {
numOut += face.mNumIndices-2;
max_out = std::max(max_out,face.mNumIndices);
max_out = std::max<uint32_t>(max_out,face.mNumIndices);
}
}
+4 -1
View File
@@ -796,10 +796,13 @@ char *OpenDDLParser::parseStringLiteral(char *in, char *end, Value **stringData)
if (*start == '\"') {
++start;
++in;
while (*in != '\"' && in != end) {
while (in != end && *in != '\"') {
++in;
++len;
}
if (in == end) {
return in;
}
*stringData = ValueAllocator::allocPrimData(Value::ValueType::ddl_string, len);
::strncpy((char *)(*stringData)->m_data, start, len);
+1 -1
View File
@@ -61,7 +61,7 @@ TEST_F( utVersion, aiGetVersionMinorTest ) {
}
TEST_F( utVersion, aiGetVersionPatchTest ) {
EXPECT_EQ(aiGetVersionPatch(), 4U );
EXPECT_EQ(aiGetVersionPatch(), 5U );
}
TEST_F( utVersion, aiGetCompileFlagsTest ) {