/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright © 2000-2003 The OGRE Team
Also see acknowledgements in Readme.html

You may use this sample code for anything you like, it is not covered by the
LGPL like the rest of the engine.
-----------------------------------------------------------------------------
*/

/**
    \file 
        RenderToTexture.cpp
    \brief
        Shows OGRE's RenderToTexture feature, and using it to drive a reflection.
*/

#include "ExampleApplication.h"
#include "ExampleFrameListener.h"

class RenderToTextureFrameListener : public ExampleFrameListener
{
protected:
    Camera* mReflectCam;
    SceneNode* mPlaneNode;
public:
    RenderToTextureFrameListener(RenderWindow* window, Camera* maincam, Camera* reflectCam, 
        SceneNode* planeSceneNode)
        :ExampleFrameListener(window, maincam), 
        mReflectCam(reflectCam), mPlaneNode(planeSceneNode)
    {

    }
    bool frameStarted(const FrameEvent& evt)
    {
        bool result = ExampleFrameListener::frameStarted(evt);

        // Make sure reflection camera is updated too
        mReflectCam->setOrientation(mCamera->getOrientation());
        mReflectCam->setPosition(mCamera->getPosition());

        // Rotate plane
        mPlaneNode->yaw(Degree(30 * evt.timeSinceLastFrame), Node::TS_PARENT);

        return result;        
    }
};

class RenderToTextureApplication : public ExampleApplication, public RenderTargetListener
{
public:
    RenderToTextureApplication() : mPlane(0) {}
    ~RenderToTextureApplication()
    {
        delete mPlane;
    }

protected:

    MovablePlane* mPlane;
    Entity* mPlaneEnt;
    Camera* mReflectCam;
    SceneNode* mPlaneNode;
    // render target events
    void preRenderTargetUpdate(const RenderTargetEvent& evt)
    {
        // Hide plane 
        mPlaneEnt->setVisible(false);

    }
    void postRenderTargetUpdate(const RenderTargetEvent& evt)
    {
        // Show plane 
        mPlaneEnt->setVisible(true);
    }

    // Just override the mandatory create scene method
    void createScene(void)
    {
        // Set ambient light
        mSceneMgr->setAmbientLight(ColourValue(0.2, 0.2, 0.2));
        // Skybox
        mSceneMgr->setSkyBox(true, "Examples/MorningSkyBox");

        // Create a light
        Light* l = mSceneMgr->createLight("MainLight");
        l->setType(Light::LT_DIRECTIONAL);
        Vector3 dir(0.5, -1, 0);
        dir.normalise();
        l->setDirection(dir);
        l->setDiffuseColour(1.0f, 1.0f, 0.8f);
        l->setSpecularColour(1.0f, 1.0f, 1.0f);


        // Create a prefab plane
        mPlane = new MovablePlane("ReflectPlane");
        mPlane->d = 0;
        mPlane->normal = Vector3::UNIT_Y;
        MeshManager::getSingleton().createPlane("ReflectionPlane", *mPlane, 2000, 2000, 
            1, 1, true, 1, 1, 1, Vector3::UNIT_Z);
        mPlaneEnt = mSceneMgr->createEntity( "Plane", "ReflectionPlane" );

        // Create an entity from a model (will be loaded automatically)
        Entity* knotEnt = mSceneMgr->createEntity("Knot", "knot.mesh");

        // Create an entity from a model (will be loaded automatically)
        Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");

        knotEnt->setMaterialName("Examples/TextureEffect2");

        // Attach the rtt entity to the root of the scene
        SceneNode* rootNode = mSceneMgr->getRootSceneNode();
        mPlaneNode = rootNode->createChildSceneNode();

        // Attach both the plane entity, and the plane definition
        mPlaneNode->attachObject(mPlaneEnt);
        mPlaneNode->attachObject(mPlane);
        mPlaneNode->translate(0, -10, 0);
        // Tilt it a little to make it interesting
        mPlaneNode->roll(Degree(5));

        rootNode->createChildSceneNode( "Head" )->attachObject( ogreHead );

        RenderTexture* rttTex = mRoot->getRenderSystem()->createRenderTexture( "RttTex", 512, 512 );
        {
            mReflectCam = mSceneMgr->createCamera("ReflectCam");
            mReflectCam->setNearClipDistance(mCamera->getNearClipDistance());
            mReflectCam->setFarClipDistance(mCamera->getFarClipDistance());
            mReflectCam->setAspectRatio(
                (Real)mWindow->getViewport(0)->getActualWidth() / 
                (Real)mWindow->getViewport(0)->getActualHeight());

            Viewport *v = rttTex->addViewport( mReflectCam );
            v->setClearEveryFrame( true );
            v->setBackgroundColour( ColourValue::Black );

            Material* mat = mSceneMgr->createMaterial("RttMat");
            TextureUnitState* t = mat->getTechnique(0)->getPass(0)->createTextureUnitState("RustedMetal.jpg");
            t = mat->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex");
            // Blend with base texture
            t->setColourOperationEx(LBX_BLEND_MANUAL, LBS_TEXTURE, LBS_CURRENT, ColourValue::White, 
                ColourValue::White, 0.25);
            t->setProjectiveTexturing(true, mReflectCam);
            rttTex->addListener(this);

            // set up linked reflection
            mReflectCam->enableReflection(mPlane);
            // Also clip
            mReflectCam->enableCustomNearClipPlane(mPlane);
        }

        // Give the plane a texture
        mPlaneEnt->setMaterialName("RttMat");


        // Add a whole bunch of extra transparent entities
        Entity *cloneEnt;
        for (int n = 0; n < 10; ++n)
        {
            // Create a new node under the root
            SceneNode* node = mSceneMgr->createSceneNode();
            // Random translate
            Vector3 nodePos;
            nodePos.x = Math::SymmetricRandom() * 750.0;
            nodePos.y = Math::SymmetricRandom() * 100.0 + 25;
            nodePos.z = Math::SymmetricRandom() * 750.0;
            node->setPosition(nodePos);
            rootNode->addChild(node);
            // Clone knot
            char cloneName[12];
            sprintf(cloneName, "Knot%d", n);
            cloneEnt = knotEnt->clone(cloneName);
            // Attach to new node
            node->attachObject(cloneEnt);

        }

        mCamera->setPosition(-50, 100, 500);
        mCamera->lookAt(0,0,0);
    }

    void createFrameListener(void)
    {
        mFrameListener= new RenderToTextureFrameListener(mWindow, mCamera, mReflectCam, mPlaneNode);
        mRoot->addFrameListener(mFrameListener);

    }

};

int main(int argc, char ** argv)
{

    // Create application object
    RenderToTextureApplication app;

    app.go();

    return 0;
}
