浏览代码

Merge branch 'master' into partial-cleanup-m3d

Kim Kulling 5 年之前
父节点
当前提交
7244f53f5f

+ 8 - 1
code/FBX/FBXConverter.cpp

@@ -2088,7 +2088,14 @@ namespace Assimp {
             TrySetTextureProperties(out_mat, textures, "Maya|TEX_emissive_map|file", aiTextureType_EMISSION_COLOR, mesh);
             TrySetTextureProperties(out_mat, textures, "Maya|TEX_metallic_map|file", aiTextureType_METALNESS, mesh);
             TrySetTextureProperties(out_mat, textures, "Maya|TEX_roughness_map|file", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
-            TrySetTextureProperties(out_mat, textures, "Maya|TEX_ao_map|file", aiTextureType_AMBIENT_OCCLUSION, mesh);            
+            TrySetTextureProperties(out_mat, textures, "Maya|TEX_ao_map|file", aiTextureType_AMBIENT_OCCLUSION, mesh);
+
+            // 3DSMax PBR
+            TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|base_color_map", aiTextureType_BASE_COLOR, mesh);
+            TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|bump_map", aiTextureType_NORMAL_CAMERA, mesh);
+            TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|emission_map", aiTextureType_EMISSION_COLOR, mesh);
+            TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|metalness_map", aiTextureType_METALNESS, mesh);
+            TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|roughness_map", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
         }
 
         void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh)

+ 3 - 2
code/Importer/IFC/IFCGeometry.cpp

@@ -138,8 +138,9 @@ void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t m
             }
         }
     }
-
-    ai_assert(outer_polygon_it != end);
+	if (outer_polygon_it == end) {
+		return;
+	}
 
     const size_t outer_polygon_size = *outer_polygon_it;
     const IfcVector3& master_normal = normals[std::distance(begin, outer_polygon_it)];

+ 3 - 2
code/PostProcessing/ConvertToLHProcess.h

@@ -137,8 +137,9 @@ public:
     // -------------------------------------------------------------------
     void Execute( aiScene* pScene);
 
-protected:
-    void ProcessMesh( aiMesh* pMesh);
+public:
+    /** Some other types of post-processing require winding order flips */
+    static void ProcessMesh( aiMesh* pMesh);
 };
 
 // ---------------------------------------------------------------------------

+ 260 - 253
code/PostProcessing/OptimizeGraph.cpp

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

+ 3 - 3
code/PostProcessing/OptimizeGraph.h

@@ -75,13 +75,13 @@ public:
     ~OptimizeGraphProcess();
 
     // -------------------------------------------------------------------
-    bool IsActive( unsigned int pFlags) const;
+    bool IsActive( unsigned int pFlags) const override;
 
     // -------------------------------------------------------------------
-    void Execute( aiScene* pScene);
+    void Execute( aiScene* pScene) override;
 
     // -------------------------------------------------------------------
-    void SetupProperties(const Importer* pImp);
+    void SetupProperties(const Importer* pImp) override;
 
     // -------------------------------------------------------------------
     /** @brief Add a list of node names to be locked and not modified.

+ 566 - 606
code/PostProcessing/PretransformVertices.cpp

@@ -45,11 +45,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Implementation of the "PretransformVertices" post processing step
 */
 
-
 #include "PretransformVertices.h"
+#include "ConvertToLHProcess.h"
 #include "ProcessHelper.h"
-#include <assimp/SceneCombiner.h>
 #include <assimp/Exceptional.h>
+#include <assimp/SceneCombiner.h>
 
 using namespace Assimp;
 
@@ -59,670 +59,630 @@ using namespace Assimp;
 
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
-PretransformVertices::PretransformVertices()
-: configKeepHierarchy (false)
-, configNormalize(false)
-, configTransform(false)
-, configTransformation()
-, mConfigPointCloud( false ) {
-    // empty
+PretransformVertices::PretransformVertices() :
+		configKeepHierarchy(false),
+		configNormalize(false),
+		configTransform(false),
+		configTransformation(),
+		mConfigPointCloud(false) {
+	// empty
 }
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
 PretransformVertices::~PretransformVertices() {
-    // nothing to do here
+	// nothing to do here
 }
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
-bool PretransformVertices::IsActive( unsigned int pFlags) const
-{
-    return  (pFlags & aiProcess_PreTransformVertices) != 0;
+bool PretransformVertices::IsActive(unsigned int pFlags) const {
+	return (pFlags & aiProcess_PreTransformVertices) != 0;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Setup import configuration
-void PretransformVertices::SetupProperties(const Importer* pImp)
-{
-    // Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE,
-    // AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION
-    configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY,0));
-    configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE,0));
-    configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION,0));
+void PretransformVertices::SetupProperties(const Importer *pImp) {
+	// Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE,
+	// AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION
+	configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, 0));
+	configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, 0));
+	configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION, 0));
 
-    configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4());
+	configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4());
 
-    mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS);
+	mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Count the number of nodes
-unsigned int PretransformVertices::CountNodes( aiNode* pcNode )
-{
-    unsigned int iRet = 1;
-    for (unsigned int i = 0;i < pcNode->mNumChildren;++i)
-    {
-        iRet += CountNodes(pcNode->mChildren[i]);
-    }
-    return iRet;
+unsigned int PretransformVertices::CountNodes(const aiNode *pcNode) const {
+	unsigned int iRet = 1;
+	for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
+		iRet += CountNodes(pcNode->mChildren[i]);
+	}
+	return iRet;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Get a bitwise combination identifying the vertex format of a mesh
-unsigned int PretransformVertices::GetMeshVFormat( aiMesh* pcMesh )
-{
-    // the vertex format is stored in aiMesh::mBones for later retrieval.
-    // there isn't a good reason to compute it a few hundred times
-    // from scratch. The pointer is unused as animations are lost
-    // during PretransformVertices.
-    if (pcMesh->mBones)
-        return (unsigned int)(uint64_t)pcMesh->mBones;
-
-
-    const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
-
-    // store the value for later use
-    pcMesh->mBones = (aiBone**)(uint64_t)iRet;
-    return iRet;
+unsigned int PretransformVertices::GetMeshVFormat(aiMesh *pcMesh) const {
+	// the vertex format is stored in aiMesh::mBones for later retrieval.
+	// there isn't a good reason to compute it a few hundred times
+	// from scratch. The pointer is unused as animations are lost
+	// during PretransformVertices.
+	if (pcMesh->mBones)
+		return (unsigned int)(uint64_t)pcMesh->mBones;
+
+	const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
+
+	// store the value for later use
+	pcMesh->mBones = (aiBone **)(uint64_t)iRet;
+	return iRet;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Count the number of vertices in the whole scene and a given
 // material index
-void PretransformVertices::CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
-    unsigned int iVFormat, unsigned int* piFaces, unsigned int* piVertices)
-{
-    for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
-    {
-        aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
-        if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
-        {
-            *piVertices += pcMesh->mNumVertices;
-            *piFaces += pcMesh->mNumFaces;
-        }
-    }
-    for (unsigned int i = 0;i < pcNode->mNumChildren;++i)
-    {
-        CountVerticesAndFaces(pcScene,pcNode->mChildren[i],iMat,
-            iVFormat,piFaces,piVertices);
-    }
+void PretransformVertices::CountVerticesAndFaces(const aiScene *pcScene, const aiNode *pcNode, unsigned int iMat,
+		unsigned int iVFormat, unsigned int *piFaces, unsigned int *piVertices) const {
+	for (unsigned int i = 0; i < pcNode->mNumMeshes; ++i) {
+		aiMesh *pcMesh = pcScene->mMeshes[pcNode->mMeshes[i]];
+		if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) {
+			*piVertices += pcMesh->mNumVertices;
+			*piFaces += pcMesh->mNumFaces;
+		}
+	}
+	for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
+		CountVerticesAndFaces(pcScene, pcNode->mChildren[i], iMat,
+				iVFormat, piFaces, piVertices);
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
 // Collect vertex/face data
-void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
-    unsigned int iVFormat, aiMesh* pcMeshOut,
-    unsigned int aiCurrent[2], unsigned int* num_refs)
-{
-    // No need to multiply if there's no transformation
-    const bool identity = pcNode->mTransformation.IsIdentity();
-    for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
-    {
-        aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
-        if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
-        {
-            // Decrement mesh reference counter
-            unsigned int& num_ref = num_refs[pcNode->mMeshes[i]];
-            ai_assert(0 != num_ref);
-            --num_ref;
-            // Save the name of the last mesh
-            if (num_ref==0)
-            {
-                pcMeshOut->mName = pcMesh->mName;
-            }
-
-            if (identity)   {
-                // copy positions without modifying them
-                ::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX],
-                    pcMesh->mVertices,
-                    pcMesh->mNumVertices * sizeof(aiVector3D));
-
-                if (iVFormat & 0x2) {
-                    // copy normals without modifying them
-                    ::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX],
-                        pcMesh->mNormals,
-                        pcMesh->mNumVertices * sizeof(aiVector3D));
-                }
-                if (iVFormat & 0x4)
-                {
-                    // copy tangents without modifying them
-                    ::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX],
-                        pcMesh->mTangents,
-                        pcMesh->mNumVertices * sizeof(aiVector3D));
-                    // copy bitangents without modifying them
-                    ::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX],
-                        pcMesh->mBitangents,
-                        pcMesh->mNumVertices * sizeof(aiVector3D));
-                }
-            }
-            else
-            {
-                // copy positions, transform them to worldspace
-                for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)  {
-                    pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n];
-                }
-                aiMatrix4x4 mWorldIT = pcNode->mTransformation;
-                mWorldIT.Inverse().Transpose();
-
-                // TODO: implement Inverse() for aiMatrix3x3
-                aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
-
-                if (iVFormat & 0x2)
-                {
-                    // copy normals, transform them to worldspace
-                    for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)  {
-                        pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] =
-                            (m * pcMesh->mNormals[n]).Normalize();
-                    }
-                }
-                if (iVFormat & 0x4)
-                {
-                    // copy tangents and bitangents, transform them to worldspace
-                    for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)  {
-                        pcMeshOut->mTangents  [aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mTangents[n]).Normalize();
-                        pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mBitangents[n]).Normalize();
-                    }
-                }
-            }
-            unsigned int p = 0;
-            while (iVFormat & (0x100 << p))
-            {
-                // copy texture coordinates
-                memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX],
-                    pcMesh->mTextureCoords[p],
-                    pcMesh->mNumVertices * sizeof(aiVector3D));
-                ++p;
-            }
-            p = 0;
-            while (iVFormat & (0x1000000 << p))
-            {
-                // copy vertex colors
-                memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX],
-                    pcMesh->mColors[p],
-                    pcMesh->mNumVertices * sizeof(aiColor4D));
-                ++p;
-            }
-            // now we need to copy all faces. since we will delete the source mesh afterwards,
-            // we don't need to reallocate the array of indices except if this mesh is
-            // referenced multiple times.
-            for (unsigned int planck = 0;planck < pcMesh->mNumFaces;++planck)
-            {
-                aiFace& f_src = pcMesh->mFaces[planck];
-                aiFace& f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck];
-
-                const unsigned int num_idx = f_src.mNumIndices;
-
-                f_dst.mNumIndices = num_idx;
-
-                unsigned int* pi;
-                if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */
-                    pi = f_dst.mIndices = f_src.mIndices;
-
-                    // offset all vertex indices
-                    for (unsigned int hahn = 0; hahn < num_idx;++hahn){
-                        pi[hahn] += aiCurrent[AI_PTVS_VERTEX];
-                    }
-                }
-                else {
-                    pi = f_dst.mIndices = new unsigned int[num_idx];
-
-                    // copy and offset all vertex indices
-                    for (unsigned int hahn = 0; hahn < num_idx;++hahn){
-                        pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX];
-                    }
-                }
-
-                // Update the mPrimitiveTypes member of the mesh
-                switch (pcMesh->mFaces[planck].mNumIndices)
-                {
-                case 0x1:
-                    pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT;
-                    break;
-                case 0x2:
-                    pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_LINE;
-                    break;
-                case 0x3:
-                    pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
-                    break;
-                default:
-                    pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
-                    break;
-                };
-            }
-            aiCurrent[AI_PTVS_VERTEX] += pcMesh->mNumVertices;
-            aiCurrent[AI_PTVS_FACE]   += pcMesh->mNumFaces;
-        }
-    }
-
-    // append all children of us
-    for (unsigned int i = 0;i < pcNode->mNumChildren;++i) {
-        CollectData(pcScene,pcNode->mChildren[i],iMat,
-            iVFormat,pcMeshOut,aiCurrent,num_refs);
-    }
+void PretransformVertices::CollectData(const aiScene *pcScene, const aiNode *pcNode, unsigned int iMat,
+		unsigned int iVFormat, aiMesh *pcMeshOut,
+		unsigned int aiCurrent[2], unsigned int *num_refs) const {
+	// No need to multiply if there's no transformation
+	const bool identity = pcNode->mTransformation.IsIdentity();
+	for (unsigned int i = 0; i < pcNode->mNumMeshes; ++i) {
+		aiMesh *pcMesh = pcScene->mMeshes[pcNode->mMeshes[i]];
+		if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) {
+			// Decrement mesh reference counter
+			unsigned int &num_ref = num_refs[pcNode->mMeshes[i]];
+			ai_assert(0 != num_ref);
+			--num_ref;
+			// Save the name of the last mesh
+			if (num_ref == 0) {
+				pcMeshOut->mName = pcMesh->mName;
+			}
+
+			if (identity) {
+				// copy positions without modifying them
+				::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX],
+						pcMesh->mVertices,
+						pcMesh->mNumVertices * sizeof(aiVector3D));
+
+				if (iVFormat & 0x2) {
+					// copy normals without modifying them
+					::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX],
+							pcMesh->mNormals,
+							pcMesh->mNumVertices * sizeof(aiVector3D));
+				}
+				if (iVFormat & 0x4) {
+					// copy tangents without modifying them
+					::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX],
+							pcMesh->mTangents,
+							pcMesh->mNumVertices * sizeof(aiVector3D));
+					// copy bitangents without modifying them
+					::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX],
+							pcMesh->mBitangents,
+							pcMesh->mNumVertices * sizeof(aiVector3D));
+				}
+			} else {
+				// copy positions, transform them to worldspace
+				for (unsigned int n = 0; n < pcMesh->mNumVertices; ++n) {
+					pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX] + n] = pcNode->mTransformation * pcMesh->mVertices[n];
+				}
+				aiMatrix4x4 mWorldIT = pcNode->mTransformation;
+				mWorldIT.Inverse().Transpose();
+
+				// TODO: implement Inverse() for aiMatrix3x3
+				aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
+
+				if (iVFormat & 0x2) {
+					// copy normals, transform them to worldspace
+					for (unsigned int n = 0; n < pcMesh->mNumVertices; ++n) {
+						pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX] + n] =
+								(m * pcMesh->mNormals[n]).Normalize();
+					}
+				}
+				if (iVFormat & 0x4) {
+					// copy tangents and bitangents, transform them to worldspace
+					for (unsigned int n = 0; n < pcMesh->mNumVertices; ++n) {
+						pcMeshOut->mTangents[aiCurrent[AI_PTVS_VERTEX] + n] = (m * pcMesh->mTangents[n]).Normalize();
+						pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX] + n] = (m * pcMesh->mBitangents[n]).Normalize();
+					}
+				}
+			}
+			unsigned int p = 0;
+			while (iVFormat & (0x100 << p)) {
+				// copy texture coordinates
+				memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX],
+						pcMesh->mTextureCoords[p],
+						pcMesh->mNumVertices * sizeof(aiVector3D));
+				++p;
+			}
+			p = 0;
+			while (iVFormat & (0x1000000 << p)) {
+				// copy vertex colors
+				memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX],
+						pcMesh->mColors[p],
+						pcMesh->mNumVertices * sizeof(aiColor4D));
+				++p;
+			}
+			// now we need to copy all faces. since we will delete the source mesh afterwards,
+			// we don't need to reallocate the array of indices except if this mesh is
+			// referenced multiple times.
+			for (unsigned int planck = 0; planck < pcMesh->mNumFaces; ++planck) {
+				aiFace &f_src = pcMesh->mFaces[planck];
+				aiFace &f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE] + planck];
+
+				const unsigned int num_idx = f_src.mNumIndices;
+
+				f_dst.mNumIndices = num_idx;
+
+				unsigned int *pi;
+				if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */
+					pi = f_dst.mIndices = f_src.mIndices;
+
+					// offset all vertex indices
+					for (unsigned int hahn = 0; hahn < num_idx; ++hahn) {
+						pi[hahn] += aiCurrent[AI_PTVS_VERTEX];
+					}
+				} else {
+					pi = f_dst.mIndices = new unsigned int[num_idx];
+
+					// copy and offset all vertex indices
+					for (unsigned int hahn = 0; hahn < num_idx; ++hahn) {
+						pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX];
+					}
+				}
+
+				// Update the mPrimitiveTypes member of the mesh
+				switch (pcMesh->mFaces[planck].mNumIndices) {
+					case 0x1:
+						pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT;
+						break;
+					case 0x2:
+						pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_LINE;
+						break;
+					case 0x3:
+						pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+						break;
+					default:
+						pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+						break;
+				};
+			}
+			aiCurrent[AI_PTVS_VERTEX] += pcMesh->mNumVertices;
+			aiCurrent[AI_PTVS_FACE] += pcMesh->mNumFaces;
+		}
+	}
+
+	// append all children of us
+	for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
+		CollectData(pcScene, pcNode->mChildren[i], iMat,
+				iVFormat, pcMeshOut, aiCurrent, num_refs);
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
 // Get a list of all vertex formats that occur for a given material index
 // The output list contains duplicate elements
-void PretransformVertices::GetVFormatList( aiScene* pcScene, unsigned int iMat,
-    std::list<unsigned int>& aiOut)
-{
-    for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
-    {
-        aiMesh* pcMesh = pcScene->mMeshes[ i ];
-        if (iMat == pcMesh->mMaterialIndex) {
-            aiOut.push_back(GetMeshVFormat(pcMesh));
-        }
-    }
+void PretransformVertices::GetVFormatList(const aiScene *pcScene, unsigned int iMat,
+		std::list<unsigned int> &aiOut) const {
+	for (unsigned int i = 0; i < pcScene->mNumMeshes; ++i) {
+		aiMesh *pcMesh = pcScene->mMeshes[i];
+		if (iMat == pcMesh->mMaterialIndex) {
+			aiOut.push_back(GetMeshVFormat(pcMesh));
+		}
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
 // Compute the absolute transformation matrices of each node
-void PretransformVertices::ComputeAbsoluteTransform( aiNode* pcNode )
-{
-    if (pcNode->mParent)    {
-        pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation;
-    }
-
-    for (unsigned int i = 0;i < pcNode->mNumChildren;++i)   {
-        ComputeAbsoluteTransform(pcNode->mChildren[i]);
-    }
+void PretransformVertices::ComputeAbsoluteTransform(aiNode *pcNode) {
+	if (pcNode->mParent) {
+		pcNode->mTransformation = pcNode->mParent->mTransformation * pcNode->mTransformation;
+	}
+
+	for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
+		ComputeAbsoluteTransform(pcNode->mChildren[i]);
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
 // Apply the node transformation to a mesh
-void PretransformVertices::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)
-{
-    // Check whether we need to transform the coordinates at all
-    if (!mat.IsIdentity()) {
-
-        if (mesh->HasPositions()) {
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                mesh->mVertices[i] = mat * mesh->mVertices[i];
-            }
-        }
-        if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
-            aiMatrix4x4 mWorldIT = mat;
-            mWorldIT.Inverse().Transpose();
-
-            // TODO: implement Inverse() for aiMatrix3x3
-            aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
-
-            if (mesh->HasNormals()) {
-                for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                    mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
-                }
-            }
-            if (mesh->HasTangentsAndBitangents()) {
-                for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                    mesh->mTangents[i]   = (m * mesh->mTangents[i]).Normalize();
-                    mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
-                }
-            }
-        }
-    }
+void PretransformVertices::ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat) const {
+	// Check whether we need to transform the coordinates at all
+	if (!mat.IsIdentity()) {
+
+		// Check for odd negative scale (mirror)
+		if (mesh->HasFaces() && mat.Determinant() < 0) {
+			// Reverse the mesh face winding order
+			FlipWindingOrderProcess::ProcessMesh(mesh);
+		}
+
+		// Update positions
+		if (mesh->HasPositions()) {
+			for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+				mesh->mVertices[i] = mat * mesh->mVertices[i];
+			}
+		}
+
+		// Update normals and tangents
+		if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
+			const aiMatrix3x3 m = aiMatrix3x3(mat).Inverse().Transpose();
+
+			if (mesh->HasNormals()) {
+				for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+					mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
+				}
+			}
+			if (mesh->HasTangentsAndBitangents()) {
+				for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+					mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
+					mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
+				}
+			}
+		}
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
 // Simple routine to build meshes in worldspace, no further optimization
-void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in,
-    unsigned int numIn, aiNode* node)
-{
-    // NOTE:
-    //  aiMesh::mNumBones store original source mesh, or UINT_MAX if not a copy
-    //  aiMesh::mBones store reference to abs. transform we multiplied with
-
-    // process meshes
-    for (unsigned int i = 0; i < node->mNumMeshes;++i) {
-        aiMesh* mesh = in[node->mMeshes[i]];
-
-        // check whether we can operate on this mesh
-        if (!mesh->mBones || *reinterpret_cast<aiMatrix4x4*>(mesh->mBones) == node->mTransformation) {
-            // yes, we can.
-            mesh->mBones = reinterpret_cast<aiBone**> (&node->mTransformation);
-            mesh->mNumBones = UINT_MAX;
-        }
-        else {
-
-            // try to find us in the list of newly created meshes
-            for (unsigned int n = 0; n < out.size(); ++n) {
-                aiMesh* ctz = out[n];
-                if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4*>(ctz->mBones) ==  node->mTransformation) {
-
-                    // ok, use this one. Update node mesh index
-                    node->mMeshes[i] = numIn + n;
-                }
-            }
-            if (node->mMeshes[i] < numIn) {
-                // Worst case. Need to operate on a full copy of the mesh
-                ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms");
-                aiMesh* ntz;
-
-                const unsigned int tmp = mesh->mNumBones; //
-                mesh->mNumBones = 0;
-                SceneCombiner::Copy(&ntz,mesh);
-                mesh->mNumBones = tmp;
-
-                ntz->mNumBones = node->mMeshes[i];
-                ntz->mBones = reinterpret_cast<aiBone**> (&node->mTransformation);
-
-                out.push_back(ntz);
-
-                node->mMeshes[i] = static_cast<unsigned int>(numIn + out.size() - 1);
-            }
-        }
-    }
-
-    // call children
-    for (unsigned int i = 0; i < node->mNumChildren;++i)
-        BuildWCSMeshes(out,in,numIn,node->mChildren[i]);
+void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **in,
+		unsigned int numIn, aiNode *node) const {
+	// NOTE:
+	//  aiMesh::mNumBones store original source mesh, or UINT_MAX if not a copy
+	//  aiMesh::mBones store reference to abs. transform we multiplied with
+
+	// process meshes
+	for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+		aiMesh *mesh = in[node->mMeshes[i]];
+
+		// check whether we can operate on this mesh
+		if (!mesh->mBones || *reinterpret_cast<aiMatrix4x4 *>(mesh->mBones) == node->mTransformation) {
+			// yes, we can.
+			mesh->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
+			mesh->mNumBones = UINT_MAX;
+		} else {
+
+			// try to find us in the list of newly created meshes
+			for (unsigned int n = 0; n < out.size(); ++n) {
+				aiMesh *ctz = out[n];
+				if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4 *>(ctz->mBones) == node->mTransformation) {
+
+					// ok, use this one. Update node mesh index
+					node->mMeshes[i] = numIn + n;
+				}
+			}
+			if (node->mMeshes[i] < numIn) {
+				// Worst case. Need to operate on a full copy of the mesh
+				ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms");
+				aiMesh *ntz;
+
+				const unsigned int tmp = mesh->mNumBones; //
+				mesh->mNumBones = 0;
+				SceneCombiner::Copy(&ntz, mesh);
+				mesh->mNumBones = tmp;
+
+				ntz->mNumBones = node->mMeshes[i];
+				ntz->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
+
+				out.push_back(ntz);
+
+				node->mMeshes[i] = static_cast<unsigned int>(numIn + out.size() - 1);
+			}
+		}
+	}
+
+	// call children
+	for (unsigned int i = 0; i < node->mNumChildren; ++i)
+		BuildWCSMeshes(out, in, numIn, node->mChildren[i]);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Reset transformation matrices to identity
-void PretransformVertices::MakeIdentityTransform(aiNode* nd)
-{
-    nd->mTransformation = aiMatrix4x4();
+void PretransformVertices::MakeIdentityTransform(aiNode *nd) const {
+	nd->mTransformation = aiMatrix4x4();
 
-    // call children
-    for (unsigned int i = 0; i < nd->mNumChildren;++i)
-        MakeIdentityTransform(nd->mChildren[i]);
+	// call children
+	for (unsigned int i = 0; i < nd->mNumChildren; ++i)
+		MakeIdentityTransform(nd->mChildren[i]);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Build reference counters for all meshes
-void PretransformVertices::BuildMeshRefCountArray(aiNode* nd, unsigned int * refs)
-{
-    for (unsigned int i = 0; i< nd->mNumMeshes;++i)
-        refs[nd->mMeshes[i]]++;
-
-    // call children
-    for (unsigned int i = 0; i < nd->mNumChildren;++i)
-        BuildMeshRefCountArray(nd->mChildren[i],refs);
+void PretransformVertices::BuildMeshRefCountArray(const aiNode *nd, unsigned int *refs) const {
+	for (unsigned int i = 0; i < nd->mNumMeshes; ++i)
+		refs[nd->mMeshes[i]]++;
+
+	// call children
+	for (unsigned int i = 0; i < nd->mNumChildren; ++i)
+		BuildMeshRefCountArray(nd->mChildren[i], refs);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
-void PretransformVertices::Execute( aiScene* pScene)
-{
-    ASSIMP_LOG_DEBUG("PretransformVerticesProcess begin");
-
-    // Return immediately if we have no meshes
-    if (!pScene->mNumMeshes)
-        return;
-
-    const unsigned int iOldMeshes = pScene->mNumMeshes;
-    const unsigned int iOldAnimationChannels = pScene->mNumAnimations;
-    const unsigned int iOldNodes = CountNodes(pScene->mRootNode);
-
-    if(configTransform) {
-        pScene->mRootNode->mTransformation = configTransformation;
-    }
-
-    // first compute absolute transformation matrices for all nodes
-    ComputeAbsoluteTransform(pScene->mRootNode);
-
-    // Delete aiMesh::mBones for all meshes. The bones are
-    // removed during this step and we need the pointer as
-    // temporary storage
-    for (unsigned int i = 0; i < pScene->mNumMeshes;++i)    {
-        aiMesh* mesh = pScene->mMeshes[i];
-
-        for (unsigned int a = 0; a < mesh->mNumBones;++a)
-            delete mesh->mBones[a];
-
-        delete[] mesh->mBones;
-        mesh->mBones = NULL;
-    }
-
-    // now build a list of output meshes
-    std::vector<aiMesh*> apcOutMeshes;
-
-    // Keep scene hierarchy? It's an easy job in this case ...
-    // we go on and transform all meshes, if one is referenced by nodes
-    // with different absolute transformations a depth copy of the mesh
-    // is required.
-    if( configKeepHierarchy ) {
-
-        // Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
-        BuildWCSMeshes(apcOutMeshes,pScene->mMeshes,pScene->mNumMeshes, pScene->mRootNode);
-
-        // ... if new meshes have been generated, append them to the end of the scene
-        if (apcOutMeshes.size() > 0) {
-            aiMesh** npp = new aiMesh*[pScene->mNumMeshes + apcOutMeshes.size()];
-
-            memcpy(npp,pScene->mMeshes,sizeof(aiMesh*)*pScene->mNumMeshes);
-            memcpy(npp+pScene->mNumMeshes,&apcOutMeshes[0],sizeof(aiMesh*)*apcOutMeshes.size());
-
-            pScene->mNumMeshes  += static_cast<unsigned int>(apcOutMeshes.size());
-            delete[] pScene->mMeshes; pScene->mMeshes = npp;
-        }
-
-        // now iterate through all meshes and transform them to worldspace
-        for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
-            ApplyTransform(pScene->mMeshes[i],*reinterpret_cast<aiMatrix4x4*>( pScene->mMeshes[i]->mBones ));
-
-            // prevent improper destruction
-            pScene->mMeshes[i]->mBones    = NULL;
-            pScene->mMeshes[i]->mNumBones = 0;
-        }
-    } else {
-        apcOutMeshes.reserve(pScene->mNumMaterials<<1u);
-        std::list<unsigned int> aiVFormats;
-
-        std::vector<unsigned int> s(pScene->mNumMeshes,0);
-        BuildMeshRefCountArray(pScene->mRootNode,&s[0]);
-
-        for (unsigned int i = 0; i < pScene->mNumMaterials;++i)     {
-            // get the list of all vertex formats for this material
-            aiVFormats.clear();
-            GetVFormatList(pScene,i,aiVFormats);
-            aiVFormats.sort();
-            aiVFormats.unique();
-            for (std::list<unsigned int>::const_iterator j =  aiVFormats.begin();j != aiVFormats.end();++j) {
-                unsigned int iVertices = 0;
-                unsigned int iFaces = 0;
-                CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices);
-                if (0 != iFaces && 0 != iVertices)
-                {
-                    apcOutMeshes.push_back(new aiMesh());
-                    aiMesh* pcMesh = apcOutMeshes.back();
-                    pcMesh->mNumFaces = iFaces;
-                    pcMesh->mNumVertices = iVertices;
-                    pcMesh->mFaces = new aiFace[iFaces];
-                    pcMesh->mVertices = new aiVector3D[iVertices];
-                    pcMesh->mMaterialIndex = i;
-                    if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices];
-                    if ((*j) & 0x4)
-                    {
-                        pcMesh->mTangents    = new aiVector3D[iVertices];
-                        pcMesh->mBitangents  = new aiVector3D[iVertices];
-                    }
-                    iFaces = 0;
-                    while ((*j) & (0x100 << iFaces))
-                    {
-                        pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
-                        if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3;
-                        else pcMesh->mNumUVComponents[iFaces] = 2;
-                        iFaces++;
-                    }
-                    iFaces = 0;
-                    while ((*j) & (0x1000000 << iFaces))
-                        pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
-
-                    // fill the mesh ...
-                    unsigned int aiTemp[2] = {0,0};
-                    CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp,&s[0]);
-                }
-            }
-        }
-
-        // If no meshes are referenced in the node graph it is possible that we get no output meshes.
-        if (apcOutMeshes.empty()) {
-            
-            throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes");
-        }
-        else
-        {
-            // now delete all meshes in the scene and build a new mesh list
-            for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
-            {
-                aiMesh* mesh = pScene->mMeshes[i];
-                mesh->mNumBones = 0;
-                mesh->mBones    = NULL;
-
-                // we're reusing the face index arrays. avoid destruction
-                for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
-                    mesh->mFaces[a].mNumIndices = 0;
-                    mesh->mFaces[a].mIndices = NULL;
-                }
-
-                delete mesh;
-
-                // Invalidate the contents of the old mesh array. We will most
-                // likely have less output meshes now, so the last entries of
-                // the mesh array are not overridden. We set them to NULL to
-                // make sure the developer gets notified when his application
-                // attempts to access these fields ...
-                mesh = NULL;
-            }
-
-            // It is impossible that we have more output meshes than
-            // input meshes, so we can easily reuse the old mesh array
-            pScene->mNumMeshes = (unsigned int)apcOutMeshes.size();
-            for (unsigned int i = 0; i < pScene->mNumMeshes;++i) {
-                pScene->mMeshes[i] = apcOutMeshes[i];
-            }
-        }
-    }
-
-    // remove all animations from the scene
-    for (unsigned int i = 0; i < pScene->mNumAnimations;++i)
-        delete pScene->mAnimations[i];
-    delete[] pScene->mAnimations;
-
-    pScene->mAnimations    = NULL;
-    pScene->mNumAnimations = 0;
-
-    // --- we need to keep all cameras and lights
-    for (unsigned int i = 0; i < pScene->mNumCameras;++i)
-    {
-        aiCamera* cam = pScene->mCameras[i];
-        const aiNode* nd = pScene->mRootNode->FindNode(cam->mName);
-        ai_assert(NULL != nd);
-
-        // multiply all properties of the camera with the absolute
-        // transformation of the corresponding node
-        cam->mPosition = nd->mTransformation * cam->mPosition;
-        cam->mLookAt   = aiMatrix3x3( nd->mTransformation ) * cam->mLookAt;
-        cam->mUp       = aiMatrix3x3( nd->mTransformation ) * cam->mUp;
-    }
-
-    for (unsigned int i = 0; i < pScene->mNumLights;++i)
-    {
-        aiLight* l = pScene->mLights[i];
-        const aiNode* nd = pScene->mRootNode->FindNode(l->mName);
-        ai_assert(NULL != nd);
-
-        // multiply all properties of the camera with the absolute
-        // transformation of the corresponding node
-        l->mPosition   = nd->mTransformation * l->mPosition;
-        l->mDirection  = aiMatrix3x3( nd->mTransformation ) * l->mDirection;
-        l->mUp         = aiMatrix3x3( nd->mTransformation ) * l->mUp;
-    }
-
-    if( !configKeepHierarchy ) {
-
-        // now delete all nodes in the scene and build a new
-        // flat node graph with a root node and some level 1 children
-        aiNode* newRoot = new aiNode();
-        newRoot->mName = pScene->mRootNode->mName;
-        delete pScene->mRootNode;
-        pScene->mRootNode = newRoot;
-
-        if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras)
-        {
-            pScene->mRootNode->mNumMeshes = 1;
-            pScene->mRootNode->mMeshes = new unsigned int[1];
-            pScene->mRootNode->mMeshes[0] = 0;
-        }
-        else
-        {
-            pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras;
-            aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
-
-            // generate mesh nodes
-            for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes)
-            {
-                aiNode* pcNode = new aiNode();
-                *nodes = pcNode;
-                pcNode->mParent = pScene->mRootNode;
-                pcNode->mName = pScene->mMeshes[i]->mName;
-
-                // setup mesh indices
-                pcNode->mNumMeshes = 1;
-                pcNode->mMeshes = new unsigned int[1];
-                pcNode->mMeshes[0] = i;
-            }
-            // generate light nodes
-            for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes)
-            {
-                aiNode* pcNode = new aiNode();
-                *nodes = pcNode;
-                pcNode->mParent = pScene->mRootNode;
-                pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u",i);
-                pScene->mLights[i]->mName = pcNode->mName;
-            }
-            // generate camera nodes
-            for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes)
-            {
-                aiNode* pcNode = new aiNode();
-                *nodes = pcNode;
-                pcNode->mParent = pScene->mRootNode;
-                pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"cam_%u",i);
-                pScene->mCameras[i]->mName = pcNode->mName;
-            }
-        }
-    }
-    else {
-        // ... and finally set the transformation matrix of all nodes to identity
-        MakeIdentityTransform(pScene->mRootNode);
-    }
-
-    if (configNormalize) {
-        // compute the boundary of all meshes
-        aiVector3D min,max;
-        MinMaxChooser<aiVector3D> ()(min,max);
-
-        for (unsigned int a = 0; a <  pScene->mNumMeshes; ++a) {
-            aiMesh* m = pScene->mMeshes[a];
-            for (unsigned int i = 0; i < m->mNumVertices;++i) {
-                min = std::min(m->mVertices[i],min);
-                max = std::max(m->mVertices[i],max);
-            }
-        }
-
-        // find the dominant axis
-        aiVector3D d = max-min;
-        const ai_real div = std::max(d.x,std::max(d.y,d.z))*ai_real( 0.5);
-
-        d = min + d * (ai_real)0.5;
-        for (unsigned int a = 0; a <  pScene->mNumMeshes; ++a) {
-            aiMesh* m = pScene->mMeshes[a];
-            for (unsigned int i = 0; i < m->mNumVertices;++i) {
-                m->mVertices[i] = (m->mVertices[i]-d)/div;
-            }
-        }
-    }
-
-    // print statistics
-    if (!DefaultLogger::isNullLogger()) {
-        ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished");
-
-        ASSIMP_LOG_INFO_F("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (", 
-            CountNodes(pScene->mRootNode) ," output nodes)" );
-        ASSIMP_LOG_INFO_F("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras." );
-        ASSIMP_LOG_INFO_F("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")");
-    }
+void PretransformVertices::Execute(aiScene *pScene) {
+	ASSIMP_LOG_DEBUG("PretransformVerticesProcess begin");
+
+	// Return immediately if we have no meshes
+	if (!pScene->mNumMeshes)
+		return;
+
+	const unsigned int iOldMeshes = pScene->mNumMeshes;
+	const unsigned int iOldAnimationChannels = pScene->mNumAnimations;
+	const unsigned int iOldNodes = CountNodes(pScene->mRootNode);
+
+	if (configTransform) {
+		pScene->mRootNode->mTransformation = configTransformation;
+	}
+
+	// first compute absolute transformation matrices for all nodes
+	ComputeAbsoluteTransform(pScene->mRootNode);
+
+	// Delete aiMesh::mBones for all meshes. The bones are
+	// removed during this step and we need the pointer as
+	// temporary storage
+	for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+		aiMesh *mesh = pScene->mMeshes[i];
+
+		for (unsigned int a = 0; a < mesh->mNumBones; ++a)
+			delete mesh->mBones[a];
+
+		delete[] mesh->mBones;
+		mesh->mBones = NULL;
+	}
+
+	// now build a list of output meshes
+	std::vector<aiMesh *> apcOutMeshes;
+
+	// Keep scene hierarchy? It's an easy job in this case ...
+	// we go on and transform all meshes, if one is referenced by nodes
+	// with different absolute transformations a depth copy of the mesh
+	// is required.
+	if (configKeepHierarchy) {
+
+		// Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
+		BuildWCSMeshes(apcOutMeshes, pScene->mMeshes, pScene->mNumMeshes, pScene->mRootNode);
+
+		// ... if new meshes have been generated, append them to the end of the scene
+		if (apcOutMeshes.size() > 0) {
+			aiMesh **npp = new aiMesh *[pScene->mNumMeshes + apcOutMeshes.size()];
+
+			memcpy(npp, pScene->mMeshes, sizeof(aiMesh *) * pScene->mNumMeshes);
+			memcpy(npp + pScene->mNumMeshes, &apcOutMeshes[0], sizeof(aiMesh *) * apcOutMeshes.size());
+
+			pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size());
+			delete[] pScene->mMeshes;
+			pScene->mMeshes = npp;
+		}
+
+		// now iterate through all meshes and transform them to worldspace
+		for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+			ApplyTransform(pScene->mMeshes[i], *reinterpret_cast<aiMatrix4x4 *>(pScene->mMeshes[i]->mBones));
+
+			// prevent improper destruction
+			pScene->mMeshes[i]->mBones = NULL;
+			pScene->mMeshes[i]->mNumBones = 0;
+		}
+	} else {
+		apcOutMeshes.reserve(pScene->mNumMaterials << 1u);
+		std::list<unsigned int> aiVFormats;
+
+		std::vector<unsigned int> s(pScene->mNumMeshes, 0);
+		BuildMeshRefCountArray(pScene->mRootNode, &s[0]);
+
+		for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) {
+			// get the list of all vertex formats for this material
+			aiVFormats.clear();
+			GetVFormatList(pScene, i, aiVFormats);
+			aiVFormats.sort();
+			aiVFormats.unique();
+			for (std::list<unsigned int>::const_iterator j = aiVFormats.begin(); j != aiVFormats.end(); ++j) {
+				unsigned int iVertices = 0;
+				unsigned int iFaces = 0;
+				CountVerticesAndFaces(pScene, pScene->mRootNode, i, *j, &iFaces, &iVertices);
+				if (0 != iFaces && 0 != iVertices) {
+					apcOutMeshes.push_back(new aiMesh());
+					aiMesh *pcMesh = apcOutMeshes.back();
+					pcMesh->mNumFaces = iFaces;
+					pcMesh->mNumVertices = iVertices;
+					pcMesh->mFaces = new aiFace[iFaces];
+					pcMesh->mVertices = new aiVector3D[iVertices];
+					pcMesh->mMaterialIndex = i;
+					if ((*j) & 0x2) pcMesh->mNormals = new aiVector3D[iVertices];
+					if ((*j) & 0x4) {
+						pcMesh->mTangents = new aiVector3D[iVertices];
+						pcMesh->mBitangents = new aiVector3D[iVertices];
+					}
+					iFaces = 0;
+					while ((*j) & (0x100 << iFaces)) {
+						pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
+						if ((*j) & (0x10000 << iFaces))
+							pcMesh->mNumUVComponents[iFaces] = 3;
+						else
+							pcMesh->mNumUVComponents[iFaces] = 2;
+						iFaces++;
+					}
+					iFaces = 0;
+					while ((*j) & (0x1000000 << iFaces))
+						pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
+
+					// fill the mesh ...
+					unsigned int aiTemp[2] = { 0, 0 };
+					CollectData(pScene, pScene->mRootNode, i, *j, pcMesh, aiTemp, &s[0]);
+				}
+			}
+		}
+
+		// If no meshes are referenced in the node graph it is possible that we get no output meshes.
+		if (apcOutMeshes.empty()) {
+
+			throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes");
+		} else {
+			// now delete all meshes in the scene and build a new mesh list
+			for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+				aiMesh *mesh = pScene->mMeshes[i];
+				mesh->mNumBones = 0;
+				mesh->mBones = NULL;
+
+				// we're reusing the face index arrays. avoid destruction
+				for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
+					mesh->mFaces[a].mNumIndices = 0;
+					mesh->mFaces[a].mIndices = NULL;
+				}
+
+				delete mesh;
+
+				// Invalidate the contents of the old mesh array. We will most
+				// likely have less output meshes now, so the last entries of
+				// the mesh array are not overridden. We set them to NULL to
+				// make sure the developer gets notified when his application
+				// attempts to access these fields ...
+				mesh = NULL;
+			}
+
+			// It is impossible that we have more output meshes than
+			// input meshes, so we can easily reuse the old mesh array
+			pScene->mNumMeshes = (unsigned int)apcOutMeshes.size();
+			for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+				pScene->mMeshes[i] = apcOutMeshes[i];
+			}
+		}
+	}
+
+	// remove all animations from the scene
+	for (unsigned int i = 0; i < pScene->mNumAnimations; ++i)
+		delete pScene->mAnimations[i];
+	delete[] pScene->mAnimations;
+
+	pScene->mAnimations = NULL;
+	pScene->mNumAnimations = 0;
+
+	// --- we need to keep all cameras and lights
+	for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {
+		aiCamera *cam = pScene->mCameras[i];
+		const aiNode *nd = pScene->mRootNode->FindNode(cam->mName);
+		ai_assert(NULL != nd);
+
+		// multiply all properties of the camera with the absolute
+		// transformation of the corresponding node
+		cam->mPosition = nd->mTransformation * cam->mPosition;
+		cam->mLookAt = aiMatrix3x3(nd->mTransformation) * cam->mLookAt;
+		cam->mUp = aiMatrix3x3(nd->mTransformation) * cam->mUp;
+	}
+
+	for (unsigned int i = 0; i < pScene->mNumLights; ++i) {
+		aiLight *l = pScene->mLights[i];
+		const aiNode *nd = pScene->mRootNode->FindNode(l->mName);
+		ai_assert(NULL != nd);
+
+		// multiply all properties of the camera with the absolute
+		// transformation of the corresponding node
+		l->mPosition = nd->mTransformation * l->mPosition;
+		l->mDirection = aiMatrix3x3(nd->mTransformation) * l->mDirection;
+		l->mUp = aiMatrix3x3(nd->mTransformation) * l->mUp;
+	}
+
+	if (!configKeepHierarchy) {
+
+		// now delete all nodes in the scene and build a new
+		// flat node graph with a root node and some level 1 children
+		aiNode *newRoot = new aiNode();
+		newRoot->mName = pScene->mRootNode->mName;
+		delete pScene->mRootNode;
+		pScene->mRootNode = newRoot;
+
+		if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras) {
+			pScene->mRootNode->mNumMeshes = 1;
+			pScene->mRootNode->mMeshes = new unsigned int[1];
+			pScene->mRootNode->mMeshes[0] = 0;
+		} else {
+			pScene->mRootNode->mNumChildren = pScene->mNumMeshes + pScene->mNumLights + pScene->mNumCameras;
+			aiNode **nodes = pScene->mRootNode->mChildren = new aiNode *[pScene->mRootNode->mNumChildren];
+
+			// generate mesh nodes
+			for (unsigned int i = 0; i < pScene->mNumMeshes; ++i, ++nodes) {
+				aiNode *pcNode = new aiNode();
+				*nodes = pcNode;
+				pcNode->mParent = pScene->mRootNode;
+				pcNode->mName = pScene->mMeshes[i]->mName;
+
+				// setup mesh indices
+				pcNode->mNumMeshes = 1;
+				pcNode->mMeshes = new unsigned int[1];
+				pcNode->mMeshes[0] = i;
+			}
+			// generate light nodes
+			for (unsigned int i = 0; i < pScene->mNumLights; ++i, ++nodes) {
+				aiNode *pcNode = new aiNode();
+				*nodes = pcNode;
+				pcNode->mParent = pScene->mRootNode;
+				pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u", i);
+				pScene->mLights[i]->mName = pcNode->mName;
+			}
+			// generate camera nodes
+			for (unsigned int i = 0; i < pScene->mNumCameras; ++i, ++nodes) {
+				aiNode *pcNode = new aiNode();
+				*nodes = pcNode;
+				pcNode->mParent = pScene->mRootNode;
+				pcNode->mName.length = ::ai_snprintf(pcNode->mName.data, MAXLEN, "cam_%u", i);
+				pScene->mCameras[i]->mName = pcNode->mName;
+			}
+		}
+	} else {
+		// ... and finally set the transformation matrix of all nodes to identity
+		MakeIdentityTransform(pScene->mRootNode);
+	}
+
+	if (configNormalize) {
+		// compute the boundary of all meshes
+		aiVector3D min, max;
+		MinMaxChooser<aiVector3D>()(min, max);
+
+		for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
+			aiMesh *m = pScene->mMeshes[a];
+			for (unsigned int i = 0; i < m->mNumVertices; ++i) {
+				min = std::min(m->mVertices[i], min);
+				max = std::max(m->mVertices[i], max);
+			}
+		}
+
+		// find the dominant axis
+		aiVector3D d = max - min;
+		const ai_real div = std::max(d.x, std::max(d.y, d.z)) * ai_real(0.5);
+
+		d = min + d * (ai_real)0.5;
+		for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
+			aiMesh *m = pScene->mMeshes[a];
+			for (unsigned int i = 0; i < m->mNumVertices; ++i) {
+				m->mVertices[i] = (m->mVertices[i] - d) / div;
+			}
+		}
+	}
+
+	// print statistics
+	if (!DefaultLogger::isNullLogger()) {
+		ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished");
+
+		ASSIMP_LOG_INFO_F("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (",
+				CountNodes(pScene->mRootNode), " output nodes)");
+		ASSIMP_LOG_INFO_F("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras.");
+		ASSIMP_LOG_INFO_F("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")");
+	}
 }

+ 81 - 81
code/PostProcessing/PretransformVertices.h

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

+ 1 - 0
code/glTF2/glTF2Importer.cpp

@@ -202,6 +202,7 @@ inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
 		}
 
         mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot));
+        mat->AddProperty(&prop.texCoord, 1, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(texType, texSlot));
 
 		if (prop.textureTransformSupported) {
 			aiUVTransform transform;

二进制
test/models/glTF2/BoxTexcoords-glTF/boxTexcoords.bin


+ 172 - 0
test/models/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf

@@ -0,0 +1,172 @@
+{
+    "asset" : {
+        "generator" : "Khronos glTF Blender I/O v1.0.5",
+        "version" : "2.0"
+    },
+    "scene" : 0,
+    "scenes" : [
+        {
+            "name" : "Scene",
+            "nodes" : [
+                0,
+                1,
+                2
+            ]
+        }
+    ],
+    "nodes" : [
+        {
+            "mesh" : 0,
+            "name" : "Cube"
+        },
+        {
+            "name" : "Light",
+            "rotation" : [
+                0.16907575726509094,
+                0.7558803558349609,
+                -0.27217137813568115,
+                0.570947527885437
+            ],
+            "translation" : [
+                4.076245307922363,
+                5.903861999511719,
+                -1.0054539442062378
+            ]
+        },
+        {
+            "name" : "Camera",
+            "rotation" : [
+                0.483536034822464,
+                0.33687159419059753,
+                -0.20870360732078552,
+                0.7804827094078064
+            ],
+            "translation" : [
+                7.358891487121582,
+                4.958309173583984,
+                6.925790786743164
+            ]
+        }
+    ],
+    "materials" : [
+        {
+            "doubleSided" : true,
+            "name" : "Material",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 0,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "metallicRoughnessTexture" : {
+                    "index" : 0,
+                    "texCoord" : 1
+                }
+            }
+        }
+    ],
+    "meshes" : [
+        {
+            "name" : "Cube",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2,
+                        "TEXCOORD_1" : 3
+                    },
+                    "indices" : 4,
+                    "material" : 0
+                }
+            ]
+        }
+    ],
+    "textures" : [
+        {
+            "source" : 0
+        }
+    ],
+    "images" : [
+        {
+            "mimeType" : "image/png",
+            "name" : "Material Base Color",
+            "uri" : "texture.png"
+        }
+    ],
+    "accessors" : [
+        {
+            "bufferView" : 0,
+            "componentType" : 5126,
+            "count" : 24,
+            "max" : [
+                1,
+                1,
+                1
+            ],
+            "min" : [
+                -1,
+                -1,
+                -1
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 1,
+            "componentType" : 5126,
+            "count" : 24,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 2,
+            "componentType" : 5126,
+            "count" : 24,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 3,
+            "componentType" : 5126,
+            "count" : 24,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 4,
+            "componentType" : 5123,
+            "count" : 36,
+            "type" : "SCALAR"
+        }
+    ],
+    "bufferViews" : [
+        {
+            "buffer" : 0,
+            "byteLength" : 288,
+            "byteOffset" : 0
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 288,
+            "byteOffset" : 288
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 192,
+            "byteOffset" : 576
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 192,
+            "byteOffset" : 768
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 72,
+            "byteOffset" : 960
+        }
+    ],
+    "buffers" : [
+        {
+            "byteLength" : 1032,
+            "uri" : "boxTexcoords.bin"
+        }
+    ]
+}

二进制
test/models/glTF2/BoxTexcoords-glTF/texture.png


+ 29 - 0
test/unit/utglTF2ImportExport.cpp

@@ -49,6 +49,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <array>
 
+#include <assimp/pbrmaterial.h>
+
 using namespace Assimp;
 
 class utglTF2ImportExport : public AbstractImportExportBase {
@@ -464,3 +466,30 @@ TEST_F(utglTF2ImportExport, sceneMetadata) {
         ASSERT_EQ(strcmp(generator.C_Str(), "COLLADA2GLTF"), 0);
     }
 }
+
+TEST_F(utglTF2ImportExport, texcoords) {
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf",
+        aiProcess_ValidateDataStructure);
+    ASSERT_NE(scene, nullptr);
+
+    ASSERT_TRUE(scene->HasMaterials());
+    const aiMaterial *material = scene->mMaterials[0];
+
+    aiString path;
+    aiTextureMapMode modes[2];
+    EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr,
+        nullptr, nullptr, modes));
+    EXPECT_STREQ(path.C_Str(), "texture.png");
+
+    int uvIndex = -1;
+    EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_DIFFUSE, 0), &uvIndex), aiReturn_SUCCESS);
+    EXPECT_EQ(uvIndex, 0);
+
+    // Using manual macro expansion of AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE here.
+    // The following works with some but not all compilers:
+    // #define APPLY(X, Y) X(Y)
+    // ..., APPLY(AI_MATKEY_GLTF_TEXTURE_TEXCOORD, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE), ...
+    EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_UNKNOWN, 0), &uvIndex), aiReturn_SUCCESS);
+    EXPECT_EQ(uvIndex, 1);
+}