vrshoot

annotate libs/assimp/FindInstancesProcess.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
rev   line source
nuclear@0 1 /*
nuclear@0 2 ---------------------------------------------------------------------------
nuclear@0 3 Open Asset Import Library (assimp)
nuclear@0 4 ---------------------------------------------------------------------------
nuclear@0 5
nuclear@0 6 Copyright (c) 2006-2012, assimp team
nuclear@0 7
nuclear@0 8 All rights reserved.
nuclear@0 9
nuclear@0 10 Redistribution and use of this software in source and binary forms,
nuclear@0 11 with or without modification, are permitted provided that the following
nuclear@0 12 conditions are met:
nuclear@0 13
nuclear@0 14 * Redistributions of source code must retain the above
nuclear@0 15 copyright notice, this list of conditions and the
nuclear@0 16 following disclaimer.
nuclear@0 17
nuclear@0 18 * Redistributions in binary form must reproduce the above
nuclear@0 19 copyright notice, this list of conditions and the
nuclear@0 20 following disclaimer in the documentation and/or other
nuclear@0 21 materials provided with the distribution.
nuclear@0 22
nuclear@0 23 * Neither the name of the assimp team, nor the names of its
nuclear@0 24 contributors may be used to endorse or promote products
nuclear@0 25 derived from this software without specific prior
nuclear@0 26 written permission of the assimp team.
nuclear@0 27
nuclear@0 28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
nuclear@0 29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
nuclear@0 30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
nuclear@0 31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
nuclear@0 32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0 33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
nuclear@0 34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
nuclear@0 35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
nuclear@0 36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
nuclear@0 37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
nuclear@0 38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0 39 ---------------------------------------------------------------------------
nuclear@0 40 */
nuclear@0 41
nuclear@0 42 /** @file FindInstancesProcess.cpp
nuclear@0 43 * @brief Implementation of the aiProcess_FindInstances postprocessing step
nuclear@0 44 */
nuclear@0 45
nuclear@0 46 #include "AssimpPCH.h"
nuclear@0 47 #include "FindInstancesProcess.h"
nuclear@0 48
nuclear@0 49 using namespace Assimp;
nuclear@0 50
nuclear@0 51 // ------------------------------------------------------------------------------------------------
nuclear@0 52 // Constructor to be privately used by Importer
nuclear@0 53 FindInstancesProcess::FindInstancesProcess()
nuclear@0 54 : configSpeedFlag (false)
nuclear@0 55 {}
nuclear@0 56
nuclear@0 57 // ------------------------------------------------------------------------------------------------
nuclear@0 58 // Destructor, private as well
nuclear@0 59 FindInstancesProcess::~FindInstancesProcess()
nuclear@0 60 {}
nuclear@0 61
nuclear@0 62 // ------------------------------------------------------------------------------------------------
nuclear@0 63 // Returns whether the processing step is present in the given flag field.
nuclear@0 64 bool FindInstancesProcess::IsActive( unsigned int pFlags) const
nuclear@0 65 {
nuclear@0 66 // FindInstances makes absolutely no sense together with PreTransformVertices
nuclear@0 67 // fixme: spawn error message somewhere else?
nuclear@0 68 return 0 != (pFlags & aiProcess_FindInstances) && 0 == (pFlags & aiProcess_PreTransformVertices);
nuclear@0 69 }
nuclear@0 70
nuclear@0 71 // ------------------------------------------------------------------------------------------------
nuclear@0 72 // Setup properties for the step
nuclear@0 73 void FindInstancesProcess::SetupProperties(const Importer* pImp)
nuclear@0 74 {
nuclear@0 75 // AI_CONFIG_FAVOUR_SPEED
nuclear@0 76 configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
nuclear@0 77 }
nuclear@0 78
nuclear@0 79 // ------------------------------------------------------------------------------------------------
nuclear@0 80 // Compare the bones of two meshes
nuclear@0 81 bool CompareBones(const aiMesh* orig, const aiMesh* inst)
nuclear@0 82 {
nuclear@0 83 for (unsigned int i = 0; i < orig->mNumBones;++i) {
nuclear@0 84 aiBone* aha = orig->mBones[i];
nuclear@0 85 aiBone* oha = inst->mBones[i];
nuclear@0 86
nuclear@0 87 if (aha->mNumWeights != oha->mNumWeights ||
nuclear@0 88 aha->mOffsetMatrix != oha->mOffsetMatrix ||
nuclear@0 89 aha->mNumWeights != oha->mNumWeights) {
nuclear@0 90 return false;
nuclear@0 91 }
nuclear@0 92
nuclear@0 93 // compare weight per weight ---
nuclear@0 94 for (unsigned int n = 0; n < aha->mNumWeights;++n) {
nuclear@0 95 if (aha->mWeights[n].mVertexId != oha->mWeights[n].mVertexId ||
nuclear@0 96 (aha->mWeights[n].mWeight - oha->mWeights[n].mWeight) < 10e-3f) {
nuclear@0 97 return false;
nuclear@0 98 }
nuclear@0 99 }
nuclear@0 100 }
nuclear@0 101 return true;
nuclear@0 102 }
nuclear@0 103
nuclear@0 104 // ------------------------------------------------------------------------------------------------
nuclear@0 105 // Update mesh indices in the node graph
nuclear@0 106 void UpdateMeshIndices(aiNode* node, unsigned int* lookup)
nuclear@0 107 {
nuclear@0 108 for (unsigned int n = 0; n < node->mNumMeshes;++n)
nuclear@0 109 node->mMeshes[n] = lookup[node->mMeshes[n]];
nuclear@0 110
nuclear@0 111 for (unsigned int n = 0; n < node->mNumChildren;++n)
nuclear@0 112 UpdateMeshIndices(node->mChildren[n],lookup);
nuclear@0 113 }
nuclear@0 114
nuclear@0 115 // ------------------------------------------------------------------------------------------------
nuclear@0 116 // Executes the post processing step on the given imported data.
nuclear@0 117 void FindInstancesProcess::Execute( aiScene* pScene)
nuclear@0 118 {
nuclear@0 119 DefaultLogger::get()->debug("FindInstancesProcess begin");
nuclear@0 120 if (pScene->mNumMeshes) {
nuclear@0 121
nuclear@0 122 // use a pseudo hash for all meshes in the scene to quickly find
nuclear@0 123 // the ones which are possibly equal. This step is executed early
nuclear@0 124 // in the pipeline, so we could, depending on the file format,
nuclear@0 125 // have several thousand small meshes. That's too much for a brute
nuclear@0 126 // everyone-against-everyone check involving up to 10 comparisons
nuclear@0 127 // each.
nuclear@0 128 boost::scoped_array<uint64_t> hashes (new uint64_t[pScene->mNumMeshes]);
nuclear@0 129 boost::scoped_array<unsigned int> remapping (new unsigned int[pScene->mNumMeshes]);
nuclear@0 130
nuclear@0 131 unsigned int numMeshesOut = 0;
nuclear@0 132 for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
nuclear@0 133
nuclear@0 134 aiMesh* inst = pScene->mMeshes[i];
nuclear@0 135 hashes[i] = GetMeshHash(inst);
nuclear@0 136
nuclear@0 137 for (int a = i-1; a >= 0; --a) {
nuclear@0 138 if (hashes[i] == hashes[a])
nuclear@0 139 {
nuclear@0 140 aiMesh* orig = pScene->mMeshes[a];
nuclear@0 141 if (!orig)
nuclear@0 142 continue;
nuclear@0 143
nuclear@0 144 // check for hash collision .. we needn't check
nuclear@0 145 // the vertex format, it *must* match due to the
nuclear@0 146 // (brilliant) construction of the hash
nuclear@0 147 if (orig->mNumBones != inst->mNumBones ||
nuclear@0 148 orig->mNumFaces != inst->mNumFaces ||
nuclear@0 149 orig->mNumVertices != inst->mNumVertices ||
nuclear@0 150 orig->mMaterialIndex != inst->mMaterialIndex ||
nuclear@0 151 orig->mPrimitiveTypes != inst->mPrimitiveTypes)
nuclear@0 152 continue;
nuclear@0 153
nuclear@0 154 // up to now the meshes are equal. find an appropriate
nuclear@0 155 // epsilon to compare position differences against
nuclear@0 156 float epsilon = ComputePositionEpsilon(inst);
nuclear@0 157 epsilon *= epsilon;
nuclear@0 158
nuclear@0 159 // now compare vertex positions, normals,
nuclear@0 160 // tangents and bitangents using this epsilon.
nuclear@0 161 if (orig->HasPositions()) {
nuclear@0 162 if(!CompareArrays(orig->mVertices,inst->mVertices,orig->mNumVertices,epsilon))
nuclear@0 163 continue;
nuclear@0 164 }
nuclear@0 165 if (orig->HasNormals()) {
nuclear@0 166 if(!CompareArrays(orig->mNormals,inst->mNormals,orig->mNumVertices,epsilon))
nuclear@0 167 continue;
nuclear@0 168 }
nuclear@0 169 if (orig->HasTangentsAndBitangents()) {
nuclear@0 170 if (!CompareArrays(orig->mTangents,inst->mTangents,orig->mNumVertices,epsilon) ||
nuclear@0 171 !CompareArrays(orig->mBitangents,inst->mBitangents,orig->mNumVertices,epsilon))
nuclear@0 172 continue;
nuclear@0 173 }
nuclear@0 174
nuclear@0 175 // use a constant epsilon for colors and UV coordinates
nuclear@0 176 static const float uvEpsilon = 10e-4f;
nuclear@0 177
nuclear@0 178 {
nuclear@0 179 unsigned int i, end = orig->GetNumUVChannels();
nuclear@0 180 for(i = 0; i < end; ++i) {
nuclear@0 181 if (!orig->mTextureCoords[i]) {
nuclear@0 182 continue;
nuclear@0 183 }
nuclear@0 184 if(!CompareArrays(orig->mTextureCoords[i],inst->mTextureCoords[i],orig->mNumVertices,uvEpsilon)) {
nuclear@0 185 break;
nuclear@0 186 }
nuclear@0 187 }
nuclear@0 188 if (i != end) {
nuclear@0 189 continue;
nuclear@0 190 }
nuclear@0 191 }
nuclear@0 192 {
nuclear@0 193 unsigned int i, end = orig->GetNumColorChannels();
nuclear@0 194 for(i = 0; i < end; ++i) {
nuclear@0 195 if (!orig->mColors[i]) {
nuclear@0 196 continue;
nuclear@0 197 }
nuclear@0 198 if(!CompareArrays(orig->mColors[i],inst->mColors[i],orig->mNumVertices,uvEpsilon)) {
nuclear@0 199 break;
nuclear@0 200 }
nuclear@0 201 }
nuclear@0 202 if (i != end) {
nuclear@0 203 continue;
nuclear@0 204 }
nuclear@0 205 }
nuclear@0 206
nuclear@0 207 // These two checks are actually quite expensive and almost *never* required.
nuclear@0 208 // Almost. That's why they're still here. But there's no reason to do them
nuclear@0 209 // in speed-targeted imports.
nuclear@0 210 if (!configSpeedFlag) {
nuclear@0 211
nuclear@0 212 // It seems to be strange, but we really need to check whether the
nuclear@0 213 // bones are identical too. Although it's extremely unprobable
nuclear@0 214 // that they're not if control reaches here, we need to deal
nuclear@0 215 // with unprobable cases, too. It could still be that there are
nuclear@0 216 // equal shapes which are deformed differently.
nuclear@0 217 if (!CompareBones(orig,inst))
nuclear@0 218 continue;
nuclear@0 219
nuclear@0 220 // For completeness ... compare even the index buffers for equality
nuclear@0 221 // face order & winding order doesn't care. Input data is in verbose format.
nuclear@0 222 boost::scoped_array<unsigned int> ftbl_orig(new unsigned int[orig->mNumVertices]);
nuclear@0 223 boost::scoped_array<unsigned int> ftbl_inst(new unsigned int[orig->mNumVertices]);
nuclear@0 224
nuclear@0 225 for (unsigned int tt = 0; tt < orig->mNumFaces;++tt) {
nuclear@0 226 aiFace& f = orig->mFaces[tt];
nuclear@0 227 for (unsigned int nn = 0; nn < f.mNumIndices;++nn)
nuclear@0 228 ftbl_orig[f.mIndices[nn]] = tt;
nuclear@0 229
nuclear@0 230 aiFace& f2 = inst->mFaces[tt];
nuclear@0 231 for (unsigned int nn = 0; nn < f2.mNumIndices;++nn)
nuclear@0 232 ftbl_inst[f2.mIndices[nn]] = tt;
nuclear@0 233 }
nuclear@0 234 if (0 != ::memcmp(ftbl_inst.get(),ftbl_orig.get(),orig->mNumVertices*sizeof(unsigned int)))
nuclear@0 235 continue;
nuclear@0 236 }
nuclear@0 237
nuclear@0 238 // We're still here. Or in other words: 'inst' is an instance of 'orig'.
nuclear@0 239 // Place a marker in our list that we can easily update mesh indices.
nuclear@0 240 remapping[i] = remapping[a];
nuclear@0 241
nuclear@0 242 // Delete the instanced mesh, we don't need it anymore
nuclear@0 243 delete inst;
nuclear@0 244 pScene->mMeshes[i] = NULL;
nuclear@0 245 break;
nuclear@0 246 }
nuclear@0 247 }
nuclear@0 248
nuclear@0 249 // If we didn't find a match for the current mesh: keep it
nuclear@0 250 if (pScene->mMeshes[i]) {
nuclear@0 251 remapping[i] = numMeshesOut++;
nuclear@0 252 }
nuclear@0 253 }
nuclear@0 254 ai_assert(0 != numMeshesOut);
nuclear@0 255 if (numMeshesOut != pScene->mNumMeshes) {
nuclear@0 256
nuclear@0 257 // Collapse the meshes array by removing all NULL entries
nuclear@0 258 for (unsigned int real = 0, i = 0; real < numMeshesOut; ++i) {
nuclear@0 259 if (pScene->mMeshes[i])
nuclear@0 260 pScene->mMeshes[real++] = pScene->mMeshes[i];
nuclear@0 261 }
nuclear@0 262
nuclear@0 263 // And update the nodegraph with our nice lookup table
nuclear@0 264 UpdateMeshIndices(pScene->mRootNode,remapping.get());
nuclear@0 265
nuclear@0 266 // write to log
nuclear@0 267 if (!DefaultLogger::isNullLogger()) {
nuclear@0 268
nuclear@0 269 char buffer[512];
nuclear@0 270 ::sprintf(buffer,"FindInstancesProcess finished. Found %i instances",pScene->mNumMeshes-numMeshesOut);
nuclear@0 271 DefaultLogger::get()->info(buffer);
nuclear@0 272 }
nuclear@0 273 pScene->mNumMeshes = numMeshesOut;
nuclear@0 274 }
nuclear@0 275 else DefaultLogger::get()->debug("FindInstancesProcess finished. No instanced meshes found");
nuclear@0 276 }
nuclear@0 277 }