| /* |
| --------------------------------------------------------------------------- |
| Open Asset Import Library (assimp) |
| --------------------------------------------------------------------------- |
| |
| Copyright (c) 2006-2017, assimp team |
| |
| |
| All rights reserved. |
| |
| Redistribution and use of this software in source and binary forms, |
| with or without modification, are permitted provided that the following |
| conditions are met: |
| |
| * Redistributions of source code must retain the above |
| copyright notice, this list of conditions and the |
| following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above |
| copyright notice, this list of conditions and the |
| following disclaimer in the documentation and/or other |
| materials provided with the distribution. |
| |
| * Neither the name of the assimp team, nor the names of its |
| contributors may be used to endorse or promote products |
| derived from this software without specific prior |
| written permission of the assimp team. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| --------------------------------------------------------------------------- |
| */ |
| |
| /** @file OptimizeGraph.cpp |
| * @brief Implementation of the aiProcess_OptimizGraph step |
| */ |
| |
| |
| #ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS |
| |
| #include "OptimizeGraph.h" |
| #include "ProcessHelper.h" |
| #include <assimp/SceneCombiner.h> |
| #include "Exceptional.h" |
| #include <stdio.h> |
| |
| using namespace Assimp; |
| |
| #define AI_RESERVED_NODE_NAME "$Reserved_And_Evil" |
| |
| /* AI_OG_USE_HASHING enables the use of hashing to speed-up std::set lookups. |
| * 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) |
| #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) |
| #endif |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Constructor to be privately used by Importer |
| OptimizeGraphProcess::OptimizeGraphProcess() |
| : mScene() |
| , nodes_in() |
| , nodes_out() |
| , count_merged() |
| {} |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Destructor, private as well |
| OptimizeGraphProcess::~OptimizeGraphProcess() |
| {} |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Returns whether the processing step is present in the given flag field. |
| bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const |
| { |
| return (0 != (pFlags & aiProcess_OptimizeGraph)); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Setup properties for the postprocessing 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); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // 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] = NULL; |
| } |
| |
| // 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 = NULL; |
| } |
| |
| 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 postprocessing step on the given scene |
| void OptimizeGraphProcess::Execute( aiScene* pScene) |
| { |
| DefaultLogger::get()->debug("OptimizeGraphProcess begin"); |
| nodes_in = nodes_out = count_merged = 0; |
| mScene = pScene; |
| |
| 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) { |
| #ifdef AI_OG_USE_HASHING |
| locked.insert(SuperFastHash((*it).c_str())); |
| #else |
| 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) { |
| |
| char buf[512]; |
| ::ai_snprintf(buf,512,"OptimizeGraphProcess finished; Input nodes: %u, Output nodes: %u",nodes_in,nodes_out); |
| DefaultLogger::get()->info(buf); |
| } |
| else DefaultLogger::get()->debug("OptimizeGraphProcess finished"); |
| } |
| meshes.clear(); |
| locked.clear(); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Buidl 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]); |
| } |
| |
| #endif // !! ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS |