/*---------------------------------------------------------------------------*
  Project:	NITRO-System - build - demos - g3d - samples - ProjMap
  File:     main.c

  Copyright 2005 Nintendo.  All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law.  They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Log: main.c,v $
  Revision 1.2  02/25/2005 03:10:48  takano_makoto
  Warning measures for signed<->unsigned casts

  Revision 1.1  01/06/2005 06:20:14  nishida_kenji
  Initial check in.

  $NoKeywords: $
 *---------------------------------------------------------------------------*/

//---------------------------------------------------------------------------
// This is a sample that displays objects that have been projection mapped.
//
// When the <material> element of the .imd file has tex_gen_mode set to pos,
// textures get mapped using projection mapping.
//
// Sample Operation
// In this sample, textures are projected to xy coordinates
// between (-1.0, -1.0) and (1.0, 1.0), where effect_mtx is a unit matrix. 
// Objects are translated along the X axis from -1.0 to 1.0.
//
// Press A Button: > Switches the method for setting the projection matrix prior to rendering.
//                     The texture gets mapped the same way no matter what the setting.
// Press X Button: > Environment map rotates around the X axis  (in the View coordinate system)
// Press Y Button: > Environment map rotates around the Y axis (in the View coordinate system)
// Press B Button: > Environment map rotates around the Z axix (in the View coordinate system)
//
//---------------------------------------------------------------------------


#include "g3d_demolib.h"

G3DDemoCamera gCamera;        // Camera structure.
G3DDemoGround gGround;        // Ground structure.

static void             InitG3DLib(void);
static NNSG3dResMdl*    LoadG3DModel(const char* path);
static void             SetCamera(G3DDemoCamera* camera);


/*-------------------------------------------------------------------------
    rotEnvMap

    Callback function that performs the process of changing the  direction of the environment map
  -------------------------------------------------------------------------*/
static void
rotProjMap(NNSG3dRS* rs)
{
    static MtxFx33 mRot = {
        FX32_ONE, 0, 0,
        0, FX32_ONE, 0,
        0, 0, FX32_ONE
    };
    MtxFx33 m;

    if (G3DDEMO_IS_PAD_PRESS(PAD_BUTTON_B))
    {
        MTX_RotZ33(&m, FX_SinIdx(256), FX_CosIdx(256));
        MTX_Concat33(&m, &mRot, &mRot);
    }

    if (G3DDEMO_IS_PAD_PRESS(PAD_BUTTON_Y))
    {
        MTX_RotY33(&m, FX_SinIdx(256), FX_CosIdx(256));
        MTX_Concat33(&m, &mRot, &mRot);
    }

    if (G3DDEMO_IS_PAD_PRESS(PAD_BUTTON_X))
    {
        MTX_RotX33(&m, FX_SinIdx(256), FX_CosIdx(256));
        MTX_Concat33(&m, &mRot, &mRot);
    }

    // By creating effect_mtx on your own you can change the direction in which the texture is projected.
    NNS_G3dGeMultMtx33(&mRot);

    // Flag is set because the created effect_mtx part been replaced.
    // When this is done, you can skip the internal library processes  that would otherwise proceed between Timing B and Timing C.
    rs->flag |= NNS_G3D_RSFLAG_SKIP;
}


/* -------------------------------------------------------------------------
  Name:         NitroMain

  Description:  Sample main.

  Arguments:    None.

  Returns:      None.
   ------------------------------------------------------------------------- */
void
NitroMain(void)
{
    NNSG3dRenderObj object;
    NNSG3dResMdl*    model;
    int state = 0;
    u16 sincos = 0;
    fx32 X = -FX32_ONE;

    G3DDemo_InitSystem();
    G3DDemo_InitMemory();
    G3DDemo_InitVRAM();

    InitG3DLib();
    G3DDemo_InitDisplay();

    // Makes the manager able to manage 4 slots of texture image slots,
    // and sets to the default manager.
    NNS_GfdInitFrmTexVramManager(4, TRUE);

    // Makes the manager able to manage 32 KB of palettes,
    // and sets to the default manager.
    NNS_GfdInitFrmPlttVramManager(0x8000, TRUE);

    model = LoadG3DModel("data/sphere_proj.nsbmd");
    SDK_ASSERTMSG(model, "load failed");

    //
    // G3D: NNSG3dRenderObj initialization
    //
    // NNSG3dRenderObj is a structure that holds every type of information relating to one model and
    // animation applied to that model.
    // The area specified by the internal member pointer must be obtained and released by the user.
    // Here mdl can be NULL. (It must be set during NNS_G3dDraw.)
    //
    NNS_G3dRenderObjInit(&object, model);

    G3DDemo_InitCamera(&gCamera, 0, 5*FX32_ONE);
    G3DDemo_InitGround(&gGround, (fx32)(1.5*FX32_ONE));

    G3DDemo_InitConsole();
    G3DDemo_Print(0,0, G3DDEMO_COLOR_YELLOW, "ProjMap");

    //
    // When customizing effect_mtx settings,
    // insert callback processing using timing B for NNS_G3D_SBC_ENVMAP.
    //  
    //
    NNS_G3dRenderObjSetCallBack(&object,
                                &rotProjMap,
                                NULL,
                                NNS_G3D_SBC_PRJMAP,
                                NNS_G3D_SBC_CALLBACK_TIMING_B);

    for(;;)
    {
        OSTick time;

        SVC_WaitVBlankIntr();
        G3DDemo_PrintApplyToHW();
        G3DDemo_ReadGamePad();

        G3DDemo_MoveCamera(&gCamera);
        G3DDemo_CalcCamera(&gCamera);

        G3X_Reset();
        G3X_ResetMtxStack();

        SetCamera(&gCamera);

        {
            //
            // Object is always rotating
            //
            MtxFx33 mRot;
            VecFx32 mTrans;
            sincos += 256;

            mTrans.x = X;
            mTrans.y = 0;
            mTrans.z = 0;
            
            MTX_RotX33(&mRot, FX_SinIdx(sincos), FX_CosIdx(sincos));
            NNS_G3dGlbSetBaseRot(&mRot);
            NNS_G3dGlbSetBaseTrans(&mTrans);

            X += FX32_ONE >> 8;
            if (X >= FX32_ONE)
                X = -FX32_ONE;
            
            //
            // Collects and transfers the state set with G3dGlbXXXX.
            // Camera and projection matrix, and the like, are set.
            //
            if (G3DDEMO_IS_PAD_TRIGGER(PAD_BUTTON_A))
            {
                state = (state + 1) % 3;
            }

            //
            // Pressing the A Button switches the way the projection matrix gets set up.
            // (The picture that displays is the same.)
            //
            switch(state)
            {
            case 0:
                NNS_G3dGlbFlushP();
                break;
            case 1:
                NNS_G3dGlbFlushVP();
                break;
            case 2:
                NNS_G3dGlbFlushWVP();
                break;
            };

            time = OS_GetTick();

            {
                //
                // G3D: NNS_G3dDraw
                // Performs drawing for all instances by passing the
                // NNSG3dRenderObj that was set up.
                //
                NNS_G3dDraw(&object);
            }

            // Time measurement.
            time = OS_GetTick() - time;
        }

        {
            // Restore rotation to origin.
            MtxFx33 mRot;
            VecFx32 mTrans = {0,0,0};
            MTX_Identity33(&mRot);
            NNS_G3dGlbSetBaseRot(&mRot);
            NNS_G3dGlbSetBaseTrans(&mTrans);

            //
            // G3D: Before calling normal G3_XXX, NNS_G3dGeComFlushBuffer()
            //      must be called and synchronized
            //
            NNS_G3dGlbFlush();
            NNS_G3dGeFlushBuffer();
            G3DDemo_DrawGround(&gGround);
        }

        /* memory swap relating to geometry and rendering engine*/
        G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_W);

        G3DDemo_Printf(16,0, G3DDEMO_COLOR_YELLOW, "TIME:%06ld usec", OS_TicksToMicroSeconds(time));
        switch(state)
        {
        case 0:
            G3DDemo_Printf(0, 1, G3DDEMO_COLOR_YELLOW, "NNS_G3dGlbFlushP  ");
            break;
        case 1:
            G3DDemo_Printf(0, 1, G3DDEMO_COLOR_YELLOW, "NNS_G3dGlbFlushVP ");
            break;
        case 2:
            G3DDemo_Printf(0, 1, G3DDEMO_COLOR_YELLOW, "NNS_G3dGlbFlushWVP");
            break;
        };
    }
}


/* -------------------------------------------------------------------------
  Name:         InitG3DLib

  Description:  Initializes G3D library.

  Arguments:    None.

  Returns:      None.
   ------------------------------------------------------------------------- */
static void
InitG3DLib(void)
{
    //
    // G3D:
    // performs default initialization
    // ------------------------------------------------------------------------
    NNS_G3dInit();
}


/* -------------------------------------------------------------------------
  Name:         LoadG3DModel

  Description:  Reads model data from file.

  Arguments:    path: Model data path name.

  Returns:      Pointer to model.
   ------------------------------------------------------------------------- */
static NNSG3dResMdl*
LoadG3DModel(const char* path)
{
    NNSG3dResMdl*        model   = NULL;
    NNSG3dResTex*        texture = NULL;
    NNSG3dResFileHeader* resFile = G3DDemo_LoadFile(path);
    BOOL status;
    SDK_NULL_ASSERT(resFile);

    // calls the default initialization function and sets up
    status = NNS_G3dResDefaultSetup(resFile);
    NNS_G3D_ASSERTMSG(status, "NNS_G3dResDefaultSetup failed");

    //
    // G3D: get model
    // nsbmd can include multiple models, so sets the index (0 when there is one model)
    // and gets the pointer to the one model.
    // --------------------------------------------------------------------
    model = NNS_G3dGetMdlByIdx(NNS_G3dGetMdlSet(resFile), 0);

    return model;
}


/* -------------------------------------------------------------------------
  Name:         SetCamera

  Description:  Sets camera matrix and projection matrix in G3D global state.

  Arguments:    camera:		pointer to the G3DDemoCamera structure

  Returns:      None.
   ------------------------------------------------------------------------- */
static void
SetCamera(G3DDemoCamera* camera)
{
    G3DDemoLookAt* lookat = &camera->lookat;
    G3DDemoPersp*  persp  = &camera->persp;

    NNS_G3dGlbPerspective(persp->fovySin, persp->fovyCos, persp->aspect, persp->nearClip, persp->farClip);
    NNS_G3dGlbLookAt(&lookat->camPos, &lookat->camUp, &lookat->target);
}

