Merge branch 'kimkulling/mikktspace_issue-2363' of https://github.com/assimp/assimp into kimkulling/mikktspace_issue-2363

This commit is contained in:
Kim Kulling
2025-07-18 20:53:37 +02:00
1126 changed files with 41188 additions and 15571 deletions

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -81,7 +81,7 @@ void ArmaturePopulate::Execute(aiScene *out) {
aiBone *bone = kvp.first;
aiNode *bone_node = kvp.second;
ASSIMP_LOG_VERBOSE_DEBUG("active node lookup: ", bone->mName.C_Str());
// lcl transform grab - done in generate_nodes :)
aiNode *armature = GetArmatureRoot(bone_node, bones);

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -189,9 +189,9 @@ bool CalcTangentsProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshIndex) {
tangent.x = (w.x * sy - v.x * ty) * dirCorrection;
tangent.y = (w.y * sy - v.y * ty) * dirCorrection;
tangent.z = (w.z * sy - v.z * ty) * dirCorrection;
bitangent.x = (- w.x * sx + v.x * tx) * dirCorrection;
bitangent.y = (- w.y * sx + v.y * tx) * dirCorrection;
bitangent.z = (- w.z * sx + v.z * tx) * dirCorrection;
bitangent.x = (w.x * sx - v.x * tx) * dirCorrection;
bitangent.y = (w.y * sx - v.y * tx) * dirCorrection;
bitangent.z = (w.z * sx - v.z * tx) * dirCorrection;
// store for every vertex of that face
for (unsigned int b = 0; b < face.mNumIndices; ++b) {
@@ -199,13 +199,15 @@ bool CalcTangentsProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshIndex) {
// project tangent and bitangent into the plane formed by the vertex' normal
aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]) - localTangent * (bitangent * localTangent);
aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
localTangent.NormalizeSafe();
localBitangent.NormalizeSafe();
// reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN.
bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z);
bool invalid_bitangent = is_special_float(localBitangent.x) || is_special_float(localBitangent.y) || is_special_float(localBitangent.z);
bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z)
|| (-0.5f < localTangent.x && localTangent.x < 0.5f && -0.5f < localTangent.y && localTangent.y < 0.5f && -0.5f < localTangent.z && localTangent.z < 0.5f);
bool invalid_bitangent = is_special_float(localBitangent.x) || is_special_float(localBitangent.y) || is_special_float(localBitangent.z)
|| (-0.5f < localBitangent.x && localBitangent.x < 0.5f && -0.5f < localBitangent.y && localBitangent.y < 0.5f && -0.5f < localBitangent.z && localBitangent.z < 0.5f);
if (invalid_tangent != invalid_bitangent) {
if (invalid_tangent) {
localTangent = meshNorm[p] ^ localBitangent;

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -346,16 +346,20 @@ void ComputeUVMappingProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("GenUVCoordsProcess begin");
char buffer[1024];
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT)
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
}
std::list<MappingInfo> mappingStack;
/* Iterate through all materials and search for non-UV mapped textures
*/
// Iterate through all materials and search for non-UV mapped textures
for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) {
mappingStack.clear();
aiMaterial *mat = pScene->mMaterials[i];
if (mat == nullptr) {
ASSIMP_LOG_INFO("Material pointer in nullptr, skipping.");
continue;
}
for (unsigned int a = 0; a < mat->mNumProperties; ++a) {
aiMaterialProperty *prop = mat->mProperties[a];
if (!::strcmp(prop->mKey.data, "$tex.mapping")) {

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -60,7 +60,7 @@ struct aiNode;
struct aiMaterial;
struct aiCamera;
namespace Assimp {
namespace Assimp {
// -----------------------------------------------------------------------------------
/** @brief The MakeLeftHandedProcess converts all imported data to a left-handed

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -187,7 +187,7 @@ bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh) {
}
} else {
vertexBones[vid] = i;
}
}
}
if(!isBoneNecessary[i]) {

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "ProcessHelper.h"
#include <fstream>
#include <algorithm>
using namespace Assimp;
@@ -91,25 +92,47 @@ void EmbedTexturesProcess::Execute(aiScene* pScene) {
ASSIMP_LOG_INFO("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." );
}
std::string EmbedTexturesProcess::tryToFindValidPath(const std::string &imagePath) const
{
// Test path directly
if (mIOHandler->Exists(imagePath)) {
return imagePath;
}
ASSIMP_LOG_WARN("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder.");
// Test path in root path
std::string testPath = mRootPath + imagePath;
if (mIOHandler->Exists(testPath)) {
return testPath;
}
// Test path basename in root path
testPath = mRootPath + imagePath.substr(imagePath.find_last_of("\\/") + 1u);
if (mIOHandler->Exists(testPath)) {
return testPath;
}
// In unix systems, '\' is a valid file name character, but some files may use \ as a directory separator.
// Try replacing '\' by '/'.
if (mIOHandler->getOsSeparator() != '\\' && imagePath.find('\\') != std::string::npos) {
ASSIMP_LOG_WARN("EmbedTexturesProcess: Cannot find image '", imagePath, "' in root folder. Will try replacing directory separators.");
testPath = imagePath;
std::replace(testPath.begin(), testPath.end(), '\\', mIOHandler->getOsSeparator());
return tryToFindValidPath(testPath);
}
ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", imagePath, ".");
return {};
}
bool EmbedTexturesProcess::addTexture(aiScene *pScene, const std::string &path) const {
std::streampos imageSize = 0;
std::string imagePath = path;
std::string imagePath = tryToFindValidPath(path);
// Test path directly
if (!mIOHandler->Exists(imagePath)) {
ASSIMP_LOG_WARN("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder.");
// Test path in root path
imagePath = mRootPath + path;
if (!mIOHandler->Exists(imagePath)) {
// Test path basename in root path
imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u);
if (!mIOHandler->Exists(imagePath)) {
ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, ".");
return false;
}
}
if (imagePath.empty()) {
return false;
}
IOStream* pFile = mIOHandler->Open(imagePath);
if (pFile == nullptr) {
ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, ".");

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -74,9 +73,11 @@ public:
void SetupProperties(const Importer* pImp) override;
/// Overwritten, @see BaseProcess
virtual void Execute(aiScene* pScene) override;
void Execute(aiScene* pScene) override;
private:
// Try several ways to attempt to resolve the image path
std::string tryToFindValidPath(const std::string &imagePath) const;
// Resolve the path and add the file content to the scene as a texture.
bool addTexture(aiScene *pScene, const std::string &path) const;

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -143,9 +143,13 @@ bool FindDegeneratesProcess::ExecuteOnMesh(aiMesh *mesh) {
for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
aiFace &face = mesh->mFaces[a];
bool first = true;
auto vertex_in_range = [numVertices = mesh->mNumVertices](unsigned int vertex_idx) { return vertex_idx < numVertices; };
// check whether the face contains degenerated entries
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
if (!std::all_of(face.mIndices, face.mIndices + face.mNumIndices, vertex_in_range))
continue;
// Polygons with more than 4 points are allowed to have double points, that is
// simulating polygons with holes just with concave polygons. However,
// double points may not come directly after another.

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -50,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/mesh.h>
class FindDegeneratesProcessTest;
namespace Assimp {
namespace Assimp {
// ---------------------------------------------------------------------------

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2023, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -19,7 +19,7 @@ conditions are met:
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
s
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
@@ -54,7 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
/**
/**
* @brief Post-processing process to find axis-aligned bounding volumes for amm meshes
* used in a scene.
*/

View File

@@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -41,9 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the post processing step to generate face
* normals for all imported faces.
*/
/**
* @file Implementation of the post-processing step to generate face
* normals for all imported faces.
*/
#include "GenFaceNormalsProcess.h"
#include <assimp/Exceptional.h>
@@ -55,7 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
// Returns whether the processing step is in the given flag field.
bool GenFaceNormalsProcess::IsActive(unsigned int pFlags) const {
force_ = (pFlags & aiProcess_ForceGenNormals) != 0;
flippedWindingOrder_ = (pFlags & aiProcess_FlipWindingOrder) != 0;
@@ -64,7 +63,7 @@ bool GenFaceNormalsProcess::IsActive(unsigned int pFlags) const {
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
// Executes the post-processing step on the given imported data.
void GenFaceNormalsProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("GenFaceNormalsProcess begin");
@@ -88,7 +87,7 @@ void GenFaceNormalsProcess::Execute(aiScene *pScene) {
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
// Executes the post-processing step on the given imported data.
bool GenFaceNormalsProcess::GenMeshFaceNormals(aiMesh *pMesh) {
if (nullptr != pMesh->mNormals) {
if (force_) {
@@ -107,8 +106,27 @@ bool GenFaceNormalsProcess::GenMeshFaceNormals(aiMesh *pMesh) {
}
// allocate an array to hold the output normals
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
const float qnan = get_qnan();
std::vector<aiVector3D> normals;
normals.resize(pMesh->mNumVertices);
// mask to indicate if a vertex was already referenced and needs to be duplicated
std::vector<bool> alreadyReferenced;
alreadyReferenced.resize(pMesh->mNumVertices, false);
std::vector<aiVector3D> duplicatedVertices;
auto storeNormalSplitVertex = [&](unsigned int index, const aiVector3D& normal) {
if (!alreadyReferenced[index]) {
normals[index] = normal;
alreadyReferenced[index] = true;
} else {
duplicatedVertices.push_back(pMesh->mVertices[index]);
normals.push_back(normal);
index = pMesh->mNumVertices + static_cast<unsigned int>(duplicatedVertices.size() - 1);
}
return index;
};
const aiVector3D undefinedNormal = aiVector3D(get_qnan());
// iterate through all faces and compute per-face normals but store them per-vertex.
for (unsigned int a = 0; a < pMesh->mNumFaces; a++) {
@@ -116,7 +134,7 @@ bool GenFaceNormalsProcess::GenMeshFaceNormals(aiMesh *pMesh) {
if (face.mNumIndices < 3) {
// either a point or a line -> no well-defined normal vector
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan);
face.mIndices[i] = storeNormalSplitVertex(face.mIndices[i], undefinedNormal);
}
continue;
}
@@ -124,15 +142,29 @@ bool GenFaceNormalsProcess::GenMeshFaceNormals(aiMesh *pMesh) {
const aiVector3D *pV1 = &pMesh->mVertices[face.mIndices[0]];
const aiVector3D *pV2 = &pMesh->mVertices[face.mIndices[1]];
const aiVector3D *pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices - 1]];
// Boolean XOR - if either but not both of these flags is set, then the winding order has
// changed and the cross product to calculate the normal needs to be reversed
if (flippedWindingOrder_ != leftHanded_)
// Boolean XOR - if either but not both of these flags are set, then the winding order has
// changed and the cross-product to calculate the normal needs to be reversed
if (flippedWindingOrder_ != leftHanded_)
std::swap(pV2, pV3);
const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe();
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
pMesh->mNormals[face.mIndices[i]] = vNor;
face.mIndices[i] = storeNormalSplitVertex(face.mIndices[i], vNor);
}
}
// store normals (and additional vertices) back into the mesh
if (!duplicatedVertices.empty()) {
const aiVector3D * oldVertices = pMesh->mVertices;
auto oldNumVertices = pMesh->mNumVertices;
pMesh->mNumVertices += static_cast<unsigned int>(duplicatedVertices.size());
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
memcpy(pMesh->mVertices, oldVertices, oldNumVertices * sizeof(aiVector3D));
memcpy(pMesh->mVertices + oldNumVertices, duplicatedVertices.data(), duplicatedVertices.size() * sizeof(aiVector3D));
delete[] oldVertices;
}
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
memcpy(pMesh->mNormals, normals.data(), normals.size() * sizeof(aiVector3D));
return true;
}

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -50,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
// ---------------------------------------------------------------------------
/**
/**
* @brief The GenFaceNormalsProcess computes face normals for all faces of all meshes
*/
class ASSIMP_API_WINONLY GenFaceNormalsProcess : public BaseProcess {

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2023, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -380,3 +380,4 @@ ai_real ImproveCacheLocalityProcess::ProcessMesh(aiMesh *pMesh, unsigned int mes
}
} // namespace Assimp

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <unordered_set>
#include <unordered_map>
#include <memory>
#include <map>
using namespace Assimp;
@@ -99,52 +100,6 @@ void JoinVerticesProcess::Execute( aiScene* pScene) {
namespace {
bool areVerticesEqual(
const Vertex &lhs,
const Vertex &rhs,
unsigned numUVChannels,
unsigned numColorChannels) {
// A little helper to find locally close vertices faster.
// Try to reuse the lookup table from the last step.
const static float epsilon = 1e-5f;
// Squared because we check against squared length of the vector difference
static const float squareEpsilon = epsilon * epsilon;
// Square compare is useful for animeshes vertices compare
if ((lhs.position - rhs.position).SquareLength() > squareEpsilon) {
return false;
}
// We just test the other attributes even if they're not present in the mesh.
// In this case they're initialized to 0 so the comparison succeeds.
// By this method the non-present attributes are effectively ignored in the comparison.
if ((lhs.normal - rhs.normal).SquareLength() > squareEpsilon) {
return false;
}
if ((lhs.tangent - rhs.tangent).SquareLength() > squareEpsilon) {
return false;
}
if ((lhs.bitangent - rhs.bitangent).SquareLength() > squareEpsilon) {
return false;
}
for (unsigned i = 0; i < numUVChannels; i++) {
if ((lhs.texcoords[i] - rhs.texcoords[i]).SquareLength() > squareEpsilon) {
return false;
}
}
for (unsigned i = 0; i < numColorChannels; i++) {
if (GetColorDifference(lhs.colors[i], rhs.colors[i]) > squareEpsilon) {
return false;
}
}
return true;
}
template<class XMesh>
void updateXMeshVertices(XMesh *pMesh, std::vector<int> &uniqueVertices) {
// replace vertex data with the unique data sets
@@ -157,7 +112,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<int> &uniqueVertices) {
// ----------------------------------------------------------------------------
// Position, if present (check made for aiAnimMesh)
if (pMesh->mVertices) {
if (pMesh->mVertices) {
std::unique_ptr<aiVector3D[]> oldVertices(pMesh->mVertices);
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
for (unsigned int a = 0; a < pMesh->mNumVertices; a++)
@@ -204,41 +159,6 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<int> &uniqueVertices) {
} // namespace
// ------------------------------------------------------------------------------------------------
// Unites identical vertices in the given mesh
// combine hashes
inline void hash_combine(std::size_t &) {
// empty
}
template <typename T, typename... Rest>
inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
hash_combine(seed, rest...);
}
//template specialization for std::hash for Vertex
template<>
struct std::hash<Vertex> {
std::size_t operator()(Vertex const& v) const noexcept {
size_t seed = 0;
hash_combine(seed, v.position.x ,v.position.y,v.position.z);
return seed;
}
};
//template specialization for std::equal_to for Vertex
template<>
struct std::equal_to<Vertex> {
equal_to(unsigned numUVChannels, unsigned numColorChannels) :
mNumUVChannels(numUVChannels),
mNumColorChannels(numColorChannels) {}
bool operator()(const Vertex &lhs, const Vertex &rhs) const {
return areVerticesEqual(lhs, rhs, mNumUVChannels, mNumColorChannels);
}
private:
unsigned mNumUVChannels;
unsigned mNumColorChannels;
};
static constexpr size_t JOINED_VERTICES_MARK = 0x80000000u;
@@ -266,7 +186,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) {
// We'll never have more vertices afterwards.
std::vector<int> uniqueVertices;
uniqueVertices.reserve( pMesh->mNumVertices);
// For each vertex the index of the vertex it was replaced by.
// Since the maximal number of vertices is 2^31-1, the most significand bit can be used to mark
@@ -276,31 +195,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) {
static_assert(AI_MAX_VERTICES == 0x7fffffff, "AI_MAX_VERTICES == 0x7fffffff");
std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
// float posEpsilonSqr;
SpatialSort *vertexFinder = nullptr;
SpatialSort _vertexFinder;
typedef std::pair<SpatialSort,float> SpatPair;
if (shared) {
std::vector<SpatPair >* avf;
shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
if (avf) {
SpatPair& blubb = (*avf)[meshIndex];
vertexFinder = &blubb.first;
// posEpsilonSqr = blubb.second;
}
}
if (!vertexFinder) {
// bad, need to compute it.
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
vertexFinder = &_vertexFinder;
// posEpsilonSqr = ComputePositionEpsilon(pMesh);
}
// Again, better waste some bytes than a realloc ...
std::vector<unsigned int> verticesFound;
verticesFound.reserve(10);
// Run an optimized code path if we don't have multiple UVs or vertex colors.
// This should yield false in more than 99% of all imports ...
const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0;
@@ -314,14 +208,8 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) {
}
}
// a map that maps a vertex to its new index
const auto numBuckets = pMesh->mNumVertices;
const auto hasher = std::hash<Vertex>();
const auto comparator = std::equal_to<Vertex>(
pMesh->GetNumUVChannels(),
pMesh->GetNumColorChannels());
std::unordered_map<Vertex, int> vertex2Index(numBuckets, hasher, comparator);
std::map<Vertex, int> vertex2Index = {};
// we can not end up with more vertices than we started with
vertex2Index.reserve(pMesh->mNumVertices);
// Now check each vertex if it brings something new to the table
int newIndex = 0;
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
@@ -336,8 +224,8 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) {
// if the vertex is not in the map then it is a new vertex add it.
if (it == vertex2Index.end()) {
// this is a new vertex give it a new index
vertex2Index[v] = newIndex;
//keep track of its index and increment 1
vertex2Index.emplace(v, newIndex);
// keep track of its index and increment 1
replaceIndex[a] = newIndex++;
// add the vertex to the unique vertices
uniqueVertices.push_back(a);

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -65,7 +64,7 @@ class ASSIMP_API JoinVerticesProcess : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
JoinVerticesProcess() = default;
JoinVerticesProcess() = default;
~JoinVerticesProcess() override = default;
// -------------------------------------------------------------------

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2023, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -53,7 +53,8 @@ namespace Assimp {
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
LimitBoneWeightsProcess::LimitBoneWeightsProcess() : mMaxWeights(AI_LMW_MAX_WEIGHTS) {
LimitBoneWeightsProcess::LimitBoneWeightsProcess() :
mMaxWeights(AI_LMW_MAX_WEIGHTS), mRemoveEmptyBones(true) {
// empty
}
@@ -67,7 +68,7 @@ bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const {
// Executes the post processing step on the given imported data.
void LimitBoneWeightsProcess::Execute( aiScene* pScene) {
ai_assert(pScene != nullptr);
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin");
for (unsigned int m = 0; m < pScene->mNumMeshes; ++m) {
@@ -87,7 +88,7 @@ void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) {
// ------------------------------------------------------------------------------------------------
static unsigned int removeEmptyBones(aiMesh *pMesh) {
ai_assert(pMesh != nullptr);
unsigned int writeBone = 0;
for (unsigned int readBone = 0; readBone< pMesh->mNumBones; ++readBone) {
aiBone* bone = pMesh->mBones[readBone];
@@ -97,7 +98,7 @@ static unsigned int removeEmptyBones(aiMesh *pMesh) {
delete bone;
}
}
return writeBone;
}

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -50,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
struct aiMesh;
namespace Assimp {
namespace Assimp {
// ---------------------------------------------------------------------------
/** MakeVerboseFormatProcess: Class to convert an asset to the verbose

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -164,7 +164,7 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode *nd, std::list<aiNode *> &n
++it;
}
if (join_master && !join.empty()) {
join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%u", count_merged++);
join_master->mName.length = ::ai_snprintf(join_master->mName.data, AI_MAXLEN, "$MergedNode_%u", count_merged++);
unsigned int out_meshes = 0;
for (std::list<aiNode *>::const_iterator it = join.cbegin(); it != join.cend(); ++it) {

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -58,7 +57,7 @@ struct aiMesh;
class OptimizeGraphProcessTest;
namespace Assimp {
namespace Assimp {
// -----------------------------------------------------------------------------
/** @brief Postprocessing step to optimize the scenegraph

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -56,7 +55,7 @@ struct aiMesh;
struct aiNode;
class OptimizeMeshesProcessTest;
namespace Assimp {
namespace Assimp {
// ---------------------------------------------------------------------------
/** @brief Postprocessing step to optimize mesh usage

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -348,7 +348,7 @@ void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **i
mesh->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
mesh->mNumBones = UINT_MAX;
continue;
}
}
// try to find us in the list of newly created meshes
for (unsigned int n = 0; n < out.size(); ++n) {
@@ -635,7 +635,7 @@ void PretransformVertices::Execute(aiScene *pScene) {
aiNode *pcNode = new aiNode();
*nodes = pcNode;
pcNode->mParent = pScene->mRootNode;
pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u", i);
pcNode->mName.length = ai_snprintf(pcNode->mName.data, AI_MAXLEN, "light_%u", i);
pScene->mLights[i]->mName = pcNode->mName;
}
// generate camera nodes
@@ -643,7 +643,7 @@ void PretransformVertices::Execute(aiScene *pScene) {
aiNode *pcNode = new aiNode();
*nodes = pcNode;
pcNode->mParent = pScene->mRootNode;
pcNode->mName.length = ::ai_snprintf(pcNode->mName.data, MAXLEN, "cam_%u", i);
pcNode->mName.length = ::ai_snprintf(pcNode->mName.data, AI_MAXLEN, "cam_%u", i);
pScene->mCameras[i]->mName = pcNode->mName;
}
}

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -52,8 +51,9 @@ namespace Assimp {
// -------------------------------------------------------------------------------
void ConvertListToStrings(const std::string &in, std::list<std::string> &out) {
const char *s = in.c_str();
const char *end = in.c_str() + in.size();
while (*s) {
SkipSpacesAndLineEnd(&s);
SkipSpacesAndLineEnd(&s, end);
if (*s == '\'') {
const char *base = ++s;
while (*s != '\'') {
@@ -66,7 +66,7 @@ void ConvertListToStrings(const std::string &in, std::list<std::string> &out) {
out.emplace_back(base, (size_t)(s - base));
++s;
} else {
out.push_back(GetNextToken(s));
out.push_back(GetNextToken(s, end));
}
}
}
@@ -176,8 +176,8 @@ unsigned int GetMeshVFormatUnique(const aiMesh *pcMesh) {
if (pcMesh->HasTangentsAndBitangents()) iRet |= 0x4;
static_assert(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
static_assert(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
static_assert(8 >= AI_MAX_NUMBER_OF_COLOR_SETS, "static_assert(8 >= AI_MAX_NUMBER_OF_COLOR_SETS)");
static_assert(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS, "static_assert(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS)");
// texture coordinates
unsigned int p = 0;

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -75,124 +75,129 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess begin");
unsigned int redundantRemoved = 0, unreferencedRemoved = 0;
if (pScene->mNumMaterials) {
// Find out which materials are referenced by meshes
std::vector<bool> abReferenced(pScene->mNumMaterials,false);
for (unsigned int i = 0;i < pScene->mNumMeshes;++i)
abReferenced[pScene->mMeshes[i]->mMaterialIndex] = true;
if (pScene->mNumMaterials == 0) {
return;
}
// If a list of materials to be excluded was given, match the list with
// our imported materials and 'salt' all positive matches to ensure that
// we get unique hashes later.
if (mConfigFixedMaterials.length()) {
// Find out which materials are referenced by meshes
std::vector<bool> abReferenced(pScene->mNumMaterials,false);
for (unsigned int i = 0;i < pScene->mNumMeshes;++i) {
abReferenced[pScene->mMeshes[i]->mMaterialIndex] = true;
}
std::list<std::string> strings;
ConvertListToStrings(mConfigFixedMaterials,strings);
// If a list of materials to be excluded was given, match the list with
// our imported materials and 'salt' all positive matches to ensure that
// we get unique hashes later.
if (mConfigFixedMaterials.length()) {
std::list<std::string> strings;
ConvertListToStrings(mConfigFixedMaterials,strings);
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
aiMaterial* mat = pScene->mMaterials[i];
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
aiMaterial* mat = pScene->mMaterials[i];
ai_assert(mat != nullptr);
aiString name;
mat->Get(AI_MATKEY_NAME,name);
aiString name;
mat->Get(AI_MATKEY_NAME,name);
if (name.length != 0) {
std::list<std::string>::const_iterator it = std::find(strings.begin(), strings.end(), name.data);
if (it != strings.end()) {
// Our brilliant 'salt': A single material property with ~ as first
// character to mark it as internal and temporary.
const int dummy = 1;
((aiMaterial*)mat)->AddProperty(&dummy,1,"~RRM.UniqueMaterial",0,0);
if (name.length) {
std::list<std::string>::const_iterator it = std::find(strings.begin(), strings.end(), name.data);
if (it != strings.end()) {
// Our brilliant 'salt': A single material property with ~ as first
// character to mark it as internal and temporary.
const int dummy = 1;
((aiMaterial*)mat)->AddProperty(&dummy,1,"~RRM.UniqueMaterial",0,0);
// Keep this material even if no mesh references it
abReferenced[i] = true;
ASSIMP_LOG_VERBOSE_DEBUG( "Found positive match in exclusion list: \'", name.data, "\'");
}
// Keep this material even if no mesh references it
abReferenced[i] = true;
ASSIMP_LOG_VERBOSE_DEBUG( "Found positive match in exclusion list: \'", name.data, "\'");
}
}
}
}
// TODO: re-implement this algorithm to work in-place
unsigned int *aiMappingTable = new unsigned int[pScene->mNumMaterials];
for ( unsigned int i=0; i<pScene->mNumMaterials; i++ ) {
aiMappingTable[ i ] = 0;
// TODO: re-implement this algorithm to work in-place
unsigned int *aiMappingTable = new unsigned int[pScene->mNumMaterials];
for ( unsigned int i=0; i<pScene->mNumMaterials; i++ ) {
aiMappingTable[ i ] = 0;
}
unsigned int iNewNum = 0;
// Iterate through all materials and calculate a hash for them
// store all hashes in a list and so a quick search whether
// we do already have a specific hash. This allows us to
// determine which materials are identical.
uint32_t *aiHashes = new uint32_t[ pScene->mNumMaterials ];
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
// No mesh is referencing this material, remove it.
if (!abReferenced[i]) {
++unreferencedRemoved;
delete pScene->mMaterials[i];
pScene->mMaterials[i] = nullptr;
continue;
}
unsigned int iNewNum = 0;
// Iterate through all materials and calculate a hash for them
// store all hashes in a list and so a quick search whether
// we do already have a specific hash. This allows us to
// determine which materials are identical.
uint32_t *aiHashes = new uint32_t[ pScene->mNumMaterials ];;
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
// No mesh is referencing this material, remove it.
if (!abReferenced[i]) {
++unreferencedRemoved;
// Check all previously mapped materials for a matching hash.
// On a match we can delete this material and just make it ref to the same index.
uint32_t me = aiHashes[i] = ComputeMaterialHash(pScene->mMaterials[i]);
for (unsigned int a = 0; a < i;++a) {
if (abReferenced[a] && me == aiHashes[a]) {
++redundantRemoved;
me = 0;
aiMappingTable[i] = aiMappingTable[a];
delete pScene->mMaterials[i];
pScene->mMaterials[i] = nullptr;
break;
}
}
// This is a new material that is referenced, add to the map.
if (me) {
aiMappingTable[i] = iNewNum++;
}
}
// If the new material count differs from the original,
// we need to rebuild the material list and remap mesh material indexes.
if (iNewNum < 1) {
delete [] aiMappingTable;
delete [] aiHashes;
pScene->mNumMaterials = 0;
return;
}
if (iNewNum != pScene->mNumMaterials) {
ai_assert(iNewNum > 0);
aiMaterial** ppcMaterials = new aiMaterial*[iNewNum];
::memset(ppcMaterials,0,sizeof(void*)*iNewNum);
for (unsigned int p = 0; p < pScene->mNumMaterials;++p) {
// if the material is not referenced ... remove it
if (!abReferenced[p]) {
continue;
}
// Check all previously mapped materials for a matching hash.
// On a match we can delete this material and just make it ref to the same index.
uint32_t me = aiHashes[i] = ComputeMaterialHash(pScene->mMaterials[i]);
for (unsigned int a = 0; a < i;++a) {
if (abReferenced[a] && me == aiHashes[a]) {
++redundantRemoved;
me = 0;
aiMappingTable[i] = aiMappingTable[a];
delete pScene->mMaterials[i];
pScene->mMaterials[i] = nullptr;
break;
// generate new names for modified materials that had no names
const unsigned int idx = aiMappingTable[p];
if (ppcMaterials[idx]) {
aiString sz;
if( ppcMaterials[idx]->Get(AI_MATKEY_NAME, sz) != AI_SUCCESS ) {
sz.length = ::ai_snprintf(sz.data, AI_MAXLEN,"JoinedMaterial_#%u",p);
((aiMaterial*)ppcMaterials[idx])->AddProperty(&sz,AI_MATKEY_NAME);
}
}
// This is a new material that is referenced, add to the map.
if (me) {
aiMappingTable[i] = iNewNum++;
} else {
ppcMaterials[idx] = pScene->mMaterials[p];
}
}
// If the new material count differs from the original,
// we need to rebuild the material list and remap mesh material indexes.
if(iNewNum < 1)
throw DeadlyImportError("No materials remaining");
if (iNewNum != pScene->mNumMaterials) {
ai_assert(iNewNum > 0);
aiMaterial** ppcMaterials = new aiMaterial*[iNewNum];
::memset(ppcMaterials,0,sizeof(void*)*iNewNum);
for (unsigned int p = 0; p < pScene->mNumMaterials;++p)
{
// if the material is not referenced ... remove it
if (!abReferenced[p]) {
continue;
}
// generate new names for modified materials that had no names
const unsigned int idx = aiMappingTable[p];
if (ppcMaterials[idx]) {
aiString sz;
if( ppcMaterials[idx]->Get(AI_MATKEY_NAME, sz) != AI_SUCCESS ) {
sz.length = ::ai_snprintf(sz.data,MAXLEN,"JoinedMaterial_#%u",p);
((aiMaterial*)ppcMaterials[idx])->AddProperty(&sz,AI_MATKEY_NAME);
}
} else {
ppcMaterials[idx] = pScene->mMaterials[p];
}
}
// update all material indices
for (unsigned int p = 0; p < pScene->mNumMeshes;++p) {
aiMesh* mesh = pScene->mMeshes[p];
ai_assert(nullptr != mesh);
mesh->mMaterialIndex = aiMappingTable[mesh->mMaterialIndex];
}
// delete the old material list
delete[] pScene->mMaterials;
pScene->mMaterials = ppcMaterials;
pScene->mNumMaterials = iNewNum;
// update all material indices
for (unsigned int p = 0; p < pScene->mNumMeshes;++p) {
aiMesh* mesh = pScene->mMeshes[p];
ai_assert(nullptr != mesh);
mesh->mMaterialIndex = aiMappingTable[mesh->mMaterialIndex];
}
// delete temporary storage
delete[] aiHashes;
delete[] aiMappingTable;
// delete the old material list
delete[] pScene->mMaterials;
pScene->mMaterials = ppcMaterials;
pScene->mNumMaterials = iNewNum;
}
// delete temporary storage
delete[] aiHashes;
delete[] aiMappingTable;
if (redundantRemoved == 0 && unreferencedRemoved == 0) {
ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess finished ");
} else {

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -51,7 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
class RemoveRedundantMatsTest;
namespace Assimp {
namespace Assimp {
// ---------------------------------------------------------------------------
/** RemoveRedundantMatsProcess: Post-processing step to remove redundant

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -83,7 +82,7 @@ public:
* The function is a request to the process to update its configuration
* basing on the Importer's configuration property list.
*/
virtual void SetupProperties(const Importer* pImp) override;
void SetupProperties(const Importer* pImp) override;
// -------------------------------------------------------------------
/** Manually setup the configuration flags for the step

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -74,13 +73,13 @@ public:
ai_real getScale() const;
/// Overwritten, @see BaseProcess
virtual bool IsActive( unsigned int pFlags ) const override;
bool IsActive( unsigned int pFlags ) const override;
/// Overwritten, @see BaseProcess
virtual void SetupProperties( const Importer* pImp ) override;
void SetupProperties( const Importer* pImp ) override;
/// Overwritten, @see BaseProcess
virtual void Execute( aiScene* pScene ) override;
void Execute( aiScene* pScene ) override;
private:
void traverseNodes( aiNode *currentNode, unsigned int nested_node_id = 0 );

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -65,37 +65,47 @@ void SortByPTypeProcess::SetupProperties(const Importer *pImp) {
mConfigRemoveMeshes = pImp->GetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, 0);
}
// ------------------------------------------------------------------------------------------------
static void clearMeshesInNode(aiNode *node) {
delete[] node->mMeshes;
node->mNumMeshes = 0;
node->mMeshes = nullptr;
}
// ------------------------------------------------------------------------------------------------
// Update changed meshes in all nodes
void UpdateNodes(const std::vector<unsigned int> &replaceMeshIndex, aiNode *node) {
ai_assert(node != nullptr);
if (node->mNumMeshes) {
unsigned int newSize = 0;
for (unsigned int m = 0; m < node->mNumMeshes; ++m) {
unsigned int add = node->mMeshes[m] << 2;
for (unsigned int i = 0; i < 4; ++i) {
if (UINT_MAX != replaceMeshIndex[add + i]) ++newSize;
}
}
if (!newSize) {
delete[] node->mMeshes;
node->mNumMeshes = 0;
node->mMeshes = nullptr;
} else {
// Try to reuse the old array if possible
unsigned int *newMeshes = (newSize > node->mNumMeshes ? new unsigned int[newSize] : node->mMeshes);
for (unsigned int m = 0; m < node->mNumMeshes; ++m) {
unsigned int add = node->mMeshes[m] << 2;
for (unsigned int i = 0; i < 4; ++i) {
if (UINT_MAX != replaceMeshIndex[add + i])
*newMeshes++ = replaceMeshIndex[add + i];
if (UINT_MAX != replaceMeshIndex[add + i]) {
++newSize;
}
}
if (newSize > node->mNumMeshes)
delete[] node->mMeshes;
node->mMeshes = newMeshes - (node->mNumMeshes = newSize);
}
if (newSize == 0) {
clearMeshesInNode(node);
return;
}
// Try to reuse the old array if possible
unsigned int *newMeshes = (newSize > node->mNumMeshes ? new unsigned int[newSize] : node->mMeshes);
for (unsigned int m = 0; m < node->mNumMeshes; ++m) {
unsigned int add = node->mMeshes[m] << 2;
for (unsigned int i = 0; i < 4; ++i) {
if (UINT_MAX != replaceMeshIndex[add + i]) {
*newMeshes++ = replaceMeshIndex[add + i];
}
}
}
if (newSize > node->mNumMeshes) {
clearMeshesInNode(node);
}
node->mMeshes = newMeshes - (node->mNumMeshes = newSize);
}
// call all subnodes recursively
@@ -126,6 +136,9 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
aiMesh *const mesh = pScene->mMeshes[i];
if (mesh->mPrimitiveTypes == 0) {
for (size_t idx = 0; idx < outMeshes.size(); ++idx) {
delete outMeshes[idx];
}
throw DeadlyImportError("Mesh with invalid primitive type: ", mesh->mName.C_Str());
}
@@ -167,6 +180,10 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
// with the largest number of primitives
unsigned int aiNumPerPType[4] = { 0, 0, 0, 0 };
aiFace *pFirstFace = mesh->mFaces;
if (pFirstFace == nullptr) {
continue;
}
aiFace *const pLastFace = pFirstFace + mesh->mNumFaces;
unsigned int numPolyVerts = 0;
@@ -308,7 +325,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
if (vert) {
*vert++ = mesh->mVertices[idx];
}
if (nor)
if (nor)
*nor++ = mesh->mNormals[idx];
if (tan) {
*tan++ = mesh->mTangents[idx];
@@ -316,13 +333,13 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
}
for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp) {
if (!uv[pp])
if (!uv[pp])
break;
*uv[pp]++ = mesh->mTextureCoords[pp][idx];
}
for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp) {
if (!cols[pp])
if (!cols[pp])
break;
*cols[pp]++ = mesh->mColors[pp][idx];
}

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -50,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
class SortByPTypeProcessTest;
namespace Assimp {
namespace Assimp {
// ---------------------------------------------------------------------------

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -394,7 +394,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
// Recursively updates the node's mesh list to account for the changed mesh list
void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const {
// rebuild the node's mesh index list
if( pNode->mNumMeshes == 0 ) {
if( pNode->mNumMeshes != 0 ) {
IndexArray newMeshList;
for( unsigned int a = 0; a < pNode->mNumMeshes; ++a) {
unsigned int srcIndex = pNode->mMeshes[a];

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -74,7 +74,7 @@ public:
/// @brief Called prior to ExecuteOnScene().
/// The function is a request to the process to update its configuration
/// basing on the Importer's configuration property list.
virtual void SetupProperties(const Importer* pImp) override;
void SetupProperties(const Importer* pImp) override;
/// @brief Will return the maximal number of bones.
/// @return The maximal number of bones.

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -100,6 +100,11 @@ void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp) {
// ------------------------------------------------------------------------------------------------
// Update a node after some meshes have been split
void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& avList) {
if (pcNode == nullptr) {
ASSIMP_LOG_WARN("UpdateNode skipped, nullptr detected.");
return;
}
// for every index in out list build a new entry
std::vector<unsigned int> aiEntries;
aiEntries.reserve(pcNode->mNumMeshes + 1);

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -1,8 +1,7 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -35,27 +34,20 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file A helper class that processes texture transformations */
#include "TextureTransform.h"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/scene.h>
#include "TextureTransform.h"
#include <assimp/StringUtils.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
TextureTransformStep::TextureTransformStep() : configFlags() {
// nothing to do here
}
namespace Assimp {
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
@@ -202,7 +194,6 @@ inline static const char* MappingModeToChar(aiTextureMapMode map) {
void TextureTransformStep::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("TransformUVCoordsProcess begin");
/* We build a per-mesh list of texture transformations we'll need
* to apply. To achieve this, we iterate through all materials,
* find all textures and get their transformations and UV indices.
@@ -390,8 +381,8 @@ void TextureTransformStep::Execute( aiScene* pScene) {
cnt = 0;
for (it = trafo.begin();it != trafo.end(); ++it,++cnt) {
if ((*it).lockedPos != AI_TT_UV_IDX_LOCK_NONE && (*it).lockedPos != cnt) {
it2 = trafo.begin();unsigned int t = 0;
while (t != (*it).lockedPos)
it2 = trafo.begin();
while ((*it2).lockedPos != (*it).lockedPos)
++it2;
if ((*it2).lockedPos != AI_TT_UV_IDX_LOCK_NONE) {
@@ -426,7 +417,6 @@ void TextureTransformStep::Execute( aiScene* pScene) {
// it shouldn't be too worse if we remove them.
unsigned int size = (unsigned int)trafo.size();
if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) {
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_ERROR(static_cast<unsigned int>(trafo.size()), " UV channels required but just ",
AI_MAX_NUMBER_OF_TEXTURECOORDS, " available");
@@ -434,7 +424,6 @@ void TextureTransformStep::Execute( aiScene* pScene) {
size = AI_MAX_NUMBER_OF_TEXTURECOORDS;
}
aiVector3D* old[AI_MAX_NUMBER_OF_TEXTURECOORDS];
for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n)
old[n] = mesh->mTextureCoords[n];
@@ -443,14 +432,13 @@ void TextureTransformStep::Execute( aiScene* pScene) {
// that we're not going to need later can be overridden.
it = trafo.begin();
for (unsigned int n = 0; n < trafo.size();++n,++it) {
if (n >= size) {
// Try to use an untransformed channel for all channels we threw over board
UpdateUVIndex((*it).updateList,untransformed);
continue;
}
outChannels++;
++outChannels;
// Write to the log
if (!DefaultLogger::isNullLogger()) {
@@ -470,15 +458,18 @@ void TextureTransformStep::Execute( aiScene* pScene) {
// Check whether we need a new buffer here
if (mesh->mTextureCoords[n]) {
it2 = it;++it2;
it2 = it;
++it2;
for (unsigned int m = n+1; m < size;++m, ++it2) {
if ((*it2).uvIndex == n){
it2 = trafo.begin();
break;
}
}
if (it2 == trafo.begin()){
if (it2 == trafo.begin()) {
{
std::unique_ptr<aiVector3D[]> oldTextureCoords(mesh->mTextureCoords[n]);
}
mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices];
}
}
@@ -536,13 +527,12 @@ void TextureTransformStep::Execute( aiScene* pScene) {
// Print some detailed statistics into the log
if (!DefaultLogger::isNullLogger()) {
if (transformedChannels) {
ASSIMP_LOG_INFO("TransformUVCoordsProcess end: ", outChannels, " output channels (in: ", inChannels, ", modified: ", transformedChannels,")");
} else {
ASSIMP_LOG_DEBUG("TransformUVCoordsProcess finished");
ASSIMP_LOG_INFO("TransformUVCoordsProcess finished");
}
}
}
} // namespace Assimp

View File

@@ -1,9 +1,7 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -44,142 +42,116 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_TEXTURE_TRANSFORM_H_INCLUDED
#define AI_TEXTURE_TRANSFORM_H_INCLUDED
#include <assimp/BaseImporter.h>
#include "Common/BaseProcess.h"
#include <assimp/BaseImporter.h>
#include <assimp/material.h>
#include <list>
struct aiNode;
struct aiMaterial;
namespace Assimp {
namespace Assimp {
#define AI_TT_UV_IDX_LOCK_TBD 0xffffffff
#define AI_TT_UV_IDX_LOCK_NONE 0xeeeeeeee
#define AI_TT_ROTATION_EPSILON ((float)AI_DEG_TO_RAD(0.5))
// ---------------------------------------------------------------------------
/** Small helper structure representing a shortcut into the material list
* to be able to update some values quickly.
*/
/**
* @brief Small helper structure representing a shortcut into the material list
* to be able to update some values quickly.
*/
struct TTUpdateInfo {
TTUpdateInfo() AI_NO_EXCEPT
: directShortcut(nullptr)
, mat(nullptr)
, semantic(0)
, index(0) {
// empty
}
//! Direct shortcut, if available
unsigned int* directShortcut;
unsigned int* directShortcut = nullptr;
//! Material
aiMaterial *mat;
aiMaterial *mat = nullptr;
//! Texture type and index
unsigned int semantic, index;
//! Texture type
unsigned int semantic = 0;
//! Texture index
unsigned int index = 0;
};
// ---------------------------------------------------------------------------
/** Helper class representing texture coordinate transformations
*/
/**
* @brief Helper class representing texture coordinate transformations
*/
struct STransformVecInfo : public aiUVTransform {
STransformVecInfo() AI_NO_EXCEPT
: uvIndex(0)
, mapU(aiTextureMapMode_Wrap)
, mapV(aiTextureMapMode_Wrap)
, lockedPos(AI_TT_UV_IDX_LOCK_NONE) {
// empty
}
//! Source texture coordinate index
unsigned int uvIndex;
unsigned int uvIndex = 0;
//! Texture mapping mode in the u, v direction
aiTextureMapMode mapU,mapV;
//! Texture mapping mode in the u direction
aiTextureMapMode mapU = aiTextureMapMode_Wrap;
//! Texture mapping mode in the v direction
aiTextureMapMode mapV = aiTextureMapMode_Wrap;
//! Locked destination UV index
//! AI_TT_UV_IDX_LOCK_TBD - to be determined
//! AI_TT_UV_IDX_LOCK_NONE - none (default)
unsigned int lockedPos;
unsigned int lockedPos = AI_TT_UV_IDX_LOCK_NONE;
//! Update info - shortcuts into all materials
//! that are referencing this transform setup
std::list<TTUpdateInfo> updateList;
// -------------------------------------------------------------------
/** Compare two transform setups
*/
inline bool operator== (const STransformVecInfo& other) const
{
inline bool operator== (const STransformVecInfo& other) const {
// We use a small epsilon here
const static float epsilon = 0.05f;
if (std::fabs( mTranslation.x - other.mTranslation.x ) > epsilon ||
std::fabs( mTranslation.y - other.mTranslation.y ) > epsilon)
{
std::fabs( mTranslation.y - other.mTranslation.y ) > epsilon) {
return false;
}
if (std::fabs( mScaling.x - other.mScaling.x ) > epsilon ||
std::fabs( mScaling.y - other.mScaling.y ) > epsilon)
{
std::fabs( mScaling.y - other.mScaling.y ) > epsilon) {
return false;
}
if (std::fabs( mRotation - other.mRotation) > epsilon)
{
if (std::fabs( mRotation - other.mRotation) > epsilon) {
return false;
}
return true;
}
inline bool operator!= (const STransformVecInfo& other) const
{
inline bool operator!= (const STransformVecInfo& other) const {
return !(*this == other);
}
// -------------------------------------------------------------------
/** Returns whether this is an untransformed texture coordinate set
*/
inline bool IsUntransformed() const
{
/**
* @brief Returns whether this is an untransformed texture coordinate set
*/
inline bool IsUntransformed() const {
return (1.0f == mScaling.x && 1.f == mScaling.y &&
!mTranslation.x && !mTranslation.y &&
mRotation < AI_TT_ROTATION_EPSILON);
!mTranslation.x && !mTranslation.y && mRotation < AI_TT_ROTATION_EPSILON);
}
// -------------------------------------------------------------------
/** Build a 3x3 matrix from the transformations
*/
inline void GetMatrix(aiMatrix3x3& mOut)
{
/**
* @brief Build a 3x3 matrix from the transformations
*/
inline void GetMatrix(aiMatrix3x3& mOut) {
mOut = aiMatrix3x3();
if (1.0f != mScaling.x || 1.0f != mScaling.y)
{
if (1.0f != mScaling.x || 1.0f != mScaling.y) {
aiMatrix3x3 mScale;
mScale.a1 = mScaling.x;
mScale.b2 = mScaling.y;
mOut = mScale;
}
if (mRotation)
{
if (mRotation) {
aiMatrix3x3 mRot;
mRot.a1 = mRot.b2 = std::cos(mRotation);
mRot.a2 = mRot.b1 = std::sin(mRotation);
mRot.a2 = -mRot.a2;
mOut *= mRot;
}
if (mTranslation.x || mTranslation.y)
{
if (mTranslation.x || mTranslation.y) {
aiMatrix3x3 mTrans;
mTrans.a3 = mTranslation.x;
mTrans.b3 = mTranslation.y;
@@ -188,16 +160,18 @@ struct STransformVecInfo : public aiUVTransform {
}
};
// ---------------------------------------------------------------------------
/** Helper step to compute final UV coordinate sets if there are scalings
/**
* @brief Helper step to compute final UV coordinate sets if there are scalings
* or rotations in the original data read from the file.
*/
class TextureTransformStep : public BaseProcess {
*/
class TextureTransformStep final : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
TextureTransformStep();
/// The default class constructor. /
TextureTransformStep() = default;
/// The default class destructor.
~TextureTransformStep() override = default;
// -------------------------------------------------------------------
@@ -211,16 +185,17 @@ public:
protected:
// -------------------------------------------------------------------
/** Preprocess a specific UV transformation setup
/**
* @brief Preprocess a specific UV transformation setup
*
* @param info Transformation setup to be preprocessed.
*/
*/
void PreProcessUVTransform(STransformVecInfo& info);
private:
unsigned int configFlags;
};
} // namespace Assimp
#endif //! AI_TEXTURE_TRANSFORM_H_INCLUDED

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -62,6 +62,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "PostProcessing/TriangulateProcess.h"
#include "PostProcessing/ProcessHelper.h"
#include "Common/PolyTools.h"
#include "contrib/earcut-hpp/earcut.hpp"
#include <memory>
#include <cstdint>
@@ -74,6 +75,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define POLY_GRID_XPAD 20
#define POLY_OUTPUT_FILE "assimp_polygons_debug.txt"
namespace mapbox::util {
template <>
struct nth<0, aiVector2D> {
inline static auto get(const aiVector2D& t) {
return t.x;
}
};
template <>
struct nth<1, aiVector2D> {
inline static auto get(const aiVector2D& t) {
return t.y;
}
};
} // namespace mapbox::util
using namespace Assimp;
namespace {
@@ -192,13 +210,13 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
const aiFace& face = pMesh->mFaces[a];
if( face.mNumIndices != 3) {
bNeed = true;
}
}
if (!bNeed)
if (!bNeed) {
return false;
}
}
else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) {
return false;
@@ -213,8 +231,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
get_normals = false;
}
if( face.mNumIndices <= 3) {
numOut++;
++numOut;
} else {
numOut += face.mNumIndices-2;
max_out = std::max(max_out,face.mNumIndices);
@@ -222,7 +239,10 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
}
// Just another check whether aiMesh::mPrimitiveTypes is correct
ai_assert(numOut != pMesh->mNumFaces);
if (numOut == pMesh->mNumFaces) {
ASSIMP_LOG_ERROR( "Invalidation detected in the number of indices: does not fit to the primitive type." );
return false;
}
aiVector3D *nor_out = nullptr;
@@ -242,7 +262,9 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
aiFace* out = new aiFace[numOut](), *curOut = out;
std::vector<aiVector3D> temp_verts3d(max_out+2); /* temporary storage for vertices */
std::vector<aiVector2D> temp_verts(max_out+2);
std::vector<std::vector<aiVector2D>> temp_poly(1); /* temporary storage for earcut.hpp */
std::vector<aiVector2D>& temp_verts = temp_poly[0];
temp_verts.reserve(max_out + 2);
NGONEncoder ngonEncoder;
@@ -262,13 +284,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
const aiVector3D* verts = pMesh->mVertices;
// use std::unique_ptr to avoid slow std::vector<bool> specialiations
std::unique_ptr<bool[]> done(new bool[max_out]);
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
aiFace& face = pMesh->mFaces[a];
unsigned int* idx = face.mIndices;
int num = (int)face.mNumIndices, ear = 0, tmp, prev = num-1, next = 0, max = num;
unsigned int num = face.mNumIndices;
// Apply vertex colors to represent the face winding?
#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
@@ -361,16 +381,16 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
// We project it onto a plane to get a 2d triangle.
// Collect all vertices of of the polygon.
for (tmp = 0; tmp < max; ++tmp) {
for (unsigned int tmp = 0; tmp < num; ++tmp) {
temp_verts3d[tmp] = verts[idx[tmp]];
}
// Get newell normal of the polygon. Store it for future use if it's a polygon-only mesh
aiVector3D n;
NewellNormal<3,3,3>(n,max,&temp_verts3d.front().x,&temp_verts3d.front().y,&temp_verts3d.front().z);
NewellNormal<3, 3, 3>(n, num, &temp_verts3d.front().x, &temp_verts3d.front().y, &temp_verts3d.front().z);
if (nor_out) {
for (tmp = 0; tmp < max; ++tmp)
nor_out[idx[tmp]] = n;
for (unsigned int tmp = 0; tmp < num; ++tmp)
nor_out[idx[tmp]] = n;
}
// Select largest normal coordinate to ignore for projection
@@ -396,10 +416,20 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
std::swap(ac,bc);
}
for (tmp =0; tmp < max; ++tmp) {
temp_verts.resize(num);
for (unsigned int tmp = 0; tmp < num; ++tmp) {
temp_verts[tmp].x = verts[idx[tmp]][ac];
temp_verts[tmp].y = verts[idx[tmp]][bc];
done[tmp] = false;
}
auto indices = mapbox::earcut(temp_poly);
for (size_t i = 0; i < indices.size(); i += 3) {
aiFace& nface = *curOut++;
nface.mIndices = new unsigned int[3];
nface.mNumIndices = 3;
nface.mIndices[0] = indices[i];
nface.mIndices[1] = indices[i + 1];
nface.mIndices[2] = indices[i + 2];
}
#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
@@ -429,120 +459,6 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
fprintf(fout,"\ntriangulation sequence: ");
#endif
//
// FIXME: currently this is the slow O(kn) variant with a worst case
// complexity of O(n^2) (I think). Can be done in O(n).
while (num > 3) {
// Find the next ear of the polygon
int num_found = 0;
for (ear = next;;prev = ear,ear = next) {
// break after we looped two times without a positive match
for (next=ear+1;done[(next>=max?next=0:next)];++next);
if (next < ear) {
if (++num_found == 2) {
break;
}
}
const aiVector2D* pnt1 = &temp_verts[ear],
*pnt0 = &temp_verts[prev],
*pnt2 = &temp_verts[next];
// Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1.
if (OnLeftSideOfLine2D(*pnt0,*pnt2,*pnt1) == 1) {
continue;
}
// Skip when three point is in a line
aiVector2D left = *pnt0 - *pnt1;
aiVector2D right = *pnt2 - *pnt1;
left.Normalize();
right.Normalize();
auto mul = left * right;
// if the angle is 0 or 180
if (std::abs(mul - 1.f) < ai_epsilon || std::abs(mul + 1.f) < ai_epsilon) {
// skip this ear
ASSIMP_LOG_WARN("Skip a ear, due to its angle is near 0 or 180.");
continue;
}
// and no other point may be contained in this triangle
for ( tmp = 0; tmp < max; ++tmp) {
// We need to compare the actual values because it's possible that multiple indexes in
// the polygon are referring to the same position. concave_polygon.obj is a sample
//
// FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in
// PointInTriangle() I'm guessing that it's actually possible to construct
// input data that would cause us to end up with no ears. The problem is,
// which epsilon? If we chose a too large value, we'd get wrong results
const aiVector2D& vtmp = temp_verts[tmp];
if ( vtmp != *pnt1 && vtmp != *pnt2 && vtmp != *pnt0 && PointInTriangle2D(*pnt0,*pnt1,*pnt2,vtmp)) {
break;
}
}
if (tmp != max) {
continue;
}
// this vertex is an ear
break;
}
if (num_found == 2) {
// Due to the 'two ear theorem', every simple polygon with more than three points must
// have 2 'ears'. Here's definitely something wrong ... but we don't give up yet.
//
// Instead we're continuing with the standard tri-fanning algorithm which we'd
// use if we had only convex polygons. That's life.
ASSIMP_LOG_ERROR("Failed to triangulate polygon (no ear found). Probably not a simple polygon?");
#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
fprintf(fout,"critical error here, no ear found! ");
#endif
num = 0;
break;
}
aiFace& nface = *curOut++;
nface.mNumIndices = 3;
if (!nface.mIndices) {
nface.mIndices = new unsigned int[3];
}
// setup indices for the new triangle ...
nface.mIndices[0] = prev;
nface.mIndices[1] = ear;
nface.mIndices[2] = next;
// exclude the ear from most further processing
done[ear] = true;
--num;
}
if (num > 0) {
// We have three indices forming the last 'ear' remaining. Collect them.
aiFace& nface = *curOut++;
nface.mNumIndices = 3;
if (!nface.mIndices) {
nface.mIndices = new unsigned int[3];
}
for (tmp = 0; done[tmp]; ++tmp);
nface.mIndices[0] = tmp;
for (++tmp; done[tmp]; ++tmp);
nface.mIndices[1] = tmp;
for (++tmp; done[tmp]; ++tmp);
nface.mIndices[2] = tmp;
}
}
#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -134,7 +134,7 @@ inline void ValidateDSProcess::DoValidationEx(T **parray, unsigned int size,
if (size == 0) {
return;
}
if (!parray) {
ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
firstName, secondName, size);
@@ -371,20 +371,7 @@ void ValidateDSProcess::Validate(const aiMesh *pMesh) {
ReportWarning("There are unreferenced vertices");
}
// texture channel 2 may not be set if channel 1 is zero ...
{
unsigned int i = 0;
for (; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
if (!pMesh->HasTextureCoords(i)) break;
}
for (; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i)
if (pMesh->HasTextureCoords(i)) {
ReportError("Texture coordinate channel %i exists "
"although the previous channel was nullptr.",
i);
}
}
// the same for the vertex colors
// vertex color channel 2 may not be set if channel 1 is zero ...
{
unsigned int i = 0;
for (; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
@@ -460,7 +447,7 @@ void ValidateDSProcess::Validate(const aiMesh *pMesh, const aiBone *pBone, float
if (pBone->mWeights[i].mVertexId >= pMesh->mNumVertices) {
ReportError("aiBone::mWeights[%i].mVertexId is out of range", i);
} else if (!pBone->mWeights[i].mWeight || pBone->mWeights[i].mWeight > 1.0f) {
ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value", i);
ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value %i. Value must be greater than zero and less than 1.", i, pBone->mWeights[i].mWeight);
}
afSum[pBone->mWeights[i].mVertexId] += pBone->mWeights[i].mWeight;
}
@@ -904,14 +891,17 @@ void ValidateDSProcess::Validate(const aiNode *pNode) {
ReportError("aiNode \"%s\" child %i \"%s\" parent is someone else: \"%s\"", pNode->mName.C_Str(), i, pChild->mName.C_Str(), parentName);
}
}
} else if (pNode->mChildren) {
ReportError("aiNode::mChildren is not nullptr for empty node %s (aiNode::mNumChildren is %i)",
nodeName, pNode->mNumChildren);
}
}
// ------------------------------------------------------------------------------------------------
void ValidateDSProcess::Validate(const aiString *pString) {
if (pString->length > MAXLEN) {
if (pString->length > AI_MAXLEN) {
ReportError("aiString::length is too large (%u, maximum is %lu)",
pString->length, MAXLEN);
pString->length, AI_MAXLEN);
}
const char *sz = pString->data;
while (true) {
@@ -920,7 +910,7 @@ void ValidateDSProcess::Validate(const aiString *pString) {
ReportError("aiString::data is invalid: the terminal zero is at a wrong offset");
}
break;
} else if (sz >= &pString->data[MAXLEN]) {
} else if (sz >= &pString->data[AI_MAXLEN]) {
ReportError("aiString::data is invalid. There is no terminal character");
}
++sz;

View File

@@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2025, assimp team
All rights reserved.
@@ -63,7 +62,7 @@ struct aiString;
struct aiCamera;
struct aiLight;
namespace Assimp {
namespace Assimp {
// --------------------------------------------------------------------------------------
/** Validates the whole ASSIMP scene data structure for correctness.