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  Also see acknowledgements in Readme.html
9 
10  You may use this sample code for anything you like, it is not covered by the
11  same license as the rest of the engine.
12  -----------------------------------------------------------------------------
13 */
14 
15 #include "OgreConfigFile.h"
16 #include "OgreStringConverter.h"
17 #include "OgreException.h"
18 #include "OgreInstancedGeometry.h"
19 #include "SdkSample.h"
20 #include "SamplePlugin.h"
21 
22 using namespace Ogre;
23 using namespace OgreBites;
24 
25 #define maxObjectsPerBatch 80
26 #ifndef FLT_MAX
27 #  define FLT_MAX         3.402823466e+38F        /* max value */
28 #endif
29 
30 const size_t numTypeMeshes = 4;
31 
32 Ogre::String meshes[]=
33 {
34 	"razor", //0
35 	"knot",
36 	"tudorhouse",
37 	"WoodPallet"//6
38 };
39 
40 enum CurrentGeomOpt{
41 	INSTANCE_OPT,
42 	STATIC_OPT,
43 	ENTITY_OPT
44 };
45 
46 class _OgreSampleClassExport  Sample_Instancing : public SdkSample
47 {
48 public:
49 
50 	//-----------------------------------------------------------------------
Sample_Instancing()51 	Sample_Instancing()
52 	{
53 		mInfo["Title"] = "Instancing";
54 		mInfo["Description"] = "A demo of different methods to handle a large number of objects.";
55 		mInfo["Thumbnail"] = "thumb_instancing.png";
56 		mInfo["Category"] = "Geometry";
57 	}
58 
59 	//-----------------------------------------------------------------------
frameRenderingQueued(const FrameEvent & evt)60 	bool frameRenderingQueued(const FrameEvent& evt)
61 	{
62 		burnCPU();
63 		return SdkSample::frameRenderingQueued(evt);
64 	}
65 
66 protected:
67 
setObjectCount(size_t val)68 	void setObjectCount(size_t val)
69 	{
70 		mNumMeshes=val;
71 	};
72 	//-----------------------------------------------------------------------
setBurnedTime(double timeBurned)73 	void setBurnedTime(double timeBurned)
74 	{
75 		mBurnAmount=timeBurned;
76 	};
77 	//-----------------------------------------------------------------------
changeSelectedMesh(size_t number)78 	void changeSelectedMesh(size_t number)
79 	{
80 		mSelectedMesh=number;
81 	}
82 
83 	//-----------------------------------------------------------------------
burnCPU()84 	void burnCPU()
85 	{
86 		double mStartTime = mTimer->getMicroseconds()/1000000.0f; //convert into seconds
87 		double mCurTime =  mStartTime;
88 		double mStopTime = mLastTime + mBurnAmount;
89 
90 		while( mCurTime < mStopTime )
91 		{
92 			mCurTime = mTimer->getMicroseconds()/1000000.0f; //convert into seconds
93 		}
94 
95 		mLastTime = mTimer->getMicroseconds()/1000000.0f; //convert into seconds
96 	}
97 	//-----------------------------------------------------------------------
destroyCurrentGeomOpt()98 	void destroyCurrentGeomOpt()
99 	{
100 		switch(mCurrentGeomOpt)
101 		{
102 		case INSTANCE_OPT: destroyInstanceGeom(); break;
103 		case STATIC_OPT: destroyStaticGeom(); break;
104 		case ENTITY_OPT: destroyEntityGeom(); break;
105 		}
106 
107 		assert (mNumRendered == posMatrices.size ());
108 		for (size_t i = 0; i < mNumRendered; i++)
109 		{
110 			delete [] posMatrices[i];
111 		}
112 
113 		posMatrices.clear();
114 	}
115 	//-----------------------------------------------------------------------
createCurrentGeomOpt()116 	void createCurrentGeomOpt()
117 	{
118 		objectCount = mNumMeshes;
119 		mNumRendered = 1;
120 
121 		while(objectCount>maxObjectsPerBatch)
122 		{
123 			mNumRendered++;
124 			objectCount-=maxObjectsPerBatch;
125 		}
126 
127 		assert (mSelectedMesh < numTypeMeshes);
128 		MeshPtr m = MeshManager::getSingleton ().getByName (meshes[mSelectedMesh] + ".mesh");
129 		if (m.isNull ())
130 		{
131 			m = MeshManager::getSingleton ().load (meshes[mSelectedMesh] + ".mesh",
132 				ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME);
133 		}
134 		const Real radius = m->getBoundingSphereRadius ();
135 
136 		// could/should print on screen mesh name,
137 		//optimisation type,
138 		//mesh vertices num,
139 		//32 bit or not,
140 		//etC..
141 
142 
143 
144 		posMatrices.resize (mNumRendered);
145 		posMatrices.reserve (mNumRendered);
146 
147 
148 		vector <Vector3 *>::type posMatCurr;
149 		posMatCurr.resize (mNumRendered);
150 		posMatCurr.reserve (mNumRendered);
151 		for (size_t i = 0; i < mNumRendered; i++)
152 		{
153 			posMatrices[i] = new Vector3[mNumMeshes];
154 			posMatCurr[i] = posMatrices[i];
155 		}
156 
157 		size_t i = 0, j = 0;
158 		for (size_t p = 0; p < mNumMeshes; p++)
159 		{
160 			for (size_t k = 0; k < mNumRendered; k++)
161 			{
162 				posMatCurr[k]->x = radius*i;
163 				posMatCurr[k]->y = k*radius;
164 
165 				posMatCurr[k]->z = radius*j;
166 				posMatCurr[k]++;
167 			}
168 			if (++j== 10)
169 			{
170 				j = 0;
171 				i++;
172 			}
173 
174 		}
175 		posMatCurr.clear ();
176 
177 
178 		switch(mCurrentGeomOpt)
179 		{
180 		case INSTANCE_OPT:createInstanceGeom();break;
181 		case STATIC_OPT:createStaticGeom ();break;
182 		case ENTITY_OPT: createEntityGeom ();break;
183 		}
184 	}
185 	//-----------------------------------------------------------------------
createInstanceGeom()186 	void createInstanceGeom()
187 	{
188 		if (Root::getSingleton ().getRenderSystem ()->getCapabilities ()->hasCapability (RSC_VERTEX_PROGRAM) == false)
189 		{
190 			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Your video card doesn't support batching", "Demo_Instance::createScene");
191 		}
192 
193 		Entity *ent = mSceneMgr->createEntity(meshes[mSelectedMesh], meshes[mSelectedMesh] + ".mesh");
194 
195 
196 		renderInstance.reserve(mNumRendered);
197 		renderInstance.resize(mNumRendered);
198 
199 		//Load a mesh to read data from.
200 		InstancedGeometry* batch = mSceneMgr->createInstancedGeometry(meshes[mSelectedMesh] + "s" );
201 		batch->setCastShadows(true);
202 
203 		batch->setBatchInstanceDimensions (Vector3(1000000, 1000000, 1000000));
204 		const size_t batchSize = (mNumMeshes > maxObjectsPerBatch) ? maxObjectsPerBatch :mNumMeshes;
205 		setupInstancedMaterialToEntity(ent);
206 		for(size_t i = 0; i < batchSize ; i++)
207 		{
208 			batch->addEntity(ent, Vector3::ZERO);
209 		}
210 		batch->setOrigin(Vector3::ZERO);
211 
212 		batch->build();
213 
214 
215 		for (size_t k = 0; k < mNumRendered-1; k++)
216 		{
217 			batch->addBatchInstance();
218 		}
219 		InstancedGeometry::BatchInstanceIterator regIt = batch->getBatchInstanceIterator();
220 		size_t k = 0;
221 		while (regIt.hasMoreElements ())
222 		{
223 
224 			InstancedGeometry::BatchInstance *r = regIt.getNext();
225 
226 			InstancedGeometry::BatchInstance::InstancedObjectIterator bit = r->getObjectIterator();
227 			int j = 0;
228 			while(bit.hasMoreElements())
229 			{
230 				InstancedGeometry::InstancedObject* obj = bit.getNext();
231 
232 				const Vector3 position (posMatrices[k][j]);
233 				obj->setPosition(position);
234 				++j;
235 
236 			}
237 			k++;
238 
239 		}
240 		batch->setVisible(true);
241 		renderInstance[0] = batch;
242 
243 		mSceneMgr->destroyEntity (ent);
244 	}
setupInstancedMaterialToEntity(Entity * ent)245 	void setupInstancedMaterialToEntity(Entity*ent)
246 	{
247 		for (Ogre::uint i = 0; i < ent->getNumSubEntities(); ++i)
248 		{
249 			SubEntity* se = ent->getSubEntity(i);
250 			String materialName= se->getMaterialName();
251 			se->setMaterialName(buildInstancedMaterial(materialName));
252 		}
253 	}
buildInstancedMaterial(const String & originalMaterialName)254 	String buildInstancedMaterial(const String &originalMaterialName)
255 	{
256 		// already instanced ?
257 		if (StringUtil::endsWith (originalMaterialName, "/instanced"))
258 			return originalMaterialName;
259 
260 		MaterialPtr originalMaterial = MaterialManager::getSingleton ().getByName (originalMaterialName);
261 
262 #if defined(INCLUDE_RTSHADER_SYSTEM)
263         originalMaterial->getBestTechnique()->setSchemeName(Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
264 #endif
265 
266 		// if originalMat doesn't exists use "Instancing" material name
267 		const String instancedMaterialName (originalMaterial.isNull() ? "Instancing" : originalMaterialName + "/Instanced");
268 		MaterialPtr  instancedMaterial = MaterialManager::getSingleton ().getByName (instancedMaterialName);
269 
270 		// already exists ?
271 		if (instancedMaterial.isNull())
272 		{
273 			instancedMaterial = originalMaterial->clone(instancedMaterialName);
274 			instancedMaterial->load();
275 			Technique::PassIterator pIt = instancedMaterial->getBestTechnique ()->getPassIterator();
276 			while (pIt.hasMoreElements())
277 			{
278 
279 				Pass * const p = pIt.getNext();
280 				p->setVertexProgram("Instancing", false);
281 				p->setShadowCasterVertexProgram("InstancingShadowCaster");
282 
283 
284 			}
285 		}
286 		instancedMaterial->load();
287 		return instancedMaterialName;
288 
289 
290 	}
291 	//-----------------------------------------------------------------------
destroyInstanceGeom()292 	void destroyInstanceGeom()
293 	{
294         mSceneMgr->destroyAllInstancedGeometry();
295 		renderInstance.clear();
296 	}
297 	//-----------------------------------------------------------------------
createStaticGeom()298 	void createStaticGeom()
299 	{
300 		Entity *ent = mSceneMgr->createEntity(meshes[mSelectedMesh], meshes[mSelectedMesh] + ".mesh");
301 
302 		renderStatic.reserve (mNumRendered);
303 		renderStatic.resize (mNumRendered);
304 
305 		StaticGeometry* geom = mSceneMgr->createStaticGeometry(meshes[mSelectedMesh] + "s");
306 
307 		geom->setRegionDimensions (Vector3(1000000, 1000000, 1000000));
308 		size_t k = 0;
309 		size_t y = 0;
310 		for (size_t i = 0; i < mNumMeshes; i++)
311 		{
312 			if (y==maxObjectsPerBatch)
313 			{
314 				y=0;
315 				k++;
316 			}
317 			geom->addEntity (ent, posMatrices[k][y]);
318 			y++;
319 		}
320 		geom->setCastShadows(true);
321 		geom->build ();
322 		renderStatic[0] = geom;
323 		mSceneMgr->destroyEntity (ent);
324 	}
325 	//-----------------------------------------------------------------------
destroyStaticGeom()326 	void destroyStaticGeom()
327 	{
328         mSceneMgr->destroyAllStaticGeometry();
329 		renderStatic.clear();
330 	}
331 	//-----------------------------------------------------------------------
createEntityGeom()332 	void createEntityGeom()
333 	{
334 		size_t k = 0;
335 		size_t y = 0;
336 		renderEntity.reserve (mNumMeshes);
337 		renderEntity.resize (mNumMeshes);
338 		nodes.reserve (mNumMeshes);
339 		nodes.resize (mNumMeshes);
340 
341 		for (size_t i = 0; i < mNumMeshes; i++)
342 		{
343 			if (y==maxObjectsPerBatch)
344 			{
345 				y=0;
346 				k++;
347 			}
348 
349 			nodes[i]=mSceneMgr->getRootSceneNode()->createChildSceneNode("node"+StringConverter::toString(i));
350 			renderEntity[i]=mSceneMgr->createEntity(meshes[mSelectedMesh]+StringConverter::toString(i), meshes[mSelectedMesh] + ".mesh");
351 			nodes[i]->attachObject(renderEntity[i]);
352 			nodes[i]->setPosition(posMatrices[k][y]);
353 
354 			y++;
355 		}
356 
357 	}
358 	//-----------------------------------------------------------------------
destroyEntityGeom()359 	void destroyEntityGeom()
360 	{
361 		size_t i;
362 		size_t j=0;
363 		for (i=0;i<mNumMeshes;i++)
364 		{
365 			String name=nodes[i]->getName();
366 			mSceneMgr->destroySceneNode(name);
367 			mSceneMgr->destroyEntity(renderEntity[i]);
368 			j++;
369 		}
370 	}
371 
setCurrentGeometryOpt(CurrentGeomOpt opt)372 	void setCurrentGeometryOpt(CurrentGeomOpt opt)
373 	{
374 		mCurrentGeomOpt=opt;
375 	}
376 
377 	//-----------------------------------------------------------------------
378 	// Just override the mandatory create scene method
setupContent()379 	void setupContent()
380 	{
381 		// Set ambient light
382 		mSceneMgr->setAmbientLight(ColourValue(0.2, 0.2, 0.2));
383 		Light* l = mSceneMgr->createLight("MainLight");
384 		//add a skybox
385 		mSceneMgr->setSkyBox(true, "Examples/MorningSkyBox", 1000);
386 		//setup the light
387 		l->setType(Light::LT_DIRECTIONAL);
388 		l->setDirection(-0.5, -0.5, 0);
389 
390 		mCamera->setPosition(500,500, 1500);
391 		mCamera->lookAt(0,0,0);
392 #if OGRE_PLATFORM != OGRE_PLATFORM_APPLE_IOS
393         setDragLook(true);
394 #endif
395 
396 		   Plane plane;
397         plane.normal = Vector3::UNIT_Y;
398         plane.d = 100;
399         MeshManager::getSingleton().createPlane("Myplane",
400             ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,
401             1500,1500,20,20,true,1,5,5,Vector3::UNIT_Z);
402         Entity* pPlaneEnt = mSceneMgr->createEntity( "plane", "Myplane" );
403         pPlaneEnt->setMaterialName("Examples/Rockwall");
404         pPlaneEnt->setCastShadows(false);
405         mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(pPlaneEnt);
406 
407 		CompositorManager::getSingleton().addCompositor(mViewport,"Bloom");
408 
409 		setupControls();
410 
411 		mNumMeshes = 160;
412 		mNumRendered = 0;
413 		mSelectedMesh = 0;
414 		mBurnAmount = 0;
415 		mCurrentGeomOpt = INSTANCE_OPT;
416 		createCurrentGeomOpt();
417 
418 		mTimer = new Ogre::Timer();
419 		mLastTime = mTimer->getMicroseconds() / 1000000.0f;
420 	}
421 
422 	//-----------------------------------------------------------------------
setupControls()423 	void setupControls()
424 	{
425 		SelectMenu* technique = mTrayMgr->createThickSelectMenu(TL_TOPLEFT, "TechniqueType", "Instancing Technique", 200, 3);
426 		technique->addItem("Instancing");
427 		technique->addItem("Static Geometry");
428 		technique->addItem("Independent Entities");
429 
430 		SelectMenu* objectType = mTrayMgr->createThickSelectMenu(TL_TOPLEFT, "ObjectType", "Object : ", 200, 4);
431 		objectType->addItem("razor");
432 		objectType->addItem("knot");
433 		objectType->addItem("tudorhouse");
434 		objectType->addItem("woodpallet");
435 
436 		mTrayMgr->createThickSlider(TL_TOPLEFT, "ObjectCountSlider", "Object count", 200, 50, 0, 1000, 101)->setValue(160, false);
437 
438 		mTrayMgr->createThickSlider(TL_TOPLEFT, "CPUOccupationSlider", "CPU Load (ms)", 200, 75, 0, 1000.0f / 60, 20);
439 
440 		mTrayMgr->createCheckBox(TL_TOPLEFT, "ShadowCheckBox", "Shadows", 200);
441 
442 		mTrayMgr->createCheckBox(TL_TOPLEFT, "PostEffectCheckBox", "Post Effect", 200);
443 
444 		mTrayMgr->showCursor();
445 	}
446 
cleanupContent()447 	void cleanupContent()
448 	{
449         mSceneMgr->destroyAllInstancedGeometry();
450         mSceneMgr->destroyAllStaticGeometry();
451         MeshManager::getSingleton().remove("Myplane");
452 		destroyCurrentGeomOpt();
453 		delete mTimer;
454 	}
455 
sliderMoved(Slider * slider)456 	void sliderMoved(Slider* slider)
457 	{
458 		if (slider->getName() == "ObjectCountSlider")
459 		{
460 			destroyCurrentGeomOpt();
461 			setObjectCount((size_t)slider->getValue());
462 			createCurrentGeomOpt();
463 		}
464 		else if (slider->getName() == "CPUOccupationSlider")
465 		{
466 			setBurnedTime(slider->getValue() / 1000.0f);
467 		}
468 	}
469 
itemSelected(SelectMenu * menu)470 	void itemSelected(SelectMenu* menu)
471 	{
472 		if (menu->getName() == "TechniqueType")
473 		{
474 			//Menu items are synchronized with enum
475 			CurrentGeomOpt selectedOption = (CurrentGeomOpt)menu->getSelectionIndex();
476 			destroyCurrentGeomOpt();
477 			setCurrentGeometryOpt(selectedOption);
478 			createCurrentGeomOpt();
479 		}
480 		else if (menu->getName() == "ObjectType")
481 		{
482 			destroyCurrentGeomOpt();
483 			changeSelectedMesh(menu->getSelectionIndex());
484 			createCurrentGeomOpt();
485 		}
486 	}
487 
checkBoxToggled(CheckBox * box)488 	void checkBoxToggled(CheckBox* box)
489 	{
490 		if (box->getName() == "ShadowCheckBox")
491 		{
492 			if (box->isChecked())
493 			{
494 				mSceneMgr->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE);
495 			}
496 			else
497 			{
498 				mSceneMgr->setShadowTechnique(SHADOWTYPE_NONE);
499 			}
500 		}
501 		else if (box->getName() == "PostEffectCheckBox")
502 		{
503 			CompositorManager::getSingleton().setCompositorEnabled(mViewport,"Bloom",box->isChecked());
504 		}
505 	}
506 
507 	double mAvgFrameTime;
508 	size_t mSelectedMesh;
509 	size_t mNumMeshes;
510 	size_t objectCount;
511 	String mDebugText;
512 	CurrentGeomOpt mCurrentGeomOpt;
513 
514 	size_t mNumRendered;
515 
516 	Ogre::Timer*mTimer;
517 	double mLastTime, mBurnAmount;
518 
519 	vector <InstancedGeometry *>::type renderInstance;
520 	vector <StaticGeometry *>::type renderStatic;
521 	vector <Entity *>::type renderEntity;
522 	vector <SceneNode *>::type nodes;
523 	vector <Vector3 *>::type posMatrices;
524 };
525