// Geometric Tools, Inc.
// http://www.geometrictools.com
// Copyright (c) 1998-2005.  All Rights Reserved
//
// The Wild Magic Library (WM3) source code is supplied under the terms of
// the license agreement
//     http://www.geometrictools.com/License/WildMagic3License.pdf
// and may not be copied or disclosed except in accordance with the terms
// of that agreement.

#include "RenderToTexture.h"

WM3_WINDOW_APPLICATION(RenderToTexture);

//----------------------------------------------------------------------------
RenderToTexture::RenderToTexture ()
    :
    WindowApplication3("RenderToTexture",0,0,640,480,ColorRGB(0.5f,0.5f,0.5f))
{
}
//----------------------------------------------------------------------------
bool RenderToTexture::OnInitialize ()
{
    if ( !WindowApplication3::OnInitialize() )
        return false;

    // create scene
    m_spkScene = new Node;
    Node* pkTrnNode = new Node;
    m_spkScene->AttachChild(pkTrnNode);
    m_spkWireframe = new WireframeState;
    m_spkScene->SetGlobalState(m_spkWireframe);
    m_spkScene->SetGlobalState(new ZBufferState);
    pkTrnNode->AttachChild(CreateModel());

    // create a screen polygon to use the render target
    Vector2f* akVertex = new Vector2f[4];
    akVertex[0] = Vector2f(0.0f,0.0f);
    akVertex[1] = Vector2f(0.2f,0.0f);
    akVertex[2] = Vector2f(0.2f,0.2f);
    akVertex[3] = Vector2f(0.0f,0.2f);
    m_spkSPolygon = new ScreenPolygon(4,akVertex,true);

    int iWidth = 256, iHeight = 256;
    unsigned char* aucData = new unsigned char[4*iWidth*iHeight];
    unsigned char* pucData = aucData;
    for (int i = 0; i < iWidth*iHeight; i++)
    {
        *pucData++ = 0xFF;
        *pucData++ = 0x00;
        *pucData++ = 0x00;
        *pucData++ = 0xFF;
    }
    Image* pkImage = new Image(Image::IT_RGBA8888,iWidth,iHeight,aucData);
    m_spkRenderTarget = new Texture(pkImage);
    m_spkRenderTarget->Filter = Texture::FM_LINEAR;
    m_spkRenderTarget->Mipmap = Texture::MM_LINEAR;
    Vector2f* akUV = new Vector2f[4];
    akUV[0] = Vector2f(0.0f,0.0f);
    akUV[1] = Vector2f(1.0f,0.0f);
    akUV[2] = Vector2f(1.0f,1.0f);
    akUV[3] = Vector2f(0.0f,1.0f);
    Vector2fArrayPtr spkUVs = new Vector2fArray(4,akUV);
    TextureEffect* pkEffect = new TextureEffect(m_spkRenderTarget,spkUVs);
    m_spkSPolygon->SetEffect(pkEffect);
    m_spkSPolygon->UpdateGS();
    m_spkSPolygon->UpdateRS();

    // center-and-fit scene in the view frustum
    m_spkScene->UpdateGS();
    pkTrnNode->Local.SetTranslate(-m_spkScene->WorldBound->GetCenter());
    m_spkCamera->SetFrustum(-0.55f,0.55f,-0.4125f,0.4125f,1.0f,1000.0f);
    Vector3f kCDir(0.0f,1.0f,0.0f);
    Vector3f kCUp(0.0f,0.0f,1.0f);
    Vector3f kCRight = kCDir.Cross(kCUp);
    Vector3f kCLoc = -3.0f*m_spkScene->WorldBound->GetRadius()*kCDir;
    m_spkCamera->SetFrame(kCLoc,kCDir,kCUp,kCRight);

    // initial update of objects
    m_spkScene->UpdateGS();
    m_spkScene->UpdateRS();

    InitializeCameraMotion(0.1f,0.001f);
    InitializeObjectMotion(m_spkScene);
    return true;
}
//----------------------------------------------------------------------------
void RenderToTexture::OnTerminate ()
{
    m_pkRenderer->DeleteOffscreen(m_spkRenderTarget);
    m_spkRenderTarget = 0;

    m_spkScene = 0;
    m_spkWireframe = 0;
    m_spkTEffect = 0;
    m_spkEMEffect = 0;
    m_spkSPolygon = 0;
    WindowApplication3::OnTerminate();
}
//----------------------------------------------------------------------------
void RenderToTexture::OnIdle ()
{
    MeasureTime();

    MoveCamera();

    if ( MoveObject() )
        m_spkScene->UpdateGS();

    if ( m_pkRenderer->BeginScene() )
    {
        if ( m_pkRenderer->BeginOffscreen(m_spkRenderTarget) )
        {
            m_pkRenderer->SetBackgroundColor(ColorRGB::WHITE);
            m_pkRenderer->ClearBuffers();
            m_pkRenderer->DrawScene(m_spkScene);
            m_pkRenderer->EndOffscreen();
        }

        m_pkRenderer->SetBackgroundColor(ColorRGB(0.5f,0.5f,0.5f));
        m_pkRenderer->ClearBuffers();
        m_pkRenderer->DrawScene(m_spkScene);
        m_pkRenderer->Draw(m_spkSPolygon);

        m_pkRenderer->EndScene();
    }
    m_pkRenderer->DisplayBackBuffer();

    UpdateFrameCount();
}
//----------------------------------------------------------------------------
bool RenderToTexture::OnKeyDown (unsigned char ucKey, int iX, int iY)
{
    if ( WindowApplication3::OnKeyDown(ucKey,iX,iY) )
        return true;

    switch ( ucKey )
    {
    case 'w':
    case 'W':
        m_spkWireframe->Enabled = !m_spkWireframe->Enabled;
        return true;

    case 's':
    case 'S':
        if ( m_pkRenderer->BeginOffscreen(m_spkRenderTarget) )
        {
            // Read the offscreen buffer into the image of the render target.
            m_pkRenderer->SaveOffscreen();
            Image* pkImage = m_spkRenderTarget->GetImage();
            pkImage->Save("offscreen.wmif");

            // Free up the resources associated with the context of the render
            // target.  In this sample, only textures were bound to the
            // context, so there is no need to release cached arrays or
            // shader programs.
            m_pkRenderer->ReleaseTextures(m_spkScene);

            m_pkRenderer->EndOffscreen();
        }
        return true;

    // Cycle through the various apply modes for the enviroment map:
    // replace, decal, modulate, blend, add
    case '+':
    case '=':
    {
        switch ( m_spkEMEffect->Textures[0]->Apply )
        {
        case Texture::AM_REPLACE:
            m_spkEMEffect->Textures[0]->Apply = Texture::AM_MODULATE;
            return true;
        case Texture::AM_MODULATE:
            m_spkEMEffect->Textures[0]->Apply = Texture::AM_BLEND;
            return true;
        case Texture::AM_BLEND:
            m_spkEMEffect->Textures[0]->Apply = Texture::AM_ADD;
            return true;
        default:
            m_spkEMEffect->Textures[0]->Apply = Texture::AM_REPLACE;
            return true;
        }
        return true;
    }
    }

    return false;
}
//----------------------------------------------------------------------------
Node* RenderToTexture::CreateModel ()
{
    Node* pkModel = new Node;

    // load face model
    Stream kIStream;
    kIStream.Load(System::GetPath("../../Data/Wmof/","Face.wmof"));
    TriMesh* pkMesh = StaticCast<TriMesh>(kIStream.GetObjectAt(0));
    pkMesh->GenerateNormals();
    pkModel->AttachChild(pkMesh);

    int iVQ = pkMesh->Vertices->GetQuantity();
    const Vector3f* akNormal = pkMesh->Normals->GetData();
    Vector2f* akUV = new Vector2f[iVQ];
    for (int i = 0; i < iVQ; i++)
    {
        akUV[i].X() = Mathf::ACos(akNormal[i].Z());
        akUV[i].Y() = Mathf::ATan2(akNormal[i].Y(),akNormal[i].X());
    }
    Vector2fArray* pkUVs = new Vector2fArray(iVQ,akUV);

    Texture* pkTexture = new Texture(
        Image::Load(System::GetPath("../../Data/Wmif/","SunFire.wmif")));
    m_spkTEffect = new TextureEffect(pkTexture,pkUVs);
    pkMesh->SetEffect(m_spkTEffect);

    // create environment map
    Image* pkImage = Image::Load(
        System::GetPath("../../Data/Wmif/","SphereMap.wmif"));
    m_spkEMEffect = new EnvironmentMapEffect(pkImage,Texture::AM_REPLACE);
    pkModel->SetEffect(m_spkEMEffect);

    return pkModel;
}
//----------------------------------------------------------------------------


