| /* |
| --------------------------------------------------------------------------- |
| 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 FindInstancesProcess.cpp |
| * @brief Implementation of the aiProcess_FindInstances postprocessing step |
| */ |
| |
| |
| #include "FindInstancesProcess.h" |
| #include <memory> |
| #include <stdio.h> |
| |
| using namespace Assimp; |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Constructor to be privately used by Importer |
| FindInstancesProcess::FindInstancesProcess() |
| : configSpeedFlag (false) |
| {} |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Destructor, private as well |
| FindInstancesProcess::~FindInstancesProcess() |
| {} |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Returns whether the processing step is present in the given flag field. |
| bool FindInstancesProcess::IsActive( unsigned int pFlags) const |
| { |
| // FindInstances makes absolutely no sense together with PreTransformVertices |
| // fixme: spawn error message somewhere else? |
| return 0 != (pFlags & aiProcess_FindInstances) && 0 == (pFlags & aiProcess_PreTransformVertices); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Setup properties for the step |
| void FindInstancesProcess::SetupProperties(const Importer* pImp) |
| { |
| // AI_CONFIG_FAVOUR_SPEED |
| configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0)); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Compare the bones of two meshes |
| bool CompareBones(const aiMesh* orig, const aiMesh* inst) |
| { |
| for (unsigned int i = 0; i < orig->mNumBones;++i) { |
| aiBone* aha = orig->mBones[i]; |
| aiBone* oha = inst->mBones[i]; |
| |
| if (aha->mNumWeights != oha->mNumWeights || |
| aha->mOffsetMatrix != oha->mOffsetMatrix) { |
| return false; |
| } |
| |
| // compare weight per weight --- |
| for (unsigned int n = 0; n < aha->mNumWeights;++n) { |
| if (aha->mWeights[n].mVertexId != oha->mWeights[n].mVertexId || |
| (aha->mWeights[n].mWeight - oha->mWeights[n].mWeight) < 10e-3f) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Update mesh indices in the node graph |
| void UpdateMeshIndices(aiNode* node, unsigned int* lookup) |
| { |
| for (unsigned int n = 0; n < node->mNumMeshes;++n) |
| node->mMeshes[n] = lookup[node->mMeshes[n]]; |
| |
| for (unsigned int n = 0; n < node->mNumChildren;++n) |
| UpdateMeshIndices(node->mChildren[n],lookup); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Executes the post processing step on the given imported data. |
| void FindInstancesProcess::Execute( aiScene* pScene) |
| { |
| DefaultLogger::get()->debug("FindInstancesProcess begin"); |
| if (pScene->mNumMeshes) { |
| |
| // use a pseudo hash for all meshes in the scene to quickly find |
| // the ones which are possibly equal. This step is executed early |
| // in the pipeline, so we could, depending on the file format, |
| // have several thousand small meshes. That's too much for a brute |
| // everyone-against-everyone check involving up to 10 comparisons |
| // each. |
| std::unique_ptr<uint64_t[]> hashes (new uint64_t[pScene->mNumMeshes]); |
| std::unique_ptr<unsigned int[]> remapping (new unsigned int[pScene->mNumMeshes]); |
| |
| unsigned int numMeshesOut = 0; |
| for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { |
| |
| aiMesh* inst = pScene->mMeshes[i]; |
| hashes[i] = GetMeshHash(inst); |
| |
| for (int a = i-1; a >= 0; --a) { |
| if (hashes[i] == hashes[a]) |
| { |
| aiMesh* orig = pScene->mMeshes[a]; |
| if (!orig) |
| continue; |
| |
| // check for hash collision .. we needn't check |
| // the vertex format, it *must* match due to the |
| // (brilliant) construction of the hash |
| if (orig->mNumBones != inst->mNumBones || |
| orig->mNumFaces != inst->mNumFaces || |
| orig->mNumVertices != inst->mNumVertices || |
| orig->mMaterialIndex != inst->mMaterialIndex || |
| orig->mPrimitiveTypes != inst->mPrimitiveTypes) |
| continue; |
| |
| // up to now the meshes are equal. find an appropriate |
| // epsilon to compare position differences against |
| float epsilon = ComputePositionEpsilon(inst); |
| epsilon *= epsilon; |
| |
| // now compare vertex positions, normals, |
| // tangents and bitangents using this epsilon. |
| if (orig->HasPositions()) { |
| if(!CompareArrays(orig->mVertices,inst->mVertices,orig->mNumVertices,epsilon)) |
| continue; |
| } |
| if (orig->HasNormals()) { |
| if(!CompareArrays(orig->mNormals,inst->mNormals,orig->mNumVertices,epsilon)) |
| continue; |
| } |
| if (orig->HasTangentsAndBitangents()) { |
| if (!CompareArrays(orig->mTangents,inst->mTangents,orig->mNumVertices,epsilon) || |
| !CompareArrays(orig->mBitangents,inst->mBitangents,orig->mNumVertices,epsilon)) |
| continue; |
| } |
| |
| // use a constant epsilon for colors and UV coordinates |
| static const float uvEpsilon = 10e-4f; |
| { |
| unsigned int i, end = orig->GetNumUVChannels(); |
| for(i = 0; i < end; ++i) { |
| if (!orig->mTextureCoords[i]) { |
| continue; |
| } |
| if(!CompareArrays(orig->mTextureCoords[i],inst->mTextureCoords[i],orig->mNumVertices,uvEpsilon)) { |
| break; |
| } |
| } |
| if (i != end) { |
| continue; |
| } |
| } |
| { |
| unsigned int i, end = orig->GetNumColorChannels(); |
| for(i = 0; i < end; ++i) { |
| if (!orig->mColors[i]) { |
| continue; |
| } |
| if(!CompareArrays(orig->mColors[i],inst->mColors[i],orig->mNumVertices,uvEpsilon)) { |
| break; |
| } |
| } |
| if (i != end) { |
| continue; |
| } |
| } |
| |
| // These two checks are actually quite expensive and almost *never* required. |
| // Almost. That's why they're still here. But there's no reason to do them |
| // in speed-targeted imports. |
| if (!configSpeedFlag) { |
| |
| // It seems to be strange, but we really need to check whether the |
| // bones are identical too. Although it's extremely unprobable |
| // that they're not if control reaches here, we need to deal |
| // with unprobable cases, too. It could still be that there are |
| // equal shapes which are deformed differently. |
| if (!CompareBones(orig,inst)) |
| continue; |
| |
| // For completeness ... compare even the index buffers for equality |
| // face order & winding order doesn't care. Input data is in verbose format. |
| std::unique_ptr<unsigned int[]> ftbl_orig(new unsigned int[orig->mNumVertices]); |
| std::unique_ptr<unsigned int[]> ftbl_inst(new unsigned int[orig->mNumVertices]); |
| |
| for (unsigned int tt = 0; tt < orig->mNumFaces;++tt) { |
| aiFace& f = orig->mFaces[tt]; |
| for (unsigned int nn = 0; nn < f.mNumIndices;++nn) |
| ftbl_orig[f.mIndices[nn]] = tt; |
| |
| aiFace& f2 = inst->mFaces[tt]; |
| for (unsigned int nn = 0; nn < f2.mNumIndices;++nn) |
| ftbl_inst[f2.mIndices[nn]] = tt; |
| } |
| if (0 != ::memcmp(ftbl_inst.get(),ftbl_orig.get(),orig->mNumVertices*sizeof(unsigned int))) |
| continue; |
| } |
| |
| // We're still here. Or in other words: 'inst' is an instance of 'orig'. |
| // Place a marker in our list that we can easily update mesh indices. |
| remapping[i] = remapping[a]; |
| |
| // Delete the instanced mesh, we don't need it anymore |
| delete inst; |
| pScene->mMeshes[i] = NULL; |
| break; |
| } |
| } |
| |
| // If we didn't find a match for the current mesh: keep it |
| if (pScene->mMeshes[i]) { |
| remapping[i] = numMeshesOut++; |
| } |
| } |
| ai_assert(0 != numMeshesOut); |
| if (numMeshesOut != pScene->mNumMeshes) { |
| |
| // Collapse the meshes array by removing all NULL entries |
| for (unsigned int real = 0, i = 0; real < numMeshesOut; ++i) { |
| if (pScene->mMeshes[i]) |
| pScene->mMeshes[real++] = pScene->mMeshes[i]; |
| } |
| |
| // And update the node graph with our nice lookup table |
| UpdateMeshIndices(pScene->mRootNode,remapping.get()); |
| |
| // write to log |
| if (!DefaultLogger::isNullLogger()) { |
| |
| char buffer[512]; |
| ::ai_snprintf(buffer,512,"FindInstancesProcess finished. Found %i instances",pScene->mNumMeshes-numMeshesOut); |
| DefaultLogger::get()->info(buffer); |
| } |
| pScene->mNumMeshes = numMeshesOut; |
| } |
| else DefaultLogger::get()->debug("FindInstancesProcess finished. No instanced meshes found"); |
| } |
| } |