1 #include "SamplePlugin.h"
2 #include "NewInstancing.h"
3
4 using namespace Ogre;
5 using namespace OgreBites;
6
7 static const char *c_instancingTechniques[] =
8 {
9 "Shader Based",
10 "Vertex Texture Fetch (VTF)",
11 "Hardware Instancing Basic",
12 "Hardware Instancing + VTF",
13 "Limited Animation - Hardware Instancing + VTF",
14 "No Instancing"
15 };
16
17 static const char *c_materialsTechniques[] =
18 {
19 "Examples/Instancing/ShaderBased/Robot",
20 "Examples/Instancing/VTF/Robot",
21 "Examples/Instancing/HWBasic/Robot",
22 "Examples/Instancing/VTF/HW/Robot",
23 "Examples/Instancing/VTF/HW/LUT/Robot",
24 "Examples/Instancing/ShaderBased/Robot"
25 };
26
27 static const char *c_materialsTechniques_dq[] =
28 {
29 "Examples/Instancing/ShaderBased/Robot_dq",
30 "Examples/Instancing/VTF/Robot_dq",
31 "Examples/Instancing/HWBasic/Robot",
32 "Examples/Instancing/VTF/HW/Robot_dq",
33 "Examples/Instancing/VTF/HW/LUT/Robot_dq",
34 "Examples/Instancing/ShaderBased/Robot_dq"
35 };
36
37 static const char *c_materialsTechniques_dq_two_weights[] =
38 {
39 "Examples/Instancing/ShaderBased/spine_dq_two_weights",
40 "Examples/Instancing/VTF/spine_dq_two_weights",
41 "Examples/Instancing/HWBasic/spine",
42 "Examples/Instancing/VTF/HW/spine_dq_two_weights",
43 "Examples/Instancing/VTF/HW/LUT/spine_dq_two_weights",
44 "Examples/Instancing/ShaderBased/spine_dq_two_weights"
45 };
46
47 static const char *c_meshNames[] =
48 {
49 "robot.mesh",
50 "spine.mesh"
51 };
52
53 //------------------------------------------------------------------------------
Sample_NewInstancing()54 Sample_NewInstancing::Sample_NewInstancing() : NUM_INST_ROW(50), NUM_INST_COLUMN(50), mCurrentManager(0), mCurrentMaterialSet(c_materialsTechniques), mCurrentFlags(0), mSkinningTechniques(NULL)
55 {
56 mInfo["Title"] = "New Instancing";
57 mInfo["Description"] = "Demonstrates how to use the new InstancedManager to setup many dynamic"
58 " instances of the same mesh with much less performance impact";
59 mInfo["Thumbnail"] = "thumb_newinstancing.png";
60 mInfo["Category"] = "Environment";
61 mInfo["Help"] = "Press Space to switch Instancing Techniques.\n"
62 "Press B to toggle bounding boxes.\n\n"
63 "Changes in the slider take effect after switching instancing technique\n"
64 "Different batch sizes give different results depending on CPU culling"
65 " and instance numbers on the scene.\n\n"
66 "If performance is too slow, try defragmenting batches once in a while";
67 }
68
69
70 //------------------------------------------------------------------------------
frameRenderingQueued(const FrameEvent & evt)71 bool Sample_NewInstancing::frameRenderingQueued(const FrameEvent& evt)
72 {
73 if( mAnimateInstances->isChecked() )
74 animateUnits( evt.timeSinceLastEvent );
75
76 if( mMoveInstances->isChecked() )
77 moveUnits( evt.timeSinceLastEvent );
78
79 return SdkSample::frameRenderingQueued(evt); // don't forget the parent class updates!
80 }
81
82 //------------------------------------------------------------------------------
keyPressed(const KeyboardEvent & evt)83 bool Sample_NewInstancing::keyPressed(const KeyboardEvent& evt)
84 {
85 Keycode key = evt.keysym.sym;
86 //Toggle bounding boxes with B key unless the help dialog is visible
87 if (key == 'b' && !mTrayMgr->isDialogVisible() && mCurrentManager)
88 {
89 bool oldShow = mCurrentManager->getSetting( InstanceManager::SHOW_BOUNDINGBOX,
90 mCurrentMaterialSet[mInstancingTechnique] );
91 mCurrentManager->setSetting( InstanceManager::SHOW_BOUNDINGBOX, !oldShow );
92 }
93
94 //Switch to next instancing technique with space bar
95 if (key == SDLK_SPACE && !mTrayMgr->isDialogVisible())
96 mTechniqueMenu->selectItem( (mTechniqueMenu->getSelectionIndex() + 1) % (NUM_TECHNIQUES+1) );
97
98 return SdkSample::keyPressed(evt);
99 }
100
101 //------------------------------------------------------------------------------
setupContent()102 void Sample_NewInstancing::setupContent()
103 {
104 //Initialize the techniques and current mesh variables
105 mInstancingTechnique = 0;
106 mCurrentMesh = 0;
107 mCurrentManager = 0;
108
109 checkHardwareSupport();
110
111 mSceneMgr->setShadowTechnique( SHADOWTYPE_TEXTURE_ADDITIVE_INTEGRATED );
112 mSceneMgr->setShadowTextureSelfShadow( true );
113 mSceneMgr->setShadowCasterRenderBackFaces( true );
114
115 if (Ogre::Root::getSingletonPtr()->getRenderSystem()->getName().find("OpenGL ES 2") == String::npos)
116 {
117 mSceneMgr->setShadowTextureConfig( 0, 2048, 2048, PF_X8R8G8B8 ); // PF_FLOAT32_R currently broken on all GL RS
118 }
119 else
120 {
121 // Use a smaller texture for GL ES 3.0
122 mSceneMgr->setShadowTextureConfig( 0, 512, 512, PF_X8R8G8B8 );
123 }
124
125 //LiSPSMShadowCameraSetup *shadowCameraSetup = new LiSPSMShadowCameraSetup();
126 //PlaneOptimalShadowCameraSetup *shadowCameraSetup = new PlaneOptimalShadowCameraSetup();
127
128 mSceneMgr->setShadowCameraSetup( FocusedShadowCameraSetup::create() );
129
130 mEntities.reserve( NUM_INST_ROW * NUM_INST_COLUMN );
131 mSceneNodes.reserve( NUM_INST_ROW * NUM_INST_COLUMN );
132
133 mSceneMgr->setSkyBox(true, "Examples/CloudyNoonSkyBox");
134
135 // create a mesh for our ground
136 MeshManager::getSingleton().createPlane("ground", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
137 Plane(Vector3::UNIT_Y, 0), 10000, 10000, 20, 20, true, 1, 6, 6, Vector3::UNIT_Z);
138
139 // create a ground entity from our mesh and attach it to the origin
140 Entity* ground = mSceneMgr->createEntity("Ground", "ground");
141 ground->setMaterialName("Examples/Instancing/Misc/Grass");
142 ground->setCastShadows(false);
143 mSceneMgr->getRootSceneNode()->attachObject(ground);
144
145 setupLighting();
146
147 // set initial camera position and speed
148 mCameraNode->setPosition( 0, 120, 100 );
149
150 setupGUI();
151
152 #if OGRE_PLATFORM != OGRE_PLATFORM_APPLE_IOS
153 setDragLook(true);
154 #endif
155
156 switchInstancingTechnique();
157 }
158 //------------------------------------------------------------------------------
setupLighting()159 void Sample_NewInstancing::setupLighting()
160 {
161 mSceneMgr->setAmbientLight( ColourValue( 0.40f, 0.40f, 0.40f ) );
162
163 ColourValue lightColour( 1, 0.5, 0.3 );
164
165 //Create main (point) light
166 Light* light = mSceneMgr->createLight();
167 light->setDiffuseColour(lightColour);
168 mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3( 0.0f, 25.0f, 0.0f ))->attachObject(light);
169 light->setSpecularColour( 0.6, 0.82, 1.0 );
170 light->setAttenuation( 3500, 0.085, 0.00008, 0.00006 );
171 light->setCastShadows( false );
172
173 //Create a dummy spot light for shadows
174 light = mSceneMgr->createLight();
175 light->setType( Light::LT_SPOTLIGHT );
176 light->setDiffuseColour( ColourValue( 0.15f, 0.35f, 0.44f ) );
177 SceneNode* ln = mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3( 250.0f, 200.0f, 250.0f ));
178 ln->attachObject(light);
179 ln->setDirection(-Vector3::UNIT_SCALE );
180 light->setSpecularColour( 0.2, 0.12, 0.11 );
181 light->setAttenuation( 3500, 0.005, 0.00002, 0.00001 );
182 light->setSpotlightRange( Degree(80), Degree(90) );
183 light->setCastShadows( true );
184 light->setLightMask( 0x00000000 );
185 }
186
187 //------------------------------------------------------------------------------
switchInstancingTechnique()188 void Sample_NewInstancing::switchInstancingTechnique()
189 {
190 randGenerator.randomize();
191 //mInstancingTechnique = (mInstancingTechnique+1) % (NUM_TECHNIQUES+1);
192 mInstancingTechnique = mTechniqueMenu->getSelectionIndex();
193
194 if( mCurrentManager )
195 mSceneMgr->destroyInstanceManager(mCurrentManager);
196
197 if( !mSupportedTechniques[mInstancingTechnique] )
198 {
199 //Hide GUI features available only to instancing
200 mCurrentManager = 0;
201 mDefragmentBatches->hide();
202 mDefragmentOptimumCull->hide();
203 return;
204 }
205
206 if( mInstancingTechnique < NUM_TECHNIQUES )
207 {
208 //Instancing
209
210 //Create the manager if we haven't already (i.e. first time)
211 //Because we use IM_USEALL as flags, the actual num of instances per batch might be much lower
212 //If you're not bandwidth limited, you may want to lift IM_VTFBESTFIT flag away
213
214 InstanceManager::InstancingTechnique technique = InstanceManager::ShaderBased;
215
216 switch( mInstancingTechnique )
217 {
218 case 0: technique = InstanceManager::ShaderBased; break;
219 case 1: technique = InstanceManager::TextureVTF; break;
220 case 2: technique = InstanceManager::HWInstancingBasic; break;
221 case 3:
222 case 4: technique = InstanceManager::HWInstancingVTF; break;
223 }
224
225 uint16 flags = IM_USEALL;
226 flags |= mCurrentFlags;
227
228 if (mInstancingTechnique == 4)
229 {
230 flags |= IM_VTFBONEMATRIXLOOKUP;
231 }
232 //Only one weight is recommended for the VTF technique, but force the use of more for the demo
233 if(mInstancingTechnique == 1 && (flags & IM_USEBONEDUALQUATERNIONS))
234 {
235 flags &= ~IM_USEONEWEIGHT;
236 }
237
238 mCurrentManager = mSceneMgr->createInstanceManager(
239 "InstanceMgr" + StringConverter::toString(mInstancingTechnique), c_meshNames[mCurrentMesh],
240 ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, technique,
241 NUM_INST_ROW * NUM_INST_COLUMN, flags);
242
243 createInstancedEntities();
244
245 //Show GUI features available only to instancing
246 mDefragmentBatches->show();
247 mDefragmentOptimumCull->show();
248 }
249 else
250 {
251 //Non-instancing
252 createEntities();
253
254 //Hide GUI features available only to instancing
255 mCurrentManager = 0;
256 mDefragmentBatches->hide();
257 mDefragmentOptimumCull->hide();
258 }
259
260 createSceneNodes();
261
262 //Show/hide "static" button, and restore config. Do this _after_ createSceneNodes()
263 if( mInstancingTechnique == InstanceManager::HWInstancingBasic ||
264 mInstancingTechnique == InstanceManager::HWInstancingVTF ||
265 mInstancingTechnique == InstanceManager::HWInstancingVTF + 1) // instancing with lookup
266 {
267 if( mSetStatic->isChecked() )
268 mCurrentManager->setBatchesAsStaticAndUpdate( mSetStatic->isChecked() );
269 mSetStatic->show();
270 }
271 else
272 mSetStatic->hide();
273 if( mInstancingTechnique < NUM_TECHNIQUES)
274 {
275 mUseSceneNodes->show();
276 }
277 else
278 {
279 mUseSceneNodes->hide();
280 }
281 }
282
283 //------------------------------------------------------------------------------
switchSkinningTechnique(int index)284 void Sample_NewInstancing::switchSkinningTechnique(int index)
285 {
286 switch(index)
287 {
288 default:
289 //Linear Skinning
290 case 0:
291 mCurrentMesh = 0;
292 mCurrentMaterialSet = c_materialsTechniques;
293 mCurrentFlags = 0;
294 break;
295 //Dual Quaternion Skinning
296 case 1:
297 mCurrentMesh = 0;
298 mCurrentMaterialSet = c_materialsTechniques_dq;
299 mCurrentFlags = IM_USEBONEDUALQUATERNIONS;
300 break;
301 //Dual Quaternion Skinning with two weights
302 case 2:
303 mCurrentMesh = 1;
304 mCurrentMaterialSet = c_materialsTechniques_dq_two_weights;
305 mCurrentFlags = IM_USEBONEDUALQUATERNIONS;
306 break;
307 };
308 }
309
310 //------------------------------------------------------------------------------
createEntities()311 void Sample_NewInstancing::createEntities()
312 {
313 for( int i=0; i<NUM_INST_ROW * NUM_INST_COLUMN; ++i )
314 {
315 //Create the non-instanced entity. Use the same shader as shader-based because:
316 //a. To prove we can (runs without modification! :-) )
317 //b. Make a fair comparison
318 Entity *ent = mSceneMgr->createEntity( c_meshNames[mCurrentMesh] );
319 ent->setMaterialName( mCurrentMaterialSet[NUM_TECHNIQUES] );
320 mEntities.push_back( ent );
321
322 //Get the animation
323 AnimationState *anim = ent->getAnimationState( "Walk" );
324 if (mAnimations.insert( anim ).second)
325 {
326 anim->setEnabled( true );
327 anim->addTime( randGenerator.nextFloat() * 10 ); //Random start offset
328 }
329 }
330 }
331 //------------------------------------------------------------------------------
createInstancedEntities()332 void Sample_NewInstancing::createInstancedEntities()
333 {
334
335 for( int i=0; i<NUM_INST_ROW; ++i )
336 {
337 for( int j=0; j<NUM_INST_COLUMN; ++j )
338 {
339 //Create the instanced entity
340 InstancedEntity *ent = mCurrentManager->createInstancedEntity(mCurrentMaterialSet[mInstancingTechnique] );
341 mEntities.push_back( ent );
342
343 //HWInstancingBasic is the only technique without animation support
344 if( mInstancingTechnique != InstanceManager::HWInstancingBasic)
345 {
346 //Get the animation
347 AnimationState *anim = ent->getAnimationState( "Walk" );
348 anim->setEnabled( true );
349 anim->addTime( randGenerator.nextFloat() * 10); //Random start offset
350 mAnimations.insert( anim );
351 }
352
353 if ((mInstancingTechnique < NUM_TECHNIQUES) && (!mUseSceneNodes->isChecked()))
354 {
355 mMovedInstances.push_back( ent );
356 ent->setOrientation(Quaternion(Radian(randGenerator.nextFloat() * 10 * 3.14159265359f), Vector3::UNIT_Y));
357 ent->setPosition( Ogre::Vector3(mEntities[0]->getBoundingRadius() * (i - NUM_INST_ROW * 0.5f), 0,
358 mEntities[0]->getBoundingRadius() * (j - NUM_INST_COLUMN * 0.5f)) );
359 }
360 }
361 }
362 }
363 //------------------------------------------------------------------------------
createSceneNodes()364 void Sample_NewInstancing::createSceneNodes()
365 {
366 //Here the SceneNodes are created. Since InstancedEntities derive from MovableObject,
367 //they behave like regular Entities on this.
368 SceneNode *rootNode = mSceneMgr->getRootSceneNode();
369
370 for( int i=0; i<NUM_INST_ROW; ++i )
371 {
372 for( int j=0; j<NUM_INST_COLUMN; ++j )
373 {
374 int idx = i * NUM_INST_COLUMN + j;
375 if ((mInstancingTechnique >= NUM_TECHNIQUES) || (mUseSceneNodes->isChecked()))
376 {
377 SceneNode *sceneNode = rootNode->createChildSceneNode();
378 sceneNode->attachObject( mEntities[idx] );
379 sceneNode->yaw( Radian( randGenerator.nextFloat() * 10 * 3.14159265359f )); //Random orientation
380 sceneNode->setPosition( mEntities[idx]->getBoundingRadius() * (i - NUM_INST_ROW * 0.5f), 0,
381 mEntities[idx]->getBoundingRadius() * (j - NUM_INST_COLUMN * 0.5f) );
382 mSceneNodes.push_back( sceneNode );
383 }
384
385 }
386 }
387 }
388 //------------------------------------------------------------------------------
clearScene()389 void Sample_NewInstancing::clearScene()
390 {
391 std::vector<MovableObject*>::const_iterator itor = mEntities.begin();
392 std::vector<MovableObject*>::const_iterator end = mEntities.end();
393
394 //Note: Destroying the instance manager automatically destroys all instanced entities
395 //created by this manager (beware of not leaving reference to those pointers)
396 while( itor != end )
397 {
398 SceneNode *sceneNode = (*itor)->getParentSceneNode();
399 if (sceneNode)
400 {
401 sceneNode->detachAllObjects();
402 sceneNode->getParentSceneNode()->removeAndDestroyChild( sceneNode );
403 }
404 if( mInstancingTechnique == NUM_TECHNIQUES )
405 mSceneMgr->destroyEntity( (*itor)->getName() );
406 else
407 mSceneMgr->destroyInstancedEntity( static_cast<InstancedEntity*>(*itor) );
408
409 ++itor;
410 }
411
412 //Free some memory, but don't destroy the manager so when we switch this technique
413 //back again it doesn't take too long
414 if( mCurrentManager )
415 mCurrentManager->cleanupEmptyBatches();
416
417 mEntities.clear();
418 mMovedInstances.clear();
419 mSceneNodes.clear();
420 mAnimations.clear();
421 }
422 //-----------------------------------------------------------------------------------
destroyManagers()423 void Sample_NewInstancing::destroyManagers()
424 {
425 mSceneMgr->destroyInstanceManager(mCurrentManager);;
426 }
427
428 //------------------------------------------------------------------------------
cleanupContent()429 void Sample_NewInstancing::cleanupContent()
430 {
431 MeshManager::getSingleton().remove("ground", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
432 clearScene();
433 destroyManagers();
434 }
435
436 //------------------------------------------------------------------------------
animateUnits(float timeSinceLast)437 void Sample_NewInstancing::animateUnits( float timeSinceLast )
438 {
439 //Iterates through all AnimationSets and updates the animation being played. Demonstrates the
440 //animation is unique and independent to each instance
441 std::set<AnimationState*>::const_iterator itor = mAnimations.begin();
442 std::set<AnimationState*>::const_iterator end = mAnimations.end();
443
444 while( itor != end )
445 {
446 (*itor)->addTime( timeSinceLast );
447 ++itor;
448 }
449 }
450
451 //------------------------------------------------------------------------------
moveUnits(float timeSinceLast)452 void Sample_NewInstancing::moveUnits( float timeSinceLast )
453 {
454 Real fMovSpeed = 1.0f;
455
456 if( !mEntities.empty() )
457 fMovSpeed = mEntities[0]->getBoundingRadius() * 0.30f;
458
459 if (!mSceneNodes.empty())
460 {
461 //Randomly move the units along their normal, bouncing around invisible walls
462 std::vector<SceneNode*>::const_iterator itor = mSceneNodes.begin();
463 std::vector<SceneNode*>::const_iterator end = mSceneNodes.end();
464
465 while( itor != end )
466 {
467 //Calculate bounces
468 Vector3 entityPos = (*itor)->getPosition();
469 Vector3 planeNormal = Vector3::ZERO;
470 if( (*itor)->getPosition().x < -5000.0f )
471 {
472 planeNormal = Vector3::UNIT_X;
473 entityPos.x = -4999.0f;
474 }
475 else if( (*itor)->getPosition().x > 5000.0f )
476 {
477 planeNormal = Vector3::NEGATIVE_UNIT_X;
478 entityPos.x = 4999.0f;
479 }
480 else if( (*itor)->getPosition().z < -5000.0f )
481 {
482 planeNormal = Vector3::UNIT_Z;
483 entityPos.z = -4999.0f;
484 }
485 else if( (*itor)->getPosition().z > 5000.0f )
486 {
487 planeNormal = Vector3::NEGATIVE_UNIT_Z;
488 entityPos.z = 4999.0f;
489 }
490
491 if( planeNormal != Vector3::ZERO )
492 {
493 const Vector3 vDir( (*itor)->getOrientation().xAxis().normalisedCopy() );
494 (*itor)->setOrientation( lookAt( planeNormal.reflect( vDir ).normalisedCopy() ) );
495 (*itor)->setPosition( entityPos );
496 }
497
498 //Move along the direction we're looking to
499 (*itor)->translate( Vector3::UNIT_X * timeSinceLast * fMovSpeed, Node::TS_LOCAL );
500 ++itor;
501 }
502 }
503 else
504 {
505 //No scene nodes (instanced entities only)
506 //Update instanced entities directly
507
508 //Randomly move the units along their normal, bouncing around invisible walls
509 std::vector<InstancedEntity*>::const_iterator itor = mMovedInstances.begin();
510 std::vector<InstancedEntity*>::const_iterator end = mMovedInstances.end();
511
512 while( itor != end )
513 {
514 //Calculate bounces
515 InstancedEntity* pEnt = *itor;
516 Vector3 entityPos = pEnt->getPosition();
517 Vector3 planeNormal = Vector3::ZERO;
518 if( pEnt->getPosition().x < -5000.0f )
519 {
520 planeNormal = Vector3::UNIT_X;
521 entityPos.x = -4999.0f;
522 }
523 else if( pEnt->getPosition().x > 5000.0f )
524 {
525 planeNormal = Vector3::NEGATIVE_UNIT_X;
526 entityPos.x = 4999.0f;
527 }
528 else if( pEnt->getPosition().z < -5000.0f )
529 {
530 planeNormal = Vector3::UNIT_Z;
531 entityPos.z = -4999.0f;
532 }
533 else if( pEnt->getPosition().z > 5000.0f )
534 {
535 planeNormal = Vector3::NEGATIVE_UNIT_Z;
536 entityPos.z = 4999.0f;
537 }
538
539 if( planeNormal != Vector3::ZERO )
540 {
541 const Vector3 vDir(pEnt->getOrientation().xAxis().normalisedCopy() );
542 pEnt->setOrientation( lookAt( planeNormal.reflect( vDir ).normalisedCopy() ), false );
543 pEnt->setPosition( entityPos, false);
544 }
545
546 //Move along the direction we're looking to
547 Vector3 transAmount = Vector3::UNIT_X * timeSinceLast * fMovSpeed;
548 pEnt->setPosition( pEnt->getPosition() + pEnt->getOrientation() * transAmount );
549 ++itor;
550 }
551 }
552 }
553
554 //------------------------------------------------------------------------------
lookAt(const Vector3 & normDir)555 Quaternion Sample_NewInstancing::lookAt( const Vector3 &normDir )
556 {
557 Quaternion retVal;
558 Vector3 xVec = Vector3::UNIT_Y.crossProduct( normDir );
559 xVec.normalise();
560
561 Vector3 yVec = normDir.crossProduct( xVec );
562 yVec.normalise();
563
564 retVal.FromAxes( xVec, yVec, normDir );
565
566 return retVal;
567 }
568
569 //------------------------------------------------------------------------------
defragmentBatches()570 void Sample_NewInstancing::defragmentBatches()
571 {
572 //Defragment batches is used after many InstancedEntities were removed (and you won't
573 //be requesting more). However, then the optimize cull option is on, it can cause
574 //quite a perf. boost on large batches (i.e. VTF) even if not a single instance was ever removed.
575 if( mCurrentManager )
576 mCurrentManager->defragmentBatches( mDefragmentOptimumCull->isChecked() );
577 }
578
579 //------------------------------------------------------------------------------
setupGUI()580 void Sample_NewInstancing::setupGUI()
581 {
582 mTechniqueMenu = mTrayMgr->createLongSelectMenu(
583 TL_TOPLEFT, "TechniqueSelectMenu", "Technique", 450, 350, 5);
584 for( int i=0; i<NUM_TECHNIQUES+1; ++i )
585 {
586 String text = c_instancingTechniques[i];
587 if( !mSupportedTechniques[i] )
588 text = "Unsupported: " + text;
589 mTechniqueMenu->addItem( text );
590 }
591 //Check box to enable dual quaternion skinning
592 mSkinningTechniques = mTrayMgr->createLongSelectMenu(TL_TOPLEFT, "SkinningTechnique", "Skinning Technique", 450, 285, 5);
593 mSkinningTechniques->addItem("Linear Skinning");
594 mSkinningTechniques->addItem("Dual Quaternion Skinning");
595 mSkinningTechniques->addItem("Dual Quaternion Skinning (2 wgts)");
596
597 //Check box to move the units
598 mMoveInstances = mTrayMgr->createCheckBox(TL_TOPRIGHT, "MoveInstances", "Move Instances", 175);
599 mMoveInstances->setChecked(false);
600
601 //Check box to animate the units
602 mAnimateInstances = mTrayMgr->createCheckBox(TL_TOPRIGHT, "AnimateInstances",
603 "Animate Instances", 175);
604 mAnimateInstances->setChecked(false);
605
606 //Checkbox to toggle shadows
607 mEnableShadows = mTrayMgr->createCheckBox(TL_TOPRIGHT, "EnableShadows",
608 "Enable Shadows", 175);
609 mEnableShadows->setChecked(true);
610
611 //Check box to make instances static (where supported)
612 mSetStatic = mTrayMgr->createCheckBox(TL_TOPRIGHT, "SetStatic", "Set Static", 175);
613 mSetStatic->setChecked(false);
614
615 //Checkbox to toggle use of scene nodes
616 mUseSceneNodes = mTrayMgr->createCheckBox(TL_TOPRIGHT, "UseSceneNodes",
617 "Use Scene Nodes", 175);
618 mUseSceneNodes->setChecked(true, false);
619
620 //Controls to control batch defragmentation on the fly
621 mDefragmentBatches = mTrayMgr->createButton(TL_RIGHT, "DefragmentBatches",
622 "Defragment Batches", 175);
623 mDefragmentOptimumCull = mTrayMgr->createCheckBox(TL_RIGHT, "DefragmentOptimumCull",
624 "Optimum Cull", 175);
625 mDefragmentOptimumCull->setChecked(true);
626
627 //Slider to control max number of instances
628 mInstancesSlider = mTrayMgr->createThickSlider( TL_TOPLEFT, "InstancesSlider", "Instances (NxN)",
629 300, 50, 4, 100, 97 );
630 mInstancesSlider->setValue( NUM_INST_ROW );
631
632 mTrayMgr->showCursor();
633 }
634
635 //------------------------------------------------------------------------------
itemSelected(SelectMenu * menu)636 void Sample_NewInstancing::itemSelected( SelectMenu* menu )
637 {
638 if (menu == mTechniqueMenu)
639 {
640 clearScene();
641 switchInstancingTechnique();
642 }
643 else if(menu == mSkinningTechniques)
644 {
645 clearScene();
646 switchSkinningTechnique(menu->getSelectionIndex());
647 switchInstancingTechnique();
648 }
649 }
650
651 //------------------------------------------------------------------------------
buttonHit(OgreBites::Button * button)652 void Sample_NewInstancing::buttonHit( OgreBites::Button* button )
653 {
654 if( button == mDefragmentBatches ) defragmentBatches();
655 }
656
657 //------------------------------------------------------------------------------
checkBoxToggled(CheckBox * box)658 void Sample_NewInstancing::checkBoxToggled( CheckBox* box )
659 {
660 if( box == mEnableShadows )
661 {
662 mSceneMgr->setShadowTechnique( mEnableShadows->isChecked() ?
663 SHADOWTYPE_TEXTURE_ADDITIVE_INTEGRATED : SHADOWTYPE_NONE );
664 }
665 else if( box == mSetStatic && mCurrentManager )
666 {
667 mCurrentManager->setBatchesAsStaticAndUpdate( mSetStatic->isChecked() );
668 }
669 else if (box == mUseSceneNodes)
670 {
671 clearScene();
672 switchInstancingTechnique();
673 }
674 }
675
676 //------------------------------------------------------------------------------
sliderMoved(Slider * slider)677 void Sample_NewInstancing::sliderMoved( Slider* slider )
678 {
679 if( slider == mInstancesSlider ) NUM_INST_ROW = static_cast<int>(mInstancesSlider->getValue());
680 NUM_INST_COLUMN = static_cast<int>(mInstancesSlider->getValue());
681 }
682
683 //------------------------------------------------------------------------------
testCapabilities(const RenderSystemCapabilities * caps)684 void Sample_NewInstancing::testCapabilities( const RenderSystemCapabilities* caps )
685 {
686 if (!caps->hasCapability(RSC_VERTEX_PROGRAM) || !caps->hasCapability(RSC_FRAGMENT_PROGRAM))
687 {
688 OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your graphics card does not support vertex and "
689 "fragment programs, so you cannot run this sample. Sorry!",
690 "NewInstancing::testCapabilities");
691 }
692
693 if (!GpuProgramManager::getSingleton().isSyntaxSupported("glsl") &&
694 !GpuProgramManager::getSingleton().isSyntaxSupported("glsl300es") &&
695 !GpuProgramManager::getSingleton().isSyntaxSupported("fp40") &&
696 !GpuProgramManager::getSingleton().isSyntaxSupported("ps_2_0") &&
697 !GpuProgramManager::getSingleton().isSyntaxSupported("ps_3_0") &&
698 !GpuProgramManager::getSingleton().isSyntaxSupported("ps_4_0") &&
699 !GpuProgramManager::getSingleton().isSyntaxSupported("ps_4_1") &&
700 !GpuProgramManager::getSingleton().isSyntaxSupported("ps_5_0"))
701 {
702 OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your card does not support the shader model needed for this sample, "
703 "so you cannot run this sample. Sorry!", "NewInstancing::testCapabilities");
704 }
705 }
706 //------------------------------------------------------------------------------
707
checkHardwareSupport()708 void Sample_NewInstancing::checkHardwareSupport()
709 {
710 //Check Technique support
711 for( int i=0; i<NUM_TECHNIQUES; ++i )
712 {
713 InstanceManager::InstancingTechnique technique;
714 switch( i )
715 {
716 case 0: technique = InstanceManager::ShaderBased; break;
717 case 1: technique = InstanceManager::TextureVTF; break;
718 case 2: technique = InstanceManager::HWInstancingBasic; break;
719 case 3:
720 case 4: technique = InstanceManager::HWInstancingVTF; break;
721 }
722
723 uint16 flags = IM_USEALL;
724 if (i == 4)
725 {
726 flags |= IM_VTFBONEMATRIXLOOKUP;
727 }
728
729 const size_t numInstances = mSceneMgr->getNumInstancesPerBatch( c_meshNames[mCurrentMesh],
730 ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME,
731 mCurrentMaterialSet[i], technique, NUM_INST_ROW * NUM_INST_COLUMN, flags );
732
733 mSupportedTechniques[i] = numInstances > 0;
734 }
735
736 //Non instancing is always supported
737 mSupportedTechniques[NUM_TECHNIQUES] = true;
738 }
739