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-2013 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 "OgreInstancedGeometry.h"
30 #include "OgreEntity.h"
31 #include "OgreSubEntity.h"
32 #include "OgreSceneNode.h"
33 #include "OgreException.h"
34 #include "OgreMesh.h"
35 #include "OgreSubMesh.h"
36 #include "OgreLogManager.h"
37 #include "OgreSceneManager.h"
38 #include "OgreCamera.h"
39 #include "OgreMaterialManager.h"
40 #include "OgreRoot.h"
41 #include "OgreRenderSystem.h"
42 #include "OgreEdgeListBuilder.h"
43 #include "OgreStringConverter.h"
44 
45 namespace Ogre {
46 
47 	#define BatchInstance_RANGE 1024
48 	#define BatchInstance_HALF_RANGE 512
49 	#define BatchInstance_MAX_INDEX 511
50 	#define BatchInstance_MIN_INDEX -512
51 
52 	//--------------------------------------------------------------------------
InstancedGeometry(SceneManager * owner,const String & name)53 	InstancedGeometry::InstancedGeometry(SceneManager* owner, const String& name):
54 		mOwner(owner),
55 		mName(name),
56 		mBuilt(false),
57 		mUpperDistance(0.0f),
58 		mSquaredUpperDistance(0.0f),
59 		mCastShadows(false),
60 		mBatchInstanceDimensions(Vector3(1000,1000,1000)),
61 		mHalfBatchInstanceDimensions(Vector3(500,500,500)),
62 		mOrigin(Vector3(0,0,0)),
63 		mVisible(true),
64         mProvideWorldInverses(false),
65         mRenderQueueID(RENDER_QUEUE_MAIN),
66         mRenderQueueIDSet(false),
67 		mObjectCount(0),
68 		mInstancedGeometryInstance(0),
69 		mSkeletonInstance(0),
70         mAnimationState(0)
71 	{
72 		mBaseSkeleton.setNull();
73 	}
74 	//--------------------------------------------------------------------------
~InstancedGeometry()75 	InstancedGeometry::~InstancedGeometry()
76 	{
77 		reset();
78 		if(mSkeletonInstance)
79 			OGRE_DELETE mSkeletonInstance;
80 
81 
82 	}
83 	//--------------------------------------------------------------------------
getInstancedGeometryInstance(void)84 	InstancedGeometry::BatchInstance*InstancedGeometry::getInstancedGeometryInstance(void)
85 	{
86 		if (!mInstancedGeometryInstance)
87 		{
88 			uint32 index = 0;
89 			// Make a name
90 			StringUtil::StrStreamType str;
91 			str << mName << ":" << index;
92 
93 			mInstancedGeometryInstance = OGRE_NEW BatchInstance(this, str.str(), mOwner, index);
94 			mOwner->injectMovableObject(mInstancedGeometryInstance);
95 			mInstancedGeometryInstance->setVisible(mVisible);
96 			mInstancedGeometryInstance->setCastShadows(mCastShadows);
97 			if (mRenderQueueIDSet)
98 			{
99 				mInstancedGeometryInstance->setRenderQueueGroup(mRenderQueueID);
100 			}
101 			mBatchInstanceMap[index] = mInstancedGeometryInstance;
102 
103 
104 		}
105 		return mInstancedGeometryInstance;
106 	}
107 	//--------------------------------------------------------------------------
getBatchInstance(const AxisAlignedBox & bounds,bool autoCreate)108 	InstancedGeometry::BatchInstance* InstancedGeometry::getBatchInstance(const AxisAlignedBox& bounds,
109 		bool autoCreate)
110 	{
111 		if (bounds.isNull())
112 			return 0;
113 
114 		// Get the BatchInstance which has the largest overlapping volume
115 		const Vector3 min = bounds.getMinimum();
116 		const Vector3 max = bounds.getMaximum();
117 
118 		// Get the min and max BatchInstance indexes
119 		ushort minx, miny, minz;
120 		ushort maxx, maxy, maxz;
121 		getBatchInstanceIndexes(min, minx, miny, minz);
122 		getBatchInstanceIndexes(max, maxx, maxy, maxz);
123 		Real maxVolume = 0.0f;
124 		ushort finalx =0 , finaly = 0, finalz = 0;
125 		for (ushort x = minx; x <= maxx; ++x)
126 		{
127 			for (ushort y = miny; y <= maxy; ++y)
128 			{
129 				for (ushort z = minz; z <= maxz; ++z)
130 				{
131 					Real vol = getVolumeIntersection(bounds, x, y, z);
132 					if (vol > maxVolume)
133 					{
134 						maxVolume = vol;
135 						finalx = x;
136 						finaly = y;
137 						finalz = z;
138 					}
139 
140 				}
141 			}
142 		}
143 
144 		assert(maxVolume > 0.0f &&
145 			"Static geometry: Problem determining closest volume match!");
146 
147 		return getBatchInstance(finalx, finaly, finalz, autoCreate);
148 
149 	}
150 	//--------------------------------------------------------------------------
getVolumeIntersection(const AxisAlignedBox & box,ushort x,ushort y,ushort z)151 	Real InstancedGeometry::getVolumeIntersection(const AxisAlignedBox& box,
152 		ushort x, ushort y, ushort z)
153 	{
154 		// Get bounds of indexed BatchInstance
155 		AxisAlignedBox BatchInstanceBounds = getBatchInstanceBounds(x, y, z);
156 		AxisAlignedBox intersectBox = BatchInstanceBounds.intersection(box);
157 		// return a 'volume' which ignores zero dimensions
158 		// since we only use this for relative comparisons of the same bounds
159 		// this will still be internally consistent
160 		Vector3 boxdiff = box.getMaximum() - box.getMinimum();
161 		Vector3 intersectDiff = intersectBox.getMaximum() - intersectBox.getMinimum();
162 
163 		return (boxdiff.x == 0 ? 1 : intersectDiff.x) *
164 			(boxdiff.y == 0 ? 1 : intersectDiff.y) *
165 			(boxdiff.z == 0 ? 1 : intersectDiff.z);
166 
167 	}
168 	//--------------------------------------------------------------------------
getBatchInstanceBounds(ushort x,ushort y,ushort z)169 	AxisAlignedBox InstancedGeometry::getBatchInstanceBounds(ushort x, ushort y, ushort z)
170 	{
171 		Vector3 min(
172 			((Real)x - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.x + mOrigin.x,
173 			((Real)y - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.y + mOrigin.y,
174 			((Real)z - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.z + mOrigin.z
175 			);
176 		Vector3 max = min + mBatchInstanceDimensions;
177 		return AxisAlignedBox(min, max);
178 	}
179 	//--------------------------------------------------------------------------
getBatchInstanceCentre(ushort x,ushort y,ushort z)180  	Vector3 InstancedGeometry::getBatchInstanceCentre(ushort x, ushort y, ushort z)
181 	{
182 		return Vector3(
183 			((Real)x - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.x + mOrigin.x
184 				+ mHalfBatchInstanceDimensions.x,
185 			((Real)y - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.y + mOrigin.y
186 				+ mHalfBatchInstanceDimensions.y,
187 			((Real)z - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.z + mOrigin.z
188 				+ mHalfBatchInstanceDimensions.z
189 			);
190 	}
191 	//--------------------------------------------------------------------------
getBatchInstance(ushort x,ushort y,ushort z,bool autoCreate)192 	InstancedGeometry::BatchInstance* InstancedGeometry::getBatchInstance(
193 			ushort x, ushort y, ushort z, bool autoCreate)
194 	{
195 		uint32 index = packIndex(x, y, z);
196 		BatchInstance* ret = getBatchInstance(index);
197 		if (!ret && autoCreate)
198 		{
199 			// Make a name
200 			StringUtil::StrStreamType str;
201 			str << mName << ":" << index;
202 			// Calculate the BatchInstance centre
203 			Vector3 centre(0,0,0);// = getBatchInstanceCentre(x, y, z);
204 			ret = OGRE_NEW BatchInstance(this, str.str(), mOwner, index/*, centre*/);
205 			mOwner->injectMovableObject(ret);
206 			ret->setVisible(mVisible);
207 			ret->setCastShadows(mCastShadows);
208 			if (mRenderQueueIDSet)
209 			{
210 				ret->setRenderQueueGroup(mRenderQueueID);
211 			}
212 			mBatchInstanceMap[index] = ret;
213 		}
214 		return ret;
215 	}
216 	//--------------------------------------------------------------------------
getBatchInstance(uint32 index)217 	InstancedGeometry::BatchInstance* InstancedGeometry::getBatchInstance(uint32 index)
218 	{
219 		BatchInstanceMap::iterator i = mBatchInstanceMap.find(index);
220 		if (i != mBatchInstanceMap.end())
221 		{
222 			return i->second;
223 		}
224 		else
225 		{
226 			return 0;
227 		}
228 
229 	}
230 	//--------------------------------------------------------------------------
getBatchInstanceIndexes(const Vector3 & point,ushort & x,ushort & y,ushort & z)231 	void InstancedGeometry::getBatchInstanceIndexes(const Vector3& point,
232 		ushort& x, ushort& y, ushort& z)
233 	{
234 		// Scale the point into multiples of BatchInstance and adjust for origin
235 		Vector3 scaledPoint = (point - mOrigin) / mBatchInstanceDimensions;
236 
237 		// Round down to 'bottom left' point which represents the cell index
238 		int ix = Math::IFloor(scaledPoint.x);
239 		int iy = Math::IFloor(scaledPoint.y);
240 		int iz = Math::IFloor(scaledPoint.z);
241 
242 		// Check bounds
243 		if (ix < BatchInstance_MIN_INDEX || ix > BatchInstance_MAX_INDEX
244 			|| iy < BatchInstance_MIN_INDEX || iy > BatchInstance_MAX_INDEX
245 			|| iz < BatchInstance_MIN_INDEX || iz > BatchInstance_MAX_INDEX)
246 		{
247 			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
248 				"Point out of bounds",
249 				"InstancedGeometry::getBatchInstanceIndexes");
250 		}
251 		// Adjust for the fact that we use unsigned values for simplicity
252 		// (requires less faffing about for negatives give 10-bit packing
253 		x = static_cast<ushort>(ix + BatchInstance_HALF_RANGE);
254 		y = static_cast<ushort>(iy + BatchInstance_HALF_RANGE);
255 		z = static_cast<ushort>(iz + BatchInstance_HALF_RANGE);
256 
257 
258 	}
259 	//--------------------------------------------------------------------------
packIndex(ushort x,ushort y,ushort z)260 	uint32 InstancedGeometry::packIndex(ushort x, ushort y, ushort z)
261 	{
262 		return x + (y << 10) + (z << 20);
263 	}
264 	//--------------------------------------------------------------------------
getBatchInstance(const Vector3 & point,bool autoCreate)265 	InstancedGeometry::BatchInstance* InstancedGeometry::getBatchInstance(const Vector3& point,
266 		bool autoCreate)
267 	{
268 		ushort x, y, z;
269 		getBatchInstanceIndexes(point, x, y, z);
270 		return getBatchInstance(x, y, z, autoCreate);
271 	}
272 	//--------------------------------------------------------------------------
calculateBounds(VertexData * vertexData,const Vector3 & position,const Quaternion & orientation,const Vector3 & scale)273 	AxisAlignedBox InstancedGeometry::calculateBounds(VertexData* vertexData,
274 		const Vector3& position, const Quaternion& orientation,
275 		const Vector3& scale)
276 	{
277 		const VertexElement* posElem =
278 			vertexData->vertexDeclaration->findElementBySemantic(
279 				VES_POSITION);
280 		HardwareVertexBufferSharedPtr vbuf =
281 			vertexData->vertexBufferBinding->getBuffer(posElem->getSource());
282 		unsigned char* vertex =
283 			static_cast<unsigned char*>(
284 				vbuf->lock(HardwareBuffer::HBL_READ_ONLY));
285 		float* pFloat;
286 
287 		Vector3 min = Vector3::ZERO, max = Vector3::UNIT_SCALE;
288 		bool first = true;
289 
290 		for(size_t j = 0; j < vertexData->vertexCount; ++j, vertex += vbuf->getVertexSize())
291 		{
292 			posElem->baseVertexPointerToElement(vertex, &pFloat);
293 
294 			Vector3 pt;
295 
296 			pt.x = (*pFloat++);
297 			pt.y = (*pFloat++);
298 			pt.z = (*pFloat++);
299 			// Transform to world (scale, rotate, translate)
300 			pt = (orientation * (pt * scale)) + position;
301 			if (first)
302 			{
303 				min = max = pt;
304 				first = false;
305 			}
306 			else
307 			{
308 				min.makeFloor(pt);
309 				max.makeCeil(pt);
310 			}
311 
312 		}
313 		vbuf->unlock();
314 		return AxisAlignedBox(min, max);
315 	}
316 	//--------------------------------------------------------------------------
addEntity(Entity * ent,const Vector3 & position,const Quaternion & orientation,const Vector3 & scale)317 	void InstancedGeometry::addEntity(Entity* ent, const Vector3& position,
318 		const Quaternion& orientation, const Vector3& scale)
319 	{
320 
321 		const MeshPtr& msh = ent->getMesh();
322 		// Validate
323 		if (msh->isLodManual())
324 		{
325 			LogManager::getSingleton().logMessage(
326 				"WARNING (InstancedGeometry): Manual LOD is not supported. "
327 				"Using only highest LOD level for mesh " + msh->getName(), LML_CRITICAL);
328 		}
329 
330 		//get the skeleton of the entity, if that's not already done
331 		if(!ent->getMesh()->getSkeleton().isNull()&&mBaseSkeleton.isNull())
332 		{
333 			mBaseSkeleton=ent->getMesh()->getSkeleton();
334 			mSkeletonInstance= OGRE_NEW SkeletonInstance(mBaseSkeleton);
335 			mSkeletonInstance->load();
336 			mAnimationState=ent->getAllAnimationStates();
337 		}
338 		AxisAlignedBox sharedWorldBounds;
339 		// queue this entities submeshes and choice of material
340 		// also build the lists of geometry to be used for the source of lods
341 
342 
343 		for (uint i = 0; i < ent->getNumSubEntities(); ++i)
344 		{
345 			SubEntity* se = ent->getSubEntity(i);
346 			QueuedSubMesh* q = OGRE_NEW QueuedSubMesh();
347 
348 			// Get the geometry for this SubMesh
349 			q->submesh = se->getSubMesh();
350 			q->geometryLodList = determineGeometry(q->submesh);
351 			q->materialName = se->getMaterialName();
352 			q->orientation = orientation;
353 			q->position = position;
354 			q->scale = scale;
355 			q->ID = mObjectCount;
356 			// Determine the bounds based on the highest LOD
357 			q->worldBounds = calculateBounds(
358 				(*q->geometryLodList)[0].vertexData,
359 					position, orientation, scale);
360 
361 			mQueuedSubMeshes.push_back(q);
362 		}
363 		mObjectCount++;
364 
365 	}
366 	//--------------------------------------------------------------------------
367 	InstancedGeometry::SubMeshLodGeometryLinkList*
determineGeometry(SubMesh * sm)368 	InstancedGeometry::determineGeometry(SubMesh* sm)
369 	{
370 		// First, determine if we've already seen this submesh before
371 		SubMeshGeometryLookup::iterator i =
372 			mSubMeshGeometryLookup.find(sm);
373 		if (i != mSubMeshGeometryLookup.end())
374 		{
375 			return i->second;
376 		}
377 		// Otherwise, we have to create a new one
378 		SubMeshLodGeometryLinkList* lodList = OGRE_NEW_T(SubMeshLodGeometryLinkList, MEMCATEGORY_GEOMETRY)();
379 		mSubMeshGeometryLookup[sm] = lodList;
380 		ushort numLods = sm->parent->isLodManual() ? 1 :
381 			sm->parent->getNumLodLevels();
382 		lodList->resize(numLods);
383 		for (ushort lod = 0; lod < numLods; ++lod)
384 		{
385 			SubMeshLodGeometryLink& geomLink = (*lodList)[lod];
386 			IndexData *lodIndexData;
387 			if (lod == 0)
388 			{
389 				lodIndexData = sm->indexData;
390 			}
391 			else
392 			{
393 				lodIndexData = sm->mLodFaceList[lod - 1];
394 			}
395 			// Can use the original mesh geometry?
396 			if (sm->useSharedVertices)
397 			{
398 				if (sm->parent->getNumSubMeshes() == 1)
399 				{
400 					// Ok, this is actually our own anyway
401 					geomLink.vertexData = sm->parent->sharedVertexData;
402 					geomLink.indexData = lodIndexData;
403 				}
404 				else
405 				{
406 					// We have to split it
407 					splitGeometry(sm->parent->sharedVertexData,
408 						lodIndexData, &geomLink);
409 				}
410 			}
411 			else
412 			{
413 				if (lod == 0)
414 				{
415 					// Ok, we can use the existing geometry; should be in full
416 					// use by just this SubMesh
417 					geomLink.vertexData = sm->vertexData;
418 					geomLink.indexData = sm->indexData;
419 				}
420 				else
421 				{
422 					// We have to split it
423 					splitGeometry(sm->vertexData,
424 						lodIndexData, &geomLink);
425 				}
426 			}
427 			assert (geomLink.vertexData->vertexStart == 0 &&
428 				"Cannot use vertexStart > 0 on indexed geometry due to "
429 				"rendersystem incompatibilities - see the docs!");
430 		}
431 
432 
433 		return lodList;
434 	}
435 	//--------------------------------------------------------------------------
splitGeometry(VertexData * vd,IndexData * id,InstancedGeometry::SubMeshLodGeometryLink * targetGeomLink)436 	void InstancedGeometry::splitGeometry(VertexData* vd, IndexData* id,
437 			InstancedGeometry::SubMeshLodGeometryLink* targetGeomLink)
438 	{
439 		// Firstly we need to scan to see how many vertices are being used
440 		// and while we're at it, build the remap we can use later
441 		bool use32bitIndexes =
442 			id->indexBuffer->getType() == HardwareIndexBuffer::IT_32BIT;
443 		IndexRemap indexRemap;
444 		if (use32bitIndexes)
445 		{
446 			uint32 *p32 = static_cast<uint32*>(id->indexBuffer->lock(
447 				id->indexStart,
448 				id->indexCount * id->indexBuffer->getIndexSize(),
449 				HardwareBuffer::HBL_READ_ONLY));
450 			buildIndexRemap(p32, id->indexCount, indexRemap);
451 			id->indexBuffer->unlock();
452 		}
453 		else
454 		{
455 			uint16 *p16 = static_cast<uint16*>(id->indexBuffer->lock(
456 				id->indexStart,
457 				id->indexCount * id->indexBuffer->getIndexSize(),
458 				HardwareBuffer::HBL_READ_ONLY));
459 			buildIndexRemap(p16, id->indexCount, indexRemap);
460 			id->indexBuffer->unlock();
461 		}
462 		if (indexRemap.size() == vd->vertexCount)
463 		{
464 			// ha, complete usage after all
465 			targetGeomLink->vertexData = vd;
466 			targetGeomLink->indexData = id;
467 			return;
468 		}
469 
470 
471 		// Create the new vertex data records
472 		targetGeomLink->vertexData = vd->clone(false);
473 		// Convenience
474 		VertexData* newvd = targetGeomLink->vertexData;
475 		//IndexData* newid = targetGeomLink->indexData;
476 		// Update the vertex count
477 		newvd->vertexCount = indexRemap.size();
478 
479 		size_t numvbufs = vd->vertexBufferBinding->getBufferCount();
480 		// Copy buffers from old to new
481 		for (unsigned short b = 0; b < numvbufs; ++b)
482 		{
483 			// Lock old buffer
484 			HardwareVertexBufferSharedPtr oldBuf =
485 				vd->vertexBufferBinding->getBuffer(b);
486 			// Create new buffer
487 			HardwareVertexBufferSharedPtr newBuf =
488 				HardwareBufferManager::getSingleton().createVertexBuffer(
489 					oldBuf->getVertexSize(),
490 					indexRemap.size(),
491 					HardwareBuffer::HBU_STATIC);
492 			// rebind
493 			newvd->vertexBufferBinding->setBinding(b, newBuf);
494 
495 			// Copy all the elements of the buffer across, by iterating over
496 			// the IndexRemap which describes how to move the old vertices
497 			// to the new ones. By nature of the map the remap is in order of
498 			// indexes in the old buffer, but note that we're not guaranteed to
499 			// address every vertex (which is kinda why we're here)
500 			uchar* pSrcBase = static_cast<uchar*>(
501 				oldBuf->lock(HardwareBuffer::HBL_READ_ONLY));
502 			uchar* pDstBase = static_cast<uchar*>(
503 				newBuf->lock(HardwareBuffer::HBL_DISCARD));
504 			size_t vertexSize = oldBuf->getVertexSize();
505 			// Buffers should be the same size
506 			assert (vertexSize == newBuf->getVertexSize());
507 
508 			for (IndexRemap::iterator r = indexRemap.begin();
509 				r != indexRemap.end(); ++r)
510 			{
511 				assert (r->first < oldBuf->getNumVertices());
512 				assert (r->second < newBuf->getNumVertices());
513 
514 				uchar* pSrc = pSrcBase + r->first * vertexSize;
515 				uchar* pDst = pDstBase + r->second * vertexSize;
516 				memcpy(pDst, pSrc, vertexSize);
517 			}
518 			// unlock
519 			oldBuf->unlock();
520 			newBuf->unlock();
521 
522 		}
523 
524 		// Now create a new index buffer
525 		HardwareIndexBufferSharedPtr ibuf =
526 			HardwareBufferManager::getSingleton().createIndexBuffer(
527 				id->indexBuffer->getType(), id->indexCount,
528 				HardwareBuffer::HBU_STATIC);
529 
530 		if (use32bitIndexes)
531 		{
532 			uint32 *pSrc32, *pDst32;
533 			pSrc32 = static_cast<uint32*>(id->indexBuffer->lock(
534 				id->indexStart, id->indexCount * id->indexBuffer->getIndexSize(),
535 				HardwareBuffer::HBL_READ_ONLY));
536 			pDst32 = static_cast<uint32*>(ibuf->lock(
537 				HardwareBuffer::HBL_DISCARD));
538 			remapIndexes(pSrc32, pDst32, indexRemap, id->indexCount);
539 			id->indexBuffer->unlock();
540 			ibuf->unlock();
541 		}
542 		else
543 		{
544 			uint16 *pSrc16, *pDst16;
545 			pSrc16 = static_cast<uint16*>(id->indexBuffer->lock(
546 				id->indexStart, id->indexCount * id->indexBuffer->getIndexSize(),
547 				HardwareBuffer::HBL_READ_ONLY));
548 			pDst16 = static_cast<uint16*>(ibuf->lock(
549 				HardwareBuffer::HBL_DISCARD));
550 			remapIndexes(pSrc16, pDst16, indexRemap, id->indexCount);
551 			id->indexBuffer->unlock();
552 			ibuf->unlock();
553 		}
554 
555 		targetGeomLink->indexData = OGRE_NEW IndexData();
556 		targetGeomLink->indexData->indexStart = 0;
557 		targetGeomLink->indexData->indexCount = id->indexCount;
558 		targetGeomLink->indexData->indexBuffer = ibuf;
559 
560 		// Store optimised geometry for deallocation later
561 		OptimisedSubMeshGeometry *optGeom = OGRE_NEW OptimisedSubMeshGeometry();
562 		optGeom->indexData = targetGeomLink->indexData;
563 		optGeom->vertexData = targetGeomLink->vertexData;
564 		mOptimisedSubMeshGeometryList.push_back(optGeom);
565 	}
566 	//--------------------------------------------------------------------------
addSceneNode(const SceneNode * node)567 	void InstancedGeometry::addSceneNode(const SceneNode* node)
568 	{
569 		SceneNode::ConstObjectIterator obji = node->getAttachedObjectIterator();
570 		while (obji.hasMoreElements())
571 		{
572 			MovableObject* mobj = obji.getNext();
573 			if (mobj->getMovableType() == "Entity")
574 			{
575 				addEntity(static_cast<Entity*>(mobj),
576 					node->_getDerivedPosition(),
577 					node->_getDerivedOrientation(),
578 					node->_getDerivedScale());
579 			}
580 		}
581 		// Iterate through all the child-nodes
582 		SceneNode::ConstChildNodeIterator nodei = node->getChildIterator();
583 
584 		while (nodei.hasMoreElements())
585 		{
586 			const SceneNode* newNode = static_cast<const SceneNode*>(nodei.getNext());
587 			// Add this subnode and its children...
588 			addSceneNode( newNode );
589 		}
590 	}
591 	//--------------------------------------------------------------------------
build(void)592 	void InstancedGeometry::build(void)
593 	{
594 		// Make sure there's nothing from previous builds
595 		destroy();
596 
597 		// Firstly allocate meshes to BatchInstances
598 		for (QueuedSubMeshList::iterator qi = mQueuedSubMeshes.begin();
599 			qi != mQueuedSubMeshes.end(); ++qi)
600 		{
601 			QueuedSubMesh* qsm = *qi;
602 			//BatchInstance* BatchInstance = getBatchInstance(qsm->worldBounds, true);
603 			BatchInstance* batchInstance = getInstancedGeometryInstance();
604 			batchInstance->assign(qsm);
605 		}
606 
607 		// Now tell each BatchInstance to build itself
608 		for (BatchInstanceMap::iterator ri = mBatchInstanceMap.begin();
609 			ri != mBatchInstanceMap.end(); ++ri)
610 		{
611 			ri->second->build();
612 		}
613 
614 
615 	}
616 	//--------------------------------------------------------------------------
addBatchInstance(void)617 	void InstancedGeometry::addBatchInstance(void)
618 	{
619 
620 
621 		BatchInstanceIterator regIt = getBatchInstanceIterator();
622 		BatchInstance* lastBatchInstance=0 ;
623 		while(regIt.hasMoreElements())
624 		{
625 			lastBatchInstance= regIt.getNext();
626 		}
627 
628         if(!lastBatchInstance)
629             OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "No batch instance found",
630                         "InstancedGeometry::addBatchInstance");
631 
632 		uint32 index=(lastBatchInstance)?lastBatchInstance->getID()+1:0;
633 		//create a new BatchInstance
634 
635 		BatchInstance*ret = OGRE_NEW BatchInstance(this, mName+":"+StringConverter::toString(index),
636 			mOwner, index);
637 
638 		ret->attachToScene();
639 
640 		mOwner->injectMovableObject(ret);
641 		ret->setVisible(mVisible);
642 		ret->setCastShadows(mCastShadows);
643 		mBatchInstanceMap[index] = ret;
644 
645 		if (mRenderQueueIDSet)
646 		{
647 				ret->setRenderQueueGroup(mRenderQueueID);
648 		}
649 
650 		const size_t numLod = lastBatchInstance->mLodValues.size();
651 		ret->mLodValues.resize(numLod);
652 		for (ushort lod = 0; lod < numLod; lod++)
653 		{
654 			ret->mLodValues[lod] =
655 			lastBatchInstance->mLodValues[lod];
656 		}
657 
658 
659 
660 		// update bounds
661 		AxisAlignedBox box(lastBatchInstance->mAABB.getMinimum(),lastBatchInstance->mAABB.getMaximum());
662 		ret->mAABB.merge(box);
663 
664 		ret->mBoundingRadius = lastBatchInstance->mBoundingRadius ;
665 		//now create  news instanced objects
666 		BatchInstance::ObjectsMap::iterator objIt;
667 		for(objIt=lastBatchInstance->getInstancesMap().begin();objIt!=lastBatchInstance->getInstancesMap().end();++objIt)
668 		{
669 			InstancedObject* instancedObject = ret->isInstancedObjectPresent(objIt->first);
670 			if(instancedObject == NULL)
671 			{
672 				if(mBaseSkeleton.isNull())
673 				{
674 					instancedObject= OGRE_NEW InstancedObject(objIt->first);
675 				}
676 				else
677 				{
678 					instancedObject= OGRE_NEW InstancedObject(objIt->first,mSkeletonInstance,mAnimationState);
679 				}
680 				ret->addInstancedObject(objIt->first,instancedObject);
681 			}
682 
683 		}
684 
685 
686 
687 		BatchInstance::LODIterator lodIterator = lastBatchInstance->getLODIterator();
688 		//parse all the LOD buckets of the BatchInstance
689 		while (lodIterator.hasMoreElements())
690 		{
691 
692 			LODBucket* lod = lodIterator.getNext();
693 			//create a new LOD bucket for the new BatchInstance
694 			LODBucket* lodBucket= OGRE_NEW LODBucket(ret, lod->getLod(), lod->getLodValue());
695 
696 			//add the LODBucket to the BatchInstance list
697 			ret->updateContainers(lodBucket);
698 
699 			LODBucket::MaterialIterator matIt = lod->getMaterialIterator();
700 			//parse all the material buckets of the LOD bucket
701 			while (matIt.hasMoreElements())
702 			{
703 
704 				MaterialBucket*mat = matIt.getNext();
705 				//create a new material bucket
706 				String materialName=mat->getMaterialName();
707 				MaterialBucket* matBucket = OGRE_NEW MaterialBucket(lodBucket,materialName);
708 
709 				//add the material bucket to the LOD buckets list and map
710 				lodBucket->updateContainers(matBucket, materialName);
711 
712 				MaterialBucket::GeometryIterator geomIt = mat->getGeometryIterator();
713 				//parse all the geometry buckets of the material bucket
714 				while(geomIt.hasMoreElements())
715 				{
716 					//get the source geometry bucket
717 					GeometryBucket *geom = geomIt.getNext();
718 					//create a new geometry bucket
719 					GeometryBucket *geomBucket = OGRE_NEW GeometryBucket(matBucket,geom->getFormatString(),geom);
720 
721 					//update the material bucket map of the material bucket
722 					matBucket->updateContainers(geomBucket, geomBucket->getFormatString() );
723 
724 					//copy bounding information
725 					geomBucket->getAABB()=geom->getAABB();
726 					geomBucket->setBoundingBox(	geom->getBoundingBox());
727 					//now setups the news InstancedObjects.
728 					for(objIt=ret->getInstancesMap().begin();objIt!=ret->getInstancesMap().end();++objIt)
729 					{
730 						//get the destination IntanciedObject
731 						InstancedObject*obj=objIt->second;
732 						InstancedObject::GeometryBucketList::iterator findIt;
733 						//check if the bucket is not already in the list
734 						findIt=std::find(obj->getGeometryBucketList().begin(),obj->getGeometryBucketList().end(),geomBucket);
735 						if(findIt==obj->getGeometryBucketList().end())
736 								obj->addBucketToList(geomBucket);
737 					}
738 
739 
740 				}
741 			}
742 		}
743 	}
744 	//--------------------------------------------------------------------------
updateContainers(LODBucket * bucket)745 	void InstancedGeometry::BatchInstance::updateContainers(LODBucket* bucket )
746 	{
747 		mLodBucketList.push_back(bucket);
748 	}
749 	//--------------------------------------------------------------------------
updateContainers(MaterialBucket * bucket,String & name)750 	void InstancedGeometry::LODBucket::updateContainers(MaterialBucket* bucket, String& name )
751 	{
752 		mMaterialBucketMap[name] = bucket;
753 	}
754 
755 	//--------------------------------------------------------------------------
getFormatString(void) const756 	String InstancedGeometry::GeometryBucket::getFormatString(void) const
757 	{
758 		return mFormatString;
759 	}
760 	//--------------------------------------------------------------------------
attachToScene()761 	void InstancedGeometry::BatchInstance::attachToScene()
762 	{
763 
764 			mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(mName/*,mCentre*/);
765 			mNode->attachObject(this);
766 	}
767 	//--------------------------------------------------------------------------
updateContainers(InstancedGeometry::GeometryBucket * bucket,const String & format)768 	void InstancedGeometry::MaterialBucket::updateContainers(InstancedGeometry::GeometryBucket* bucket, const String & format)
769 	{
770 		mCurrentGeometryMap[format]=bucket;
771 		mGeometryBucketList.push_back(bucket);
772 	}
setMaterial(const String & name)773 	void InstancedGeometry::MaterialBucket:: setMaterial(const String & name)
774 	{
775 		mMaterial=MaterialManager::getSingleton().getByName(name);
776 	}
777 	//--------------------------------------------------------------------------
destroy(void)778 	void InstancedGeometry::destroy(void)
779 	{
780 		RenderOperationVector::iterator it;
781 		for(it=mRenderOps.begin();it!=mRenderOps.end();++it)
782 		{
783 			OGRE_DELETE (*it)->vertexData;
784 			OGRE_DELETE (*it)->indexData;
785 
786 		}
787 		mRenderOps.clear();
788 
789 		// delete the BatchInstances
790 		for (BatchInstanceMap::iterator i = mBatchInstanceMap.begin();
791 			i != mBatchInstanceMap.end(); ++i)
792 		{
793 			mOwner->extractMovableObject(i->second);
794 			OGRE_DELETE i->second;
795 
796 		}
797 		mBatchInstanceMap.clear();
798 		mInstancedGeometryInstance = NULL;
799 	}
800 	//--------------------------------------------------------------------------
reset(void)801 	void InstancedGeometry::reset(void)
802 	{
803 		destroy();
804 
805 		for (QueuedSubMeshList::iterator i = mQueuedSubMeshes.begin();
806 			i != mQueuedSubMeshes.end(); ++i)
807 		{
808 			OGRE_DELETE *i;
809 
810 		}
811 		mQueuedSubMeshes.clear();
812 		// Delete precached geoemtry lists
813 		for (SubMeshGeometryLookup::iterator l = mSubMeshGeometryLookup.begin();
814 			l != mSubMeshGeometryLookup.end(); ++l)
815 		{
816 			OGRE_DELETE_T(l->second, SubMeshLodGeometryLinkList, MEMCATEGORY_GEOMETRY);
817 
818 		}
819 		mSubMeshGeometryLookup.clear();
820 		// Delete optimised geometry
821 		for (OptimisedSubMeshGeometryList::iterator o = mOptimisedSubMeshGeometryList.begin();
822 			o != mOptimisedSubMeshGeometryList.end(); ++o)
823 		{
824 			OGRE_DELETE *o;
825 
826 		}
827 		mOptimisedSubMeshGeometryList.clear();
828 
829 	}
830 	//--------------------------------------------------------------------------
setVisible(bool visible)831 	void InstancedGeometry::setVisible(bool visible)
832 	{
833 
834 		mVisible = visible;
835 		// tell any existing BatchInstances
836 		for (BatchInstanceMap::iterator ri = mBatchInstanceMap.begin();
837 			ri != mBatchInstanceMap.end(); ++ri)
838 		{
839 
840 
841 			ri->second->setVisible(visible);
842 		}
843 	}
844 	//--------------------------------------------------------------------------
setCastShadows(bool castShadows)845 	void InstancedGeometry::setCastShadows(bool castShadows)
846 	{
847 		mCastShadows = castShadows;
848 		// tell any existing BatchInstances
849 		for (BatchInstanceMap::iterator ri = mBatchInstanceMap.begin();
850 			ri != mBatchInstanceMap.end(); ++ri)
851 		{
852 			ri->second->setCastShadows(castShadows);
853 		}
854 
855 	}
856 	//--------------------------------------------------------------------------
setRenderQueueGroup(uint8 queueID)857     void InstancedGeometry::setRenderQueueGroup(uint8 queueID)
858 	{
859 		assert(queueID <= RENDER_QUEUE_MAX && "Render queue out of range!");
860 		mRenderQueueIDSet = true;
861 		mRenderQueueID = queueID;
862 		// tell any existing BatchInstances
863 		for (BatchInstanceMap::iterator ri = mBatchInstanceMap.begin();
864 			ri != mBatchInstanceMap.end(); ++ri)
865 		{
866 			ri->second->setRenderQueueGroup(queueID);
867 		}
868 	}
869 	//--------------------------------------------------------------------------
getRenderQueueGroup(void) const870 	uint8 InstancedGeometry::getRenderQueueGroup(void) const
871 	{
872 		return mRenderQueueID;
873 	}
874 	//--------------------------------------------------------------------------
dump(const String & filename) const875 	void InstancedGeometry::dump(const String& filename) const
876 	{
877 		std::ofstream of(filename.c_str());
878 		of << "Static Geometry Report for " << mName << std::endl;
879 		of << "-------------------------------------------------" << std::endl;
880 		of << "Number of queued submeshes: " << mQueuedSubMeshes.size() << std::endl;
881 		of << "Number of BatchInstances: " << mBatchInstanceMap.size() << std::endl;
882 		of << "BatchInstance dimensions: " << mBatchInstanceDimensions << std::endl;
883 		of << "Origin: " << mOrigin << std::endl;
884 		of << "Max distance: " << mUpperDistance << std::endl;
885 		of << "Casts shadows?: " << mCastShadows << std::endl;
886 		of << std::endl;
887 		for (BatchInstanceMap::const_iterator ri = mBatchInstanceMap.begin();
888 			ri != mBatchInstanceMap.end(); ++ri)
889 		{
890 			ri->second->dump(of);
891 		}
892 		of << "-------------------------------------------------" << std::endl;
893 	}
894 	//--------------------------------------------------------------------------
setProvideWorldInverses(bool flag)895     void InstancedGeometry::setProvideWorldInverses(bool flag)
896     {
897         mProvideWorldInverses = flag;
898     }
899 	//---------------------------------------------------------------------
visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)900 	void InstancedGeometry::visitRenderables(Renderable::Visitor* visitor,
901 		bool debugRenderables)
902 	{
903 		for (BatchInstanceMap::const_iterator ri = mBatchInstanceMap.begin();
904 			ri != mBatchInstanceMap.end(); ++ri)
905 		{
906 			ri->second->visitRenderables(visitor, debugRenderables);
907 		}
908 
909 	}
910 	//--------------------------------------------------------------------------
InstancedObject(unsigned short index,SkeletonInstance * skeleton,AnimationStateSet * animations)911 	InstancedGeometry::InstancedObject::InstancedObject(unsigned short index,SkeletonInstance *skeleton, AnimationStateSet*animations)
912 		: mIndex(index),
913 		mTransformation(Matrix4::ZERO),
914 		mOrientation(Quaternion::IDENTITY),
915 		mScale(Vector3::UNIT_SCALE),
916 		mPosition(Vector3::ZERO),
917 		mSkeletonInstance(skeleton),
918 		mBoneWorldMatrices(NULL),
919         mBoneMatrices(NULL),
920         mNumBoneMatrices(0),
921 		mFrameAnimationLastUpdated(std::numeric_limits<unsigned long>::max())
922 
923 	{
924 
925 			mSkeletonInstance->load();
926 
927 			mAnimationState = OGRE_NEW AnimationStateSet();
928 			mNumBoneMatrices = mSkeletonInstance->getNumBones();
929 			mBoneMatrices = OGRE_ALLOC_T(Matrix4, mNumBoneMatrices, MEMCATEGORY_ANIMATION);
930 			AnimationStateIterator it=animations->getAnimationStateIterator();
931 			while (it.hasMoreElements())
932 			{
933 				AnimationState*anim= it.getNext();
934 				mAnimationState->createAnimationState(anim->getAnimationName(),anim->getTimePosition(),anim->getLength(),
935 				anim->getWeight());
936 
937 			}
938 
939 	}
940 	//--------------------------------------------------------------------------
InstancedObject(unsigned short index)941 	InstancedGeometry::InstancedObject::InstancedObject(unsigned short index)
942 		:mIndex(index),
943 		mTransformation(Matrix4::ZERO),
944 		mOrientation(Quaternion::IDENTITY),
945 		mScale(Vector3::UNIT_SCALE),
946 		mPosition(Vector3::ZERO),
947 		mSkeletonInstance(0),
948 		mBoneWorldMatrices(0),
949         mBoneMatrices(0),
950 		mAnimationState(0),
951         mNumBoneMatrices(0),
952 		mFrameAnimationLastUpdated(std::numeric_limits<unsigned long>::max())
953 	{
954 	}
955 	//--------------------------------------------------------------------------
~InstancedObject()956 	InstancedGeometry::InstancedObject::~InstancedObject()
957 	{
958 		mGeometryBucketList.clear();
959 		OGRE_DELETE mAnimationState;
960 		OGRE_FREE(mBoneMatrices, MEMCATEGORY_ANIMATION);
961 		OGRE_FREE(mBoneWorldMatrices, MEMCATEGORY_ANIMATION);
962 	}
963 	//--------------------------------------------------------------------------
addBucketToList(GeometryBucket * bucket)964 	void InstancedGeometry::InstancedObject::addBucketToList(GeometryBucket*bucket)
965 	{
966 		mGeometryBucketList.push_back(bucket);
967 	}
968 	//--------------------------------------------------------------------------
setPosition(Vector3 position)969 	void InstancedGeometry::InstancedObject::setPosition( Vector3  position)
970 	{
971 
972 		mPosition=position;
973 		needUpdate();
974 		BatchInstance*parentBatchInstance=(*(mGeometryBucketList.begin()))->getParent()->getParent()->getParent();
975 		parentBatchInstance->updateBoundingBox();
976 
977 	}
978 	//--------------------------------------------------------------------------
translate(const Vector3 & d)979 	void InstancedGeometry::InstancedObject::translate(const Vector3 &d)
980 	{
981 		mPosition += d;
982 		needUpdate();
983 	}
984 	//--------------------------------------------------------------------------
translate(const Matrix3 & axes,const Vector3 & move)985 	void InstancedGeometry::InstancedObject::translate(const Matrix3 & axes,const Vector3 &move)
986 	{
987 		Vector3 derived = axes * move;
988         translate(derived);
989 	}
990 	//--------------------------------------------------------------------------
getLocalAxes() const991 	Matrix3 InstancedGeometry::InstancedObject::getLocalAxes() const
992 	{
993 		Vector3 axisX = Vector3::UNIT_X;
994         Vector3 axisY = Vector3::UNIT_Y;
995         Vector3 axisZ = Vector3::UNIT_Z;
996 
997         axisX = mOrientation * axisX;
998         axisY = mOrientation * axisY;
999         axisZ = mOrientation * axisZ;
1000 
1001         return Matrix3(axisX.x, axisY.x, axisZ.x,
1002                        axisX.y, axisY.y, axisZ.y,
1003                        axisX.z, axisY.z, axisZ.z);
1004 	}
1005 	//--------------------------------------------------------------------------
getPosition(void) const1006 	const Vector3 & InstancedGeometry::InstancedObject::getPosition(void) const
1007 	{
1008 		return mPosition;
1009 	}
1010 	//--------------------------------------------------------------------------
yaw(const Radian & angle)1011 	void InstancedGeometry::InstancedObject::yaw(const Radian&angle)
1012 	{
1013 		Quaternion q;
1014         q.FromAngleAxis(angle,Vector3::UNIT_Y);
1015         rotate(q);
1016 	}
1017 	//--------------------------------------------------------------------------
pitch(const Radian & angle)1018 	void InstancedGeometry::InstancedObject::pitch(const Radian&angle)
1019 	{
1020 		Quaternion q;
1021         q.FromAngleAxis(angle,Vector3::UNIT_X);
1022         rotate(q);
1023 	}
1024 	//--------------------------------------------------------------------------
roll(const Radian & angle)1025 	void InstancedGeometry::InstancedObject::roll(const Radian&angle)
1026 	{
1027 		Quaternion q;
1028         q.FromAngleAxis(angle,Vector3::UNIT_Z);
1029         rotate(q);
1030 
1031 	}
1032 	//--------------------------------------------------------------------------
setScale(const Vector3 & scale)1033 	void InstancedGeometry::InstancedObject::setScale(const Vector3&scale)
1034 	{
1035 		mScale=scale;
1036 		needUpdate();
1037 	}
1038 
getScale() const1039 	const Vector3& InstancedGeometry::InstancedObject::getScale() const
1040 	{
1041 		return mScale;
1042 	}
1043 
1044 	//--------------------------------------------------------------------------
rotate(const Quaternion & q)1045 	void InstancedGeometry::InstancedObject::rotate(const Quaternion& q)
1046 	{
1047 
1048             mOrientation = mOrientation * q;
1049 			 needUpdate();
1050 	}
1051 	//--------------------------------------------------------------------------
setOrientation(const Quaternion & q)1052 	void InstancedGeometry::InstancedObject::setOrientation(const Quaternion& q)
1053 	{
1054         mOrientation = q;
1055 		needUpdate();
1056 	}
1057 	//--------------------------------------------------------------------------
setPositionAndOrientation(Vector3 p,const Quaternion & q)1058 	void InstancedGeometry::InstancedObject::setPositionAndOrientation(Vector3 p, const Quaternion& q)
1059 	{
1060 		mPosition = p;
1061         mOrientation = q;
1062         needUpdate();
1063 		BatchInstance* parentBatchInstance=(*(mGeometryBucketList.begin()))->getParent()->getParent()->getParent();
1064 		parentBatchInstance->updateBoundingBox();
1065 	}
1066 
1067     //--------------------------------------------------------------------------
getOrientation(void)1068     Quaternion &InstancedGeometry::InstancedObject::getOrientation(void)
1069     {
1070         return mOrientation;
1071     }
1072 
1073 	//--------------------------------------------------------------------------
needUpdate()1074 	void InstancedGeometry::InstancedObject::needUpdate()
1075 	{
1076 		 mTransformation.makeTransform(
1077                 mPosition,
1078                 mScale,
1079                 mOrientation);
1080 
1081 
1082 	}
1083 	//--------------------------------------------------------------------------
updateAnimation(void)1084 	void InstancedGeometry::InstancedObject::updateAnimation(void)
1085 	{
1086 
1087 		if(mSkeletonInstance)
1088 		{
1089 			GeometryBucketList::iterator it;
1090 		    mSkeletonInstance->setAnimationState(*mAnimationState);
1091             mSkeletonInstance->_getBoneMatrices(mBoneMatrices);
1092 
1093 			 // Allocate bone world matrices on demand, for better memory footprint
1094             // when using software animation.
1095             if (!mBoneWorldMatrices)
1096             {
1097                 mBoneWorldMatrices = OGRE_ALLOC_T(Matrix4, mNumBoneMatrices, MEMCATEGORY_ANIMATION);
1098             }
1099 
1100             for (unsigned short i = 0; i < mNumBoneMatrices; ++i)
1101             {
1102                 mBoneWorldMatrices[i] =  mTransformation * mBoneMatrices[i];
1103 
1104             }
1105 
1106 
1107 
1108 		}
1109 
1110 	}
1111 	//--------------------------------------------------------------------------
getAnimationState(const String & name) const1112 	AnimationState* InstancedGeometry::InstancedObject::getAnimationState(const String& name) const
1113     {
1114         if (!mAnimationState)
1115         {
1116             OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Object is not animated",
1117                 "InstancedGeometry::InstancedObject::getAnimationState");
1118         }
1119 //		AnimationStateIterator it=mAnimationState->getAnimationStateIterator();
1120 //		while (it.hasMoreElements())
1121 //		{
1122 //			AnimationState*anim= it.getNext();
1123 //
1124 //
1125 //		}
1126 		return mAnimationState->getAnimationState(name);
1127     }
1128 	//--------------------------------------------------------------------------
getBatchInstanceIterator(void)1129 	InstancedGeometry::BatchInstanceIterator InstancedGeometry::getBatchInstanceIterator(void)
1130 	{
1131 		return BatchInstanceIterator(mBatchInstanceMap.begin(), mBatchInstanceMap.end());
1132 	}
1133 	//--------------------------------------------------------------------------
BatchInstance(InstancedGeometry * parent,const String & name,SceneManager * mgr,uint32 BatchInstanceID)1134 	InstancedGeometry::BatchInstance::BatchInstance(InstancedGeometry* parent, const String& name,
1135 		SceneManager* mgr, uint32 BatchInstanceID)
1136 		: MovableObject(name), mParent(parent), mSceneMgr(mgr), mNode(0),
1137 		mBatchInstanceID(BatchInstanceID), mBoundingRadius(0.0f),
1138 		mCurrentLod(0), mCamera(0), mLodStrategy(0)
1139 	{
1140 	}
1141 	//--------------------------------------------------------------------------
~BatchInstance()1142 	InstancedGeometry::BatchInstance::~BatchInstance()
1143 	{
1144 		if (mNode)
1145 		{
1146 			mNode->getParentSceneNode()->removeChild(mNode);
1147 			mSceneMgr->destroySceneNode(mNode->getName());
1148 			mNode = 0;
1149 		}
1150 		// delete
1151 		for (LODBucketList::iterator i = mLodBucketList.begin();
1152 			i != mLodBucketList.end(); ++i)
1153 		{
1154 			OGRE_DELETE *i;
1155 		}
1156 		mLodBucketList.clear();
1157 		ObjectsMap::iterator o;
1158 
1159 		for(o=mInstancesMap.begin();o!=mInstancesMap.end();++o)
1160 		{
1161 			OGRE_DELETE o->second;
1162 		}
1163 		mInstancesMap.clear();
1164 		// no need to delete queued meshes, these are managed in InstancedGeometry
1165 	}
1166 	//--------------------------------------------------------------------------
assign(QueuedSubMesh * qmesh)1167 	void InstancedGeometry::BatchInstance::assign(QueuedSubMesh* qmesh)
1168 	{
1169 		mQueuedSubMeshes.push_back(qmesh);
1170 
1171         // Set/check LOD strategy
1172         const LodStrategy *lodStrategy = qmesh->submesh->parent->getLodStrategy();
1173         if (mLodStrategy == 0)
1174         {
1175             mLodStrategy = lodStrategy;
1176 
1177             // First LOD mandatory, and always from base LOD value
1178             mLodValues.push_back(mLodStrategy->getBaseValue());
1179         }
1180         else
1181         {
1182             if (mLodStrategy != lodStrategy)
1183                 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Lod strategies do not match",
1184                     "InstancedGeometry::InstancedObject::assign");
1185         }
1186 
1187 		// update LOD values
1188 		ushort lodLevels = qmesh->submesh->parent->getNumLodLevels();
1189 		assert(qmesh->geometryLodList->size() == lodLevels);
1190 
1191 		while(mLodValues.size() < lodLevels)
1192 		{
1193 			mLodValues.push_back(0.0f);
1194 		}
1195 		// Make sure LOD levels are max of all at the requested level
1196 		for (ushort lod = 1; lod < lodLevels; ++lod)
1197 		{
1198 			const MeshLodUsage& meshLod =
1199 				qmesh->submesh->parent->getLodLevel(lod);
1200 			mLodValues[lod] = std::max(mLodValues[lod],
1201 				meshLod.value);
1202 		}
1203 
1204 		// update bounds
1205 		// Transform world bounds relative to our centre
1206 		AxisAlignedBox localBounds(
1207 			qmesh->worldBounds.getMinimum() ,
1208 			qmesh->worldBounds.getMaximum());
1209 		mAABB.merge(localBounds);
1210 		mBoundingRadius = Math::boundingRadiusFromAABB(mAABB);
1211 
1212 	}
1213 	//--------------------------------------------------------------------------
build()1214 	void InstancedGeometry::BatchInstance::build()
1215 	{
1216 		// Create a node
1217 		mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(mName);
1218 		mNode->attachObject(this);
1219 		// We need to create enough LOD buckets to deal with the highest LOD
1220 		// we encountered in all the meshes queued
1221 		for (ushort lod = 0; lod < mLodValues.size(); ++lod)
1222 		{
1223 			LODBucket* lodBucket =
1224 				OGRE_NEW LODBucket(this, lod, mLodValues[lod]);
1225 			mLodBucketList.push_back(lodBucket);
1226 			// Now iterate over the meshes and assign to LODs
1227 			// LOD bucket will pick the right LOD to use
1228 			QueuedSubMeshList::iterator qi, qiend;
1229 			qiend = mQueuedSubMeshes.end();
1230 			for (qi = mQueuedSubMeshes.begin(); qi != qiend; ++qi)
1231 			{
1232 				lodBucket->assign(*qi, lod);
1233 			}
1234 			// now build
1235 			lodBucket->build();
1236 		}
1237 
1238 
1239 	}
1240 
1241 	//--------------------------------------------------------------------------
updateBoundingBox()1242 	void InstancedGeometry::BatchInstance::updateBoundingBox()
1243 	{
1244 			AxisAlignedBox aabb;
1245 
1246 			//Get the first GeometryBucket to get the aabb
1247 			LODIterator lodIterator = getLODIterator();
1248 			if( lodIterator.hasMoreElements() )
1249 			{
1250 				LODBucket* lod = lodIterator.getNext();
1251 				LODBucket::MaterialIterator matIt = lod->getMaterialIterator();
1252 				if( matIt.hasMoreElements() )
1253 				{
1254 					MaterialBucket*mat = matIt.getNext();
1255 					MaterialBucket::GeometryIterator geomIt = mat->getGeometryIterator();
1256 					if( geomIt.hasMoreElements() )
1257 					{
1258 						GeometryBucket *geom = geomIt.getNext();
1259 						aabb = geom->getAABB();
1260 					}
1261 				}
1262 			}
1263 
1264 			ObjectsMap::iterator objIt;
1265 			Vector3 vMin( Vector3::ZERO );
1266 			Vector3 vMax( Vector3::ZERO );
1267 			if( !mInstancesMap.empty() )
1268 			{
1269 				objIt = mInstancesMap.begin();
1270 				vMin = objIt->second->getPosition() + aabb.getMinimum();
1271 				vMax = objIt->second->getPosition() + aabb.getMaximum();
1272 			}
1273 
1274 			for( objIt=mInstancesMap.begin(); objIt!=mInstancesMap.end(); ++objIt )
1275 			{
1276 				const Vector3 &position = objIt->second->getPosition();
1277 				const Vector3 &scale	= objIt->second->getScale();
1278 
1279 				vMin.x = std::min( vMin.x, position.x + aabb.getMinimum().x * scale.x );
1280 				vMin.y = std::min( vMin.y, position.y + aabb.getMinimum().y * scale.y );
1281 				vMin.z = std::min( vMin.z, position.z + aabb.getMinimum().z * scale.z );
1282 
1283 				vMax.x = std::max( vMax.x, position.x + aabb.getMaximum().x * scale.x );
1284 				vMax.y = std::max( vMax.y, position.y + aabb.getMaximum().y * scale.y );
1285 				vMax.z = std::max( vMax.z, position.z + aabb.getMaximum().z * scale.z );
1286 			}
1287 
1288 			aabb.setExtents( vMin, vMax );
1289 
1290 			//Now apply the bounding box
1291 			lodIterator = getLODIterator();
1292 			while( lodIterator.hasMoreElements() )
1293 			{
1294 				LODBucket* lod = lodIterator.getNext();
1295 				LODBucket::MaterialIterator matIt = lod->getMaterialIterator();
1296 				while (matIt.hasMoreElements())
1297 				{
1298 					MaterialBucket*mat = matIt.getNext();
1299 					MaterialBucket::GeometryIterator geomIt = mat->getGeometryIterator();
1300 					while( geomIt.hasMoreElements() )
1301 					{
1302 						GeometryBucket *geom = geomIt.getNext();
1303 						geom->setBoundingBox( aabb );
1304                         this->mNode->_updateBounds();
1305 						mAABB = aabb;
1306 					}
1307 				}
1308 			}
1309 	}
1310 
1311 	//--------------------------------------------------------------------------
addInstancedObject(unsigned short index,InstancedObject * object)1312 	void InstancedGeometry::BatchInstance::addInstancedObject(unsigned short index,InstancedObject* object)
1313 	{
1314 		mInstancesMap[index]=object;
1315 	}
1316 	//--------------------------------------------------------------------------
isInstancedObjectPresent(unsigned short index)1317 	 InstancedGeometry::InstancedObject* InstancedGeometry::BatchInstance::isInstancedObjectPresent(unsigned short index)
1318 	{
1319 		if (mInstancesMap.find(index)!=mInstancesMap.end())
1320 			return mInstancesMap[index];
1321 		else return NULL;
1322 	}
1323 	//--------------------------------------------------------------------------
1324 	InstancedGeometry::BatchInstance::InstancedObjectIterator
getObjectIterator()1325 	InstancedGeometry::BatchInstance::getObjectIterator()
1326 	{
1327 		return InstancedObjectIterator(mInstancesMap.begin(), mInstancesMap.end());
1328 	}
1329 	//--------------------------------------------------------------------------
getMovableType(void) const1330 	const String& InstancedGeometry::BatchInstance::getMovableType(void) const
1331 	{
1332 		static String sType = "InstancedGeometry";
1333 		return sType;
1334 	}
1335 	//--------------------------------------------------------------------------
_notifyCurrentCamera(Camera * cam)1336 	void InstancedGeometry::BatchInstance::_notifyCurrentCamera(Camera* cam)
1337 	{
1338         // Set camera
1339         mCamera = cam;
1340 
1341 
1342         // Cache squared view depth for use by GeometryBucket
1343         mSquaredViewDepth = mParentNode->getSquaredViewDepth(cam->getLodCamera());
1344 
1345         // No LOD strategy set yet, skip (this indicates that there are no submeshes)
1346         if (mLodStrategy == 0)
1347             return;
1348 
1349         // Sanity check
1350         assert(!mLodValues.empty());
1351 
1352         // Calculate LOD value
1353         Real lodValue = mLodStrategy->getValue(this, cam);
1354 
1355         // Store LOD value for this strategy
1356         mLodValue = lodValue;
1357 
1358         // Get LOD index
1359         mCurrentLod = mLodStrategy->getIndex(lodValue, mLodValues);
1360 	}
1361 	//--------------------------------------------------------------------------
getBoundingBox(void) const1362 	const AxisAlignedBox& InstancedGeometry::BatchInstance::getBoundingBox(void) const
1363 	{
1364 		return mAABB;
1365 	}
1366 	//--------------------------------------------------------------------------
setBoundingBox(AxisAlignedBox & box)1367 	void InstancedGeometry::BatchInstance::setBoundingBox(AxisAlignedBox & box)
1368 	{
1369 		mAABB=box;
1370 	}
1371 	//--------------------------------------------------------------------------
getBoundingRadius(void) const1372 	Real InstancedGeometry::BatchInstance::getBoundingRadius(void) const
1373 	{
1374 		return mBoundingRadius;
1375 	}
1376 	//--------------------------------------------------------------------------
_updateRenderQueue(RenderQueue * queue)1377 	void InstancedGeometry::BatchInstance::_updateRenderQueue(RenderQueue* queue)
1378 	{
1379 		ObjectsMap::iterator it;
1380 		//we parse the Instanced Object map to update the animations.
1381 
1382 		for (it=mInstancesMap.begin();it!=mInstancesMap.end();++it)
1383 		{
1384 			it->second->updateAnimation();
1385 
1386 		}
1387 
1388 		mLodBucketList[mCurrentLod]->addRenderables(queue, mRenderQueueID,
1389 			mLodValue);
1390 	}
1391 	//---------------------------------------------------------------------
visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)1392 	void InstancedGeometry::BatchInstance::visitRenderables(
1393 		Renderable::Visitor* visitor, bool debugRenderables)
1394 	{
1395 		for (LODBucketList::iterator i = mLodBucketList.begin(); i != mLodBucketList.end(); ++i)
1396 		{
1397 			(*i)->visitRenderables(visitor, debugRenderables);
1398 		}
1399 
1400 	}
1401 	//--------------------------------------------------------------------------
isVisible(void) const1402 	bool InstancedGeometry::BatchInstance::isVisible(void) const
1403 	{
1404 		return mVisible && !mBeyondFarDistance;
1405 	}
1406 	//--------------------------------------------------------------------------
1407 	InstancedGeometry::BatchInstance::LODIterator
getLODIterator(void)1408 	InstancedGeometry::BatchInstance::getLODIterator(void)
1409 	{
1410 		return LODIterator(mLodBucketList.begin(), mLodBucketList.end());
1411 	}
1412 	//--------------------------------------------------------------------------
getLights(void) const1413 	const LightList& InstancedGeometry::BatchInstance::getLights(void) const
1414 	{
1415 		return queryLights();
1416 	}
1417 	//--------------------------------------------------------------------------
dump(std::ofstream & of) const1418 	void InstancedGeometry::BatchInstance::dump(std::ofstream& of) const
1419 	{
1420 		of << "BatchInstance " << mBatchInstanceID << std::endl;
1421 		of << "--------------------------" << std::endl;
1422 		of << "Local AABB: " << mAABB << std::endl;
1423 		of << "Bounding radius: " << mBoundingRadius << std::endl;
1424 		of << "Number of LODs: " << mLodBucketList.size() << std::endl;
1425 
1426 		for (LODBucketList::const_iterator i = mLodBucketList.begin();
1427 			i != mLodBucketList.end(); ++i)
1428 		{
1429 			(*i)->dump(of);
1430 		}
1431 		of << "--------------------------" << std::endl;
1432 	}
1433 	//--------------------------------------------------------------------------
1434 	//--------------------------------------------------------------------------
LODBucket(BatchInstance * parent,unsigned short lod,Real lodValue)1435 	InstancedGeometry::LODBucket::LODBucket(BatchInstance* parent, unsigned short lod,
1436 		Real lodValue)
1437 		: mParent(parent), mLod(lod), mLodValue(lodValue)
1438 	{
1439 	}
1440 	//--------------------------------------------------------------------------
~LODBucket()1441 	InstancedGeometry::LODBucket::~LODBucket()
1442 	{
1443 		// delete
1444 		for (MaterialBucketMap::iterator i = mMaterialBucketMap.begin();
1445 			i != mMaterialBucketMap.end(); ++i)
1446 		{
1447 			OGRE_DELETE i->second;
1448 		}
1449 		mMaterialBucketMap.clear();
1450 		for(QueuedGeometryList::iterator qi = mQueuedGeometryList.begin();
1451 			qi != mQueuedGeometryList.end(); ++qi)
1452 		{
1453 			OGRE_DELETE *qi;
1454 		}
1455 		mQueuedGeometryList.clear();
1456 		// no need to delete queued meshes, these are managed in InstancedGeometry
1457 	}
1458 	//--------------------------------------------------------------------------
assign(QueuedSubMesh * qmesh,ushort atLod)1459 	void InstancedGeometry::LODBucket::assign(QueuedSubMesh* qmesh, ushort atLod)
1460 	{
1461 		QueuedGeometry* q = OGRE_NEW QueuedGeometry();
1462 		mQueuedGeometryList.push_back(q);
1463 		q->position = qmesh->position;
1464 		q->orientation = qmesh->orientation;
1465 		q->scale = qmesh->scale;
1466 		q->ID = qmesh->ID;
1467 		if (qmesh->geometryLodList->size() > atLod)
1468 		{
1469 			// This submesh has enough lods, use the right one
1470 			q->geometry = &(*qmesh->geometryLodList)[atLod];
1471 		}
1472 		else
1473 		{
1474 			// Not enough lods, use the lowest one we have
1475 			q->geometry =
1476 				&(*qmesh->geometryLodList)[qmesh->geometryLodList->size() - 1];
1477 		}
1478 		// Locate a material bucket
1479 		MaterialBucket* mbucket = 0;
1480 		MaterialBucketMap::iterator m =
1481 			mMaterialBucketMap.find(qmesh->materialName);
1482 		if (m != mMaterialBucketMap.end())
1483 		{
1484 			mbucket = m->second;
1485 		}
1486 		else
1487 		{
1488 			mbucket = OGRE_NEW MaterialBucket(this, qmesh->materialName);
1489 			mMaterialBucketMap[qmesh->materialName] = mbucket;
1490 		}
1491 		mbucket->assign(q);
1492 	}
1493 	//--------------------------------------------------------------------------
build()1494 	void InstancedGeometry::LODBucket::build()
1495 	{
1496 		// Just pass this on to child buckets
1497 
1498 		for (MaterialBucketMap::iterator i = mMaterialBucketMap.begin();
1499 			i != mMaterialBucketMap.end(); ++i)
1500 		{
1501 			i->second->build();
1502 		}
1503 	}
1504 	//--------------------------------------------------------------------------
addRenderables(RenderQueue * queue,uint8 group,Real lodValue)1505 	void InstancedGeometry::LODBucket::addRenderables(RenderQueue* queue,
1506 		uint8 group, Real lodValue)
1507 	{
1508 		// Just pass this on to child buckets
1509 		MaterialBucketMap::iterator i, iend;
1510 		iend =  mMaterialBucketMap.end();
1511 		for (i = mMaterialBucketMap.begin(); i != iend; ++i)
1512 		{
1513 			i->second->addRenderables(queue, group, lodValue);
1514 		}
1515 	}
1516 	//---------------------------------------------------------------------
visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)1517 	void InstancedGeometry::LODBucket::visitRenderables(Renderable::Visitor* visitor,
1518 		bool debugRenderables)
1519 	{
1520 		MaterialBucketMap::iterator i, iend;
1521 		iend =  mMaterialBucketMap.end();
1522 		for (i = mMaterialBucketMap.begin(); i != iend; ++i)
1523 		{
1524 			i->second->visitRenderables(visitor, debugRenderables);
1525 		}
1526 
1527 	}
1528 	//--------------------------------------------------------------------------
1529 	InstancedGeometry::LODBucket::MaterialIterator
getMaterialIterator(void)1530 	InstancedGeometry::LODBucket::getMaterialIterator(void)
1531 	{
1532 		return MaterialIterator(
1533 			mMaterialBucketMap.begin(), mMaterialBucketMap.end());
1534 	}
1535 	//--------------------------------------------------------------------------
dump(std::ofstream & of) const1536 	void InstancedGeometry::LODBucket::dump(std::ofstream& of) const
1537 	{
1538 		of << "LOD Bucket " << mLod << std::endl;
1539 		of << "------------------" << std::endl;
1540 		of << "Lod Value: " << mLodValue << std::endl;
1541 		of << "Number of Materials: " << mMaterialBucketMap.size() << std::endl;
1542 		for (MaterialBucketMap::const_iterator i = mMaterialBucketMap.begin();
1543 			i != mMaterialBucketMap.end(); ++i)
1544 		{
1545 			i->second->dump(of);
1546 		}
1547 		of << "------------------" << std::endl;
1548 
1549 	}
1550 	//--------------------------------------------------------------------------
1551 	//--------------------------------------------------------------------------
MaterialBucket(LODBucket * parent,const String & materialName)1552 	InstancedGeometry::MaterialBucket::MaterialBucket(LODBucket* parent,
1553 		const String& materialName)
1554 		: mParent(parent)
1555 		, mMaterialName(materialName)
1556 		, mTechnique(0)
1557 		, mLastIndex(0)
1558 	{
1559 		mMaterial = MaterialManager::getSingleton().getByName(mMaterialName);
1560 	}
1561 	//--------------------------------------------------------------------------
~MaterialBucket()1562 	InstancedGeometry::MaterialBucket::~MaterialBucket()
1563 	{
1564 		// delete
1565 		for (GeometryBucketList::iterator i = mGeometryBucketList.begin();
1566 			i != mGeometryBucketList.end(); ++i)
1567 		{
1568 			OGRE_DELETE *i;
1569 		}
1570 		mGeometryBucketList.clear();
1571 		// no need to delete queued meshes, these are managed in InstancedGeometry
1572 
1573 	}
1574 	//--------------------------------------------------------------------------
1575 
assign(QueuedGeometry * qgeom)1576 	void InstancedGeometry::MaterialBucket::assign(QueuedGeometry* qgeom)
1577 	{
1578 		// Look up any current geometry
1579 		String formatString = getGeometryFormatString(qgeom->geometry);
1580 		CurrentGeometryMap::iterator gi = mCurrentGeometryMap.find(formatString);
1581 		bool newBucket = true;
1582 		if (gi != mCurrentGeometryMap.end())
1583 		{
1584 			// Found existing geometry, try to assign
1585 			newBucket = !gi->second->assign(qgeom);
1586 			// Note that this bucket will be replaced as the 'current'
1587 			// for this format string below since it's out of space
1588 		}
1589 		// Do we need to create a new one?
1590 		if (newBucket)
1591 		{
1592 			GeometryBucket* gbucket = OGRE_NEW GeometryBucket(this, formatString,
1593 				qgeom->geometry->vertexData, qgeom->geometry->indexData);
1594 			// Add to main list
1595 			mGeometryBucketList.push_back(gbucket);
1596 			// Also index in 'current' list
1597 			mCurrentGeometryMap[formatString] = gbucket;
1598 			if (!gbucket->assign(qgeom))
1599 			{
1600 				OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
1601 					"Somehow we couldn't fit the requested geometry even in a "
1602 					"brand new GeometryBucket!! Must be a bug, please report.",
1603 					"InstancedGeometry::MaterialBucket::assign");
1604 			}
1605 		}
1606 	}
1607 	//--------------------------------------------------------------------------
build()1608 	void InstancedGeometry::MaterialBucket::build()
1609 	{
1610 		mTechnique = 0;
1611 		mMaterial = MaterialManager::getSingleton().getByName(mMaterialName);
1612 		if (mMaterial.isNull())
1613 		{
1614 			OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
1615 				"Material '" + mMaterialName + "' not found.",
1616 				"InstancedGeometry::MaterialBucket::build");
1617 		}
1618 		mMaterial->load();
1619 		// tell the geometry buckets to build
1620 
1621 		for (GeometryBucketList::iterator i = mGeometryBucketList.begin();
1622 			i != mGeometryBucketList.end(); ++i)
1623 		{
1624 			(*i)->build();
1625 		}
1626 	}
1627 	//--------------------------------------------------------------------------
addRenderables(RenderQueue * queue,uint8 group,Real lodValue)1628 	void InstancedGeometry::MaterialBucket::addRenderables(RenderQueue* queue,
1629 		uint8 group, Real lodValue)
1630 	{
1631         // Get batch instance
1632         BatchInstance *batchInstance = mParent->getParent();
1633 
1634         // Get material LOD strategy
1635         const LodStrategy *materialLodStrategy = mMaterial->getLodStrategy();
1636 
1637         // If material strategy doesn't match, recompute LOD value with correct strategy
1638         if (materialLodStrategy != batchInstance->mLodStrategy)
1639             lodValue = materialLodStrategy->getValue(batchInstance, batchInstance->mCamera);
1640 
1641 		// Determine the current material technique
1642 		mTechnique = mMaterial->getBestTechnique(
1643 			mMaterial->getLodIndex(lodValue));
1644 		GeometryBucketList::iterator i, iend;
1645 		iend =  mGeometryBucketList.end();
1646 
1647 		for (i = mGeometryBucketList.begin(); i != iend; ++i)
1648 		{
1649 			queue->addRenderable(*i, group);
1650 		}
1651 
1652 	}
1653 	//---------------------------------------------------------------------
visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)1654 	void InstancedGeometry::MaterialBucket::visitRenderables(
1655 		Renderable::Visitor* visitor, bool debugRenderables)
1656 	{
1657 		GeometryBucketList::iterator i, iend;
1658 		iend =  mGeometryBucketList.end();
1659 		for (i = mGeometryBucketList.begin(); i != iend; ++i)
1660 		{
1661 			(*i)->visitRenderables(visitor, debugRenderables);
1662 		}
1663 
1664 	}
1665 	//--------------------------------------------------------------------------
getGeometryFormatString(SubMeshLodGeometryLink * geom)1666 	String InstancedGeometry::MaterialBucket::getGeometryFormatString(
1667 		SubMeshLodGeometryLink* geom)
1668 	{
1669 		// Formulate an identifying string for the geometry format
1670 		// Must take into account the vertex declaration and the index type
1671 		// Format is (all lines separated by '|'):
1672 		// Index type
1673 		// Vertex element (repeating)
1674 		//   source
1675 		//   semantic
1676 		//   type
1677 		StringUtil::StrStreamType str;
1678 
1679 		str << geom->indexData->indexBuffer->getType() << "|";
1680 		const VertexDeclaration::VertexElementList& elemList =
1681 			geom->vertexData->vertexDeclaration->getElements();
1682 		VertexDeclaration::VertexElementList::const_iterator ei, eiend;
1683 		eiend = elemList.end();
1684 		for (ei = elemList.begin(); ei != eiend; ++ei)
1685 		{
1686 			const VertexElement& elem = *ei;
1687 			str << elem.getSource() << "|";
1688 			str << elem.getSource() << "|";
1689 			str << elem.getSemantic() << "|";
1690 			str << elem.getType() << "|";
1691 		}
1692 
1693 		return str.str();
1694 
1695 	}
1696 	//--------------------------------------------------------------------------
1697 	InstancedGeometry::MaterialBucket::GeometryIterator
getGeometryIterator(void)1698 	InstancedGeometry::MaterialBucket::getGeometryIterator(void)
1699 	{
1700 		return GeometryIterator(
1701 			mGeometryBucketList.begin(), mGeometryBucketList.end());
1702 	}
1703 	//--------------------------------------------------------------------------
dump(std::ofstream & of) const1704 	void InstancedGeometry::MaterialBucket::dump(std::ofstream& of) const
1705 	{
1706 		of << "Material Bucket " << mMaterialName << std::endl;
1707 		of << "--------------------------------------------------" << std::endl;
1708 		of << "Geometry buckets: " << mGeometryBucketList.size() << std::endl;
1709 		for (GeometryBucketList::const_iterator i = mGeometryBucketList.begin();
1710 			i != mGeometryBucketList.end(); ++i)
1711 		{
1712 			(*i)->dump(of);
1713 		}
1714 		of << "--------------------------------------------------" << std::endl;
1715 
1716 	}
1717 	//--------------------------------------------------------------------------
1718 	//--------------------------------------------------------------------------
GeometryBucket(MaterialBucket * parent,const String & formatString,const VertexData * vData,const IndexData * iData)1719 	InstancedGeometry::GeometryBucket::GeometryBucket(MaterialBucket* parent,
1720 		const String& formatString, const VertexData* vData,
1721 		const IndexData* iData)
1722 	: SimpleRenderable()
1723 	, mParent(parent)
1724 	, mFormatString(formatString)
1725 	, mVertexData(0)
1726 	, mIndexData(0)
1727 	{
1728 		_initGeometryBucket(vData, iData);
1729 	}
1730 	//--------------------------------------------------------------------------
GeometryBucket(const String & name,MaterialBucket * parent,const String & formatString,const VertexData * vData,const IndexData * iData)1731 	InstancedGeometry::GeometryBucket::GeometryBucket(const String& name, MaterialBucket* parent,
1732 		const String& formatString, const VertexData* vData,
1733 		const IndexData* iData)
1734 	: SimpleRenderable(name)
1735 	, mParent(parent)
1736 	, mFormatString(formatString)
1737 	, mVertexData(0)
1738 	, mIndexData(0)
1739 	{
1740 		_initGeometryBucket(vData, iData);
1741 	}
1742 	//--------------------------------------------------------------------------
GeometryBucket(MaterialBucket * parent,const String & formatString,GeometryBucket * bucket)1743 	InstancedGeometry::GeometryBucket::GeometryBucket(MaterialBucket* parent,
1744 		const String& formatString,GeometryBucket* bucket)
1745 	: SimpleRenderable()
1746 	, mParent(parent)
1747 	, mFormatString(formatString)
1748 	, mVertexData(0)
1749 	, mIndexData(0)
1750 	{
1751 		_initGeometryBucket(bucket);
1752 	}
1753 	//--------------------------------------------------------------------------
GeometryBucket(const String & name,MaterialBucket * parent,const String & formatString,GeometryBucket * bucket)1754 	InstancedGeometry::GeometryBucket::GeometryBucket(const String& name, MaterialBucket* parent,
1755 		const String& formatString,GeometryBucket* bucket)
1756 	: SimpleRenderable(name)
1757 	, mParent(parent)
1758 	, mFormatString(formatString)
1759 	, mVertexData(0)
1760 	, mIndexData(0)
1761 	{
1762 		_initGeometryBucket(bucket);
1763 	}
1764 	//--------------------------------------------------------------------------
_initGeometryBucket(const VertexData * vData,const IndexData * iData)1765 	void InstancedGeometry::GeometryBucket::_initGeometryBucket(const VertexData* vData, const IndexData* iData)
1766 	{
1767 	   	mBatch=mParent->getParent()->getParent()->getParent();
1768 		if(!mBatch->getBaseSkeleton().isNull())
1769 			setCustomParameter(0,Vector4(mBatch->getBaseSkeleton()->getNumBones(),0,0,0));
1770 		//mRenderOperation=OGRE_NEW RenderOperation();
1771 		// Clone the structure from the example
1772 		mVertexData = vData->clone(false);
1773 
1774 		mRenderOp.useIndexes = true;
1775 		mRenderOp.indexData = OGRE_NEW IndexData();
1776 
1777 		mRenderOp.indexData->indexCount = 0;
1778 		mRenderOp.indexData->indexStart = 0;
1779 		mRenderOp.vertexData = OGRE_NEW VertexData();
1780 		mRenderOp.vertexData->vertexCount = 0;
1781 
1782         // VertexData constructor creates vertexDeclaration; must release to avoid
1783         // memory leak
1784         HardwareBufferManager::getSingleton().destroyVertexDeclaration(mRenderOp.vertexData->vertexDeclaration);
1785 		mRenderOp.vertexData->vertexDeclaration = vData->vertexDeclaration->clone();
1786 		mIndexType = iData->indexBuffer->getType();
1787 		// Derive the max vertices
1788 		if (mIndexType == HardwareIndexBuffer::IT_32BIT)
1789 		{
1790 			mMaxVertexIndex = 0xFFFFFFFF;
1791 		}
1792 		else
1793 		{
1794 			mMaxVertexIndex = 0xFFFF;
1795 		}
1796 
1797 
1798 		size_t offset=0;
1799 		unsigned short texCoordOffset=0;
1800 		unsigned short texCoordSource=0;
1801 
1802 		const Ogre::VertexElement*elem=mRenderOp.vertexData->vertexDeclaration->findElementBySemantic(VES_TEXTURE_COORDINATES);
1803 
1804 		if (elem != NULL)
1805 		{
1806 			texCoordSource=elem->getSource();
1807 		}
1808 		for(ushort i=0;i<mRenderOp.vertexData->vertexDeclaration->getElementCount();i++)
1809 		{
1810 			if(mRenderOp.vertexData->vertexDeclaration->getElement(i)->getSemantic() == VES_TEXTURE_COORDINATES)
1811 			{
1812 				texCoordOffset++;
1813 			}
1814 			if(texCoordSource==mRenderOp.vertexData->vertexDeclaration->getElement(i)->getSource())
1815 			{
1816 				offset+= VertexElement::getTypeSize(
1817 				mRenderOp.vertexData->vertexDeclaration->getElement(i)->getType());
1818 			}
1819 		}
1820 
1821 		mRenderOp.vertexData->vertexDeclaration->addElement(texCoordSource, offset, VET_FLOAT1, VES_TEXTURE_COORDINATES, texCoordOffset);
1822  		mTexCoordIndex = texCoordOffset;
1823 
1824 	}
1825 	//--------------------------------------------------------------------------
_initGeometryBucket(GeometryBucket * bucket)1826 	void InstancedGeometry::GeometryBucket::_initGeometryBucket(GeometryBucket* bucket)
1827 	{
1828 
1829 	   	mBatch=mParent->getParent()->getParent()->getParent();
1830 		if(!mBatch->getBaseSkeleton().isNull())
1831 			setCustomParameter(0,Vector4(mBatch->getBaseSkeleton()->getNumBones(),0,0,0));
1832 		bucket->getRenderOperation(mRenderOp);
1833 		mVertexData=mRenderOp.vertexData;
1834 		mIndexData=mRenderOp.indexData;
1835 		setBoundingBox(AxisAlignedBox(-10000,-10000,-10000,
1836 		10000,10000,10000));
1837 
1838 	}
1839 	//--------------------------------------------------------------------------
~GeometryBucket()1840 	InstancedGeometry::GeometryBucket::~GeometryBucket()
1841 	{
1842 	}
1843 
1844 	//--------------------------------------------------------------------------
getBoundingRadius(void) const1845 	Real InstancedGeometry::GeometryBucket::getBoundingRadius(void) const
1846 	{
1847 		return 1;
1848 	}
1849 	//--------------------------------------------------------------------------
getMaterial(void) const1850 	const MaterialPtr& InstancedGeometry::GeometryBucket::getMaterial(void) const
1851 	{
1852 		return mParent->getMaterial();
1853 	}
1854 	//--------------------------------------------------------------------------
getTechnique(void) const1855 	Technique* InstancedGeometry::GeometryBucket::getTechnique(void) const
1856 	{
1857 		return mParent->getCurrentTechnique();
1858 	}
1859 	//--------------------------------------------------------------------------
getWorldTransforms(Matrix4 * xform) const1860 	void InstancedGeometry::GeometryBucket::getWorldTransforms(Matrix4* xform) const
1861 	{
1862 			// Should be the identity transform, but lets allow transformation of the
1863 		// nodes the BatchInstances are attached to for kicks
1864 		if(mBatch->getBaseSkeleton().isNull())
1865 		{
1866 			BatchInstance::ObjectsMap::iterator it,itbegin,itend,newit;
1867 			itbegin=mParent->getParent()->getParent()->getInstancesMap().begin();
1868 			itend=mParent->getParent()->getParent()->getInstancesMap().end();
1869 
1870             if( mParent->getParent()->getParent()->getParent()->getProvideWorldInverses() )
1871             {
1872                 // For shaders that use normal maps on instanced geometry objects,
1873                 // we can pass the world transform inverse matrices alongwith with
1874                 // the world matrices. This reduces our usable geometry limit by
1875                 // half in each instance.
1876 			    for (it=itbegin;
1877 				    it!=itend;
1878 				    ++it,xform+=2)
1879 			    {
1880 
1881 					    *xform = it->second->mTransformation;
1882                         *(xform+1) = xform->inverse();
1883 			    }
1884             }
1885             else
1886             {
1887                 for (it=itbegin;
1888                     it!=itend;
1889                     ++it,xform++)
1890                 {
1891 
1892                     *xform = it->second->mTransformation;
1893                 }
1894             }
1895 		}
1896 		else
1897 		{
1898 			BatchInstance::ObjectsMap::iterator it,itbegin,itend,newit;
1899 			itbegin=mParent->getParent()->getParent()->getInstancesMap().begin();
1900 			itend=mParent->getParent()->getParent()->getInstancesMap().end();
1901 
1902 			for (it=itbegin;
1903 				it!=itend;
1904 				++it)
1905 			{
1906 
1907                 if( mParent->getParent()->getParent()->getParent()->getProvideWorldInverses() )
1908                 {
1909 				    for(int i=0;i<it->second->mNumBoneMatrices;++i,xform+=2)
1910 				    {
1911 					    *xform = it->second->mBoneWorldMatrices[i];
1912                         *(xform+1) = xform->inverse();
1913 				    }
1914                 }
1915                 else
1916                 {
1917                     for(int i=0;i<it->second->mNumBoneMatrices;++i,xform++)
1918                     {
1919                         *xform = it->second->mBoneWorldMatrices[i];
1920                     }
1921                 }
1922 			}
1923 
1924 		}
1925 
1926 	}
1927 	//--------------------------------------------------------------------------
getNumWorldTransforms(void) const1928 	unsigned short InstancedGeometry::GeometryBucket::getNumWorldTransforms(void) const
1929 	{
1930         bool bSendInverseXfrm = mParent->getParent()->getParent()->getParent()->getProvideWorldInverses();
1931 
1932 		if(mBatch->getBaseSkeleton().isNull())
1933 		{
1934 			BatchInstance* batch=mParent->getParent()->getParent();
1935             return static_cast<ushort>(batch->getInstancesMap().size() * (bSendInverseXfrm ? 2 : 1));
1936 		}
1937 		else
1938 		{
1939 			BatchInstance* batch=mParent->getParent()->getParent();
1940 			return static_cast<ushort>(
1941 				mBatch->getBaseSkeleton()->getNumBones()*batch->getInstancesMap().size() * (bSendInverseXfrm ? 2 : 1));
1942 		}
1943 	}
1944 	//--------------------------------------------------------------------------
getSquaredViewDepth(const Camera * cam) const1945 	Real InstancedGeometry::GeometryBucket::getSquaredViewDepth(const Camera* cam) const
1946 	{
1947         const BatchInstance *batchInstance = mParent->getParent()->getParent();
1948         if (cam == batchInstance->mCamera)
1949             return batchInstance->mSquaredViewDepth;
1950         else
1951             return batchInstance->getParentNode()->getSquaredViewDepth(cam->getLodCamera());
1952 	}
1953 	//--------------------------------------------------------------------------
getLights(void) const1954 	const LightList& InstancedGeometry::GeometryBucket::getLights(void) const
1955 	{
1956 		return mParent->getParent()->getParent()->getLights();
1957 	}
1958 	//--------------------------------------------------------------------------
getCastsShadows(void) const1959 	bool InstancedGeometry::GeometryBucket::getCastsShadows(void) const
1960 	{
1961 		return mParent->getParent()->getParent()->getCastShadows();
1962 	}
1963 	//--------------------------------------------------------------------------
assign(QueuedGeometry * qgeom)1964 	bool InstancedGeometry::GeometryBucket::assign(QueuedGeometry* qgeom)
1965 	{
1966 		// Do we have enough space?
1967 		if (mRenderOp.vertexData->vertexCount + qgeom->geometry->vertexData->vertexCount
1968 			> mMaxVertexIndex)
1969 		{
1970 			return false;
1971 		}
1972 
1973 		mQueuedGeometry.push_back(qgeom);
1974 		mRenderOp.vertexData->vertexCount += qgeom->geometry->vertexData->vertexCount;
1975 		mRenderOp.indexData->indexCount += qgeom->geometry->indexData->indexCount;
1976 
1977 		return true;
1978 	}
1979 
1980 	//--------------------------------------------------------------------------
build()1981 	void InstancedGeometry::GeometryBucket::build()
1982 	{
1983 
1984 
1985 
1986 		// Ok, here's where we transfer the vertices and indexes to the shared
1987 		// buffers
1988 		// Shortcuts
1989 		VertexDeclaration* dcl = mRenderOp.vertexData->vertexDeclaration;
1990 		VertexBufferBinding* binds =mVertexData->vertexBufferBinding;
1991 
1992 		// create index buffer, and lock
1993 		mRenderOp.indexData->indexBuffer = HardwareBufferManager::getSingleton()
1994 			.createIndexBuffer(mIndexType, mRenderOp.indexData->indexCount,
1995 				HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1996 		uint32* p32Dest = 0;
1997 		uint16* p16Dest = 0;
1998 
1999 		if (mIndexType == HardwareIndexBuffer::IT_32BIT)
2000 		{
2001 			p32Dest = static_cast<uint32*>(
2002 				mRenderOp.indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
2003 		}
2004 		else
2005 		{
2006 			p16Dest = static_cast<uint16*>(
2007 				mRenderOp.indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
2008 		}
2009 
2010 		// create all vertex buffers, and lock
2011 		ushort b;
2012 		//ushort posBufferIdx = dcl->findElementBySemantic(VES_POSITION)->getSource();
2013 
2014 		vector<uchar*>::type destBufferLocks;
2015 		vector<VertexDeclaration::VertexElementList>::type bufferElements;
2016 
2017 		for (b = 0; b < binds->getBufferCount(); ++b)
2018 		{
2019 
2020 			size_t vertexCount = mRenderOp.vertexData->vertexCount;
2021 
2022 			HardwareVertexBufferSharedPtr vbuf =
2023 				HardwareBufferManager::getSingleton().createVertexBuffer(
2024 					dcl->getVertexSize(b),
2025 					vertexCount,
2026 					HardwareBuffer::HBU_STATIC_WRITE_ONLY);
2027 			binds->setBinding(b, vbuf);
2028 			uchar* pLock = static_cast<uchar*>(
2029 				vbuf->lock(HardwareBuffer::HBL_DISCARD));
2030 			destBufferLocks.push_back(pLock);
2031 			// Pre-cache vertex elements per buffer
2032 			bufferElements.push_back(dcl->findElementsBySource(b));
2033 			mRenderOp.vertexData->vertexBufferBinding->setBinding(b,vbuf);
2034 		}
2035 
2036 
2037 		// Iterate over the geometry items
2038 		size_t indexOffset = 0;
2039 
2040 		QueuedGeometryList::iterator gi, giend;
2041 		giend = mQueuedGeometry.end();
2042 
2043 		// to generate the boundingBox
2044 		Real Xmin,Ymin,Zmin,Xmax,Ymax,Zmax;
2045 		Xmin=0;
2046 		Ymin=0;
2047 		Zmin=0;
2048 		Xmax=0;
2049 		Ymax=0;
2050 		Zmax=0;
2051 		QueuedGeometry* precGeom = *(mQueuedGeometry.begin());
2052 		unsigned short index=0;
2053 		if( mParent->getLastIndex()!=0)
2054 			index=mParent->getLastIndex()+1;
2055 
2056 		for (gi = mQueuedGeometry.begin(); gi != giend; ++gi)
2057 		{
2058 
2059 			QueuedGeometry* geom = *gi;
2060 			if(precGeom->ID!=geom->ID)
2061 					index++;
2062 
2063 			//create  a new instanced object
2064 			InstancedObject* instancedObject = mParent->getParent()->getParent()->isInstancedObjectPresent(index);
2065 			if(instancedObject == NULL)
2066 			{
2067 				if(mBatch->getBaseSkeleton().isNull())
2068 				{
2069 					instancedObject= OGRE_NEW InstancedObject(index);
2070 				}
2071 				else
2072 				{
2073 					instancedObject= OGRE_NEW InstancedObject(index,mBatch->getBaseSkeletonInstance(),
2074 						mBatch->getBaseAnimationState());
2075 				}
2076 				mParent->getParent()->getParent()->addInstancedObject(index,instancedObject);
2077 
2078 			}
2079 			instancedObject->addBucketToList(this);
2080 
2081 
2082 
2083 			// Copy indexes across with offset
2084 			IndexData* srcIdxData = geom->geometry->indexData;
2085 			if (mIndexType == HardwareIndexBuffer::IT_32BIT)
2086 			{
2087 				// Lock source indexes
2088 				uint32* pSrc = static_cast<uint32*>(
2089 					srcIdxData->indexBuffer->lock(
2090 						srcIdxData->indexStart,
2091 						srcIdxData->indexCount * srcIdxData->indexBuffer->getIndexSize(),
2092 						HardwareBuffer::HBL_READ_ONLY));
2093 
2094 				copyIndexes(pSrc, p32Dest, srcIdxData->indexCount, indexOffset);
2095 				p32Dest += srcIdxData->indexCount;
2096 				srcIdxData->indexBuffer->unlock();
2097 			}
2098 			else
2099 			{
2100 
2101 				// Lock source indexes
2102 				uint16* pSrc = static_cast<uint16*>(
2103 					srcIdxData->indexBuffer->lock(
2104 					srcIdxData->indexStart,
2105 					srcIdxData->indexCount * srcIdxData->indexBuffer->getIndexSize(),
2106 					HardwareBuffer::HBL_READ_ONLY));
2107 
2108 				copyIndexes(pSrc, p16Dest, srcIdxData->indexCount, indexOffset);
2109 				p16Dest += srcIdxData->indexCount;
2110 				srcIdxData->indexBuffer->unlock();
2111 			}
2112 
2113 			// Now deal with vertex buffers
2114 			// we can rely on buffer counts / formats being the same
2115 			VertexData* srcVData = geom->geometry->vertexData;
2116 			VertexBufferBinding* srcBinds = srcVData->vertexBufferBinding;
2117 
2118 			for (b = 0; b < binds->getBufferCount(); ++b)
2119 			{
2120 
2121 				// lock source
2122 				HardwareVertexBufferSharedPtr srcBuf =
2123 					srcBinds->getBuffer(b);
2124 				uchar* pSrcBase = static_cast<uchar*>(
2125 					srcBuf->lock(HardwareBuffer::HBL_READ_ONLY));
2126 				// Get buffer lock pointer, we'll update this later
2127 				uchar* pDstBase = destBufferLocks[b];
2128 				size_t bufInc = srcBuf->getVertexSize();
2129 
2130 				// Iterate over vertices
2131 				float *pSrcReal, *pDstReal;
2132 				Vector3 tmp;
2133 
2134 
2135 
2136 				for (size_t v = 0; v < srcVData->vertexCount; ++v)
2137 				{
2138 					//to know if the current buffer is the one with the buffer or not
2139 					bool isTheBufferWithIndex=false;
2140 					// Iterate over vertex elements
2141 					VertexDeclaration::VertexElementList& elems =
2142 						bufferElements[b];
2143 					VertexDeclaration::VertexElementList::iterator ei;
2144 
2145 					for (ei = elems.begin(); ei != elems.end(); ++ei)
2146 					{
2147 						VertexElement& elem = *ei;
2148 						elem.baseVertexPointerToElement(pSrcBase, &pSrcReal);
2149 						elem.baseVertexPointerToElement(pDstBase, &pDstReal);
2150 						if(elem.getSemantic()==VES_TEXTURE_COORDINATES && elem.getIndex()==mTexCoordIndex)
2151 						{
2152 							isTheBufferWithIndex=true;
2153 							*pDstReal++ = static_cast<float>(index);
2154 						}
2155 						else
2156 						{
2157 							switch (elem.getSemantic())
2158 							{
2159 								case VES_POSITION:
2160 									tmp.x = pSrcReal[0];
2161 									tmp.y = pSrcReal[1];
2162 									tmp.z = pSrcReal[2];
2163 									if(tmp.x<Xmin)
2164 										Xmin = tmp.x;
2165 									if(tmp.y<Ymin)
2166 										Ymin = tmp.y;
2167 									if(tmp.z<Zmin)
2168 										Zmin = tmp.z;
2169 									if(tmp.x>Xmax)
2170 										Xmax = tmp.x;
2171 									if(tmp.y>Ymax)
2172 										Ymax = tmp.y;
2173 									if(tmp.z>Zmax)
2174 										Zmax = tmp.z;
2175 								default:
2176 								// just raw copy
2177 								memcpy(pDstReal, pSrcReal,
2178 									VertexElement::getTypeSize(elem.getType()));
2179 								break;
2180 							};
2181 
2182 						}
2183 
2184 
2185 					}
2186 					if (isTheBufferWithIndex)
2187 					pDstBase += bufInc+4;
2188 					else
2189 					pDstBase += bufInc;
2190 					pSrcBase += bufInc;
2191 
2192 				}
2193 
2194 				// Update pointer
2195 				destBufferLocks[b] = pDstBase;
2196 				srcBuf->unlock();
2197 
2198 
2199 			}
2200 			indexOffset += geom->geometry->vertexData->vertexCount;
2201 
2202 
2203 		precGeom=geom;
2204 
2205 		}
2206 		mParent->setLastIndex(index);
2207 		// Unlock everything
2208 		mRenderOp.indexData->indexBuffer->unlock();
2209 		for (b = 0; b < binds->getBufferCount(); ++b)
2210 		{
2211 			binds->getBuffer(b)->unlock();
2212 		}
2213 
2214 	OGRE_DELETE mVertexData;
2215 	OGRE_DELETE mIndexData;
2216 
2217 	mVertexData=mRenderOp.vertexData;
2218 	mIndexData=mRenderOp.indexData;
2219 	mBatch->getRenderOperationVector().push_back(&mRenderOp);
2220 	setBoundingBox(AxisAlignedBox(Xmin,Ymin,Zmin,Xmax,Ymax,Zmax));
2221 	mAABB=AxisAlignedBox(Xmin,Ymin,Zmin,Xmax,Ymax,Zmax);
2222 
2223 	}
2224 	//--------------------------------------------------------------------------
dump(std::ofstream & of) const2225 	void InstancedGeometry::GeometryBucket::dump(std::ofstream& of) const
2226 	{
2227 		of << "Geometry Bucket" << std::endl;
2228 		of << "---------------" << std::endl;
2229 		of << "Format string: " << mFormatString << std::endl;
2230 		of << "Geometry items: " << mQueuedGeometry.size() << std::endl;
2231 		of << "---------------" << std::endl;
2232 
2233 	}
2234 	//--------------------------------------------------------------------------
visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)2235 	void InstancedGeometry::GeometryBucket::visitRenderables(
2236 		Renderable::Visitor* visitor, bool debugRenderables)
2237 	{
2238 		visitor->visit(this, mParent->getParent()->getLod(), false);
2239 	}
2240 	//---------------------------------------------------------------------
2241 
2242 }
2243 
2244