update assimp to 5.2.3 Bugfix-Release

This commit is contained in:
AzaezelX 2022-04-26 11:56:24 -05:00
parent 3f796d2a06
commit f297476092
1150 changed files with 165834 additions and 112019 deletions

View file

@ -0,0 +1,265 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* 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
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
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.
----------------------------------------------------------------------
*/
#include "ArmaturePopulate.h"
#include <assimp/BaseImporter.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <iostream>
namespace Assimp {
/// The default class constructor.
ArmaturePopulate::ArmaturePopulate() :
BaseProcess() {
// do nothing
}
/// The class destructor.
ArmaturePopulate::~ArmaturePopulate() {
// do nothing
}
bool ArmaturePopulate::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_PopulateArmatureData) != 0;
}
void ArmaturePopulate::SetupProperties(const Importer *) {
// do nothing
}
void ArmaturePopulate::Execute(aiScene *out) {
// Now convert all bone positions to the correct mOffsetMatrix
std::vector<aiBone *> bones;
std::vector<aiNode *> nodes;
std::map<aiBone *, aiNode *> bone_stack;
BuildBoneList(out->mRootNode, out->mRootNode, out, bones);
BuildNodeList(out->mRootNode, nodes);
BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes);
ASSIMP_LOG_DEBUG("Bone stack size: ", bone_stack.size());
for (std::pair<aiBone *, aiNode *> kvp : bone_stack) {
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);
#ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS
// set up bone armature id
bone->mArmature = armature;
// set this bone node to be referenced properly
ai_assert(bone_node);
bone->mNode = bone_node;
#endif
}
}
// Reprocess all nodes to calculate bone transforms properly based on the REAL
// mOffsetMatrix not the local.
// Before this would use mesh transforms which is wrong for bone transforms
// Before this would work for simple character skeletons but not complex meshes
// with multiple origins
// Source: sketch fab log cutter fbx
void ArmaturePopulate::BuildBoneList(aiNode *current_node,
const aiNode *root_node,
const aiScene *scene,
std::vector<aiBone *> &bones) {
ai_assert(scene);
for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) {
aiNode *child = current_node->mChildren[nodeId];
ai_assert(child);
// check for bones
for (unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId) {
ai_assert(child->mMeshes);
unsigned int mesh_index = child->mMeshes[meshId];
aiMesh *mesh = scene->mMeshes[mesh_index];
ai_assert(mesh);
for (unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) {
aiBone *bone = mesh->mBones[boneId];
ai_assert(nullptr != bone);
// duplicate mehes exist with the same bones sometimes :)
// so this must be detected
if (std::find(bones.begin(), bones.end(), bone) == bones.end()) {
// add the element once
bones.emplace_back(bone);
}
}
// find mesh and get bones
// then do recursive lookup for bones in root node hierarchy
}
BuildBoneList(child, root_node, scene, bones);
}
}
// Prepare flat node list which can be used for non recursive lookups later
void ArmaturePopulate::BuildNodeList(const aiNode *current_node,
std::vector<aiNode *> &nodes) {
ai_assert(nullptr != current_node);
for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) {
aiNode *child = current_node->mChildren[nodeId];
ai_assert(child);
if (child->mNumMeshes == 0) {
nodes.emplace_back(child);
}
BuildNodeList(child, nodes);
}
}
// 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,
const aiScene*,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
std::vector<aiNode *> &node_stack) {
if (node_stack.empty()) {
return;
}
ai_assert(nullptr != root_node);
for (aiBone *bone : bones) {
ai_assert(bone);
aiNode *node = GetNodeFromStack(bone->mName, node_stack);
if (node == nullptr) {
node_stack.clear();
BuildNodeList(root_node, node_stack);
ASSIMP_LOG_VERBOSE_DEBUG("Resetting bone stack: nullptr element ", bone->mName.C_Str());
node = GetNodeFromStack(bone->mName, node_stack);
if (nullptr == node) {
ASSIMP_LOG_ERROR("serious import issue node for bone was not detected");
continue;
}
}
ASSIMP_LOG_VERBOSE_DEBUG("Successfully added bone[", bone->mName.C_Str(), "] to stack and bone node is: ", node->mName.C_Str());
bone_stack.insert(std::pair<aiBone *, aiNode *>(bone, node));
}
}
// Returns the armature root node
// 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) {
while (nullptr != bone_node) {
if (!IsBoneNode(bone_node->mName, bone_list)) {
ASSIMP_LOG_VERBOSE_DEBUG("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str());
return bone_node;
}
bone_node = bone_node->mParent;
}
ASSIMP_LOG_ERROR("GetArmatureRoot() can't find armature!");
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
// (serious to be fixed) Known flaw: nodes which have more than one bone could
// be prematurely dropped from stack
aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name,
std::vector<aiNode *> &nodes) {
std::vector<aiNode *>::iterator iter;
aiNode *found = nullptr;
for (iter = nodes.begin(); iter < nodes.end(); ++iter) {
aiNode *element = *iter;
ai_assert(nullptr != element);
// node valid and node name matches
if (element->mName == node_name) {
found = element;
break;
}
}
if (found != nullptr) {
ASSIMP_LOG_INFO("Removed node from stack: ", found->mName.C_Str());
// now pop the element from the node list
nodes.erase(iter);
return found;
}
// unique names can cause this problem
ASSIMP_LOG_ERROR("[Serious] GetNodeFromStack() can't find node from stack!");
return nullptr;
}
} // Namespace Assimp

View file

@ -0,0 +1,112 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* 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
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
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.
----------------------------------------------------------------------
*/
#ifndef ARMATURE_POPULATE_H_
#define ARMATURE_POPULATE_H_
#include "Common/BaseProcess.h"
#include <assimp/BaseImporter.h>
#include <vector>
#include <map>
struct aiNode;
struct aiBone;
namespace Assimp {
// ---------------------------------------------------------------------------
/** Armature Populate: This is a post process designed
* To save you time when importing models into your game engines
* This was originally designed only for fbx but will work with other formats
* it is intended to auto populate aiBone data with armature and the aiNode
* This is very useful when dealing with skinned meshes
* or when dealing with many different skeletons
* It's off by default but recommend that you try it and use it
* It should reduce down any glue code you have in your
* importers
* You can contact RevoluPowered <gordon@gordonite.tech>
* For more info about this
*/
class ASSIMP_API ArmaturePopulate : public BaseProcess {
public:
/// The default class constructor.
ArmaturePopulate();
/// The class destructor.
virtual ~ArmaturePopulate();
/// Overwritten, @see BaseProcess
virtual bool IsActive( unsigned int pFlags ) const;
/// Overwritten, @see BaseProcess
virtual void SetupProperties( const Importer* pImp );
/// Overwritten, @see BaseProcess
virtual void Execute( aiScene* pScene );
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);
static void BuildNodeList(const aiNode *current_node,
std::vector<aiNode *> &nodes);
static void BuildBoneList(aiNode *current_node, const aiNode *root_node,
const aiScene *scene,
std::vector<aiBone *> &bones);
static void BuildBoneStack(aiNode *current_node, const aiNode *root_node,
const aiScene *scene,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
std::vector<aiNode *> &node_stack);
};
} // Namespace Assimp
#endif // SCALE_PROCESS_H_

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -55,54 +55,49 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
CalcTangentsProcess::CalcTangentsProcess()
: configMaxAngle( AI_DEG_TO_RAD(45.f) )
, configSourceUV( 0 ) {
CalcTangentsProcess::CalcTangentsProcess() :
configMaxAngle(float(AI_DEG_TO_RAD(45.f))), configSourceUV(0) {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
CalcTangentsProcess::~CalcTangentsProcess()
{
CalcTangentsProcess::~CalcTangentsProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool CalcTangentsProcess::IsActive( unsigned int pFlags) const
{
bool CalcTangentsProcess::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_CalcTangentSpace) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void CalcTangentsProcess::SetupProperties(const Importer* pImp)
{
ai_assert( NULL != pImp );
void CalcTangentsProcess::SetupProperties(const Importer *pImp) {
ai_assert(nullptr != pImp);
// get the current value of the property
configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f);
configMaxAngle = std::max(std::min(configMaxAngle,45.0f),0.0f);
configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE, 45.f);
configMaxAngle = std::max(std::min(configMaxAngle, 45.0f), 0.0f);
configMaxAngle = AI_DEG_TO_RAD(configMaxAngle);
configSourceUV = pImp->GetPropertyInteger(AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX,0);
configSourceUV = pImp->GetPropertyInteger(AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX, 0);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void CalcTangentsProcess::Execute( aiScene* pScene)
{
ai_assert( NULL != pScene );
void CalcTangentsProcess::Execute(aiScene *pScene) {
ai_assert(nullptr != pScene);
ASSIMP_LOG_DEBUG("CalcTangentsProcess begin");
bool bHas = false;
for ( unsigned int a = 0; a < pScene->mNumMeshes; a++ ) {
if(ProcessMesh( pScene->mMeshes[a],a))bHas = true;
for (unsigned int a = 0; a < pScene->mNumMeshes; a++) {
if (ProcessMesh(pScene->mMeshes[a], a)) bHas = true;
}
if ( bHas ) {
if (bHas) {
ASSIMP_LOG_INFO("CalcTangentsProcess finished. Tangents have been calculated");
} else {
ASSIMP_LOG_DEBUG("CalcTangentsProcess finished");
@ -111,8 +106,7 @@ void CalcTangentsProcess::Execute( aiScene* pScene)
// ------------------------------------------------------------------------------------------------
// Calculates tangents and bi-tangents for the given mesh
bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
{
bool CalcTangentsProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshIndex) {
// we assume that the mesh is still in the verbose vertex format where each face has its own set
// of vertices and no vertices are shared between faces. Sadly I don't know any quick test to
// assert() it here.
@ -124,54 +118,48 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// If the mesh consists of lines and/or points but not of
// triangles or higher-order polygons the normal vectors
// are undefined.
if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON)))
{
if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) {
ASSIMP_LOG_INFO("Tangents are undefined for line and point meshes");
return false;
}
// what we can check, though, is if the mesh has normals and texture coordinates. That's a requirement
if( pMesh->mNormals == NULL)
{
if (pMesh->mNormals == nullptr) {
ASSIMP_LOG_ERROR("Failed to compute tangents; need normals");
return false;
}
if( configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV] )
{
ASSIMP_LOG_ERROR((Formatter::format("Failed to compute tangents; need UV data in channel"),configSourceUV));
if (configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV]) {
ASSIMP_LOG_ERROR("Failed to compute tangents; need UV data in channel", configSourceUV);
return false;
}
const float angleEpsilon = 0.9999f;
std::vector<bool> vertexDone( pMesh->mNumVertices, false);
std::vector<bool> vertexDone(pMesh->mNumVertices, false);
const float qnan = get_qnan();
// create space for the tangents and bitangents
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
const aiVector3D* meshPos = pMesh->mVertices;
const aiVector3D* meshNorm = pMesh->mNormals;
const aiVector3D* meshTex = pMesh->mTextureCoords[configSourceUV];
aiVector3D* meshTang = pMesh->mTangents;
aiVector3D* meshBitang = pMesh->mBitangents;
const aiVector3D *meshPos = pMesh->mVertices;
const aiVector3D *meshNorm = pMesh->mNormals;
const aiVector3D *meshTex = pMesh->mTextureCoords[configSourceUV];
aiVector3D *meshTang = pMesh->mTangents;
aiVector3D *meshBitang = pMesh->mBitangents;
// calculate the tangent and bitangent for every face
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
const aiFace& face = pMesh->mFaces[a];
if (face.mNumIndices < 3)
{
for (unsigned int a = 0; a < pMesh->mNumFaces; a++) {
const aiFace &face = pMesh->mFaces[a];
if (face.mNumIndices < 3) {
// There are less than three indices, thus the tangent vector
// is not defined. We are finished with these vertices now,
// their tangent vectors are set to qnan.
for (unsigned int i = 0; i < face.mNumIndices;++i)
{
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
unsigned int idx = face.mIndices[i];
vertexDone [idx] = true;
meshTang [idx] = aiVector3D(qnan);
meshBitang [idx] = aiVector3D(qnan);
vertexDone[idx] = true;
meshTang[idx] = aiVector3D(qnan);
meshBitang[idx] = aiVector3D(qnan);
}
continue;
@ -190,9 +178,11 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y;
float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f;
// when t1, t2, t3 in same position in UV space, just use default UV direction.
if ( sx * ty == sy * tx ) {
sx = 0.0; sy = 1.0;
tx = 1.0; ty = 0.0;
if (sx * ty == sy * tx) {
sx = 0.0;
sy = 1.0;
tx = 1.0;
ty = 0.0;
}
// tangent points in the direction where to positive X axis of the texture coord's would point in model space
@ -201,18 +191,19 @@ 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 ) {
for (unsigned int b = 0; b < face.mNumIndices; ++b) {
unsigned int p = face.mIndices[b];
// 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.NormalizeSafe(); localBitangent.NormalizeSafe();
aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]) - localTangent * (bitangent * localTangent);
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);
@ -228,31 +219,28 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
}
// and write it into the mesh.
meshTang[ p ] = localTangent;
meshBitang[ p ] = localBitangent;
meshTang[p] = localTangent;
meshBitang[p] = localBitangent;
}
}
// create a helper to quickly find locally close vertices among the vertex array
// FIX: check whether we can reuse the SpatialSort of a previous step
SpatialSort* vertexFinder = NULL;
SpatialSort _vertexFinder;
float posEpsilon;
if (shared)
{
std::vector<std::pair<SpatialSort,float> >* avf;
shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
if (avf)
{
std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
SpatialSort *vertexFinder = nullptr;
SpatialSort _vertexFinder;
float posEpsilon = 10e-6f;
if (shared) {
std::vector<std::pair<SpatialSort, float>> *avf;
shared->GetProperty(AI_SPP_SPATIAL_SORT, avf);
if (avf) {
std::pair<SpatialSort, float> &blubb = avf->operator[](meshIndex);
vertexFinder = &blubb.first;
posEpsilon = blubb.second;;
posEpsilon = blubb.second;
;
}
}
if (!vertexFinder)
{
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
if (!vertexFinder) {
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof(aiVector3D));
vertexFinder = &_vertexFinder;
posEpsilon = ComputePositionEpsilon(pMesh);
}
@ -263,56 +251,52 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// in the second pass we now smooth out all tangents and bitangents at the same local position
// if they are not too far off.
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
{
if( vertexDone[a])
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
if (vertexDone[a])
continue;
const aiVector3D& origPos = pMesh->mVertices[a];
const aiVector3D& origNorm = pMesh->mNormals[a];
const aiVector3D& origTang = pMesh->mTangents[a];
const aiVector3D& origBitang = pMesh->mBitangents[a];
closeVertices.resize( 0 );
const aiVector3D &origPos = pMesh->mVertices[a];
const aiVector3D &origNorm = pMesh->mNormals[a];
const aiVector3D &origTang = pMesh->mTangents[a];
const aiVector3D &origBitang = pMesh->mBitangents[a];
closeVertices.resize(0);
// find all vertices close to that position
vertexFinder->FindPositions( origPos, posEpsilon, verticesFound);
vertexFinder->FindPositions(origPos, posEpsilon, verticesFound);
closeVertices.reserve (verticesFound.size()+5);
closeVertices.push_back( a);
closeVertices.reserve(verticesFound.size() + 5);
closeVertices.push_back(a);
// look among them for other vertices sharing the same normal and a close-enough tangent/bitangent
for( unsigned int b = 0; b < verticesFound.size(); b++)
{
for (unsigned int b = 0; b < verticesFound.size(); b++) {
unsigned int idx = verticesFound[b];
if( vertexDone[idx])
if (vertexDone[idx])
continue;
if( meshNorm[idx] * origNorm < angleEpsilon)
if (meshNorm[idx] * origNorm < angleEpsilon)
continue;
if( meshTang[idx] * origTang < fLimit)
if (meshTang[idx] * origTang < fLimit)
continue;
if( meshBitang[idx] * origBitang < fLimit)
if (meshBitang[idx] * origBitang < fLimit)
continue;
// it's similar enough -> add it to the smoothing group
closeVertices.push_back( idx);
closeVertices.push_back(idx);
vertexDone[idx] = true;
}
// smooth the tangents and bitangents of all vertices that were found to be close enough
aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0);
for( unsigned int b = 0; b < closeVertices.size(); ++b)
{
smoothTangent += meshTang[ closeVertices[b] ];
smoothBitangent += meshBitang[ closeVertices[b] ];
aiVector3D smoothTangent(0, 0, 0), smoothBitangent(0, 0, 0);
for (unsigned int b = 0; b < closeVertices.size(); ++b) {
smoothTangent += meshTang[closeVertices[b]];
smoothBitangent += meshBitang[closeVertices[b]];
}
smoothTangent.Normalize();
smoothBitangent.Normalize();
// and write it back into all affected tangents
for( unsigned int b = 0; b < closeVertices.size(); ++b)
{
meshTang[ closeVertices[b] ] = smoothTangent;
meshBitang[ closeVertices[b] ] = smoothBitangent;
for (unsigned int b = 0; b < closeVertices.size(); ++b) {
meshTang[closeVertices[b]] = smoothTangent;
meshBitang[closeVertices[b]] = smoothBitangent;
}
}
return true;

View file

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

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -122,7 +122,7 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
const aiFace& face = mesh->mFaces[fidx];
if (face.mNumIndices < 3) continue; // triangles and polygons only, please
unsigned int small = face.mNumIndices, large = small;
unsigned int smallV = face.mNumIndices, large = smallV;
bool zero = false, one = false, round_to_zero = false;
// Check whether this face lies on a UV seam. We can just guess,
@ -133,7 +133,7 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
{
if (out[face.mIndices[n]].x < LOWER_LIMIT)
{
small = n;
smallV = n;
// If we have a U value very close to 0 we can't
// round the others to 0, too.
@ -151,7 +151,7 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
one = true;
}
}
if (small != face.mNumIndices && large != face.mNumIndices)
if (smallV != face.mNumIndices && large != face.mNumIndices)
{
for (unsigned int n = 0; n < face.mNumIndices;++n)
{

View file

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

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -49,10 +49,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* better location.
*/
#include "ConvertToLHProcess.h"
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
using namespace Assimp;
@ -62,8 +61,10 @@ using namespace Assimp;
namespace {
template <typename aiMeshType>
void flipUVs(aiMeshType* pMesh) {
if (pMesh == nullptr) { return; }
void flipUVs(aiMeshType *pMesh) {
if (pMesh == nullptr) {
return;
}
// mirror texture y coordinate
for (unsigned int tcIdx = 0; tcIdx < AI_MAX_NUMBER_OF_TEXTURECOORDS; tcIdx++) {
if (!pMesh->HasTextureCoords(tcIdx)) {
@ -80,8 +81,8 @@ void flipUVs(aiMeshType* pMesh) {
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
MakeLeftHandedProcess::MakeLeftHandedProcess()
: BaseProcess() {
MakeLeftHandedProcess::MakeLeftHandedProcess() :
BaseProcess() {
// empty
}
@ -93,40 +94,36 @@ MakeLeftHandedProcess::~MakeLeftHandedProcess() {
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool MakeLeftHandedProcess::IsActive( unsigned int pFlags) const
{
bool MakeLeftHandedProcess::IsActive(unsigned int pFlags) const {
return 0 != (pFlags & aiProcess_MakeLeftHanded);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void MakeLeftHandedProcess::Execute( aiScene* pScene)
{
void MakeLeftHandedProcess::Execute(aiScene *pScene) {
// Check for an existent root node to proceed
ai_assert(pScene->mRootNode != NULL);
ai_assert(pScene->mRootNode != nullptr);
ASSIMP_LOG_DEBUG("MakeLeftHandedProcess begin");
// recursively convert all the nodes
ProcessNode( pScene->mRootNode, aiMatrix4x4());
ProcessNode(pScene->mRootNode, aiMatrix4x4());
// process the meshes accordingly
for ( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) {
ProcessMesh( pScene->mMeshes[ a ] );
for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
ProcessMesh(pScene->mMeshes[a]);
}
// process the materials accordingly
for ( unsigned int a = 0; a < pScene->mNumMaterials; ++a ) {
ProcessMaterial( pScene->mMaterials[ a ] );
for (unsigned int a = 0; a < pScene->mNumMaterials; ++a) {
ProcessMaterial(pScene->mMaterials[a]);
}
// transform all animation channels as well
for( unsigned int a = 0; a < pScene->mNumAnimations; a++)
{
aiAnimation* anim = pScene->mAnimations[a];
for( unsigned int b = 0; b < anim->mNumChannels; b++)
{
aiNodeAnim* nodeAnim = anim->mChannels[b];
ProcessAnimation( nodeAnim);
for (unsigned int a = 0; a < pScene->mNumAnimations; a++) {
aiAnimation *anim = pScene->mAnimations[a];
for (unsigned int b = 0; b < anim->mNumChannels; b++) {
aiNodeAnim *nodeAnim = anim->mChannels[b];
ProcessAnimation(nodeAnim);
}
}
ASSIMP_LOG_DEBUG("MakeLeftHandedProcess finished");
@ -134,8 +131,7 @@ void MakeLeftHandedProcess::Execute( aiScene* pScene)
// ------------------------------------------------------------------------------------------------
// Recursively converts a node, all of its children and all of its meshes
void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation)
{
void MakeLeftHandedProcess::ProcessNode(aiNode *pNode, const aiMatrix4x4 &pParentGlobalRotation) {
// mirror all base vectors at the local Z axis
pNode->mTransformation.c1 = -pNode->mTransformation.c1;
pNode->mTransformation.c2 = -pNode->mTransformation.c2;
@ -150,43 +146,38 @@ void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pPare
pNode->mTransformation.d3 = -pNode->mTransformation.d3; // useless, but anyways...
// continue for all children
for( size_t a = 0; a < pNode->mNumChildren; ++a ) {
ProcessNode( pNode->mChildren[ a ], pParentGlobalRotation * pNode->mTransformation );
for (size_t a = 0; a < pNode->mNumChildren; ++a) {
ProcessNode(pNode->mChildren[a], pParentGlobalRotation * pNode->mTransformation);
}
}
// ------------------------------------------------------------------------------------------------
// Converts a single mesh to left handed coordinates.
void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) {
if ( nullptr == pMesh ) {
ASSIMP_LOG_ERROR( "Nullptr to mesh found." );
void MakeLeftHandedProcess::ProcessMesh(aiMesh *pMesh) {
if (nullptr == pMesh) {
ASSIMP_LOG_ERROR("Nullptr to mesh found.");
return;
}
// mirror positions, normals and stuff along the Z axis
for( size_t a = 0; a < pMesh->mNumVertices; ++a)
{
for (size_t a = 0; a < pMesh->mNumVertices; ++a) {
pMesh->mVertices[a].z *= -1.0f;
if (pMesh->HasNormals()) {
pMesh->mNormals[a].z *= -1.0f;
}
if( pMesh->HasTangentsAndBitangents())
{
if (pMesh->HasTangentsAndBitangents()) {
pMesh->mTangents[a].z *= -1.0f;
pMesh->mBitangents[a].z *= -1.0f;
}
}
// mirror anim meshes positions, normals and stuff along the Z axis
for (size_t m = 0; m < pMesh->mNumAnimMeshes; ++m)
{
for (size_t a = 0; a < pMesh->mAnimMeshes[m]->mNumVertices; ++a)
{
for (size_t m = 0; m < pMesh->mNumAnimMeshes; ++m) {
for (size_t a = 0; a < pMesh->mAnimMeshes[m]->mNumVertices; ++a) {
pMesh->mAnimMeshes[m]->mVertices[a].z *= -1.0f;
if (pMesh->mAnimMeshes[m]->HasNormals()) {
pMesh->mAnimMeshes[m]->mNormals[a].z *= -1.0f;
}
if (pMesh->mAnimMeshes[m]->HasTangentsAndBitangents())
{
if (pMesh->mAnimMeshes[m]->HasTangentsAndBitangents()) {
pMesh->mAnimMeshes[m]->mTangents[a].z *= -1.0f;
pMesh->mAnimMeshes[m]->mBitangents[a].z *= -1.0f;
}
@ -194,9 +185,8 @@ void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) {
}
// mirror offset matrices of all bones
for( size_t a = 0; a < pMesh->mNumBones; ++a)
{
aiBone* bone = pMesh->mBones[a];
for (size_t a = 0; a < pMesh->mNumBones; ++a) {
aiBone *bone = pMesh->mBones[a];
bone->mOffsetMatrix.a3 = -bone->mOffsetMatrix.a3;
bone->mOffsetMatrix.b3 = -bone->mOffsetMatrix.b3;
bone->mOffsetMatrix.d3 = -bone->mOffsetMatrix.d3;
@ -206,29 +196,28 @@ void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) {
}
// mirror bitangents as well as they're derived from the texture coords
if( pMesh->HasTangentsAndBitangents())
{
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
if (pMesh->HasTangentsAndBitangents()) {
for (unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mBitangents[a] *= -1.0f;
}
}
// ------------------------------------------------------------------------------------------------
// Converts a single material to left handed coordinates.
void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) {
if ( nullptr == _mat ) {
ASSIMP_LOG_ERROR( "Nullptr to aiMaterial found." );
void MakeLeftHandedProcess::ProcessMaterial(aiMaterial *_mat) {
if (nullptr == _mat) {
ASSIMP_LOG_ERROR("Nullptr to aiMaterial found.");
return;
}
aiMaterial* mat = (aiMaterial*)_mat;
for (unsigned int a = 0; a < mat->mNumProperties;++a) {
aiMaterialProperty* prop = mat->mProperties[a];
aiMaterial *mat = (aiMaterial *)_mat;
for (unsigned int a = 0; a < mat->mNumProperties; ++a) {
aiMaterialProperty *prop = mat->mProperties[a];
// Mapping axis for UV mappings?
if (!::strcmp( prop->mKey.data, "$tex.mapaxis")) {
ai_assert( prop->mDataLength >= sizeof(aiVector3D)); /* something is wrong with the validation if we end up here */
aiVector3D* pff = (aiVector3D*)prop->mData;
if (!::strcmp(prop->mKey.data, "$tex.mapaxis")) {
ai_assert(prop->mDataLength >= sizeof(aiVector3D)); // something is wrong with the validation if we end up here
aiVector3D *pff = (aiVector3D *)prop->mData;
pff->z *= -1.f;
}
}
@ -236,15 +225,13 @@ void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) {
// ------------------------------------------------------------------------------------------------
// Converts the given animation to LH coordinates.
void MakeLeftHandedProcess::ProcessAnimation( aiNodeAnim* pAnim)
{
void MakeLeftHandedProcess::ProcessAnimation(aiNodeAnim *pAnim) {
// position keys
for( unsigned int a = 0; a < pAnim->mNumPositionKeys; a++)
for (unsigned int a = 0; a < pAnim->mNumPositionKeys; a++)
pAnim->mPositionKeys[a].mValue.z *= -1.0f;
// rotation keys
for( unsigned int a = 0; a < pAnim->mNumRotationKeys; a++)
{
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;
@ -258,55 +245,50 @@ void MakeLeftHandedProcess::ProcessAnimation( aiNodeAnim* pAnim)
}
#endif // !! ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
#ifndef ASSIMP_BUILD_NO_FLIPUVS_PROCESS
#ifndef ASSIMP_BUILD_NO_FLIPUVS_PROCESS
// # FlipUVsProcess
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FlipUVsProcess::FlipUVsProcess()
{}
FlipUVsProcess::FlipUVsProcess() {}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FlipUVsProcess::~FlipUVsProcess()
{}
FlipUVsProcess::~FlipUVsProcess() {}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FlipUVsProcess::IsActive( unsigned int pFlags) const
{
bool FlipUVsProcess::IsActive(unsigned int pFlags) const {
return 0 != (pFlags & aiProcess_FlipUVs);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FlipUVsProcess::Execute( aiScene* pScene)
{
void FlipUVsProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("FlipUVsProcess begin");
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i)
ProcessMesh(pScene->mMeshes[i]);
for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
for (unsigned int i = 0; i < pScene->mNumMaterials; ++i)
ProcessMaterial(pScene->mMaterials[i]);
ASSIMP_LOG_DEBUG("FlipUVsProcess finished");
}
// ------------------------------------------------------------------------------------------------
// Converts a single material
void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat)
{
aiMaterial* mat = (aiMaterial*)_mat;
for (unsigned int a = 0; a < mat->mNumProperties;++a) {
aiMaterialProperty* prop = mat->mProperties[a];
if( !prop ) {
ASSIMP_LOG_DEBUG( "Property is null" );
void FlipUVsProcess::ProcessMaterial(aiMaterial *_mat) {
aiMaterial *mat = (aiMaterial *)_mat;
for (unsigned int a = 0; a < mat->mNumProperties; ++a) {
aiMaterialProperty *prop = mat->mProperties[a];
if (!prop) {
ASSIMP_LOG_VERBOSE_DEBUG("Property is null");
continue;
}
// UV transformation key?
if (!::strcmp( prop->mKey.data, "$tex.uvtrafo")) {
ai_assert( prop->mDataLength >= sizeof(aiUVTransform)); /* something is wrong with the validation if we end up here */
aiUVTransform* uv = (aiUVTransform*)prop->mData;
if (!::strcmp(prop->mKey.data, "$tex.uvtrafo")) {
ai_assert(prop->mDataLength >= sizeof(aiUVTransform)); // something is wrong with the validation if we end up here
aiUVTransform *uv = (aiUVTransform *)prop->mData;
// just flip it, that's everything
uv->mTranslation.y *= -1.f;
@ -317,8 +299,7 @@ void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat)
// ------------------------------------------------------------------------------------------------
// Converts a single mesh
void FlipUVsProcess::ProcessMesh( aiMesh* pMesh)
{
void FlipUVsProcess::ProcessMesh(aiMesh *pMesh) {
flipUVs(pMesh);
for (unsigned int idx = 0; idx < pMesh->mNumAnimMeshes; idx++) {
flipUVs(pMesh->mAnimMeshes[idx]);
@ -326,44 +307,38 @@ void FlipUVsProcess::ProcessMesh( aiMesh* pMesh)
}
#endif // !ASSIMP_BUILD_NO_FLIPUVS_PROCESS
#ifndef ASSIMP_BUILD_NO_FLIPWINDING_PROCESS
#ifndef ASSIMP_BUILD_NO_FLIPWINDING_PROCESS
// # FlipWindingOrderProcess
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FlipWindingOrderProcess::FlipWindingOrderProcess()
{}
FlipWindingOrderProcess::FlipWindingOrderProcess() {}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FlipWindingOrderProcess::~FlipWindingOrderProcess()
{}
FlipWindingOrderProcess::~FlipWindingOrderProcess() {}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FlipWindingOrderProcess::IsActive( unsigned int pFlags) const
{
bool FlipWindingOrderProcess::IsActive(unsigned int pFlags) const {
return 0 != (pFlags & aiProcess_FlipWindingOrder);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FlipWindingOrderProcess::Execute( aiScene* pScene)
{
void FlipWindingOrderProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("FlipWindingOrderProcess begin");
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i)
ProcessMesh(pScene->mMeshes[i]);
ASSIMP_LOG_DEBUG("FlipWindingOrderProcess finished");
}
// ------------------------------------------------------------------------------------------------
// Converts a single mesh
void FlipWindingOrderProcess::ProcessMesh( aiMesh* pMesh)
{
void FlipWindingOrderProcess::ProcessMesh(aiMesh *pMesh) {
// invert the order of all faces in this mesh
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
aiFace& face = pMesh->mFaces[a];
for (unsigned int a = 0; a < pMesh->mNumFaces; a++) {
aiFace &face = pMesh->mFaces[a];
for (unsigned int b = 0; b < face.mNumIndices / 2; b++) {
std::swap(face.mIndices[b], face.mIndices[face.mNumIndices - 1 - b]);
}
@ -371,39 +346,34 @@ void FlipWindingOrderProcess::ProcessMesh( aiMesh* pMesh)
// invert the order of all components in this mesh anim meshes
for (unsigned int m = 0; m < pMesh->mNumAnimMeshes; m++) {
aiAnimMesh* animMesh = pMesh->mAnimMeshes[m];
aiAnimMesh *animMesh = pMesh->mAnimMeshes[m];
unsigned int numVertices = animMesh->mNumVertices;
if (animMesh->HasPositions()) {
for (unsigned int a = 0; a < numVertices; a++)
{
for (unsigned int a = 0; a < numVertices; a++) {
std::swap(animMesh->mVertices[a], animMesh->mVertices[numVertices - 1 - a]);
}
}
if (animMesh->HasNormals()) {
for (unsigned int a = 0; a < numVertices; a++)
{
for (unsigned int a = 0; a < numVertices; a++) {
std::swap(animMesh->mNormals[a], animMesh->mNormals[numVertices - 1 - a]);
}
}
for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++) {
if (animMesh->HasTextureCoords(i)) {
for (unsigned int a = 0; a < numVertices; a++)
{
for (unsigned int a = 0; a < numVertices; a++) {
std::swap(animMesh->mTextureCoords[i][a], animMesh->mTextureCoords[i][numVertices - 1 - a]);
}
}
}
if (animMesh->HasTangentsAndBitangents()) {
for (unsigned int a = 0; a < numVertices; a++)
{
for (unsigned int a = 0; a < numVertices; a++) {
std::swap(animMesh->mTangents[a], animMesh->mTangents[numVertices - 1 - a]);
std::swap(animMesh->mBitangents[a], animMesh->mBitangents[numVertices - 1 - a]);
}
}
for (unsigned int v = 0; v < AI_MAX_NUMBER_OF_COLOR_SETS; v++) {
if (animMesh->HasVertexColors(v)) {
for (unsigned int a = 0; a < numVertices; a++)
{
for (unsigned int a = 0; a < numVertices; a++) {
std::swap(animMesh->mColors[v][a], animMesh->mColors[v][numVertices - 1 - a]);
}
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -137,8 +137,9 @@ public:
// -------------------------------------------------------------------
void Execute( aiScene* pScene);
protected:
void ProcessMesh( aiMesh* pMesh);
public:
/** Some other types of post-processing require winding order flips */
static void ProcessMesh( aiMesh* pMesh);
};
// ---------------------------------------------------------------------------

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -148,7 +148,7 @@ void DeboneProcess::Execute( aiScene* pScene)
}
if(!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO_F("Removed %u bones. Input bones:", in - out, ". Output bones: ", out);
ASSIMP_LOG_INFO("Removed %u bones. Input bones:", in - out, ". Output bones: ", out);
}
// and destroy the source mesh. It should be completely contained inside the new submeshes
@ -414,7 +414,8 @@ void DeboneProcess::UpdateNode(aiNode* pNode) const
}
if( pNode->mNumMeshes > 0 ) {
delete [] pNode->mMeshes; pNode->mMeshes = NULL;
delete[] pNode->mMeshes;
pNode->mMeshes = nullptr;
}
pNode->mNumMeshes = static_cast<unsigned int>(newMeshList.size());

View file

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

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -98,12 +98,14 @@ void DropFaceNormalsProcess::Execute( aiScene* pScene) {
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* pMesh) {
if (NULL == pMesh->mNormals) {
bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* mesh) {
ai_assert(nullptr != mesh);
if (nullptr == mesh->mNormals) {
return false;
}
delete[] pMesh->mNormals;
pMesh->mNormals = nullptr;
delete[] mesh->mNormals;
mesh->mNormals = nullptr;
return true;
}

View file

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

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -41,6 +40,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "EmbedTexturesProcess.h"
#include <assimp/IOStream.hpp>
#include <assimp/IOSystem.hpp>
#include <assimp/ParsingUtils.h>
#include "ProcessHelper.h"
@ -48,11 +49,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
EmbedTexturesProcess::EmbedTexturesProcess()
: BaseProcess() {
EmbedTexturesProcess::EmbedTexturesProcess() :
BaseProcess() {
// empty
}
EmbedTexturesProcess::~EmbedTexturesProcess() {
// empty
}
bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const {
@ -62,15 +65,16 @@ bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const {
void EmbedTexturesProcess::SetupProperties(const Importer* pImp) {
mRootPath = pImp->GetPropertyString("sourceFilePath");
mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u);
mIOHandler = pImp->GetIOHandler();
}
void EmbedTexturesProcess::Execute(aiScene* pScene) {
if (pScene == nullptr || pScene->mRootNode == nullptr) return;
if (pScene == nullptr || pScene->mRootNode == nullptr || mIOHandler == nullptr){
return;
}
aiString path;
uint32_t embeddedTexturesCount = 0u;
for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) {
auto material = pScene->mMaterials[matId];
@ -85,7 +89,7 @@ void EmbedTexturesProcess::Execute(aiScene* pScene) {
// Indeed embed
if (addTexture(pScene, path.data)) {
auto embeddedTextureId = pScene->mNumTextures - 1u;
::ai_snprintf(path.data, 1024, "*%u", embeddedTextureId);
path.length = ::ai_snprintf(path.data, 1024, "*%u", embeddedTextureId);
material->AddProperty(&path, AI_MATKEY_TEXTURE(tt, texId));
embeddedTexturesCount++;
}
@ -93,41 +97,46 @@ void EmbedTexturesProcess::Execute(aiScene* pScene) {
}
}
ASSIMP_LOG_INFO_F("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." );
ASSIMP_LOG_INFO("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." );
}
bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const {
bool EmbedTexturesProcess::addTexture(aiScene *pScene, const std::string &path) const {
std::streampos imageSize = 0;
std::string imagePath = path;
// Test path directly
std::ifstream file(imagePath, std::ios::binary | std::ios::ate);
if ((imageSize = file.tellg()) == std::streampos(-1)) {
ASSIMP_LOG_WARN_F("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder.");
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;
file.open(imagePath, std::ios::binary | std::ios::ate);
if ((imageSize = file.tellg()) == std::streampos(-1)) {
if (!mIOHandler->Exists(imagePath)) {
// Test path basename in root path
imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u);
file.open(imagePath, std::ios::binary | std::ios::ate);
if ((imageSize = file.tellg()) == std::streampos(-1)) {
ASSIMP_LOG_ERROR_F("EmbedTexturesProcess: Unable to embed texture: ", path, ".");
if (!mIOHandler->Exists(imagePath)) {
ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, ".");
return false;
}
}
}
IOStream* pFile = mIOHandler->Open(imagePath);
if (pFile == nullptr) {
ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, ".");
return false;
}
imageSize = pFile->FileSize();
aiTexel* imageContent = new aiTexel[ 1ul + static_cast<unsigned long>( imageSize ) / sizeof(aiTexel)];
file.seekg(0, std::ios::beg);
file.read(reinterpret_cast<char*>(imageContent), imageSize);
pFile->Seek(0, aiOrigin_SET);
pFile->Read(reinterpret_cast<char*>(imageContent), imageSize, 1);
mIOHandler->Close(pFile);
// Enlarging the textures table
unsigned int textureId = pScene->mNumTextures++;
auto oldTextures = pScene->mTextures;
pScene->mTextures = new aiTexture*[pScene->mNumTextures];
::memmove(pScene->mTextures, oldTextures, sizeof(aiTexture*) * (pScene->mNumTextures - 1u));
delete [] oldTextures;
// Add the new texture
auto pTexture = new aiTexture;
@ -136,7 +145,7 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const {
pTexture->pcData = imageContent;
auto extension = path.substr(path.find_last_of('.') + 1u);
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
extension = ai_tolower(extension);
if (extension == "jpeg") {
extension = "jpg";
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -48,6 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
struct aiNode;
class IOSystem;
namespace Assimp {
/**
@ -76,10 +78,11 @@ public:
private:
// Resolve the path and add the file content to the scene as a texture.
bool addTexture(aiScene* pScene, std::string path) const;
bool addTexture(aiScene *pScene, const std::string &path) const;
private:
std::string mRootPath;
IOSystem* mIOHandler = nullptr;
};
} // namespace Assimp

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -45,25 +43,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Implementation of the FindDegenerates post-process step.
*/
// internal headers
#include "ProcessHelper.h"
#include "FindDegenerates.h"
#include <assimp/Exceptional.h>
#include <unordered_map>
using namespace Assimp;
//remove mesh at position 'index' from the scene
static void removeMesh(aiScene* pScene, unsigned const index);
//correct node indices to meshes and remove references to deleted mesh
static void updateSceneGraph(aiNode* pNode, unsigned const index);
// 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);
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FindDegeneratesProcess::FindDegeneratesProcess()
: mConfigRemoveDegenerates( false )
, mConfigCheckAreaOfTriangle( false ){
FindDegeneratesProcess::FindDegeneratesProcess() :
mConfigRemoveDegenerates( false ),
mConfigCheckAreaOfTriangle( false ){
// empty
}
@ -91,50 +87,50 @@ void FindDegeneratesProcess::SetupProperties(const Importer* pImp) {
// Executes the post processing step on the given imported data.
void FindDegeneratesProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin");
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
//Do not process point cloud, ExecuteOnMesh works only with faces data
if ( nullptr == pScene) {
return;
}
std::unordered_map<unsigned int, unsigned int> meshMap;
meshMap.reserve(pScene->mNumMeshes);
const unsigned int originalNumMeshes = pScene->mNumMeshes;
unsigned int targetIndex = 0;
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
// Do not process point cloud, ExecuteOnMesh works only with faces data
if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) {
removeMesh(pScene, i);
--i; //the current i is removed, do not skip the next one
delete pScene->mMeshes[i];
// Not strictly required, but clean:
pScene->mMeshes[i] = nullptr;
} else {
meshMap[i] = targetIndex;
pScene->mMeshes[targetIndex] = pScene->mMeshes[i];
++targetIndex;
}
}
pScene->mNumMeshes = targetIndex;
if (meshMap.size() < originalNumMeshes) {
updateSceneGraph(pScene->mRootNode, meshMap);
}
ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished");
}
static void removeMesh(aiScene* pScene, unsigned const index) {
//we start at index and copy the pointers one position forward
//save the mesh pointer to delete it later
auto delete_me = pScene->mMeshes[index];
for (unsigned i = index; i < pScene->mNumMeshes - 1; ++i) {
pScene->mMeshes[i] = pScene->mMeshes[i+1];
}
pScene->mMeshes[pScene->mNumMeshes - 1] = nullptr;
--(pScene->mNumMeshes);
delete delete_me;
//removing a mesh also requires updating all references to it in the scene graph
updateSceneGraph(pScene->mRootNode, index);
}
static void updateSceneGraph(aiNode* pNode, unsigned const index) {
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) {
if (pNode->mMeshes[i] > index) {
--(pNode->mMeshes[i]);
continue;
}
if (pNode->mMeshes[i] == index) {
for (unsigned j = i; j < pNode->mNumMeshes -1; ++j) {
pNode->mMeshes[j] = pNode->mMeshes[j+1];
}
--(pNode->mNumMeshes);
--i;
continue;
const unsigned int sourceMeshIndex = pNode->mMeshes[i];
auto it = meshMap.find(sourceMeshIndex);
if (it != meshMap.end()) {
pNode->mMeshes[targetIndex] = it->second;
++targetIndex;
}
}
pNode->mNumMeshes = targetIndex;
//recurse to all children
for (unsigned i = 0; i < pNode->mNumChildren; ++i) {
updateSceneGraph(pNode->mChildren[i], index);
updateSceneGraph(pNode->mChildren[i], meshMap);
}
}
@ -225,7 +221,7 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
if ( mConfigCheckAreaOfTriangle ) {
if ( face.mNumIndices == 3 ) {
ai_real area = calculateAreaOfTriangle( face, mesh );
if ( area < 1e-6 ) {
if (area < ai_epsilon) {
if ( mConfigRemoveDegenerates ) {
remove_me[ a ] = true;
++deg;
@ -289,13 +285,13 @@ evil_jump_outside:
if (!mesh->mNumFaces) {
//The whole mesh consists of degenerated faces
//signal upward, that this mesh should be deleted.
ASSIMP_LOG_DEBUG("FindDegeneratesProcess removed a mesh full of degenerated primitives");
ASSIMP_LOG_VERBOSE_DEBUG("FindDegeneratesProcess removed a mesh full of degenerated primitives");
return true;
}
}
if (deg && !DefaultLogger::isNullLogger()) {
ASSIMP_LOG_WARN_F( "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-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -137,7 +137,7 @@ void FindInstancesProcess::Execute( aiScene* pScene)
aiMesh* inst = pScene->mMeshes[i];
hashes[i] = GetMeshHash(inst);
// Find an appropriate epsilon
// Find an appropriate epsilon
// to compare position differences against
float epsilon = ComputePositionEpsilon(inst);
epsilon *= epsilon;
@ -243,7 +243,7 @@ void FindInstancesProcess::Execute( aiScene* pScene)
// Delete the instanced mesh, we don't need it anymore
delete inst;
pScene->mMeshes[i] = NULL;
pScene->mMeshes[i] = nullptr;
break;
}
}
@ -256,7 +256,7 @@ void FindInstancesProcess::Execute( aiScene* pScene)
ai_assert(0 != numMeshesOut);
if (numMeshesOut != pScene->mNumMeshes) {
// Collapse the meshes array by removing all NULL entries
// Collapse the meshes array by removing all nullptr entries
for (unsigned int real = 0, i = 0; real < numMeshesOut; ++i) {
if (pScene->mMeshes[i])
pScene->mMeshes[real++] = pScene->mMeshes[i];
@ -267,7 +267,7 @@ void FindInstancesProcess::Execute( aiScene* pScene)
// write to log
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO_F( "FindInstancesProcess finished. Found ", (pScene->mNumMeshes - numMeshesOut), " instances" );
ASSIMP_LOG_INFO( "FindInstancesProcess finished. Found ", (pScene->mNumMeshes - numMeshesOut), " instances" );
}
pScene->mNumMeshes = numMeshesOut;
} else {

View file

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

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -44,15 +42,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file Defines a post processing step to search an importer's output
for data that is obviously invalid */
#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
// internal headers
#include "FindInvalidDataProcess.h"
#include "ProcessHelper.h"
#include <assimp/Macros.h>
#include <assimp/Exceptional.h>
#include <assimp/qnan.h>
@ -60,9 +55,8 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FindInvalidDataProcess::FindInvalidDataProcess()
: configEpsilon(0.0)
, mIgnoreTexCoods( false ){
FindInvalidDataProcess::FindInvalidDataProcess() :
configEpsilon(0.0), mIgnoreTexCoods(false) {
// nothing to do here
}
@ -74,47 +68,47 @@ FindInvalidDataProcess::~FindInvalidDataProcess() {
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FindInvalidDataProcess::IsActive( unsigned int pFlags) const {
bool FindInvalidDataProcess::IsActive(unsigned int pFlags) const {
return 0 != (pFlags & aiProcess_FindInvalidData);
}
// ------------------------------------------------------------------------------------------------
// Setup import configuration
void FindInvalidDataProcess::SetupProperties(const Importer* pImp) {
void FindInvalidDataProcess::SetupProperties(const Importer *pImp) {
// Get the current value of AI_CONFIG_PP_FID_ANIM_ACCURACY
configEpsilon = (0 != pImp->GetPropertyFloat(AI_CONFIG_PP_FID_ANIM_ACCURACY,0.f));
configEpsilon = (0 != pImp->GetPropertyFloat(AI_CONFIG_PP_FID_ANIM_ACCURACY, 0.f));
mIgnoreTexCoods = pImp->GetPropertyBool(AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS, false);
}
// ------------------------------------------------------------------------------------------------
// Update mesh references in the node graph
void UpdateMeshReferences(aiNode* node, const std::vector<unsigned int>& meshMapping) {
if (node->mNumMeshes) {
void UpdateMeshReferences(aiNode *node, const std::vector<unsigned int> &meshMapping) {
if (node->mNumMeshes) {
unsigned int out = 0;
for (unsigned int a = 0; a < node->mNumMeshes;++a) {
for (unsigned int a = 0; a < node->mNumMeshes; ++a) {
unsigned int ref = node->mMeshes[a];
if (UINT_MAX != (ref = meshMapping[ref])) {
if (UINT_MAX != (ref = meshMapping[ref])) {
node->mMeshes[out++] = ref;
}
}
// just let the members that are unused, that's much cheaper
// than a full array realloc'n'copy party ...
if(!(node->mNumMeshes = out)) {
node->mNumMeshes = out;
if (0 == out) {
delete[] node->mMeshes;
node->mMeshes = NULL;
node->mMeshes = nullptr;
}
}
// recursively update all children
for (unsigned int i = 0; i < node->mNumChildren;++i) {
UpdateMeshReferences(node->mChildren[i],meshMapping);
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
UpdateMeshReferences(node->mChildren[i], meshMapping);
}
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FindInvalidDataProcess::Execute( aiScene* pScene) {
void FindInvalidDataProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("FindInvalidDataProcess begin");
bool out = false;
@ -122,33 +116,32 @@ void FindInvalidDataProcess::Execute( aiScene* pScene) {
unsigned int real = 0;
// Process meshes
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
int result;
if ((result = ProcessMesh( pScene->mMeshes[a]))) {
for (unsigned int a = 0; a < pScene->mNumMeshes; a++) {
int result = ProcessMesh(pScene->mMeshes[a]);
if (0 == result) {
out = true;
if (2 == result) {
// remove this mesh
delete pScene->mMeshes[a];
AI_DEBUG_INVALIDATE_PTR(pScene->mMeshes[a]);
meshMapping[a] = UINT_MAX;
continue;
}
}
if (2 == result) {
// remove this mesh
delete pScene->mMeshes[a];
pScene->mMeshes[a] = nullptr;
meshMapping[a] = UINT_MAX;
out = true;
continue;
}
pScene->mMeshes[real] = pScene->mMeshes[a];
meshMapping[a] = real++;
}
// Process animations
for (unsigned int a = 0; a < pScene->mNumAnimations;++a) {
ProcessAnimation( pScene->mAnimations[a]);
for (unsigned int animIdx = 0; animIdx < pScene->mNumAnimations; ++animIdx) {
ProcessAnimation(pScene->mAnimations[animIdx]);
}
if (out) {
if ( real != pScene->mNumMeshes) {
if (out) {
if (real != pScene->mNumMeshes) {
if (!real) {
throw DeadlyImportError("No meshes remaining");
}
@ -156,7 +149,7 @@ 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);
UpdateMeshReferences(pScene->mRootNode, meshMapping);
pScene->mNumMeshes = real;
}
@ -168,35 +161,32 @@ void FindInvalidDataProcess::Execute( aiScene* pScene) {
// ------------------------------------------------------------------------------------------------
template <typename T>
inline
const char* ValidateArrayContents(const T* /*arr*/, unsigned int /*size*/,
const std::vector<bool>& /*dirtyMask*/, bool /*mayBeIdentical = false*/, bool /*mayBeZero = true*/)
{
inline const char *ValidateArrayContents(const T * /*arr*/, unsigned int /*size*/,
const std::vector<bool> & /*dirtyMask*/, bool /*mayBeIdentical = false*/, bool /*mayBeZero = true*/) {
return nullptr;
}
// ------------------------------------------------------------------------------------------------
template <>
inline
const char* ValidateArrayContents<aiVector3D>(const aiVector3D* arr, unsigned int size,
const std::vector<bool>& dirtyMask, bool mayBeIdentical , bool mayBeZero ) {
inline const char *ValidateArrayContents<aiVector3D>(const aiVector3D *arr, unsigned int size,
const std::vector<bool> &dirtyMask, bool mayBeIdentical, bool mayBeZero) {
bool b = false;
unsigned int cnt = 0;
for (unsigned int i = 0; i < size;++i) {
for (unsigned int i = 0; i < size; ++i) {
if (dirtyMask.size() && dirtyMask[i]) {
continue;
}
++cnt;
const aiVector3D& v = arr[i];
if (is_special_float(v.x) || is_special_float(v.y) || is_special_float(v.z)) {
const aiVector3D &v = arr[i];
if (is_special_float(v.x) || is_special_float(v.y) || is_special_float(v.z)) {
return "INF/NAN was found in a vector component";
}
if (!mayBeZero && !v.x && !v.y && !v.z ) {
if (!mayBeZero && !v.x && !v.y && !v.z) {
return "Found zero-length vector";
}
if (i && v != arr[i-1])b = true;
if (i && v != arr[i - 1]) b = true;
}
if (cnt > 1 && !b && !mayBeIdentical) {
return "All vectors are identical";
@ -206,14 +196,13 @@ const char* ValidateArrayContents<aiVector3D>(const aiVector3D* arr, unsigned in
// ------------------------------------------------------------------------------------------------
template <typename T>
inline
bool ProcessArray(T*& in, unsigned int num,const char* name,
const std::vector<bool>& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true) {
const char* err = ValidateArrayContents(in,num,dirtyMask,mayBeIdentical,mayBeZero);
if (err) {
ASSIMP_LOG_ERROR_F( "FindInvalidDataProcess fails on mesh ", name, ": ", err);
inline bool ProcessArray(T *&in, unsigned int num, const char *name,
const std::vector<bool> &dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true) {
const char *err = ValidateArrayContents(in, num, dirtyMask, mayBeIdentical, mayBeZero);
if (err) {
ASSIMP_LOG_ERROR("FindInvalidDataProcess fails on mesh ", name, ": ", err);
delete[] in;
in = NULL;
in = nullptr;
return true;
}
return false;
@ -221,49 +210,46 @@ bool ProcessArray(T*& in, unsigned int num,const char* name,
// ------------------------------------------------------------------------------------------------
template <typename T>
AI_FORCE_INLINE bool EpsilonCompare(const T& n, const T& s, ai_real epsilon);
AI_FORCE_INLINE bool EpsilonCompare(const T &n, const T &s, ai_real epsilon);
// ------------------------------------------------------------------------------------------------
AI_FORCE_INLINE bool EpsilonCompare(ai_real n, ai_real s, ai_real epsilon) {
return std::fabs(n-s)>epsilon;
return std::fabs(n - s) > epsilon;
}
// ------------------------------------------------------------------------------------------------
template <>
bool EpsilonCompare<aiVectorKey>(const aiVectorKey& n, const aiVectorKey& s, ai_real epsilon) {
return
EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) &&
EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) &&
EpsilonCompare(n.mValue.z,s.mValue.z,epsilon);
bool EpsilonCompare<aiVectorKey>(const aiVectorKey &n, const aiVectorKey &s, ai_real epsilon) {
return EpsilonCompare(n.mValue.x, s.mValue.x, epsilon) &&
EpsilonCompare(n.mValue.y, s.mValue.y, epsilon) &&
EpsilonCompare(n.mValue.z, s.mValue.z, epsilon);
}
// ------------------------------------------------------------------------------------------------
template <>
bool EpsilonCompare<aiQuatKey>(const aiQuatKey& n, const aiQuatKey& s, ai_real epsilon) {
return
EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) &&
EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) &&
EpsilonCompare(n.mValue.z,s.mValue.z,epsilon) &&
EpsilonCompare(n.mValue.w,s.mValue.w,epsilon);
bool EpsilonCompare<aiQuatKey>(const aiQuatKey &n, const aiQuatKey &s, ai_real epsilon) {
return EpsilonCompare(n.mValue.x, s.mValue.x, epsilon) &&
EpsilonCompare(n.mValue.y, s.mValue.y, epsilon) &&
EpsilonCompare(n.mValue.z, s.mValue.z, epsilon) &&
EpsilonCompare(n.mValue.w, s.mValue.w, epsilon);
}
// ------------------------------------------------------------------------------------------------
template <typename T>
inline
bool AllIdentical(T* in, unsigned int num, ai_real epsilon) {
inline bool AllIdentical(T *in, unsigned int num, ai_real epsilon) {
if (num <= 1) {
return true;
}
if (fabs(epsilon) > 0.f) {
for (unsigned int i = 0; i < num-1;++i) {
if (!EpsilonCompare(in[i],in[i+1],epsilon)) {
for (unsigned int i = 0; i < num - 1; ++i) {
if (!EpsilonCompare(in[i], in[i + 1], epsilon)) {
return false;
}
}
} else {
for (unsigned int i = 0; i < num-1;++i) {
if (in[i] != in[i+1]) {
for (unsigned int i = 0; i < num - 1; ++i) {
if (in[i] != in[i + 1]) {
return false;
}
}
@ -273,16 +259,16 @@ bool AllIdentical(T* in, unsigned int num, ai_real epsilon) {
// ------------------------------------------------------------------------------------------------
// Search an animation for invalid content
void FindInvalidDataProcess::ProcessAnimation (aiAnimation* anim) {
void FindInvalidDataProcess::ProcessAnimation(aiAnimation *anim) {
// Process all animation channels
for ( unsigned int a = 0; a < anim->mNumChannels; ++a ) {
ProcessAnimationChannel( anim->mChannels[a]);
for (unsigned int a = 0; a < anim->mNumChannels; ++a) {
ProcessAnimationChannel(anim->mChannels[a]);
}
}
// ------------------------------------------------------------------------------------------------
void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) {
ai_assert( nullptr != anim );
void FindInvalidDataProcess::ProcessAnimationChannel(aiNodeAnim *anim) {
ai_assert(nullptr != anim);
if (anim->mNumPositionKeys == 0 && anim->mNumRotationKeys == 0 && anim->mNumScalingKeys == 0) {
ai_assert_entry();
return;
@ -292,7 +278,7 @@ void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) {
// we can remove al keys except one.
// POSITIONS
int i = 0;
if (anim->mNumPositionKeys > 1 && AllIdentical(anim->mPositionKeys,anim->mNumPositionKeys,configEpsilon)) {
if (anim->mNumPositionKeys > 1 && AllIdentical(anim->mPositionKeys, anim->mNumPositionKeys, configEpsilon)) {
aiVectorKey v = anim->mPositionKeys[0];
// Reallocate ... we need just ONE element, it makes no sense to reuse the array
@ -303,7 +289,7 @@ void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) {
}
// ROTATIONS
if (anim->mNumRotationKeys > 1 && AllIdentical(anim->mRotationKeys,anim->mNumRotationKeys,configEpsilon)) {
if (anim->mNumRotationKeys > 1 && AllIdentical(anim->mRotationKeys, anim->mNumRotationKeys, configEpsilon)) {
aiQuatKey v = anim->mRotationKeys[0];
// Reallocate ... we need just ONE element, it makes no sense to reuse the array
@ -314,7 +300,7 @@ void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) {
}
// SCALINGS
if (anim->mNumScalingKeys > 1 && AllIdentical(anim->mScalingKeys,anim->mNumScalingKeys,configEpsilon)) {
if (anim->mNumScalingKeys > 1 && AllIdentical(anim->mScalingKeys, anim->mNumScalingKeys, configEpsilon)) {
aiVectorKey v = anim->mScalingKeys[0];
// Reallocate ... we need just ONE element, it makes no sense to reuse the array
@ -323,22 +309,21 @@ void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) {
anim->mScalingKeys[0] = v;
i = 1;
}
if ( 1 == i ) {
if (1 == i) {
ASSIMP_LOG_WARN("Simplified dummy tracks with just one key");
}
}
// ------------------------------------------------------------------------------------------------
// Search a mesh for invalid contents
int FindInvalidDataProcess::ProcessMesh(aiMesh* pMesh)
{
int FindInvalidDataProcess::ProcessMesh(aiMesh *pMesh) {
bool ret = false;
std::vector<bool> dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces != 0);
// Ignore elements that are not referenced by vertices.
// (they are, for example, caused by the FindDegenerates step)
for (unsigned int m = 0; m < pMesh->mNumFaces; ++m) {
const aiFace& f = pMesh->mFaces[m];
const aiFace &f = pMesh->mFaces[m];
for (unsigned int i = 0; i < f.mNumIndices; ++i) {
dirtyMask[f.mIndices[i]] = false;
@ -361,7 +346,7 @@ int FindInvalidDataProcess::ProcessMesh(aiMesh* pMesh)
// delete all subsequent texture coordinate sets.
for (unsigned int a = i + 1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
delete[] pMesh->mTextureCoords[a];
pMesh->mTextureCoords[a] = NULL;
pMesh->mTextureCoords[a] = nullptr;
pMesh->mNumUVComponents[a] = 0;
}
@ -374,19 +359,17 @@ int FindInvalidDataProcess::ProcessMesh(aiMesh* pMesh)
// they are invalid or not.
// Normals and tangents are undefined for point and line faces.
if (pMesh->mNormals || pMesh->mTangents) {
if (pMesh->mNormals || pMesh->mTangents) {
if (aiPrimitiveType_POINT & pMesh->mPrimitiveTypes ||
aiPrimitiveType_LINE & pMesh->mPrimitiveTypes)
{
aiPrimitiveType_LINE & pMesh->mPrimitiveTypes) {
if (aiPrimitiveType_TRIANGLE & pMesh->mPrimitiveTypes ||
aiPrimitiveType_POLYGON & pMesh->mPrimitiveTypes)
{
aiPrimitiveType_POLYGON & pMesh->mPrimitiveTypes) {
// We need to update the lookup-table
for (unsigned int m = 0; m < pMesh->mNumFaces;++m) {
const aiFace& f = pMesh->mFaces[ m ];
for (unsigned int m = 0; m < pMesh->mNumFaces; ++m) {
const aiFace &f = pMesh->mFaces[m];
if (f.mNumIndices < 3) {
if (f.mNumIndices < 3) {
dirtyMask[f.mIndices[0]] = true;
if (f.mNumIndices == 2) {
dirtyMask[f.mIndices[1]] = true;
@ -402,19 +385,21 @@ int FindInvalidDataProcess::ProcessMesh(aiMesh* pMesh)
}
// Process mesh normals
if (pMesh->mNormals && ProcessArray(pMesh->mNormals,pMesh->mNumVertices,
"normals",dirtyMask,true,false))
if (pMesh->mNormals && ProcessArray(pMesh->mNormals, pMesh->mNumVertices,
"normals", dirtyMask, true, false))
ret = true;
// Process mesh tangents
if (pMesh->mTangents && ProcessArray(pMesh->mTangents,pMesh->mNumVertices,"tangents",dirtyMask)) {
delete[] pMesh->mBitangents; pMesh->mBitangents = NULL;
if (pMesh->mTangents && ProcessArray(pMesh->mTangents, pMesh->mNumVertices, "tangents", dirtyMask)) {
delete[] pMesh->mBitangents;
pMesh->mBitangents = nullptr;
ret = true;
}
// Process mesh bitangents
if (pMesh->mBitangents && ProcessArray(pMesh->mBitangents,pMesh->mNumVertices,"bitangents",dirtyMask)) {
delete[] pMesh->mTangents; pMesh->mTangents = NULL;
if (pMesh->mBitangents && ProcessArray(pMesh->mBitangents, pMesh->mNumVertices, "bitangents", dirtyMask)) {
delete[] pMesh->mTangents;
pMesh->mTangents = nullptr;
ret = true;
}
}

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -48,14 +47,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "Common/BaseProcess.h"
#include <assimp/types.h>
#include <assimp/anim.h>
#include <assimp/types.h>
struct aiMesh;
class FindInvalidDataProcessTest;
namespace Assimp {
namespace Assimp {
// ---------------------------------------------------------------------------
/** The FindInvalidData post-processing step. It searches the mesh data
@ -70,31 +69,31 @@ public:
// -------------------------------------------------------------------
//
bool IsActive( unsigned int pFlags) const;
bool IsActive(unsigned int pFlags) const;
// -------------------------------------------------------------------
// Setup import settings
void SetupProperties(const Importer* pImp);
void SetupProperties(const Importer *pImp);
// -------------------------------------------------------------------
// Run the step
void Execute( aiScene* pScene);
void Execute(aiScene *pScene);
// -------------------------------------------------------------------
/** 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);
int ProcessMesh(aiMesh *pMesh);
// -------------------------------------------------------------------
/** Executes the post-processing step on the given animation
* @param anim The animation to process. */
void ProcessAnimation (aiAnimation* anim);
void ProcessAnimation(aiAnimation *anim);
// -------------------------------------------------------------------
/** Executes the post-processing step on the given anim channel
* @param anim The animation channel to process.*/
void ProcessAnimationChannel (aiNodeAnim* anim);
void ProcessAnimationChannel(aiNodeAnim *anim);
private:
ai_real configEpsilon;

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -164,7 +164,7 @@ bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index)
// now compare the volumes of the bounding boxes
if (std::fabs(fDelta0_x * fDelta0_y * fDelta0_z) < std::fabs(fDelta1_x * fDelta1_yz)) {
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO_F("Mesh ", index, ": Normals are facing inwards (or the mesh is planar)", index);
ASSIMP_LOG_INFO("Mesh ", index, ": Normals are facing inwards (or the mesh is planar)", index);
}
// Invert normals

View file

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

View file

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

View file

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

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -45,41 +45,38 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* normals for all imported faces.
*/
#include "GenFaceNormalsProcess.h"
#include <assimp/Exceptional.h>
#include <assimp/postprocess.h>
#include <assimp/qnan.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Exceptional.h>
#include <assimp/qnan.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
GenFaceNormalsProcess::GenFaceNormalsProcess()
{
GenFaceNormalsProcess::GenFaceNormalsProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
GenFaceNormalsProcess::~GenFaceNormalsProcess()
{
GenFaceNormalsProcess::~GenFaceNormalsProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const {
bool GenFaceNormalsProcess::IsActive(unsigned int pFlags) const {
force_ = (pFlags & aiProcess_ForceGenNormals) != 0;
return (pFlags & aiProcess_GenNormals) != 0;
flippedWindingOrder_ = (pFlags & aiProcess_FlipWindingOrder) != 0;
return (pFlags & aiProcess_GenNormals) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenFaceNormalsProcess::Execute( aiScene* pScene) {
void GenFaceNormalsProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("GenFaceNormalsProcess begin");
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
@ -87,33 +84,35 @@ void GenFaceNormalsProcess::Execute( aiScene* pScene) {
}
bool bHas = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
if(this->GenMeshFaceNormals( pScene->mMeshes[a])) {
for (unsigned int a = 0; a < pScene->mNumMeshes; a++) {
if (this->GenMeshFaceNormals(pScene->mMeshes[a])) {
bHas = true;
}
}
if (bHas) {
if (bHas) {
ASSIMP_LOG_INFO("GenFaceNormalsProcess finished. "
"Face normals have been calculated");
"Face normals have been calculated");
} else {
ASSIMP_LOG_DEBUG("GenFaceNormalsProcess finished. "
"Normals are already there");
"Normals are already there");
}
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
{
if (NULL != pMesh->mNormals) {
if (force_) delete[] pMesh->mNormals;
else return false;
bool GenFaceNormalsProcess::GenMeshFaceNormals(aiMesh *pMesh) {
if (nullptr != pMesh->mNormals) {
if (force_) {
delete[] pMesh->mNormals;
} else {
return false;
}
}
// If the mesh consists of lines and/or points but not of
// triangles or higher-order polygons the normal vectors
// are undefined.
if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) {
if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) {
ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes");
return false;
}
@ -123,22 +122,24 @@ bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
const float qnan = 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++) {
const aiFace& face = pMesh->mFaces[a];
if (face.mNumIndices < 3) {
for (unsigned int a = 0; a < pMesh->mNumFaces; a++) {
const aiFace &face = pMesh->mFaces[a];
if (face.mNumIndices < 3) {
// either a point or a line -> no well-defined normal vector
for (unsigned int i = 0;i < face.mNumIndices;++i) {
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan);
}
continue;
}
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]];
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 );
const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe();
for (unsigned int i = 0;i < face.mNumIndices;++i) {
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
pMesh->mNormals[face.mIndices[i]] = vNor;
}
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -80,6 +80,7 @@ public:
private:
bool GenMeshFaceNormals(aiMesh* pcMesh);
mutable bool force_ = false;
mutable bool flippedWindingOrder_ = false;
};
} // end of namespace Assimp

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -45,8 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* normals for all imported faces.
*/
// internal headers
#include "GenVertexNormalsProcess.h"
#include "ProcessHelper.h"
@ -57,8 +55,8 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
GenVertexNormalsProcess::GenVertexNormalsProcess()
: configMaxAngle( AI_DEG_TO_RAD( 175.f ) ) {
GenVertexNormalsProcess::GenVertexNormalsProcess() :
configMaxAngle(AI_DEG_TO_RAD(175.f)) {
// empty
}
@ -70,25 +68,23 @@ GenVertexNormalsProcess::~GenVertexNormalsProcess() {
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const
{
bool GenVertexNormalsProcess::IsActive(unsigned int pFlags) const {
force_ = (pFlags & aiProcess_ForceGenNormals) != 0;
flippedWindingOrder_ = (pFlags & aiProcess_FlipWindingOrder) != 0;
return (pFlags & aiProcess_GenSmoothNormals) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenVertexNormalsProcess::SetupProperties(const Importer* pImp)
{
void GenVertexNormalsProcess::SetupProperties(const Importer *pImp) {
// Get the current value of the AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE property
configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,(ai_real)175.0);
configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle,(ai_real)175.0),(ai_real)0.0));
configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, (ai_real)175.0);
configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle, (ai_real)175.0), (ai_real)0.0));
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenVertexNormalsProcess::Execute( aiScene* pScene)
{
void GenVertexNormalsProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("GenVertexNormalsProcess begin");
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
@ -96,34 +92,34 @@ void GenVertexNormalsProcess::Execute( aiScene* pScene)
}
bool bHas = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
if(GenMeshVertexNormals( pScene->mMeshes[a],a))
for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
if (GenMeshVertexNormals(pScene->mMeshes[a], a))
bHas = true;
}
if (bHas) {
if (bHas) {
ASSIMP_LOG_INFO("GenVertexNormalsProcess finished. "
"Vertex normals have been calculated");
"Vertex normals have been calculated");
} else {
ASSIMP_LOG_DEBUG("GenVertexNormalsProcess finished. "
"Normals are already there");
"Normals are already there");
}
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int meshIndex)
{
if (NULL != pMesh->mNormals) {
if (force_) delete[] pMesh->mNormals;
else return false;
bool GenVertexNormalsProcess::GenMeshVertexNormals(aiMesh *pMesh, unsigned int meshIndex) {
if (nullptr != pMesh->mNormals) {
if (force_)
delete[] pMesh->mNormals;
else
return false;
}
// If the mesh consists of lines and/or points but not of
// triangles or higher-order polygons the normal vectors
// are undefined.
if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON)))
{
if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) {
ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes");
return false;
}
@ -133,75 +129,73 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
// Compute per-face normals but store them per-vertex
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
const aiFace& face = pMesh->mFaces[a];
if (face.mNumIndices < 3)
{
for (unsigned int a = 0; a < pMesh->mNumFaces; a++) {
const aiFace &face = pMesh->mFaces[a];
if (face.mNumIndices < 3) {
// either a point or a line -> no normal vector
for (unsigned int i = 0;i < face.mNumIndices;++i) {
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan);
}
continue;
}
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]];
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 );
const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe();
for (unsigned int i = 0;i < face.mNumIndices;++i) {
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
pMesh->mNormals[face.mIndices[i]] = vNor;
}
}
// Set up a SpatialSort to quickly find all vertices close to a given position
// check whether we can reuse the SpatialSort of a previous step.
SpatialSort* vertexFinder = NULL;
SpatialSort _vertexFinder;
ai_real posEpsilon = ai_real( 1e-5 );
SpatialSort *vertexFinder = nullptr;
SpatialSort _vertexFinder;
ai_real posEpsilon = ai_real(1e-5);
if (shared) {
std::vector<std::pair<SpatialSort,ai_real> >* avf;
shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
if (avf)
{
std::pair<SpatialSort,ai_real>& blubb = avf->operator [] (meshIndex);
std::vector<std::pair<SpatialSort, ai_real>> *avf;
shared->GetProperty(AI_SPP_SPATIAL_SORT, avf);
if (avf) {
std::pair<SpatialSort, ai_real> &blubb = avf->operator[](meshIndex);
vertexFinder = &blubb.first;
posEpsilon = blubb.second;
}
}
if (!vertexFinder) {
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
if (!vertexFinder) {
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof(aiVector3D));
vertexFinder = &_vertexFinder;
posEpsilon = ComputePositionEpsilon(pMesh);
}
std::vector<unsigned int> verticesFound;
aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices];
aiVector3D *pcNew = new aiVector3D[pMesh->mNumVertices];
if (configMaxAngle >= AI_DEG_TO_RAD( 175.f )) {
if (configMaxAngle >= AI_DEG_TO_RAD(175.f)) {
// There is no angle limit. Thus all vertices with positions close
// to each other will receive the same vertex normal. This allows us
// to optimize the whole algorithm a little bit ...
std::vector<bool> abHad(pMesh->mNumVertices,false);
for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
std::vector<bool> abHad(pMesh->mNumVertices, false);
for (unsigned int i = 0; i < pMesh->mNumVertices; ++i) {
if (abHad[i]) {
continue;
}
// Get all vertices that share this one ...
vertexFinder->FindPositions( pMesh->mVertices[i], posEpsilon, verticesFound);
vertexFinder->FindPositions(pMesh->mVertices[i], posEpsilon, verticesFound);
aiVector3D pcNor;
for (unsigned int a = 0; a < verticesFound.size(); ++a) {
const aiVector3D& v = pMesh->mNormals[verticesFound[a]];
if (is_not_qnan(v.x))pcNor += v;
const aiVector3D &v = pMesh->mNormals[verticesFound[a]];
if (is_not_qnan(v.x)) pcNor += v;
}
pcNor.NormalizeSafe();
// Write the smoothed normal back to all affected normals
for (unsigned int a = 0; a < verticesFound.size(); ++a)
{
for (unsigned int a = 0; a < verticesFound.size(); ++a) {
unsigned int vidx = verticesFound[a];
pcNew[vidx] = pcNor;
abHad[vidx] = true;
@ -210,11 +204,11 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int
}
// Slower code path if a smooth angle is set. There are many ways to achieve
// the effect, this one is the most straightforward one.
else {
else {
const ai_real fLimit = std::cos(configMaxAngle);
for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
for (unsigned int i = 0; i < pMesh->mNumVertices; ++i) {
// Get all vertices that share this one ...
vertexFinder->FindPositions( pMesh->mVertices[i] , posEpsilon, verticesFound);
vertexFinder->FindPositions(pMesh->mVertices[i], posEpsilon, verticesFound);
aiVector3D vr = pMesh->mNormals[i];

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -104,6 +104,7 @@ private:
/** Configuration option: maximum smoothing angle, in radians*/
ai_real configMaxAngle;
mutable bool force_ = false;
mutable bool flippedWindingOrder_ = false;
};
} // end of namespace Assimp

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -109,7 +109,7 @@ void ImproveCacheLocalityProcess::Execute( aiScene* pScene) {
}
if (!DefaultLogger::isNullLogger()) {
if (numf > 0) {
ASSIMP_LOG_INFO_F("Cache relevant are ", numm, " meshes (", numf, " faces). Average output ACMR is ", out / numf);
ASSIMP_LOG_INFO("Cache relevant are ", numm, " meshes (", numf, " faces). Average output ACMR is ", out / numf);
}
ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess finished. ");
}
@ -355,7 +355,7 @@ ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int me
// very intense verbose logging ... prepare for much text if there are many meshes
if ( DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
ASSIMP_LOG_DEBUG_F("Mesh %u | ACMR in: ", meshNum, " out: ", fACMR, " | ~", fACMR2, ((fACMR - fACMR2) / fACMR) * 100.f);
ASSIMP_LOG_VERBOSE_DEBUG("Mesh %u | ACMR in: ", meshNum, " out: ", fACMR, " | ~", fACMR2, ((fACMR - fACMR2) / fACMR) * 100.f);
}
fACMR2 *= pMesh->mNumFaces;

View file

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

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -100,7 +100,7 @@ void JoinVerticesProcess::Execute( aiScene* pScene)
if (iNumOldVertices == iNumVertices) {
ASSIMP_LOG_DEBUG("JoinVerticesProcess finished ");
} else {
ASSIMP_LOG_INFO_F("JoinVerticesProcess finished | Verts in: ", iNumOldVertices,
ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices,
" out: ", iNumVertices, " | ~",
((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f );
}
@ -266,7 +266,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
// float posEpsilonSqr;
SpatialSort* vertexFinder = NULL;
SpatialSort *vertexFinder = nullptr;
SpatialSort _vertexFinder;
typedef std::pair<SpatialSort,float> SpatPair;
@ -373,7 +373,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
}
if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
ASSIMP_LOG_DEBUG_F(
ASSIMP_LOG_VERBOSE_DEBUG(
"Mesh ",meshIndex,
" (",
(pMesh->mName.length ? pMesh->mName.data : "unnamed"),
@ -408,7 +408,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
std::vector<aiVertexWeight> newWeights;
newWeights.reserve( bone->mNumWeights);
if ( NULL != bone->mWeights ) {
if (nullptr != bone->mWeights) {
for ( unsigned int b = 0; b < bone->mNumWeights; b++ ) {
const aiVertexWeight& ow = bone->mWeights[ b ];
// if the vertex is a unique one, translate it
@ -420,7 +420,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
}
}
} else {
ASSIMP_LOG_ERROR( "X-Export: aiBone shall contain weights, but pointer to them is NULL." );
ASSIMP_LOG_ERROR( "X-Export: aiBone shall contain weights, but pointer to them is nullptr." );
}
if (newWeights.size() > 0) {

View file

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

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "LimitBoneWeightsProcess.h"
#include <assimp/SmallVector.h>
#include <assimp/StringUtils.h>
#include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp>
@ -52,7 +53,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
LimitBoneWeightsProcess::LimitBoneWeightsProcess()
@ -76,10 +76,12 @@ bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void LimitBoneWeightsProcess::Execute( aiScene* pScene) {
void LimitBoneWeightsProcess::Execute( aiScene* pScene)
{
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin");
for (unsigned int a = 0; a < pScene->mNumMeshes; ++a ) {
ProcessMesh(pScene->mMeshes[a]);
for (unsigned int m = 0; m < pScene->mNumMeshes; ++m) {
ProcessMesh(pScene->mMeshes[m]);
}
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess end");
@ -95,107 +97,100 @@ void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp)
// ------------------------------------------------------------------------------------------------
// Unites identical vertices in the given mesh
void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh)
{
if( !pMesh->HasBones())
if (!pMesh->HasBones())
return;
// collect all bone weights per vertex
typedef std::vector< std::vector< Weight > > WeightsPerVertex;
WeightsPerVertex vertexWeights( pMesh->mNumVertices);
typedef SmallVector<Weight,8> VertexWeightArray;
typedef std::vector<VertexWeightArray> WeightsPerVertex;
WeightsPerVertex vertexWeights(pMesh->mNumVertices);
size_t maxVertexWeights = 0;
// collect all weights per vertex
for( unsigned int a = 0; a < pMesh->mNumBones; a++)
for (unsigned int b = 0; b < pMesh->mNumBones; ++b)
{
const aiBone* bone = pMesh->mBones[a];
for( unsigned int b = 0; b < bone->mNumWeights; b++)
const aiBone* bone = pMesh->mBones[b];
for (unsigned int w = 0; w < bone->mNumWeights; ++w)
{
const aiVertexWeight& w = bone->mWeights[b];
vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight));
const aiVertexWeight& vw = bone->mWeights[w];
if (vertexWeights.size() <= vw.mVertexId)
continue;
vertexWeights[vw.mVertexId].push_back(Weight(b, vw.mWeight));
maxVertexWeights = std::max(maxVertexWeights, vertexWeights[vw.mVertexId].size());
}
}
if (maxVertexWeights <= mMaxWeights)
return;
unsigned int removed = 0, old_bones = pMesh->mNumBones;
// now cut the weight count if it exceeds the maximum
bool bChanged = false;
for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
for (WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
{
if( vit->size() <= mMaxWeights)
if (vit->size() <= mMaxWeights)
continue;
bChanged = true;
// more than the defined maximum -> first sort by weight in descending order. That's
// why we defined the < operator in such a weird way.
std::sort( vit->begin(), vit->end());
std::sort(vit->begin(), vit->end());
// now kill everything beyond the maximum count
unsigned int m = static_cast<unsigned int>(vit->size());
vit->erase( vit->begin() + mMaxWeights, vit->end());
removed += static_cast<unsigned int>(m-vit->size());
vit->resize(mMaxWeights);
removed += static_cast<unsigned int>(m - vit->size());
// and renormalize the weights
float sum = 0.0f;
for( std::vector<Weight>::const_iterator it = vit->begin(); it != vit->end(); ++it ) {
for(const Weight* it = vit->begin(); it != vit->end(); ++it) {
sum += it->mWeight;
}
if( 0.0f != sum ) {
if (0.0f != sum) {
const float invSum = 1.0f / sum;
for( std::vector<Weight>::iterator it = vit->begin(); it != vit->end(); ++it ) {
for(Weight* it = vit->begin(); it != vit->end(); ++it) {
it->mWeight *= invSum;
}
}
}
if (bChanged) {
// rebuild the vertex weight array for all bones
typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone;
WeightsPerBone boneWeights( pMesh->mNumBones);
for( unsigned int a = 0; a < vertexWeights.size(); a++)
// clear weight count for all bone
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)
{
const VertexWeightArray& vw = vertexWeights[a];
for (const Weight* it = vw.begin(); it != vw.end(); ++it)
{
const std::vector<Weight>& vw = vertexWeights[a];
for( std::vector<Weight>::const_iterator it = vw.begin(); it != vw.end(); ++it)
boneWeights[it->mBone].push_back( aiVertexWeight( a, it->mWeight));
}
// and finally copy the vertex weight list over to the mesh's bones
std::vector<bool> abNoNeed(pMesh->mNumBones,false);
bChanged = false;
for( unsigned int a = 0; a < pMesh->mNumBones; a++)
{
const std::vector<aiVertexWeight>& bw = boneWeights[a];
aiBone* bone = pMesh->mBones[a];
if ( bw.empty() )
{
abNoNeed[a] = bChanged = true;
continue;
}
// copy the weight list. should always be less weights than before, so we don't need a new allocation
ai_assert( bw.size() <= bone->mNumWeights);
bone->mNumWeights = static_cast<unsigned int>( bw.size() );
::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight));
}
if (bChanged) {
// the number of new bones is smaller than before, so we can reuse the old array
aiBone** ppcCur = pMesh->mBones;aiBone** ppcSrc = ppcCur;
for (std::vector<bool>::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) {
if (*iter) {
delete *ppcSrc;
--pMesh->mNumBones;
}
else *ppcCur++ = *ppcSrc;
++ppcSrc;
}
}
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones );
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;
}
}
pMesh->mNumBones = writeBone;
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones);
}
}

View file

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

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file Implementation of the post processing step "MakeVerboseFormat"
*/
#include "MakeVerboseFormat.h"
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
@ -51,26 +50,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
MakeVerboseFormatProcess::MakeVerboseFormatProcess()
{
MakeVerboseFormatProcess::MakeVerboseFormatProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
MakeVerboseFormatProcess::~MakeVerboseFormatProcess()
{
MakeVerboseFormatProcess::~MakeVerboseFormatProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void MakeVerboseFormatProcess::Execute( aiScene* pScene)
{
ai_assert(NULL != pScene);
void MakeVerboseFormatProcess::Execute(aiScene *pScene) {
ai_assert(nullptr != pScene);
ASSIMP_LOG_DEBUG("MakeVerboseFormatProcess begin");
bool bHas = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{
if( MakeVerboseFormat( pScene->mMeshes[a]))
for (unsigned int a = 0; a < pScene->mNumMeshes; a++) {
if (MakeVerboseFormat(pScene->mMeshes[a]))
bHas = true;
}
if (bHas) {
@ -84,29 +79,26 @@ void MakeVerboseFormatProcess::Execute( aiScene* pScene)
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
{
ai_assert(NULL != pcMesh);
bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh *pcMesh) {
ai_assert(nullptr != pcMesh);
unsigned int iOldNumVertices = pcMesh->mNumVertices;
const unsigned int iNumVerts = pcMesh->mNumFaces*3;
const unsigned int iNumVerts = pcMesh->mNumFaces * 3;
aiVector3D* pvPositions = new aiVector3D[ iNumVerts ];
aiVector3D *pvPositions = new aiVector3D[iNumVerts];
aiVector3D* pvNormals = NULL;
if (pcMesh->HasNormals())
{
aiVector3D *pvNormals = nullptr;
if (pcMesh->HasNormals()) {
pvNormals = new aiVector3D[iNumVerts];
}
aiVector3D* pvTangents = NULL, *pvBitangents = NULL;
if (pcMesh->HasTangentsAndBitangents())
{
aiVector3D *pvTangents = nullptr, *pvBitangents = nullptr;
if (pcMesh->HasTangentsAndBitangents()) {
pvTangents = new aiVector3D[iNumVerts];
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] = { 0 };
aiColor4D *apvColorSets[AI_MAX_NUMBER_OF_COLOR_SETS] = { 0 };
unsigned int p = 0;
while (pcMesh->HasTextureCoords(p))
@ -117,26 +109,21 @@ bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
apvColorSets[p++] = new aiColor4D[iNumVerts];
// allocate enough memory to hold output bones and vertex weights ...
std::vector<aiVertexWeight>* newWeights = new std::vector<aiVertexWeight>[pcMesh->mNumBones];
for (unsigned int i = 0;i < pcMesh->mNumBones;++i) {
newWeights[i].reserve(pcMesh->mBones[i]->mNumWeights*3);
std::vector<aiVertexWeight> *newWeights = new std::vector<aiVertexWeight>[pcMesh->mNumBones];
for (unsigned int i = 0; i < pcMesh->mNumBones; ++i) {
newWeights[i].reserve(pcMesh->mBones[i]->mNumWeights * 3);
}
// iterate through all faces and build a clean list
unsigned int iIndex = 0;
for (unsigned int a = 0; a< pcMesh->mNumFaces;++a)
{
aiFace* pcFace = &pcMesh->mFaces[a];
for (unsigned int q = 0; q < pcFace->mNumIndices;++q,++iIndex)
{
for (unsigned int a = 0; a < pcMesh->mNumFaces; ++a) {
aiFace *pcFace = &pcMesh->mFaces[a];
for (unsigned int q = 0; q < pcFace->mNumIndices; ++q, ++iIndex) {
// need to build a clean list of bones, too
for (unsigned int i = 0;i < pcMesh->mNumBones;++i)
{
for (unsigned int a = 0; a < pcMesh->mBones[i]->mNumWeights;a++)
{
const aiVertexWeight& w = pcMesh->mBones[i]->mWeights[a];
if(pcFace->mIndices[q] == w.mVertexId)
{
for (unsigned int i = 0; i < pcMesh->mNumBones; ++i) {
for (unsigned int boneIdx = 0; boneIdx < pcMesh->mBones[i]->mNumWeights; ++boneIdx) {
const aiVertexWeight &w = pcMesh->mBones[i]->mWeights[boneIdx];
if (pcFace->mIndices[q] == w.mVertexId) {
aiVertexWeight wNew;
wNew.mVertexId = iIndex;
wNew.mWeight = w.mWeight;
@ -147,45 +134,39 @@ bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
pvPositions[iIndex] = pcMesh->mVertices[pcFace->mIndices[q]];
if (pcMesh->HasNormals())
{
if (pcMesh->HasNormals()) {
pvNormals[iIndex] = pcMesh->mNormals[pcFace->mIndices[q]];
}
if (pcMesh->HasTangentsAndBitangents())
{
if (pcMesh->HasTangentsAndBitangents()) {
pvTangents[iIndex] = pcMesh->mTangents[pcFace->mIndices[q]];
pvBitangents[iIndex] = pcMesh->mBitangents[pcFace->mIndices[q]];
}
unsigned int p = 0;
while (pcMesh->HasTextureCoords(p))
{
apvTextureCoords[p][iIndex] = pcMesh->mTextureCoords[p][pcFace->mIndices[q]];
++p;
unsigned int pp = 0;
while (pcMesh->HasTextureCoords(pp)) {
apvTextureCoords[pp][iIndex] = pcMesh->mTextureCoords[pp][pcFace->mIndices[q]];
++pp;
}
p = 0;
while (pcMesh->HasVertexColors(p))
{
apvColorSets[p][iIndex] = pcMesh->mColors[p][pcFace->mIndices[q]];
++p;
pp = 0;
while (pcMesh->HasVertexColors(pp)) {
apvColorSets[pp][iIndex] = pcMesh->mColors[pp][pcFace->mIndices[q]];
++pp;
}
pcFace->mIndices[q] = iIndex;
}
}
// build output vertex weights
for (unsigned int i = 0;i < pcMesh->mNumBones;++i)
{
delete [] pcMesh->mBones[i]->mWeights;
for (unsigned int i = 0; i < pcMesh->mNumBones; ++i) {
delete[] pcMesh->mBones[i]->mWeights;
if (!newWeights[i].empty()) {
pcMesh->mBones[i]->mWeights = new aiVertexWeight[newWeights[i].size()];
aiVertexWeight *weightToCopy = &( newWeights[i][0] );
pcMesh->mBones[i]->mNumWeights = static_cast<unsigned int>(newWeights[i].size());
aiVertexWeight *weightToCopy = &(newWeights[i][0]);
memcpy(pcMesh->mBones[i]->mWeights, weightToCopy,
sizeof(aiVertexWeight) * newWeights[i].size());
sizeof(aiVertexWeight) * newWeights[i].size());
} else {
pcMesh->mBones[i]->mWeights = NULL;
pcMesh->mBones[i]->mWeights = nullptr;
}
}
delete[] newWeights;
@ -195,28 +176,24 @@ bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
pcMesh->mVertices = pvPositions;
p = 0;
while (pcMesh->HasTextureCoords(p))
{
while (pcMesh->HasTextureCoords(p)) {
delete[] pcMesh->mTextureCoords[p];
pcMesh->mTextureCoords[p] = apvTextureCoords[p];
++p;
}
p = 0;
while (pcMesh->HasVertexColors(p))
{
while (pcMesh->HasVertexColors(p)) {
delete[] pcMesh->mColors[p];
pcMesh->mColors[p] = apvColorSets[p];
++p;
}
pcMesh->mNumVertices = iNumVerts;
if (pcMesh->HasNormals())
{
if (pcMesh->HasNormals()) {
delete[] pcMesh->mNormals;
pcMesh->mNormals = pvNormals;
}
if (pcMesh->HasTangentsAndBitangents())
{
if (pcMesh->HasTangentsAndBitangents()) {
delete[] pcMesh->mTangents;
pcMesh->mTangents = pvTangents;
delete[] pcMesh->mBitangents;
@ -225,15 +202,14 @@ bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
return (pcMesh->mNumVertices != iOldNumVertices);
}
// ------------------------------------------------------------------------------------------------
bool IsMeshInVerboseFormat(const aiMesh* mesh) {
bool IsMeshInVerboseFormat(const aiMesh *mesh) {
// avoid slow vector<bool> specialization
std::vector<unsigned int> seen(mesh->mNumVertices,0);
for(unsigned int i = 0; i < mesh->mNumFaces; ++i) {
const aiFace& f = mesh->mFaces[i];
for(unsigned int j = 0; j < f.mNumIndices; ++j) {
if(++seen[f.mIndices[j]] == 2) {
std::vector<unsigned int> seen(mesh->mNumVertices, 0);
for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
const aiFace &f = mesh->mFaces[i];
for (unsigned int j = 0; j < f.mNumIndices; ++j) {
if (++seen[f.mIndices[j]] == 2) {
// found a duplicate index
return false;
}
@ -244,9 +220,9 @@ bool IsMeshInVerboseFormat(const aiMesh* mesh) {
}
// ------------------------------------------------------------------------------------------------
bool MakeVerboseFormatProcess::IsVerboseFormat(const aiScene* pScene) {
for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
if(!IsMeshInVerboseFormat(pScene->mMeshes[i])) {
bool MakeVerboseFormatProcess::IsVerboseFormat(const aiScene *pScene) {
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
if (!IsMeshInVerboseFormat(pScene->mMeshes[i])) {
return false;
}
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -98,7 +98,7 @@ public:
// -------------------------------------------------------------------
/** Checks whether the scene is already in verbose format.
* @param pScene The data to check.
* @param pScene The data to check.
* @return true if the scene is already in verbose format. */
static bool IsVerboseFormat(const aiScene* pScene);

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -43,13 +43,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Implementation of the aiProcess_OptimizGraph step
*/
#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
#include "OptimizeGraph.h"
#include "ProcessHelper.h"
#include <assimp/SceneCombiner.h>
#include "ConvertToLHProcess.h"
#include <assimp/Exceptional.h>
#include <assimp/SceneCombiner.h>
#include <stdio.h>
using namespace Assimp;
@ -60,292 +60,299 @@ using namespace Assimp;
* The unhashed variant should be faster, except for *very* large data sets
*/
#ifdef AI_OG_USE_HASHING
// Use our standard hashing function to compute the hash
# define AI_OG_GETKEY(str) SuperFastHash(str.data,str.length)
// Use our standard hashing function to compute the hash
#define AI_OG_GETKEY(str) SuperFastHash(str.data, str.length)
#else
// Otherwise hope that std::string will utilize a static buffer
// for shorter node names. This would avoid endless heap copying.
# define AI_OG_GETKEY(str) std::string(str.data)
// Otherwise hope that std::string will utilize a static buffer
// for shorter node names. This would avoid endless heap copying.
#define AI_OG_GETKEY(str) std::string(str.data)
#endif
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
OptimizeGraphProcess::OptimizeGraphProcess()
: mScene()
, nodes_in()
, nodes_out()
, count_merged() {
// empty
OptimizeGraphProcess::OptimizeGraphProcess() :
mScene(),
nodes_in(),
nodes_out(),
count_merged() {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
OptimizeGraphProcess::~OptimizeGraphProcess() {
// empty
// empty
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const {
return (0 != (pFlags & aiProcess_OptimizeGraph));
bool OptimizeGraphProcess::IsActive(unsigned int pFlags) const {
return (0 != (pFlags & aiProcess_OptimizeGraph));
}
// ------------------------------------------------------------------------------------------------
// Setup properties for the post-processing step
void OptimizeGraphProcess::SetupProperties(const Importer* pImp) {
// Get value of AI_CONFIG_PP_OG_EXCLUDE_LIST
std::string tmp = pImp->GetPropertyString(AI_CONFIG_PP_OG_EXCLUDE_LIST,"");
AddLockedNodeList(tmp);
void OptimizeGraphProcess::SetupProperties(const Importer *pImp) {
// Get value of AI_CONFIG_PP_OG_EXCLUDE_LIST
std::string tmp = pImp->GetPropertyString(AI_CONFIG_PP_OG_EXCLUDE_LIST, "");
AddLockedNodeList(tmp);
}
// ------------------------------------------------------------------------------------------------
// Collect new children
void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& nodes) {
nodes_in += nd->mNumChildren;
void OptimizeGraphProcess::CollectNewChildren(aiNode *nd, std::list<aiNode *> &nodes) {
nodes_in += nd->mNumChildren;
// Process children
std::list<aiNode*> child_nodes;
for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
CollectNewChildren(nd->mChildren[i],child_nodes);
nd->mChildren[i] = nullptr;
}
// Process children
std::list<aiNode *> child_nodes;
for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
CollectNewChildren(nd->mChildren[i], child_nodes);
nd->mChildren[i] = nullptr;
}
// Check whether we need this node; if not we can replace it by our own children (warn, danger of incest).
if (locked.find(AI_OG_GETKEY(nd->mName)) == locked.end() ) {
for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
// Check whether we need this node; if not we can replace it by our own children (warn, danger of incest).
if (locked.find(AI_OG_GETKEY(nd->mName)) == locked.end()) {
for (std::list<aiNode *>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
if (locked.find(AI_OG_GETKEY((*it)->mName)) == locked.end()) {
(*it)->mTransformation = nd->mTransformation * (*it)->mTransformation;
nodes.push_back(*it);
if (locked.find(AI_OG_GETKEY((*it)->mName)) == locked.end()) {
(*it)->mTransformation = nd->mTransformation * (*it)->mTransformation;
nodes.push_back(*it);
it = child_nodes.erase(it);
continue;
}
++it;
}
it = child_nodes.erase(it);
continue;
}
++it;
}
if (nd->mNumMeshes || !child_nodes.empty()) {
nodes.push_back(nd);
} else {
delete nd; /* bye, node */
return;
}
} else {
if (nd->mNumMeshes || !child_nodes.empty()) {
nodes.push_back(nd);
} else {
delete nd; /* bye, node */
return;
}
} else {
// Retain our current position in the hierarchy
nodes.push_back(nd);
// Retain our current position in the hierarchy
nodes.push_back(nd);
// Now check for possible optimizations in our list of child nodes. join as many as possible
aiNode* join_master = NULL;
aiMatrix4x4 inv;
// Now check for possible optimizations in our list of child nodes. join as many as possible
aiNode *join_master = nullptr;
aiMatrix4x4 inv;
const LockedSetType::const_iterator end = locked.end();
const LockedSetType::const_iterator end = locked.end();
std::list<aiNode*> join;
for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
aiNode* child = *it;
if (child->mNumChildren == 0 && locked.find(AI_OG_GETKEY(child->mName)) == end) {
std::list<aiNode *> join;
for (std::list<aiNode *>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
aiNode *child = *it;
if (child->mNumChildren == 0 && locked.find(AI_OG_GETKEY(child->mName)) == end) {
// There may be no instanced meshes
unsigned int n = 0;
for (; n < child->mNumMeshes;++n) {
if (meshes[child->mMeshes[n]] > 1) {
break;
}
}
if (n == child->mNumMeshes) {
if (!join_master) {
join_master = child;
inv = join_master->mTransformation;
inv.Inverse();
} else {
child->mTransformation = inv * child->mTransformation ;
// There may be no instanced meshes
unsigned int n = 0;
for (; n < child->mNumMeshes; ++n) {
if (meshes[child->mMeshes[n]] > 1) {
break;
}
}
if (n == child->mNumMeshes) {
if (!join_master) {
join_master = child;
inv = join_master->mTransformation;
inv.Inverse();
} else {
child->mTransformation = inv * child->mTransformation;
join.push_back(child);
it = child_nodes.erase(it);
continue;
}
}
}
++it;
}
if (join_master && !join.empty()) {
join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i",count_merged++);
join.push_back(child);
it = child_nodes.erase(it);
continue;
}
}
}
++it;
}
if (join_master && !join.empty()) {
join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%u", count_merged++);
unsigned int out_meshes = 0;
for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) {
out_meshes += (*it)->mNumMeshes;
}
unsigned int out_meshes = 0;
for (std::list<aiNode *>::const_iterator it = join.cbegin(); it != join.cend(); ++it) {
out_meshes += (*it)->mNumMeshes;
}
// copy all mesh references in one array
if (out_meshes) {
unsigned int* meshes = new unsigned int[out_meshes+join_master->mNumMeshes], *tmp = meshes;
for (unsigned int n = 0; n < join_master->mNumMeshes;++n) {
*tmp++ = join_master->mMeshes[n];
}
// copy all mesh references in one array
if (out_meshes) {
unsigned int *meshIdxs = new unsigned int[out_meshes + join_master->mNumMeshes], *tmp = meshIdxs;
for (unsigned int n = 0; n < join_master->mNumMeshes; ++n) {
*tmp++ = join_master->mMeshes[n];
}
for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) {
for (unsigned int n = 0; n < (*it)->mNumMeshes; ++n) {
for (const aiNode *join_node : join) {
for (unsigned int n = 0; n < join_node->mNumMeshes; ++n) {
*tmp = (*it)->mMeshes[n];
aiMesh* mesh = mScene->mMeshes[*tmp++];
*tmp = join_node->mMeshes[n];
aiMesh *mesh = mScene->mMeshes[*tmp++];
// manually move the mesh into the right coordinate system
const aiMatrix3x3 IT = aiMatrix3x3( (*it)->mTransformation ).Inverse().Transpose();
for (unsigned int a = 0; a < mesh->mNumVertices; ++a) {
// Assume the transformation is affine
// manually move the mesh into the right coordinate system
mesh->mVertices[a] *= (*it)->mTransformation;
// Check for odd negative scale (mirror)
if (join_node->mTransformation.Determinant() < 0) {
// Reverse the mesh face winding order
FlipWindingOrderProcess::ProcessMesh(mesh);
}
if (mesh->HasNormals())
mesh->mNormals[a] *= IT;
// Update positions, normals and tangents
const aiMatrix3x3 IT = aiMatrix3x3(join_node->mTransformation).Inverse().Transpose();
for (unsigned int a = 0; a < mesh->mNumVertices; ++a) {
if (mesh->HasTangentsAndBitangents()) {
mesh->mTangents[a] *= IT;
mesh->mBitangents[a] *= IT;
}
}
}
delete *it; // bye, node
}
delete[] join_master->mMeshes;
join_master->mMeshes = meshes;
join_master->mNumMeshes += out_meshes;
}
}
}
// reassign children if something changed
if (child_nodes.empty() || child_nodes.size() > nd->mNumChildren) {
mesh->mVertices[a] *= join_node->mTransformation;
delete[] nd->mChildren;
if (mesh->HasNormals())
mesh->mNormals[a] *= IT;
if (!child_nodes.empty()) {
nd->mChildren = new aiNode*[child_nodes.size()];
}
else nd->mChildren = nullptr;
}
if (mesh->HasTangentsAndBitangents()) {
mesh->mTangents[a] *= IT;
mesh->mBitangents[a] *= IT;
}
}
}
delete join_node; // bye, node
}
delete[] join_master->mMeshes;
join_master->mMeshes = meshIdxs;
join_master->mNumMeshes += out_meshes;
}
}
}
// reassign children if something changed
if (child_nodes.empty() || child_nodes.size() > nd->mNumChildren) {
nd->mNumChildren = static_cast<unsigned int>(child_nodes.size());
delete[] nd->mChildren;
if (nd->mChildren) {
aiNode** tmp = nd->mChildren;
for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) {
aiNode* node = *tmp++ = *it;
node->mParent = nd;
}
}
if (!child_nodes.empty()) {
nd->mChildren = new aiNode *[child_nodes.size()];
} else
nd->mChildren = nullptr;
}
nodes_out += static_cast<unsigned int>(child_nodes.size());
nd->mNumChildren = static_cast<unsigned int>(child_nodes.size());
if (nd->mChildren) {
aiNode **tmp = nd->mChildren;
for (std::list<aiNode *>::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) {
aiNode *node = *tmp++ = *it;
node->mParent = nd;
}
}
nodes_out += static_cast<unsigned int>(child_nodes.size());
}
// ------------------------------------------------------------------------------------------------
// Execute the post-processing step on the given scene
void OptimizeGraphProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("OptimizeGraphProcess begin");
nodes_in = nodes_out = count_merged = 0;
mScene = pScene;
void OptimizeGraphProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("OptimizeGraphProcess begin");
nodes_in = nodes_out = count_merged = 0;
mScene = pScene;
meshes.resize(pScene->mNumMeshes,0);
FindInstancedMeshes(pScene->mRootNode);
meshes.resize(pScene->mNumMeshes, 0);
FindInstancedMeshes(pScene->mRootNode);
// build a blacklist of identifiers. If the name of a node matches one of these, we won't touch it
locked.clear();
for (std::list<std::string>::const_iterator it = locked_nodes.begin(); it != locked_nodes.end(); ++it) {
// build a blacklist of identifiers. If the name of a node matches one of these, we won't touch it
locked.clear();
for (std::list<std::string>::const_iterator it = locked_nodes.begin(); it != locked_nodes.end(); ++it) {
#ifdef AI_OG_USE_HASHING
locked.insert(SuperFastHash((*it).c_str()));
locked.insert(SuperFastHash((*it).c_str()));
#else
locked.insert(*it);
locked.insert(*it);
#endif
}
}
for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) {
for (unsigned int a = 0; a < pScene->mAnimations[i]->mNumChannels; ++a) {
aiNodeAnim* anim = pScene->mAnimations[i]->mChannels[a];
locked.insert(AI_OG_GETKEY(anim->mNodeName));
}
}
for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) {
for (unsigned int a = 0; a < pScene->mAnimations[i]->mNumChannels; ++a) {
aiNodeAnim *anim = pScene->mAnimations[i]->mChannels[a];
locked.insert(AI_OG_GETKEY(anim->mNodeName));
}
}
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
for (unsigned int a = 0; a < pScene->mMeshes[i]->mNumBones; ++a) {
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
for (unsigned int a = 0; a < pScene->mMeshes[i]->mNumBones; ++a) {
aiBone* bone = pScene->mMeshes[i]->mBones[a];
locked.insert(AI_OG_GETKEY(bone->mName));
aiBone *bone = pScene->mMeshes[i]->mBones[a];
locked.insert(AI_OG_GETKEY(bone->mName));
// HACK: Meshes referencing bones may not be transformed; we need to look them.
// The easiest way to do this is to increase their reference counters ...
meshes[i] += 2;
}
}
// HACK: Meshes referencing bones may not be transformed; we need to look them.
// The easiest way to do this is to increase their reference counters ...
meshes[i] += 2;
}
}
for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {
aiCamera* cam = pScene->mCameras[i];
locked.insert(AI_OG_GETKEY(cam->mName));
}
for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {
aiCamera *cam = pScene->mCameras[i];
locked.insert(AI_OG_GETKEY(cam->mName));
}
for (unsigned int i = 0; i < pScene->mNumLights; ++i) {
aiLight* lgh = pScene->mLights[i];
locked.insert(AI_OG_GETKEY(lgh->mName));
}
for (unsigned int i = 0; i < pScene->mNumLights; ++i) {
aiLight *lgh = pScene->mLights[i];
locked.insert(AI_OG_GETKEY(lgh->mName));
}
// Insert a dummy master node and make it read-only
aiNode* dummy_root = new aiNode(AI_RESERVED_NODE_NAME);
locked.insert(AI_OG_GETKEY(dummy_root->mName));
// Insert a dummy master node and make it read-only
aiNode *dummy_root = new aiNode(AI_RESERVED_NODE_NAME);
locked.insert(AI_OG_GETKEY(dummy_root->mName));
const aiString prev = pScene->mRootNode->mName;
pScene->mRootNode->mParent = dummy_root;
const aiString prev = pScene->mRootNode->mName;
pScene->mRootNode->mParent = dummy_root;
dummy_root->mChildren = new aiNode*[dummy_root->mNumChildren = 1];
dummy_root->mChildren[0] = pScene->mRootNode;
dummy_root->mChildren = new aiNode *[dummy_root->mNumChildren = 1];
dummy_root->mChildren[0] = pScene->mRootNode;
// Do our recursive processing of scenegraph nodes. For each node collect
// a fully new list of children and allow their children to place themselves
// on the same hierarchy layer as their parents.
std::list<aiNode*> nodes;
CollectNewChildren (dummy_root,nodes);
// Do our recursive processing of scenegraph nodes. For each node collect
// a fully new list of children and allow their children to place themselves
// on the same hierarchy layer as their parents.
std::list<aiNode *> nodes;
CollectNewChildren(dummy_root, nodes);
ai_assert(nodes.size() == 1);
ai_assert(nodes.size() == 1);
if (dummy_root->mNumChildren == 0) {
pScene->mRootNode = NULL;
throw DeadlyImportError("After optimizing the scene graph, no data remains");
}
if (dummy_root->mNumChildren == 0) {
pScene->mRootNode = nullptr;
throw DeadlyImportError("After optimizing the scene graph, no data remains");
}
if (dummy_root->mNumChildren > 1) {
pScene->mRootNode = dummy_root;
if (dummy_root->mNumChildren > 1) {
pScene->mRootNode = dummy_root;
// Keep the dummy node but assign the name of the old root node to it
pScene->mRootNode->mName = prev;
}
else {
// Keep the dummy node but assign the name of the old root node to it
pScene->mRootNode->mName = prev;
} else {
// Remove the dummy root node again.
pScene->mRootNode = dummy_root->mChildren[0];
// Remove the dummy root node again.
pScene->mRootNode = dummy_root->mChildren[0];
dummy_root->mChildren[0] = NULL;
delete dummy_root;
}
dummy_root->mChildren[0] = nullptr;
delete dummy_root;
}
pScene->mRootNode->mParent = NULL;
if (!DefaultLogger::isNullLogger()) {
if ( nodes_in != nodes_out) {
ASSIMP_LOG_INFO_F("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out);
} else {
ASSIMP_LOG_DEBUG("OptimizeGraphProcess finished");
}
}
meshes.clear();
locked.clear();
pScene->mRootNode->mParent = nullptr;
if (!DefaultLogger::isNullLogger()) {
if (nodes_in != nodes_out) {
ASSIMP_LOG_INFO("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out);
} else {
ASSIMP_LOG_DEBUG("OptimizeGraphProcess finished");
}
}
meshes.clear();
locked.clear();
}
// ------------------------------------------------------------------------------------------------
// Build a LUT of all instanced meshes
void OptimizeGraphProcess::FindInstancedMeshes (aiNode* pNode)
{
for (unsigned int i = 0; i < pNode->mNumMeshes;++i) {
++meshes[pNode->mMeshes[i]];
}
void OptimizeGraphProcess::FindInstancedMeshes(aiNode *pNode) {
for (unsigned int i = 0; i < pNode->mNumMeshes; ++i) {
++meshes[pNode->mMeshes[i]];
}
for (unsigned int i = 0; i < pNode->mNumChildren; ++i)
FindInstancedMeshes(pNode->mChildren[i]);
for (unsigned int i = 0; i < pNode->mNumChildren; ++i)
FindInstancedMeshes(pNode->mChildren[i]);
}
#endif // !! ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -75,13 +75,13 @@ public:
~OptimizeGraphProcess();
// -------------------------------------------------------------------
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 Add a list of node names to be locked and not modified.

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -151,7 +151,7 @@ void OptimizeMeshesProcess::Execute( aiScene* pScene)
std::copy(output.begin(),output.end(),mScene->mMeshes);
if (output.size() != num_old) {
ASSIMP_LOG_DEBUG_F("OptimizeMeshesProcess finished. Input meshes: ", num_old, ", Output meshes: ", pScene->mNumMeshes);
ASSIMP_LOG_DEBUG("OptimizeMeshesProcess finished. Input meshes: ", num_old, ", Output meshes: ", pScene->mNumMeshes);
} else {
ASSIMP_LOG_DEBUG( "OptimizeMeshesProcess finished" );
}

View file

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

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -59,7 +59,7 @@ struct aiNode;
class PretransformVerticesTest;
namespace Assimp {
namespace Assimp {
// ---------------------------------------------------------------------------
/** The PretransformVertices pre-transforms all vertices in the node tree
@ -68,97 +68,97 @@ namespace Assimp {
*/
class ASSIMP_API PretransformVertices : public BaseProcess {
public:
PretransformVertices ();
~PretransformVertices ();
PretransformVertices();
~PretransformVertices();
// -------------------------------------------------------------------
// Check whether step is active
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
// Check whether step is active
bool IsActive(unsigned int pFlags) const override;
// -------------------------------------------------------------------
// Execute step on a given scene
void Execute( aiScene* pScene);
// -------------------------------------------------------------------
// Execute step on a given scene
void Execute(aiScene *pScene) override;
// -------------------------------------------------------------------
// Setup import settings
void SetupProperties(const Importer* pImp);
// -------------------------------------------------------------------
// Setup import settings
void SetupProperties(const Importer *pImp) override;
// -------------------------------------------------------------------
/** @brief Toggle the 'keep hierarchy' option
// -------------------------------------------------------------------
/** @brief Toggle the 'keep hierarchy' option
* @param keep true for keep configuration.
*/
void KeepHierarchy(bool keep) {
configKeepHierarchy = keep;
}
void KeepHierarchy(bool keep) {
configKeepHierarchy = keep;
}
// -------------------------------------------------------------------
/** @brief Check whether 'keep hierarchy' is currently enabled.
// -------------------------------------------------------------------
/** @brief Check whether 'keep hierarchy' is currently enabled.
* @return ...
*/
bool IsHierarchyKept() const {
return configKeepHierarchy;
}
bool IsHierarchyKept() const {
return configKeepHierarchy;
}
private:
// -------------------------------------------------------------------
// Count the number of nodes
unsigned int CountNodes( aiNode* pcNode );
// -------------------------------------------------------------------
// Count the number of nodes
unsigned int CountNodes(const aiNode *pcNode) const;
// -------------------------------------------------------------------
// Get a bitwise combination identifying the vertex format of a mesh
unsigned int GetMeshVFormat(aiMesh* pcMesh);
// -------------------------------------------------------------------
// Get a bitwise combination identifying the vertex format of a mesh
unsigned int GetMeshVFormat(aiMesh *pcMesh) const;
// -------------------------------------------------------------------
// Count the number of vertices in the whole scene and a given
// material index
void CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode,
unsigned int iMat,
unsigned int iVFormat,
unsigned int* piFaces,
unsigned int* piVertices);
// -------------------------------------------------------------------
// Count the number of vertices in the whole scene and a given
// material index
void CountVerticesAndFaces(const aiScene *pcScene, const aiNode *pcNode,
unsigned int iMat,
unsigned int iVFormat,
unsigned int *piFaces,
unsigned int *piVertices) const;
// -------------------------------------------------------------------
// Collect vertex/face data
void CollectData( aiScene* pcScene, aiNode* pcNode,
unsigned int iMat,
unsigned int iVFormat,
aiMesh* pcMeshOut,
unsigned int aiCurrent[2],
unsigned int* num_refs);
// -------------------------------------------------------------------
// Collect vertex/face data
void CollectData(const aiScene *pcScene, const aiNode *pcNode,
unsigned int iMat,
unsigned int iVFormat,
aiMesh *pcMeshOut,
unsigned int aiCurrent[2],
unsigned int *num_refs) const;
// -------------------------------------------------------------------
// Get a list of all vertex formats that occur for a given material
// The output list contains duplicate elements
void GetVFormatList( aiScene* pcScene, unsigned int iMat,
std::list<unsigned int>& aiOut);
// -------------------------------------------------------------------
// 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;
// -------------------------------------------------------------------
// Compute the absolute transformation matrices of each node
void ComputeAbsoluteTransform( aiNode* pcNode );
// -------------------------------------------------------------------
// Compute the absolute transformation matrices of each node
void ComputeAbsoluteTransform(aiNode *pcNode);
// -------------------------------------------------------------------
// Simple routine to build meshes in worldspace, no further optimization
void BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in,
unsigned int numIn, aiNode* node);
// -------------------------------------------------------------------
// Simple routine to build meshes in worldspace, no further optimization
void BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **in,
unsigned int numIn, aiNode *node) const;
// -------------------------------------------------------------------
// Apply the node transformation to a mesh
void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat);
// -------------------------------------------------------------------
// Apply the node transformation to a mesh
void ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat) const;
// -------------------------------------------------------------------
// Reset transformation matrices to identity
void MakeIdentityTransform(aiNode* nd);
// -------------------------------------------------------------------
// Reset transformation matrices to identity
void MakeIdentityTransform(aiNode *nd) const;
// -------------------------------------------------------------------
// Build reference counters for all meshes
void BuildMeshRefCountArray(aiNode* nd, unsigned int * refs);
// -------------------------------------------------------------------
// Build reference counters for all meshes
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 mConfigPointCloud;
//! Configuration option: keep scene hierarchy as long as possible
bool configKeepHierarchy;
bool configNormalize;
bool configTransform;
aiMatrix4x4 configTransformation;
bool mConfigPointCloud;
};
} // end of namespace Assimp

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -43,22 +43,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// @file ProcessHelper.cpp
/** Implement shared utility functions for postprocessing steps */
#include "ProcessHelper.h"
#include <limits>
namespace Assimp {
// -------------------------------------------------------------------------------
void ConvertListToStrings(const std::string& in, std::list<std::string>& out)
{
const char* s = in.c_str();
void ConvertListToStrings(const std::string &in, std::list<std::string> &out) {
const char *s = in.c_str();
while (*s) {
SkipSpacesAndLineEnd(&s);
if (*s == '\'') {
const char* base = ++s;
const char *base = ++s;
while (*s != '\'') {
++s;
if (*s == '\0') {
@ -66,43 +63,39 @@ void ConvertListToStrings(const std::string& in, std::list<std::string>& out)
return;
}
}
out.push_back(std::string(base,(size_t)(s-base)));
out.push_back(std::string(base, (size_t)(s - base)));
++s;
}
else {
} else {
out.push_back(GetNextToken(s));
}
}
}
// -------------------------------------------------------------------------------
void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max,
const aiMatrix4x4& m)
{
min = aiVector3D ( ai_real( 10e10 ), ai_real( 10e10 ), ai_real( 10e10 ) );
max = aiVector3D ( ai_real( -10e10 ), ai_real( -10e10 ), ai_real( -10e10 ) );
for (unsigned int i = 0;i < mesh->mNumVertices;++i)
{
void FindAABBTransformed(const aiMesh *mesh, aiVector3D &min, aiVector3D &max,
const aiMatrix4x4 &m) {
min = aiVector3D(ai_real(10e10), ai_real(10e10), ai_real(10e10));
max = aiVector3D(ai_real(-10e10), ai_real(-10e10), ai_real(-10e10));
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
const aiVector3D v = m * mesh->mVertices[i];
min = std::min(v,min);
max = std::max(v,max);
min = std::min(v, min);
max = std::max(v, max);
}
}
// -------------------------------------------------------------------------------
void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max)
{
ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max);
out = min + (max-min)*(ai_real)0.5;
void FindMeshCenter(aiMesh *mesh, aiVector3D &out, aiVector3D &min, aiVector3D &max) {
ArrayBounds(mesh->mVertices, mesh->mNumVertices, min, max);
out = min + (max - min) * (ai_real)0.5;
}
// -------------------------------------------------------------------------------
void FindSceneCenter (aiScene* scene, aiVector3D& out, aiVector3D& min, aiVector3D& max) {
if ( NULL == scene ) {
void FindSceneCenter(aiScene *scene, aiVector3D &out, aiVector3D &min, aiVector3D &max) {
if (nullptr == scene) {
return;
}
if ( 0 == scene->mNumMeshes ) {
if (0 == scene->mNumMeshes) {
return;
}
FindMeshCenter(scene->mMeshes[0], out, min, max);
@ -116,79 +109,71 @@ void FindSceneCenter (aiScene* scene, aiVector3D& out, aiVector3D& min, aiVector
if (max[1] < tmax[1]) max[1] = tmax[1];
if (max[2] < tmax[2]) max[2] = tmax[2];
}
out = min + (max-min)*(ai_real)0.5;
}
// -------------------------------------------------------------------------------
void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,
aiVector3D& max, const aiMatrix4x4& m)
{
FindAABBTransformed(mesh,min,max,m);
out = min + (max-min)*(ai_real)0.5;
out = min + (max - min) * (ai_real)0.5;
}
// -------------------------------------------------------------------------------
void FindMeshCenter (aiMesh* mesh, aiVector3D& out)
{
aiVector3D min,max;
FindMeshCenter(mesh,out,min,max);
void FindMeshCenterTransformed(aiMesh *mesh, aiVector3D &out, aiVector3D &min,
aiVector3D &max, const aiMatrix4x4 &m) {
FindAABBTransformed(mesh, min, max, m);
out = min + (max - min) * (ai_real)0.5;
}
// -------------------------------------------------------------------------------
void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,
const aiMatrix4x4& m)
{
aiVector3D min,max;
FindMeshCenterTransformed(mesh,out,min,max,m);
void FindMeshCenter(aiMesh *mesh, aiVector3D &out) {
aiVector3D min, max;
FindMeshCenter(mesh, out, min, max);
}
// -------------------------------------------------------------------------------
ai_real ComputePositionEpsilon(const aiMesh* pMesh)
{
const ai_real epsilon = ai_real( 1e-4 );
void FindMeshCenterTransformed(aiMesh *mesh, aiVector3D &out,
const aiMatrix4x4 &m) {
aiVector3D min, max;
FindMeshCenterTransformed(mesh, out, min, max, m);
}
// -------------------------------------------------------------------------------
ai_real ComputePositionEpsilon(const aiMesh *pMesh) {
const ai_real epsilon = ai_real(1e-4);
// calculate the position bounds so we have a reliable epsilon to check position differences against
aiVector3D minVec, maxVec;
ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec);
ArrayBounds(pMesh->mVertices, pMesh->mNumVertices, minVec, maxVec);
return (maxVec - minVec).Length() * epsilon;
}
// -------------------------------------------------------------------------------
ai_real ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num)
{
ai_assert( NULL != pMeshes );
ai_real ComputePositionEpsilon(const aiMesh *const *pMeshes, size_t num) {
ai_assert(nullptr != pMeshes);
const ai_real epsilon = ai_real( 1e-4 );
const ai_real epsilon = ai_real(1e-4);
// calculate the position bounds so we have a reliable epsilon to check position differences against
aiVector3D minVec, maxVec, mi, ma;
MinMaxChooser<aiVector3D>()(minVec,maxVec);
MinMaxChooser<aiVector3D>()(minVec, maxVec);
for (size_t a = 0; a < num; ++a) {
const aiMesh* pMesh = pMeshes[a];
ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,mi,ma);
const aiMesh *pMesh = pMeshes[a];
ArrayBounds(pMesh->mVertices, pMesh->mNumVertices, mi, ma);
minVec = std::min(minVec,mi);
maxVec = std::max(maxVec,ma);
minVec = std::min(minVec, mi);
maxVec = std::max(maxVec, ma);
}
return (maxVec - minVec).Length() * epsilon;
}
// -------------------------------------------------------------------------------
unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh)
{
ai_assert(NULL != pcMesh);
unsigned int GetMeshVFormatUnique(const aiMesh *pcMesh) {
ai_assert(nullptr != pcMesh);
// FIX: the hash may never be 0. Otherwise a comparison against
// nullptr could be successful
unsigned int iRet = 1;
// normals
if (pcMesh->HasNormals())iRet |= 0x2;
if (pcMesh->HasNormals()) iRet |= 0x2;
// tangents and bitangents
if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4;
if (pcMesh->HasTangentsAndBitangents()) iRet |= 0x4;
#ifdef BOOST_STATIC_ASSERT
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
@ -197,8 +182,7 @@ unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh)
// texture coordinates
unsigned int p = 0;
while (pcMesh->HasTextureCoords(p))
{
while (pcMesh->HasTextureCoords(p)) {
iRet |= (0x100 << p);
if (3 == pcMesh->mNumUVComponents[p])
iRet |= (0x10000 << p);
@ -207,74 +191,32 @@ unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh)
}
// vertex colors
p = 0;
while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++);
while (pcMesh->HasVertexColors(p))
iRet |= (0x1000000 << p++);
return iRet;
}
// -------------------------------------------------------------------------------
VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh)
{
VertexWeightTable *ComputeVertexBoneWeightTable(const aiMesh *pMesh) {
if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) {
return NULL;
return nullptr;
}
VertexWeightTable* avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices];
for (unsigned int i = 0; i < pMesh->mNumBones;++i) {
VertexWeightTable *avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices];
for (unsigned int i = 0; i < pMesh->mNumBones; ++i) {
aiBone* bone = pMesh->mBones[i];
for (unsigned int a = 0; a < bone->mNumWeights;++a) {
const aiVertexWeight& weight = bone->mWeights[a];
avPerVertexWeights[weight.mVertexId].push_back( std::pair<unsigned int,float>(i,weight.mWeight) );
aiBone *bone = pMesh->mBones[i];
for (unsigned int a = 0; a < bone->mNumWeights; ++a) {
const aiVertexWeight &weight = bone->mWeights[a];
avPerVertexWeights[weight.mVertexId].push_back(std::pair<unsigned int, float>(i, weight.mWeight));
}
}
return avPerVertexWeights;
}
// -------------------------------------------------------------------------------
const char* TextureTypeToString(aiTextureType in)
{
switch (in)
{
case aiTextureType_NONE:
return "n/a";
case aiTextureType_DIFFUSE:
return "Diffuse";
case aiTextureType_SPECULAR:
return "Specular";
case aiTextureType_AMBIENT:
return "Ambient";
case aiTextureType_EMISSIVE:
return "Emissive";
case aiTextureType_OPACITY:
return "Opacity";
case aiTextureType_NORMALS:
return "Normals";
case aiTextureType_HEIGHT:
return "Height";
case aiTextureType_SHININESS:
return "Shininess";
case aiTextureType_DISPLACEMENT:
return "Displacement";
case aiTextureType_LIGHTMAP:
return "Lightmap";
case aiTextureType_REFLECTION:
return "Reflection";
case aiTextureType_UNKNOWN:
return "Unknown";
default:
break;
}
ai_assert(false);
return "BUG";
}
// -------------------------------------------------------------------------------
const char* MappingTypeToString(aiTextureMapping in)
{
switch (in)
{
const char *MappingTypeToString(aiTextureMapping in) {
switch (in) {
case aiTextureMapping_UV:
return "UV";
case aiTextureMapping_BOX:
@ -292,24 +234,22 @@ const char* MappingTypeToString(aiTextureMapping in)
}
ai_assert(false);
return "BUG";
return "BUG";
}
// -------------------------------------------------------------------------------
aiMesh* MakeSubmesh(const aiMesh *pMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags)
{
aiMesh *MakeSubmesh(const aiMesh *pMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags) {
aiMesh *oMesh = new aiMesh();
std::vector<unsigned int> vMap(pMesh->mNumVertices,UINT_MAX);
std::vector<unsigned int> vMap(pMesh->mNumVertices, UINT_MAX);
size_t numSubVerts = 0;
size_t numSubFaces = subMeshFaces.size();
for(unsigned int i=0;i<numSubFaces;i++) {
for (unsigned int i = 0; i < numSubFaces; i++) {
const aiFace &f = pMesh->mFaces[subMeshFaces[i]];
for(unsigned int j=0;j<f.mNumIndices;j++) {
if(vMap[f.mIndices[j]]==UINT_MAX) {
for (unsigned int j = 0; j < f.mNumIndices; j++) {
if (vMap[f.mIndices[j]] == UINT_MAX) {
vMap[f.mIndices[j]] = static_cast<unsigned int>(numSubVerts++);
}
}
@ -325,114 +265,114 @@ aiMesh* MakeSubmesh(const aiMesh *pMesh, const std::vector<unsigned int> &subMes
oMesh->mNumFaces = static_cast<unsigned int>(subMeshFaces.size());
oMesh->mNumVertices = static_cast<unsigned int>(numSubVerts);
oMesh->mVertices = new aiVector3D[numSubVerts];
if( pMesh->HasNormals() ) {
if (pMesh->HasNormals()) {
oMesh->mNormals = new aiVector3D[numSubVerts];
}
if( pMesh->HasTangentsAndBitangents() ) {
if (pMesh->HasTangentsAndBitangents()) {
oMesh->mTangents = new aiVector3D[numSubVerts];
oMesh->mBitangents = new aiVector3D[numSubVerts];
}
for( size_t a = 0; pMesh->HasTextureCoords(static_cast<unsigned int>(a)) ; ++a ) {
for (size_t a = 0; pMesh->HasTextureCoords(static_cast<unsigned int>(a)); ++a) {
oMesh->mTextureCoords[a] = new aiVector3D[numSubVerts];
oMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
}
for( size_t a = 0; pMesh->HasVertexColors( static_cast<unsigned int>(a)); ++a ) {
for (size_t a = 0; pMesh->HasVertexColors(static_cast<unsigned int>(a)); ++a) {
oMesh->mColors[a] = new aiColor4D[numSubVerts];
}
// and copy over the data, generating faces with linear indices along the way
oMesh->mFaces = new aiFace[numSubFaces];
for(unsigned int a = 0; a < numSubFaces; ++a ) {
for (unsigned int a = 0; a < numSubFaces; ++a) {
const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
aiFace& dstFace = oMesh->mFaces[a];
const aiFace &srcFace = pMesh->mFaces[subMeshFaces[a]];
aiFace &dstFace = oMesh->mFaces[a];
dstFace.mNumIndices = srcFace.mNumIndices;
dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
// accumulate linearly all the vertices of the source face
for( size_t b = 0; b < dstFace.mNumIndices; ++b ) {
for (size_t b = 0; b < dstFace.mNumIndices; ++b) {
dstFace.mIndices[b] = vMap[srcFace.mIndices[b]];
}
}
for(unsigned int srcIndex = 0; srcIndex < pMesh->mNumVertices; ++srcIndex ) {
for (unsigned int srcIndex = 0; srcIndex < pMesh->mNumVertices; ++srcIndex) {
unsigned int nvi = vMap[srcIndex];
if(nvi==UINT_MAX) {
if (nvi == UINT_MAX) {
continue;
}
oMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
if( pMesh->HasNormals() ) {
if (pMesh->HasNormals()) {
oMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
}
if( pMesh->HasTangentsAndBitangents() ) {
if (pMesh->HasTangentsAndBitangents()) {
oMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
oMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
}
for( size_t c = 0, cc = pMesh->GetNumUVChannels(); c < cc; ++c ) {
oMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
for (size_t c = 0, cc = pMesh->GetNumUVChannels(); c < cc; ++c) {
oMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
}
for( size_t c = 0, cc = pMesh->GetNumColorChannels(); c < cc; ++c ) {
for (size_t c = 0, cc = pMesh->GetNumColorChannels(); c < cc; ++c) {
oMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
}
}
if(~subFlags&AI_SUBMESH_FLAGS_SANS_BONES) {
std::vector<unsigned int> subBones(pMesh->mNumBones,0);
if (~subFlags & AI_SUBMESH_FLAGS_SANS_BONES) {
std::vector<unsigned int> subBones(pMesh->mNumBones, 0);
for(unsigned int a=0;a<pMesh->mNumBones;++a) {
const aiBone* bone = pMesh->mBones[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++) {
for (unsigned int b = 0; b < bone->mNumWeights; b++) {
unsigned int v = vMap[bone->mWeights[b].mVertexId];
if(v!=UINT_MAX) {
if (v != UINT_MAX) {
subBones[a]++;
}
}
}
for(unsigned int a=0;a<pMesh->mNumBones;++a) {
if(subBones[a]>0) {
for (unsigned int a = 0; a < pMesh->mNumBones; ++a) {
if (subBones[a] > 0) {
oMesh->mNumBones++;
}
}
if(oMesh->mNumBones) {
oMesh->mBones = new aiBone*[oMesh->mNumBones]();
if (oMesh->mNumBones) {
oMesh->mBones = new aiBone *[oMesh->mNumBones]();
unsigned int nbParanoia = oMesh->mNumBones;
oMesh->mNumBones = 0; //rewind
for(unsigned int a=0;a<pMesh->mNumBones;++a) {
if(subBones[a]==0) {
for (unsigned int a = 0; a < pMesh->mNumBones; ++a) {
if (subBones[a] == 0) {
continue;
}
aiBone *newBone = new aiBone;
oMesh->mBones[oMesh->mNumBones++] = newBone;
const aiBone* bone = pMesh->mBones[a];
const aiBone *bone = pMesh->mBones[a];
newBone->mName = bone->mName;
newBone->mOffsetMatrix = bone->mOffsetMatrix;
newBone->mWeights = new aiVertexWeight[subBones[a]];
for(unsigned int b=0;b<bone->mNumWeights;b++) {
for (unsigned int b = 0; b < bone->mNumWeights; b++) {
const unsigned int v = vMap[bone->mWeights[b].mVertexId];
if(v!=UINT_MAX) {
aiVertexWeight w(v,bone->mWeights[b].mWeight);
if (v != UINT_MAX) {
aiVertexWeight w(v, bone->mWeights[b].mWeight);
newBone->mWeights[newBone->mNumWeights++] = w;
}
}
}
ai_assert(nbParanoia==oMesh->mNumBones);
ai_assert(nbParanoia == oMesh->mNumBones);
(void)nbParanoia; // remove compiler warning on release build
}
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -43,16 +43,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_PROCESS_HELPER_H_INCLUDED
#define AI_PROCESS_HELPER_H_INCLUDED
#include <assimp/postprocess.h>
#include <assimp/anim.h>
#include <assimp/mesh.h>
#include <assimp/material.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/SpatialSort.h>
#include "Common/BaseProcess.h"
#include <assimp/ParsingUtils.h>
#include <assimp/SpatialSort.h>
#include <list>
@ -63,86 +63,83 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifdef __cplusplus
namespace std {
// std::min for aiVector3D
template <typename TReal>
inline ::aiVector3t<TReal> min (const ::aiVector3t<TReal>& a, const ::aiVector3t<TReal>& b) {
return ::aiVector3t<TReal> (min(a.x,b.x),min(a.y,b.y),min(a.z,b.z));
}
// std::min for aiVector3D
template <typename TReal>
inline ::aiVector3t<TReal> min(const ::aiVector3t<TReal> &a, const ::aiVector3t<TReal> &b) {
return ::aiVector3t<TReal>(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z));
}
// std::max for aiVector3t<TReal>
template <typename TReal>
inline ::aiVector3t<TReal> max (const ::aiVector3t<TReal>& a, const ::aiVector3t<TReal>& b) {
return ::aiVector3t<TReal> (max(a.x,b.x),max(a.y,b.y),max(a.z,b.z));
}
// std::max for aiVector3t<TReal>
template <typename TReal>
inline ::aiVector3t<TReal> max(const ::aiVector3t<TReal> &a, const ::aiVector3t<TReal> &b) {
return ::aiVector3t<TReal>(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z));
}
// std::min for aiVector2t<TReal>
template <typename TReal>
inline ::aiVector2t<TReal> min (const ::aiVector2t<TReal>& a, const ::aiVector2t<TReal>& b) {
return ::aiVector2t<TReal> (min(a.x,b.x),min(a.y,b.y));
}
// std::min for aiVector2t<TReal>
template <typename TReal>
inline ::aiVector2t<TReal> min(const ::aiVector2t<TReal> &a, const ::aiVector2t<TReal> &b) {
return ::aiVector2t<TReal>(min(a.x, b.x), min(a.y, b.y));
}
// std::max for aiVector2t<TReal>
template <typename TReal>
inline ::aiVector2t<TReal> max (const ::aiVector2t<TReal>& a, const ::aiVector2t<TReal>& b) {
return ::aiVector2t<TReal> (max(a.x,b.x),max(a.y,b.y));
}
// std::max for aiVector2t<TReal>
template <typename TReal>
inline ::aiVector2t<TReal> max(const ::aiVector2t<TReal> &a, const ::aiVector2t<TReal> &b) {
return ::aiVector2t<TReal>(max(a.x, b.x), max(a.y, b.y));
}
// std::min for aiColor4D
template <typename TReal>
inline ::aiColor4t<TReal> min (const ::aiColor4t<TReal>& a, const ::aiColor4t<TReal>& b) {
return ::aiColor4t<TReal> (min(a.r,b.r),min(a.g,b.g),min(a.b,b.b),min(a.a,b.a));
}
// std::min for aiColor4D
template <typename TReal>
inline ::aiColor4t<TReal> min(const ::aiColor4t<TReal> &a, const ::aiColor4t<TReal> &b) {
return ::aiColor4t<TReal>(min(a.r, b.r), min(a.g, b.g), min(a.b, b.b), min(a.a, b.a));
}
// std::max for aiColor4D
template <typename TReal>
inline ::aiColor4t<TReal> max (const ::aiColor4t<TReal>& a, const ::aiColor4t<TReal>& b) {
return ::aiColor4t<TReal> (max(a.r,b.r),max(a.g,b.g),max(a.b,b.b),max(a.a,b.a));
}
// std::max for aiColor4D
template <typename TReal>
inline ::aiColor4t<TReal> max(const ::aiColor4t<TReal> &a, const ::aiColor4t<TReal> &b) {
return ::aiColor4t<TReal>(max(a.r, b.r), max(a.g, b.g), max(a.b, b.b), max(a.a, b.a));
}
// std::min for aiQuaterniont<TReal>
template <typename TReal>
inline ::aiQuaterniont<TReal> min(const ::aiQuaterniont<TReal> &a, const ::aiQuaterniont<TReal> &b) {
return ::aiQuaterniont<TReal>(min(a.w, b.w), min(a.x, b.x), min(a.y, b.y), min(a.z, b.z));
}
// std::min for aiQuaterniont<TReal>
template <typename TReal>
inline ::aiQuaterniont<TReal> min (const ::aiQuaterniont<TReal>& a, const ::aiQuaterniont<TReal>& b) {
return ::aiQuaterniont<TReal> (min(a.w,b.w),min(a.x,b.x),min(a.y,b.y),min(a.z,b.z));
}
// std::max for aiQuaterniont<TReal>
template <typename TReal>
inline ::aiQuaterniont<TReal> max(const ::aiQuaterniont<TReal> &a, const ::aiQuaterniont<TReal> &b) {
return ::aiQuaterniont<TReal>(max(a.w, b.w), max(a.x, b.x), max(a.y, b.y), max(a.z, b.z));
}
// std::max for aiQuaterniont<TReal>
template <typename TReal>
inline ::aiQuaterniont<TReal> max (const ::aiQuaterniont<TReal>& a, const ::aiQuaterniont<TReal>& b) {
return ::aiQuaterniont<TReal> (max(a.w,b.w),max(a.x,b.x),max(a.y,b.y),max(a.z,b.z));
}
// std::min for aiVectorKey
inline ::aiVectorKey min(const ::aiVectorKey &a, const ::aiVectorKey &b) {
return ::aiVectorKey(min(a.mTime, b.mTime), min(a.mValue, b.mValue));
}
// std::max for aiVectorKey
inline ::aiVectorKey max(const ::aiVectorKey &a, const ::aiVectorKey &b) {
return ::aiVectorKey(max(a.mTime, b.mTime), max(a.mValue, b.mValue));
}
// std::min for aiQuatKey
inline ::aiQuatKey min(const ::aiQuatKey &a, const ::aiQuatKey &b) {
return ::aiQuatKey(min(a.mTime, b.mTime), min(a.mValue, b.mValue));
}
// std::min for aiVectorKey
inline ::aiVectorKey min (const ::aiVectorKey& a, const ::aiVectorKey& b) {
return ::aiVectorKey (min(a.mTime,b.mTime),min(a.mValue,b.mValue));
}
// std::max for aiQuatKey
inline ::aiQuatKey max(const ::aiQuatKey &a, const ::aiQuatKey &b) {
return ::aiQuatKey(max(a.mTime, b.mTime), max(a.mValue, b.mValue));
}
// std::max for aiVectorKey
inline ::aiVectorKey max (const ::aiVectorKey& a, const ::aiVectorKey& b) {
return ::aiVectorKey (max(a.mTime,b.mTime),max(a.mValue,b.mValue));
}
// std::min for aiVertexWeight
inline ::aiVertexWeight min(const ::aiVertexWeight &a, const ::aiVertexWeight &b) {
return ::aiVertexWeight(min(a.mVertexId, b.mVertexId),static_cast<ai_real>(min(a.mWeight, b.mWeight)));
}
// std::min for aiQuatKey
inline ::aiQuatKey min (const ::aiQuatKey& a, const ::aiQuatKey& b) {
return ::aiQuatKey (min(a.mTime,b.mTime),min(a.mValue,b.mValue));
}
// std::max for aiQuatKey
inline ::aiQuatKey max (const ::aiQuatKey& a, const ::aiQuatKey& b) {
return ::aiQuatKey (max(a.mTime,b.mTime),max(a.mValue,b.mValue));
}
// std::min for aiVertexWeight
inline ::aiVertexWeight min (const ::aiVertexWeight& a, const ::aiVertexWeight& b) {
return ::aiVertexWeight (min(a.mVertexId,b.mVertexId),min(a.mWeight,b.mWeight));
}
// std::max for aiVertexWeight
inline ::aiVertexWeight max (const ::aiVertexWeight& a, const ::aiVertexWeight& b) {
return ::aiVertexWeight (max(a.mVertexId,b.mVertexId),max(a.mWeight,b.mWeight));
}
// std::max for aiVertexWeight
inline ::aiVertexWeight max(const ::aiVertexWeight &a, const ::aiVertexWeight &b) {
return ::aiVertexWeight(max(a.mVertexId, b.mVertexId), static_cast<ai_real>(max(a.mWeight, b.mWeight)));
}
} // end namespace std
#endif // !! C++
@ -154,60 +151,80 @@ namespace Assimp {
template <typename T>
struct MinMaxChooser;
template <> struct MinMaxChooser<float> {
void operator ()(float& min,float& max) {
template <>
struct MinMaxChooser<float> {
void operator()(float &min, float &max) {
max = -1e10f;
min = 1e10f;
}};
template <> struct MinMaxChooser<double> {
void operator ()(double& min,double& max) {
min = 1e10f;
}
};
template <>
struct MinMaxChooser<double> {
void operator()(double &min, double &max) {
max = -1e10;
min = 1e10;
}};
template <> struct MinMaxChooser<unsigned int> {
void operator ()(unsigned int& min,unsigned int& max) {
min = 1e10;
}
};
template <>
struct MinMaxChooser<unsigned int> {
void operator()(unsigned int &min, unsigned int &max) {
max = 0;
min = (1u<<(sizeof(unsigned int)*8-1));
}};
min = (1u << (sizeof(unsigned int) * 8 - 1));
}
};
template <typename T> struct MinMaxChooser< aiVector3t<T> > {
void operator ()(aiVector3t<T>& min,aiVector3t<T>& max) {
max = aiVector3t<T>(-1e10f,-1e10f,-1e10f);
min = aiVector3t<T>( 1e10f, 1e10f, 1e10f);
}};
template <typename T> struct MinMaxChooser< aiVector2t<T> > {
void operator ()(aiVector2t<T>& min,aiVector2t<T>& max) {
max = aiVector2t<T>(-1e10f,-1e10f);
min = aiVector2t<T>( 1e10f, 1e10f);
}};
template <typename T> struct MinMaxChooser< aiColor4t<T> > {
void operator ()(aiColor4t<T>& min,aiColor4t<T>& max) {
max = aiColor4t<T>(-1e10f,-1e10f,-1e10f,-1e10f);
min = aiColor4t<T>( 1e10f, 1e10f, 1e10f, 1e10f);
}};
template <typename T>
struct MinMaxChooser<aiVector3t<T>> {
void operator()(aiVector3t<T> &min, aiVector3t<T> &max) {
max = aiVector3t<T>(-1e10f, -1e10f, -1e10f);
min = aiVector3t<T>(1e10f, 1e10f, 1e10f);
}
};
template <typename T>
struct MinMaxChooser<aiVector2t<T>> {
void operator()(aiVector2t<T> &min, aiVector2t<T> &max) {
max = aiVector2t<T>(-1e10f, -1e10f);
min = aiVector2t<T>(1e10f, 1e10f);
}
};
template <typename T>
struct MinMaxChooser<aiColor4t<T>> {
void operator()(aiColor4t<T> &min, aiColor4t<T> &max) {
max = aiColor4t<T>(-1e10f, -1e10f, -1e10f, -1e10f);
min = aiColor4t<T>(1e10f, 1e10f, 1e10f, 1e10f);
}
};
template <typename T> struct MinMaxChooser< aiQuaterniont<T> > {
void operator ()(aiQuaterniont<T>& min,aiQuaterniont<T>& max) {
max = aiQuaterniont<T>(-1e10f,-1e10f,-1e10f,-1e10f);
min = aiQuaterniont<T>( 1e10f, 1e10f, 1e10f, 1e10f);
}};
template <typename T>
struct MinMaxChooser<aiQuaterniont<T>> {
void operator()(aiQuaterniont<T> &min, aiQuaterniont<T> &max) {
max = aiQuaterniont<T>(-1e10f, -1e10f, -1e10f, -1e10f);
min = aiQuaterniont<T>(1e10f, 1e10f, 1e10f, 1e10f);
}
};
template <> struct MinMaxChooser<aiVectorKey> {
void operator ()(aiVectorKey& min,aiVectorKey& max) {
MinMaxChooser<double>()(min.mTime,max.mTime);
MinMaxChooser<aiVector3D>()(min.mValue,max.mValue);
}};
template <> struct MinMaxChooser<aiQuatKey> {
void operator ()(aiQuatKey& min,aiQuatKey& max) {
MinMaxChooser<double>()(min.mTime,max.mTime);
MinMaxChooser<aiQuaternion>()(min.mValue,max.mValue);
}};
template <>
struct MinMaxChooser<aiVectorKey> {
void operator()(aiVectorKey &min, aiVectorKey &max) {
MinMaxChooser<double>()(min.mTime, max.mTime);
MinMaxChooser<aiVector3D>()(min.mValue, max.mValue);
}
};
template <>
struct MinMaxChooser<aiQuatKey> {
void operator()(aiQuatKey &min, aiQuatKey &max) {
MinMaxChooser<double>()(min.mTime, max.mTime);
MinMaxChooser<aiQuaternion>()(min.mValue, max.mValue);
}
};
template <> struct MinMaxChooser<aiVertexWeight> {
void operator ()(aiVertexWeight& min,aiVertexWeight& max) {
MinMaxChooser<unsigned int>()(min.mVertexId,max.mVertexId);
MinMaxChooser<float>()(min.mWeight,max.mWeight);
}};
template <>
struct MinMaxChooser<aiVertexWeight> {
void operator()(aiVertexWeight &min, aiVertexWeight &max) {
MinMaxChooser<unsigned int>()(min.mVertexId, max.mVertexId);
MinMaxChooser<ai_real>()(min.mWeight, max.mWeight);
}
};
// -------------------------------------------------------------------------------
/** @brief Find the min/max values of an array of Ts
@ -217,36 +234,31 @@ template <> struct MinMaxChooser<aiVertexWeight> {
* @param[out] max maximum value
*/
template <typename T>
inline void ArrayBounds(const T* in, unsigned int size, T& min, T& max)
{
MinMaxChooser<T> ()(min,max);
for (unsigned int i = 0; i < size;++i) {
min = std::min(in[i],min);
max = std::max(in[i],max);
inline void ArrayBounds(const T *in, unsigned int size, T &min, T &max) {
MinMaxChooser<T>()(min, max);
for (unsigned int i = 0; i < size; ++i) {
min = std::min(in[i], min);
max = std::max(in[i], max);
}
}
// -------------------------------------------------------------------------------
/** Little helper function to calculate the quadratic difference
* of two colours.
* of two colors.
* @param pColor1 First color
* @param pColor2 second color
* @return Quadratic color difference */
inline ai_real GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pColor2)
{
const aiColor4D c (pColor1.r - pColor2.r, pColor1.g - pColor2.g, pColor1.b - pColor2.b, pColor1.a - pColor2.a);
return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a;
inline ai_real GetColorDifference(const aiColor4D &pColor1, const aiColor4D &pColor2) {
const aiColor4D c(pColor1.r - pColor2.r, pColor1.g - pColor2.g, pColor1.b - pColor2.b, pColor1.a - pColor2.a);
return c.r * c.r + c.g * c.g + c.b * c.b + c.a * c.a;
}
// -------------------------------------------------------------------------------
/** @brief Extract single strings from a list of identifiers
* @param in Input string list.
* @param out Receives a list of clean output strings
* @sdee #AI_CONFIG_PP_OG_EXCLUDE_LIST */
void ConvertListToStrings(const std::string& in, std::list<std::string>& out);
void ConvertListToStrings(const std::string &in, std::list<std::string> &out);
// -------------------------------------------------------------------------------
/** @brief Compute the AABB of a mesh after applying a given transform
@ -254,8 +266,7 @@ void ConvertListToStrings(const std::string& in, std::list<std::string>& out);
* @param[out] min Receives minimum transformed vertex
* @param[out] max Receives maximum transformed vertex
* @param m Transformation matrix to be applied */
void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, const aiMatrix4x4& m);
void FindAABBTransformed(const aiMesh *mesh, aiVector3D &min, aiVector3D &max, const aiMatrix4x4 &m);
// -------------------------------------------------------------------------------
/** @brief Helper function to determine the 'real' center of a mesh
@ -265,7 +276,7 @@ void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max,
* @param[out] min Minimum vertex of the mesh
* @param[out] max maximum vertex of the mesh
* @param[out] out Center point */
void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max);
void FindMeshCenter(aiMesh *mesh, aiVector3D &out, aiVector3D &min, aiVector3D &max);
// -------------------------------------------------------------------------------
/** @brief Helper function to determine the 'real' center of a scene
@ -275,112 +286,91 @@ void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D&
* @param[out] min Minimum vertex of the scene
* @param[out] max maximum vertex of the scene
* @param[out] out Center point */
void FindSceneCenter (aiScene* scene, aiVector3D& out, aiVector3D& min, aiVector3D& max);
void FindSceneCenter(aiScene *scene, aiVector3D &out, aiVector3D &min, aiVector3D &max);
// -------------------------------------------------------------------------------
// Helper function to determine the 'real' center of a mesh after applying a given transform
void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,aiVector3D& max, const aiMatrix4x4& m);
void FindMeshCenterTransformed(aiMesh *mesh, aiVector3D &out, aiVector3D &min, aiVector3D &max, const aiMatrix4x4 &m);
// -------------------------------------------------------------------------------
// Helper function to determine the 'real' center of a mesh
void FindMeshCenter (aiMesh* mesh, aiVector3D& out);
void FindMeshCenter(aiMesh *mesh, aiVector3D &out);
// -------------------------------------------------------------------------------
// Helper function to determine the 'real' center of a mesh after applying a given transform
void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,const aiMatrix4x4& m);
void FindMeshCenterTransformed(aiMesh *mesh, aiVector3D &out, const aiMatrix4x4 &m);
// -------------------------------------------------------------------------------
// Compute a good epsilon value for position comparisons on a mesh
ai_real ComputePositionEpsilon(const aiMesh* pMesh);
ai_real ComputePositionEpsilon(const aiMesh *pMesh);
// -------------------------------------------------------------------------------
// Compute a good epsilon value for position comparisons on a array of meshes
ai_real ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num);
ai_real ComputePositionEpsilon(const aiMesh *const *pMeshes, size_t num);
// -------------------------------------------------------------------------------
// Compute an unique value for the vertex format of a mesh
unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh);
unsigned int GetMeshVFormatUnique(const aiMesh *pcMesh);
// defs for ComputeVertexBoneWeightTable()
typedef std::pair <unsigned int,float> PerVertexWeight;
typedef std::vector <PerVertexWeight> VertexWeightTable;
using PerVertexWeight = std::pair<unsigned int, float>;
using VertexWeightTable = std::vector<PerVertexWeight>;
// -------------------------------------------------------------------------------
// Compute a per-vertex bone weight table
VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh);
// -------------------------------------------------------------------------------
// Get a string for a given aiTextureType
const char* TextureTypeToString(aiTextureType in);
VertexWeightTable *ComputeVertexBoneWeightTable(const aiMesh *pMesh);
// -------------------------------------------------------------------------------
// Get a string for a given aiTextureMapping
const char* MappingTypeToString(aiTextureMapping in);
const char *MappingTypeToString(aiTextureMapping in);
// flags for MakeSubmesh()
#define AI_SUBMESH_FLAGS_SANS_BONES 0x1
// -------------------------------------------------------------------------------
// Split a mesh given a list of faces to be contained in the sub mesh
aiMesh* MakeSubmesh(const aiMesh *superMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags);
aiMesh *MakeSubmesh(const aiMesh *superMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags);
// -------------------------------------------------------------------------------
// Utility postprocess step to share the spatial sort tree between
// Utility post-process step to share the spatial sort tree between
// all steps which use it to speedup its computations.
class ComputeSpatialSortProcess : public BaseProcess
{
bool IsActive( unsigned int pFlags) const
{
return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace |
aiProcess_GenNormals | aiProcess_JoinIdenticalVertices));
class ComputeSpatialSortProcess : public BaseProcess {
bool IsActive(unsigned int pFlags) const {
return nullptr != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace |
aiProcess_GenNormals | aiProcess_JoinIdenticalVertices));
}
void Execute( aiScene* pScene)
{
void Execute(aiScene *pScene) {
typedef std::pair<SpatialSort, ai_real> _Type;
ASSIMP_LOG_DEBUG("Generate spatially-sorted vertex cache");
std::vector<_Type>* p = new std::vector<_Type>(pScene->mNumMeshes);
std::vector<_Type> *p = new std::vector<_Type>(pScene->mNumMeshes);
std::vector<_Type>::iterator it = p->begin();
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i, ++it) {
aiMesh* mesh = pScene->mMeshes[i];
_Type& blubb = *it;
blubb.first.Fill(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D));
aiMesh *mesh = pScene->mMeshes[i];
_Type &blubb = *it;
blubb.first.Fill(mesh->mVertices, mesh->mNumVertices, sizeof(aiVector3D));
blubb.second = ComputePositionEpsilon(mesh);
}
shared->AddProperty(AI_SPP_SPATIAL_SORT,p);
shared->AddProperty(AI_SPP_SPATIAL_SORT, p);
}
};
// -------------------------------------------------------------------------------
// ... and the same again to cleanup the whole stuff
class DestroySpatialSortProcess : public BaseProcess
{
bool IsActive( unsigned int pFlags) const
{
return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace |
aiProcess_GenNormals | aiProcess_JoinIdenticalVertices));
class DestroySpatialSortProcess : public BaseProcess {
bool IsActive(unsigned int pFlags) const {
return nullptr != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace |
aiProcess_GenNormals | aiProcess_JoinIdenticalVertices));
}
void Execute( aiScene* /*pScene*/)
{
void Execute(aiScene * /*pScene*/) {
shared->RemoveProperty(AI_SPP_SPATIAL_SORT);
}
};
} // namespace Assimp
} // ! namespace Assimp
#endif // !! AI_PROCESS_HELPER_H_INCLUDED

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/ParsingUtils.h>
#include "ProcessHelper.h"
#include "Material/MaterialSystem.h"
#include <assimp/Exceptional.h>
#include <stdio.h>
using namespace Assimp;
@ -122,7 +123,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
// Keep this material even if no mesh references it
abReferenced[i] = true;
ASSIMP_LOG_DEBUG_F( "Found positive match in exclusion list: \'", name.data, "\'");
ASSIMP_LOG_VERBOSE_DEBUG( "Found positive match in exclusion list: \'", name.data, "\'");
}
}
}
@ -171,6 +172,8 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
}
// 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];
@ -197,7 +200,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
// update all material indices
for (unsigned int p = 0; p < pScene->mNumMeshes;++p) {
aiMesh* mesh = pScene->mMeshes[p];
ai_assert( NULL!=mesh );
ai_assert(nullptr != mesh);
mesh->mMaterialIndex = aiMappingTable[mesh->mMaterialIndex];
}
// delete the old material list
@ -215,7 +218,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
}
else
{
ASSIMP_LOG_INFO_F("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ",
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-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -81,7 +81,7 @@ public:
/** @brief Set list of fixed (inmutable) materials
* @param fixed See #AI_CONFIG_PP_RRM_EXCLUDE_LIST
*/
void SetFixedMaterialsString(const std::string& fixed = "") {
void SetFixedMaterialsString(const std::string& fixed = std::string()) {
mConfigFixedMaterials = fixed;
}

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -44,43 +44,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* any parts of the mesh structure from the imported data.
*/
#include "RemoveVCProcess.h"
#include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
RemoveVCProcess::RemoveVCProcess() :
configDeleteFlags()
, mScene()
{}
configDeleteFlags(), mScene() {}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
RemoveVCProcess::~RemoveVCProcess()
{}
RemoveVCProcess::~RemoveVCProcess() {}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool RemoveVCProcess::IsActive( unsigned int pFlags) const
{
bool RemoveVCProcess::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_RemoveComponent) != 0;
}
// ------------------------------------------------------------------------------------------------
// Small helper function to delete all elements in a T** aray using delete
// Small helper function to delete all elements in a T** array using delete
template <typename T>
inline void ArrayDelete(T**& in, unsigned int& num)
{
inline void ArrayDelete(T **&in, unsigned int &num) {
for (unsigned int i = 0; i < num; ++i)
delete in[i];
delete[] in;
in = NULL;
in = nullptr;
num = 0;
}
@ -108,9 +102,9 @@ bool UpdateNodeGraph(aiNode* node,std::list<aiNode*>& childsOfParent,bool root)
{
childsOfParent.insert(childsOfParent.end(),mine.begin(),mine.end());
// set all children to NULL to make sure they are not deleted when we delete ourself
// 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] = NULL;
node->mChildren[i] = nullptr;
}
b = true;
delete node;
@ -143,86 +137,74 @@ bool UpdateNodeGraph(aiNode* node,std::list<aiNode*>& childsOfParent,bool root)
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void RemoveVCProcess::Execute( aiScene* pScene)
{
void RemoveVCProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("RemoveVCProcess begin");
bool bHas = false; //,bMasked = false;
mScene = pScene;
// handle animations
if ( configDeleteFlags & aiComponent_ANIMATIONS)
{
if (configDeleteFlags & aiComponent_ANIMATIONS) {
bHas = true;
ArrayDelete(pScene->mAnimations,pScene->mNumAnimations);
ArrayDelete(pScene->mAnimations, pScene->mNumAnimations);
}
// handle textures
if ( configDeleteFlags & aiComponent_TEXTURES)
{
if (configDeleteFlags & aiComponent_TEXTURES) {
bHas = true;
ArrayDelete(pScene->mTextures,pScene->mNumTextures);
ArrayDelete(pScene->mTextures, pScene->mNumTextures);
}
// handle materials
if ( configDeleteFlags & aiComponent_MATERIALS && pScene->mNumMaterials)
{
if (configDeleteFlags & aiComponent_MATERIALS && pScene->mNumMaterials) {
bHas = true;
for (unsigned int i = 1;i < pScene->mNumMaterials;++i)
for (unsigned int i = 1; i < pScene->mNumMaterials; ++i)
delete pScene->mMaterials[i];
pScene->mNumMaterials = 1;
aiMaterial* helper = (aiMaterial*) pScene->mMaterials[0];
ai_assert(NULL != helper);
aiMaterial *helper = (aiMaterial *)pScene->mMaterials[0];
ai_assert(nullptr != helper);
helper->Clear();
// gray
aiColor3D clr(0.6f,0.6f,0.6f);
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
aiColor3D clr(0.6f, 0.6f, 0.6f);
helper->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
// add a small ambient color value
clr = aiColor3D(0.05f,0.05f,0.05f);
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
clr = aiColor3D(0.05f, 0.05f, 0.05f);
helper->AddProperty(&clr, 1, AI_MATKEY_COLOR_AMBIENT);
aiString s;
s.Set("Dummy_MaterialsRemoved");
helper->AddProperty(&s,AI_MATKEY_NAME);
helper->AddProperty(&s, AI_MATKEY_NAME);
}
// handle light sources
if ( configDeleteFlags & aiComponent_LIGHTS)
{
bHas = true;
ArrayDelete(pScene->mLights,pScene->mNumLights);
if (configDeleteFlags & aiComponent_LIGHTS) {
bHas = true;
ArrayDelete(pScene->mLights, pScene->mNumLights);
}
// handle camneras
if ( configDeleteFlags & aiComponent_CAMERAS)
{
if (configDeleteFlags & aiComponent_CAMERAS) {
bHas = true;
ArrayDelete(pScene->mCameras,pScene->mNumCameras);
ArrayDelete(pScene->mCameras, pScene->mNumCameras);
}
// handle meshes
if (configDeleteFlags & aiComponent_MESHES)
{
if (configDeleteFlags & aiComponent_MESHES) {
bHas = true;
ArrayDelete(pScene->mMeshes,pScene->mNumMeshes);
}
else
{
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{
if( ProcessMesh( pScene->mMeshes[a]))
ArrayDelete(pScene->mMeshes, pScene->mNumMeshes);
} else {
for (unsigned int a = 0; a < pScene->mNumMeshes; a++) {
if (ProcessMesh(pScene->mMeshes[a]))
bHas = true;
}
}
// now check whether the result is still a full scene
if (!pScene->mNumMeshes || !pScene->mNumMaterials)
{
if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
ASSIMP_LOG_DEBUG("Setting AI_SCENE_FLAGS_INCOMPLETE flag");
@ -240,63 +222,55 @@ void RemoveVCProcess::Execute( aiScene* pScene)
// ------------------------------------------------------------------------------------------------
// Setup configuration properties for the step
void RemoveVCProcess::SetupProperties(const Importer* pImp)
{
configDeleteFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS,0x0);
if (!configDeleteFlags)
{
void RemoveVCProcess::SetupProperties(const Importer *pImp) {
configDeleteFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, 0x0);
if (!configDeleteFlags) {
ASSIMP_LOG_WARN("RemoveVCProcess: AI_CONFIG_PP_RVC_FLAGS is zero.");
}
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh)
{
bool RemoveVCProcess::ProcessMesh(aiMesh *pMesh) {
bool ret = false;
// if all materials have been deleted let the material
// index of the mesh point to the created default material
if ( configDeleteFlags & aiComponent_MATERIALS)
if (configDeleteFlags & aiComponent_MATERIALS)
pMesh->mMaterialIndex = 0;
// handle normals
if (configDeleteFlags & aiComponent_NORMALS && pMesh->mNormals)
{
if (configDeleteFlags & aiComponent_NORMALS && pMesh->mNormals) {
delete[] pMesh->mNormals;
pMesh->mNormals = NULL;
pMesh->mNormals = nullptr;
ret = true;
}
// handle tangents and bitangents
if (configDeleteFlags & aiComponent_TANGENTS_AND_BITANGENTS && pMesh->mTangents)
{
if (configDeleteFlags & aiComponent_TANGENTS_AND_BITANGENTS && pMesh->mTangents) {
delete[] pMesh->mTangents;
pMesh->mTangents = NULL;
pMesh->mTangents = nullptr;
delete[] pMesh->mBitangents;
pMesh->mBitangents = NULL;
pMesh->mBitangents = nullptr;
ret = true;
}
// handle texture coordinates
bool b = (0 != (configDeleteFlags & aiComponent_TEXCOORDS));
for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++real)
{
if (!pMesh->mTextureCoords[i])break;
if (configDeleteFlags & aiComponent_TEXCOORDSn(real) || b)
{
delete [] pMesh->mTextureCoords[i];
pMesh->mTextureCoords[i] = NULL;
for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++real) {
if (!pMesh->mTextureCoords[i]) break;
if (configDeleteFlags & aiComponent_TEXCOORDSn(real) || b) {
delete[] pMesh->mTextureCoords[i];
pMesh->mTextureCoords[i] = nullptr;
ret = true;
if (!b)
{
if (!b) {
// collapse the rest of the array
for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a)
pMesh->mTextureCoords[a-1] = pMesh->mTextureCoords[a];
for (unsigned int a = i + 1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a)
pMesh->mTextureCoords[a - 1] = pMesh->mTextureCoords[a];
pMesh->mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS-1] = NULL;
pMesh->mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS - 1] = nullptr;
continue;
}
}
@ -305,22 +279,19 @@ bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh)
// handle vertex colors
b = (0 != (configDeleteFlags & aiComponent_COLORS));
for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_COLOR_SETS; ++real)
{
if (!pMesh->mColors[i])break;
if (configDeleteFlags & aiComponent_COLORSn(i) || b)
{
delete [] pMesh->mColors[i];
pMesh->mColors[i] = NULL;
for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_COLOR_SETS; ++real) {
if (!pMesh->mColors[i]) break;
if (configDeleteFlags & aiComponent_COLORSn(i) || b) {
delete[] pMesh->mColors[i];
pMesh->mColors[i] = nullptr;
ret = true;
if (!b)
{
if (!b) {
// collapse the rest of the array
for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a)
pMesh->mColors[a-1] = pMesh->mColors[a];
for (unsigned int a = i + 1; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a)
pMesh->mColors[a - 1] = pMesh->mColors[a];
pMesh->mColors[AI_MAX_NUMBER_OF_COLOR_SETS-1] = NULL;
pMesh->mColors[AI_MAX_NUMBER_OF_COLOR_SETS - 1] = nullptr;
continue;
}
}
@ -328,9 +299,8 @@ bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh)
}
// handle bones
if (configDeleteFlags & aiComponent_BONEWEIGHTS && pMesh->mBones)
{
ArrayDelete(pMesh->mBones,pMesh->mNumBones);
if (configDeleteFlags & aiComponent_BONEWEIGHTS && pMesh->mBones) {
ArrayDelete(pMesh->mBones, pMesh->mNumBones);
ret = true;
}
return ret;

View file

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

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -75,7 +75,7 @@ void ScaleProcess::SetupProperties( const Importer* pImp ) {
// File scaling * Application Scaling
float importerScale = pImp->GetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, 1.0f );
// apply scale to the scale
// apply scale to the scale
// helps prevent bugs with backward compatibility for anyone using normal scaling.
mScale *= importerScale;
}
@ -84,7 +84,7 @@ 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 );
@ -96,7 +96,7 @@ void ScaleProcess::Execute( aiScene* pScene ) {
if ( nullptr == pScene->mRootNode ) {
return;
}
// Process animations and update position transform to new unit system
for( unsigned int animationID = 0; animationID < pScene->mNumAnimations; animationID++ )
{
@ -105,7 +105,7 @@ void ScaleProcess::Execute( aiScene* pScene ) {
for( unsigned int animationChannel = 0; animationChannel < animation->mNumChannels; animationChannel++)
{
aiNodeAnim* anim = animation->mChannels[animationChannel];
for( unsigned int posKey = 0; posKey < anim->mNumPositionKeys; posKey++)
{
aiVectorKey& vectorKey = anim->mPositionKeys[posKey];
@ -116,9 +116,9 @@ void ScaleProcess::Execute( aiScene* pScene ) {
for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++)
{
aiMesh *mesh = pScene->mMeshes[meshID];
// Reconstruct mesh vertexes to the new unit system
aiMesh *mesh = pScene->mMeshes[meshID];
// Reconstruct mesh vertices to the new unit system
for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++)
{
aiVector3D& vertex = mesh->mVertices[vertexID];
@ -129,9 +129,9 @@ void ScaleProcess::Execute( aiScene* pScene ) {
// bone placement / scaling
for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++)
{
// Reconstruct matrix by transform rather than by scale
// Reconstruct matrix by transform rather than by scale
// This prevent scale values being changed which can
// be meaningful in some cases
// be meaningful in some cases
// like when you want the modeller to see 1:1 compatibility.
aiBone* bone = mesh->mBones[boneID];
@ -139,10 +139,10 @@ void ScaleProcess::Execute( aiScene* pScene ) {
aiQuaternion rotation;
bone->mOffsetMatrix.Decompose( scale, rotation, pos);
aiMatrix4x4 translation;
aiMatrix4x4::Translation( pos * mScale, translation );
aiMatrix4x4 scaling;
aiMatrix4x4::Scaling( aiVector3D(scale), scaling );
@ -157,7 +157,7 @@ void ScaleProcess::Execute( aiScene* pScene ) {
for( unsigned int animMeshID = 0; animMeshID < mesh->mNumAnimMeshes; animMeshID++)
{
aiAnimMesh * animMesh = mesh->mAnimMeshes[animMeshID];
for( unsigned int vertexID = 0; vertexID < animMesh->mNumVertices; vertexID++)
{
aiVector3D& vertex = animMesh->mVertices[vertexID];
@ -169,31 +169,31 @@ void ScaleProcess::Execute( aiScene* pScene ) {
traverseNodes( pScene->mRootNode );
}
void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) {
void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) {
applyScaling( node );
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 );
traverseNodes( node->mChildren[i], nested_node_id+1 );
}
}
void ScaleProcess::applyScaling( aiNode *currentNode ) {
if ( nullptr != currentNode ) {
// Reconstruct matrix by transform rather than by scale
// Reconstruct matrix by transform rather than by scale
// This prevent scale values being changed which can
// be meaningful in some cases
// like when you want the modeller to
// be meaningful in some cases
// like when you want the modeller to
// see 1:1 compatibility.
aiVector3D pos, scale;
aiQuaternion rotation;
currentNode->mTransformation.Decompose( scale, rotation, pos);
aiMatrix4x4 translation;
aiMatrix4x4::Translation( pos * mScale, translation );
aiMatrix4x4 scaling;
// note: we do not use mScale here, this is on purpose.

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -55,8 +55,8 @@ namespace Assimp {
// ---------------------------------------------------------------------------
/** ScaleProcess: Class to rescale the whole model.
* Now rescales animations, bones, and blend shapes properly.
* Please note this will not write to 'scale' transform it will rewrite mesh
* and matrixes so that your scale values
* Please note this will not write to 'scale' transform it will rewrite mesh
* and matrixes so that your scale values
* from your model package are preserved, so this is completely intentional
* bugs should be reported as soon as they are found.
*/
@ -94,4 +94,4 @@ private:
} // Namespace Assimp
#endif // SCALE_PROCESS_H_
#endif // SCALE_PROCESS_H_

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -45,112 +45,99 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* SortByPTypeProcess post-process steps.
*/
// internal headers
#include "ProcessHelper.h"
#include "SortByPTypeProcess.h"
#include "ProcessHelper.h"
#include <assimp/Exceptional.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
SortByPTypeProcess::SortByPTypeProcess()
: mConfigRemoveMeshes( 0 ) {
SortByPTypeProcess::SortByPTypeProcess() :
mConfigRemoveMeshes(0) {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
SortByPTypeProcess::~SortByPTypeProcess()
{
SortByPTypeProcess::~SortByPTypeProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool SortByPTypeProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_SortByPType) != 0;
bool SortByPTypeProcess::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_SortByPType) != 0;
}
// ------------------------------------------------------------------------------------------------
void SortByPTypeProcess::SetupProperties(const Importer* pImp)
{
mConfigRemoveMeshes = pImp->GetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE,0);
void SortByPTypeProcess::SetupProperties(const Importer *pImp) {
mConfigRemoveMeshes = pImp->GetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, 0);
}
// ------------------------------------------------------------------------------------------------
// Update changed meshes in all nodes
void UpdateNodes(const std::vector<unsigned int>& replaceMeshIndex, aiNode* node)
{
if (node->mNumMeshes)
{
void UpdateNodes(const std::vector<unsigned int> &replaceMeshIndex, aiNode *node) {
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;
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)
{
if (!newSize) {
delete[] node->mMeshes;
node->mNumMeshes = 0;
node->mMeshes = NULL;
}
else
{
node->mMeshes = nullptr;
} else {
// Try to reuse the old array if possible
unsigned int* newMeshes = (newSize > node->mNumMeshes
? new unsigned int[newSize] : node->mMeshes);
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];
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)
delete[] node->mMeshes;
node->mMeshes = newMeshes-(node->mNumMeshes = newSize);
node->mMeshes = newMeshes - (node->mNumMeshes = newSize);
}
}
// call all subnodes recursively
for (unsigned int m = 0; m < node->mNumChildren; ++m)
UpdateNodes(replaceMeshIndex,node->mChildren[m]);
UpdateNodes(replaceMeshIndex, node->mChildren[m]);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void SortByPTypeProcess::Execute( aiScene* pScene) {
if ( 0 == pScene->mNumMeshes) {
void SortByPTypeProcess::Execute(aiScene *pScene) {
if (0 == pScene->mNumMeshes) {
ASSIMP_LOG_DEBUG("SortByPTypeProcess skipped, there are no meshes");
return;
}
ASSIMP_LOG_DEBUG("SortByPTypeProcess begin");
unsigned int aiNumMeshesPerPType[4] = {0,0,0,0};
unsigned int aiNumMeshesPerPType[4] = { 0, 0, 0, 0 };
std::vector<aiMesh*> outMeshes;
outMeshes.reserve(pScene->mNumMeshes<<1u);
std::vector<aiMesh *> outMeshes;
outMeshes.reserve(static_cast<size_t>(pScene->mNumMeshes) << 1u);
bool bAnyChanges = false;
std::vector<unsigned int> replaceMeshIndex(pScene->mNumMeshes*4,UINT_MAX);
std::vector<unsigned int> replaceMeshIndex(pScene->mNumMeshes * 4, UINT_MAX);
std::vector<unsigned int>::iterator meshIdx = replaceMeshIndex.begin();
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
aiMesh* const mesh = pScene->mMeshes[i];
ai_assert(0 != mesh->mPrimitiveTypes);
aiMesh *const mesh = pScene->mMeshes[i];
if (mesh->mPrimitiveTypes == 0) {
throw DeadlyImportError("Mesh with invalid primitive type: ", mesh->mName.C_Str());
}
// if there's just one primitive type in the mesh there's nothing to do for us
unsigned int num = 0;
@ -173,11 +160,11 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
if (1 == num) {
if (!(mConfigRemoveMeshes & mesh->mPrimitiveTypes)) {
*meshIdx = static_cast<unsigned int>( outMeshes.size() );
*meshIdx = static_cast<unsigned int>(outMeshes.size());
outMeshes.push_back(mesh);
} else {
delete mesh;
pScene->mMeshes[ i ] = nullptr;
pScene->mMeshes[i] = nullptr;
bAnyChanges = true;
}
@ -188,32 +175,29 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
// reuse our current mesh arrays for the submesh
// with the largest number of primitives
unsigned int aiNumPerPType[4] = {0,0,0,0};
aiFace* pFirstFace = mesh->mFaces;
aiFace* const pLastFace = pFirstFace + mesh->mNumFaces;
unsigned int aiNumPerPType[4] = { 0, 0, 0, 0 };
aiFace *pFirstFace = mesh->mFaces;
aiFace *const pLastFace = pFirstFace + mesh->mNumFaces;
unsigned int numPolyVerts = 0;
for (;pFirstFace != pLastFace; ++pFirstFace) {
for (; pFirstFace != pLastFace; ++pFirstFace) {
if (pFirstFace->mNumIndices <= 3)
++aiNumPerPType[pFirstFace->mNumIndices-1];
else
{
++aiNumPerPType[pFirstFace->mNumIndices - 1];
else {
++aiNumPerPType[3];
numPolyVerts += pFirstFace-> mNumIndices;
numPolyVerts += pFirstFace->mNumIndices;
}
}
VertexWeightTable* avw = ComputeVertexBoneWeightTable(mesh);
for (unsigned int real = 0; real < 4; ++real,++meshIdx)
{
if ( !aiNumPerPType[real] || mConfigRemoveMeshes & (1u << real))
{
VertexWeightTable *avw = ComputeVertexBoneWeightTable(mesh);
for (unsigned int real = 0; real < 4; ++real, ++meshIdx) {
if (!aiNumPerPType[real] || mConfigRemoveMeshes & (1u << real)) {
continue;
}
*meshIdx = (unsigned int) outMeshes.size();
*meshIdx = (unsigned int)outMeshes.size();
outMeshes.push_back(new aiMesh());
aiMesh* out = outMeshes.back();
aiMesh *out = outMeshes.back();
// the name carries the adjacency information between the meshes
out->mName = mesh->mName;
@ -224,13 +208,13 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
// allocate output storage
out->mNumFaces = aiNumPerPType[real];
aiFace* outFaces = out->mFaces = new aiFace[out->mNumFaces];
aiFace *outFaces = out->mFaces = new aiFace[out->mNumFaces];
out->mNumVertices = (3 == real ? numPolyVerts : out->mNumFaces * (real+1));
out->mNumVertices = (3 == real ? numPolyVerts : out->mNumFaces * (real + 1));
aiVector3D *vert(nullptr), *nor(nullptr), *tan(nullptr), *bit(nullptr);
aiVector3D *uv [AI_MAX_NUMBER_OF_TEXTURECOORDS];
aiColor4D *cols [AI_MAX_NUMBER_OF_COLOR_SETS];
aiVector3D *uv[AI_MAX_NUMBER_OF_TEXTURECOORDS];
aiColor4D *cols[AI_MAX_NUMBER_OF_COLOR_SETS];
if (mesh->mVertices) {
vert = out->mVertices = new aiVector3D[out->mNumVertices];
@ -241,11 +225,11 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
}
if (mesh->mTangents) {
tan = out->mTangents = new aiVector3D[out->mNumVertices];
tan = out->mTangents = new aiVector3D[out->mNumVertices];
bit = out->mBitangents = new aiVector3D[out->mNumVertices];
}
for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_TEXTURECOORDS;++j) {
for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++j) {
uv[j] = nullptr;
if (mesh->mTextureCoords[j]) {
uv[j] = out->mTextureCoords[j] = new aiVector3D[out->mNumVertices];
@ -254,73 +238,127 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
out->mNumUVComponents[j] = mesh->mNumUVComponents[j];
}
for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_COLOR_SETS;++j) {
for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_COLOR_SETS; ++j) {
cols[j] = nullptr;
if (mesh->mColors[j]) {
cols[j] = out->mColors[j] = new aiColor4D[out->mNumVertices];
}
}
typedef std::vector< aiVertexWeight > TempBoneInfo;
std::vector< TempBoneInfo > tempBones(mesh->mNumBones);
if (mesh->mNumAnimMeshes > 0 && mesh->mAnimMeshes) {
out->mNumAnimMeshes = mesh->mNumAnimMeshes;
out->mAnimMeshes = new aiAnimMesh *[out->mNumAnimMeshes];
}
for (unsigned int j = 0; j < mesh->mNumAnimMeshes; ++j) {
aiAnimMesh *animMesh = mesh->mAnimMeshes[j];
aiAnimMesh *outAnimMesh = out->mAnimMeshes[j] = new aiAnimMesh;
outAnimMesh->mNumVertices = out->mNumVertices;
if (animMesh->mVertices)
outAnimMesh->mVertices = new aiVector3D[out->mNumVertices];
else
outAnimMesh->mVertices = nullptr;
if (animMesh->mNormals)
outAnimMesh->mNormals = new aiVector3D[out->mNumVertices];
else
outAnimMesh->mNormals = nullptr;
if (animMesh->mTangents)
outAnimMesh->mTangents = new aiVector3D[out->mNumVertices];
else
outAnimMesh->mTangents = nullptr;
if (animMesh->mBitangents)
outAnimMesh->mBitangents = new aiVector3D[out->mNumVertices];
else
outAnimMesh->mBitangents = nullptr;
for (int jj = 0; jj < AI_MAX_NUMBER_OF_COLOR_SETS; ++jj) {
if (animMesh->mColors[jj])
outAnimMesh->mColors[jj] = new aiColor4D[out->mNumVertices];
else
outAnimMesh->mColors[jj] = nullptr;
}
for (int jj = 0; jj < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++jj) {
if (animMesh->mTextureCoords[jj])
outAnimMesh->mTextureCoords[jj] = new aiVector3D[out->mNumVertices];
else
outAnimMesh->mTextureCoords[jj] = nullptr;
}
}
typedef std::vector<aiVertexWeight> TempBoneInfo;
std::vector<TempBoneInfo> tempBones(mesh->mNumBones);
// try to guess how much storage we'll need
for (unsigned int q = 0; q < mesh->mNumBones;++q)
{
tempBones[q].reserve(mesh->mBones[q]->mNumWeights / (num-1));
for (unsigned int q = 0; q < mesh->mNumBones; ++q) {
tempBones[q].reserve(mesh->mBones[q]->mNumWeights / (num - 1));
}
unsigned int outIdx = 0;
for (unsigned int m = 0; m < mesh->mNumFaces; ++m)
{
aiFace& in = mesh->mFaces[m];
if ((real == 3 && in.mNumIndices <= 3) || (real != 3 && in.mNumIndices != real+1))
{
unsigned int amIdx = 0; // AnimMesh index
for (unsigned int m = 0; m < mesh->mNumFaces; ++m) {
aiFace &in = mesh->mFaces[m];
if ((real == 3 && in.mNumIndices <= 3) || (real != 3 && in.mNumIndices != real + 1)) {
continue;
}
outFaces->mNumIndices = in.mNumIndices;
outFaces->mIndices = in.mIndices;
outFaces->mIndices = in.mIndices;
for (unsigned int q = 0; q < in.mNumIndices; ++q)
{
for (unsigned int q = 0; q < in.mNumIndices; ++q) {
unsigned int idx = in.mIndices[q];
// process all bones of this index
if (avw)
{
VertexWeightTable& tbl = avw[idx];
if (avw) {
VertexWeightTable &tbl = avw[idx];
for (VertexWeightTable::const_iterator it = tbl.begin(), end = tbl.end();
it != end; ++it)
{
tempBones[ (*it).first ].push_back( aiVertexWeight(outIdx, (*it).second) );
it != end; ++it) {
tempBones[(*it).first].push_back(aiVertexWeight(outIdx, (*it).second));
}
}
if (vert)
{
if (vert) {
*vert++ = mesh->mVertices[idx];
//mesh->mVertices[idx].x = get_qnan();
}
if (nor )*nor++ = mesh->mNormals[idx];
if (tan )
{
*tan++ = mesh->mTangents[idx];
*bit++ = mesh->mBitangents[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;
for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp) {
if (!uv[pp]) break;
*uv[pp]++ = mesh->mTextureCoords[pp][idx];
}
for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp)
{
if (!cols[pp])break;
for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp) {
if (!cols[pp]) break;
*cols[pp]++ = mesh->mColors[pp][idx];
}
unsigned int pp = 0;
for (; pp < mesh->mNumAnimMeshes; ++pp) {
aiAnimMesh *animMesh = mesh->mAnimMeshes[pp];
aiAnimMesh *outAnimMesh = out->mAnimMeshes[pp];
if (animMesh->mVertices)
outAnimMesh->mVertices[amIdx] = animMesh->mVertices[idx];
if (animMesh->mNormals)
outAnimMesh->mNormals[amIdx] = animMesh->mNormals[idx];
if (animMesh->mTangents)
outAnimMesh->mTangents[amIdx] = animMesh->mTangents[idx];
if (animMesh->mBitangents)
outAnimMesh->mBitangents[amIdx] = animMesh->mBitangents[idx];
for (int jj = 0; jj < AI_MAX_NUMBER_OF_COLOR_SETS; ++jj) {
if (animMesh->mColors[jj])
outAnimMesh->mColors[jj][amIdx] = animMesh->mColors[jj][idx];
}
for (int jj = 0; jj < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++jj) {
if (animMesh->mTextureCoords[jj])
outAnimMesh->mTextureCoords[jj][amIdx] = animMesh->mTextureCoords[jj][idx];
}
}
if (pp == mesh->mNumAnimMeshes)
amIdx++;
in.mIndices[q] = outIdx++;
}
@ -330,19 +368,22 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
ai_assert(outFaces == out->mFaces + out->mNumFaces);
// now generate output bones
for (unsigned int q = 0; q < mesh->mNumBones;++q)
if (!tempBones[q].empty())++out->mNumBones;
for (unsigned int q = 0; q < mesh->mNumBones; ++q) {
if (!tempBones[q].empty()) {
++out->mNumBones;
}
}
if (out->mNumBones)
{
out->mBones = new aiBone*[out->mNumBones];
for (unsigned int q = 0, real = 0; q < mesh->mNumBones;++q)
{
TempBoneInfo& in = tempBones[q];
if (in.empty())continue;
if (out->mNumBones) {
out->mBones = new aiBone *[out->mNumBones];
for (unsigned int q = 0, boneIdx = 0; q < mesh->mNumBones; ++q) {
TempBoneInfo &in = tempBones[q];
if (in.empty()) {
continue;
}
aiBone* srcBone = mesh->mBones[q];
aiBone* bone = out->mBones[real] = new aiBone();
aiBone *srcBone = mesh->mBones[q];
aiBone *bone = out->mBones[boneIdx] = new aiBone();
bone->mName = srcBone->mName;
bone->mOffsetMatrix = srcBone->mOffsetMatrix;
@ -350,9 +391,9 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
bone->mNumWeights = (unsigned int)in.size();
bone->mWeights = new aiVertexWeight[bone->mNumWeights];
::memcpy(bone->mWeights,&in[0],bone->mNumWeights*sizeof(aiVertexWeight));
::memcpy(bone->mWeights, &in[0], bone->mNumWeights * sizeof(aiVertexWeight));
++real;
++boneIdx;
}
}
}
@ -364,40 +405,35 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
delete mesh;
// avoid invalid pointer
pScene->mMeshes[i] = NULL;
pScene->mMeshes[i] = nullptr;
}
if (outMeshes.empty())
{
if (outMeshes.empty()) {
// This should not occur
throw DeadlyImportError("No meshes remaining");
}
// If we added at least one mesh process all nodes in the node
// graph and update their respective mesh indices.
if (bAnyChanges)
{
UpdateNodes(replaceMeshIndex,pScene->mRootNode);
if (bAnyChanges) {
UpdateNodes(replaceMeshIndex, pScene->mRootNode);
}
if (outMeshes.size() != pScene->mNumMeshes)
{
if (outMeshes.size() != pScene->mNumMeshes) {
delete[] pScene->mMeshes;
pScene->mNumMeshes = (unsigned int)outMeshes.size();
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
}
::memcpy(pScene->mMeshes,&outMeshes[0],pScene->mNumMeshes*sizeof(void*));
::memcpy(pScene->mMeshes, &outMeshes[0], pScene->mNumMeshes * sizeof(void *));
if (!DefaultLogger::isNullLogger())
{
if (!DefaultLogger::isNullLogger()) {
char buffer[1024];
::ai_snprintf(buffer,1024,"Points: %u%s, Lines: %u%s, Triangles: %u%s, Polygons: %u%s (Meshes, X = removed)",
aiNumMeshesPerPType[0], ((mConfigRemoveMeshes & aiPrimitiveType_POINT) ? "X" : ""),
aiNumMeshesPerPType[1], ((mConfigRemoveMeshes & aiPrimitiveType_LINE) ? "X" : ""),
aiNumMeshesPerPType[2], ((mConfigRemoveMeshes & aiPrimitiveType_TRIANGLE) ? "X" : ""),
aiNumMeshesPerPType[3], ((mConfigRemoveMeshes & aiPrimitiveType_POLYGON) ? "X" : ""));
::ai_snprintf(buffer, 1024, "Points: %u%s, Lines: %u%s, Triangles: %u%s, Polygons: %u%s (Meshes, X = removed)",
aiNumMeshesPerPType[0], ((mConfigRemoveMeshes & aiPrimitiveType_POINT) ? "X" : ""),
aiNumMeshesPerPType[1], ((mConfigRemoveMeshes & aiPrimitiveType_LINE) ? "X" : ""),
aiNumMeshesPerPType[2], ((mConfigRemoveMeshes & aiPrimitiveType_TRIANGLE) ? "X" : ""),
aiNumMeshesPerPType[3], ((mConfigRemoveMeshes & aiPrimitiveType_POLYGON) ? "X" : ""));
ASSIMP_LOG_INFO(buffer);
ASSIMP_LOG_DEBUG("SortByPTypeProcess finished");
}
}

View file

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

View file

@ -0,0 +1,480 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* 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
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/// @file SplitByBoneCountProcess.cpp
/// Implementation of the SplitByBoneCount postprocessing step
// internal headers of the post-processing framework
#include "SplitByBoneCountProcess.h"
#include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp>
#include <limits>
#include <assimp/TinyFormatter.h>
#include <assimp/Exceptional.h>
#include <set>
using namespace Assimp;
using namespace Assimp::Formatter;
// ------------------------------------------------------------------------------------------------
// Constructor
SplitByBoneCountProcess::SplitByBoneCountProcess()
{
// set default, might be overridden by importer config
mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES;
}
// ------------------------------------------------------------------------------------------------
// Destructor
SplitByBoneCountProcess::~SplitByBoneCountProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag.
bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const
{
return !!(pFlags & aiProcess_SplitByBoneCount);
}
// ------------------------------------------------------------------------------------------------
// Updates internal properties
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)
{
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 )
{
isNecessary = true;
break;
}
if( !isNecessary )
{
ASSIMP_LOG_DEBUG("SplitByBoneCountProcess early-out: no meshes with more than ", mMaxBoneCount, " bones." );
return;
}
// we need to do something. Let's go.
mSubMeshIndices.clear();
mSubMeshIndices.resize( pScene->mNumMeshes);
// build a new array of meshes for the scene
std::vector<aiMesh*> meshes;
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() )
{
// store new meshes and indices of the new meshes
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
{
// 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);
}
}
// rebuild the scene's mesh array
pScene->mNumMeshes = static_cast<unsigned int>(meshes.size());
delete [] pScene->mMeshes;
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
std::copy( meshes.begin(), meshes.end(), pScene->mMeshes);
// recurse through all nodes and translate the node's mesh indices to fit the new mesh array
UpdateNode( pScene->mRootNode);
ASSIMP_LOG_DEBUG( "SplitByBoneCountProcess end: split ", mSubMeshIndices.size(), " meshes into ", meshes.size(), " submeshes." );
}
// ------------------------------------------------------------------------------------------------
// Splits the given mesh by bone count.
void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const
{
// skip if not necessary
if( pMesh->mNumBones <= mMaxBoneCount )
{
return;
}
// necessary optimisation: build a list of all affecting bones for each vertex
// 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)
{
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].push_back( BoneWeight( 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 )
{
// 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;
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)
{
// skip if the face is already stored in a submesh
if( isFaceHandled[a] )
{
continue;
}
// a small local set of new bones for the current face. State of all used bones for that face
// can only be updated AFTER the face is completely analysed. Thanks to imre for the fix.
std::set<unsigned int> newBonesAtCurrentFace;
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);
}
}
}
// 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 )
{
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++;
}
}
// store the face index and the vertex count
subMeshFaces.push_back( a);
numSubMeshVertices += face.mNumIndices;
// remember that this face is handled
isFaceHandled[a] = true;
numFacesHandled++;
}
// create a new mesh to hold this subset of the source mesh
aiMesh* newMesh = new aiMesh;
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);
// 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() )
{
newMesh->mNormals = new aiVector3D[newMesh->mNumVertices];
}
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) )
{
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) )
{
newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices];
}
}
// 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 )
{
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 )
{
unsigned int srcIndex = srcFace.mIndices[b];
dstFace.mIndices[b] = nvi;
previousVertexIndices[nvi] = srcIndex;
newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
if( pMesh->HasNormals() )
{
newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
}
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) )
{
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) )
{
newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
}
}
nvi++;
}
}
ai_assert( nvi == numSubMeshVertices );
// Create the bones for the new submesh: first create the bone array
newMesh->mNumBones = 0;
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] )
{
continue;
}
// create the new bone
const aiBone* srcBone = pMesh->mBones[a];
aiBone* dstBone = new aiBone;
mappedBoneIndex[a] = newMesh->mNumBones;
newMesh->mBones[newMesh->mNumBones++] = dstBone;
dstBone->mName = srcBone->mName;
dstBone->mOffsetMatrix = srcBone->mOffsetMatrix;
dstBone->mNumWeights = 0;
}
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 )
{
unsigned int oldIndex = previousVertexIndices[a];
const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[oldIndex];
for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b )
{
unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
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 )
{
aiBone* bone = newMesh->mBones[a];
ai_assert( bone->mNumWeights > 0 );
bone->mWeights = new aiVertexWeight[bone->mNumWeights];
bone->mNumWeights = 0; // for counting up in the next step
}
// 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)
{
// 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)
{
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;
newMesh->mBones[newBoneIndex]->mNumWeights++;
dstWeight->mVertexId = a;
dstWeight->mWeight = bonesOnThisVertex[b].second;
}
}
// ... and copy all the morph targets for all the vertices which made it into the new submesh
if (pMesh->mNumAnimMeshes > 0) {
newMesh->mNumAnimMeshes = pMesh->mNumAnimMeshes;
newMesh->mAnimMeshes = new aiAnimMesh*[newMesh->mNumAnimMeshes];
for (unsigned int morphIdx = 0; morphIdx < newMesh->mNumAnimMeshes; ++morphIdx) {
aiAnimMesh* origTarget = pMesh->mAnimMeshes[morphIdx];
aiAnimMesh* newTarget = new aiAnimMesh;
newTarget->mName = origTarget->mName;
newTarget->mWeight = origTarget->mWeight;
newTarget->mNumVertices = numSubMeshVertices;
newTarget->mVertices = new aiVector3D[numSubMeshVertices];
newMesh->mAnimMeshes[morphIdx] = newTarget;
if (origTarget->HasNormals()) {
newTarget->mNormals = new aiVector3D[numSubMeshVertices];
}
if (origTarget->HasTangentsAndBitangents()) {
newTarget->mTangents = new aiVector3D[numSubMeshVertices];
newTarget->mBitangents = new aiVector3D[numSubMeshVertices];
}
for( unsigned int vi = 0; vi < numSubMeshVertices; ++vi) {
// find the source vertex for it in the source mesh
unsigned int previousIndex = previousVertexIndices[vi];
newTarget->mVertices[vi] = origTarget->mVertices[previousIndex];
if (newTarget->HasNormals()) {
newTarget->mNormals[vi] = origTarget->mNormals[previousIndex];
}
if (newTarget->HasTangentsAndBitangents()) {
newTarget->mTangents[vi] = origTarget->mTangents[previousIndex];
newTarget->mBitangents[vi] = origTarget->mBitangents[previousIndex];
}
}
}
}
// I have the strange feeling that this will break apart at some point in time...
}
}
// ------------------------------------------------------------------------------------------------
// Recursively updates the node's mesh list to account for the changed mesh list
void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const
{
// rebuild the node's mesh index list
if( pNode->mNumMeshes > 0 )
{
std::vector<unsigned int> newMeshList;
for( unsigned int a = 0; a < pNode->mNumMeshes; ++a)
{
unsigned int srcIndex = pNode->mMeshes[a];
const std::vector<unsigned int>& replaceMeshes = mSubMeshIndices[srcIndex];
newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end());
}
delete [] pNode->mMeshes;
pNode->mNumMeshes = static_cast<unsigned int>(newMeshList.size());
pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes);
}
// do that also recursively for all children
for( unsigned int a = 0; a < pNode->mNumChildren; ++a )
{
UpdateNode( pNode->mChildren[a]);
}
}

View file

@ -0,0 +1,111 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* 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
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/// @file SplitByBoneCountProcess.h
/// Defines a post processing step to split meshes with many bones into submeshes
#ifndef AI_SPLITBYBONECOUNTPROCESS_H_INC
#define AI_SPLITBYBONECOUNTPROCESS_H_INC
#include <vector>
#include "Common/BaseProcess.h"
#include <assimp/mesh.h>
#include <assimp/scene.h>
namespace Assimp
{
/** Postprocessing filter to split meshes with many bones into submeshes
* so that each submesh has a certain max bone count.
*
* Applied BEFORE the JoinVertices-Step occurs.
* Returns NON-UNIQUE vertices, splits by bone count.
*/
class SplitByBoneCountProcess : public BaseProcess
{
public:
SplitByBoneCountProcess();
~SplitByBoneCountProcess();
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;
/** 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);
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);
/// 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.
/// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary.
void SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const;
/// Recursively updates the node's mesh list to account for the changed mesh list
void UpdateNode( aiNode* pNode) const;
public:
/// 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;
};
} // end of namespace Assimp
#endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -40,7 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/**
/**
* @file Implementation of the SplitLargeMeshes postprocessing step
*/
@ -129,7 +129,7 @@ void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode,
pcNode->mMeshes[b] = aiEntries[b];
}
// recusively update all other nodes
// recursively update all other nodes
for (unsigned int i = 0; i < pcNode->mNumChildren;++i) {
UpdateNode ( pcNode->mChildren[i], avList );
}
@ -353,7 +353,7 @@ void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene) {
std::vector<std::pair<aiMesh*, unsigned int> > avList;
//Check for point cloud first,
//Check for point cloud first,
//Do not process point cloud, splitMesh works only with faces data
for (unsigned int a = 0; a < pScene->mNumMeshes; a++) {
if ( pScene->mMeshes[a]->mPrimitiveTypes == aiPrimitiveType_POINT ) {
@ -575,8 +575,9 @@ void SplitLargeMeshesProcess_Vertex::SplitMesh(
for (unsigned int k = 0; k < pMesh->mNumBones;++k) {
// check whether the bone is existing
BoneWeightList* pcWeightList;
if ((pcWeightList = (BoneWeightList*)pcMesh->mBones[k])) {
aiBone* pcOldBone = pMesh->mBones[k];
pcWeightList = (BoneWeightList *)pcMesh->mBones[k];
if (nullptr != pcWeightList) {
aiBone *pcOldBone = pMesh->mBones[k];
aiBone* pcOut( nullptr );
*ppCurrent++ = pcOut = new aiBone();
pcOut->mName = aiString(pcOldBone->mName);

View file

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

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -92,9 +92,8 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
* are applied is - as always - scaling, rotation, translation.
*/
char szTemp[512];
int rounded = 0;
int rounded;
char szTemp[512];
/* Optimize the rotation angle. That's slightly difficult as
* we have an inprecise floating-point number (when comparing
@ -105,10 +104,11 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
if (info.mRotation)
{
float out = info.mRotation;
if ((rounded = static_cast<int>((info.mRotation / static_cast<float>(AI_MATH_TWO_PI)))))
rounded = static_cast<int>((info.mRotation / static_cast<float>(AI_MATH_TWO_PI)));
if (rounded)
{
out -= rounded * static_cast<float>(AI_MATH_PI);
ASSIMP_LOG_INFO_F("Texture coordinate rotation ", info.mRotation, " can be simplified to ", out);
ASSIMP_LOG_INFO("Texture coordinate rotation ", info.mRotation, " can be simplified to ", out);
}
// Next step - convert negative rotation angles to positives
@ -125,7 +125,8 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
* type (e.g. if mirroring is active there IS a difference between
* offset 2 and 3)
*/
if ((rounded = (int)info.mTranslation.x)) {
rounded = (int)info.mTranslation.x;
if (rounded) {
float out = 0.0f;
szTemp[0] = 0;
if (aiTextureMapMode_Wrap == info.mapU) {
@ -158,7 +159,8 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
* type (e.g. if mirroring is active there IS a difference between
* offset 2 and 3)
*/
if ((rounded = (int)info.mTranslation.y)) {
rounded = (int)info.mTranslation.y;
if (rounded) {
float out = 0.0f;
szTemp[0] = 0;
if (aiTextureMapMode_Wrap == info.mapV) {
@ -176,7 +178,7 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
}
else if (aiTextureMapMode_Clamp == info.mapV || aiTextureMapMode_Decal == info.mapV) {
// Clamp - translations beyond 1,1 are senseless
::ai_snprintf(szTemp,512,"[c] UV V offset %f canbe clamped to 1.0f",info.mTranslation.y);
::ai_snprintf(szTemp,512,"[c] UV V offset %f can be clamped to 1.0f",info.mTranslation.y);
out = 1.f;
}
@ -185,7 +187,6 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
info.mTranslation.y = out;
}
}
return;
}
// ------------------------------------------------------------------------------------------------
@ -428,7 +429,7 @@ void TextureTransformStep::Execute( aiScene* pScene)
// at the end of the list
bool ref[AI_MAX_NUMBER_OF_TEXTURECOORDS];
for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n)
ref[n] = (!mesh->mTextureCoords[n] ? true : false);
ref[n] = !mesh->mTextureCoords[n];
for (it = trafo.begin();it != trafo.end(); ++it)
ref[(*it).uvIndex] = true;
@ -447,7 +448,7 @@ void TextureTransformStep::Execute( aiScene* pScene)
if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) {
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_ERROR_F(static_cast<unsigned int>(trafo.size()), " UV channels required but just ",
ASSIMP_LOG_ERROR(static_cast<unsigned int>(trafo.size()), " UV channels required but just ",
AI_MAX_NUMBER_OF_TEXTURECOORDS, " available");
}
size = AI_MAX_NUMBER_OF_TEXTURECOORDS;
@ -507,7 +508,7 @@ void TextureTransformStep::Execute( aiScene* pScene)
aiVector3D* dest, *end;
dest = mesh->mTextureCoords[n];
ai_assert(NULL != src);
ai_assert(nullptr != src);
// Copy the data to the destination array
if (dest != src)
@ -538,7 +539,7 @@ void TextureTransformStep::Execute( aiScene* pScene)
m5.a3 += trl.x; m5.b3 += trl.y;
matrix = m2 * m4 * matrix * m3 * m5;
for (src = dest; src != end; ++src) { /* manual homogenious divide */
for (src = dest; src != end; ++src) { /* manual homogeneous divide */
src->z = 1.f;
*src = matrix * *src;
src->x /= src->z;
@ -556,7 +557,7 @@ void TextureTransformStep::Execute( aiScene* pScene)
if (!DefaultLogger::isNullLogger()) {
if (transformedChannels) {
ASSIMP_LOG_INFO_F("TransformUVCoordsProcess end: ", outChannels, " output channels (in: ", inChannels, ", modified: ", transformedChannels,")");
ASSIMP_LOG_INFO("TransformUVCoordsProcess end: ", outChannels, " output channels (in: ", inChannels, ", modified: ", transformedChannels,")");
} else {
ASSIMP_LOG_DEBUG("TransformUVCoordsProcess finished");
}

View file

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

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -64,6 +64,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "Common/PolyTools.h"
#include <memory>
#include <cstdint>
//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
//#define AI_BUILD_TRIANGULATE_DEBUG_POLYS
@ -75,6 +76,87 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
namespace {
/**
* @brief Helper struct used to simplify NGON encoding functions.
*/
struct NGONEncoder {
NGONEncoder() : mLastNGONFirstIndex((unsigned int)-1) {}
/**
* @brief Encode the current triangle, and make sure it is recognized as a triangle.
*
* This method will rotate indices in tri if needed in order to avoid tri to be considered
* part of the previous ngon. This method is to be used whenever you want to emit a real triangle,
* and make sure it is seen as a triangle.
*
* @param tri Triangle to encode.
*/
void ngonEncodeTriangle(aiFace * tri) {
ai_assert(tri->mNumIndices == 3);
// Rotate indices in new triangle to avoid ngon encoding false ngons
// Otherwise, the new triangle would be considered part of the previous NGON.
if (isConsideredSameAsLastNgon(tri)) {
std::swap(tri->mIndices[0], tri->mIndices[2]);
std::swap(tri->mIndices[1], tri->mIndices[2]);
}
mLastNGONFirstIndex = tri->mIndices[0];
}
/**
* @brief Encode a quad (2 triangles) in ngon encoding, and make sure they are seen as a single ngon.
*
* @param tri1 First quad triangle
* @param tri2 Second quad triangle
*
* @pre Triangles must be properly fanned from the most appropriate vertex.
*/
void ngonEncodeQuad(aiFace *tri1, aiFace *tri2) {
ai_assert(tri1->mNumIndices == 3);
ai_assert(tri2->mNumIndices == 3);
ai_assert(tri1->mIndices[0] == tri2->mIndices[0]);
// If the selected fanning vertex is the same as the previously
// emitted ngon, we use the opposite vertex which also happens to work
// for tri-fanning a concave quad.
// ref: https://github.com/assimp/assimp/pull/3695#issuecomment-805999760
if (isConsideredSameAsLastNgon(tri1)) {
// Right-rotate indices for tri1 (index 2 becomes the new fanning vertex)
std::swap(tri1->mIndices[0], tri1->mIndices[2]);
std::swap(tri1->mIndices[1], tri1->mIndices[2]);
// Left-rotate indices for tri2 (index 2 becomes the new fanning vertex)
std::swap(tri2->mIndices[1], tri2->mIndices[2]);
std::swap(tri2->mIndices[0], tri2->mIndices[2]);
ai_assert(tri1->mIndices[0] == tri2->mIndices[0]);
}
mLastNGONFirstIndex = tri1->mIndices[0];
}
/**
* @brief Check whether this triangle would be considered part of the lastly emitted ngon or not.
*
* @param tri Current triangle.
* @return true If used as is, this triangle will be part of last ngon.
* @return false If used as is, this triangle is not considered part of the last ngon.
*/
bool isConsideredSameAsLastNgon(const aiFace * tri) const {
ai_assert(tri->mNumIndices == 3);
return tri->mIndices[0] == mLastNGONFirstIndex;
}
private:
unsigned int mLastNGONFirstIndex;
};
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
TriangulateProcess::TriangulateProcess()
@ -141,7 +223,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
}
// Find out how many output faces we'll get
unsigned int numOut = 0, max_out = 0;
uint32_t numOut = 0, max_out = 0;
bool get_normals = true;
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
aiFace& face = pMesh->mFaces[a];
@ -161,7 +243,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
// Just another check whether aiMesh::mPrimitiveTypes is correct
ai_assert(numOut != pMesh->mNumFaces);
aiVector3D* nor_out = NULL;
aiVector3D *nor_out = nullptr;
// if we don't have normals yet, but expect them to be a cheap side
// product of triangulation anyway, allocate storage for them.
@ -174,10 +256,15 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
// The mesh becomes NGON encoded now, during the triangulation process.
pMesh->mPrimitiveTypes |= aiPrimitiveType_NGONEncodingFlag;
aiFace* out = new aiFace[numOut](), *curOut = out;
std::vector<aiVector3D> temp_verts3d(max_out+2); /* temporary storage for vertices */
std::vector<aiVector2D> temp_verts(max_out+2);
NGONEncoder ngonEncoder;
// Apply vertex colors to represent the face winding?
#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
if (!pMesh->mColors[0])
@ -219,8 +306,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
aiFace& nface = *curOut++;
nface.mNumIndices = face.mNumIndices;
nface.mIndices = face.mIndices;
face.mIndices = nullptr;
// points and lines don't require ngon encoding (and are not supported either!)
if (nface.mNumIndices == 3) ngonEncoder.ngonEncodeTriangle(&nface);
face.mIndices = NULL;
continue;
}
// optimized code for quadrilaterals
@ -272,7 +362,10 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
sface.mIndices[2] = temp[(start_vertex + 3) % 4];
// prevent double deletion of the indices field
face.mIndices = NULL;
face.mIndices = nullptr;
ngonEncoder.ngonEncodeQuad(&nface, &sface);
continue;
}
else
@ -283,11 +376,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
// modeling suite to make extensive use of highly concave, monster polygons ...
// so we need to apply the full 'ear cutting' algorithm to get it right.
// RERQUIREMENT: polygon is expected to be simple and *nearly* planar.
// REQUIREMENT: polygon is expected to be simple and *nearly* planar.
// We project it onto a plane to get a 2d triangle.
// Collect all vertices of of the polygon.
for (tmp = 0; tmp < max; ++tmp) {
for (tmp = 0; tmp < max; ++tmp) {
temp_verts3d[tmp] = verts[idx[tmp]];
}
@ -419,7 +512,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
num = 0;
break;
curOut -= (max-num); /* undo all previous work */
/*curOut -= (max-num); // undo all previous work
for (tmp = 0; tmp < max-2; ++tmp) {
aiFace& nface = *curOut++;
@ -433,7 +526,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
}
num = 0;
break;
break;*/
}
aiFace& nface = *curOut++;
@ -490,7 +583,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
// 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_DEBUG("Dropping triangle with area 0");
// ASSIMP_LOG_VERBOSE_DEBUG("Dropping triangle with area 0");
// --curOut;
// delete[] f->mIndices;
@ -507,11 +600,16 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
i[0] = idx[i[0]];
i[1] = idx[i[1]];
i[2] = idx[i[2]];
// IMPROVEMENT: Polygons are not supported yet by this ngon encoding + triangulation step.
// So we encode polygons as regular triangles. No way to reconstruct the original
// polygon in this case.
ngonEncoder.ngonEncodeTriangle(f);
++f;
}
delete[] face.mIndices;
face.mIndices = NULL;
face.mIndices = nullptr;
}
#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS

View file

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

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -55,6 +55,7 @@ struct aiBone;
struct aiMesh;
struct aiAnimation;
struct aiNodeAnim;
struct aiMeshMorphAnim;
struct aiTexture;
struct aiMaterial;
struct aiNode;
@ -150,6 +151,13 @@ protected:
void Validate( const aiAnimation* pAnimation,
const aiNodeAnim* pBoneAnim);
/** Validates a mesh morph animation channel.
* @param pAnimation Input animation.
* @param pMeshMorphAnim Mesh morph animation channel.
* */
void Validate( const aiAnimation* pAnimation,
const aiMeshMorphAnim* pMeshMorphAnim);
// -------------------------------------------------------------------
/** Validates a node and all of its subnodes
* @param Node Input node*/