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