ADD Vertex utility class to simplify conversion from and to interleaved vertices.

Refactor JoinVerticesProcess to utilize the new utility.
ADD basic operators for aiColor4D, move to dedicated header and implementation file.
ADD some utility functions to SpatialSort.
ADD my existing Catmull-Clark implementation to Assimp for all model formats with support for subdivision surfaces. Slightly WIP, likely to produce errors on non-closed meshes. Currently only implemented in the AC3D loader.
Switch to byteswap intrinsics instead of inline assembly (bswap). Currently MSVC only.
FIX phong shading in assimp_view.
VertexTriangleAdjacency class now also works on arbitrary polygons - UNTESTED, tbd.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@532 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
This commit is contained in:
aramis_acg
2010-01-28 21:17:25 +00:00
parent ce29aca94e
commit 2e3fee99f0
29 changed files with 1947 additions and 316 deletions

View File

@@ -48,22 +48,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "JoinVerticesProcess.h"
#include "ProcessHelper.h"
#include "Vertex.h"
using namespace Assimp;
namespace Assimp
{
// Data structure to keep a vertex in an interlaced format
struct Vertex
{
aiVector3D mPosition;
aiVector3D mNormal;
aiVector3D mTangent, mBitangent;
aiColor4D mColors [AI_MAX_NUMBER_OF_COLOR_SETS];
aiVector3D mTexCoords [AI_MAX_NUMBER_OF_TEXTURECOORDS];
};
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
JoinVerticesProcess::JoinVerticesProcess()
@@ -131,8 +118,9 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
BOOST_STATIC_ASSERT( AI_MAX_NUMBER_OF_TEXTURECOORDS == 4);
// Return early if we don't have any positions
if (!pMesh->HasPositions() || !pMesh->HasFaces())
if (!pMesh->HasPositions() || !pMesh->HasFaces()) {
return 0;
}
// We'll never have more vertices afterwards.
std::vector<Vertex> uniqueVertices;
@@ -143,34 +131,39 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// for each vertex whether it was replaced by an existing unique vertex (true) or a new vertex was created for it (false)
std::vector<bool> isVertexUnique( pMesh->mNumVertices, false);
// A little helper to find locally close vertices faster
// FIX: check whether we can reuse the SpatialSort of a previous step
// A little helper to find locally close vertices faster.
// Try to reuse the lookup table from the last step.
const static float epsilon = 1e-5f;
float posEpsilonSqr;
SpatialSort* vertexFinder = NULL;
SpatialSort _vertexFinder;
SpatialSort* vertexFinder = NULL;
SpatialSort _vertexFinder;
typedef std::pair<SpatialSort,float> SpatPair;
if (shared) {
std::vector<std::pair<SpatialSort,float> >* avf;
std::vector<SpatPair >* avf;
shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
if (avf) {
std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
vertexFinder = &blubb.first;posEpsilonSqr = blubb.second;
SpatPair& blubb = (*avf)[meshIndex];
vertexFinder = &blubb.first;
posEpsilonSqr = blubb.second;
}
}
if (!vertexFinder) {
// bad, need to compute it.
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
vertexFinder = &_vertexFinder;
posEpsilonSqr = ComputePositionEpsilon(pMesh);
}
// squared because we check against squared length of the vector difference
// Squared because we check against squared length of the vector difference
static const float squareEpsilon = epsilon * epsilon;
// again, better waste some bytes than a realloc ...
// Again, better waste some bytes than a realloc ...
std::vector<unsigned int> verticesFound;
verticesFound.reserve(10);
// run an optimized code path if we don't have multiple UVs or vertex colors
// Run an optimized code path if we don't have multiple UVs or vertex colors.
// This should yield false in more than 99% of all imports ...
const bool complex = (
pMesh->mTextureCoords[1] ||
pMesh->mTextureCoords[2] ||
@@ -181,49 +174,22 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
pMesh->mColors[3] );
// Now check each vertex if it brings something new to the table
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
{
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
// collect the vertex data
Vertex v;
v.mPosition = pMesh->mVertices[a];
if (pMesh->mNormals)
v.mNormal = pMesh->mNormals[a];
if (pMesh->mTangents)
v.mTangent = pMesh->mTangents[a];
if (pMesh->mBitangents)
v.mBitangent = pMesh->mBitangents[a];
if (pMesh->mColors[0]) { // manually unrolled here
v.mColors[0] = pMesh->mColors[0][a];
if (pMesh->mColors[1]) {
v.mColors[1] = pMesh->mColors[1][a];
if (pMesh->mColors[2]) {
v.mColors[2] = pMesh->mColors[2][a];
if (pMesh->mColors[3]) {
v.mColors[3] = pMesh->mColors[3][a];
}}}}
if (pMesh->mTextureCoords[0]) { // manually unrolled here
v.mTexCoords[0] = pMesh->mTextureCoords[0][a];
if (pMesh->mTextureCoords[1]) {
v.mTexCoords[1] = pMesh->mTextureCoords[1][a];
if (pMesh->mTextureCoords[2]) {
v.mTexCoords[2] = pMesh->mTextureCoords[2][a];
if (pMesh->mTextureCoords[3]) {
v.mTexCoords[3] = pMesh->mTextureCoords[3][a];
}}}}
Vertex v(pMesh,a);
// collect all vertices that are close enough to the given position
vertexFinder->FindPositions( v.mPosition, posEpsilonSqr, verticesFound);
vertexFinder->FindPositions( v.position, posEpsilonSqr, verticesFound);
unsigned int matchIndex = 0xffffffff;
// check all unique vertices close to the position if this vertex is already present among them
for( unsigned int b = 0; b < verticesFound.size(); b++)
{
for( unsigned int b = 0; b < verticesFound.size(); b++) {
const unsigned int vidx = verticesFound[b];
const unsigned int uidx = replaceIndex[ vidx];
if( uidx == 0xffffffff || !isVertexUnique[ vidx])
if( uidx == 0xffffffff || !isVertexUnique[ vidx]) {
continue;
}
const Vertex& uv = uniqueVertices[ uidx];
// Position mismatch is impossible - the vertex finder already discarded all non-matching positions
@@ -231,36 +197,35 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// We just test the other attributes even if they're not present in the mesh.
// In this case they're initialized to 0 so the comparision succeeds.
// By this method the non-present attributes are effectively ignored in the comparision.
if( (uv.mNormal - v.mNormal).SquareLength() > squareEpsilon)
if( (uv.normal - v.normal).SquareLength() > squareEpsilon)
continue;
if( (uv.mTangent - v.mTangent).SquareLength() > squareEpsilon)
if( (uv.texcoords[0] - v.texcoords[0]).SquareLength() > squareEpsilon)
continue;
if( (uv.mBitangent - v.mBitangent).SquareLength() > squareEpsilon)
if( (uv.tangent - v.tangent).SquareLength() > squareEpsilon)
continue;
if( (uv.mTexCoords[0] - v.mTexCoords[0]).SquareLength() > squareEpsilon)
if( (uv.bitangent - v.bitangent).SquareLength() > squareEpsilon)
continue;
// Usually we won't have vertex colors or multiple UVs, so we can skip from here
// Actually this increases runtime performance slightly.
if (complex)
{
// Actually this increases runtime performance slightly, at least if branch
// prediction is on our side.
if (complex){
// manually unrolled because continue wouldn't work as desired in an inner loop
if( GetColorDifference( uv.mColors[0], v.mColors[0]) > squareEpsilon)
if( GetColorDifference( uv.colors[0], v.colors[0]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[1], v.mColors[1]) > squareEpsilon)
if( GetColorDifference( uv.colors[1], v.colors[1]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[2], v.mColors[2]) > squareEpsilon)
if( GetColorDifference( uv.colors[2], v.colors[2]) > squareEpsilon)
continue;
if( GetColorDifference( uv.mColors[3], v.mColors[3]) > squareEpsilon)
if( GetColorDifference( uv.colors[3], v.colors[3]) > squareEpsilon)
continue;
// texture coord matching manually unrolled as well
if( (uv.mTexCoords[1] - v.mTexCoords[1]).SquareLength() > squareEpsilon)
if( (uv.texcoords[1] - v.texcoords[1]).SquareLength() > squareEpsilon)
continue;
if( (uv.mTexCoords[2] - v.mTexCoords[2]).SquareLength() > squareEpsilon)
if( (uv.texcoords[2] - v.texcoords[2]).SquareLength() > squareEpsilon)
continue;
if( (uv.mTexCoords[3] - v.mTexCoords[3]).SquareLength() > squareEpsilon)
if( (uv.texcoords[3] - v.texcoords[3]).SquareLength() > squareEpsilon)
continue;
}
@@ -285,8 +250,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
}
}
if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE)
{
if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
char szBuff[128]; // should be sufficiently large in every case
::sprintf(szBuff,"Mesh %i | Verts in: %i out: %i | ~%.1f%%",
meshIndex,
@@ -299,57 +263,62 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// replace vertex data with the unique data sets
pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
// ----------------------------------------------------------------------------
// NOTE - we're *not* calling Vertex::SortBack() because it would check for
// presence of every single vertex component once PER VERTEX. And our CPU
// dislikes branches, even if they're easily predictable.
// ----------------------------------------------------------------------------
// Position
delete [] pMesh->mVertices;
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mVertices[a] = uniqueVertices[a].mPosition;
pMesh->mVertices[a] = uniqueVertices[a].position;
// Normals, if present
if( pMesh->mNormals)
{
delete [] pMesh->mNormals;
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mNormals[a] = uniqueVertices[a].mNormal;
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
pMesh->mNormals[a] = uniqueVertices[a].normal;
}
}
// Tangents, if present
if( pMesh->mTangents)
{
delete [] pMesh->mTangents;
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mTangents[a] = uniqueVertices[a].mTangent;
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
pMesh->mTangents[a] = uniqueVertices[a].tangent;
}
}
// Bitangents as well
if( pMesh->mBitangents)
{
delete [] pMesh->mBitangents;
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mBitangents[a] = uniqueVertices[a].mBitangent;
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
pMesh->mBitangents[a] = uniqueVertices[a].bitangent;
}
}
// Vertex colors
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
for( unsigned int a = 0; pMesh->HasVertexColors(a); a++)
{
if( !pMesh->mColors[a])
break;
delete [] pMesh->mColors[a];
pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
for( unsigned int b = 0; b < pMesh->mNumVertices; b++)
pMesh->mColors[a][b] = uniqueVertices[b].mColors[a];
for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
pMesh->mColors[a][b] = uniqueVertices[b].colors[a];
}
}
// Texture coords
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
for( unsigned int a = 0; pMesh->HasTextureCoords(a); a++)
{
if( !pMesh->mTextureCoords[a])
break;
delete [] pMesh->mTextureCoords[a];
pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
for( unsigned int b = 0; b < pMesh->mNumVertices; b++)
pMesh->mTextureCoords[a][b] = uniqueVertices[b].mTexCoords[a];
for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a];
}
}
// adjust the indices in all faces
@@ -407,8 +376,9 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
*/
delete bone;
--pMesh->mNumBones;
for (unsigned int n = a; n < pMesh->mNumBones; ++n)
for (unsigned int n = a; n < pMesh->mNumBones; ++n) {
pMesh->mBones[n] = pMesh->mBones[n+1];
}
--a;
DefaultLogger::get()->warn("Removing bone -> no weights remaining");