text: /**
  *   Modified by: Juan Camilo Acosta Arango (ja0335 )
  *   Date: 09-04-2014
  **/
  
  #ifndef __PNTriangles_H__
  #define __PNTriangles_H__
  
  #include "SdkSample.h"
  #include "OgreImage.h"
  #include "OgreBillboard.h"
  
  using namespace Ogre;
  using namespace OgreBites;
  
  class _OgreSampleClassExport Sample_PNTriangles : public SdkSample
  {
  public:
  
      Sample_PNTriangles()
          : mMoveLights (true)
      {
          mInfo["Title"] = "PNTriangles";
          mInfo["Description"] = "Sample for parametric PN-Triangles tessellation algorithm";
          mInfo["Thumbnail"] = "thumb_tessellation.png";
          mInfo["Category"] = "Unsorted";
          mInfo["Help"] = "Top Left: Multi-frame\nTop Right: Scrolling\nBottom Left: Rotation\nBottom Right: Scaling";
      }
  
      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!", "Sample_PNTrianglesTessellation::testCapabilities");
          }
          if (!caps->hasCapability(RSC_TESSELLATION_HULL_PROGRAM) || !caps->hasCapability(RSC_TESSELLATION_DOMAIN_PROGRAM))
          {
              OGRE_EXCEPT(Exception::ERR_INVALID_STATE, "Your graphics card does not support tessellation shaders. Sorry!",
                  "Sample_PNTrianglesTessellation:testCapabilities");
          }
          if (!GpuProgramManager::getSingleton().isSyntaxSupported("vs_5_0") &&
              !GpuProgramManager::getSingleton().isSyntaxSupported("hs_5_0") &&
              !GpuProgramManager::getSingleton().isSyntaxSupported("ds_5_0") &&
              !GpuProgramManager::getSingleton().isSyntaxSupported("ps_5_0") &&
              !GpuProgramManager::getSingleton().isSyntaxSupported("hlsl"))
          {
              OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your card does not support the shader model 5.0 needed for this sample, "
                  "so you cannot run this sample. Sorry!", "Sample_PNTrianglesTessellation::testCapabilities");
          }
      }
  
      bool frameRenderingQueued(const FrameEvent& evt)
      {
          if (mMoveLights)
          {
              // rotate the light pivots
              mLightPivot1->roll(Degree(evt.timeSinceLastFrame * 10));
              mLightPivot2->roll(Degree(evt.timeSinceLastFrame * 15));
          }
  
          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());
  
              if( menu->getSelectionIndex() == 2 )
                  mTessellationAmount->show();
              else
                  mTessellationAmount->hide();
          }
      }
  
      void checkBoxToggled(CheckBox* box)
      {
          if (box->getName() == "Wire")
          {
              if( mCamera->getPolygonMode() == PM_WIREFRAME )
                  mCamera->setPolygonMode(PM_SOLID);
              else
                  mCamera->setPolygonMode(PM_WIREFRAME);
          }
          else 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;
          }
      }
  
      void sliderMoved(Slider* slider)
      {
          if( slider->getName() == "tessellationAmount" )
          {
              MaterialPtr lMaterialPtr = MaterialManager::getSingleton().getByName( mMaterialMenu->getSelectedItem() );
              lMaterialPtr->getTechnique(0)->getPass(0)->getTessellationHullProgramParameters()->setNamedConstant( "g_tessellationAmount", slider->getValue() );
          }
      }
  
  protected:
  
      void setupContent()
      {
          // create our main node to attach our entities to
          mObjectNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
  
          setupModels();
          setupLights();
          setupControls();
          
          // set our camera
          mCamera->setFOVy(Ogre::Degree(50.0));
          mCamera->setFOVy(Ogre::Degree(50.0));
          mCamera->setNearClipDistance(0.01f);
          mCamera->lookAt(Ogre::Vector3::ZERO);
          mCamera->setPosition(0, 0, 500);
          
  
          // Set our camera to orbit around the origin at a suitable distance
          mCameraMan->setStyle(CS_ORBIT);
          mCameraMan->setYawPitchDist(Radian(0), Radian(0), 400);
  
          mTrayMgr->showCursor();
      }
  
      void unloadResources()
      {
  
      }
  
      void setupModels()
      {
          StringVector matNames;
  
          matNames.push_back("Ogre/NoTessellation");
          matNames.push_back("Ogre/TessellationExample");
          matNames.push_back("Ogre/SimpleTessellation");
          //matNames.push_back("Ogre/AdaptiveTessellation");
          matNames.push_back("Ogre/AdaptivePNTrianglesTessellation");
          
          mPossibilities["ogrehead.mesh"] = matNames;
          mPossibilities["knot.mesh"] = matNames;
          mPossibilities["uv_sphere.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); 
          mViewport->setBackgroundColour(ColourValue(0.41f, 0.41f, 0.41f));
  
          // create pivot nodes
          mLightPivot1 = mSceneMgr->getRootSceneNode()->createChildSceneNode();
          mLightPivot2 = mSceneMgr->getRootSceneNode()->createChildSceneNode();
  
          Light* l;
          BillboardSet* bbs;
  
          // create white light
          l = mSceneMgr->createLight();
          l->setDiffuseColour(1.0f, 1.0f, 1.0f);
          l->setSpecularColour(1.0f, 1.0f, 1.0f);
          l->setDirection(Ogre::Vector3::UNIT_X*-1.0f);
          l->setPosition(200, 0, 0);
          // 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->setDiffuseColour(1.0f, 0.0f, 0.0f);
          l->setSpecularColour(1.0f, 0.0f, 0.0f);
          l->setDirection(Ogre::Vector3::UNIT_X);
          l->setPosition(-200, 0, 0);
          // create white flare
          bbs = mSceneMgr->createBillboardSet();
          bbs->setMaterialName("Examples/Flare");
          bbs->createBillboard(-200, 0, 0)->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);
          
          mTrayMgr->createCheckBox(TL_TOPLEFT, "Wire", "Wire Frame")->setChecked(false, false);
          // 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);
          
          mTessellationAmount = mTrayMgr->createThickSlider(TL_TOPLEFT, "tessellationAmount", "Tessellation Amount", 200, 40, 1, 8, 8);
          mTessellationAmount->hide();
  
          // 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);
          mPossibilities.clear();
      }
  
      std::map<String, StringVector> mPossibilities;
      SceneNode* mObjectNode;
      SceneNode* mLightPivot1;
      SceneNode* mLightPivot2;
      bool mMoveLights;
      SelectMenu* mMeshMenu;
      SelectMenu* mMaterialMenu;
      Slider*     mTessellationAmount;
  
  };
  
  #endif
  