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 }
|