

/**
    \file 
        LightShaft.h
    \brief
        Specialisation of OGRE's framework application to show the
        LightShaft feature
*/


#include "ExampleApplication.h"
Light* mSunLight;
Entity* mEntity;
Entity* pPlaneEnt;
SceneNode* KnotNode;
SceneNode*BillboardsNode;
String CUSTOM_ROCKWALL_MATERIAL("Ogre/DepthShadowmap/Receiver/RockWall");
String CUSTOM_CASTER_MATERIAL("Ogre/DepthShadowmap/Caster/Float");
String CUSTOM_RECEIVER_MATERIAL("Ogre/DepthShadowmap/Receiver/Float");
String CUSTOM_KNOT_MATERIAL("KnotShadowReceiver");

ColourValue mMinLightColour(0.2, 0.1, 0.0);

// Event handler to add ability to alter curvature
class LightShaftFrameListener : public ExampleFrameListener
{
protected:
    Real mCurvature;
    Real mTiling;
    SceneManager* mSceneMgr;
public:
    LightShaftFrameListener(RenderWindow* win, Camera* cam, SceneManager* mgr)
        : ExampleFrameListener(win, cam)
    {
        mSceneMgr = mgr;
        mCurvature = 1;
        mTiling = 15;

    }

    bool frameStarted(const FrameEvent& evt)
    {
        
        if(!ExampleFrameListener::frameStarted(evt))
            return false;
       //add a little rotation to animate the scene
		KnotNode->yaw(Degree(evt.timeSinceLastFrame*4));
        return true;

    }
};

class LightShaftApplication : public ExampleApplication
{

public:
    LightShaftApplication()
    {
    }

protected:
	ShadowTechnique mCurrentShadowTechnique;
	MovablePlane* mPlane;
	bool mIsOpenGL;
    // Just override the mandatory create scene method
    void createScene(void)
    {
      	if (Root::getSingleton().getRenderSystem()->getName().find("GL") != String::npos)
		{
			mIsOpenGL = true;
		}
		else
		{
			mIsOpenGL = false;
		}
		generalSceneSetup();
    }
	
	void generalSceneSetup()
    {


        // do this first so we generate edge lists
        mSceneMgr->setShadowTechnique(SHADOWTYPE_TEXTURE_ADDITIVE);
		mCurrentShadowTechnique = SHADOWTYPE_TEXTURE_ADDITIVE;

        // Set ambient light off
        mSceneMgr->setAmbientLight(ColourValue(0.0, 0.0, 0.0));

        // Fixed light, dim
        mSunLight = mSceneMgr->createLight("SunLight");
        mSunLight->setType(Light::LT_SPOTLIGHT);
        mSunLight->setPosition(100,105,150);
        mSunLight->setSpotlightRange(Degree(30), Degree(50));
        Vector3 dir;
        dir = -mSunLight->getPosition();
        dir.normalise();
        mSunLight->setDirection(dir);
        mSunLight->setDiffuseColour(0.85, 0.85, 0.88);
        mSunLight->setSpecularColour(0.9, 0.9, 1);

        // Create light node
		SceneNode*   mLightNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(
            "MovingLightNode");

        BillboardSet* bbs = mSceneMgr->createBillboardSet("lightbbs", 1);
        bbs->setMaterialName("Examples/Flare");
        Billboard* bb = bbs->createBillboard(0,0,0,mMinLightColour);
        // attach
		mLightNode->setPosition(100,105,150);
        mLightNode->attachObject(bbs);


   
        // Prepare athene mesh for normalmapping
        MeshPtr pAthene = MeshManager::getSingleton().load("knot.mesh", 
            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
        unsigned short src, dest;
        if (!pAthene->suggestTangentVectorBuildParams(VES_TANGENT, src, dest))
        {
            pAthene->buildTangentVectors(VES_TANGENT, src, dest);
        }

        
        KnotNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
        mEntity = mSceneMgr->createEntity( "knot", "knot.mesh" );
        mEntity->setMaterialName(CUSTOM_KNOT_MATERIAL);
        KnotNode->attachObject( mEntity );
        KnotNode->translate(0,-27, 0);
     
		KnotNode->scale(0.4,0.4,0.4);

	
		
		//generation of the billboards that will fake the volumetric lighting
		BillboardsNode=mSceneMgr->getRootSceneNode()->createChildSceneNode("BillboardNode");
				BillboardSet*bills=  mSceneMgr->createBillboardSet("lightBillboardSet", 20);
		bills->setDefaultWidth(256);
		bills->setDefaultHeight(256);
		bills->setVisible(true);
		bills->setMaterialName("LightShafts");

		for(int i=0;i<50;i++)
		{
			bills->createBillboard(0,0,-200+5*i);	
		}

		BillboardsNode->attachObject(bills);

	

        // Skybox
        mSceneMgr->setSkyBox(true, "Examples/StormySkyBox");

        // Floor plane (use POSM plane def)
		mPlane = new MovablePlane("*mPlane");
        mPlane->normal = Vector3::UNIT_Y;
        mPlane->d = 107;
        MeshManager::getSingleton().createPlane("Myplane",
            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, *mPlane,
            1500,1500,50,50,true,1,5,5,Vector3::UNIT_Z);
        pPlaneEnt = mSceneMgr->createEntity( "plane", "Myplane" );
        pPlaneEnt->setMaterialName(CUSTOM_KNOT_MATERIAL);
        pPlaneEnt->setCastShadows(false);
		SceneNode*pPlaneNode=mSceneMgr->getRootSceneNode()->createChildSceneNode();
        pPlaneNode->attachObject(pPlaneEnt);
		pPlaneNode->setPosition(-200,0,-200);


		if (mRoot->getRenderSystem()->getCapabilities()->hasCapability(RSC_HWRENDER_TO_TEXTURE))
        {
            // In D3D, use a 1024x1024 shadow texture
            mSceneMgr->setShadowTextureSettings(512, 2);
        }
        else
        {
            // Use 512x512 texture in GL since we can't go higher than the window res
            mSceneMgr->setShadowTextureSettings(512, 2);
        }
        mSceneMgr->setShadowColour(ColourValue(0.5, 0.5, 0.5));

        // incase infinite far distance is not supported
        mCamera->setFarClipDistance(100000);
		
        mSceneMgr->setShowDebugShadows(false);

		//setup the camera to have a nice point of view
		mCamera->setPosition(-320, -20, -390);
		mCamera->lookAt(0, -27, 0);

		setupShadows();

    }
	void setupShadows()
	{
		if (mIsOpenGL)
			{
				// GL performs much better if you pick half-float format
				mSceneMgr->setShadowTexturePixelFormat(PF_FLOAT16_R);
			}
			else
			{
				// D3D is the opposite - if you ask for PF_FLOAT16_R you
				// get an integer format instead! You can ask for PF_FLOAT16_GR
				// but the precision doesn't work well
				mSceneMgr->setShadowTexturePixelFormat(PF_FLOAT32_R);
			}
			mSceneMgr->setShadowTextureCasterMaterial(CUSTOM_CASTER_MATERIAL);
			mSceneMgr->setShadowTextureReceiverMaterial(CUSTOM_RECEIVER_MATERIAL);
			mSceneMgr->setShadowTextureSelfShadow(true);	
			// Sort out base materials
			pPlaneEnt->setMaterialName(CUSTOM_ROCKWALL_MATERIAL);
		
	}
	
    // Create new frame listener
    void createFrameListener(void)
    {
        mFrameListener= new LightShaftFrameListener(mWindow, mCamera, mSceneMgr);
        mRoot->addFrameListener(mFrameListener);
    }


    bool setup()
    {
        ExampleApplication::setup();
        LogManager::getSingleton().setLogDetail( LL_BOREME );
        return true;
    }
};
