Merge branch 'master' into fix_gltf2_export_componentType_error

pull/1925/head
Kim Kulling 2018-05-06 19:39:37 +02:00 committed by GitHub
commit 327a43c26f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 381 additions and 300 deletions

View File

@ -208,8 +208,15 @@ OPTION(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT "default value of all ASSIMP_BUILD_
# macro to add the CMake Option ADD_ASSIMP_IMPORTER_<name> which enables compile of loader
# this way selective loaders can be compiled (reduces filesize + compile time)
MACRO(ADD_ASSIMP_IMPORTER name)
OPTION(ASSIMP_BUILD_${name}_IMPORTER "build the ${name} importer" ${ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT})
IF(ASSIMP_BUILD_${name}_IMPORTER)
IF (ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT)
set(ASSIMP_IMPORTER_ENABLED TRUE)
IF (DEFINED ASSIMP_BUILD_${name}_IMPORTER AND NOT ASSIMP_BUILD_${name}_IMPORTER)
set(ASSIMP_IMPORTER_ENABLED FALSE)
ENDIF ()
ELSE ()
set(ASSIMP_IMPORTER_ENABLED ${ASSIMP_BUILD_${name}_IMPORTER})
ENDIF ()
IF (ASSIMP_IMPORTER_ENABLED)
LIST(APPEND ASSIMP_LOADER_SRCS ${ARGN})
SET(ASSIMP_IMPORTERS_ENABLED "${ASSIMP_IMPORTERS_ENABLED} ${name}")
SET(${name}_SRCS ${ARGN})

View File

@ -154,9 +154,9 @@ Exporter::ExportFormatEntry gExporters[] =
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf2", &ExportSceneGLTF2,
Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb2", &ExportSceneGLB2,
Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
#endif

View File

@ -874,7 +874,7 @@ void Converter::ConvertModel( const Model& model, aiNode& nd, const aiMatrix4x4&
const MeshGeometry* const mesh = dynamic_cast< const MeshGeometry* >( geo );
if ( mesh ) {
const std::vector<unsigned int>& indices = ConvertMesh( *mesh, model, node_global_transform );
const std::vector<unsigned int>& indices = ConvertMesh( *mesh, model, node_global_transform, nd);
std::copy( indices.begin(), indices.end(), std::back_inserter( meshes ) );
}
else {
@ -891,7 +891,7 @@ void Converter::ConvertModel( const Model& model, aiNode& nd, const aiMatrix4x4&
}
std::vector<unsigned int> Converter::ConvertMesh( const MeshGeometry& mesh, const Model& model,
const aiMatrix4x4& node_global_transform )
const aiMatrix4x4& node_global_transform, aiNode& nd)
{
std::vector<unsigned int> temp;
@ -915,17 +915,17 @@ std::vector<unsigned int> Converter::ConvertMesh( const MeshGeometry& mesh, cons
const MatIndexArray::value_type base = mindices[ 0 ];
for( MatIndexArray::value_type index : mindices ) {
if ( index != base ) {
return ConvertMeshMultiMaterial( mesh, model, node_global_transform );
return ConvertMeshMultiMaterial( mesh, model, node_global_transform, nd);
}
}
}
// faster code-path, just copy the data
temp.push_back( ConvertMeshSingleMaterial( mesh, model, node_global_transform ) );
temp.push_back( ConvertMeshSingleMaterial( mesh, model, node_global_transform, nd) );
return temp;
}
aiMesh* Converter::SetupEmptyMesh( const MeshGeometry& mesh )
aiMesh* Converter::SetupEmptyMesh( const MeshGeometry& mesh, aiNode& nd)
{
aiMesh* const out_mesh = new aiMesh();
meshes.push_back( out_mesh );
@ -940,15 +940,19 @@ aiMesh* Converter::SetupEmptyMesh( const MeshGeometry& mesh )
if ( name.length() ) {
out_mesh->mName.Set( name );
}
else
{
out_mesh->mName = nd.mName;
}
return out_mesh;
}
unsigned int Converter::ConvertMeshSingleMaterial( const MeshGeometry& mesh, const Model& model,
const aiMatrix4x4& node_global_transform )
const aiMatrix4x4& node_global_transform, aiNode& nd)
{
const MatIndexArray& mindices = mesh.GetMaterialIndices();
aiMesh* const out_mesh = SetupEmptyMesh( mesh );
aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd);
const std::vector<aiVector3D>& vertices = mesh.GetVertices();
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
@ -1072,7 +1076,7 @@ unsigned int Converter::ConvertMeshSingleMaterial( const MeshGeometry& mesh, con
}
std::vector<unsigned int> Converter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model,
const aiMatrix4x4& node_global_transform )
const aiMatrix4x4& node_global_transform, aiNode& nd)
{
const MatIndexArray& mindices = mesh.GetMaterialIndices();
ai_assert( mindices.size() );
@ -1083,7 +1087,7 @@ std::vector<unsigned int> Converter::ConvertMeshMultiMaterial( const MeshGeometr
for( MatIndexArray::value_type index : mindices ) {
if ( had.find( index ) == had.end() ) {
indices.push_back( ConvertMeshMultiMaterial( mesh, model, index, node_global_transform ) );
indices.push_back( ConvertMeshMultiMaterial( mesh, model, index, node_global_transform, nd) );
had.insert( index );
}
}
@ -1093,9 +1097,10 @@ std::vector<unsigned int> Converter::ConvertMeshMultiMaterial( const MeshGeometr
unsigned int Converter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model,
MatIndexArray::value_type index,
const aiMatrix4x4& node_global_transform )
const aiMatrix4x4& node_global_transform,
aiNode& nd)
{
aiMesh* const out_mesh = SetupEmptyMesh( mesh );
aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd);
const MatIndexArray& mindices = mesh.GetMaterialIndices();
const std::vector<aiVector3D>& vertices = mesh.GetVertices();

View File

@ -172,23 +172,23 @@ private:
// ------------------------------------------------------------------------------------------------
// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh, const Model& model,
const aiMatrix4x4& node_global_transform);
const aiMatrix4x4& node_global_transform, aiNode& nd);
// ------------------------------------------------------------------------------------------------
aiMesh* SetupEmptyMesh(const MeshGeometry& mesh);
aiMesh* SetupEmptyMesh(const MeshGeometry& mesh, aiNode& nd);
// ------------------------------------------------------------------------------------------------
unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model,
const aiMatrix4x4& node_global_transform);
const aiMatrix4x4& node_global_transform, aiNode& nd);
// ------------------------------------------------------------------------------------------------
std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
const aiMatrix4x4& node_global_transform);
const aiMatrix4x4& node_global_transform, aiNode& nd);
// ------------------------------------------------------------------------------------------------
unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
MatIndexArray::value_type index,
const aiMatrix4x4& node_global_transform);
const aiMatrix4x4& node_global_transform, aiNode& nd);
// ------------------------------------------------------------------------------------------------
static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */

View File

@ -108,6 +108,125 @@ void JoinVerticesProcess::Execute( aiScene* pScene)
pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
}
namespace {
bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex)
{
// A little helper to find locally close vertices faster.
// Try to reuse the lookup table from the last step.
const static float epsilon = 1e-5f;
// Squared because we check against squared length of the vector difference
static const float squareEpsilon = epsilon * epsilon;
// Square compare is useful for animeshes vertexes compare
if ((lhs.position - rhs.position).SquareLength() > squareEpsilon) {
return false;
}
// We just test the other attributes even if they're not present in the mesh.
// In this case they're initialized to 0 so the comparison succeeds.
// By this method the non-present attributes are effectively ignored in the comparison.
if ((lhs.normal - rhs.normal).SquareLength() > squareEpsilon) {
return false;
}
if ((lhs.texcoords[0] - rhs.texcoords[0]).SquareLength() > squareEpsilon) {
return false;
}
if ((lhs.tangent - rhs.tangent).SquareLength() > squareEpsilon) {
return false;
}
if ((lhs.bitangent - rhs.bitangent).SquareLength() > squareEpsilon) {
return false;
}
// Usually we won't have vertex colors or multiple UVs, so we can skip from here
// Actually this increases runtime performance slightly, at least if branch
// prediction is on our side.
if (complex) {
for (int i = 0; i < 8; i++) {
if (i > 0 && (lhs.texcoords[i] - rhs.texcoords[i]).SquareLength() > squareEpsilon) {
return false;
}
if (GetColorDifference(lhs.colors[i], rhs.colors[i]) > squareEpsilon) {
return false;
}
}
}
return true;
}
template<class XMesh>
void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
// 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, if present (check made for aiAnimMesh)
if (pMesh->mVertices)
{
delete [] pMesh->mVertices;
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
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].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].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].bitangent;
}
}
// Vertex colors
for (unsigned int a = 0; pMesh->HasVertexColors(a); a++)
{
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].colors[a];
}
}
// Texture coords
for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++)
{
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].texcoords[a];
}
}
}
} // namespace
// ------------------------------------------------------------------------------------------------
// Unites identical vertices in the given mesh
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
@ -132,9 +251,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
static_assert(AI_MAX_VERTICES == 0x7fffffff, "AI_MAX_VERTICES == 0x7fffffff");
std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
// 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;
@ -156,9 +272,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// posEpsilonSqr = ComputePositionEpsilon(pMesh);
}
// 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 ...
std::vector<unsigned int> verticesFound;
verticesFound.reserve(10);
@ -166,6 +279,16 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// 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->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1);
const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0;
// We'll never have more vertices afterwards.
std::vector<std::vector<Vertex>> uniqueAnimatedVertices;
if (hasAnimMeshes) {
uniqueAnimatedVertices.resize(pMesh->mNumAnimMeshes);
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices);
}
}
// Now check each vertex if it brings something new to the table
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
@ -178,74 +301,32 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// 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++) {
const unsigned int vidx = verticesFound[b];
const unsigned int uidx = replaceIndex[ vidx];
if( uidx & 0x80000000)
continue;
const Vertex& uv = uniqueVertices[ uidx];
// Position mismatch is impossible - the vertex finder already discarded all non-matching positions
// We just test the other attributes even if they're not present in the mesh.
// In this case they're initialized to 0 so the comparison succeeds.
// By this method the non-present attributes are effectively ignored in the comparison.
if( (uv.normal - v.normal).SquareLength() > squareEpsilon)
continue;
if( (uv.texcoords[0] - v.texcoords[0]).SquareLength() > squareEpsilon)
continue;
if( (uv.tangent - v.tangent).SquareLength() > squareEpsilon)
continue;
if( (uv.bitangent - v.bitangent).SquareLength() > squareEpsilon)
if (!areVerticesEqual(v, uv, complex)) {
continue;
}
// Usually we won't have vertex colors or multiple UVs, so we can skip from here
// Actually this increases runtime performance slightly, at least if branch
// prediction is on our side.
if (complex){
// manually unrolled because continue wouldn't work as desired in an inner loop,
// also because some compilers seem to fail the task. Colors and UV coords
// are interleaved since the higher entries are most likely to be
// zero and thus useless. By interleaving the arrays, vertices are,
// on average, rejected earlier.
if( (uv.texcoords[1] - v.texcoords[1]).SquareLength() > squareEpsilon)
continue;
if( GetColorDifference( uv.colors[0], v.colors[0]) > squareEpsilon)
continue;
if( (uv.texcoords[2] - v.texcoords[2]).SquareLength() > squareEpsilon)
continue;
if( GetColorDifference( uv.colors[1], v.colors[1]) > squareEpsilon)
continue;
if( (uv.texcoords[3] - v.texcoords[3]).SquareLength() > squareEpsilon)
continue;
if( GetColorDifference( uv.colors[2], v.colors[2]) > squareEpsilon)
continue;
if( (uv.texcoords[4] - v.texcoords[4]).SquareLength() > squareEpsilon)
continue;
if( GetColorDifference( uv.colors[3], v.colors[3]) > squareEpsilon)
continue;
if( (uv.texcoords[5] - v.texcoords[5]).SquareLength() > squareEpsilon)
continue;
if( GetColorDifference( uv.colors[4], v.colors[4]) > squareEpsilon)
continue;
if( (uv.texcoords[6] - v.texcoords[6]).SquareLength() > squareEpsilon)
continue;
if( GetColorDifference( uv.colors[5], v.colors[5]) > squareEpsilon)
continue;
if( (uv.texcoords[7] - v.texcoords[7]).SquareLength() > squareEpsilon)
continue;
if( GetColorDifference( uv.colors[6], v.colors[6]) > squareEpsilon)
continue;
if( GetColorDifference( uv.colors[7], v.colors[7]) > squareEpsilon)
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
@ -264,6 +345,12 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// no unique vertex matches it up to now -> so add it
replaceIndex[a] = (unsigned int)uniqueVertices.size();
uniqueVertices.push_back( v);
if (hasAnimMeshes) {
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
uniqueAnimatedVertices[animMeshIndex].push_back(aniMeshVertex);
}
}
}
}
@ -281,64 +368,10 @@ 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].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].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].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].bitangent;
}
}
// Vertex colors
for( unsigned int a = 0; pMesh->HasVertexColors(a); a++)
{
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].colors[a];
}
}
// Texture coords
for( unsigned int a = 0; pMesh->HasTextureCoords(a); a++)
{
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].texcoords[a];
updateXMeshVertices(pMesh, uniqueVertices);
if (hasAnimMeshes) {
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
updateXMeshVertices(pMesh->mAnimMeshes[animMeshIndex], uniqueAnimatedVertices[animMeshIndex]);
}
}

View File

@ -114,14 +114,13 @@ static const std::string MaterialExt = ".mtl";
ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMtl)
: filename(_filename)
, pScene(pScene)
, vp()
, vn()
, vt()
, vc()
, mVpMap()
, vp()
, useVc(false)
, mVnMap()
, mVtMap()
, mVcMap()
, mVpMap()
, mMeshes()
, endl("\n") {
// make sure that all formatting happens using the standard, C locale and not the user's current locale
@ -268,27 +267,22 @@ void ObjExporter::WriteGeometryFile(bool noMtl) {
AddNode(pScene->mRootNode, mBase);
// write vertex positions with colors, if any
mVpMap.getVectors( vp );
mVcMap.getColors( vc );
if ( vc.empty() ) {
mVpMap.getKeys( vp );
if ( !useVc ) {
mOutput << "# " << vp.size() << " vertex positions" << endl;
for ( const aiVector3D& v : vp ) {
mOutput << "v " << v.x << " " << v.y << " " << v.z << endl;
for ( const vertexData& v : vp ) {
mOutput << "v " << v.vp.x << " " << v.vp.y << " " << v.vp.z << endl;
}
} else {
mOutput << "# " << vp.size() << " vertex positions and colors" << endl;
size_t colIdx = 0;
for ( const aiVector3D& v : vp ) {
if ( colIdx < vc.size() ) {
mOutput << "v " << v.x << " " << v.y << " " << v.z << " " << vc[ colIdx ].r << " " << vc[ colIdx ].g << " " << vc[ colIdx ].b << endl;
}
++colIdx;
for ( const vertexData& v : vp ) {
mOutput << "v " << v.vp.x << " " << v.vp.y << " " << v.vp.z << " " << v.vc.r << " " << v.vc.g << " " << v.vc.b << endl;
}
}
mOutput << endl;
// write uv coordinates
mVtMap.getVectors(vt);
mVtMap.getKeys(vt);
mOutput << "# " << vt.size() << " UV coordinates" << endl;
for(const aiVector3D& v : vt) {
mOutput << "vt " << v.x << " " << v.y << " " << v.z << endl;
@ -296,7 +290,7 @@ void ObjExporter::WriteGeometryFile(bool noMtl) {
mOutput << endl;
// write vertex normals
mVnMap.getVectors(vn);
mVnMap.getKeys(vn);
mOutput << "# " << vn.size() << " vertex normals" << endl;
for(const aiVector3D& v : vn) {
mOutput << "vn " << v.x << " " << v.y << " " << v.z << endl;
@ -337,54 +331,15 @@ void ObjExporter::WriteGeometryFile(bool noMtl) {
}
}
// ------------------------------------------------------------------------------------------------
int ObjExporter::vecIndexMap::getIndex(const aiVector3D& vec) {
vecIndexMap::dataType::iterator vertIt = vecMap.find(vec);
// vertex already exists, so reference it
if(vertIt != vecMap.end()){
return vertIt->second;
}
vecMap[vec] = mNextIndex;
int ret = mNextIndex;
mNextIndex++;
return ret;
}
// ------------------------------------------------------------------------------------------------
void ObjExporter::vecIndexMap::getVectors( std::vector<aiVector3D>& vecs ) {
vecs.resize(vecMap.size());
for(vecIndexMap::dataType::iterator it = vecMap.begin(); it != vecMap.end(); ++it){
vecs[it->second-1] = it->first;
}
}
// ------------------------------------------------------------------------------------------------
int ObjExporter::colIndexMap::getIndex( const aiColor4D& col ) {
colIndexMap::dataType::iterator vertIt = colMap.find( col );
// vertex already exists, so reference it
if ( vertIt != colMap.end() ) {
return vertIt->second;
}
colMap[ col ] = mNextIndex;
int ret = mNextIndex;
mNextIndex++;
return ret;
}
// ------------------------------------------------------------------------------------------------
void ObjExporter::colIndexMap::getColors( std::vector<aiColor4D> &colors ) {
colors.resize( colMap.size() );
for ( colIndexMap::dataType::iterator it = colMap.begin(); it != colMap.end(); ++it ) {
colors[ it->second - 1 ] = it->first;
}
}
// ------------------------------------------------------------------------------------------------
void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) {
mMeshes.push_back(MeshInstance() );
MeshInstance& mesh = mMeshes.back();
if ( nullptr != m->mColors[ 0 ] ) {
useVc = true;
}
mesh.name = std::string( name.data, name.length );
mesh.matname = GetMaterialName(m->mMaterialIndex);
@ -410,7 +365,13 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
const unsigned int idx = f.mIndices[a];
aiVector3D vert = mat * m->mVertices[idx];
face.indices[a].vp = mVpMap.getIndex(vert);
if ( nullptr != m->mColors[ 0 ] ) {
aiColor4D col4 = m->mColors[ 0 ][ idx ];
face.indices[a].vp = mVpMap.getIndex({vert, aiColor3D(col4.r, col4.g, col4.b)});
} else {
face.indices[a].vp = mVpMap.getIndex({vert, aiColor3D(0,0,0)});
}
if (m->mNormals) {
aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx];
@ -419,13 +380,6 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
face.indices[a].vn = 0;
}
if ( nullptr != m->mColors[ 0 ] ) {
aiColor4D col4 = m->mColors[ 0 ][ idx ];
face.indices[ a ].vc = mVcMap.getIndex( col4 );
} else {
face.indices[ a ].vc = 0;
}
if ( m->mTextureCoords[ 0 ] ) {
face.indices[a].vt = mVtMap.getIndex(m->mTextureCoords[0][idx]);
} else {

View File

@ -77,13 +77,12 @@ private:
FaceVertex()
: vp()
, vn()
, vt()
, vc() {
, vt() {
// empty
}
// one-based, 0 means: 'does not exist'
unsigned int vp, vn, vt, vc;
unsigned int vp, vn, vt;
};
struct Face {
@ -106,8 +105,37 @@ private:
private:
std::string filename;
const aiScene* const pScene;
std::vector<aiVector3D> vp, vn, vt;
struct vertexData {
aiVector3D vp;
aiColor3D vc; // OBJ does not support 4D color
};
std::vector<aiVector3D> vn, vt;
std::vector<aiColor4D> vc;
std::vector<vertexData> vp;
bool useVc;
struct vertexDataCompare {
bool operator() ( const vertexData& a, const vertexData& b ) const {
// position
if (a.vp.x < b.vp.x) return true;
if (a.vp.x > b.vp.x) return false;
if (a.vp.y < b.vp.y) return true;
if (a.vp.y > b.vp.y) return false;
if (a.vp.z < b.vp.z) return true;
if (a.vp.z > b.vp.z) return false;
// color
if (a.vc.r < b.vc.r) return true;
if (a.vc.r > b.vc.r) return false;
if (a.vc.g < b.vc.g) return true;
if (a.vc.g > b.vc.g) return false;
if (a.vc.b < b.vc.b) return true;
if (a.vc.b > b.vc.b) return false;
return false;
}
};
struct aiVectorCompare {
bool operator() (const aiVector3D& a, const aiVector3D& b) const {
@ -120,52 +148,37 @@ private:
}
};
struct aiColor4Compare {
bool operator() ( const aiColor4D& a, const aiColor4D& b ) const {
if ( a.r < b.r ) return true;
if ( a.r > b.r ) return false;
if ( a.g < b.g ) return true;
if ( a.g > b.g ) return false;
if ( a.b < b.b ) return true;
if ( a.b > b.b ) return false;
if ( a.a < b.a ) return true;
if ( a.a > b.a ) return false;
return false;
}
};
class vecIndexMap {
template <class T, class Compare = std::less<T>>
class indexMap {
int mNextIndex;
typedef std::map<aiVector3D, int, aiVectorCompare> dataType;
typedef std::map<T, int, Compare> dataType;
dataType vecMap;
public:
vecIndexMap()
indexMap()
: mNextIndex(1) {
// empty
}
int getIndex(const aiVector3D& vec);
void getVectors( std::vector<aiVector3D>& vecs );
int getIndex(const T& key) {
typename dataType::iterator vertIt = vecMap.find(key);
// vertex already exists, so reference it
if(vertIt != vecMap.end()){
return vertIt->second;
}
return vecMap[key] = mNextIndex++;
};
void getKeys( std::vector<T>& keys ) {
keys.resize(vecMap.size());
for(typename dataType::iterator it = vecMap.begin(); it != vecMap.end(); ++it){
keys[it->second-1] = it->first;
}
};
};
class colIndexMap {
int mNextIndex;
typedef std::map<aiColor4D, int, aiColor4Compare> dataType;
dataType colMap;
public:
colIndexMap()
: mNextIndex( 1 ) {
// empty
}
int getIndex( const aiColor4D& col );
void getColors( std::vector<aiColor4D> &colors );
};
vecIndexMap mVpMap, mVnMap, mVtMap;
colIndexMap mVcMap;
indexMap<aiVector3D, aiVectorCompare> mVnMap, mVtMap;
indexMap<vertexData, vertexDataCompare> mVpMap;
std::vector<MeshInstance> mMeshes;
// this endl() doesn't flush() the stream

View File

@ -194,16 +194,16 @@ PlyExporter::PlyExporter(const char* _filename, const aiScene* pScene, bool bina
for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) {
if (!c) {
mOutput << "property " << typeName << " r" << endl;
mOutput << "property " << typeName << " g" << endl;
mOutput << "property " << typeName << " b" << endl;
mOutput << "property " << typeName << " a" << endl;
mOutput << "property " << "uchar" << " red" << endl;
mOutput << "property " << "uchar" << " green" << endl;
mOutput << "property " << "uchar" << " blue" << endl;
mOutput << "property " << "uchar" << " alpha" << endl;
}
else {
mOutput << "property " << typeName << " r" << c << endl;
mOutput << "property " << typeName << " g" << c << endl;
mOutput << "property " << typeName << " b" << c << endl;
mOutput << "property " << typeName << " a" << c << endl;
mOutput << "property " << "uchar" << " red" << c << endl;
mOutput << "property " << "uchar" << " green" << c << endl;
mOutput << "property " << "uchar" << " blue" << c << endl;
mOutput << "property " << "uchar" << " alpha" << c << endl;
}
}
@ -288,13 +288,13 @@ void PlyExporter::WriteMeshVerts(const aiMesh* m, unsigned int components)
for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) {
if (m->HasVertexColors(c)) {
mOutput <<
" " << m->mColors[c][i].r <<
" " << m->mColors[c][i].g <<
" " << m->mColors[c][i].b <<
" " << m->mColors[c][i].a;
" " << (int)(m->mColors[c][i].r * 255) <<
" " << (int)(m->mColors[c][i].g * 255) <<
" " << (int)(m->mColors[c][i].b * 255) <<
" " << (int)(m->mColors[c][i].a * 255);
}
else {
mOutput << " -1.0 -1.0 -1.0 -1.0";
mOutput << " 0 0 0";
}
}

View File

@ -85,8 +85,6 @@ void SortByPTypeProcess::SetupProperties(const Importer* pImp)
// Update changed meshes in all nodes
void UpdateNodes(const std::vector<unsigned int>& replaceMeshIndex, aiNode* node)
{
// std::vector<unsigned int>::const_iterator it;
if (node->mNumMeshes)
{
unsigned int newSize = 0;
@ -133,10 +131,8 @@ void UpdateNodes(const std::vector<unsigned int>& replaceMeshIndex, aiNode* node
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void SortByPTypeProcess::Execute( aiScene* pScene)
{
if (!pScene->mNumMeshes)
{
void SortByPTypeProcess::Execute( aiScene* pScene) {
if ( 0 == pScene->mNumMeshes) {
ASSIMP_LOG_DEBUG("SortByPTypeProcess skipped, there are no meshes");
return;
}
@ -152,42 +148,38 @@ void SortByPTypeProcess::Execute( aiScene* pScene)
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)
{
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
aiMesh* const mesh = pScene->mMeshes[i];
ai_assert(0 != mesh->mPrimitiveTypes);
// if there's just one primitive type in the mesh there's nothing to do for us
unsigned int num = 0;
if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT)
{
if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT) {
++aiNumMeshesPerPType[0];
++num;
}
if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE)
{
if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) {
++aiNumMeshesPerPType[1];
++num;
}
if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE)
{
if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) {
++aiNumMeshesPerPType[2];
++num;
}
if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)
{
if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON) {
++aiNumMeshesPerPType[3];
++num;
}
if (1 == num)
{
if (!(configRemoveMeshes & mesh->mPrimitiveTypes))
{
*meshIdx = (unsigned int) outMeshes.size();
if (1 == num) {
if (!(configRemoveMeshes & mesh->mPrimitiveTypes)) {
*meshIdx = static_cast<unsigned int>( outMeshes.size() );
outMeshes.push_back(mesh);
} else {
delete mesh;
pScene->mMeshes[ i ] = nullptr;
bAnyChanges = true;
}
else bAnyChanges = true;
meshIdx += 4;
continue;
@ -195,14 +187,13 @@ void SortByPTypeProcess::Execute( aiScene* pScene)
bAnyChanges = true;
// reuse our current mesh arrays for the submesh
// with the largest numer of primitives
// 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 numPolyVerts = 0;
for (;pFirstFace != pLastFace; ++pFirstFace)
{
for (;pFirstFace != pLastFace; ++pFirstFace) {
if (pFirstFace->mNumIndices <= 3)
++aiNumPerPType[pFirstFace->mNumIndices-1];
else

View File

@ -890,6 +890,8 @@ unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode* n)
{
Ref<Node> node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node"));
node->name = n->mName.C_Str();
if (!n->mTransformation.IsIdentity()) {
node->matrix.isPresent = true;
CopyValue(n->mTransformation, node->matrix.value);

View File

@ -134,6 +134,30 @@ public:
}
}
// ----------------------------------------------------------------------------
/** Extract a particular vertex from a anim mesh and interleave all components */
explicit Vertex(const aiAnimMesh* msh, unsigned int idx) {
ai_assert(idx < msh->mNumVertices);
position = msh->mVertices[idx];
if (msh->HasNormals()) {
normal = msh->mNormals[idx];
}
if (msh->HasTangentsAndBitangents()) {
tangent = msh->mTangents[idx];
bitangent = msh->mBitangents[idx];
}
for (unsigned int i = 0; msh->HasTextureCoords(i); ++i) {
texcoords[i] = msh->mTextureCoords[i][idx];
}
for (unsigned int i = 0; msh->HasVertexColors(i); ++i) {
colors[i] = msh->mColors[i][idx];
}
}
public:
Vertex& operator += (const Vertex& v) {

View File

@ -1,13 +1,13 @@
g cube
v 0.0 0.0 0.0 124 110 120
v 0.0 0.0 1.0 24 0 121
v 0.0 1.0 0.0 4 0 44
v 0.0 1.0 1.0 224 0 10
v 1.0 0.0 0.0 24 200 25
v 1.0 0.0 1.0 124 10 56
v 1.0 1.0 0.0 78 10 50
v 1.0 1.0 1.0 23 0 200
v 0.0 0.0 0.0 0.48627 0.43137 0.47059
v 0.0 0.0 1.0 0.09412 0.00000 0.47451
v 0.0 1.0 0.0 0.01569 0.00000 0.17255
v 0.0 1.0 1.0 0.87843 0.00000 0.03922
v 1.0 0.0 0.0 0.09412 0.78431 0.09804
v 1.0 0.0 1.0 0.48627 0.03922 0.21961
v 1.0 1.0 0.0 0.30588 0.03922 0.19608
v 1.0 1.0 1.0 0.09020 0.00000 0.78431
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0

View File

@ -0,0 +1,30 @@
g cube
v 0.0 0.0 0.0 0.0 0.0 0.0
v 0.0 0.0 1.0 1.0 0.6 0.3
v 0.0 1.0 0.0 0.0 0.0 0.0
v 0.0 1.0 1.0 0.3 0.6 1.0
v 1.0 0.0 0.0 0.0 0.0 0.0
v 1.0 0.0 1.0 1.0 0.6 0.3
v 1.0 1.0 0.0 0.0 0.0 0.0
v 1.0 1.0 1.0 0.3 0.6 1.0
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0
vn 0.0 1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8//3
f 5//5 7//5 8//5
f 5//5 8//5 6//5
f 1//4 5//4 6//4
f 1//4 6//4 2//4
f 2//1 6//1 8//1
f 2//1 8//1 4//1

View File

@ -267,6 +267,28 @@ TEST_F( utObjImportExport, issue809_vertex_color_Test ) {
#endif // ASSIMP_BUILD_NO_EXPORT
}
TEST_F( utObjImportExport, issue1923_vertex_color_Test ) {
::Assimp::Importer importer;
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_with_vertexcolors_uni.obj", aiProcess_ValidateDataStructure );
EXPECT_NE( nullptr, scene );
scene = importer.GetOrphanedScene();
#ifndef ASSIMP_BUILD_NO_EXPORT
::Assimp::Exporter exporter;
const aiExportDataBlob* blob = exporter.ExportToBlob( scene, "obj");
EXPECT_NE( nullptr, blob );
const aiScene *sceneReImport = importer.ReadFileFromMemory( blob->data, blob->size, aiProcess_ValidateDataStructure );
EXPECT_NE( nullptr, scene );
SceneDiffer differ;
EXPECT_TRUE( differ.isEqual( scene, sceneReImport ) );
#endif // ASSIMP_BUILD_NO_EXPORT
delete scene;
}
TEST_F( utObjImportExport, issue1453_segfault ) {
static const std::string ObjModel =
"v 0.0 0.0 0.0\n"