Sfoglia il codice sorgente

Merge branch 'master' into fix_gltf2_export_componentType_error

Kim Kulling 7 anni fa
parent
commit
327a43c26f

+ 9 - 2
code/CMakeLists.txt

@@ -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})

+ 2 - 2
code/Exporter.cpp

@@ -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
 

+ 16 - 11
code/FBXConverter.cpp

@@ -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();

+ 5 - 5
code/FBXConverter.h

@@ -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() */

+ 155 - 122
code/JoinVerticesProcess.cpp

@@ -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)
+            if (!areVerticesEqual(v, uv, complex)) {
                 continue;
-            if( (uv.tangent - v.tangent).SquareLength() > squareEpsilon)
-                continue;
-            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, 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]);
         }
     }
 

+ 22 - 68
code/ObjExporter.cpp

@@ -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 {

+ 57 - 44
code/ObjExporter.h

@@ -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,66 +105,80 @@ private:
 private:
     std::string filename;
     const aiScene* const pScene;
-    std::vector<aiVector3D> vp, vn, vt;
-    std::vector<aiColor4D> vc;
 
-    struct aiVectorCompare {
-        bool operator() (const aiVector3D& a, const aiVector3D& b) const {
-            if(a.x < b.x) return true;
-            if(a.x > b.x) return false;
-            if(a.y < b.y) return true;
-            if(a.y > b.y) return false;
-            if(a.z < b.z) return true;
+    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 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;
+    struct aiVectorCompare { 
+        bool operator() (const aiVector3D& a, const aiVector3D& b) const { 
+            if(a.x < b.x) return true; 
+            if(a.x > b.x) return false; 
+            if(a.y < b.y) return true; 
+            if(a.y > b.y) return false; 
+            if(a.z < b.z) return true; 
             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 );
-    };
-
-    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 );
+        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;
+            }
+        };
     };
 
-    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

+ 13 - 13
code/PlyExporter.cpp

@@ -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";
             }
         }
 

+ 16 - 25
code/SortByPTypeProcess.cpp

@@ -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

+ 3 - 1
code/glTF2Exporter.cpp

@@ -758,7 +758,7 @@ void glTF2Exporter::ExportMeshes()
 			if (c)
 				p.attributes.color.push_back(c);
 		}
-		
+
 		/*************** Vertices indices ****************/
 		if (aim->mNumFaces > 0) {
 			std::vector<IndicesType> indices;
@@ -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);

+ 24 - 0
include/assimp/Vertex.h

@@ -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) {

+ 8 - 8
test/models/OBJ/cube_with_vertexcolors.obj

@@ -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

+ 30 - 0
test/models/OBJ/cube_with_vertexcolors_uni.obj

@@ -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

+ 22 - 0
test/unit/utObjImportExport.cpp

@@ -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"