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