/src/ogre/OgreMain/src/OgreInstanceManager.cpp
Line | Count | Source |
1 | | /* |
2 | | ----------------------------------------------------------------------------- |
3 | | This source file is part of OGRE |
4 | | (Object-oriented Graphics Rendering Engine) |
5 | | For the latest info, see http://www.ogre3d.org/ |
6 | | |
7 | | Copyright (c) 2000-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | #include "OgreStableHeaders.h" |
29 | | #include "OgreInstanceManager.h" |
30 | | #include "OgreInstanceBatchHW.h" |
31 | | #include "OgreInstanceBatchHW_VTF.h" |
32 | | #include "OgreInstanceBatchShader.h" |
33 | | #include "OgreInstanceBatchVTF.h" |
34 | | |
35 | | namespace Ogre |
36 | | { |
37 | | InstanceManager::InstanceManager( const String &customName, SceneManager *sceneManager, |
38 | | const String &meshName, const String &groupName, |
39 | | InstancingTechnique instancingTechnique, uint16 instancingFlags, |
40 | | size_t instancesPerBatch, unsigned short subMeshIdx, bool useBoneMatrixLookup ) : |
41 | 0 | mName( customName ), |
42 | 0 | mIdCount( 0 ), |
43 | 0 | mInstancesPerBatch( instancesPerBatch ), |
44 | 0 | mInstancingTechnique( instancingTechnique ), |
45 | 0 | mInstancingFlags( instancingFlags ), |
46 | 0 | mSubMeshIdx( subMeshIdx ), |
47 | 0 | mSceneManager( sceneManager ), |
48 | 0 | mMaxLookupTableInstances(16), |
49 | 0 | mNumCustomParams( 0 ) |
50 | 0 | { |
51 | 0 | mMeshReference = MeshManager::getSingleton().load( meshName, groupName ); |
52 | |
|
53 | 0 | if(mMeshReference->sharedVertexData) |
54 | 0 | unshareVertices(mMeshReference); |
55 | |
|
56 | 0 | if( mMeshReference->hasSkeleton() && mMeshReference->getSkeleton() ) |
57 | 0 | mMeshReference->getSubMesh(mSubMeshIdx)->_compileBoneAssignments(); |
58 | 0 | } |
59 | | |
60 | | InstanceManager::~InstanceManager() |
61 | 0 | { |
62 | | //Remove all batches from all materials we created |
63 | 0 | for (auto& i : mInstanceBatches) |
64 | 0 | { |
65 | 0 | for (auto *it : i.second) |
66 | 0 | OGRE_DELETE it; |
67 | 0 | } |
68 | 0 | } |
69 | | //---------------------------------------------------------------------- |
70 | | void InstanceManager::setInstancesPerBatch( size_t instancesPerBatch ) |
71 | 0 | { |
72 | 0 | OgreAssert(mInstanceBatches.empty(), "can only be changed before building the batch"); |
73 | 0 | mInstancesPerBatch = instancesPerBatch; |
74 | 0 | } |
75 | | |
76 | | //---------------------------------------------------------------------- |
77 | | void InstanceManager::setMaxLookupTableInstances( size_t maxLookupTableInstances ) |
78 | 0 | { |
79 | 0 | OgreAssert(mInstanceBatches.empty(), "can only be changed before building the batch"); |
80 | 0 | mMaxLookupTableInstances = maxLookupTableInstances; |
81 | 0 | } |
82 | | |
83 | | //---------------------------------------------------------------------- |
84 | | void InstanceManager::setNumCustomParams( unsigned char numCustomParams ) |
85 | 0 | { |
86 | 0 | OgreAssert(mInstanceBatches.empty(), "can only be changed before building the batch"); |
87 | 0 | mNumCustomParams = numCustomParams; |
88 | 0 | } |
89 | | //---------------------------------------------------------------------- |
90 | | size_t InstanceManager::getMaxOrBestNumInstancesPerBatch( const String &materialName, size_t suggestedSize, |
91 | | uint16 flags ) |
92 | 0 | { |
93 | | //Get the material |
94 | 0 | MaterialPtr mat = MaterialManager::getSingleton().getByName( materialName, |
95 | 0 | mMeshReference->getGroup() ); |
96 | 0 | InstanceBatch *batch = 0; |
97 | | |
98 | | //Base material couldn't be found |
99 | 0 | if( !mat ) |
100 | 0 | return 0; |
101 | | |
102 | 0 | switch( mInstancingTechnique ) |
103 | 0 | { |
104 | 0 | case ShaderBased: |
105 | 0 | batch = OGRE_NEW InstanceBatchShader( this, mMeshReference, mat, suggestedSize, |
106 | 0 | 0, mName + "/TempBatch" ); |
107 | 0 | break; |
108 | 0 | case TextureVTF: |
109 | 0 | batch = OGRE_NEW InstanceBatchVTF( this, mMeshReference, mat, suggestedSize, |
110 | 0 | 0, mName + "/TempBatch" ); |
111 | 0 | static_cast<InstanceBatchVTF*>(batch)->setBoneDualQuaternions((mInstancingFlags & IM_USEBONEDUALQUATERNIONS) != 0); |
112 | 0 | static_cast<InstanceBatchVTF*>(batch)->setUseOneWeight((mInstancingFlags & IM_USEONEWEIGHT) != 0); |
113 | 0 | static_cast<InstanceBatchVTF*>(batch)->setForceOneWeight((mInstancingFlags & IM_FORCEONEWEIGHT) != 0); |
114 | 0 | break; |
115 | 0 | case HWInstancingBasic: |
116 | 0 | batch = OGRE_NEW InstanceBatchHW( this, mMeshReference, mat, suggestedSize, |
117 | 0 | 0, mName + "/TempBatch" ); |
118 | 0 | break; |
119 | 0 | case HWInstancingVTF: |
120 | 0 | batch = OGRE_NEW InstanceBatchHW_VTF( this, mMeshReference, mat, suggestedSize, |
121 | 0 | 0, mName + "/TempBatch" ); |
122 | 0 | static_cast<InstanceBatchHW_VTF*>(batch)->setBoneMatrixLookup((mInstancingFlags & IM_VTFBONEMATRIXLOOKUP) != 0, mMaxLookupTableInstances); |
123 | 0 | static_cast<InstanceBatchHW_VTF*>(batch)->setBoneDualQuaternions((mInstancingFlags & IM_USEBONEDUALQUATERNIONS) != 0); |
124 | 0 | static_cast<InstanceBatchHW_VTF*>(batch)->setUseOneWeight((mInstancingFlags & IM_USEONEWEIGHT) != 0); |
125 | 0 | static_cast<InstanceBatchHW_VTF*>(batch)->setForceOneWeight((mInstancingFlags & IM_FORCEONEWEIGHT) != 0); |
126 | 0 | break; |
127 | 0 | default: |
128 | 0 | OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, |
129 | 0 | "Unimplemented instancing technique: " + |
130 | 0 | StringConverter::toString(mInstancingTechnique), |
131 | 0 | "InstanceBatch::getMaxOrBestNumInstancesPerBatches()"); |
132 | 0 | } |
133 | | |
134 | 0 | const size_t retVal = batch->calculateMaxNumInstances( mMeshReference->getSubMesh(mSubMeshIdx), |
135 | 0 | flags ); |
136 | |
|
137 | 0 | OGRE_DELETE batch; |
138 | |
|
139 | 0 | return retVal; |
140 | 0 | } |
141 | | //---------------------------------------------------------------------- |
142 | | InstancedEntity* InstanceManager::createInstancedEntity( const String &materialName ) |
143 | 0 | { |
144 | 0 | InstanceBatch *instanceBatch; |
145 | |
|
146 | 0 | if( mInstanceBatches.empty() || |
147 | 0 | (mInstanceBatches[materialName].size () == 0)) |
148 | 0 | instanceBatch = buildNewBatch( materialName, true ); |
149 | 0 | else |
150 | 0 | instanceBatch = getFreeBatch( materialName ); |
151 | |
|
152 | 0 | return instanceBatch->createInstancedEntity(); |
153 | 0 | } |
154 | | //----------------------------------------------------------------------- |
155 | | inline InstanceBatch* InstanceManager::getFreeBatch( const String &materialName ) |
156 | 0 | { |
157 | 0 | InstanceBatchVec &batchVec = mInstanceBatches[materialName]; |
158 | 0 | for (auto *b : batchVec) |
159 | 0 | { |
160 | 0 | if(!b->isBatchFull()) |
161 | 0 | return b; |
162 | 0 | } |
163 | | |
164 | | //None found, or they're all full |
165 | 0 | return buildNewBatch( materialName, false ); |
166 | 0 | } |
167 | | //----------------------------------------------------------------------- |
168 | | InstanceBatch* InstanceManager::buildNewBatch( const String &materialName, bool firstTime ) |
169 | 0 | { |
170 | | //Get the bone to index map for the batches |
171 | 0 | Mesh::IndexMap &idxMap = mMeshReference->getSubMesh(mSubMeshIdx)->blendIndexToBoneIndexMap; |
172 | 0 | idxMap = idxMap.empty() ? mMeshReference->sharedBlendIndexToBoneIndexMap : idxMap; |
173 | | |
174 | | //Get the material |
175 | 0 | MaterialPtr mat = MaterialManager::getSingleton().getByName( materialName, |
176 | 0 | mMeshReference->getGroup() ); |
177 | | |
178 | | //Get the array of batches grouped by this material |
179 | 0 | InstanceBatchVec &materialInstanceBatch = mInstanceBatches[materialName]; |
180 | |
|
181 | 0 | InstanceBatch *batch = 0; |
182 | |
|
183 | 0 | switch( mInstancingTechnique ) |
184 | 0 | { |
185 | 0 | case ShaderBased: |
186 | 0 | batch = OGRE_NEW InstanceBatchShader( this, mMeshReference, mat, mInstancesPerBatch, |
187 | 0 | &idxMap, mName + "/InstanceBatch_" + |
188 | 0 | StringConverter::toString(mIdCount++) ); |
189 | 0 | break; |
190 | 0 | case TextureVTF: |
191 | 0 | batch = OGRE_NEW InstanceBatchVTF( this, mMeshReference, mat, mInstancesPerBatch, |
192 | 0 | &idxMap, mName + "/InstanceBatch_" + |
193 | 0 | StringConverter::toString(mIdCount++) ); |
194 | 0 | static_cast<InstanceBatchVTF*>(batch)->setBoneDualQuaternions((mInstancingFlags & IM_USEBONEDUALQUATERNIONS) != 0); |
195 | 0 | static_cast<InstanceBatchVTF*>(batch)->setUseOneWeight((mInstancingFlags & IM_USEONEWEIGHT) != 0); |
196 | 0 | static_cast<InstanceBatchVTF*>(batch)->setForceOneWeight((mInstancingFlags & IM_FORCEONEWEIGHT) != 0); |
197 | 0 | break; |
198 | 0 | case HWInstancingBasic: |
199 | 0 | batch = OGRE_NEW InstanceBatchHW( this, mMeshReference, mat, mInstancesPerBatch, |
200 | 0 | &idxMap, mName + "/InstanceBatch_" + |
201 | 0 | StringConverter::toString(mIdCount++) ); |
202 | 0 | break; |
203 | 0 | case HWInstancingVTF: |
204 | 0 | batch = OGRE_NEW InstanceBatchHW_VTF( this, mMeshReference, mat, mInstancesPerBatch, |
205 | 0 | &idxMap, mName + "/InstanceBatch_" + |
206 | 0 | StringConverter::toString(mIdCount++) ); |
207 | 0 | static_cast<InstanceBatchHW_VTF*>(batch)->setBoneMatrixLookup((mInstancingFlags & IM_VTFBONEMATRIXLOOKUP) != 0, mMaxLookupTableInstances); |
208 | 0 | static_cast<InstanceBatchHW_VTF*>(batch)->setBoneDualQuaternions((mInstancingFlags & IM_USEBONEDUALQUATERNIONS) != 0); |
209 | 0 | static_cast<InstanceBatchHW_VTF*>(batch)->setUseOneWeight((mInstancingFlags & IM_USEONEWEIGHT) != 0); |
210 | 0 | static_cast<InstanceBatchHW_VTF*>(batch)->setForceOneWeight((mInstancingFlags & IM_FORCEONEWEIGHT) != 0); |
211 | 0 | break; |
212 | 0 | default: |
213 | 0 | OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, |
214 | 0 | "Unimplemented instancing technique: " + |
215 | 0 | StringConverter::toString(mInstancingTechnique), |
216 | 0 | "InstanceBatch::buildNewBatch()"); |
217 | 0 | } |
218 | | |
219 | 0 | batch->_notifyManager( mSceneManager ); |
220 | | |
221 | |
|
222 | 0 | if( !firstTime ) |
223 | 0 | { |
224 | | //TODO: Check different materials have the same mInstancesPerBatch upper limit |
225 | | //otherwise we can't share |
226 | 0 | batch->buildFrom( mMeshReference->getSubMesh(mSubMeshIdx), mSharedRenderOperation ); |
227 | 0 | } |
228 | 0 | else |
229 | 0 | { |
230 | | //Ensure we don't request more than we can |
231 | 0 | const size_t maxInstPerBatch = batch->calculateMaxNumInstances( mMeshReference-> |
232 | 0 | getSubMesh(mSubMeshIdx), mInstancingFlags ); |
233 | 0 | mInstancesPerBatch = std::min( maxInstPerBatch, mInstancesPerBatch ); |
234 | 0 | batch->_setInstancesPerBatch( mInstancesPerBatch ); |
235 | |
|
236 | 0 | OgreAssert(mInstancesPerBatch, "unsupported instancing technique"); |
237 | | //TODO: Create a "merge" function that merges all submeshes into one big submesh |
238 | | //instead of just sending submesh #0 |
239 | | |
240 | | //Get the RenderOperation to be shared with further instances. |
241 | 0 | mSharedRenderOperation = batch->build( mMeshReference->getSubMesh(mSubMeshIdx) ); |
242 | 0 | } |
243 | | |
244 | 0 | const BatchSettings &batchSettings = mBatchSettings[materialName]; |
245 | 0 | batch->setCastShadows( batchSettings.setting[CAST_SHADOWS] ); |
246 | | |
247 | | //Batches need to be part of a scene node so that their renderable can be rendered |
248 | 0 | SceneNode *sceneNode = mSceneManager->getRootSceneNode()->createChildSceneNode(); |
249 | 0 | sceneNode->attachObject( batch ); |
250 | 0 | sceneNode->showBoundingBox( batchSettings.setting[SHOW_BOUNDINGBOX] ); |
251 | |
|
252 | 0 | materialInstanceBatch.push_back( batch ); |
253 | |
|
254 | 0 | return batch; |
255 | 0 | } |
256 | | //----------------------------------------------------------------------- |
257 | | void InstanceManager::cleanupEmptyBatches(void) |
258 | 0 | { |
259 | | //Do this now to avoid any dangling pointer inside mDirtyBatches |
260 | 0 | _updateDirtyBatches(); |
261 | |
|
262 | 0 | InstanceBatchMap::iterator itor = mInstanceBatches.begin(); |
263 | 0 | InstanceBatchMap::iterator end = mInstanceBatches.end(); |
264 | |
|
265 | 0 | while( itor != end ) |
266 | 0 | { |
267 | 0 | InstanceBatchVec::iterator it = itor->second.begin(); |
268 | 0 | InstanceBatchVec::iterator en = itor->second.end(); |
269 | |
|
270 | 0 | while( it != en ) |
271 | 0 | { |
272 | 0 | if( (*it)->isBatchUnused() ) |
273 | 0 | { |
274 | 0 | OGRE_DELETE *it; |
275 | | //Remove it from the list swapping with the last element and popping back |
276 | 0 | size_t idx = it - itor->second.begin(); |
277 | 0 | *it = itor->second.back(); |
278 | 0 | itor->second.pop_back(); |
279 | | |
280 | | //Restore invalidated iterators |
281 | 0 | it = itor->second.begin() + idx; |
282 | 0 | en = itor->second.end(); |
283 | 0 | } |
284 | 0 | else |
285 | 0 | ++it; |
286 | 0 | } |
287 | |
|
288 | 0 | ++itor; |
289 | 0 | } |
290 | | |
291 | | //By this point it may happen that all mInstanceBatches' objects are also empty |
292 | | //however if we call mInstanceBatches.clear(), next time we'll create an InstancedObject |
293 | | //we'll end up calling buildFirstTime() instead of buildNewBatch(), which is not the idea |
294 | | //(takes more time and will leak the shared render operation) |
295 | 0 | } |
296 | | //----------------------------------------------------------------------- |
297 | | void InstanceManager::defragmentBatches( bool optimizeCull, |
298 | | InstanceBatch::InstancedEntityVec &usedEntities, |
299 | | InstanceBatch::CustomParamsVec &usedParams, |
300 | | InstanceBatchVec &fragmentedBatches ) |
301 | 0 | { |
302 | 0 | InstanceBatchVec::iterator itor = fragmentedBatches.begin(); |
303 | 0 | InstanceBatchVec::iterator end = fragmentedBatches.end(); |
304 | |
|
305 | 0 | while( itor != end && !usedEntities.empty() ) |
306 | 0 | { |
307 | 0 | if( !(*itor)->isStatic() ) |
308 | 0 | (*itor)->_defragmentBatch( optimizeCull, usedEntities, usedParams ); |
309 | 0 | ++itor; |
310 | 0 | } |
311 | |
|
312 | 0 | InstanceBatchVec::iterator lastImportantBatch = itor; |
313 | |
|
314 | 0 | while( itor != end ) |
315 | 0 | { |
316 | 0 | if( !(*itor)->isStatic() ) |
317 | 0 | { |
318 | | //If we get here, this means we hit remaining batches which will be unused. |
319 | | //Destroy them |
320 | | //Call this to avoid freeing InstancedEntities that were just reparented |
321 | 0 | (*itor)->_defragmentBatchDiscard(); |
322 | 0 | OGRE_DELETE *itor; |
323 | 0 | } |
324 | 0 | else |
325 | 0 | { |
326 | | //This isn't a meaningless batch, move it forward so it doesn't get wipe |
327 | | //when we resize the container (faster than removing element by element) |
328 | 0 | *lastImportantBatch++ = *itor; |
329 | 0 | } |
330 | |
|
331 | 0 | ++itor; |
332 | 0 | } |
333 | | |
334 | | //Remove remaining batches all at once from the vector |
335 | 0 | const size_t remainingBatches = end - lastImportantBatch; |
336 | 0 | fragmentedBatches.resize( fragmentedBatches.size() - remainingBatches ); |
337 | 0 | } |
338 | | //----------------------------------------------------------------------- |
339 | | void InstanceManager::defragmentBatches( bool optimizeCulling ) |
340 | 0 | { |
341 | | //Do this now to avoid any dangling pointer inside mDirtyBatches |
342 | 0 | _updateDirtyBatches(); |
343 | | |
344 | | //Do this for every material |
345 | 0 | for (auto& b : mInstanceBatches) |
346 | 0 | { |
347 | 0 | InstanceBatch::InstancedEntityVec usedEntities; |
348 | 0 | InstanceBatch::CustomParamsVec usedParams; |
349 | 0 | usedEntities.reserve(b.second.size() * mInstancesPerBatch ); |
350 | | |
351 | | //Collect all Instanced Entities being used by _all_ batches from this material |
352 | 0 | for (auto *it : b.second) |
353 | 0 | { |
354 | | //Don't collect instances from static batches, we assume they're correctly set |
355 | | //Plus, we don't want to put InstancedEntities from non-static into static batches |
356 | 0 | if (!it->isStatic()) |
357 | 0 | it->getInstancedEntitiesInUse(usedEntities, usedParams); |
358 | 0 | } |
359 | |
|
360 | 0 | defragmentBatches(optimizeCulling, usedEntities, usedParams, b.second ); |
361 | 0 | } |
362 | 0 | } |
363 | | //----------------------------------------------------------------------- |
364 | | void InstanceManager::setSetting( BatchSettingId id, bool value, const String &materialName ) |
365 | 0 | { |
366 | 0 | assert( id < NUM_SETTINGS ); |
367 | |
|
368 | 0 | if( materialName == BLANKSTRING ) |
369 | 0 | { |
370 | | //Setup all existing materials |
371 | 0 | for (auto& i : mInstanceBatches) |
372 | 0 | { |
373 | 0 | mBatchSettings[i.first].setting[id] = value; |
374 | 0 | applySettingToBatches( id, value, i.second ); |
375 | 0 | } |
376 | 0 | } |
377 | 0 | else |
378 | 0 | { |
379 | | //Setup a given material |
380 | 0 | mBatchSettings[materialName].setting[id] = value; |
381 | |
|
382 | 0 | InstanceBatchMap::const_iterator itor = mInstanceBatches.find( materialName ); |
383 | | //Don't crash or throw if the batch with that material hasn't been created yet |
384 | 0 | if( itor != mInstanceBatches.end() ) |
385 | 0 | applySettingToBatches( id, value, itor->second ); |
386 | 0 | } |
387 | 0 | } |
388 | | //----------------------------------------------------------------------- |
389 | | bool InstanceManager::getSetting( BatchSettingId id, const String &materialName ) const |
390 | 0 | { |
391 | 0 | assert( id < NUM_SETTINGS ); |
392 | |
|
393 | 0 | BatchSettingsMap::const_iterator itor = mBatchSettings.find( materialName ); |
394 | 0 | if( itor != mBatchSettings.end() ) |
395 | 0 | return itor->second.setting[id]; //Return current setting |
396 | | |
397 | | //Return default |
398 | 0 | return BatchSettings().setting[id]; |
399 | 0 | } |
400 | | bool InstanceManager::hasSettings(const String& materialName) const |
401 | 0 | { |
402 | 0 | return mBatchSettings.find(materialName) != mBatchSettings.end(); |
403 | 0 | } |
404 | | //----------------------------------------------------------------------- |
405 | | void InstanceManager::applySettingToBatches( BatchSettingId id, bool value, |
406 | | const InstanceBatchVec &container ) |
407 | 0 | { |
408 | 0 | for (auto *s : container) |
409 | 0 | { |
410 | 0 | switch(id) |
411 | 0 | { |
412 | 0 | case CAST_SHADOWS: |
413 | 0 | s->setCastShadows(value); |
414 | 0 | break; |
415 | 0 | case SHOW_BOUNDINGBOX: |
416 | 0 | s->getParentSceneNode()->showBoundingBox(value); |
417 | 0 | break; |
418 | 0 | default: |
419 | 0 | break; |
420 | 0 | } |
421 | 0 | } |
422 | 0 | } |
423 | | //----------------------------------------------------------------------- |
424 | | void InstanceManager::setBatchesAsStaticAndUpdate( bool bStatic ) |
425 | 0 | { |
426 | 0 | for (auto& b : mInstanceBatches) |
427 | 0 | { |
428 | 0 | for (auto *it : b.second) |
429 | 0 | { |
430 | 0 | it->setStaticAndUpdate(bStatic); |
431 | 0 | } |
432 | 0 | } |
433 | 0 | } |
434 | | //----------------------------------------------------------------------- |
435 | | void InstanceManager::_addDirtyBatch( InstanceBatch *dirtyBatch ) |
436 | 0 | { |
437 | 0 | if( mDirtyBatches.empty() ) |
438 | 0 | mSceneManager->_addDirtyInstanceManager( this ); |
439 | |
|
440 | 0 | mDirtyBatches.push_back( dirtyBatch ); |
441 | 0 | } |
442 | | //----------------------------------------------------------------------- |
443 | | void InstanceManager::_updateDirtyBatches(void) |
444 | 0 | { |
445 | 0 | for (auto *d : mDirtyBatches) |
446 | 0 | { |
447 | 0 | d->_updateBounds(); |
448 | 0 | } |
449 | 0 | mDirtyBatches.clear(); |
450 | 0 | } |
451 | | //----------------------------------------------------------------------- |
452 | | // Helper functions to unshare the vertices |
453 | | //----------------------------------------------------------------------- |
454 | | typedef std::map<uint32, uint32> IndicesMap; |
455 | | |
456 | | template< typename TIndexType > |
457 | | void collectUsedIndices(IndicesMap& indicesMap, IndexData* idxData) |
458 | 0 | { |
459 | 0 | HardwareBufferLockGuard indexLock(idxData->indexBuffer, |
460 | 0 | idxData->indexStart * sizeof(TIndexType), |
461 | 0 | idxData->indexCount * sizeof(TIndexType), |
462 | 0 | HardwareBuffer::HBL_READ_ONLY); |
463 | 0 | TIndexType *data = (TIndexType*)indexLock.pData; |
464 | |
|
465 | 0 | for (size_t i = 0; i < idxData->indexCount; i++) |
466 | 0 | { |
467 | 0 | TIndexType index = data[i]; |
468 | 0 | if (indicesMap.find(index) == indicesMap.end()) |
469 | 0 | { |
470 | | //We need to guarantee that the size is read before an entry is added, hence these are on separate lines. |
471 | 0 | uint32 size = (uint32)(indicesMap.size()); |
472 | 0 | indicesMap[index] = size; |
473 | 0 | } |
474 | 0 | } |
475 | 0 | } Unexecuted instantiation: void Ogre::collectUsedIndices<unsigned short>(std::__1::map<unsigned int, unsigned int, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, unsigned int> > >&, Ogre::IndexData*) Unexecuted instantiation: void Ogre::collectUsedIndices<unsigned int>(std::__1::map<unsigned int, unsigned int, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, unsigned int> > >&, Ogre::IndexData*) |
476 | | //----------------------------------------------------------------------- |
477 | | template< typename TIndexType > |
478 | | void copyIndexBuffer(IndexData* idxData, const IndicesMap& indicesMap, uint32 indexStart) |
479 | 0 | { |
480 | 0 | size_t start = std::max(indexStart, idxData->indexStart); |
481 | 0 | size_t count = idxData->indexCount - (start - idxData->indexStart); |
482 | | |
483 | | //We get errors if we try to lock a zero size buffer. |
484 | 0 | if (count == 0) { |
485 | 0 | return; |
486 | 0 | } |
487 | 0 | HardwareBufferLockGuard indexLock(idxData->indexBuffer, |
488 | 0 | start * sizeof(TIndexType), |
489 | 0 | count * sizeof(TIndexType), |
490 | 0 | HardwareBuffer::HBL_NORMAL); |
491 | 0 | TIndexType *data = (TIndexType*)indexLock.pData; |
492 | |
|
493 | 0 | for (size_t i = 0; i < count; i++) |
494 | 0 | { |
495 | | //Access data and write on two separate lines, to avoid compiler confusion. |
496 | 0 | auto originalPos = data[i]; |
497 | 0 | auto indexEntry = indicesMap.find(originalPos); |
498 | 0 | data[i] = (TIndexType)(indexEntry->second); |
499 | 0 | } |
500 | 0 | } Unexecuted instantiation: void Ogre::copyIndexBuffer<unsigned short>(Ogre::IndexData*, std::__1::map<unsigned int, unsigned int, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, unsigned int> > > const&, unsigned int) Unexecuted instantiation: void Ogre::copyIndexBuffer<unsigned int>(Ogre::IndexData*, std::__1::map<unsigned int, unsigned int, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, unsigned int> > > const&, unsigned int) |
501 | | //----------------------------------------------------------------------- |
502 | | void InstanceManager::unshareVertices(const Ogre::MeshPtr &mesh) |
503 | 0 | { |
504 | | // Retrieve data to copy bone assignments |
505 | 0 | const Mesh::VertexBoneAssignmentList& boneAssignments = mesh->getBoneAssignments(); |
506 | 0 | size_t curVertexOffset = 0; |
507 | | |
508 | | // Access shared vertices |
509 | 0 | VertexData* sharedVertexData = mesh->sharedVertexData; |
510 | |
|
511 | 0 | for (size_t subMeshIdx = 0; subMeshIdx < mesh->getNumSubMeshes(); subMeshIdx++) |
512 | 0 | { |
513 | 0 | SubMesh *subMesh = mesh->getSubMesh(subMeshIdx); |
514 | |
|
515 | 0 | IndexData *indexData = subMesh->indexData; |
516 | 0 | HardwareIndexBuffer::IndexType idxType = indexData->indexBuffer->getType(); |
517 | 0 | IndicesMap indicesMap; |
518 | 0 | if (idxType == HardwareIndexBuffer::IT_16BIT) { |
519 | 0 | collectUsedIndices<uint16>(indicesMap, indexData); |
520 | 0 | } else { |
521 | 0 | collectUsedIndices<uint32>(indicesMap, indexData); |
522 | 0 | } |
523 | | |
524 | | //Also collect indices for all LOD faces. |
525 | 0 | for (auto& lodIndex : subMesh->mLodFaceList) { |
526 | | //Typically the LOD indices would use the same buffer type as the main index. But we'll check to make extra sure. |
527 | 0 | if (lodIndex->indexBuffer->getType() == HardwareIndexBuffer::IT_16BIT) { |
528 | 0 | collectUsedIndices<uint16>(indicesMap, lodIndex); |
529 | 0 | } else { |
530 | 0 | collectUsedIndices<uint32>(indicesMap, lodIndex); |
531 | 0 | } |
532 | 0 | } |
533 | |
|
534 | 0 | VertexData *newVertexData = new VertexData(); |
535 | 0 | newVertexData->vertexCount = indicesMap.size(); |
536 | 0 | newVertexData->vertexDeclaration = sharedVertexData->vertexDeclaration->clone(); |
537 | |
|
538 | 0 | for (uint16 bufIdx = 0; bufIdx < uint16(sharedVertexData->vertexBufferBinding->getBufferCount()); bufIdx++) |
539 | 0 | { |
540 | 0 | HardwareVertexBufferSharedPtr sharedVertexBuffer = sharedVertexData->vertexBufferBinding->getBuffer(bufIdx); |
541 | 0 | size_t vertexSize = sharedVertexBuffer->getVertexSize(); |
542 | |
|
543 | 0 | HardwareVertexBufferSharedPtr newVertexBuffer = HardwareBufferManager::getSingleton().createVertexBuffer |
544 | 0 | (vertexSize, newVertexData->vertexCount, sharedVertexBuffer->getUsage(), sharedVertexBuffer->hasShadowBuffer()); |
545 | |
|
546 | 0 | HardwareBufferLockGuard oldLock(sharedVertexBuffer, 0, sharedVertexData->vertexCount * vertexSize, HardwareBuffer::HBL_READ_ONLY); |
547 | 0 | HardwareBufferLockGuard newLock(newVertexBuffer, 0, newVertexData->vertexCount * vertexSize, HardwareBuffer::HBL_NORMAL); |
548 | |
|
549 | 0 | for (auto& id : indicesMap) |
550 | 0 | { |
551 | 0 | memcpy((uint8*)newLock.pData + vertexSize * id.second, |
552 | 0 | (uint8*)oldLock.pData + vertexSize * id.first, vertexSize); |
553 | 0 | } |
554 | |
|
555 | 0 | newVertexData->vertexBufferBinding->setBinding(bufIdx, newVertexBuffer); |
556 | 0 | } |
557 | |
|
558 | 0 | if (idxType == HardwareIndexBuffer::IT_16BIT) |
559 | 0 | { |
560 | 0 | copyIndexBuffer<uint16>(indexData, indicesMap, 0); |
561 | 0 | } |
562 | 0 | else |
563 | 0 | { |
564 | 0 | copyIndexBuffer<uint32>(indexData, indicesMap, 0); |
565 | 0 | } |
566 | | |
567 | | //Need to adjust all of the LOD faces too. |
568 | 0 | size_t lastIndexEnd = 0; |
569 | 0 | for (size_t i = 0; i < subMesh->mLodFaceList.size(); ++i) { |
570 | 0 | auto lodIndex = subMesh->mLodFaceList[i]; |
571 | | //When using "generated" mesh lods, the indices are shared, so that index[n] would overlap with |
572 | | //index[n]. We need to take this into account to make sure we don't process data twice (with incorrect |
573 | | //data as a result). We check if the index either is the first one, or if it has a different buffer |
574 | | //than the previous one we processed. Since the overlap seems to be progressing we only need to keep |
575 | | // track of the last used index. |
576 | 0 | if (i == 0 || lodIndex->indexBuffer != subMesh->mLodFaceList[i - 1]->indexBuffer) { |
577 | 0 | lastIndexEnd = 0; |
578 | 0 | } |
579 | | |
580 | | //Typically the LOD indices would use the same buffer type as the main index. But we'll check to make extra sure. |
581 | 0 | if (lodIndex->indexBuffer->getType() == HardwareIndexBuffer::IT_16BIT) { |
582 | 0 | copyIndexBuffer<uint16>(lodIndex, indicesMap, lastIndexEnd); |
583 | 0 | } else { |
584 | 0 | copyIndexBuffer<uint32>(lodIndex, indicesMap, lastIndexEnd); |
585 | 0 | } |
586 | 0 | lastIndexEnd = lodIndex->indexStart + lodIndex->indexCount; |
587 | 0 | } |
588 | | |
589 | | // Store new attributes |
590 | 0 | subMesh->resetVertexData(newVertexData); |
591 | | |
592 | | // Transfer bone assignments to the submesh |
593 | 0 | size_t offset = curVertexOffset + newVertexData->vertexCount; |
594 | 0 | for (auto& b : boneAssignments) |
595 | 0 | { |
596 | 0 | size_t vertexIdx = b.first; |
597 | 0 | if (vertexIdx > offset) |
598 | 0 | break; |
599 | | |
600 | 0 | VertexBoneAssignment boneAssignment = b.second; |
601 | 0 | boneAssignment.vertexIndex = static_cast<unsigned int>(boneAssignment.vertexIndex - curVertexOffset); |
602 | 0 | subMesh->addBoneAssignment(boneAssignment); |
603 | 0 | } |
604 | 0 | curVertexOffset = newVertexData->vertexCount + 1; |
605 | 0 | } |
606 | | |
607 | | // Release shared vertex data |
608 | 0 | mesh->resetVertexData(); |
609 | 0 | mesh->clearBoneAssignments(); |
610 | |
|
611 | 0 | if( mesh->isEdgeListBuilt() ) |
612 | 0 | { |
613 | 0 | mesh->freeEdgeList(); |
614 | 0 | mesh->buildEdgeList(); |
615 | 0 | } |
616 | 0 | } |
617 | | //----------------------------------------------------------------------- |
618 | | InstanceManager::InstanceBatchIterator InstanceManager::getInstanceBatchIterator( const String &materialName ) const |
619 | 0 | { |
620 | 0 | InstanceBatchMap::const_iterator it = mInstanceBatches.find( materialName ); |
621 | 0 | if(it != mInstanceBatches.end()) |
622 | 0 | return InstanceBatchIterator( it->second.begin(), it->second.end() ); |
623 | | |
624 | 0 | OGRE_EXCEPT(Exception::ERR_INVALID_STATE, "Cannot create instance batch iterator. " |
625 | 0 | "Material " + materialName + " cannot be found"); |
626 | 0 | } |
627 | | } |