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

  Copyright 2004,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.11  02/25/2005 03:09:22  takano_makoto
  Warning measures for signed<->unsigned casts

  Revision 1.10  11/12/2004 05:40:32  nishida
  Revised project name of file header.

  Revision 1.9  11/04/2004 10:43:25  nishida
  Fixed function header of SetCamera function.

  Revision 1.8  09/10/2004 12:39:29  nishida_kenji
  revised

  Revision 1.7  08/05/2004 00:11:41  nishida_kenji
  time NNS_G3dDraw only.

  Revision 1.6  08/02/2004 01:51:39  nishida_kenji
  add introduction.

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

//---------------------------------------------------------------------------
//
// Sample for the method of setting Projection matrix and Position/Vector matrix
//
// G3D normally sets up the projection matrix, current matrix and such of the position/vector matrix
// by using NNS_G3dGlbFlushP, NNS_G3dGlbFlushVP
// and NNS_G3dGlbFlushWVP, before drawing a model with NNS_G3dDraw.
// The difference between these three functions depends on what are set to the projection matrix and position/vector matrix.
// With the NNS_G3dGlbFlushP function, projective transformation matrix is set to the projection matrix,
// and the combination of the camera matrix and the object's BaseSRT matrix is set to the position/vector matrix.
// With the NNS_G3dGlbFlushVP function, the combination of the projective transformation matrix and camera matrix is set to the projection matrix,
// and the object's BaseSRT matrix is set to the position/vector matrix.
// With the NNS_G3dGlbFlushWVP function, the combination of the projective transformation matrix, camera matrix and the BaseSRT matrix of the object make up the projection matrix, but unit matrices are set for the position/vector matrix.
//   
//   
//
// There are two reasons for supporting multiple matrix setup methods.
// 1: There are some instances such as where you want to take the position/vector matrix to process it,
//    where you need coordinates in the camera space, where you need world coordinates,
//    or where you need local coordinates of the model (joint). If the setup is done with necessary functions,
//    there is no need to multiply matrices to obtain the value.
// 2: If the local coordinates of the model (joint) is calculated beforehand,
//    and a computed matrix is set on the position/vector matrix stack,
//    it is possible to change the position of all objects and the camera position merely by changing the projection matrix.
//     
//
// Sample Operation
// Use the A button to select one of the following functions to be called before rendering objects:
// NNS_G3dGlbFlushP / NNS_G3dGlbFlushVP / NNS_G3dGlbFlushWVP.
//   Note the following abbreviations
// P: Projection
// V: View
// W: World
//  
//
// HOWTO:
// Call NNS_G3dGlbFlushP, NNS_G3dGlbFlushVP, or NNS_G3dGlbFlushWVP before rendering objects.
//   
//
//---------------------------------------------------------------------------

#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);


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

  Description:  Sample main.

  Arguments:    None.

  Returns:      None.
   ------------------------------------------------------------------------- */
void
NitroMain(void)
{
    enum
    {
        Enum_NNS_G3dGlbFlushP   = 0,
        Enum_NNS_G3dGlbFlushVP  = 1,
        Enum_NNS_G3dGlbFlushWVP = 2
    };

    //  which function it is using
    int cameraFlag = Enum_NNS_G3dGlbFlushP;

    NNSG3dRenderObj object;
    NNSG3dResMdl*    model;

    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/billboard.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, 4 * FX32_ONE, 16 * FX32_ONE);
    G3DDemo_InitGround(&gGround, (fx32)(1.5 * FX32_ONE));

    G3DDemo_InitConsole();
    G3DDemo_Print(0,0, G3DDEMO_COLOR_YELLOW, "CameraMtx");
    G3DDemo_Print(0,2, G3DDEMO_COLOR_WHITE, "change:A");

    for(;;)
    {
        OSTick time;

        SVC_WaitVBlankIntr();

        G3DDemo_PrintApplyToHW();
        G3DDemo_ReadGamePad();

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

        G3X_Reset();
        G3X_ResetMtxStack();

        SetCamera(&gCamera);

        {
            VecFx32 scale = {FX32_ONE << 2, FX32_ONE << 2, FX32_ONE << 2};
            NNS_G3dGlbSetBaseScale(&scale);

            //
            // G3D:
            // Collects and transfers the state set with G3dGlbXXXX.
            // Camera and projection matrix, and the like, are set.
            //

            // When using NNS_G3dGlbFlushAlt, the current projection matrix can be multiplied by projective transformation matrix, camera matrix,
            // and base SRT matrix. For position/vector matrix (stack), only the local transformation matrix is stored.
            // Therefore, by changing the current projection matrix, without calculating the position/vector matrix (stack) again,
            // multiple models with the same posture can be drawn. (The calculation result needs to be left in the matrix stack.)
            switch(cameraFlag) {
            case Enum_NNS_G3dGlbFlushP:
                //  Projection matrix cannot be multiplied by camera matrix
                NNS_G3dGlbFlushP();
                break;
            case Enum_NNS_G3dGlbFlushVP:
                //
                NNS_G3dGlbFlushVP();
                break;
            case Enum_NNS_G3dGlbFlushWVP:
                //  The camera matrix is multiplied by the projection matrix.
                NNS_G3dGlbFlushWVP();
                break;
            }

            //  Start time measurement
            time = OS_GetTick();
            {
                //
                // G3D: NNS_G3dDraw
                // Performs drawing for all instances by passing the
                // NNSG3dRenderObj that was set up.
                // The picture is the same for using NNS_G3dGlbFlush() or NNS_G3dGlbFlushAlt().
                NNS_G3dDraw(&object);
            }
            // Time measurement result
            time = OS_GetTick() - time;
        }

        {
            // restore scaling
            VecFx32 scale = {FX32_ONE, FX32_ONE, FX32_ONE};
            NNS_G3dGlbSetBaseScale(&scale);
            //
            // 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);

        //  Switches cameraFlag every time A button is pressed
        if(G3DDEMO_IS_PAD_TRIGGER(PAD_BUTTON_A)) {
            cameraFlag++;
            cameraFlag = cameraFlag % 3;
        }
        
        //   Displays which function is called now
        switch(cameraFlag)
        {
        case Enum_NNS_G3dGlbFlushP:
            G3DDemo_Printf(0,4, G3DDEMO_COLOR_GREEN, "G3dGlbFlushP  ");
            break;
        case Enum_NNS_G3dGlbFlushVP:
            G3DDemo_Printf(0,4, G3DDEMO_COLOR_GREEN, "G3dGlbFlushVP ");
            break;
        case Enum_NNS_G3dGlbFlushWVP:
            G3DDemo_Printf(0,4, G3DDEMO_COLOR_GREEN, "G3dGlbFlushWVP");
            break;
        }
        
        //   Displays the contents of the current projection matrix and current pos/vector matrix
        switch(cameraFlag)
        {
        case Enum_NNS_G3dGlbFlushP:
            G3DDemo_Printf(0,6, G3DDEMO_COLOR_WHITE, "Projection Mtx:");
            G3DDemo_Printf(2,7, G3DDEMO_COLOR_MAGENTA, "Pj                   ");
            G3DDemo_Printf(0,8, G3DDEMO_COLOR_WHITE, "Pos/Vector Mtx:");
            G3DDemo_Printf(2,9, G3DDEMO_COLOR_MAGENTA, "Camera * BaseSRT ");
            break;
        case Enum_NNS_G3dGlbFlushVP:
            G3DDemo_Printf(0,6, G3DDEMO_COLOR_WHITE, "Projection Mtx:");
            G3DDemo_Printf(2,7, G3DDEMO_COLOR_MAGENTA, "Pj * Camera          ");
            G3DDemo_Printf(0,8, G3DDEMO_COLOR_WHITE, "Pos/Vector Mtx:");
            G3DDemo_Printf(2,9, G3DDEMO_COLOR_MAGENTA, "BaseSRT              ");
            break;
        case Enum_NNS_G3dGlbFlushWVP:
            G3DDemo_Printf(0,6, G3DDEMO_COLOR_WHITE, "Projection Mtx:");
            G3DDemo_Printf(2,7, G3DDEMO_COLOR_MAGENTA, "Pj * Camera * BaseSRT");
            G3DDemo_Printf(0,8, G3DDEMO_COLOR_WHITE, "Pos/Vector Mtx:");
            G3DDemo_Printf(2,9, G3DDEMO_COLOR_MAGENTA, "Identity Mtx         ");
            break;
        }
        
        G3DDemo_Printf(16,0, G3DDEMO_COLOR_YELLOW, "TIME:%06ld usec", OS_TicksToMicroSeconds(time));
    }
}


/* -------------------------------------------------------------------------
  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;
    NNS_G3D_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);
}

