update assimp lib

This commit is contained in:
marauder2k7 2024-12-09 20:22:47 +00:00
parent 03a348deb7
commit d3f8fee74e
1725 changed files with 196314 additions and 62009 deletions

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -43,15 +43,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/DefaultLogger.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <iostream>
namespace Assimp {
/// The default class constructor.
ArmaturePopulate::ArmaturePopulate() = default;
static bool IsBoneNode(const aiString &bone_name, std::vector<aiBone *> &bones) {
for (aiBone *bone : bones) {
if (bone->mName == bone_name) {
return true;
}
}
/// The class destructor.
ArmaturePopulate::~ArmaturePopulate() = default;
return false;
}
bool ArmaturePopulate::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_PopulateArmatureData) != 0;
@ -70,7 +73,7 @@ void ArmaturePopulate::Execute(aiScene *out) {
BuildBoneList(out->mRootNode, out->mRootNode, out, bones);
BuildNodeList(out->mRootNode, nodes);
BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes);
BuildBoneStack(out->mRootNode, out, bones, bone_stack, nodes);
ASSIMP_LOG_DEBUG("Bone stack size: ", bone_stack.size());
@ -78,9 +81,8 @@ 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 :)
// bone->mOffsetMatrix = bone_node->mTransformation;
aiNode *armature = GetArmatureRoot(bone_node, bones);
ai_assert(armature);
@ -159,8 +161,7 @@ void ArmaturePopulate::BuildNodeList(const aiNode *current_node,
// A bone stack allows us to have multiple armatures, with the same bone names
// A bone stack allows us also to retrieve bones true transform even with
// duplicate names :)
void ArmaturePopulate::BuildBoneStack(aiNode *,
const aiNode *root_node,
void ArmaturePopulate::BuildBoneStack(const aiNode *root_node,
const aiScene*,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
@ -196,8 +197,7 @@ void ArmaturePopulate::BuildBoneStack(aiNode *,
// This is required to be detected for a bone initially, it will recurse up
// until it cannot find another bone and return the node No known failure
// points. (yet)
aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node,
std::vector<aiBone *> &bone_list) {
aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, std::vector<aiBone *> &bone_list) {
while (nullptr != bone_node) {
if (!IsBoneNode(bone_node->mName, bone_list)) {
ASSIMP_LOG_VERBOSE_DEBUG("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str());
@ -212,18 +212,6 @@ aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node,
return nullptr;
}
// Simple IsBoneNode check if this could be a bone
bool ArmaturePopulate::IsBoneNode(const aiString &bone_name,
std::vector<aiBone *> &bones) {
for (aiBone *bone : bones) {
if (bone->mName == bone_name) {
return true;
}
}
return false;
}
// Pop this node by name from the stack if found
// Used in multiple armature situations with duplicate node / bone names
// Known flaw: cannot have nodes with bone names, will be fixed in later release

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -69,10 +69,10 @@ namespace Assimp {
class ASSIMP_API ArmaturePopulate : public BaseProcess {
public:
/// The default class constructor.
ArmaturePopulate();
ArmaturePopulate() = default;
/// The class destructor.
virtual ~ArmaturePopulate();
virtual ~ArmaturePopulate() = default;
/// Overwritten, @see BaseProcess
virtual bool IsActive( unsigned int pFlags ) const;
@ -86,9 +86,6 @@ public:
static aiNode *GetArmatureRoot(aiNode *bone_node,
std::vector<aiBone *> &bone_list);
static bool IsBoneNode(const aiString &bone_name,
std::vector<aiBone *> &bones);
static aiNode *GetNodeFromStack(const aiString &node_name,
std::vector<aiNode *> &nodes);
@ -99,7 +96,7 @@ public:
const aiScene *scene,
std::vector<aiBone *> &bones);
static void BuildBoneStack(aiNode *current_node, const aiNode *root_node,
static void BuildBoneStack(const aiNode *root_node,
const aiScene *scene,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
@ -108,5 +105,4 @@ public:
} // Namespace Assimp
#endif // SCALE_PROCESS_H_

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
@ -60,10 +60,6 @@ CalcTangentsProcess::CalcTangentsProcess() :
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
CalcTangentsProcess::~CalcTangentsProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool CalcTangentsProcess::IsActive(unsigned int pFlags) const {
@ -189,9 +185,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 +195,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,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -59,14 +59,11 @@ namespace Assimp
* because the joining of vertices also considers tangents and bitangents for
* uniqueness.
*/
class ASSIMP_API_WINONLY CalcTangentsProcess : public BaseProcess
{
class ASSIMP_API_WINONLY CalcTangentsProcess : public BaseProcess {
public:
CalcTangentsProcess();
~CalcTangentsProcess();
~CalcTangentsProcess() override = default;
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag.
* @param pFlags The processing flags the importer was called with.
@ -74,24 +71,21 @@ public:
* @return true if the process is present in this flag fields,
* false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** Called prior to ExecuteOnScene().
* The function is a request to the process to update its configuration
* basing on the Importer's configuration property list.
*/
void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer* pImp) override;
// setter for configMaxAngle
inline void SetMaxSmoothAngle(float f)
{
void SetMaxSmoothAngle(float f) {
configMaxAngle =f;
}
protected:
// -------------------------------------------------------------------
/** Calculates tangents and bitangents for a specific mesh.
* @param pMesh The mesh to process.
@ -103,10 +97,9 @@ protected:
/** Executes the post processing step on the given imported data.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
private:
/** Configuration option: maximum smoothing angle, in radians*/
float configMaxAngle;
unsigned int configSourceUV;

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -42,8 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file GenUVCoords step */
#include "ComputeUVMappingProcess.h"
#include "Geometry/GeometryUtils.h"
#include "ProcessHelper.h"
#include <assimp/Exceptional.h>
@ -51,47 +50,25 @@ using namespace Assimp;
namespace {
const static aiVector3D base_axis_y(0.0,1.0,0.0);
const static aiVector3D base_axis_x(1.0,0.0,0.0);
const static aiVector3D base_axis_z(0.0,0.0,1.0);
const static ai_real angle_epsilon = ai_real( 0.95 );
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
ComputeUVMappingProcess::ComputeUVMappingProcess() = default;
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
ComputeUVMappingProcess::~ComputeUVMappingProcess() = default;
const static aiVector3D base_axis_y(0.0, 1.0, 0.0);
const static aiVector3D base_axis_x(1.0, 0.0, 0.0);
const static aiVector3D base_axis_z(0.0, 0.0, 1.0);
const static ai_real angle_epsilon = ai_real(0.95);
} // namespace
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool ComputeUVMappingProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_GenUVCoords) != 0;
}
// ------------------------------------------------------------------------------------------------
// Check whether a ray intersects a plane and find the intersection point
inline bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos,
const aiVector3D& planeNormal, aiVector3D& pos)
{
const ai_real b = planeNormal * (planePos - ray.pos);
ai_real h = ray.dir * planeNormal;
if ((h < 10e-5 && h > -10e-5) || (h = b/h) < 0)
return false;
pos = ray.pos + (ray.dir * h);
return true;
bool ComputeUVMappingProcess::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_GenUVCoords) != 0;
}
// ------------------------------------------------------------------------------------------------
// Find the first empty UV channel in a mesh
inline unsigned int FindEmptyUVChannel (aiMesh* mesh)
{
for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS;++m)
if (!mesh->mTextureCoords[m])return m;
inline unsigned int FindEmptyUVChannel(aiMesh *mesh) {
for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++m)
if (!mesh->mTextureCoords[m]) {
return m;
}
ASSIMP_LOG_ERROR("Unable to compute UV coordinates, no free UV slot found");
return UINT_MAX;
@ -99,22 +76,22 @@ inline unsigned int FindEmptyUVChannel (aiMesh* mesh)
// ------------------------------------------------------------------------------------------------
// Try to remove UV seams
void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
{
void RemoveUVSeams(aiMesh *mesh, aiVector3D *out) {
// TODO: just a very rough algorithm. I think it could be done
// much easier, but I don't know how and am currently too tired to
// to think about a better solution.
const static ai_real LOWER_LIMIT = ai_real( 0.1 );
const static ai_real UPPER_LIMIT = ai_real( 0.9 );
const static ai_real LOWER_LIMIT = ai_real(0.1);
const static ai_real UPPER_LIMIT = ai_real(0.9);
const static ai_real LOWER_EPSILON = ai_real( 10e-3 );
const static ai_real UPPER_EPSILON = ai_real( 1.0-10e-3 );
const static ai_real LOWER_EPSILON = ai_real(10e-3);
const static ai_real UPPER_EPSILON = ai_real(1.0 - 10e-3);
for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx)
{
const aiFace& face = mesh->mFaces[fidx];
if (face.mNumIndices < 3) continue; // triangles and polygons only, please
for (unsigned int fidx = 0; fidx < mesh->mNumFaces; ++fidx) {
const aiFace &face = mesh->mFaces[fidx];
if (face.mNumIndices < 3) {
continue; // triangles and polygons only, please
}
unsigned int smallV = face.mNumIndices, large = smallV;
bool zero = false, one = false, round_to_zero = false;
@ -123,20 +100,18 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
// but the assumption that a face with at least one very small
// on the one side and one very large U coord on the other side
// lies on a UV seam should work for most cases.
for (unsigned int n = 0; n < face.mNumIndices;++n)
{
if (out[face.mIndices[n]].x < LOWER_LIMIT)
{
for (unsigned int n = 0; n < face.mNumIndices; ++n) {
if (out[face.mIndices[n]].x < LOWER_LIMIT) {
smallV = n;
// If we have a U value very close to 0 we can't
// round the others to 0, too.
if (out[face.mIndices[n]].x <= LOWER_EPSILON)
zero = true;
else round_to_zero = true;
else
round_to_zero = true;
}
if (out[face.mIndices[n]].x > UPPER_LIMIT)
{
if (out[face.mIndices[n]].x > UPPER_LIMIT) {
large = n;
// If we have a U value very close to 1 we can't
@ -145,10 +120,8 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
one = true;
}
}
if (smallV != face.mNumIndices && large != face.mNumIndices)
{
for (unsigned int n = 0; n < face.mNumIndices;++n)
{
if (smallV != face.mNumIndices && large != face.mNumIndices) {
for (unsigned int n = 0; n < face.mNumIndices; ++n) {
// If the u value is over the upper limit and no other u
// value of that face is 0, round it to 0
if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero)
@ -164,9 +137,8 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
// Due to numerical inaccuracies one U coord becomes 0, the
// other 1. But we do still have a third UV coord to determine
// to which side we must round to.
else if (one && zero)
{
if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON)
else if (one && zero) {
if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON)
out[face.mIndices[n]].x = 0.0;
else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON)
out[face.mIndices[n]].x = 1.0;
@ -177,8 +149,7 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
{
void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh *mesh, const aiVector3D &axis, aiVector3D *out) {
aiVector3D center, min, max;
FindMeshCenter(mesh, center, min, max);
@ -186,7 +157,7 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
// currently the mapping axis will always be one of x,y,z, except if the
// PretransformVertices step is used (it transforms the meshes into worldspace,
// thus changing the mapping axis)
if (axis * base_axis_x >= angle_epsilon) {
if (axis * base_axis_x >= angle_epsilon) {
// For each point get a normalized projection vector in the sphere,
// get its longitude and latitude and map them to their respective
@ -200,58 +171,54 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
// Thus we can derive:
// lat = arcsin (z)
// lon = arctan (y/x)
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.z, diff.y) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
(std::asin (diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D diff = (mesh->mVertices[pnt] - center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.z, diff.y) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
(std::asin(diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
}
}
else if (axis * base_axis_y >= angle_epsilon) {
} else if (axis * base_axis_y >= angle_epsilon) {
// ... just the same again
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.x, diff.z) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
(std::asin (diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D diff = (mesh->mVertices[pnt] - center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.x, diff.z) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
(std::asin(diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
}
}
else if (axis * base_axis_z >= angle_epsilon) {
} else if (axis * base_axis_z >= angle_epsilon) {
// ... just the same again
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
(std::asin (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D diff = (mesh->mVertices[pnt] - center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
(std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
}
}
// slower code path in case the mapping axis is not one of the coordinate system axes
else {
else {
aiMatrix4x4 mTrafo;
aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
aiMatrix4x4::FromToMatrix(axis, base_axis_y, mTrafo);
// again the same, except we're applying a transformation now
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D diff = ((mTrafo*mesh->mVertices[pnt])-center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
(std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D diff = ((mTrafo * mesh->mVertices[pnt]) - center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
(std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
}
}
// Now find and remove UV seams. A seam occurs if a face has a tcoord
// close to zero on the one side, and a tcoord close to one on the
// other side.
RemoveUVSeams(mesh,out);
RemoveUVSeams(mesh, out);
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
{
void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh *mesh, const aiVector3D &axis, aiVector3D *out) {
aiVector3D center, min, max;
// If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
// currently the mapping axis will always be one of x,y,z, except if the
// PretransformVertices step is used (it transforms the meshes into worldspace,
// thus changing the mapping axis)
if (axis * base_axis_x >= angle_epsilon) {
if (axis * base_axis_x >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
const ai_real diff = max.x - min.x;
@ -259,116 +226,110 @@ void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector
// directly to the texture V axis. The other axis is derived from
// the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where
// 'c' is the center point of the mesh.
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
aiVector3D& uv = out[pnt];
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D &pos = mesh->mVertices[pnt];
aiVector3D &uv = out[pnt];
uv.y = (pos.x - min.x) / diff;
uv.x = (std::atan2( pos.z - center.z, pos.y - center.y) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
uv.x = (std::atan2(pos.z - center.z, pos.y - center.y) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI;
}
}
else if (axis * base_axis_y >= angle_epsilon) {
} else if (axis * base_axis_y >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
const ai_real diff = max.y - min.y;
// just the same ...
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
aiVector3D& uv = out[pnt];
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D &pos = mesh->mVertices[pnt];
aiVector3D &uv = out[pnt];
uv.y = (pos.y - min.y) / diff;
uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
uv.x = (std::atan2(pos.x - center.x, pos.z - center.z) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI;
}
}
else if (axis * base_axis_z >= angle_epsilon) {
} else if (axis * base_axis_z >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
const ai_real diff = max.z - min.z;
// just the same ...
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
aiVector3D& uv = out[pnt];
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D &pos = mesh->mVertices[pnt];
aiVector3D &uv = out[pnt];
uv.y = (pos.z - min.z) / diff;
uv.x = (std::atan2( pos.y - center.y, pos.x - center.x) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
uv.x = (std::atan2(pos.y - center.y, pos.x - center.x) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI;
}
}
// slower code path in case the mapping axis is not one of the coordinate system axes
else {
aiMatrix4x4 mTrafo;
aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
aiMatrix4x4::FromToMatrix(axis, base_axis_y, mTrafo);
FindMeshCenterTransformed(mesh, center, min, max, mTrafo);
const ai_real diff = max.y - min.y;
// again the same, except we're applying a transformation now
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt){
const aiVector3D pos = mTrafo* mesh->mVertices[pnt];
aiVector3D& uv = out[pnt];
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D pos = mTrafo * mesh->mVertices[pnt];
aiVector3D &uv = out[pnt];
uv.y = (pos.y - min.y) / diff;
uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
uv.x = (std::atan2(pos.x - center.x, pos.z - center.z) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI;
}
}
// Now find and remove UV seams. A seam occurs if a face has a tcoord
// close to zero on the one side, and a tcoord close to one on the
// other side.
RemoveUVSeams(mesh,out);
RemoveUVSeams(mesh, out);
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
{
ai_real diffu,diffv;
void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh *mesh, const aiVector3D &axis, aiVector3D *out) {
ai_real diffu, diffv;
aiVector3D center, min, max;
// If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
// currently the mapping axis will always be one of x,y,z, except if the
// PretransformVertices step is used (it transforms the meshes into worldspace,
// thus changing the mapping axis)
if (axis * base_axis_x >= angle_epsilon) {
if (axis * base_axis_x >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
diffu = max.z - min.z;
diffv = max.y - min.y;
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
out[pnt].Set((pos.z - min.z) / diffu,(pos.y - min.y) / diffv,0.0);
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D &pos = mesh->mVertices[pnt];
out[pnt].Set((pos.z - min.z) / diffu, (pos.y - min.y) / diffv, 0.0);
}
}
else if (axis * base_axis_y >= angle_epsilon) {
} else if (axis * base_axis_y >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
diffu = max.x - min.x;
diffv = max.z - min.z;
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0);
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D &pos = mesh->mVertices[pnt];
out[pnt].Set((pos.x - min.x) / diffu, (pos.z - min.z) / diffv, 0.0);
}
}
else if (axis * base_axis_z >= angle_epsilon) {
} else if (axis * base_axis_z >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
diffu = max.x - min.x;
diffv = max.y - min.y;
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
out[pnt].Set((pos.x - min.x) / diffu,(pos.y - min.y) / diffv,0.0);
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D &pos = mesh->mVertices[pnt];
out[pnt].Set((pos.x - min.x) / diffu, (pos.y - min.y) / diffv, 0.0);
}
}
// slower code path in case the mapping axis is not one of the coordinate system axes
else
{
else {
aiMatrix4x4 mTrafo;
aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
aiMatrix4x4::FromToMatrix(axis, base_axis_y, mTrafo);
FindMeshCenterTransformed(mesh, center, min, max, mTrafo);
diffu = max.x - min.x;
diffv = max.z - min.z;
// again the same, except we're applying a transformation now
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D pos = mTrafo * mesh->mVertices[pnt];
out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0);
out[pnt].Set((pos.x - min.x) / diffu, (pos.z - min.z) / diffv, 0.0);
}
}
@ -376,41 +337,38 @@ void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D&
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeBoxMapping( aiMesh*, aiVector3D* )
{
void ComputeUVMappingProcess::ComputeBoxMapping(aiMesh *, aiVector3D *) {
ASSIMP_LOG_ERROR("Mapping type currently not implemented");
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::Execute( aiScene* pScene)
{
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
*/
for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
{
// 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];
for (unsigned int a = 0; a < mat->mNumProperties;++a)
{
aiMaterialProperty* prop = mat->mProperties[a];
if (!::strcmp( prop->mKey.data, "$tex.mapping"))
{
aiTextureMapping& mapping = *((aiTextureMapping*)prop->mData);
if (aiTextureMapping_UV != mapping)
{
if (!DefaultLogger::isNullLogger())
{
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")) {
aiTextureMapping &mapping = *((aiTextureMapping *)prop->mData);
if (aiTextureMapping_UV != mapping) {
if (!DefaultLogger::isNullLogger()) {
ai_snprintf(buffer, 1024, "Found non-UV mapped texture (%s,%u). Mapping type: %s",
aiTextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex,
MappingTypeToString(mapping));
aiTextureTypeToString((aiTextureType)prop->mSemantic), prop->mIndex,
MappingTypeToString(mapping));
ASSIMP_LOG_INFO(buffer);
}
@ -418,70 +376,62 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
if (aiTextureMapping_OTHER == mapping)
continue;
MappingInfo info (mapping);
MappingInfo info(mapping);
// Get further properties - currently only the major axis
for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2)
{
aiMaterialProperty* prop2 = mat->mProperties[a2];
for (unsigned int a2 = 0; a2 < mat->mNumProperties; ++a2) {
aiMaterialProperty *prop2 = mat->mProperties[a2];
if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex)
continue;
if ( !::strcmp( prop2->mKey.data, "$tex.mapaxis")) {
info.axis = *((aiVector3D*)prop2->mData);
if (!::strcmp(prop2->mKey.data, "$tex.mapaxis")) {
info.axis = *((aiVector3D *)prop2->mData);
break;
}
}
unsigned int idx( 99999999 );
unsigned int idx(99999999);
// Check whether we have this mapping mode already
std::list<MappingInfo>::iterator it = std::find (mappingStack.begin(),mappingStack.end(), info);
if (mappingStack.end() != it)
{
std::list<MappingInfo>::iterator it = std::find(mappingStack.begin(), mappingStack.end(), info);
if (mappingStack.end() != it) {
idx = (*it).uv;
}
else
{
} else {
/* We have found a non-UV mapped texture. Now
* we need to find all meshes using this material
* that we can compute UV channels for them.
*/
for (unsigned int m = 0; m < pScene->mNumMeshes;++m)
{
aiMesh* mesh = pScene->mMeshes[m];
* we need to find all meshes using this material
* that we can compute UV channels for them.
*/
for (unsigned int m = 0; m < pScene->mNumMeshes; ++m) {
aiMesh *mesh = pScene->mMeshes[m];
unsigned int outIdx = 0;
if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == UINT_MAX ||
!mesh->mNumVertices)
{
if (mesh->mMaterialIndex != i || (outIdx = FindEmptyUVChannel(mesh)) == UINT_MAX ||
!mesh->mNumVertices) {
continue;
}
// Allocate output storage
aiVector3D* p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices];
aiVector3D *p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices];
switch (mapping)
{
switch (mapping) {
case aiTextureMapping_SPHERE:
ComputeSphereMapping(mesh,info.axis,p);
ComputeSphereMapping(mesh, info.axis, p);
break;
case aiTextureMapping_CYLINDER:
ComputeCylinderMapping(mesh,info.axis,p);
ComputeCylinderMapping(mesh, info.axis, p);
break;
case aiTextureMapping_PLANE:
ComputePlaneMapping(mesh,info.axis,p);
ComputePlaneMapping(mesh, info.axis, p);
break;
case aiTextureMapping_BOX:
ComputeBoxMapping(mesh,p);
ComputeBoxMapping(mesh, p);
break;
default:
ai_assert(false);
}
if (m && idx != outIdx)
{
if (m && idx != outIdx) {
ASSIMP_LOG_WARN("UV index mismatch. Not all meshes assigned to "
"this material have equal numbers of UV channels. The UV index stored in "
"the material structure does therefore not apply for all meshes. ");
"this material have equal numbers of UV channels. The UV index stored in "
"the material structure does therefore not apply for all meshes. ");
}
idx = outIdx;
}
@ -491,7 +441,7 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
// Update the material property list
mapping = aiTextureMapping_UV;
((aiMaterial*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex));
((aiMaterial *)mat)->AddProperty(&idx, 1, AI_MATKEY_UVWSRC(prop->mSemantic, prop->mIndex));
}
}
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -59,13 +59,10 @@ namespace Assimp {
/** ComputeUVMappingProcess - converts special mappings, such as spherical,
* cylindrical or boxed to proper UV coordinates for rendering.
*/
class ComputeUVMappingProcess : public BaseProcess
{
public:
ComputeUVMappingProcess();
~ComputeUVMappingProcess();
class ComputeUVMappingProcess : public BaseProcess {
public:
ComputeUVMappingProcess() = default;
~ComputeUVMappingProcess() override = default;
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
@ -73,14 +70,14 @@ public:
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
protected:
@ -125,8 +122,7 @@ protected:
private:
// temporary structure to describe a mapping
struct MappingInfo
{
struct MappingInfo {
explicit MappingInfo(aiTextureMapping _type)
: type (_type)
, axis (0.f,1.f,0.f)
@ -137,8 +133,7 @@ private:
aiVector3D axis;
unsigned int uv;
bool operator== (const MappingInfo& other)
{
bool operator== (const MappingInfo& other) {
return type == other.type && axis == other.axis;
}
};

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -79,14 +77,6 @@ void flipUVs(aiMeshType *pMesh) {
} // namespace
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
MakeLeftHandedProcess::MakeLeftHandedProcess() = default;
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
MakeLeftHandedProcess::~MakeLeftHandedProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool MakeLeftHandedProcess::IsActive(unsigned int pFlags) const {
@ -121,6 +111,12 @@ void MakeLeftHandedProcess::Execute(aiScene *pScene) {
ProcessAnimation(nodeAnim);
}
}
// process the cameras accordingly
for( unsigned int a = 0; a < pScene->mNumCameras; ++a)
{
ProcessCamera(pScene->mCameras[a]);
}
ASSIMP_LOG_DEBUG("MakeLeftHandedProcess finished");
}
@ -227,18 +223,18 @@ void MakeLeftHandedProcess::ProcessAnimation(aiNodeAnim *pAnim) {
// rotation keys
for (unsigned int a = 0; a < pAnim->mNumRotationKeys; a++) {
/* That's the safe version, but the float errors add up. So we try the short version instead
aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix();
rotmat.a3 = -rotmat.a3; rotmat.b3 = -rotmat.b3;
rotmat.c1 = -rotmat.c1; rotmat.c2 = -rotmat.c2;
aiQuaternion rotquat( rotmat);
pAnim->mRotationKeys[a].mValue = rotquat;
*/
pAnim->mRotationKeys[a].mValue.x *= -1.0f;
pAnim->mRotationKeys[a].mValue.y *= -1.0f;
}
}
// ------------------------------------------------------------------------------------------------
// Converts a single camera to left handed coordinates.
void MakeLeftHandedProcess::ProcessCamera( aiCamera* pCam)
{
pCam->mLookAt = ai_real(2.0f) * pCam->mPosition - pCam->mLookAt;
}
#endif // !! ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
#ifndef ASSIMP_BUILD_NO_FLIPUVS_PROCESS
// # FlipUVsProcess
@ -305,14 +301,6 @@ void FlipUVsProcess::ProcessMesh(aiMesh *pMesh) {
#ifndef ASSIMP_BUILD_NO_FLIPWINDING_PROCESS
// # FlipWindingOrderProcess
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FlipWindingOrderProcess::FlipWindingOrderProcess() = default;
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FlipWindingOrderProcess::~FlipWindingOrderProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FlipWindingOrderProcess::IsActive(unsigned int pFlags) const {

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -59,6 +58,7 @@ struct aiMesh;
struct aiNodeAnim;
struct aiNode;
struct aiMaterial;
struct aiCamera;
namespace Assimp {
@ -72,22 +72,18 @@ namespace Assimp {
*
* @note RH-LH and LH-RH is the same, so this class can be used for both
*/
class MakeLeftHandedProcess : public BaseProcess
{
class MakeLeftHandedProcess : public BaseProcess {
public:
MakeLeftHandedProcess();
~MakeLeftHandedProcess();
MakeLeftHandedProcess() = default;
~MakeLeftHandedProcess() override = default;
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
protected:
// -------------------------------------------------------------------
/** Recursively converts a node and all of its children
*/
@ -114,30 +110,36 @@ protected:
* @param pAnim The bone animation to transform
*/
void ProcessAnimation( aiNodeAnim* pAnim);
// -------------------------------------------------------------------
/** Converts a single camera to left handed coordinates.
* The camera viewing direction is inverted by reflecting mLookAt
* across mPosition.
* @param pCam The camera to convert
*/
void ProcessCamera( aiCamera* pCam);
};
// ---------------------------------------------------------------------------
/** Postprocessing step to flip the face order of the imported data
*/
class FlipWindingOrderProcess : public BaseProcess
{
class FlipWindingOrderProcess : public BaseProcess {
friend class Importer;
public:
/** Constructor to be privately used by Importer */
FlipWindingOrderProcess();
FlipWindingOrderProcess() = default;
/** Destructor, private as well */
~FlipWindingOrderProcess();
~FlipWindingOrderProcess() override = default;
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
public:
/** Some other types of post-processing require winding order flips */
static void ProcessMesh( aiMesh* pMesh);
};

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -43,42 +42,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// @file DeboneProcess.cpp
/** Implementation of the DeboneProcess post processing step */
// internal headers of the post-processing framework
#include "ProcessHelper.h"
#include "DeboneProcess.h"
#include <stdio.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
DeboneProcess::DeboneProcess()
{
mNumBones = 0;
mNumBonesCanDoWithout = 0;
mThreshold = AI_DEBONE_THRESHOLD;
mAllOrNone = false;
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
DeboneProcess::~DeboneProcess() = default;
DeboneProcess::DeboneProcess() : mNumBones(0), mNumBonesCanDoWithout(0), mThreshold(AI_DEBONE_THRESHOLD), mAllOrNone(false) {}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool DeboneProcess::IsActive( unsigned int pFlags) const
{
bool DeboneProcess::IsActive( unsigned int pFlags) const {
return (pFlags & aiProcess_Debone) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void DeboneProcess::SetupProperties(const Importer* pImp)
{
void DeboneProcess::SetupProperties(const Importer* pImp) {
// get the current value of the property
mAllOrNone = pImp->GetPropertyInteger(AI_CONFIG_PP_DB_ALL_OR_NONE,0)?true:false;
mThreshold = pImp->GetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD,AI_DEBONE_THRESHOLD);
@ -86,8 +69,7 @@ void DeboneProcess::SetupProperties(const Importer* pImp)
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void DeboneProcess::Execute( aiScene* pScene)
{
void DeboneProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("DeboneProcess begin");
if(!pScene->mNumMeshes) {
@ -104,7 +86,7 @@ void DeboneProcess::Execute( aiScene* pScene)
if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones)) {
for(unsigned int a = 0; a < pScene->mNumMeshes; a++) {
if(splitList[a]) {
numSplits++;
++numSplits;
}
}
}
@ -117,10 +99,8 @@ void DeboneProcess::Execute( aiScene* pScene)
// build a new array of meshes for the scene
std::vector<aiMesh*> meshes;
for(unsigned int a=0;a<pScene->mNumMeshes;a++)
{
for (unsigned int a=0;a<pScene->mNumMeshes; ++a) {
aiMesh* srcMesh = pScene->mMeshes[a];
std::vector<std::pair<aiMesh*,const aiBone*> > newMeshes;
if(splitList[a]) {
@ -133,13 +113,13 @@ void DeboneProcess::Execute( aiScene* pScene)
// store new meshes and indices of the new meshes
for(unsigned int b=0;b<newMeshes.size();b++) {
const aiString *find = newMeshes[b].second?&newMeshes[b].second->mName:0;
const aiString *find = newMeshes[b].second ? &newMeshes[b].second->mName : nullptr;
aiNode *theNode = find?pScene->mRootNode->FindNode(*find):0;
aiNode *theNode = find ? pScene->mRootNode->FindNode(*find) : nullptr;
std::pair<unsigned int,aiNode*> push_pair(static_cast<unsigned int>(meshes.size()),theNode);
mSubMeshIndices[a].push_back(push_pair);
meshes.push_back(newMeshes[b].first);
mSubMeshIndices[a].emplace_back(push_pair);
meshes.emplace_back(newMeshes[b].first);
out+=newMeshes[b].first->mNumBones;
}
@ -150,10 +130,9 @@ void DeboneProcess::Execute( aiScene* pScene)
// and destroy the source mesh. It should be completely contained inside the new submeshes
delete srcMesh;
}
else {
} else {
// Mesh is kept unchanged - store it's new place in the mesh array
mSubMeshIndices[a].emplace_back(static_cast<unsigned int>(meshes.size()), (aiNode *)0);
mSubMeshIndices[a].emplace_back(static_cast<unsigned int>(meshes.size()), (aiNode *)nullptr);
meshes.push_back(srcMesh);
}
}
@ -173,8 +152,7 @@ void DeboneProcess::Execute( aiScene* pScene)
// ------------------------------------------------------------------------------------------------
// Counts bones total/removable in a given mesh.
bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh)
{
bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh) {
if(!pMesh->HasBones()) {
return false;
}
@ -193,25 +171,23 @@ bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh)
for(unsigned int i=0;i<pMesh->mNumBones;i++) {
for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++) {
float w = pMesh->mBones[i]->mWeights[j].mWeight;
if(w==0.0f) {
if (w == 0.0f) {
continue;
}
unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId;
if(w>=mThreshold) {
if(vertexBones[vid]!=cUnowned) {
if(vertexBones[vid]==i) //double entry
{
if (w >= mThreshold) {
if (vertexBones[vid] != cUnowned) {
//double entry
if(vertexBones[vid]==i) {
ASSIMP_LOG_WARN("Encountered double entry in bone weights");
}
else //TODO: track attraction in order to break tie
{
} else {
//TODO: track attraction in order to break tie
vertexBones[vid] = cCoowned;
}
}
else vertexBones[vid] = i;
} else {
vertexBones[vid] = i;
}
}
if(!isBoneNecessary[i]) {
@ -227,13 +203,16 @@ bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh)
if(isInterstitialRequired) {
for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]];
for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
for (unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]];
if(v!=w) {
if(v<pMesh->mNumBones) isBoneNecessary[v] = true;
if(w<pMesh->mNumBones) isBoneNecessary[w] = true;
if (v != w) {
if(v<pMesh->mNumBones) {
isBoneNecessary[v] = true;
}
if (w<pMesh->mNumBones) {
isBoneNecessary[w] = true;
}
}
}
}
@ -252,8 +231,7 @@ bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh)
// ------------------------------------------------------------------------------------------------
// Splits the given mesh by bone count.
void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const
{
void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const {
// same deal here as ConsiderMesh basically
std::vector<bool> isBoneNecessary(pMesh->mNumBones,false);
@ -341,7 +319,7 @@ void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector< std::pair< aiMe
}
aiMesh *baseMesh = MakeSubmesh(pMesh,subFaces,0);
std::pair<aiMesh*,const aiBone*> push_pair(baseMesh,(const aiBone*)0);
std::pair<aiMesh *, const aiBone *> push_pair(baseMesh, (const aiBone *)nullptr);
poNewMeshes.push_back(push_pair);
}
@ -371,8 +349,7 @@ void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector< std::pair< aiMe
// ------------------------------------------------------------------------------------------------
// Recursively updates the node's mesh list to account for the changed mesh list
void DeboneProcess::UpdateNode(aiNode* pNode) const
{
void DeboneProcess::UpdateNode(aiNode* pNode) const {
// rebuild the node's mesh index list
std::vector<unsigned int> newMeshList;
@ -382,9 +359,7 @@ void DeboneProcess::UpdateNode(aiNode* pNode) const
unsigned int m = static_cast<unsigned int>(pNode->mNumMeshes), n = static_cast<unsigned int>(mSubMeshIndices.size());
// first pass, look for meshes which have not moved
for(unsigned int a=0;a<m;a++) {
unsigned int srcIndex = pNode->mMeshes[a];
const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[srcIndex];
unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size());
@ -398,8 +373,7 @@ void DeboneProcess::UpdateNode(aiNode* pNode) const
// second pass, collect deboned meshes
for(unsigned int a=0;a<n;a++)
{
for(unsigned int a=0;a<n;a++) {
const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[a];
unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size());
@ -430,8 +404,7 @@ void DeboneProcess::UpdateNode(aiNode* pNode) const
// ------------------------------------------------------------------------------------------------
// Apply the node transformation to a mesh
void DeboneProcess::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const
{
void DeboneProcess::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const {
// Check whether we need to transform the coordinates at all
if (!mat.IsIdentity()) {

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -70,7 +70,7 @@ namespace Assimp {
class DeboneProcess : public BaseProcess {
public:
DeboneProcess();
~DeboneProcess();
~DeboneProcess() override = default;
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag.
@ -79,14 +79,14 @@ public:
* @return true if the process is present in this flag fields,
* false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** Called prior to ExecuteOnScene().
* The function is a request to the process to update its configuration
* basing on the Importer's configuration property list.
*/
void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer* pImp) override;
protected:
// -------------------------------------------------------------------
@ -94,7 +94,7 @@ protected:
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
/** Counts bones total/removable in a given mesh.

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
@ -42,35 +42,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file Implementation of the post processing step to drop face
* normals for all imported faces.
*/
* normals for all imported faces.
*/
#include "DropFaceNormalsProcess.h"
#include <assimp/Exceptional.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Exceptional.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
DropFaceNormalsProcess::DropFaceNormalsProcess() = default;
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
DropFaceNormalsProcess::~DropFaceNormalsProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool DropFaceNormalsProcess::IsActive( unsigned int pFlags) const {
return (pFlags & aiProcess_DropNormals) != 0;
bool DropFaceNormalsProcess::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_DropNormals) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void DropFaceNormalsProcess::Execute( aiScene* pScene) {
void DropFaceNormalsProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("DropFaceNormalsProcess begin");
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
@ -78,21 +69,21 @@ void DropFaceNormalsProcess::Execute( aiScene* pScene) {
}
bool bHas = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
bHas |= this->DropMeshFaceNormals( pScene->mMeshes[a]);
for (unsigned int a = 0; a < pScene->mNumMeshes; a++) {
bHas |= this->DropMeshFaceNormals(pScene->mMeshes[a]);
}
if (bHas) {
if (bHas) {
ASSIMP_LOG_INFO("DropFaceNormalsProcess finished. "
"Face normals have been removed");
"Face normals have been removed");
} else {
ASSIMP_LOG_DEBUG("DropFaceNormalsProcess finished. "
"No normals were present");
"No normals were present");
}
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* mesh) {
bool DropFaceNormalsProcess::DropMeshFaceNormals(aiMesh *mesh) {
ai_assert(nullptr != mesh);
if (nullptr == mesh->mNormals) {

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -55,8 +55,8 @@ namespace Assimp {
*/
class ASSIMP_API_WINONLY DropFaceNormalsProcess : public BaseProcess {
public:
DropFaceNormalsProcess();
~DropFaceNormalsProcess();
DropFaceNormalsProcess() = default;
~DropFaceNormalsProcess() override = default;
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
@ -64,15 +64,14 @@ public:
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
private:
bool DropMeshFaceNormals(aiMesh* pcMesh);

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -46,13 +46,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "ProcessHelper.h"
#include <fstream>
#include <algorithm>
using namespace Assimp;
EmbedTexturesProcess::EmbedTexturesProcess() = default;
EmbedTexturesProcess::~EmbedTexturesProcess() = default;
bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_EmbedTextures) != 0;
}
@ -95,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,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -62,21 +62,23 @@ namespace Assimp {
class ASSIMP_API EmbedTexturesProcess : public BaseProcess {
public:
/// The default class constructor.
EmbedTexturesProcess();
EmbedTexturesProcess() = default;
/// The class destructor.
virtual ~EmbedTexturesProcess();
~EmbedTexturesProcess() override = default;
/// Overwritten, @see BaseProcess
virtual bool IsActive(unsigned int pFlags) const;
bool IsActive(unsigned int pFlags) const override;
/// Overwritten, @see BaseProcess
virtual void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer* pImp) override;
/// Overwritten, @see BaseProcess
virtual void Execute(aiScene* pScene);
virtual 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-2024, assimp team
All rights reserved.
@ -41,10 +41,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file FindDegenerates.cpp
* @brief Implementation of the FindDegenerates post-process step.
*/
*/
#include "ProcessHelper.h"
#include "FindDegenerates.h"
#include "Geometry/GeometryUtils.h"
#include "ProcessHelper.h"
#include <assimp/Exceptional.h>
@ -53,39 +54,35 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// Correct node indices to meshes and remove references to deleted mesh
static void updateSceneGraph(aiNode* pNode, const std::unordered_map<unsigned int, unsigned int>& meshMap);
static void updateSceneGraph(aiNode *pNode, const std::unordered_map<unsigned int, unsigned int> &meshMap);
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FindDegeneratesProcess::FindDegeneratesProcess() :
mConfigRemoveDegenerates( false ),
mConfigCheckAreaOfTriangle( false ){
mConfigRemoveDegenerates(false),
mConfigCheckAreaOfTriangle(false) {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FindDegeneratesProcess::~FindDegeneratesProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const {
bool FindDegeneratesProcess::IsActive(unsigned int pFlags) const {
return 0 != (pFlags & aiProcess_FindDegenerates);
}
// ------------------------------------------------------------------------------------------------
// Setup import configuration
void FindDegeneratesProcess::SetupProperties(const Importer* pImp) {
void FindDegeneratesProcess::SetupProperties(const Importer *pImp) {
// Get the current value of AI_CONFIG_PP_FD_REMOVE
mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) );
mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE, 0));
mConfigCheckAreaOfTriangle = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA));
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FindDegeneratesProcess::Execute( aiScene* pScene) {
void FindDegeneratesProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin");
if ( nullptr == pScene) {
if (nullptr == pScene) {
return;
}
@ -115,7 +112,7 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished");
}
static void updateSceneGraph(aiNode* pNode, const std::unordered_map<unsigned int, unsigned int>& meshMap) {
static void updateSceneGraph(aiNode *pNode, const std::unordered_map<unsigned int, unsigned int> &meshMap) {
unsigned int targetIndex = 0;
for (unsigned i = 0; i < pNode->mNumMeshes; ++i) {
const unsigned int sourceMeshIndex = pNode->mMeshes[i];
@ -126,102 +123,75 @@ static void updateSceneGraph(aiNode* pNode, const std::unordered_map<unsigned in
}
}
pNode->mNumMeshes = targetIndex;
//recurse to all children
// recurse to all children
for (unsigned i = 0; i < pNode->mNumChildren; ++i) {
updateSceneGraph(pNode->mChildren[i], meshMap);
}
}
static ai_real heron( ai_real a, ai_real b, ai_real c ) {
ai_real s = (a + b + c) / 2;
ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 );
return area;
}
static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) {
const ai_real lx = ( vB.x - vA.x );
const ai_real ly = ( vB.y - vA.y );
const ai_real lz = ( vB.z - vA.z );
ai_real a = lx*lx + ly*ly + lz*lz;
ai_real d = pow( a, (ai_real)0.5 );
return d;
}
static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) {
ai_real area = 0;
aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] );
aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] );
aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] );
ai_real a( distance3D( vA, vB ) );
ai_real b( distance3D( vB, vC ) );
ai_real c( distance3D( vC, vA ) );
area = heron( a, b, c );
return area;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported mesh
bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
bool FindDegeneratesProcess::ExecuteOnMesh(aiMesh *mesh) {
mesh->mPrimitiveTypes = 0;
std::vector<bool> remove_me;
if (mConfigRemoveDegenerates) {
remove_me.resize( mesh->mNumFaces, false );
remove_me.resize(mesh->mNumFaces, false);
}
unsigned int deg = 0, limit;
for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) {
aiFace& face = mesh->mFaces[a];
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.
limit = face.mNumIndices;
if (face.mNumIndices > 4) {
limit = std::min( limit, i+2 );
limit = std::min(limit, i + 2);
}
for (unsigned int t = i+1; t < limit; ++t) {
if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) {
for (unsigned int t = i + 1; t < limit; ++t) {
if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]]) {
// we have found a matching vertex position
// remove the corresponding index from the array
--face.mNumIndices;
--limit;
for (unsigned int m = t; m < face.mNumIndices; ++m) {
face.mIndices[ m ] = face.mIndices[ m+1 ];
face.mIndices[m] = face.mIndices[m + 1];
}
--t;
// NOTE: we set the removed vertex index to an unique value
// to make sure the developer gets notified when his
// application attempts to access this data.
face.mIndices[ face.mNumIndices ] = 0xdeadbeef;
face.mIndices[face.mNumIndices] = 0xdeadbeef;
if(first) {
if (first) {
++deg;
first = false;
}
if ( mConfigRemoveDegenerates ) {
remove_me[ a ] = true;
if (mConfigRemoveDegenerates) {
remove_me[a] = true;
goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
}
}
}
if ( mConfigCheckAreaOfTriangle ) {
if ( face.mNumIndices == 3 ) {
ai_real area = calculateAreaOfTriangle( face, mesh );
if (mConfigCheckAreaOfTriangle) {
if (face.mNumIndices == 3) {
ai_real area = GeometryUtils::calculateAreaOfTriangle(face, mesh);
if (area < ai_epsilon) {
if ( mConfigRemoveDegenerates ) {
remove_me[ a ] = true;
if (mConfigRemoveDegenerates) {
remove_me[a] = true;
++deg;
goto evil_jump_outside;
}
@ -233,8 +203,7 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
}
// We need to update the primitive flags array of the mesh.
switch (face.mNumIndices)
{
switch (face.mNumIndices) {
case 1u:
mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
break;
@ -248,30 +217,28 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
break;
};
evil_jump_outside:
evil_jump_outside:
continue;
}
// If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
if (mConfigRemoveDegenerates && deg) {
unsigned int n = 0;
for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
{
aiFace& face_src = mesh->mFaces[a];
for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
aiFace &face_src = mesh->mFaces[a];
if (!remove_me[a]) {
aiFace& face_dest = mesh->mFaces[n++];
aiFace &face_dest = mesh->mFaces[n++];
// Do a manual copy, keep the index array
face_dest.mNumIndices = face_src.mNumIndices;
face_dest.mIndices = face_src.mIndices;
face_dest.mIndices = face_src.mIndices;
if (&face_src != &face_dest) {
// clear source
face_src.mNumIndices = 0;
face_src.mIndices = nullptr;
}
}
else {
} else {
// Otherwise delete it if we don't need this face
delete[] face_src.mIndices;
face_src.mIndices = nullptr;
@ -281,15 +248,15 @@ evil_jump_outside:
// Just leave the rest of the array unreferenced, we don't care for now
mesh->mNumFaces = n;
if (!mesh->mNumFaces) {
//The whole mesh consists of degenerated faces
//signal upward, that this mesh should be deleted.
// The whole mesh consists of degenerated faces
// signal upward, that this mesh should be deleted.
ASSIMP_LOG_VERBOSE_DEBUG("FindDegeneratesProcess removed a mesh full of degenerated primitives");
return true;
}
}
if (deg && !DefaultLogger::isNullLogger()) {
ASSIMP_LOG_WARN( "Found ", deg, " degenerated primitives");
ASSIMP_LOG_WARN("Found ", deg, " degenerated primitives");
}
return false;
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -59,19 +59,19 @@ namespace Assimp {
class ASSIMP_API FindDegeneratesProcess : public BaseProcess {
public:
FindDegeneratesProcess();
~FindDegeneratesProcess();
~FindDegeneratesProcess() override = default;
// -------------------------------------------------------------------
// Check whether step is active
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
// Execute step on a given scene
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
// Setup import settings
void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer* pImp) override;
// -------------------------------------------------------------------
// Execute step on a given mesh
@ -105,23 +105,19 @@ private:
bool mConfigCheckAreaOfTriangle;
};
inline
void FindDegeneratesProcess::EnableInstantRemoval(bool enabled) {
inline void FindDegeneratesProcess::EnableInstantRemoval(bool enabled) {
mConfigRemoveDegenerates = enabled;
}
inline
bool FindDegeneratesProcess::IsInstantRemoval() const {
inline bool FindDegeneratesProcess::IsInstantRemoval() const {
return mConfigRemoveDegenerates;
}
inline
void FindDegeneratesProcess::EnableAreaCheck( bool enabled ) {
inline void FindDegeneratesProcess::EnableAreaCheck( bool enabled ) {
mConfigCheckAreaOfTriangle = enabled;
}
inline
bool FindDegeneratesProcess::isAreaCheckEnabled() const {
inline bool FindDegeneratesProcess::isAreaCheckEnabled() const {
return mConfigCheckAreaOfTriangle;
}

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
@ -58,10 +58,6 @@ FindInstancesProcess::FindInstancesProcess()
: configSpeedFlag (false)
{}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FindInstancesProcess::~FindInstancesProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FindInstancesProcess::IsActive( unsigned int pFlags) const

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -50,7 +50,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "PostProcessing/ProcessHelper.h"
class FindInstancesProcessTest;
namespace Assimp {
namespace Assimp {
// -------------------------------------------------------------------------------
/** @brief Get a pseudo(!)-hash representing a mesh.
@ -60,8 +61,7 @@ namespace Assimp {
* @param in Input mesh
* @return Hash.
*/
inline
uint64_t GetMeshHash(aiMesh* in) {
inline uint64_t GetMeshHash(aiMesh* in) {
ai_assert(nullptr != in);
// ... get an unique value representing the vertex format of the mesh
@ -83,8 +83,7 @@ uint64_t GetMeshHash(aiMesh* in) {
* @param e Epsilon
* @return true if the arrays are identical
*/
inline
bool CompareArrays(const aiVector3D* first, const aiVector3D* second,
inline bool CompareArrays(const aiVector3D* first, const aiVector3D* second,
unsigned int size, float e) {
for (const aiVector3D* end = first+size; first != end; ++first,++second) {
if ( (*first - *second).SquareLength() >= e)
@ -107,31 +106,27 @@ inline bool CompareArrays(const aiColor4D* first, const aiColor4D* second,
// ---------------------------------------------------------------------------
/** @brief A post-processing steps to search for instanced meshes
*/
class FindInstancesProcess : public BaseProcess
{
class FindInstancesProcess : public BaseProcess {
public:
FindInstancesProcess();
~FindInstancesProcess();
~FindInstancesProcess() override = default;
public:
// -------------------------------------------------------------------
// Check whether step is active in given flags combination
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
// Execute step on a given scene
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
// Setup properties prior to executing the process
void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer* pImp) override;
private:
bool configSpeedFlag;
}; // ! end class FindInstancesProcess
} // ! end namespace Assimp
#endif // !! AI_FINDINSTANCES_H_INC

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -60,10 +60,6 @@ FindInvalidDataProcess::FindInvalidDataProcess() :
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FindInvalidDataProcess::~FindInvalidDataProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FindInvalidDataProcess::IsActive(unsigned int pFlags) const {
@ -86,6 +82,9 @@ void UpdateMeshReferences(aiNode *node, const std::vector<unsigned int> &meshMap
for (unsigned int a = 0; a < node->mNumMeshes; ++a) {
unsigned int ref = node->mMeshes[a];
if (ref >= meshMapping.size())
throw DeadlyImportError("Invalid mesh ref");
if (UINT_MAX != (ref = meshMapping[ref])) {
node->mMeshes[out++] = ref;
}
@ -147,7 +146,13 @@ void FindInvalidDataProcess::Execute(aiScene *pScene) {
// we need to remove some meshes.
// therefore we'll also need to remove all references
// to them from the scenegraph
UpdateMeshReferences(pScene->mRootNode, meshMapping);
try {
UpdateMeshReferences(pScene->mRootNode, meshMapping);
} catch (const std::exception&) {
// fix the real number of meshes otherwise we'll get double free in the scene destructor
pScene->mNumMeshes = real;
throw;
}
pScene->mNumMeshes = real;
}
@ -268,7 +273,8 @@ void FindInvalidDataProcess::ProcessAnimation(aiAnimation *anim) {
void FindInvalidDataProcess::ProcessAnimationChannel(aiNodeAnim *anim) {
ai_assert(nullptr != anim);
if (anim->mNumPositionKeys == 0 && anim->mNumRotationKeys == 0 && anim->mNumScalingKeys == 0) {
ai_assert_entry();
ASSIMP_LOG_ERROR("Invalid node anuimation instance detected.");
return;
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -64,35 +64,37 @@ namespace Assimp {
* which have zero normal vectors. */
class ASSIMP_API FindInvalidDataProcess : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
FindInvalidDataProcess();
~FindInvalidDataProcess();
~FindInvalidDataProcess() override = default;
// -------------------------------------------------------------------
//
bool IsActive(unsigned int pFlags) const;
/// Returns active state.
bool IsActive(unsigned int pFlags) const override;
// -------------------------------------------------------------------
// Setup import settings
void SetupProperties(const Importer *pImp);
/// Setup import settings
void SetupProperties(const Importer *pImp) override;
// -------------------------------------------------------------------
// Run the step
void Execute(aiScene *pScene);
/// Run the step
void Execute(aiScene *pScene) override;
// -------------------------------------------------------------------
/** Executes the post-processing step on the given mesh
* @param pMesh The mesh to process.
* @return 0 - nothing, 1 - removed sth, 2 - please delete me */
/// Executes the post-processing step on the given mesh
/// @param pMesh The mesh to process.
/// @return 0 - nothing, 1 - removed sth, 2 - please delete me */
int ProcessMesh(aiMesh *pMesh);
// -------------------------------------------------------------------
/** Executes the post-processing step on the given animation
* @param anim The animation to process. */
/// Executes the post-processing step on the given animation
/// @param anim The animation to process. */
void ProcessAnimation(aiAnimation *anim);
// -------------------------------------------------------------------
/** Executes the post-processing step on the given anim channel
* @param anim The animation channel to process.*/
/// Executes the post-processing step on the given anim channel
/// @param anim The animation channel to process.*/
void ProcessAnimationChannel(aiNodeAnim *anim);
private:

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
@ -56,26 +56,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FixInfacingNormalsProcess::FixInfacingNormalsProcess() = default;
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FixInfacingNormalsProcess::~FixInfacingNormalsProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FixInfacingNormalsProcess::IsActive( unsigned int pFlags) const
{
bool FixInfacingNormalsProcess::IsActive( unsigned int pFlags) const {
return (pFlags & aiProcess_FixInfacingNormals) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FixInfacingNormalsProcess::Execute( aiScene* pScene)
{
void FixInfacingNormalsProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess begin");
bool bHas( false );

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -49,8 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
struct aiMesh;
namespace Assimp
{
namespace Assimp {
// ---------------------------------------------------------------------------
/** The FixInfacingNormalsProcess tries to determine whether the normal
@ -59,8 +58,10 @@ namespace Assimp
*/
class FixInfacingNormalsProcess : public BaseProcess {
public:
FixInfacingNormalsProcess();
~FixInfacingNormalsProcess();
// -------------------------------------------------------------------
/// The default class constructor / destructor.
FixInfacingNormalsProcess() = default;
~FixInfacingNormalsProcess() override = default;
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
@ -68,14 +69,14 @@ public:
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
protected:

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -48,10 +48,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
GenBoundingBoxesProcess::GenBoundingBoxesProcess() = default;
GenBoundingBoxesProcess::~GenBoundingBoxesProcess() = default;
bool GenBoundingBoxesProcess::IsActive(unsigned int pFlags) const {
return 0 != ( pFlags & aiProcess_GenBoundingBoxes );
}

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, 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,18 +54,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
/** Post-processing process to find axis-aligned bounding volumes for amm meshes
* used in a scene
/**
* @brief Post-processing process to find axis-aligned bounding volumes for amm meshes
* used in a scene.
*/
class ASSIMP_API GenBoundingBoxesProcess : public BaseProcess {
public:
/// The class constructor.
GenBoundingBoxesProcess();
/// The class destructor.
~GenBoundingBoxesProcess();
/// Will return true, if aiProcess_GenBoundingBoxes is defined.
// -------------------------------------------------------------------
/// The default class constructor / destructor.
GenBoundingBoxesProcess() = default;
~GenBoundingBoxesProcess() override = default;
// -------------------------------------------------------------------
/// @brief Will return true, if aiProcess_GenBoundingBoxes is defined.
bool IsActive(unsigned int pFlags) const override;
/// The execution callback.
// -------------------------------------------------------------------
/// @brief The execution callback.
void Execute(aiScene* pScene) override;
};

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, 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,23 +54,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
GenFaceNormalsProcess::GenFaceNormalsProcess() = default;
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
GenFaceNormalsProcess::~GenFaceNormalsProcess() = default;
// ------------------------------------------------------------------------------------------------
// 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;
leftHanded_ = (pFlags & aiProcess_MakeLeftHanded) != 0;
return (pFlags & aiProcess_GenNormals) != 0;
}
// ------------------------------------------------------------------------------------------------
// 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");
@ -95,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_) {
@ -114,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++) {
@ -123,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;
}
@ -131,13 +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]];
if (flippedWindingOrder_)
std::swap( pV2, pV3 );
// 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,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -47,40 +47,39 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "Common/BaseProcess.h"
#include <assimp/mesh.h>
namespace Assimp
{
namespace Assimp {
// ---------------------------------------------------------------------------
/** The GenFaceNormalsProcess computes face normals for all faces of all meshes
*/
class ASSIMP_API_WINONLY GenFaceNormalsProcess : public BaseProcess
{
/**
* @brief The GenFaceNormalsProcess computes face normals for all faces of all meshes
*/
class ASSIMP_API_WINONLY GenFaceNormalsProcess : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
GenFaceNormalsProcess() = default;
~GenFaceNormalsProcess() override = default;
GenFaceNormalsProcess();
~GenFaceNormalsProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
private:
bool GenMeshFaceNormals(aiMesh* pcMesh);
mutable bool force_ = false;
mutable bool flippedWindingOrder_ = false;
mutable bool leftHanded_ = false;
};
} // end of namespace Assimp

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -60,15 +58,12 @@ GenVertexNormalsProcess::GenVertexNormalsProcess() :
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
GenVertexNormalsProcess::~GenVertexNormalsProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool GenVertexNormalsProcess::IsActive(unsigned int pFlags) const {
force_ = (pFlags & aiProcess_ForceGenNormals) != 0;
flippedWindingOrder_ = (pFlags & aiProcess_FlipWindingOrder) != 0;
leftHanded_ = (pFlags & aiProcess_MakeLeftHanded) != 0;
return (pFlags & aiProcess_GenSmoothNormals) != 0;
}
@ -108,10 +103,11 @@ void GenVertexNormalsProcess::Execute(aiScene *pScene) {
// Executes the post processing step on the given imported data.
bool GenVertexNormalsProcess::GenMeshVertexNormals(aiMesh *pMesh, unsigned int meshIndex) {
if (nullptr != pMesh->mNormals) {
if (force_)
delete[] pMesh->mNormals;
else
if (!force_) {
return false;
}
delete[] pMesh->mNormals;
pMesh->mNormals = nullptr;
}
// If the mesh consists of lines and/or points but not of
@ -141,8 +137,11 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals(aiMesh *pMesh, unsigned int m
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]];
if (flippedWindingOrder_)
std::swap( pV2, pV3 );
// 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_) {
std::swap(pV2, pV3);
}
const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe();
for (unsigned int i = 0; i < face.mNumIndices; ++i) {

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -60,8 +60,10 @@ namespace Assimp {
*/
class ASSIMP_API GenVertexNormalsProcess : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
GenVertexNormalsProcess();
~GenVertexNormalsProcess();
~GenVertexNormalsProcess() override = default;
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag.
@ -70,22 +72,21 @@ public:
* @return true if the process is present in this flag fields,
* false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** Called prior to ExecuteOnScene().
* The function is a request to the process to update its configuration
* basing on the Importer's configuration property list.
*/
void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer* pImp) override;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// setter for configMaxAngle
inline void SetMaxSmoothAngle(ai_real f) {
@ -105,6 +106,7 @@ private:
ai_real configMaxAngle;
mutable bool force_ = false;
mutable bool flippedWindingOrder_ = false;
mutable bool leftHanded_ = false;
};
} // end of namespace Assimp

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -59,35 +57,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdio.h>
#include <stack>
using namespace Assimp;
namespace Assimp {
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
ImproveCacheLocalityProcess::ImproveCacheLocalityProcess()
: mConfigCacheDepth(PP_ICL_PTCACHE_SIZE) {
ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() :
mConfigCacheDepth(PP_ICL_PTCACHE_SIZE) {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
ImproveCacheLocalityProcess::~ImproveCacheLocalityProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool ImproveCacheLocalityProcess::IsActive( unsigned int pFlags) const {
bool ImproveCacheLocalityProcess::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_ImproveCacheLocality) != 0;
}
// ------------------------------------------------------------------------------------------------
// Setup configuration
void ImproveCacheLocalityProcess::SetupProperties(const Importer* pImp) {
void ImproveCacheLocalityProcess::SetupProperties(const Importer *pImp) {
// AI_CONFIG_PP_ICL_PTCACHE_SIZE controls the target cache size for the optimizer
mConfigCacheDepth = pImp->GetPropertyInteger(AI_CONFIG_PP_ICL_PTCACHE_SIZE,PP_ICL_PTCACHE_SIZE);
mConfigCacheDepth = pImp->GetPropertyInteger(AI_CONFIG_PP_ICL_PTCACHE_SIZE, PP_ICL_PTCACHE_SIZE);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void ImproveCacheLocalityProcess::Execute( aiScene* pScene) {
void ImproveCacheLocalityProcess::Execute(aiScene *pScene) {
if (!pScene->mNumMeshes) {
ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess skipped; there are no meshes");
return;
@ -97,11 +91,11 @@ void ImproveCacheLocalityProcess::Execute( aiScene* pScene) {
float out = 0.f;
unsigned int numf = 0, numm = 0;
for( unsigned int a = 0; a < pScene->mNumMeshes; ++a ){
const float res = ProcessMesh( pScene->mMeshes[a],a);
for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
const float res = ProcessMesh(pScene->mMeshes[a], a);
if (res) {
numf += pScene->mMeshes[a]->mNumFaces;
out += res;
out += res;
++numm;
}
}
@ -113,9 +107,54 @@ void ImproveCacheLocalityProcess::Execute( aiScene* pScene) {
}
}
// ------------------------------------------------------------------------------------------------
static ai_real calculateInputACMR(aiMesh *pMesh, const aiFace *const pcEnd,
unsigned int configCacheDepth, unsigned int meshNum) {
ai_real fACMR = 0.0f;
unsigned int *piFIFOStack = new unsigned int[configCacheDepth];
memset(piFIFOStack, 0xff, configCacheDepth * sizeof(unsigned int));
unsigned int *piCur = piFIFOStack;
const unsigned int *const piCurEnd = piFIFOStack + configCacheDepth;
// count the number of cache misses
unsigned int iCacheMisses = 0;
for (const aiFace *pcFace = pMesh->mFaces; pcFace != pcEnd; ++pcFace) {
for (unsigned int qq = 0; qq < 3; ++qq) {
bool bInCache = false;
for (unsigned int *pp = piFIFOStack; pp < piCurEnd; ++pp) {
if (*pp == pcFace->mIndices[qq]) {
// the vertex is in cache
bInCache = true;
break;
}
}
if (!bInCache) {
++iCacheMisses;
if (piCurEnd == piCur) {
piCur = piFIFOStack;
}
*piCur++ = pcFace->mIndices[qq];
}
}
}
delete[] piFIFOStack;
fACMR = (ai_real)iCacheMisses / pMesh->mNumFaces;
if (3.0 == fACMR) {
char szBuff[128]; // should be sufficiently large in every case
// the JoinIdenticalVertices process has not been executed on this
// mesh, otherwise this value would normally be at least minimally
// smaller than 3.0 ...
ai_snprintf(szBuff, 128, "Mesh %u: Not suitable for vcache optimization", meshNum);
ASSIMP_LOG_WARN(szBuff);
return static_cast<ai_real>(0.f);
}
return fACMR;
}
// ------------------------------------------------------------------------------------------------
// Improves the cache coherency of a specific mesh
ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum) {
ai_real ImproveCacheLocalityProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshNum) {
// TODO: rewrite this to use std::vector or boost::shared_array
ai_assert(nullptr != pMesh);
@ -130,91 +169,57 @@ ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int me
return static_cast<ai_real>(0.f);
}
if(pMesh->mNumVertices <= mConfigCacheDepth) {
if (pMesh->mNumVertices <= mConfigCacheDepth) {
return static_cast<ai_real>(0.f);
}
ai_real fACMR = 3.f;
const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces;
const aiFace *const pcEnd = pMesh->mFaces + pMesh->mNumFaces;
// Input ACMR is for logging purposes only
if (!DefaultLogger::isNullLogger()) {
unsigned int* piFIFOStack = new unsigned int[mConfigCacheDepth];
memset(piFIFOStack,0xff,mConfigCacheDepth*sizeof(unsigned int));
unsigned int* piCur = piFIFOStack;
const unsigned int* const piCurEnd = piFIFOStack + mConfigCacheDepth;
// count the number of cache misses
unsigned int iCacheMisses = 0;
for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace) {
for (unsigned int qq = 0; qq < 3;++qq) {
bool bInCache = false;
for (unsigned int* pp = piFIFOStack;pp < piCurEnd;++pp) {
if (*pp == pcFace->mIndices[qq]) {
// the vertex is in cache
bInCache = true;
break;
}
}
if (!bInCache) {
++iCacheMisses;
if (piCurEnd == piCur) {
piCur = piFIFOStack;
}
*piCur++ = pcFace->mIndices[qq];
}
}
}
delete[] piFIFOStack;
fACMR = (ai_real) iCacheMisses / pMesh->mNumFaces;
if (3.0 == fACMR) {
char szBuff[128]; // should be sufficiently large in every case
// the JoinIdenticalVertices process has not been executed on this
// mesh, otherwise this value would normally be at least minimally
// smaller than 3.0 ...
ai_snprintf(szBuff,128,"Mesh %u: Not suitable for vcache optimization",meshNum);
ASSIMP_LOG_WARN(szBuff);
return static_cast<ai_real>(0.f);
}
if (!DefaultLogger::isNullLogger()) {
fACMR = calculateInputACMR(pMesh, pcEnd, mConfigCacheDepth, meshNum);
}
// first we need to build a vertex-triangle adjacency list
VertexTriangleAdjacency adj(pMesh->mFaces,pMesh->mNumFaces, pMesh->mNumVertices,true);
VertexTriangleAdjacency adj(pMesh->mFaces, pMesh->mNumFaces, pMesh->mNumVertices, true);
// build a list to store per-vertex caching time stamps
unsigned int* const piCachingStamps = new unsigned int[pMesh->mNumVertices];
memset(piCachingStamps,0x0,pMesh->mNumVertices*sizeof(unsigned int));
std::vector<unsigned int> piCachingStamps;
piCachingStamps.resize(pMesh->mNumVertices);
memset(&piCachingStamps[0], 0x0, pMesh->mNumVertices * sizeof(unsigned int));
// allocate an empty output index buffer. We store the output indices in one large array.
// Since the number of triangles won't change the input faces can be reused. This is how
// we save thousands of redundant mini allocations for aiFace::mIndices
const unsigned int iIdxCnt = pMesh->mNumFaces*3;
unsigned int* const piIBOutput = new unsigned int[iIdxCnt];
unsigned int* piCSIter = piIBOutput;
const unsigned int iIdxCnt = pMesh->mNumFaces * 3;
std::vector<unsigned int> piIBOutput;
piIBOutput.resize(iIdxCnt);
std::vector<unsigned int>::iterator piCSIter = piIBOutput.begin();
// allocate the flag array to hold the information
// whether a face has already been emitted or not
std::vector<bool> abEmitted(pMesh->mNumFaces,false);
std::vector<bool> abEmitted(pMesh->mNumFaces, false);
// dead-end vertex index stack
std::stack<unsigned int, std::vector<unsigned int> > sDeadEndVStack;
std::stack<unsigned int, std::vector<unsigned int>> sDeadEndVStack;
// create a copy of the piNumTriPtr buffer
unsigned int* const piNumTriPtr = adj.mLiveTriangles;
unsigned int *const piNumTriPtr = adj.mLiveTriangles;
const std::vector<unsigned int> piNumTriPtrNoModify(piNumTriPtr, piNumTriPtr + pMesh->mNumVertices);
// get the largest number of referenced triangles and allocate the "candidate buffer"
unsigned int iMaxRefTris = 0; {
const unsigned int* piCur = adj.mLiveTriangles;
const unsigned int* const piCurEnd = adj.mLiveTriangles+pMesh->mNumVertices;
for (;piCur != piCurEnd;++piCur) {
iMaxRefTris = std::max(iMaxRefTris,*piCur);
unsigned int iMaxRefTris = 0;
{
const unsigned int *piCur = adj.mLiveTriangles;
const unsigned int *const piCurEnd = adj.mLiveTriangles + pMesh->mNumVertices;
for (; piCur != piCurEnd; ++piCur) {
iMaxRefTris = std::max(iMaxRefTris, *piCur);
}
}
ai_assert(iMaxRefTris > 0);
unsigned int* piCandidates = new unsigned int[iMaxRefTris*3];
std::vector<unsigned int> piCandidates;
piCandidates.resize(iMaxRefTris * 3);
unsigned int iCacheMisses = 0;
// ...................................................................................
@ -249,23 +254,23 @@ ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int me
int ivdx = 0;
int ics = 1;
int iStampCnt = mConfigCacheDepth+1;
while (ivdx >= 0) {
int iStampCnt = mConfigCacheDepth + 1;
while (ivdx >= 0) {
unsigned int icnt = piNumTriPtrNoModify[ivdx];
unsigned int* piList = adj.GetAdjacentTriangles(ivdx);
unsigned int* piCurCandidate = piCandidates;
unsigned int *piList = adj.GetAdjacentTriangles(ivdx);
std::vector<unsigned int>::iterator piCurCandidate = piCandidates.begin();
// get all triangles in the neighborhood
for (unsigned int tri = 0; tri < icnt;++tri) {
for (unsigned int tri = 0; tri < icnt; ++tri) {
// if they have not yet been emitted, add them to the output IB
const unsigned int fidx = *piList++;
if (!abEmitted[fidx]) {
if (!abEmitted[fidx]) {
// so iterate through all vertices of the current triangle
const aiFace* pcFace = &pMesh->mFaces[ fidx ];
unsigned nind = pcFace->mNumIndices;
const aiFace *pcFace = &pMesh->mFaces[fidx];
const unsigned nind = pcFace->mNumIndices;
for (unsigned ind = 0; ind < nind; ind++) {
unsigned dp = pcFace->mIndices[ind];
@ -285,7 +290,7 @@ ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int me
*piCSIter++ = dp;
// if the vertex is not yet in cache, set its cache count
if (iStampCnt-piCachingStamps[dp] > mConfigCacheDepth) {
if (iStampCnt - piCachingStamps[dp] > mConfigCacheDepth) {
piCachingStamps[dp] = iStampCnt++;
++iCacheMisses;
}
@ -301,16 +306,16 @@ ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int me
// get next fanning vertex
ivdx = -1;
int max_priority = -1;
for (unsigned int* piCur = piCandidates;piCur != piCurCandidate;++piCur) {
for (std::vector<unsigned int>::iterator piCur = piCandidates.begin(); piCur != piCurCandidate; ++piCur) {
const unsigned int dp = *piCur;
// must have live triangles
if (piNumTriPtr[dp] > 0) {
if (piNumTriPtr[dp] > 0) {
int priority = 0;
// will the vertex be in cache, even after fanning occurs?
unsigned int tmp;
if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= mConfigCacheDepth) {
if ((tmp = iStampCnt - piCachingStamps[dp]) + 2 * piNumTriPtr[dp] <= mConfigCacheDepth) {
priority = tmp;
}
@ -328,7 +333,7 @@ ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int me
while (!sDeadEndVStack.empty()) {
unsigned int iCachedIdx = sDeadEndVStack.top();
sDeadEndVStack.pop();
if (piNumTriPtr[ iCachedIdx ] > 0) {
if (piNumTriPtr[iCachedIdx] > 0) {
ivdx = iCachedIdx;
break;
}
@ -337,9 +342,9 @@ ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int me
if (-1 == ivdx) {
// well, there isn't such a vertex. Simply get the next vertex in input order and
// hope it is not too bad ...
while (ics < (int)pMesh->mNumVertices) {
while (ics < (int)pMesh->mNumVertices) {
++ics;
if (piNumTriPtr[ics] > 0) {
if (piNumTriPtr[ics] > 0) {
ivdx = ics;
break;
}
@ -349,29 +354,30 @@ ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int me
}
ai_real fACMR2 = 0.0f;
if (!DefaultLogger::isNullLogger()) {
fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
fACMR2 = static_cast<ai_real>(iCacheMisses / pMesh->mNumFaces);
const ai_real averageACMR = ((fACMR - fACMR2) / fACMR) * 100.f;
// very intense verbose logging ... prepare for much text if there are many meshes
if ( DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
ASSIMP_LOG_VERBOSE_DEBUG("Mesh %u | ACMR in: ", meshNum, " out: ", fACMR, " | ~", fACMR2, ((fACMR - fACMR2) / fACMR) * 100.f);
if (DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
ASSIMP_LOG_VERBOSE_DEBUG("Mesh ", meshNum, "| ACMR in: ", fACMR, " out: ", fACMR2, " | average ACMR ", averageACMR);
}
fACMR2 *= pMesh->mNumFaces;
}
// sort the output index buffer back to the input array
piCSIter = piIBOutput;
for (aiFace* pcFace = pMesh->mFaces; pcFace != pcEnd;++pcFace) {
unsigned nind = pcFace->mNumIndices;
unsigned * ind = pcFace->mIndices;
if (nind > 0) ind[0] = *piCSIter++;
if (nind > 1) ind[1] = *piCSIter++;
if (nind > 2) ind[2] = *piCSIter++;
}
// delete temporary storage
delete[] piCachingStamps;
delete[] piIBOutput;
delete[] piCandidates;
// sort the output index buffer back to the input array
piCSIter = piIBOutput.begin();
for (aiFace *pcFace = pMesh->mFaces; pcFace != pcEnd; ++pcFace) {
unsigned nind = pcFace->mNumIndices;
unsigned *ind = pcFace->mIndices;
if (nind > 0)
ind[0] = *piCSIter++;
if (nind > 1)
ind[1] = *piCSIter++;
if (nind > 2)
ind[2] = *piCSIter++;
}
return fACMR2;
}
} // namespace Assimp

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -51,8 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
struct aiMesh;
namespace Assimp
{
namespace Assimp {
// ---------------------------------------------------------------------------
/** The ImproveCacheLocalityProcess reorders all faces for improved vertex
@ -61,26 +60,24 @@ namespace Assimp
*
* @note This step expects triagulated input data.
*/
class ImproveCacheLocalityProcess : public BaseProcess
{
class ImproveCacheLocalityProcess : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
ImproveCacheLocalityProcess();
~ImproveCacheLocalityProcess();
public:
~ImproveCacheLocalityProcess() override = default;
// -------------------------------------------------------------------
// Check whether the pp step is active
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
// Executes the pp step on a given scene
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
// Configures the pp step
void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer* pImp) override;
protected:
// -------------------------------------------------------------------

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -53,15 +53,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdio.h>
#include <unordered_set>
#include <unordered_map>
#include <memory>
#include <map>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
JoinVerticesProcess::JoinVerticesProcess() = default;
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
JoinVerticesProcess::~JoinVerticesProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
@ -95,7 +90,7 @@ void JoinVerticesProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("JoinVerticesProcess finished ");
return;
}
// Show statistics
ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices,
" out: ", iNumVertices, " | ~",
@ -105,55 +100,8 @@ void JoinVerticesProcess::Execute( aiScene* pScene) {
namespace {
bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex) {
// 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.texcoords[0] - rhs.texcoords[0]).SquareLength() > squareEpsilon) {
return false;
}
if ((lhs.tangent - rhs.tangent).SquareLength() > squareEpsilon) {
return false;
}
if ((lhs.bitangent - rhs.bitangent).SquareLength() > squareEpsilon) {
return false;
}
// Usually we won't have vertex colors or multiple UVs, so we can skip from here
// Actually this increases runtime performance slightly, at least if branch
// prediction is on our side.
if (complex) {
for (int i = 0; i < 8; i++) {
if (i > 0 && (lhs.texcoords[i] - rhs.texcoords[i]).SquareLength() > squareEpsilon) {
return false;
}
if (GetColorDifference(lhs.colors[i], rhs.colors[i]) > squareEpsilon) {
return false;
}
}
}
return true;
}
template<class XMesh>
void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
void updateXMeshVertices(XMesh *pMesh, std::vector<int> &uniqueVertices) {
// replace vertex data with the unique data sets
pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
@ -165,86 +113,55 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
// Position, if present (check made for aiAnimMesh)
if (pMesh->mVertices) {
delete [] pMesh->mVertices;
std::unique_ptr<aiVector3D[]> oldVertices(pMesh->mVertices);
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
pMesh->mVertices[a] = uniqueVertices[a].position;
}
for (unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mVertices[a] = oldVertices[uniqueVertices[a]];
}
// Normals, if present
if (pMesh->mNormals) {
delete [] pMesh->mNormals;
std::unique_ptr<aiVector3D[]> oldNormals(pMesh->mNormals);
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
pMesh->mNormals[a] = uniqueVertices[a].normal;
}
for (unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mNormals[a] = oldNormals[uniqueVertices[a]];
}
// Tangents, if present
if (pMesh->mTangents) {
delete [] pMesh->mTangents;
std::unique_ptr<aiVector3D[]> oldTangents(pMesh->mTangents);
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
pMesh->mTangents[a] = uniqueVertices[a].tangent;
}
for (unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mTangents[a] = oldTangents[uniqueVertices[a]];
}
// Bitangents as well
if (pMesh->mBitangents) {
delete [] pMesh->mBitangents;
std::unique_ptr<aiVector3D[]> oldBitangents(pMesh->mBitangents);
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
pMesh->mBitangents[a] = uniqueVertices[a].bitangent;
}
for (unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mBitangents[a] = oldBitangents[uniqueVertices[a]];
}
// Vertex colors
for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) {
delete [] pMesh->mColors[a];
std::unique_ptr<aiColor4D[]> oldColors(pMesh->mColors[a]);
pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
pMesh->mColors[a][b] = uniqueVertices[b].colors[a];
}
for (unsigned int b = 0; b < pMesh->mNumVertices; b++)
pMesh->mColors[a][b] = oldColors[uniqueVertices[b]];
}
// Texture coords
for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) {
delete [] pMesh->mTextureCoords[a];
std::unique_ptr<aiVector3D[]> oldTextureCoords(pMesh->mTextureCoords[a]);
pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
for (unsigned int b = 0; b < pMesh->mNumVertices; b++) {
pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a];
}
for (unsigned int b = 0; b < pMesh->mNumVertices; b++)
pMesh->mTextureCoords[a][b] = oldTextureCoords[uniqueVertices[b]];
}
}
} // 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> {
bool operator()(const Vertex &lhs, const Vertex &rhs) const {
return areVerticesEqual(lhs, rhs, false);
}
};
static constexpr size_t JOINED_VERTICES_MARK = 0x80000000u;
// now start the JoinVerticesProcess
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) {
static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8");
@ -258,18 +175,17 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) {
// We should care only about used vertices, not all of them
// (this can happen due to original file vertices buffer being used by
// multiple meshes)
std::unordered_set<unsigned int> usedVertexIndices;
usedVertexIndices.reserve(pMesh->mNumVertices);
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
std::vector<bool> usedVertexIndicesMask;
usedVertexIndicesMask.resize(pMesh->mNumVertices, false);
for (unsigned int a = 0; a < pMesh->mNumFaces; a++) {
aiFace& face = pMesh->mFaces[a];
for( unsigned int b = 0; b < face.mNumIndices; b++) {
usedVertexIndices.insert(face.mIndices[b]);
for (unsigned int b = 0; b < face.mNumIndices; b++) {
usedVertexIndicesMask[face.mIndices[b]] = true;
}
}
// We'll never have more vertices afterwards.
std::vector<Vertex> uniqueVertices;
uniqueVertices.reserve( pMesh->mNumVertices);
std::vector<int> uniqueVertices;
// 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
@ -279,52 +195,26 @@ 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;
// We'll never have more vertices afterwards.
std::vector<std::vector<Vertex>> uniqueAnimatedVertices;
std::vector<std::vector<int>> uniqueAnimatedVertices;
if (hasAnimMeshes) {
uniqueAnimatedVertices.resize(pMesh->mNumAnimMeshes);
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices);
}
}
// a map that maps a vertix to its new index
std::unordered_map<Vertex,int> vertex2Index;
// a map that maps a vertex to its new index
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++) {
// if the vertex is unused Do nothing
if (usedVertexIndices.find(a) == usedVertexIndices.end()) {
if (!usedVertexIndicesMask[a]) {
continue;
}
// collect the vertex data
@ -334,19 +224,20 @@ 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(v);
uniqueVertices.push_back(a);
if (hasAnimMeshes) {
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
uniqueAnimatedVertices[animMeshIndex].emplace_back(pMesh->mAnimMeshes[animMeshIndex], a);
uniqueAnimatedVertices[animMeshIndex].emplace_back(a);
}
}
} else{
// if the vertex is already there just find the replace index that is appropriate to it
replaceIndex[a] = it->second;
// mark it with JOINED_VERTICES_MARK
replaceIndex[a] = it->second | JOINED_VERTICES_MARK;
}
}
@ -375,7 +266,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) {
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
aiFace& face = pMesh->mFaces[a];
for( unsigned int b = 0; b < face.mNumIndices; b++) {
face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000;
face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~JOINED_VERTICES_MARK;
}
}
@ -389,17 +280,8 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) {
for ( unsigned int b = 0; b < bone->mNumWeights; b++ ) {
const aiVertexWeight& ow = bone->mWeights[ b ];
// if the vertex is a unique one, translate it
if ( !( replaceIndex[ ow.mVertexId ] & 0x80000000 ) ) {
bool weightAlreadyExists = false;
for (std::vector<aiVertexWeight>::iterator vit = newWeights.begin(); vit != newWeights.end(); ++vit) {
if (vit->mVertexId == replaceIndex[ow.mVertexId]) {
weightAlreadyExists = true;
break;
}
}
if (weightAlreadyExists) {
continue;
}
// filter out joined vertices by JOINED_VERTICES_MARK.
if ( !( replaceIndex[ ow.mVertexId ] & JOINED_VERTICES_MARK ) ) {
aiVertexWeight nw;
nw.mVertexId = replaceIndex[ ow.mVertexId ];
nw.mWeight = ow.mWeight;

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -51,8 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
struct aiMesh;
namespace Assimp
{
namespace Assimp {
// ---------------------------------------------------------------------------
/** The JoinVerticesProcess unites identical vertices in all imported meshes.
@ -64,8 +63,10 @@ namespace Assimp
*/
class ASSIMP_API JoinVerticesProcess : public BaseProcess {
public:
JoinVerticesProcess();
~JoinVerticesProcess();
// -------------------------------------------------------------------
/// The default class constructor / destructor.
JoinVerticesProcess() = default;
~JoinVerticesProcess() override = default;
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
@ -73,14 +74,14 @@ public:
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
/** Unites identical vertices in the given mesh.

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -36,13 +35,7 @@ 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.
----------------------------------------------------------------------
*/
/** Implementation of the LimitBoneWeightsProcess post processing step */
---------------------------------------------------------------------- */
#include "LimitBoneWeightsProcess.h"
#include <assimp/SmallVector.h>
#include <assimp/StringUtils.h>
@ -51,30 +44,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/scene.h>
#include <stdio.h>
using namespace Assimp;
namespace Assimp {
// Make sure this value is set.
#ifndef AI_LMW_MAX_WEIGHTS
# define AI_LMW_MAX_WEIGHTS 16
#endif
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
LimitBoneWeightsProcess::LimitBoneWeightsProcess()
{
mMaxWeights = AI_LMW_MAX_WEIGHTS;
LimitBoneWeightsProcess::LimitBoneWeightsProcess() :
mMaxWeights(AI_LMW_MAX_WEIGHTS), mRemoveEmptyBones(true) {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
LimitBoneWeightsProcess::~LimitBoneWeightsProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const
{
bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const {
return (pFlags & aiProcess_LimitBoneWeights) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void LimitBoneWeightsProcess::Execute( aiScene* pScene)
{
void LimitBoneWeightsProcess::Execute( aiScene* pScene) {
ai_assert(pScene != nullptr);
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin");
for (unsigned int m = 0; m < pScene->mNumMeshes; ++m) {
@ -86,16 +80,31 @@ void LimitBoneWeightsProcess::Execute( aiScene* pScene)
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp)
{
// get the current value of the property
void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) {
this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS);
this->mRemoveEmptyBones = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, 1) != 0;
}
// ------------------------------------------------------------------------------------------------
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];
if (bone->mNumWeights > 0) {
pMesh->mBones[writeBone++] = bone;
} else {
delete bone;
}
}
return writeBone;
}
// ------------------------------------------------------------------------------------------------
// Unites identical vertices in the given mesh
void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh)
{
void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh) {
if (!pMesh->HasBones())
return;
@ -105,11 +114,9 @@ void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh)
WeightsPerVertex vertexWeights(pMesh->mNumVertices);
size_t maxVertexWeights = 0;
for (unsigned int b = 0; b < pMesh->mNumBones; ++b)
{
for (unsigned int b = 0; b < pMesh->mNumBones; ++b) {
const aiBone* bone = pMesh->mBones[b];
for (unsigned int w = 0; w < bone->mNumWeights; ++w)
{
for (unsigned int w = 0; w < bone->mNumWeights; ++w) {
const aiVertexWeight& vw = bone->mWeights[w];
if (vertexWeights.size() <= vw.mVertexId)
@ -126,8 +133,7 @@ void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh)
unsigned int removed = 0, old_bones = pMesh->mNumBones;
// now cut the weight count if it exceeds the maximum
for (WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
{
for (WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit) {
if (vit->size() <= mMaxWeights)
continue;
@ -154,40 +160,27 @@ void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh)
}
// clear weight count for all bone
for (unsigned int a = 0; a < pMesh->mNumBones; ++a)
{
for (unsigned int a = 0; a < pMesh->mNumBones; ++a) {
pMesh->mBones[a]->mNumWeights = 0;
}
// rebuild the vertex weight array for all bones
for (unsigned int a = 0; a < vertexWeights.size(); ++a)
{
for (unsigned int a = 0; a < vertexWeights.size(); ++a) {
const VertexWeightArray& vw = vertexWeights[a];
for (const Weight* it = vw.begin(); it != vw.end(); ++it)
{
for (const Weight* it = vw.begin(); it != vw.end(); ++it) {
aiBone* bone = pMesh->mBones[it->mBone];
bone->mWeights[bone->mNumWeights++] = aiVertexWeight(a, it->mWeight);
}
}
// remove empty bones
unsigned int writeBone = 0;
for (unsigned int readBone = 0; readBone< pMesh->mNumBones; ++readBone)
{
aiBone* bone = pMesh->mBones[readBone];
if (bone->mNumWeights > 0)
{
pMesh->mBones[writeBone++] = bone;
}
else
{
delete bone;
}
if (mRemoveEmptyBones) {
pMesh->mNumBones = removeEmptyBones(pMesh);
}
pMesh->mNumBones = writeBone;
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones);
}
}
} // namespace Assimp

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -74,8 +74,10 @@ namespace Assimp {
*/
class ASSIMP_API LimitBoneWeightsProcess : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
LimitBoneWeightsProcess();
~LimitBoneWeightsProcess();
~LimitBoneWeightsProcess() override = default;
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag.
@ -84,27 +86,27 @@ public:
* @return true if the process is present in this flag fields,
* false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** Called prior to ExecuteOnScene().
* The function is a request to the process to update its configuration
* basing on the Importer's configuration property list.
*/
void SetupProperties(const Importer* pImp);
// -------------------------------------------------------------------
/** Limits the bone weight count for all vertices in the given mesh.
* @param pMesh The mesh to process.
*/
void ProcessMesh( aiMesh* pMesh);
void SetupProperties(const Importer* pImp) override;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
/** Limits the bone weight count for all vertices in the given mesh.
* @param pMesh The mesh to process.
*/
void ProcessMesh( aiMesh* pMesh);
// -------------------------------------------------------------------
/** Describes a bone weight on a vertex */
@ -131,6 +133,7 @@ public:
/** Maximum number of bones influencing any single vertex. */
unsigned int mMaxWeights;
bool mRemoveEmptyBones;
};
} // end of namespace Assimp

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
@ -49,10 +49,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
MakeVerboseFormatProcess::MakeVerboseFormatProcess() = default;
// ------------------------------------------------------------------------------------------------
MakeVerboseFormatProcess::~MakeVerboseFormatProcess() = default;
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void MakeVerboseFormatProcess::Execute(aiScene *pScene) {
@ -93,8 +89,8 @@ bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh *pcMesh) {
pvBitangents = new aiVector3D[iNumVerts];
}
aiVector3D *apvTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS] = { 0 };
aiColor4D *apvColorSets[AI_MAX_NUMBER_OF_COLOR_SETS] = { 0 };
aiVector3D *apvTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS] = { nullptr };
aiColor4D *apvColorSets[AI_MAX_NUMBER_OF_COLOR_SETS] = { nullptr };
unsigned int p = 0;
while (pcMesh->HasTextureCoords(p))

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -66,22 +66,19 @@ namespace Assimp {
* The step has been added because it was required by the viewer, however
* it has been moved to the main library since others might find it
* useful, too. */
class ASSIMP_API_WINONLY MakeVerboseFormatProcess : public BaseProcess
{
public:
MakeVerboseFormatProcess();
~MakeVerboseFormatProcess();
class ASSIMP_API_WINONLY MakeVerboseFormatProcess : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
MakeVerboseFormatProcess() = default;
~MakeVerboseFormatProcess() override = default;
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not */
bool IsActive( unsigned int /*pFlags*/ ) const
bool IsActive( unsigned int /*pFlags*/ ) const override
{
// NOTE: There is no direct flag that corresponds to
// this postprocess step.
@ -92,7 +89,7 @@ public:
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at. */
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
public:

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -78,10 +78,6 @@ OptimizeGraphProcess::OptimizeGraphProcess() :
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
OptimizeGraphProcess::~OptimizeGraphProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool OptimizeGraphProcess::IsActive(unsigned int pFlags) const {
@ -168,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,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -71,8 +71,10 @@ namespace Assimp {
*/
class OptimizeGraphProcess : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
OptimizeGraphProcess();
~OptimizeGraphProcess();
~OptimizeGraphProcess() override = default;
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const override;

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
@ -69,10 +69,6 @@ OptimizeMeshesProcess::OptimizeMeshesProcess()
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
OptimizeMeshesProcess::~OptimizeMeshesProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool OptimizeMeshesProcess::IsActive( unsigned int pFlags) const

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -68,11 +68,10 @@ namespace Assimp {
*/
class OptimizeMeshesProcess : public BaseProcess {
public:
/// @brief The class constructor.
// -------------------------------------------------------------------
/// The default class constructor / destructor.
OptimizeMeshesProcess();
/// @brief The class destructor.
~OptimizeMeshesProcess();
~OptimizeMeshesProcess() override = default;
/** @brief Internal utility to store additional mesh info
*/
@ -94,16 +93,14 @@ public:
unsigned int output_id;
};
public:
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer* pImp) override;
// -------------------------------------------------------------------
/** @brief Specify whether you want meshes with different

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -41,9 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file PretransformVertices.cpp
* @brief Implementation of the "PretransformVertices" post processing step
*/
/// @file PretransformVertices.cpp
/// @brief Implementation of the "PretransformVertices" post processing step
#include "PretransformVertices.h"
#include "ConvertToLHProcess.h"
@ -57,20 +54,44 @@ using namespace Assimp;
#define AI_PTVS_VERTEX 0x0
#define AI_PTVS_FACE 0x1
namespace {
// Get a bitwise combination identifying the vertex format of a mesh
static unsigned int GetMeshVFormat(aiMesh *pcMesh) {
// the vertex format is stored in aiMesh::mBones for later retrieval.
// there isn't a good reason to compute it a few hundred times
// from scratch. The pointer is unused as animations are lost
// during PretransformVertices.
if (pcMesh->mBones)
return (unsigned int)(uint64_t)pcMesh->mBones;
const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
// store the value for later use
pcMesh->mBones = (aiBone **)(uint64_t)iRet;
return iRet;
}
// Get a list of all vertex formats that occur for a given material index
// The output list contains duplicate elements
static void GetVFormatList(const aiScene *pcScene, unsigned int iMat, std::list<unsigned int> &aiOut) {
for (unsigned int i = 0; i < pcScene->mNumMeshes; ++i) {
aiMesh *pcMesh = pcScene->mMeshes[i];
if (iMat == pcMesh->mMaterialIndex) {
aiOut.push_back(GetMeshVFormat(pcMesh));
}
}
}
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
PretransformVertices::PretransformVertices() :
configKeepHierarchy(false),
configNormalize(false),
configTransform(false),
configTransformation(),
mConfigPointCloud(false) {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
PretransformVertices::~PretransformVertices() = default;
mConfigKeepHierarchy(false),
mConfigNormalize(false),
mConfigTransform(false),
mConfigTransformation(),
mConfigPointCloud(false) {}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
@ -83,11 +104,11 @@ bool PretransformVertices::IsActive(unsigned int pFlags) const {
void PretransformVertices::SetupProperties(const Importer *pImp) {
// Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE,
// AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION
configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, 0));
configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, 0));
configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION, 0));
mConfigKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, 0));
mConfigNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, 0));
mConfigTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION, 0));
configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4());
mConfigTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4());
mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS);
}
@ -103,25 +124,7 @@ unsigned int PretransformVertices::CountNodes(const aiNode *pcNode) const {
}
// ------------------------------------------------------------------------------------------------
// Get a bitwise combination identifying the vertex format of a mesh
unsigned int PretransformVertices::GetMeshVFormat(aiMesh *pcMesh) const {
// the vertex format is stored in aiMesh::mBones for later retrieval.
// there isn't a good reason to compute it a few hundred times
// from scratch. The pointer is unused as animations are lost
// during PretransformVertices.
if (pcMesh->mBones)
return (unsigned int)(uint64_t)pcMesh->mBones;
const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
// store the value for later use
pcMesh->mBones = (aiBone **)(uint64_t)iRet;
return iRet;
}
// ------------------------------------------------------------------------------------------------
// Count the number of vertices in the whole scene and a given
// material index
// Count the number of vertices in the whole scene and a given material index
void PretransformVertices::CountVerticesAndFaces(const aiScene *pcScene, const aiNode *pcNode, unsigned int iMat,
unsigned int iVFormat, unsigned int *piFaces, unsigned int *piVertices) const {
for (unsigned int i = 0; i < pcNode->mNumMeshes; ++i) {
@ -132,8 +135,7 @@ void PretransformVertices::CountVerticesAndFaces(const aiScene *pcScene, const a
}
}
for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
CountVerticesAndFaces(pcScene, pcNode->mChildren[i], iMat,
iVFormat, piFaces, piVertices);
CountVerticesAndFaces(pcScene, pcNode->mChildren[i], iMat, iVFormat, piFaces, piVertices);
}
}
@ -276,19 +278,6 @@ void PretransformVertices::CollectData(const aiScene *pcScene, const aiNode *pcN
}
}
// ------------------------------------------------------------------------------------------------
// Get a list of all vertex formats that occur for a given material index
// The output list contains duplicate elements
void PretransformVertices::GetVFormatList(const aiScene *pcScene, unsigned int iMat,
std::list<unsigned int> &aiOut) const {
for (unsigned int i = 0; i < pcScene->mNumMeshes; ++i) {
aiMesh *pcMesh = pcScene->mMeshes[i];
if (iMat == pcMesh->mMaterialIndex) {
aiOut.push_back(GetMeshVFormat(pcMesh));
}
}
}
// ------------------------------------------------------------------------------------------------
// Compute the absolute transformation matrices of each node
void PretransformVertices::ComputeAbsoluteTransform(aiNode *pcNode) {
@ -305,35 +294,37 @@ void PretransformVertices::ComputeAbsoluteTransform(aiNode *pcNode) {
// Apply the node transformation to a mesh
void PretransformVertices::ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat) const {
// Check whether we need to transform the coordinates at all
if (!mat.IsIdentity()) {
if (mat.IsIdentity()) {
return;
}
// Check for odd negative scale (mirror)
if (mesh->HasFaces() && mat.Determinant() < 0) {
// Reverse the mesh face winding order
FlipWindingOrderProcess::ProcessMesh(mesh);
// Check for odd negative scale (mirror)
if (mesh->HasFaces() && mat.Determinant() < 0) {
// Reverse the mesh face winding order
FlipWindingOrderProcess::ProcessMesh(mesh);
}
// Update positions
if (mesh->HasPositions()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mVertices[i] = mat * mesh->mVertices[i];
}
}
// Update positions
if (mesh->HasPositions()) {
// Update normals and tangents
if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
const aiMatrix3x3 m = aiMatrix3x3(mat).Inverse().Transpose();
if (mesh->HasNormals()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mVertices[i] = mat * mesh->mVertices[i];
}
mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
}
}
// Update normals and tangents
if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
const aiMatrix3x3 m = aiMatrix3x3(mat).Inverse().Transpose();
if (mesh->HasNormals()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
}
}
if (mesh->HasTangentsAndBitangents()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
}
if (mesh->HasTangentsAndBitangents()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
}
}
}
@ -356,40 +347,41 @@ void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **i
// yes, we can.
mesh->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
mesh->mNumBones = UINT_MAX;
} else {
continue;
}
// try to find us in the list of newly created meshes
for (unsigned int n = 0; n < out.size(); ++n) {
aiMesh *ctz = out[n];
if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4 *>(ctz->mBones) == node->mTransformation) {
// try to find us in the list of newly created meshes
for (unsigned int n = 0; n < out.size(); ++n) {
aiMesh *ctz = out[n];
if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4 *>(ctz->mBones) == node->mTransformation) {
// ok, use this one. Update node mesh index
node->mMeshes[i] = numIn + n;
}
// ok, use this one. Update node mesh index
node->mMeshes[i] = numIn + n;
}
if (node->mMeshes[i] < numIn) {
// Worst case. Need to operate on a full copy of the mesh
ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms");
aiMesh *ntz;
}
if (node->mMeshes[i] < numIn) {
// Worst case. Need to operate on a full copy of the mesh
ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms");
aiMesh *ntz;
const unsigned int tmp = mesh->mNumBones; //
mesh->mNumBones = 0;
SceneCombiner::Copy(&ntz, mesh);
mesh->mNumBones = tmp;
const unsigned int cacheNumBones = mesh->mNumBones; //
mesh->mNumBones = 0;
SceneCombiner::Copy(&ntz, mesh);
mesh->mNumBones = cacheNumBones;
ntz->mNumBones = node->mMeshes[i];
ntz->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
ntz->mNumBones = node->mMeshes[i];
ntz->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
out.push_back(ntz);
out.push_back(ntz);
node->mMeshes[i] = static_cast<unsigned int>(numIn + out.size() - 1);
}
node->mMeshes[i] = static_cast<unsigned int>(numIn + out.size() - 1);
}
}
// call children
for (unsigned int i = 0; i < node->mNumChildren; ++i)
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
BuildWCSMeshes(out, in, numIn, node->mChildren[i]);
}
}
// ------------------------------------------------------------------------------------------------
@ -398,8 +390,9 @@ void PretransformVertices::MakeIdentityTransform(aiNode *nd) const {
nd->mTransformation = aiMatrix4x4();
// call children
for (unsigned int i = 0; i < nd->mNumChildren; ++i)
for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
MakeIdentityTransform(nd->mChildren[i]);
}
}
// ------------------------------------------------------------------------------------------------
@ -409,8 +402,27 @@ void PretransformVertices::BuildMeshRefCountArray(const aiNode *nd, unsigned int
refs[nd->mMeshes[i]]++;
// call children
for (unsigned int i = 0; i < nd->mNumChildren; ++i)
for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
BuildMeshRefCountArray(nd->mChildren[i], refs);
}
}
// ------------------------------------------------------------------------------------------------
static void appendNewMeshesToScene(aiScene *pScene, std::vector<aiMesh*> &apcOutMeshes) {
ai_assert(pScene != nullptr);
if (apcOutMeshes.empty()) {
return;
}
aiMesh **npp = new aiMesh *[pScene->mNumMeshes + apcOutMeshes.size()];
::memcpy(npp, pScene->mMeshes, sizeof(aiMesh *) * pScene->mNumMeshes);
::memcpy(npp + pScene->mNumMeshes, &apcOutMeshes[0], sizeof(aiMesh *) * apcOutMeshes.size());
pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size());
delete[] pScene->mMeshes;
pScene->mMeshes = npp;
}
// ------------------------------------------------------------------------------------------------
@ -422,12 +434,12 @@ void PretransformVertices::Execute(aiScene *pScene) {
if (!pScene->mNumMeshes)
return;
const unsigned int iOldMeshes = pScene->mNumMeshes;
const unsigned int iOldAnimationChannels = pScene->mNumAnimations;
const unsigned int iOldNodes = CountNodes(pScene->mRootNode);
const unsigned int oldMeshes = pScene->mNumMeshes;
const unsigned int oldAnimationChannels = pScene->mNumAnimations;
const unsigned int oldNodes = CountNodes(pScene->mRootNode);
if (configTransform) {
pScene->mRootNode->mTransformation = configTransformation * pScene->mRootNode->mTransformation;
if (mConfigTransform) {
pScene->mRootNode->mTransformation = mConfigTransformation * pScene->mRootNode->mTransformation;
}
// first compute absolute transformation matrices for all nodes
@ -453,22 +465,13 @@ void PretransformVertices::Execute(aiScene *pScene) {
// we go on and transform all meshes, if one is referenced by nodes
// with different absolute transformations a depth copy of the mesh
// is required.
if (configKeepHierarchy) {
if (mConfigKeepHierarchy) {
// Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
BuildWCSMeshes(apcOutMeshes, pScene->mMeshes, pScene->mNumMeshes, pScene->mRootNode);
// ... if new meshes have been generated, append them to the end of the scene
if (apcOutMeshes.size() > 0) {
aiMesh **npp = new aiMesh *[pScene->mNumMeshes + apcOutMeshes.size()];
memcpy(npp, pScene->mMeshes, sizeof(aiMesh *) * pScene->mNumMeshes);
memcpy(npp + pScene->mNumMeshes, &apcOutMeshes[0], sizeof(aiMesh *) * apcOutMeshes.size());
pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size());
delete[] pScene->mMeshes;
pScene->mMeshes = npp;
}
appendNewMeshesToScene(pScene, apcOutMeshes);
// now iterate through all meshes and transform them to world-space
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
@ -492,34 +495,35 @@ void PretransformVertices::Execute(aiScene *pScene) {
aiVFormats.sort();
aiVFormats.unique();
for (std::list<unsigned int>::const_iterator j = aiVFormats.begin(); j != aiVFormats.end(); ++j) {
unsigned int iVertices = 0;
unsigned int iFaces = 0;
CountVerticesAndFaces(pScene, pScene->mRootNode, i, *j, &iFaces, &iVertices);
if (0 != iFaces && 0 != iVertices) {
unsigned int numVertices = 0u;
unsigned int numFaces = 0u;
CountVerticesAndFaces(pScene, pScene->mRootNode, i, *j, &numFaces, &numVertices);
if (0 != numFaces && 0 != numVertices) {
apcOutMeshes.push_back(new aiMesh());
aiMesh *pcMesh = apcOutMeshes.back();
pcMesh->mNumFaces = iFaces;
pcMesh->mNumVertices = iVertices;
pcMesh->mFaces = new aiFace[iFaces];
pcMesh->mVertices = new aiVector3D[iVertices];
pcMesh->mNumFaces = numFaces;
pcMesh->mNumVertices = numVertices;
pcMesh->mFaces = new aiFace[numFaces];
pcMesh->mVertices = new aiVector3D[numVertices];
pcMesh->mMaterialIndex = i;
if ((*j) & 0x2) pcMesh->mNormals = new aiVector3D[iVertices];
if ((*j) & 0x2) pcMesh->mNormals = new aiVector3D[numVertices];
if ((*j) & 0x4) {
pcMesh->mTangents = new aiVector3D[iVertices];
pcMesh->mBitangents = new aiVector3D[iVertices];
pcMesh->mTangents = new aiVector3D[numVertices];
pcMesh->mBitangents = new aiVector3D[numVertices];
}
iFaces = 0;
while ((*j) & (0x100 << iFaces)) {
pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
if ((*j) & (0x10000 << iFaces))
pcMesh->mNumUVComponents[iFaces] = 3;
else
pcMesh->mNumUVComponents[iFaces] = 2;
iFaces++;
numFaces = 0;
while ((*j) & (0x100 << numFaces)) {
pcMesh->mTextureCoords[numFaces] = new aiVector3D[numVertices];
if ((*j) & (0x10000 << numFaces)) {
pcMesh->mNumUVComponents[numFaces] = 3;
} else {
pcMesh->mNumUVComponents[numFaces] = 2;
}
++numFaces;
}
iFaces = 0;
while ((*j) & (0x1000000 << iFaces))
pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
numFaces = 0;
while ((*j) & (0x1000000 << numFaces))
pcMesh->mColors[numFaces++] = new aiColor4D[numVertices];
// fill the mesh ...
unsigned int aiTemp[2] = { 0, 0 };
@ -581,7 +585,7 @@ void PretransformVertices::Execute(aiScene *pScene) {
// multiply all properties of the camera with the absolute
// transformation of the corresponding node
cam->mPosition = nd->mTransformation * cam->mPosition;
cam->mLookAt = aiMatrix3x3(nd->mTransformation) * cam->mLookAt;
cam->mLookAt = nd->mTransformation * cam->mLookAt;
cam->mUp = aiMatrix3x3(nd->mTransformation) * cam->mUp;
}
@ -597,7 +601,7 @@ void PretransformVertices::Execute(aiScene *pScene) {
l->mUp = aiMatrix3x3(nd->mTransformation) * l->mUp;
}
if (!configKeepHierarchy) {
if (!mConfigKeepHierarchy) {
// now delete all nodes in the scene and build a new
// flat node graph with a root node and some level 1 children
@ -631,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
@ -639,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;
}
}
@ -648,7 +652,7 @@ void PretransformVertices::Execute(aiScene *pScene) {
MakeIdentityTransform(pScene->mRootNode);
}
if (configNormalize) {
if (mConfigNormalize) {
// compute the boundary of all meshes
aiVector3D min, max;
MinMaxChooser<aiVector3D>()(min, max);
@ -678,9 +682,9 @@ void PretransformVertices::Execute(aiScene *pScene) {
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished");
ASSIMP_LOG_INFO("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (",
ASSIMP_LOG_INFO("Removed ", oldNodes, " nodes and ", oldAnimationChannels, " animation channels (",
CountNodes(pScene->mRootNode), " output nodes)");
ASSIMP_LOG_INFO("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras.");
ASSIMP_LOG_INFO("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")");
ASSIMP_LOG_INFO("Moved ", oldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")");
}
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -68,8 +68,10 @@ namespace Assimp {
*/
class ASSIMP_API PretransformVertices : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
PretransformVertices();
~PretransformVertices();
~PretransformVertices() override = default;
// -------------------------------------------------------------------
// Check whether step is active
@ -88,7 +90,7 @@ public:
* @param keep true for keep configuration.
*/
void KeepHierarchy(bool keep) {
configKeepHierarchy = keep;
mConfigKeepHierarchy = keep;
}
// -------------------------------------------------------------------
@ -96,7 +98,7 @@ public:
* @return ...
*/
bool IsHierarchyKept() const {
return configKeepHierarchy;
return mConfigKeepHierarchy;
}
private:
@ -106,7 +108,7 @@ private:
// -------------------------------------------------------------------
// Get a bitwise combination identifying the vertex format of a mesh
unsigned int GetMeshVFormat(aiMesh *pcMesh) const;
//unsigned int GetMeshVFormat(aiMesh *pcMesh) const;
// -------------------------------------------------------------------
// Count the number of vertices in the whole scene and a given
@ -129,8 +131,8 @@ private:
// -------------------------------------------------------------------
// Get a list of all vertex formats that occur for a given material
// The output list contains duplicate elements
void GetVFormatList(const aiScene *pcScene, unsigned int iMat,
std::list<unsigned int> &aiOut) const;
/*void GetVFormatList(const aiScene *pcScene, unsigned int iMat,
std::list<unsigned int> &aiOut) const;*/
// -------------------------------------------------------------------
// Compute the absolute transformation matrices of each node
@ -154,10 +156,10 @@ private:
void BuildMeshRefCountArray(const aiNode *nd, unsigned int *refs) const;
//! Configuration option: keep scene hierarchy as long as possible
bool configKeepHierarchy;
bool configNormalize;
bool configTransform;
aiMatrix4x4 configTransformation;
bool mConfigKeepHierarchy;
bool mConfigNormalize;
bool mConfigTransform;
aiMatrix4x4 mConfigTransformation;
bool mConfigPointCloud;
};

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -52,8 +52,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 +67,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));
}
}
}
@ -175,10 +176,9 @@ unsigned int GetMeshVFormatUnique(const aiMesh *pcMesh) {
// tangents and bitangents
if (pcMesh->HasTangentsAndBitangents()) iRet |= 0x4;
#ifdef BOOST_STATIC_ASSERT
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
#endif
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,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -45,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// internal headers
#include "RemoveRedundantMaterials.h"
#include <assimp/ParsingUtils.h>
#include "ProcessHelper.h"
@ -57,164 +54,153 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
RemoveRedundantMatsProcess::RemoveRedundantMatsProcess()
: mConfigFixedMaterials() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
RemoveRedundantMatsProcess::~RemoveRedundantMatsProcess() = default;
RemoveRedundantMatsProcess::RemoveRedundantMatsProcess() : mConfigFixedMaterials() {}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const
{
bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const {
return (pFlags & aiProcess_RemoveRedundantMaterials) != 0;
}
// ------------------------------------------------------------------------------------------------
// Setup import properties
void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp)
{
void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp) {
// Get value of AI_CONFIG_PP_RRM_EXCLUDE_LIST
mConfigFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,"");
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
{
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;
}
// 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 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()) {
// 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);
std::list<std::string> strings;
ConvertListToStrings(mConfigFixedMaterials,strings);
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);
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
aiMaterial* mat = pScene->mMaterials[i];
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);
aiString name;
mat->Get(AI_MATKEY_NAME,name);
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;
}
// 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;
}
// delete temporary storage
delete[] aiHashes;
delete[] aiMappingTable;
// 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;
}
// delete temporary storage
delete[] aiHashes;
delete[] aiMappingTable;
}
if (redundantRemoved == 0 && unreferencedRemoved == 0)
{
if (redundantRemoved == 0 && unreferencedRemoved == 0) {
ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess finished ");
}
else
{
} else {
ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ",
unreferencedRemoved, " unused materials.");
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -59,23 +59,22 @@ namespace Assimp {
*/
class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess {
public:
/// The default class constructor.
// -------------------------------------------------------------------
/// The default class constructor / destructor.
RemoveRedundantMatsProcess();
/// The class destructor.
~RemoveRedundantMatsProcess();
~RemoveRedundantMatsProcess() override = default;
// -------------------------------------------------------------------
// Check whether step is active
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
// Execute step on a given scene
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
// Setup import settings
void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer* pImp) override;
// -------------------------------------------------------------------
/** @brief Set list of fixed (inmutable) materials

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -56,10 +54,6 @@ using namespace Assimp;
RemoveVCProcess::RemoveVCProcess() :
configDeleteFlags(), mScene() {}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
RemoveVCProcess::~RemoveVCProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool RemoveVCProcess::IsActive(unsigned int pFlags) const {
@ -78,63 +72,6 @@ inline void ArrayDelete(T **&in, unsigned int &num) {
num = 0;
}
#if 0
// ------------------------------------------------------------------------------------------------
// Updates the node graph - removes all nodes which have the "remove" flag set and the
// "don't remove" flag not set. Nodes with meshes are never deleted.
bool UpdateNodeGraph(aiNode* node,std::list<aiNode*>& childsOfParent,bool root)
{
bool b = false;
std::list<aiNode*> mine;
for (unsigned int i = 0; i < node->mNumChildren;++i)
{
if(UpdateNodeGraph(node->mChildren[i],mine,false))
b = true;
}
// somewhat tricky ... mNumMeshes must be originally 0 and MSB2 may not be set,
// so we can do a simple comparison against MSB here
if (!root && AI_RC_UINT_MSB == node->mNumMeshes )
{
// this node needs to be removed
if(node->mNumChildren)
{
childsOfParent.insert(childsOfParent.end(),mine.begin(),mine.end());
// set all children to nullptr to make sure they are not deleted when we delete ourself
for (unsigned int i = 0; i < node->mNumChildren;++i)
node->mChildren[i] = nullptr;
}
b = true;
delete node;
}
else
{
AI_RC_UNMASK(node->mNumMeshes);
childsOfParent.push_back(node);
if (b)
{
// reallocate the array of our children here
node->mNumChildren = (unsigned int)mine.size();
aiNode** const children = new aiNode*[mine.size()];
aiNode** ptr = children;
for (std::list<aiNode*>::iterator it = mine.begin(), end = mine.end();
it != end; ++it)
{
*ptr++ = *it;
}
delete[] node->mChildren;
node->mChildren = children;
return false;
}
}
return b;
}
#endif
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void RemoveVCProcess::Execute(aiScene *pScene) {

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -58,11 +58,10 @@ namespace Assimp {
*/
class ASSIMP_API RemoveVCProcess : public BaseProcess {
public:
/// The default class constructor.
// -------------------------------------------------------------------
/// The default class constructor / destructor.
RemoveVCProcess();
/// The class destructor.
~RemoveVCProcess();
~RemoveVCProcess() override = default;
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
@ -70,37 +69,35 @@ public:
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
/** 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);
virtual void SetupProperties(const Importer* pImp) override;
// -------------------------------------------------------------------
/** Manually setup the configuration flags for the step
*
* @param Bitwise combination of the #aiComponent enumerated values.
*/
void SetDeleteFlags(unsigned int f)
{
void SetDeleteFlags(unsigned int f) {
configDeleteFlags = f;
}
// -------------------------------------------------------------------
/** Query the current configuration.
*/
unsigned int GetDeleteFlags() const
{
unsigned int GetDeleteFlags() const {
return configDeleteFlags;
}

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -47,25 +46,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
ScaleProcess::ScaleProcess()
: BaseProcess()
, mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) {
// ------------------------------------------------------------------------------------------------
ScaleProcess::ScaleProcess() : BaseProcess(), mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) {
// empty
}
ScaleProcess::~ScaleProcess() = default;
// ------------------------------------------------------------------------------------------------
void ScaleProcess::setScale( ai_real scale ) {
mScale = scale;
}
// ------------------------------------------------------------------------------------------------
ai_real ScaleProcess::getScale() const {
return mScale;
}
// ------------------------------------------------------------------------------------------------
bool ScaleProcess::IsActive( unsigned int pFlags ) const {
return ( pFlags & aiProcess_GlobalScale ) != 0;
}
// ------------------------------------------------------------------------------------------------
void ScaleProcess::SetupProperties( const Importer* pImp ) {
// User scaling
mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 1.0f );
@ -78,14 +79,15 @@ void ScaleProcess::SetupProperties( const Importer* pImp ) {
mScale *= importerScale;
}
// ------------------------------------------------------------------------------------------------
void ScaleProcess::Execute( aiScene* pScene ) {
if(mScale == 1.0f) {
return; // nothing to scale
}
ai_assert( mScale != 0 );
ai_assert( nullptr != pScene );
ai_assert( nullptr != pScene->mRootNode );
ai_assert(mScale != 0 );
ai_assert(nullptr != pScene );
ai_assert(nullptr != pScene->mRootNode );
if ( nullptr == pScene ) {
return;
@ -96,37 +98,30 @@ void ScaleProcess::Execute( aiScene* pScene ) {
}
// Process animations and update position transform to new unit system
for( unsigned int animationID = 0; animationID < pScene->mNumAnimations; animationID++ )
{
for( unsigned int animationID = 0; animationID < pScene->mNumAnimations; animationID++ ) {
aiAnimation* animation = pScene->mAnimations[animationID];
for( unsigned int animationChannel = 0; animationChannel < animation->mNumChannels; animationChannel++)
{
for( unsigned int animationChannel = 0; animationChannel < animation->mNumChannels; animationChannel++) {
aiNodeAnim* anim = animation->mChannels[animationChannel];
for( unsigned int posKey = 0; posKey < anim->mNumPositionKeys; posKey++)
{
for( unsigned int posKey = 0; posKey < anim->mNumPositionKeys; posKey++) {
aiVectorKey& vectorKey = anim->mPositionKeys[posKey];
vectorKey.mValue *= mScale;
}
}
}
for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++)
{
for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++) {
aiMesh *mesh = pScene->mMeshes[meshID];
// Reconstruct mesh vertices to the new unit system
for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++)
{
for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++) {
aiVector3D& vertex = mesh->mVertices[vertexID];
vertex *= mScale;
}
// bone placement / scaling
for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++)
{
for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++) {
// Reconstruct matrix by transform rather than by scale
// This prevent scale values being changed which can
// be meaningful in some cases
@ -144,7 +139,7 @@ void ScaleProcess::Execute( aiScene* pScene ) {
aiMatrix4x4 scaling;
aiMatrix4x4::Scaling( aiVector3D(scale), scaling );
aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix());
const aiMatrix4x4 RotMatrix = aiMatrix4x4(rotation.GetMatrix());
bone->mOffsetMatrix = translation * RotMatrix * scaling;
}
@ -152,12 +147,10 @@ void ScaleProcess::Execute( aiScene* pScene ) {
// animation mesh processing
// convert by position rather than scale.
for( unsigned int animMeshID = 0; animMeshID < mesh->mNumAnimMeshes; animMeshID++)
{
for( unsigned int animMeshID = 0; animMeshID < mesh->mNumAnimMeshes; animMeshID++) {
aiAnimMesh * animMesh = mesh->mAnimMeshes[animMeshID];
for( unsigned int vertexID = 0; vertexID < animMesh->mNumVertices; vertexID++)
{
for( unsigned int vertexID = 0; vertexID < animMesh->mNumVertices; vertexID++) {
aiVector3D& vertex = animMesh->mVertices[vertexID];
vertex *= mScale;
}
@ -167,16 +160,17 @@ void ScaleProcess::Execute( aiScene* pScene ) {
traverseNodes( pScene->mRootNode );
}
// ------------------------------------------------------------------------------------------------
void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) {
applyScaling( node );
for( size_t i = 0; i < node->mNumChildren; i++)
{
for( size_t i = 0; i < node->mNumChildren; i++) {
// recurse into the tree until we are done!
traverseNodes( node->mChildren[i], nested_node_id+1 );
}
}
// ------------------------------------------------------------------------------------------------
void ScaleProcess::applyScaling( aiNode *currentNode ) {
if ( nullptr != currentNode ) {
// Reconstruct matrix by transform rather than by scale

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -62,11 +62,10 @@ namespace Assimp {
*/
class ASSIMP_API ScaleProcess : public BaseProcess {
public:
/// The default class constructor.
// -------------------------------------------------------------------
/// The default class constructor / destructor.
ScaleProcess();
/// The class destructor.
virtual ~ScaleProcess();
~ScaleProcess() override = default;
/// Will set the scale manually.
void setScale( ai_real scale );
@ -75,13 +74,13 @@ public:
ai_real getScale() const;
/// Overwritten, @see BaseProcess
virtual bool IsActive( unsigned int pFlags ) const;
virtual bool IsActive( unsigned int pFlags ) const override;
/// Overwritten, @see BaseProcess
virtual void SetupProperties( const Importer* pImp );
virtual void SetupProperties( const Importer* pImp ) override;
/// Overwritten, @see BaseProcess
virtual void Execute( aiScene* pScene );
virtual void Execute( aiScene* pScene ) override;
private:
void traverseNodes( aiNode *currentNode, unsigned int nested_node_id = 0 );

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -54,14 +52,7 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
SortByPTypeProcess::SortByPTypeProcess() :
mConfigRemoveMeshes(0) {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
SortByPTypeProcess::~SortByPTypeProcess() = default;
SortByPTypeProcess::SortByPTypeProcess() : mConfigRemoveMeshes(0) {}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
@ -74,42 +65,53 @@ 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
for (unsigned int m = 0; m < node->mNumChildren; ++m)
for (unsigned int m = 0; m < node->mNumChildren; ++m) {
UpdateNodes(replaceMeshIndex, node->mChildren[m]);
}
}
// ------------------------------------------------------------------------------------------------
@ -159,7 +161,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
if (1 == num) {
if (!(mConfigRemoveMeshes & mesh->mPrimitiveTypes)) {
*meshIdx = static_cast<unsigned int>(outMeshes.size());
outMeshes.push_back(mesh);
outMeshes.emplace_back(mesh);
} else {
delete mesh;
pScene->mMeshes[i] = nullptr;
@ -175,6 +177,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;
@ -315,21 +321,23 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
if (vert) {
*vert++ = mesh->mVertices[idx];
//mesh->mVertices[idx].x = get_qnan();
}
if (nor) *nor++ = mesh->mNormals[idx];
if (nor)
*nor++ = mesh->mNormals[idx];
if (tan) {
*tan++ = mesh->mTangents[idx];
*bit++ = mesh->mBitangents[idx];
}
for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp) {
if (!uv[pp]) break;
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]) break;
if (!cols[pp])
break;
*cols[pp]++ = mesh->mColors[pp][idx];
}
@ -355,7 +363,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
}
}
if (pp == mesh->mNumAnimMeshes)
amIdx++;
++amIdx;
in.mIndices[q] = outIdx++;
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -60,17 +60,19 @@ namespace Assimp {
*/
class ASSIMP_API SortByPTypeProcess : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
SortByPTypeProcess();
~SortByPTypeProcess();
~SortByPTypeProcess() override = default;
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer* pImp) override;
private:
int mConfigRemoveMeshes;

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -40,7 +39,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/// @file SplitByBoneCountProcess.cpp
/// Implementation of the SplitByBoneCount postprocessing step
@ -59,47 +57,34 @@ using namespace Assimp::Formatter;
// ------------------------------------------------------------------------------------------------
// Constructor
SplitByBoneCountProcess::SplitByBoneCountProcess()
{
// set default, might be overridden by importer config
mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES;
}
// ------------------------------------------------------------------------------------------------
// Destructor
SplitByBoneCountProcess::~SplitByBoneCountProcess() = default;
SplitByBoneCountProcess::SplitByBoneCountProcess() : mMaxBoneCount(AI_SBBC_DEFAULT_MAX_BONES) {}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag.
bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const
{
bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const {
return !!(pFlags & aiProcess_SplitByBoneCount);
}
// ------------------------------------------------------------------------------------------------
// Updates internal properties
void SplitByBoneCountProcess::SetupProperties(const Importer* pImp)
{
void SplitByBoneCountProcess::SetupProperties(const Importer* pImp) {
mMaxBoneCount = pImp->GetPropertyInteger(AI_CONFIG_PP_SBBC_MAX_BONES,AI_SBBC_DEFAULT_MAX_BONES);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void SplitByBoneCountProcess::Execute( aiScene* pScene)
{
void SplitByBoneCountProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("SplitByBoneCountProcess begin");
// early out
bool isNecessary = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; ++a)
if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount )
{
if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount ) {
isNecessary = true;
break;
}
if( !isNecessary )
{
if( !isNecessary ) {
ASSIMP_LOG_DEBUG("SplitByBoneCountProcess early-out: no meshes with more than ", mMaxBoneCount, " bones." );
return;
}
@ -111,28 +96,23 @@ void SplitByBoneCountProcess::Execute( aiScene* pScene)
// build a new array of meshes for the scene
std::vector<aiMesh*> meshes;
for( unsigned int a = 0; a < pScene->mNumMeshes; ++a)
{
for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
aiMesh* srcMesh = pScene->mMeshes[a];
std::vector<aiMesh*> newMeshes;
SplitMesh( pScene->mMeshes[a], newMeshes);
// mesh was split
if( !newMeshes.empty() )
{
if( !newMeshes.empty() ) {
// store new meshes and indices of the new meshes
for( unsigned int b = 0; b < newMeshes.size(); ++b)
{
for( unsigned int b = 0; b < newMeshes.size(); ++b) {
mSubMeshIndices[a].push_back( static_cast<unsigned int>(meshes.size()));
meshes.push_back( newMeshes[b]);
}
// and destroy the source mesh. It should be completely contained inside the new submeshes
delete srcMesh;
}
else
{
} else {
// Mesh is kept unchanged - store it's new place in the mesh array
mSubMeshIndices[a].push_back( static_cast<unsigned int>(meshes.size()));
meshes.push_back( srcMesh);
@ -153,11 +133,9 @@ void SplitByBoneCountProcess::Execute( aiScene* pScene)
// ------------------------------------------------------------------------------------------------
// Splits the given mesh by bone count.
void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const
{
void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const {
// skip if not necessary
if( pMesh->mNumBones <= mMaxBoneCount )
{
if( pMesh->mNumBones <= mMaxBoneCount ) {
return;
}
@ -165,42 +143,35 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
// TODO: (thom) maybe add a custom allocator here to avoid allocating tens of thousands of small arrays
typedef std::pair<unsigned int, float> BoneWeight;
std::vector< std::vector<BoneWeight> > vertexBones( pMesh->mNumVertices);
for( unsigned int a = 0; a < pMesh->mNumBones; ++a)
{
for( unsigned int a = 0; a < pMesh->mNumBones; ++a) {
const aiBone* bone = pMesh->mBones[a];
for( unsigned int b = 0; b < bone->mNumWeights; ++b)
{
if (bone->mWeights[b].mWeight > 0.0f)
{
int vertexId = bone->mWeights[b].mVertexId;
vertexBones[vertexId].emplace_back(a, bone->mWeights[b].mWeight);
if (vertexBones[vertexId].size() > mMaxBoneCount)
{
throw DeadlyImportError("SplitByBoneCountProcess: Single face requires more bones than specified max bone count!");
for( unsigned int b = 0; b < bone->mNumWeights; ++b) {
if (bone->mWeights[b].mWeight > 0.0f) {
int vertexId = bone->mWeights[b].mVertexId;
vertexBones[vertexId].emplace_back(a, bone->mWeights[b].mWeight);
if (vertexBones[vertexId].size() > mMaxBoneCount) {
throw DeadlyImportError("SplitByBoneCountProcess: Single face requires more bones than specified max bone count!");
}
}
}
}
}
unsigned int numFacesHandled = 0;
std::vector<bool> isFaceHandled( pMesh->mNumFaces, false);
while( numFacesHandled < pMesh->mNumFaces )
{
while( numFacesHandled < pMesh->mNumFaces ) {
// which bones are used in the current submesh
unsigned int numBones = 0;
std::vector<bool> isBoneUsed( pMesh->mNumBones, false);
// indices of the faces which are going to go into this submesh
std::vector<unsigned int> subMeshFaces;
IndexArray subMeshFaces;
subMeshFaces.reserve( pMesh->mNumFaces);
// accumulated vertex count of all the faces in this submesh
unsigned int numSubMeshVertices = 0;
// add faces to the new submesh as long as all bones affecting the faces' vertices fit in the limit
for( unsigned int a = 0; a < pMesh->mNumFaces; ++a)
{
for( unsigned int a = 0; a < pMesh->mNumFaces; ++a) {
// skip if the face is already stored in a submesh
if( isFaceHandled[a] )
{
if( isFaceHandled[a] ) {
continue;
}
// a small local set of new bones for the current face. State of all used bones for that face
@ -209,33 +180,27 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
const aiFace& face = pMesh->mFaces[a];
// check every vertex if its bones would still fit into the current submesh
for( unsigned int b = 0; b < face.mNumIndices; ++b )
{
const std::vector<BoneWeight>& vb = vertexBones[face.mIndices[b]];
for( unsigned int c = 0; c < vb.size(); ++c)
{
unsigned int boneIndex = vb[c].first;
if( !isBoneUsed[boneIndex] )
{
newBonesAtCurrentFace.insert(boneIndex);
for( unsigned int b = 0; b < face.mNumIndices; ++b ) {
const std::vector<BoneWeight>& vb = vertexBones[face.mIndices[b]];
for( unsigned int c = 0; c < vb.size(); ++c) {
unsigned int boneIndex = vb[c].first;
if( !isBoneUsed[boneIndex] ) {
newBonesAtCurrentFace.insert(boneIndex);
}
}
}
}
// leave out the face if the new bones required for this face don't fit the bone count limit anymore
if( numBones + newBonesAtCurrentFace.size() > mMaxBoneCount )
{
if( numBones + newBonesAtCurrentFace.size() > mMaxBoneCount ) {
continue;
}
// mark all new bones as necessary
for (std::set<unsigned int>::iterator it = newBonesAtCurrentFace.begin(); it != newBonesAtCurrentFace.end(); ++it)
{
if (!isBoneUsed[*it])
{
isBoneUsed[*it] = true;
numBones++;
}
for (std::set<unsigned int>::iterator it = newBonesAtCurrentFace.begin(); it != newBonesAtCurrentFace.end(); ++it) {
if (!isBoneUsed[*it]) {
isBoneUsed[*it] = true;
++numBones;
}
}
// store the face index and the vertex count
@ -244,44 +209,37 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
// remember that this face is handled
isFaceHandled[a] = true;
numFacesHandled++;
++numFacesHandled;
}
// create a new mesh to hold this subset of the source mesh
aiMesh* newMesh = new aiMesh;
if( pMesh->mName.length > 0 )
{
if( pMesh->mName.length > 0 ) {
newMesh->mName.Set( format() << pMesh->mName.data << "_sub" << poNewMeshes.size());
}
newMesh->mMaterialIndex = pMesh->mMaterialIndex;
newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
poNewMeshes.push_back( newMesh);
poNewMeshes.emplace_back( newMesh);
// create all the arrays for this mesh if the old mesh contained them
newMesh->mNumVertices = numSubMeshVertices;
newMesh->mNumFaces = static_cast<unsigned int>(subMeshFaces.size());
newMesh->mVertices = new aiVector3D[newMesh->mNumVertices];
if( pMesh->HasNormals() )
{
if( pMesh->HasNormals() ) {
newMesh->mNormals = new aiVector3D[newMesh->mNumVertices];
}
if( pMesh->HasTangentsAndBitangents() )
{
if( pMesh->HasTangentsAndBitangents() ) {
newMesh->mTangents = new aiVector3D[newMesh->mNumVertices];
newMesh->mBitangents = new aiVector3D[newMesh->mNumVertices];
}
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
{
if( pMesh->HasTextureCoords( a) )
{
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) {
if( pMesh->HasTextureCoords( a) ) {
newMesh->mTextureCoords[a] = new aiVector3D[newMesh->mNumVertices];
}
newMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
}
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
{
if( pMesh->HasVertexColors( a) )
{
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) {
if( pMesh->HasVertexColors( a) ) {
newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices];
}
}
@ -289,42 +247,34 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
// and copy over the data, generating faces with linear indices along the way
newMesh->mFaces = new aiFace[subMeshFaces.size()];
unsigned int nvi = 0; // next vertex index
std::vector<unsigned int> previousVertexIndices( numSubMeshVertices, std::numeric_limits<unsigned int>::max()); // per new vertex: its index in the source mesh
for( unsigned int a = 0; a < subMeshFaces.size(); ++a )
{
IndexArray previousVertexIndices( numSubMeshVertices, std::numeric_limits<unsigned int>::max()); // per new vertex: its index in the source mesh
for( unsigned int a = 0; a < subMeshFaces.size(); ++a ) {
const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
aiFace& dstFace = newMesh->mFaces[a];
dstFace.mNumIndices = srcFace.mNumIndices;
dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
// accumulate linearly all the vertices of the source face
for( unsigned int b = 0; b < dstFace.mNumIndices; ++b )
{
for( unsigned int b = 0; b < dstFace.mNumIndices; ++b ) {
unsigned int srcIndex = srcFace.mIndices[b];
dstFace.mIndices[b] = nvi;
previousVertexIndices[nvi] = srcIndex;
newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
if( pMesh->HasNormals() )
{
if( pMesh->HasNormals() ) {
newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
}
if( pMesh->HasTangentsAndBitangents() )
{
if( pMesh->HasTangentsAndBitangents() ) {
newMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
newMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
}
for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c )
{
if( pMesh->HasTextureCoords( c) )
{
for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c ) {
if( pMesh->HasTextureCoords( c) ) {
newMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
}
}
for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c )
{
if( pMesh->HasVertexColors( c) )
{
for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c ) {
if( pMesh->HasVertexColors( c) ) {
newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
}
}
@ -340,10 +290,8 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
newMesh->mBones = new aiBone*[numBones];
std::vector<unsigned int> mappedBoneIndex( pMesh->mNumBones, std::numeric_limits<unsigned int>::max());
for( unsigned int a = 0; a < pMesh->mNumBones; ++a )
{
if( !isBoneUsed[a] )
{
for( unsigned int a = 0; a < pMesh->mNumBones; ++a ) {
if( !isBoneUsed[a] ) {
continue;
}
@ -360,24 +308,20 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
ai_assert( newMesh->mNumBones == numBones );
// iterate over all new vertices and count which bones affected its old vertex in the source mesh
for( unsigned int a = 0; a < numSubMeshVertices; ++a )
{
for( unsigned int a = 0; a < numSubMeshVertices; ++a ) {
unsigned int oldIndex = previousVertexIndices[a];
const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[oldIndex];
for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b )
{
for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b ) {
unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
if( newBoneIndex != std::numeric_limits<unsigned int>::max() )
{
if( newBoneIndex != std::numeric_limits<unsigned int>::max() ) {
newMesh->mBones[newBoneIndex]->mNumWeights++;
}
}
}
// allocate all bone weight arrays accordingly
for( unsigned int a = 0; a < newMesh->mNumBones; ++a )
{
for( unsigned int a = 0; a < newMesh->mNumBones; ++a ) {
aiBone* bone = newMesh->mBones[a];
ai_assert( bone->mNumWeights > 0 );
bone->mWeights = new aiVertexWeight[bone->mNumWeights];
@ -385,16 +329,14 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
}
// now copy all the bone vertex weights for all the vertices which made it into the new submesh
for( unsigned int a = 0; a < numSubMeshVertices; ++a)
{
for( unsigned int a = 0; a < numSubMeshVertices; ++a) {
// find the source vertex for it in the source mesh
unsigned int previousIndex = previousVertexIndices[a];
// these bones were affecting it
const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[previousIndex];
// all of the bones affecting it should be present in the new submesh, or else
// the face it comprises shouldn't be present
for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b)
{
for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b) {
unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
ai_assert( newBoneIndex != std::numeric_limits<unsigned int>::max() );
aiVertexWeight* dstWeight = newMesh->mBones[newBoneIndex]->mWeights + newMesh->mBones[newBoneIndex]->mNumWeights;
@ -450,16 +392,13 @@ 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
{
void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const {
// rebuild the node's mesh index list
if( pNode->mNumMeshes > 0 )
{
std::vector<unsigned int> newMeshList;
for( unsigned int a = 0; a < pNode->mNumMeshes; ++a)
{
if( pNode->mNumMeshes != 0 ) {
IndexArray newMeshList;
for( unsigned int a = 0; a < pNode->mNumMeshes; ++a) {
unsigned int srcIndex = pNode->mMeshes[a];
const std::vector<unsigned int>& replaceMeshes = mSubMeshIndices[srcIndex];
const IndexArray& replaceMeshes = mSubMeshIndices[srcIndex];
newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end());
}
@ -470,8 +409,7 @@ void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const
}
// do that also recursively for all children
for( unsigned int a = 0; a < pNode->mNumChildren; ++a )
{
for( unsigned int a = 0; a < pNode->mNumChildren; ++a ) {
UpdateNode( pNode->mChildren[a]);
}
}

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -51,9 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/mesh.h>
#include <assimp/scene.h>
namespace Assimp
{
namespace Assimp {
/** Postprocessing filter to split meshes with many bones into submeshes
* so that each submesh has a certain max bone count.
@ -61,34 +58,33 @@ namespace Assimp
* Applied BEFORE the JoinVertices-Step occurs.
* Returns NON-UNIQUE vertices, splits by bone count.
*/
class SplitByBoneCountProcess : public BaseProcess
{
class SplitByBoneCountProcess : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
SplitByBoneCountProcess();
~SplitByBoneCountProcess();
~SplitByBoneCountProcess() override = default;
public:
/** Returns whether the processing step is present in the given flag.
* @param pFlags The processing flags the importer was called with. A
* bitwise combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields,
* false if not.
*/
bool IsActive( unsigned int pFlags) const;
/// @brief Returns whether the processing step is present in the given flag.
/// @param pFlags The processing flags the importer was called with. A
/// bitwise combination of #aiPostProcessSteps.
/// @return true if the process is present in this flag fields, false if not.
bool IsActive( unsigned int pFlags) const override;
/** 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);
/// @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;
/// @brief Will return the maximal number of bones.
/// @return The maximal number of bones.
size_t getMaxNumberOfBones() const;
protected:
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
/// Executes the post processing step on the given imported data.
/// At the moment a process is not supposed to fail.
/// @param pScene The imported data to work at.
void Execute( aiScene* pScene) override;
/// Splits the given mesh by bone count.
/// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split.
@ -98,14 +94,19 @@ protected:
/// Recursively updates the node's mesh list to account for the changed mesh list
void UpdateNode( aiNode* pNode) const;
public:
private:
/// Max bone count. Splitting occurs if a mesh has more than that number of bones.
size_t mMaxBoneCount;
/// Per mesh index: Array of indices of the new submeshes.
std::vector< std::vector<unsigned int> > mSubMeshIndices;
using IndexArray = std::vector<unsigned int>;
std::vector<IndexArray> mSubMeshIndices;
};
inline size_t SplitByBoneCountProcess::getMaxNumberOfBones() const {
return mMaxBoneCount;
}
} // end of namespace Assimp
#endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -40,9 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/**
* @file Implementation of the SplitLargeMeshes postprocessing step
*/
/// @file Implementation of the SplitLargeMeshes postprocessing step
// internal headers of the post-processing framework
#include "SplitLargeMeshes.h"
@ -55,9 +52,6 @@ SplitLargeMeshesProcess_Triangle::SplitLargeMeshesProcess_Triangle() {
LIMIT = AI_SLM_DEFAULT_MAX_TRIANGLES;
}
// ------------------------------------------------------------------------------------------------
SplitLargeMeshesProcess_Triangle::~SplitLargeMeshesProcess_Triangle() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool SplitLargeMeshesProcess_Triangle::IsActive( unsigned int pFlags) const {
@ -78,22 +72,22 @@ void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene) {
this->SplitMesh(a, pScene->mMeshes[a],avList);
}
if (avList.size() != pScene->mNumMeshes) {
// it seems something has been split. rebuild the mesh list
delete[] pScene->mMeshes;
pScene->mNumMeshes = (unsigned int)avList.size();
pScene->mMeshes = new aiMesh*[avList.size()];
for (unsigned int i = 0; i < avList.size();++i) {
pScene->mMeshes[i] = avList[i].first;
}
// now we need to update all nodes
this->UpdateNode(pScene->mRootNode,avList);
ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split");
} else {
if (avList.size() == pScene->mNumMeshes) {
ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do");
}
// it seems something has been split. rebuild the mesh list
delete[] pScene->mMeshes;
pScene->mNumMeshes = (unsigned int)avList.size();
pScene->mMeshes = new aiMesh*[avList.size()];
for (unsigned int i = 0; i < avList.size();++i) {
pScene->mMeshes[i] = avList[i].first;
}
// now we need to update all nodes
this->UpdateNode(pScene->mRootNode,avList);
ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split");
}
// ------------------------------------------------------------------------------------------------
@ -105,8 +99,12 @@ 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) {
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);
@ -329,9 +327,6 @@ SplitLargeMeshesProcess_Vertex::SplitLargeMeshesProcess_Vertex() {
LIMIT = AI_SLM_DEFAULT_MAX_VERTICES;
}
// ------------------------------------------------------------------------------------------------
SplitLargeMeshesProcess_Vertex::~SplitLargeMeshesProcess_Vertex() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool SplitLargeMeshesProcess_Vertex::IsActive( unsigned int pFlags) const {

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -83,16 +83,15 @@ class SplitLargeMeshesProcess_Vertex;
* Applied BEFORE the JoinVertices-Step occurs.
* Returns NON-UNIQUE vertices, splits by triangle number.
*/
class ASSIMP_API SplitLargeMeshesProcess_Triangle : public BaseProcess
{
class ASSIMP_API SplitLargeMeshesProcess_Triangle : public BaseProcess {
friend class SplitLargeMeshesProcess_Vertex;
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
SplitLargeMeshesProcess_Triangle();
~SplitLargeMeshesProcess_Triangle();
~SplitLargeMeshesProcess_Triangle() override = default;
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag.
* @param pFlags The processing flags the importer was called with. A
@ -100,16 +99,14 @@ public:
* @return true if the process is present in this flag fields,
* false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** 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);
void SetupProperties(const Importer* pImp) override;
//! Set the split limit - needed for unit testing
inline void SetLimit(unsigned int l)
@ -119,14 +116,12 @@ public:
inline unsigned int GetLimit() const
{return LIMIT;}
public:
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
//! Apply the algorithm to a given mesh
@ -144,36 +139,31 @@ public:
unsigned int LIMIT;
};
// ---------------------------------------------------------------------------
/** Post-processing filter to split large meshes into sub-meshes
*
* Applied AFTER the JoinVertices-Step occurs.
* Returns UNIQUE vertices, splits by vertex number.
*/
class ASSIMP_API SplitLargeMeshesProcess_Vertex : public BaseProcess
{
class ASSIMP_API SplitLargeMeshesProcess_Vertex : public BaseProcess {
public:
SplitLargeMeshesProcess_Vertex();
~SplitLargeMeshesProcess_Vertex();
~SplitLargeMeshesProcess_Vertex() override = default;
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @param pFlags The processing flags the importer was called with. A bitwise
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** 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);
void SetupProperties(const Importer* pImp) override;
//! Set the split limit - needed for unit testing
inline void SetLimit(unsigned int l)
@ -183,14 +173,12 @@ public:
inline unsigned int GetLimit() const
{return LIMIT;}
public:
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
//! Apply the algorithm to a given mesh

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -42,8 +41,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file A helper class that processes texture transformations */
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp>
@ -56,33 +53,24 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
TextureTransformStep::TextureTransformStep() :
configFlags()
{
TextureTransformStep::TextureTransformStep() : configFlags() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
TextureTransformStep::~TextureTransformStep() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool TextureTransformStep::IsActive( unsigned int pFlags) const
{
bool TextureTransformStep::IsActive( unsigned int pFlags) const {
return (pFlags & aiProcess_TransformUVCoords) != 0;
}
// ------------------------------------------------------------------------------------------------
// Setup properties
void TextureTransformStep::SetupProperties(const Importer* pImp)
{
void TextureTransformStep::SetupProperties(const Importer* pImp) {
configFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_TUV_EVALUATE,AI_UVTRAFO_ALL);
}
// ------------------------------------------------------------------------------------------------
void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
{
void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info) {
/* This function tries to simplify the input UV transformation.
* That's very important as it allows us to reduce the number
* of output UV channels. The order in which the transformations
@ -90,7 +78,7 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
*/
int rounded;
char szTemp[512];
char szTemp[512] = {};
/* Optimize the rotation angle. That's slightly difficult as
* we have an inprecise floating-point number (when comparing
@ -98,12 +86,10 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
* an epsilon of 5 degrees). If there is a rotation value, we can't
* perform any further optimizations.
*/
if (info.mRotation)
{
if (info.mRotation) {
float out = info.mRotation;
rounded = static_cast<int>((info.mRotation / static_cast<float>(AI_MATH_TWO_PI)));
if (rounded)
{
if (rounded) {
out -= rounded * static_cast<float>(AI_MATH_PI);
ASSIMP_LOG_INFO("Texture coordinate rotation ", info.mRotation, " can be simplified to ", out);
}
@ -187,8 +173,7 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
}
// ------------------------------------------------------------------------------------------------
void UpdateUVIndex(const std::list<TTUpdateInfo>& l, unsigned int n)
{
void UpdateUVIndex(const std::list<TTUpdateInfo>& l, unsigned int n) {
// Don't set if == 0 && wasn't set before
for (std::list<TTUpdateInfo>::const_iterator it = l.begin();it != l.end(); ++it) {
const TTUpdateInfo& info = *it;
@ -203,8 +188,7 @@ void UpdateUVIndex(const std::list<TTUpdateInfo>& l, unsigned int n)
}
// ------------------------------------------------------------------------------------------------
inline const char* MappingModeToChar(aiTextureMapMode map)
{
inline static const char* MappingModeToChar(aiTextureMapMode map) {
if (aiTextureMapMode_Wrap == map)
return "-w";
@ -215,8 +199,7 @@ inline const char* MappingModeToChar(aiTextureMapMode map)
}
// ------------------------------------------------------------------------------------------------
void TextureTransformStep::Execute( aiScene* pScene)
{
void TextureTransformStep::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("TransformUVCoordsProcess begin");
@ -407,8 +390,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) {
@ -508,8 +491,9 @@ void TextureTransformStep::Execute( aiScene* pScene)
ai_assert(nullptr != src);
// Copy the data to the destination array
if (dest != src)
if (dest != src) {
::memcpy(dest,src,sizeof(aiVector3D)*mesh->mNumVertices);
}
end = dest + mesh->mNumVertices;

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -193,28 +193,23 @@ struct STransformVecInfo : public aiUVTransform {
/** 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 : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
TextureTransformStep();
~TextureTransformStep();
public:
~TextureTransformStep() override = default;
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer* pImp) override;
protected:
// -------------------------------------------------------------------
/** Preprocess a specific UV transformation setup
*
@ -223,10 +218,9 @@ protected:
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-2024, assimp team
All rights reserved.
@ -156,26 +156,15 @@ namespace {
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
TriangulateProcess::TriangulateProcess() = default;
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
TriangulateProcess::~TriangulateProcess() = default;
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool TriangulateProcess::IsActive( unsigned int pFlags) const
{
bool TriangulateProcess::IsActive( unsigned int pFlags) const {
return (pFlags & aiProcess_Triangulate) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void TriangulateProcess::Execute( aiScene* pScene)
{
void TriangulateProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("TriangulateProcess begin");
bool bHas = false;
@ -196,21 +185,20 @@ void TriangulateProcess::Execute( aiScene* pScene)
// ------------------------------------------------------------------------------------------------
// Triangulates the given mesh.
bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
{
bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
// Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases
if (!pMesh->mPrimitiveTypes) {
bool bNeed = false;
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;
@ -225,18 +213,19 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
get_normals = false;
}
if( face.mNumIndices <= 3) {
numOut++;
}
else {
++numOut;
} else {
numOut += face.mNumIndices-2;
max_out = std::max(max_out,face.mNumIndices);
}
}
// 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;
// if we don't have normals yet, but expect them to be a cheap side
@ -464,7 +453,22 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
*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)) {
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;
}
@ -505,22 +509,6 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
#endif
num = 0;
break;
/*curOut -= (max-num); // undo all previous work
for (tmp = 0; tmp < max-2; ++tmp) {
aiFace& nface = *curOut++;
nface.mNumIndices = 3;
if (!nface.mIndices)
nface.mIndices = new unsigned int[3];
nface.mIndices[0] = 0;
nface.mIndices[1] = tmp+1;
nface.mIndices[2] = tmp+2;
}
num = 0;
break;*/
}
aiFace& nface = *curOut++;
@ -574,23 +562,6 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
for(aiFace* f = last_face; f != curOut; ) {
unsigned int* i = f->mIndices;
// drop dumb 0-area triangles - deactivated for now:
//FindDegenerates post processing step can do the same thing
//if (std::fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) {
// ASSIMP_LOG_VERBOSE_DEBUG("Dropping triangle with area 0");
// --curOut;
// delete[] f->mIndices;
// f->mIndices = nullptr;
// for(aiFace* ff = f; ff != curOut; ++ff) {
// ff->mNumIndices = (ff+1)->mNumIndices;
// ff->mIndices = (ff+1)->mIndices;
// (ff+1)->mIndices = nullptr;
// }
// continue;
//}
i[0] = idx[i[0]];
i[1] = idx[i[1]];
i[2] = idx[i[2]];

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -61,8 +61,10 @@ namespace Assimp {
*/
class ASSIMP_API TriangulateProcess : public BaseProcess {
public:
TriangulateProcess();
~TriangulateProcess();
// -------------------------------------------------------------------
/// The default class constructor / destructor.
TriangulateProcess() = default;
~TriangulateProcess() override = default;
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
@ -70,14 +72,14 @@ public:
* combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields, false if not.
*/
bool IsActive( unsigned int pFlags) const;
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
/** Triangulates the given mesh.

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -60,12 +58,7 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
ValidateDSProcess::ValidateDSProcess() :
mScene() {}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
ValidateDSProcess::~ValidateDSProcess() = default;
ValidateDSProcess::ValidateDSProcess() : mScene(nullptr) {}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
@ -80,7 +73,7 @@ AI_WONT_RETURN void ValidateDSProcess::ReportError(const char *msg, ...) {
va_start(args, msg);
char szBuffer[3000];
const int iLen = vsprintf(szBuffer, msg, args);
const int iLen = vsnprintf(szBuffer, sizeof(szBuffer), msg, args);
ai_assert(iLen > 0);
va_end(args);
@ -95,7 +88,7 @@ void ValidateDSProcess::ReportWarning(const char *msg, ...) {
va_start(args, msg);
char szBuffer[3000];
const int iLen = vsprintf(szBuffer, msg, args);
const int iLen = vsnprintf(szBuffer, sizeof(szBuffer), msg, args);
ai_assert(iLen > 0);
va_end(args);
@ -115,18 +108,21 @@ inline int HasNameMatch(const aiString &in, aiNode *node) {
template <typename T>
inline void ValidateDSProcess::DoValidation(T **parray, unsigned int size, const char *firstName, const char *secondName) {
// validate all entries
if (size) {
if (!parray) {
ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
firstName, secondName, size);
}
for (unsigned int i = 0; i < size; ++i) {
if (!parray[i]) {
ReportError("aiScene::%s[%i] is nullptr (aiScene::%s is %i)",
firstName, i, secondName, size);
}
Validate(parray[i]);
if (size == 0) {
return;
}
if (!parray) {
ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
firstName, secondName, size);
}
for (unsigned int i = 0; i < size; ++i) {
if (!parray[i]) {
ReportError("aiScene::%s[%i] is nullptr (aiScene::%s is %i)",
firstName, i, secondName, size);
}
Validate(parray[i]);
}
}
@ -135,25 +131,27 @@ template <typename T>
inline void ValidateDSProcess::DoValidationEx(T **parray, unsigned int size,
const char *firstName, const char *secondName) {
// validate all entries
if (size) {
if (!parray) {
ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
firstName, secondName, size);
if (size == 0) {
return;
}
if (!parray) {
ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
firstName, secondName, size);
}
for (unsigned int i = 0; i < size; ++i) {
if (!parray[i]) {
ReportError("aiScene::%s[%u] is nullptr (aiScene::%s is %u)",
firstName, i, secondName, size);
}
for (unsigned int i = 0; i < size; ++i) {
if (!parray[i]) {
ReportError("aiScene::%s[%u] is nullptr (aiScene::%s is %u)",
firstName, i, secondName, size);
}
Validate(parray[i]);
Validate(parray[i]);
// check whether there are duplicate names
for (unsigned int a = i + 1; a < size; ++a) {
if (parray[i]->mName == parray[a]->mName) {
ReportError("aiScene::%s[%u] has the same name as "
"aiScene::%s[%u]",
firstName, i, secondName, a);
}
// check whether there are duplicate names
for (unsigned int a = i + 1; a < size; ++a) {
if (parray[i]->mName == parray[a]->mName) {
ReportError("aiScene::%s[%u] has the same name as "
"aiScene::%s[%u]",
firstName, i, secondName, a);
}
}
}
@ -234,12 +232,6 @@ void ValidateDSProcess::Execute(aiScene *pScene) {
if (pScene->mNumMaterials) {
DoValidation(pScene->mMaterials, pScene->mNumMaterials, "mMaterials", "mNumMaterials");
}
#if 0
// NOTE: ScenePreprocessor generates a default material if none is there
else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) {
ReportError("aiScene::mNumMaterials is 0. At least one material must be there");
}
#endif
else if (pScene->mMaterials) {
ReportError("aiScene::mMaterials is non-null although there are no materials");
}
@ -272,8 +264,7 @@ void ValidateDSProcess::Validate(const aiCamera *pCamera) {
if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear)
ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear");
// FIX: there are many 3ds files with invalid FOVs. No reason to
// reject them at all ... a warning is appropriate.
// There are many 3ds files with invalid FOVs. No reason to reject them at all ... a warning is appropriate.
if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI)
ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV", pCamera->mHorizontalFOV);
}
@ -295,7 +286,6 @@ void ValidateDSProcess::Validate(const aiMesh *pMesh) {
switch (face.mNumIndices) {
case 0:
ReportError("aiMesh::mFaces[%i].mNumIndices is 0", i);
break;
case 1:
if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT)) {
ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimitiveTypes "
@ -367,15 +357,6 @@ void ValidateDSProcess::Validate(const aiMesh *pMesh) {
if (face.mIndices[a] >= pMesh->mNumVertices) {
ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range", i, a);
}
// the MSB flag is temporarily used by the extra verbose
// mode to tell us that the JoinVerticesProcess might have
// been executed already.
/*if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && !(this->mScene->mFlags & AI_SCENE_FLAGS_ALLOW_SHARED) &&
abRefList[face.mIndices[a]])
{
ReportError("aiMesh::mVertices[%i] is referenced twice - second "
"time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a);
}*/
abRefList[face.mIndices[a]] = true;
}
}
@ -390,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) {
@ -471,7 +439,7 @@ void ValidateDSProcess::Validate(const aiMesh *pMesh, const aiBone *pBone, float
this->Validate(&pBone->mName);
if (!pBone->mNumWeights) {
//ReportError("aiBone::mNumWeights is zero");
ReportWarning("aiBone::mNumWeights is zero");
}
// check whether all vertices affected by this bone are valid
@ -479,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;
}
@ -916,16 +884,24 @@ void ValidateDSProcess::Validate(const aiNode *pNode) {
nodeName, pNode->mNumChildren);
}
for (unsigned int i = 0; i < pNode->mNumChildren; ++i) {
Validate(pNode->mChildren[i]);
const aiNode *pChild = pNode->mChildren[i];
Validate(pChild);
if (pChild->mParent != pNode) {
const char *parentName = (pChild->mParent != nullptr) ? pChild->mParent->mName.C_Str() : "null";
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) {
@ -934,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,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -69,22 +69,20 @@ namespace Assimp {
/** Validates the whole ASSIMP scene data structure for correctness.
* ImportErrorException is thrown of the scene is corrupt.*/
// --------------------------------------------------------------------------------------
class ValidateDSProcess : public BaseProcess
{
class ValidateDSProcess : public BaseProcess {
public:
// -------------------------------------------------------------------
/// The default class constructor / destructor.
ValidateDSProcess();
~ValidateDSProcess();
public:
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const;
~ValidateDSProcess() override = default;
// -------------------------------------------------------------------
void Execute( aiScene* pScene);
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
void Execute( aiScene* pScene) override;
protected:
// -------------------------------------------------------------------
/** Report a validation error. This will throw an exception,
* control won't return.