text: #ifndef __Dot3Bump_H__
  #define __Dot3Bump_H__
  
  #include "SdkSample.h"
  #include "OgreBillboard.h"
  
  using namespace Ogre;
  using namespace OgreBites;
  
  class _OgreSampleClassExport Sample_Dot3Bump : public SdkSample
  {
  public:
  
      Sample_Dot3Bump()
          : mMoveLights (true)
      {
          mInfo["Title"] = "Bump Mapping";
          mInfo["Description"] = "Shows how to use the dot product blending operation and normalization cube map "
              "to achieve a bump mapping effect. Tangent space computations made through the guide of the tutorial "
              "on bump mapping from http://users.ox.ac.uk/~univ1234 by paul.baker@univ.ox.ac.uk.";
          mInfo["Thumbnail"] = "thumb_bump.png";
          mInfo["Category"] = "Lighting";
          mInfo["Help"] = "Left click and drag anywhere in the scene to look around. Let go again to show "
              "cursor and access widgets. Use WASD keys to move.";
      }
  
      StringVector getRequiredPlugins()
      {
          StringVector names;
          if(!GpuProgramManager::getSingleton().isSyntaxSupported("glsles")
  		&& !GpuProgramManager::getSingleton().isSyntaxSupported("glsl")
  		&& !GpuProgramManager::getSingleton().isSyntaxSupported("hlsl"))
  			names.push_back("Cg Program Manager");
          return names;
      }
  
      void testCapabilities(const RenderSystemCapabilities* caps)
      {
          if (!caps->hasCapability(RSC_VERTEX_PROGRAM) || !(caps->hasCapability(RSC_FRAGMENT_PROGRAM)))
          {
              OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your graphics card does not support vertex and fragment programs, "
                  "so you cannot run this sample. Sorry!", "Dot3BumpSample::testCapabilities");
          }
  
          if (!GpuProgramManager::getSingleton().isSyntaxSupported("arbfp1") &&
              !GpuProgramManager::getSingleton().isSyntaxSupported("ps_2_0") &&
              !GpuProgramManager::getSingleton().isSyntaxSupported("ps_4_0") &&
              !GpuProgramManager::getSingleton().isSyntaxSupported("glsl")   &&
              !GpuProgramManager::getSingleton().isSyntaxSupported("glsles"))
          {
              OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your card does not support the shader model needed for this sample, "
                  "so you cannot run this sample. Sorry!", "Dot3BumpSample::testCapabilities");
          }
      }
  
      bool frameRenderingQueued(const FrameEvent& evt)
      {
          if (mMoveLights)
          {
              // rotate the light pivots
              mLightPivot1->roll(Degree(evt.timeSinceLastFrame * 30));
              mLightPivot2->roll(Degree(evt.timeSinceLastFrame * 10));
          }
  
          return SdkSample::frameRenderingQueued(evt);  // don't forget the parent class updates!
      }
  
      void itemSelected(SelectMenu* menu)
      {
          if (menu == mMeshMenu)
          {
              // change to the selected entity
              mObjectNode->detachAllObjects();
              mObjectNode->attachObject(mSceneMgr->getEntity(mMeshMenu->getSelectedItem()));
  
              // remember which material is currently selected
              int index = std::max<int>(0, mMaterialMenu->getSelectionIndex());
  
              // update the material menu's options
              mMaterialMenu->setItems(mPossibilities[mMeshMenu->getSelectedItem()]);
  
              mMaterialMenu->selectItem(index);   // select the material with the saved index
          }
          else
          {
              // set the selected material for the active mesh
              ((Entity*)mObjectNode->getAttachedObject(0))->setMaterialName(menu->getSelectedItem());
          }
      }
  
      void checkBoxToggled(CheckBox* box)
      {
          if (StringUtil::startsWith(box->getName(), "Light", false))
          {
              // get the light pivot that corresponds to this checkbox
              SceneNode* pivot = box->getName() == "Light1" ? mLightPivot1 : mLightPivot2;
              SceneNode::ObjectIterator it = pivot->getAttachedObjectIterator();
  
              while (it.hasMoreElements())  // toggle visibility of light and billboard set
              {
                  MovableObject* o = it.getNext();
                  o->setVisible(box->isChecked());
              }
  
          }
          else if (box->getName() == "MoveLights")
          {
              mMoveLights = !mMoveLights;
          }
      }
  
  protected:
  
      void setupContent()
      {
          // create our main node to attach our entities to
          mObjectNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
  
          setupModels();
          setupLights();
          setupControls();
  
          mCameraNode->setPosition(0, 0, 500);
  
  #if OGRE_PLATFORM != OGRE_PLATFORM_APPLE_IOS
          setDragLook(true);
  #endif
      }
  
  
  
      void loadResources()
      {       
  #ifdef INCLUDE_RTSHADER_SYSTEM
          Ogre::StringVector groupVector = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
          Ogre::StringVector::iterator itGroup = groupVector.begin();
          Ogre::StringVector::iterator itGroupEnd = groupVector.end();
          Ogre::String shaderCoreLibsPath;
  
  
          for (; itGroup != itGroupEnd; ++itGroup)
          {
              Ogre::ResourceGroupManager::LocationList resLocationsList = Ogre::ResourceGroupManager::getSingleton().getResourceLocationList(*itGroup);
              Ogre::ResourceGroupManager::LocationList::iterator it = resLocationsList.begin();
              Ogre::ResourceGroupManager::LocationList::iterator itEnd = resLocationsList.end();
              bool coreLibsFound = false;
  
              // Find the location of the core shader libs
              for (; it != itEnd; ++it)
              {
                  if ((*it)->archive->getName().find("RTShaderLib") != Ogre::String::npos)
                  {
                      shaderCoreLibsPath = (*it)->archive->getName() + "/";   
                      coreLibsFound = true;
                      break;
                  }
              }
  
              // Core libs path found in the current group.
              if (coreLibsFound) 
                  break; 
          }
              
  #endif
      }
  
      void unloadResources()
      {
  
      }
  
      void setupModels()
      {
          StringVector matNames;
  
          matNames.push_back("Examples/BumpMapping/MultiLight");
          matNames.push_back("Examples/BumpMapping/MultiLightSpecular");
          matNames.push_back("Examples/OffsetMapping/Specular");
          matNames.push_back("Examples/ShowUV");
          matNames.push_back("Examples/ShowNormals");
          matNames.push_back("Examples/ShowTangents");
  
  #ifdef INCLUDE_RTSHADER_SYSTEM
          matNames.push_back("RTSS/NormalMapping_SinglePass");
          matNames.push_back("RTSS/NormalMapping_MultiPass");
  #endif
  
      
          mPossibilities["ogrehead.mesh"] = matNames;
          mPossibilities["knot.mesh"] = matNames;
  
          matNames.clear();
          matNames.push_back("Examples/Athene/NormalMapped");
          matNames.push_back("Examples/Athene/NormalMappedSpecular");
          matNames.push_back("Examples/Athene/NormalMappedSpecular");
          matNames.push_back("Examples/ShowUV");
          matNames.push_back("Examples/ShowNormals");
          matNames.push_back("Examples/ShowTangents");
  #ifdef INCLUDE_RTSHADER_SYSTEM
          matNames.push_back("RTSS/Athene/NormalMapping_SinglePass");
          matNames.push_back("RTSS/Athene/NormalMapping_MultiPass");
  #endif
  
          mPossibilities["athene.mesh"] = matNames;
  
          for (std::map<String, StringVector>::iterator it = mPossibilities.begin(); it != mPossibilities.end(); it++)
          {
              // load each mesh with non-default hardware buffer usage options
              MeshPtr mesh = MeshManager::getSingleton().load(it->first, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
                  HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY);
  
              // build tangent vectors for our mesh
              unsigned short src, dest;
              if (!mesh->suggestTangentVectorBuildParams(VES_TANGENT, src, dest))
              {
                  mesh->buildTangentVectors(VES_TANGENT, src, dest);
                  // this version cleans mirrored and rotated UVs but requires quality models
                  // mesh->buildTangentVectors(VES_TANGENT, src, dest, true, true);
              }
  
              // create an entity from the mesh and set the first available material
              Entity* ent = mSceneMgr->createEntity(mesh->getName(), mesh->getName());
              ent->setMaterialName(it->second.front());
          }
      }
  
      void setupLights()
      {
          mSceneMgr->setAmbientLight(ColourValue::Black);   // disable ambient lighting
  
          // create pivot nodes
          mLightPivot1 = mSceneMgr->getRootSceneNode()->createChildSceneNode();
          mLightPivot2 = mSceneMgr->getRootSceneNode()->createChildSceneNode();
  
          Light* l;
          BillboardSet* bbs;
  
          // create white light
          l = mSceneMgr->createLight();
          l->setPosition(200, 0, 0);
          l->setDiffuseColour(1, 1, 1);
          l->setSpecularColour(1, 1, 1);
          // create white flare
          bbs = mSceneMgr->createBillboardSet();
          bbs->setMaterialName("Examples/Flare");
          bbs->createBillboard(200, 0, 0)->setColour(ColourValue::White);
  
          mLightPivot1->attachObject(l);
          mLightPivot1->attachObject(bbs);
  
          // create red light
          l = mSceneMgr->createLight();
          l->setPosition(40, 200, 50);
          l->setDiffuseColour(1, 0, 0);
          l->setSpecularColour(1, 0.8, 0.8);
          // create white flare
          bbs = mSceneMgr->createBillboardSet();
          bbs->setMaterialName("Examples/Flare");
          bbs->createBillboard(50, 200, 50)->setColour(ColourValue::Red);
  
          mLightPivot2->attachObject(l);
          mLightPivot2->attachObject(bbs);
      }
  
      void setupControls()
      {
          mTrayMgr->showCursor();
  
          // make room for the controls
          mTrayMgr->showLogo(TL_TOPRIGHT);
          mTrayMgr->showFrameStats(TL_TOPRIGHT);
          mTrayMgr->toggleAdvancedFrameStats();
  
          // create a menu to choose the model displayed
          mMeshMenu = mTrayMgr->createLongSelectMenu(TL_BOTTOM, "Mesh", "Mesh", 370, 290, 10);
          for (std::map<String, StringVector>::iterator it = mPossibilities.begin(); it != mPossibilities.end(); it++)
              mMeshMenu->addItem(it->first);
  
          // create a menu to choose the material used by the model
          mMaterialMenu = mTrayMgr->createLongSelectMenu(TL_BOTTOM, "Material", "Material", 370, 290, 10);
  
          // create checkboxes to toggle lights
          mTrayMgr->createCheckBox(TL_TOPLEFT, "Light1", "Light A")->setChecked(true, false);
          mTrayMgr->createCheckBox(TL_TOPLEFT, "Light2", "Light B")->setChecked(true, false);
          mTrayMgr->createCheckBox(TL_TOPLEFT, "MoveLights", "Move Lights")->setChecked(true, false);
  
          // a friendly reminder
          StringVector names;
          names.push_back("Help");
          mTrayMgr->createParamsPanel(TL_TOPLEFT, "Help", 100, names)->setParamValue(0, "H/F1");
  
          mMeshMenu->selectItem(0);  // select first mesh
      }
  
      void cleanupContent()
      {
          // clean up properly to avoid interfering with subsequent samples
          for (std::map<String, StringVector>::iterator it = mPossibilities.begin(); it != mPossibilities.end(); it++)
              MeshManager::getSingleton().unload(it->first, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
          mPossibilities.clear();
      }
  
      std::map<String, StringVector> mPossibilities;
      SceneNode* mObjectNode;
      SceneNode* mLightPivot1;
      SceneNode* mLightPivot2;
      bool mMoveLights;
      SelectMenu* mMeshMenu;
      SelectMenu* mMaterialMenu;
  };
  
  #endif
  