/*---------------------------------------------------------------------------*
  Project:  NITRO-System - build - demos - g3d - samples - ManualSetup
  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.5  02/25/2005 03:10:05  takano_makoto
  Warning measures for signed<->unsigned casts

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

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

  Revision 1.2  10/07/2004 05:13:48  nishida_kenji
  Add ManualRelease.

  Revision 1.1  09/22/2004 08:49:12  nishida_kenji
  Initial check in.

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

//---------------------------------------------------------------------------
//
// Sample that sets up the model resource without using NNS_G3dResDefaultSetup
//
// Loads the binary data of the model into the main memory,
// and sets up without using NNS_G3dResDefaultSetup, and displays the model.
//
// Sample Operation
// Displays model.
// 
// HOWTO:
// See descriptions in the code.
//
//---------------------------------------------------------------------------

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


static void
ManualSetup(void* pResFile /* pResFile is a pointer to the loaded .nsbmd file.*/)
{
    //
    // Sets up by yourself without using NNS_G3dResDefaultSetup.
    // This code sets up only the model of the index 0 that are in .nsbmd.
    //
    NNSG3dResMdlSet* mdlSet = NNS_G3dGetMdlSet(pResFile);
    NNSG3dResTex* texture = NNS_G3dGetTex(pResFile); // Returns NULL when there is no texture data.

    //
    // The following procedure is required for initializing model data.
    // Initialization process is not required for the models that do not have texture.
    //
    // 1: Obtain the size of the necessary texture palette region.
    // 2: Create a key (same format as the gfd library) for the VRAM region that is used for texture and palettes.
    // 3: Set the key to the texture block of the model data.
    // 4: Transfer texture or palette to VRAM.
    // 5: Bind materials and texture that are in model resource.
    //
    // The procedure 4 and 5 can be in the reverse order.
    //
    if (texture)
    {
        //
        // 1: Obtain the required size of the texture palette region.
        //
        u32 szTex     = NNS_G3dTexGetRequiredSize(texture);
        u32 szTex4x4  = NNS_G3dTex4x4GetRequiredSize(texture);
        u32 szPltt    = NNS_G3dPlttGetRequiredSize(texture);

        //
        // 2: Create a key for the VRAM to be used for the texture and palette (same format as gfd library).
        //    
        //
        // The size of the region that is set to a key must be at least the required size.
        // Also, the user can create a special key as long as it is valid.
        // A key can be created directly by using NNS_GfdMakeTexKey and NNS_GfdMakePlttKey.
        // 
        //
        u32 keyTex    = NNS_GfdAllocTexVram(szTex, FALSE, 0);
        u32 keyTex4x4 = NNS_GfdAllocTexVram(szTex4x4, TRUE, 0);
        u32 keyPltt   = NNS_GfdAllocPlttVram(szPltt, FALSE, TRUE);

        OS_Printf("keyTex    = 0x%08x addr = 0x%08x size = 0x%08x\n", keyTex   , NNS_GfdGetTexKeyAddr(keyTex   ), NNS_GfdGetTexKeySize(keyTex   ));
        OS_Printf("keyTex4x4 = 0x%08x addr = 0x%08x size = 0x%08x\n", keyTex4x4, NNS_GfdGetTexKeyAddr(keyTex4x4), NNS_GfdGetTexKeySize(keyTex4x4));
        OS_Printf("keyPltt   = 0x%08x addr = 0x%08x size = 0x%08x\n", keyPltt  , NNS_GfdGetPlttKeyAddr(keyPltt ), NNS_GfdGetPlttKeySize(keyPltt ));

        //
        // 3: Set the key to the texture block of the model data.
        // 
        NNS_G3dTexSetTexKey(texture, keyTex, keyTex4x4);
        NNS_G3dPlttSetPlttKey(texture, keyPltt);

        //
        // 4: Transfer texture or palette to VRAM.
        //
        // Transfer the texture and palette according to the key information that is set.
        // If TRUE is set to the second argument, BeginLoadTex and EndLoadTex are executed internally every time.
        // If set to FALSE, it does not do anything.
        //
        NNS_G3dTexLoad(texture, TRUE);
        NNS_G3dPlttLoad(texture, TRUE);

        //
        // 5: Bind materials and texture that are in model resource.
        //
        // Bind materials and texture that are in model resource.
        // By using NNS_G3dBindMdlSet, set up the texture related data (TexImageParam TexPlttBase) in the model resource materials.
        // 
        // The function waits for a BOOL type return value, and if the return value is TRUE, all material having the texture in the model
        // is bound to the corresponding texture.
        // If FALSE, materials which have not been bound to a texture still remain.
        //
        // Note that materials which have already been bound cannot be bound to a new texture unless they have been released.
        // (Basically, textures bound first have priority when binding multiple texture blocks that have the same texture name.)
        //   
        //
        {
            BOOL result;
            result = NNS_G3dBindMdlSet(mdlSet, texture);
            SDK_ASSERT(result);
        }
    }
}


static void
ManualRelease(void* pResFile /* pResFile is a pointer to the loaded .nsbmd file.*/)
{
    //
    // Release it by yourself without using NNS_G3dResDefaultRelease
    //

    NNSG3dResTex* texture = NNS_G3dGetTex(pResFile); // Returns NULL when there is no texture data.

    //
    // The release process of the model data requires the following procedure.
    // The release process is not required for the models that do not have texture.
    //
    // 1: Clear the association to the texture resource that is set in the model resource.
    // 2: Remove the key from the texture resource.
    // 3: (If it is used) Use the VRAM manager and release the VRAM region that corresponds to the key.
    //
    if (texture)
    {
        NNSG3dPlttKey plttKey;
        NNSG3dTexKey  texKey, tex4x4Key;
        int status;
        NNSG3dResMdlSet* mdlSet = NNS_G3dGetMdlSet(pResFile);

        //
        // 1: Clear the association to the texture resource that is set in the model resource.
        //
        NNS_G3dReleaseMdlSet(mdlSet);

        // 2: Remove the key from the texture resource.
        plttKey = NNS_G3dPlttReleasePlttKey(texture);
        NNS_G3dTexReleaseTexKey(texture, &texKey, &tex4x4Key);

        //
        // 3: (If it is used) Use the VRAM manager and release the VRAM region that corresponds to the key.
        //
        // Frees the VRAM using the VRAM manager when the VRAM manager is being used.
        //  
        // Note, however, that it's impossible to use the same memory with the manager for the frame system used in this sample because the memory cannot be freed independently.
        //  
        // If the user is going to be managing VRAM on his or her own, the following code needs to be adopted by the user.
        //  
        //
        if (plttKey > 0)
        {
            // Released if secured in texture palette slot
            status = NNS_GfdFreePlttVram(plttKey);
            NNS_G3D_ASSERTMSG(!status, "NNS_GfdFreePlttVram failed");
        }

        if (tex4x4Key > 0)
        {
            // Released if secured in texture image slot
            status = NNS_GfdFreeTexVram(tex4x4Key);
            NNS_G3D_ASSERTMSG(!status, "NNS_GfdFreeTexVram failed");
        }

        if (texKey > 0)
        {
            // Released if secured in texture image slot
            status = NNS_GfdFreeTexVram(texKey);
            NNS_G3D_ASSERTMSG(!status, "NNS_GfdFreeTexVram failed");
        }
    }
}


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

  Description:  Sample main.

  Arguments:    None.

  Returns:      None.
 ------------------------------------------------------------------------- */
void
NitroMain(void)
{
    NNSG3dRenderObj object;
    NNSG3dResMdl*   pModel;
    void*           pResFile;
    BOOL            isSetup = TRUE;

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

    {
        //
        // load model resource
        //
        pResFile = G3DDemo_LoadFile("data/human_run_t.nsbmd");
        SDK_ASSERTMSG(pResFile, "load failed");

        ManualSetup(pResFile);

        //
        // get index #0 model from model set in model resource
        //
        pModel = NNS_G3dGetMdlByIdx(NNS_G3dGetMdlSet(pResFile), 0);
        SDK_ASSERTMSG(pModel, "model not found");

        //
        // 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, pModel);
    }

    G3DDemo_InitCamera(&gCamera, 10*FX32_ONE, 16*FX32_ONE);
    G3DDemo_InitGround(&gGround, (fx32)(1.5*FX32_ONE));

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

    for(;;)
    {
        OSTick time;

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

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

        G3X_Reset();
        G3X_ResetMtxStack();

        SetCamera(&gCamera);

        if (G3DDEMO_IS_PAD_TRIGGER(PAD_BUTTON_A))
        {
            if (isSetup)
            {
                ManualRelease(pResFile);
                pModel = NULL;
                isSetup = FALSE;
            }
            else
            {
                ManualSetup(pResFile);
                pModel = NNS_G3dGetMdlByIdx(NNS_G3dGetMdlSet(pResFile), 0);
                SDK_ASSERTMSG(pModel, "model not found");
                NNS_G3dRenderObjInit(&object, pModel);

                isSetup = TRUE;
            }
        }

        if (isSetup)
        {
            // apply scaling and try to make smaller
            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.
            //
            NNS_G3dGlbFlushP();

            time = OS_GetTick();
            {
                //
                // G3D: NNS_G3dDraw
                // Performs drawing for all instances by passing the
                // NNSG3dRenderObj that was set up.
                //
                NNS_G3dDraw(&object);
            }
            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_G3dGlbFlushP();
            NNS_G3dGeFlushBuffer();
            G3DDemo_DrawGround(&gGround);
        }

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

        if (isSetup)
        {
            G3DDemo_Printf(16,0, G3DDEMO_COLOR_YELLOW, "TIME:%06d usec", OS_TicksToMicroSeconds(time));
            G3DDemo_Printf(0, 1, G3DDEMO_COLOR_GREEN, "Press A Button to Release");
        }
        else
        {
            G3DDemo_Printf(16,0, G3DDEMO_COLOR_YELLOW, "TIME:           ");
            G3DDemo_Printf(0, 1, G3DDEMO_COLOR_GREEN, "Press A Button to Setup  ");
        }
    }
}


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

  Description:  Initializes G3D library.

  Arguments:    None.

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


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

