Merge pull request #4527 from motazmuhammad/master
Accelerate the Merge vertex post processing steppull/4532/head v5.2.4
commit
c8dafe0d28
|
@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2022, assimp team
|
Copyright (c) 2006-2022, assimp team
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
@ -45,41 +43,38 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
* for all imported meshes
|
* for all imported meshes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
|
#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
|
||||||
|
|
||||||
#include "JoinVerticesProcess.h"
|
#include "JoinVerticesProcess.h"
|
||||||
#include "ProcessHelper.h"
|
#include "ProcessHelper.h"
|
||||||
#include <assimp/Vertex.h>
|
#include <assimp/Vertex.h>
|
||||||
#include <assimp/TinyFormatter.h>
|
#include <assimp/TinyFormatter.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Constructor to be privately used by Importer
|
// Constructor to be privately used by Importer
|
||||||
JoinVerticesProcess::JoinVerticesProcess()
|
JoinVerticesProcess::JoinVerticesProcess() {
|
||||||
{
|
|
||||||
// nothing to do here
|
// nothing to do here
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Destructor, private as well
|
// Destructor, private as well
|
||||||
JoinVerticesProcess::~JoinVerticesProcess()
|
JoinVerticesProcess::~JoinVerticesProcess() {
|
||||||
{
|
|
||||||
// nothing to do here
|
// nothing to do here
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Returns whether the processing step is present in the given flag field.
|
// Returns whether the processing step is present in the given flag field.
|
||||||
bool JoinVerticesProcess::IsActive( unsigned int pFlags) const
|
bool JoinVerticesProcess::IsActive( unsigned int pFlags) const {
|
||||||
{
|
|
||||||
return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
|
return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Executes the post processing step on the given imported data.
|
// Executes the post processing step on the given imported data.
|
||||||
void JoinVerticesProcess::Execute( aiScene* pScene)
|
void JoinVerticesProcess::Execute( aiScene* pScene) {
|
||||||
{
|
|
||||||
ASSIMP_LOG_DEBUG("JoinVerticesProcess begin");
|
ASSIMP_LOG_DEBUG("JoinVerticesProcess begin");
|
||||||
|
|
||||||
// get the total number of vertices BEFORE the step is executed
|
// get the total number of vertices BEFORE the step is executed
|
||||||
|
@ -92,27 +87,29 @@ void JoinVerticesProcess::Execute( aiScene* pScene)
|
||||||
|
|
||||||
// execute the step
|
// execute the step
|
||||||
int iNumVertices = 0;
|
int iNumVertices = 0;
|
||||||
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
|
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
|
||||||
iNumVertices += ProcessMesh( pScene->mMeshes[a],a);
|
iNumVertices += ProcessMesh( pScene->mMeshes[a],a);
|
||||||
|
}
|
||||||
|
|
||||||
|
pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
|
||||||
|
|
||||||
// if logging is active, print detailed statistics
|
// if logging is active, print detailed statistics
|
||||||
if (!DefaultLogger::isNullLogger()) {
|
if (!DefaultLogger::isNullLogger()) {
|
||||||
if (iNumOldVertices == iNumVertices) {
|
if (iNumOldVertices == iNumVertices) {
|
||||||
ASSIMP_LOG_DEBUG("JoinVerticesProcess finished ");
|
ASSIMP_LOG_DEBUG("JoinVerticesProcess finished ");
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show statistics
|
||||||
ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices,
|
ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices,
|
||||||
" out: ", iNumVertices, " | ~",
|
" out: ", iNumVertices, " | ~",
|
||||||
((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f );
|
((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex)
|
bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex) {
|
||||||
{
|
|
||||||
// A little helper to find locally close vertices faster.
|
// A little helper to find locally close vertices faster.
|
||||||
// Try to reuse the lookup table from the last step.
|
// Try to reuse the lookup table from the last step.
|
||||||
const static float epsilon = 1e-5f;
|
const static float epsilon = 1e-5f;
|
||||||
|
@ -171,8 +168,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// Position, if present (check made for aiAnimMesh)
|
// Position, if present (check made for aiAnimMesh)
|
||||||
if (pMesh->mVertices)
|
if (pMesh->mVertices) {
|
||||||
{
|
|
||||||
delete [] pMesh->mVertices;
|
delete [] pMesh->mVertices;
|
||||||
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
|
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
|
||||||
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
||||||
|
@ -181,8 +177,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normals, if present
|
// Normals, if present
|
||||||
if (pMesh->mNormals)
|
if (pMesh->mNormals) {
|
||||||
{
|
|
||||||
delete [] pMesh->mNormals;
|
delete [] pMesh->mNormals;
|
||||||
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
|
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
|
||||||
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
||||||
|
@ -190,8 +185,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Tangents, if present
|
// Tangents, if present
|
||||||
if (pMesh->mTangents)
|
if (pMesh->mTangents) {
|
||||||
{
|
|
||||||
delete [] pMesh->mTangents;
|
delete [] pMesh->mTangents;
|
||||||
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
|
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
|
||||||
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
||||||
|
@ -199,8 +193,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Bitangents as well
|
// Bitangents as well
|
||||||
if (pMesh->mBitangents)
|
if (pMesh->mBitangents) {
|
||||||
{
|
|
||||||
delete [] pMesh->mBitangents;
|
delete [] pMesh->mBitangents;
|
||||||
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
|
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
|
||||||
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
||||||
|
@ -208,8 +201,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Vertex colors
|
// Vertex colors
|
||||||
for (unsigned int a = 0; pMesh->HasVertexColors(a); a++)
|
for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) {
|
||||||
{
|
|
||||||
delete [] pMesh->mColors[a];
|
delete [] pMesh->mColors[a];
|
||||||
pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
|
pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
|
||||||
for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
|
for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
|
||||||
|
@ -217,8 +209,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Texture coords
|
// Texture coords
|
||||||
for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++)
|
for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) {
|
||||||
{
|
|
||||||
delete [] pMesh->mTextureCoords[a];
|
delete [] pMesh->mTextureCoords[a];
|
||||||
pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
|
pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
|
||||||
for (unsigned int b = 0; b < pMesh->mNumVertices; b++) {
|
for (unsigned int b = 0; b < pMesh->mNumVertices; b++) {
|
||||||
|
@ -226,12 +217,40 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Unites identical vertices in the given mesh
|
// Unites identical vertices in the given mesh
|
||||||
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
// combine hashes
|
||||||
{
|
inline void hash_combine(std::size_t &) {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Rest>
|
||||||
|
inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
|
||||||
|
std::hash<T> hasher;
|
||||||
|
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
|
||||||
|
hash_combine(seed, rest...);
|
||||||
|
}
|
||||||
|
//template specialization for std::hash for Vertex
|
||||||
|
template<>
|
||||||
|
struct std::hash<Vertex> {
|
||||||
|
std::size_t operator()(Vertex const& v) const noexcept {
|
||||||
|
size_t seed = 0;
|
||||||
|
hash_combine(seed, v.position.x ,v.position.y,v.position.z);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//template specialization for std::equal_to for Vertex
|
||||||
|
template<>
|
||||||
|
struct std::equal_to<Vertex> {
|
||||||
|
bool operator()(const Vertex &lhs, const Vertex &rhs) const {
|
||||||
|
return areVerticesEqual(lhs, rhs, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// now start the JoinVerticesProcess
|
||||||
|
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) {
|
||||||
static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8");
|
static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8");
|
||||||
static_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8");
|
static_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8");
|
||||||
|
|
||||||
|
@ -245,8 +264,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
// multiple meshes)
|
// multiple meshes)
|
||||||
std::unordered_set<unsigned int> usedVertexIndices;
|
std::unordered_set<unsigned int> usedVertexIndices;
|
||||||
usedVertexIndices.reserve(pMesh->mNumVertices);
|
usedVertexIndices.reserve(pMesh->mNumVertices);
|
||||||
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
|
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
|
||||||
{
|
|
||||||
aiFace& face = pMesh->mFaces[a];
|
aiFace& face = pMesh->mFaces[a];
|
||||||
for( unsigned int b = 0; b < face.mNumIndices; b++) {
|
for( unsigned int b = 0; b < face.mNumIndices; b++) {
|
||||||
usedVertexIndices.insert(face.mIndices[b]);
|
usedVertexIndices.insert(face.mIndices[b]);
|
||||||
|
@ -292,7 +310,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
|
|
||||||
// Run an optimized code path if we don't have multiple UVs or vertex colors.
|
// Run an optimized code path if we don't have multiple UVs or vertex colors.
|
||||||
// This should yield false in more than 99% of all imports ...
|
// This should yield false in more than 99% of all imports ...
|
||||||
const bool complex = ( pMesh->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1);
|
|
||||||
const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0;
|
const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0;
|
||||||
|
|
||||||
// We'll never have more vertices afterwards.
|
// We'll never have more vertices afterwards.
|
||||||
|
@ -303,72 +320,38 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices);
|
uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// a map that maps a vertix to its new index
|
||||||
|
std::unordered_map<Vertex,int> vertex2Index;
|
||||||
|
// we can not end up with more vertices than we started with
|
||||||
|
vertex2Index.reserve(pMesh->mNumVertices);
|
||||||
// Now check each vertex if it brings something new to the table
|
// Now check each vertex if it brings something new to the table
|
||||||
|
int newIndex = 0;
|
||||||
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
||||||
|
// if the vertex is unused Do nothing
|
||||||
if (usedVertexIndices.find(a) == usedVertexIndices.end()) {
|
if (usedVertexIndices.find(a) == usedVertexIndices.end()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect the vertex data
|
// collect the vertex data
|
||||||
Vertex v(pMesh,a);
|
Vertex v(pMesh,a);
|
||||||
|
// is the vertex already in the map?
|
||||||
// collect all vertices that are close enough to the given position
|
auto it = vertex2Index.find(v);
|
||||||
vertexFinder->FindIdenticalPositions( v.position, verticesFound);
|
// if the vertex is not in the map then it is a new vertex add it.
|
||||||
unsigned int matchIndex = 0xffffffff;
|
if (it == vertex2Index.end()) {
|
||||||
|
// this is a new vertex give it a new index
|
||||||
// check all unique vertices close to the position if this vertex is already present among them
|
vertex2Index[v] = newIndex;
|
||||||
for( unsigned int b = 0; b < verticesFound.size(); b++) {
|
//keep track of its index and increment 1
|
||||||
const unsigned int vidx = verticesFound[b];
|
replaceIndex[a] = newIndex++;
|
||||||
const unsigned int uidx = replaceIndex[ vidx];
|
// add the vertex to the unique vertices
|
||||||
if( uidx & 0x80000000)
|
uniqueVertices.push_back(v);
|
||||||
continue;
|
|
||||||
|
|
||||||
const Vertex& uv = uniqueVertices[ uidx];
|
|
||||||
|
|
||||||
if (!areVerticesEqual(v, uv, complex)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasAnimMeshes) {
|
|
||||||
// If given vertex is animated, then it has to be preserver 1 to 1 (base mesh and animated mesh require same topology)
|
|
||||||
// NOTE: not doing this totaly breaks anim meshes as they don't have their own faces (they use pMesh->mFaces)
|
|
||||||
bool breaksAnimMesh = false;
|
|
||||||
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
|
|
||||||
const Vertex& animatedUV = uniqueAnimatedVertices[animMeshIndex][ uidx];
|
|
||||||
Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
|
|
||||||
if (!areVerticesEqual(aniMeshVertex, animatedUV, complex)) {
|
|
||||||
breaksAnimMesh = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (breaksAnimMesh) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we're still here -> this vertex perfectly matches our given vertex
|
|
||||||
matchIndex = uidx;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// found a replacement vertex among the uniques?
|
|
||||||
if( matchIndex != 0xffffffff)
|
|
||||||
{
|
|
||||||
// store where to found the matching unique vertex
|
|
||||||
replaceIndex[a] = matchIndex | 0x80000000;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// no unique vertex matches it up to now -> so add it
|
|
||||||
replaceIndex[a] = (unsigned int)uniqueVertices.size();
|
|
||||||
uniqueVertices.push_back( v);
|
|
||||||
if (hasAnimMeshes) {
|
if (hasAnimMeshes) {
|
||||||
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
|
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
|
||||||
Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
|
Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
|
||||||
uniqueAnimatedVertices[animMeshIndex].push_back(aniMeshVertex);
|
uniqueAnimatedVertices[animMeshIndex].push_back(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else{
|
||||||
|
// if the vertex is already there just find the replace index that is appropriate to it
|
||||||
|
replaceIndex[a] = it->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,8 +377,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// adjust the indices in all faces
|
// adjust the indices in all faces
|
||||||
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
|
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
|
||||||
{
|
|
||||||
aiFace& face = pMesh->mFaces[a];
|
aiFace& face = pMesh->mFaces[a];
|
||||||
for( unsigned int b = 0; b < face.mNumIndices; b++) {
|
for( unsigned int b = 0; b < face.mNumIndices; b++) {
|
||||||
face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000;
|
face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000;
|
||||||
|
|
Loading…
Reference in New Issue