nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file OptimizeMeshes.cpp nuclear@0: * @brief Implementation of the aiProcess_OptimizeMeshes step nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: #include "OptimizeMeshes.h" nuclear@0: #include "ProcessHelper.h" nuclear@0: #include "SceneCombiner.h" nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: OptimizeMeshesProcess::OptimizeMeshesProcess() nuclear@0: : pts (false) nuclear@0: , max_verts (0xffffffff) nuclear@0: , max_faces (0xffffffff) nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: OptimizeMeshesProcess::~OptimizeMeshesProcess() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the processing step is present in the given flag field. nuclear@0: bool OptimizeMeshesProcess::IsActive( unsigned int pFlags) const nuclear@0: { nuclear@0: // Our behaviour needs to be different if the SortByPType or SplitLargeMeshes nuclear@0: // steps are active. Thus we need to query their flags here and store the nuclear@0: // information, although we're breaking const-correctness. nuclear@0: // That's a serious design flaw, consider redesign. nuclear@0: if( 0 != (pFlags & aiProcess_OptimizeMeshes) ) { nuclear@0: pts = (0 != (pFlags & aiProcess_SortByPType)); nuclear@0: max_verts = (0 != (pFlags & aiProcess_SplitLargeMeshes)) ? 0xdeadbeef : 0; nuclear@0: return true; nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Setup properties for the postprocessing step nuclear@0: void OptimizeMeshesProcess::SetupProperties(const Importer* pImp) nuclear@0: { nuclear@0: if (max_verts == 0xdeadbeef /* magic hack */) { nuclear@0: max_faces = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); nuclear@0: max_verts = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Execute step nuclear@0: void OptimizeMeshesProcess::Execute( aiScene* pScene) nuclear@0: { nuclear@0: const unsigned int num_old = pScene->mNumMeshes; nuclear@0: if (num_old <= 1) { nuclear@0: DefaultLogger::get()->debug("Skipping OptimizeMeshesProcess"); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: DefaultLogger::get()->debug("OptimizeMeshesProcess begin"); nuclear@0: mScene = pScene; nuclear@0: nuclear@0: // need to clear persistent members from previous runs nuclear@0: merge_list.clear(); nuclear@0: output.clear(); nuclear@0: nuclear@0: merge_list.reserve(pScene->mNumMeshes); nuclear@0: output.reserve(pScene->mNumMeshes); nuclear@0: nuclear@0: // Prepare lookup tables nuclear@0: meshes.resize(pScene->mNumMeshes); nuclear@0: FindInstancedMeshes(pScene->mRootNode); nuclear@0: if (max_verts == 0xdeadbeef) /* undo the magic hack */ nuclear@0: max_verts = 0xffffffff; nuclear@0: nuclear@0: // ... instanced meshes are immediately processed and added to the output list nuclear@0: for (unsigned int i = 0, n = 0; i < pScene->mNumMeshes;++i) { nuclear@0: meshes[i].vertex_format = GetMeshVFormatUnique(pScene->mMeshes[i]); nuclear@0: nuclear@0: if (meshes[i].instance_cnt > 1 && meshes[i].output_id == 0xffffffff) { nuclear@0: meshes[i].output_id = n++; nuclear@0: output.push_back(mScene->mMeshes[i]); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // and process all nodes in the scenegraoh recursively nuclear@0: ProcessNode(pScene->mRootNode); nuclear@0: if (!output.size()) { nuclear@0: throw DeadlyImportError("OptimizeMeshes: No meshes remaining; there's definitely something wrong"); nuclear@0: } nuclear@0: nuclear@0: meshes.clear(); nuclear@0: ai_assert(output.size() <= num_old); nuclear@0: nuclear@0: mScene->mNumMeshes = output.size(); nuclear@0: std::copy(output.begin(),output.end(),mScene->mMeshes); nuclear@0: nuclear@0: if (output.size() != num_old) { nuclear@0: char tmp[512]; nuclear@0: ::sprintf(tmp,"OptimizeMeshesProcess finished. Input meshes: %i, Output meshes: %i",num_old,pScene->mNumMeshes); nuclear@0: DefaultLogger::get()->info(tmp); nuclear@0: } nuclear@0: else DefaultLogger::get()->debug("OptimizeMeshesProcess finished"); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Process meshes for a single node nuclear@0: void OptimizeMeshesProcess::ProcessNode( aiNode* pNode) nuclear@0: { nuclear@0: for (unsigned int i = 0; i < pNode->mNumMeshes;++i) { nuclear@0: unsigned int& im = pNode->mMeshes[i]; nuclear@0: nuclear@0: if (meshes[im].instance_cnt > 1) { nuclear@0: im = meshes[im].output_id; nuclear@0: } nuclear@0: else { nuclear@0: merge_list.clear(); nuclear@0: unsigned int verts = 0, faces = 0; nuclear@0: nuclear@0: // Find meshes to merge with us nuclear@0: for (unsigned int a = i+1; a < pNode->mNumMeshes;++a) { nuclear@0: register unsigned int am = pNode->mMeshes[a]; nuclear@0: if (meshes[am].instance_cnt == 1 && CanJoin(im,am,verts,faces)) { nuclear@0: nuclear@0: merge_list.push_back(mScene->mMeshes[am]); nuclear@0: verts += mScene->mMeshes[am]->mNumVertices; nuclear@0: faces += mScene->mMeshes[am]->mNumFaces; nuclear@0: nuclear@0: --pNode->mNumMeshes; nuclear@0: for (unsigned int n = a; n < pNode->mNumMeshes; ++n) nuclear@0: pNode->mMeshes[n] = pNode->mMeshes[n+1]; nuclear@0: nuclear@0: --a; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // and merge all meshes which we found, replace the old ones nuclear@0: if (!merge_list.empty()) { nuclear@0: merge_list.push_back(mScene->mMeshes[im]); nuclear@0: nuclear@0: aiMesh* out; nuclear@0: SceneCombiner::MergeMeshes(&out,0,merge_list.begin(),merge_list.end()); nuclear@0: output.push_back(out); nuclear@0: } nuclear@0: else { nuclear@0: output.push_back(mScene->mMeshes[im]); nuclear@0: } nuclear@0: im = output.size()-1; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: for (unsigned int i = 0; i < pNode->mNumChildren; ++i) nuclear@0: ProcessNode(pNode->mChildren[i]); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Check whether two meshes can be joined nuclear@0: bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned int verts, unsigned int faces ) nuclear@0: { nuclear@0: if (meshes[a].vertex_format != meshes[b].vertex_format) nuclear@0: return false; nuclear@0: nuclear@0: aiMesh* ma = mScene->mMeshes[a], *mb = mScene->mMeshes[b]; nuclear@0: nuclear@0: if ((0xffffffff != max_verts && verts+mb->mNumVertices > max_verts) || nuclear@0: (0xffffffff != max_faces && faces+mb->mNumFaces > max_faces)) { nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // Never merge unskinned meshes with skinned meshes nuclear@0: if (ma->mMaterialIndex != mb->mMaterialIndex || ma->HasBones() != mb->HasBones()) nuclear@0: return false; nuclear@0: nuclear@0: // Never merge meshes with different kinds of primitives if SortByPType did already nuclear@0: // do its work. We would destroy everything again ... nuclear@0: if (pts && ma->mPrimitiveTypes != mb->mPrimitiveTypes) nuclear@0: return false; nuclear@0: nuclear@0: // If both meshes are skinned, check whether we have many bones defined in both meshes. nuclear@0: // If yes, we can savely join them. nuclear@0: if (ma->HasBones()) { nuclear@0: // TODO nuclear@0: return false; nuclear@0: } nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Buidl a LUT of all instanced meshes nuclear@0: void OptimizeMeshesProcess::FindInstancedMeshes (aiNode* pNode) nuclear@0: { nuclear@0: for (unsigned int i = 0; i < pNode->mNumMeshes;++i) nuclear@0: ++meshes[pNode->mMeshes[i]].instance_cnt; nuclear@0: nuclear@0: for (unsigned int i = 0; i < pNode->mNumChildren; ++i) nuclear@0: FindInstancedMeshes(pNode->mChildren[i]); nuclear@0: } nuclear@0: nuclear@0: #endif // !! ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS