/*---------------------------------------------------------------------------*
  Project:  NITRO-System - build - demos - g3d - samples - MultiModel
  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.

  $Id: main.c,v 1.11 2005/02/25 03:10:32 takano_makoto Exp $
  $NoKeywords: $
 *---------------------------------------------------------------------------*/

//---------------------------------------------------------------------------
//
// Sample for when multiple models are in one .nsbmd file
//
// If multiple .imd files are given as input to the g3dcvtr, the multiple models can be collected in a single .nsbmd file.
//   When this is done, it's possible to gather textures and palettes that have the same name and content in one place,
// and thereby reduce the amount of memory used.
//
// Sample Operation
// Specifies index from model set in resource by NNS_G3dGetMdlByIdx,
// and gets data of each model resource. Each model resource shares the texture.
// (You can confirm using g3dcvtr ./data/two_cubes.nsbmd.)
//
// HOWTO
// 1: For example, if you execute g3dcvtr file1.imd file2.imd ... fileN.imd -o files.nsbmd,
//    fileN models from file1 are collected in one file called files.nsbmd.
//    In this case, textures and palettes with the same name and contents are collected in one texture/palette.
// 2: When the texture/palette given by NNS_G3dResDefaultSetup is loaded into VRAM,
//    they are bound to each model resource.
// 3: With the NNS_G3dGetMdlByIdx function, you can specify the index and get each model resource.
//    Also, by using the NNS_G3D_GET_MDL macro (defined by util.h), you can specify as a literal character string
//    the part that removed the extension from the .imd file name.
//
//---------------------------------------------------------------------------

#include "g3d_demolib.h"

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

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


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

  Description:  Sample main.

  Arguments:    None.

  Returns:      None.
   ------------------------------------------------------------------------- */
void
NitroMain(void)
{
    //  there are two models
    NNSG3dRenderObj object1,object2;
    NNSG3dResMdl*    model1 = NULL;
    NNSG3dResMdl*    model2 = NULL;

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

    //  get two models from one binary
    LoadG3DModel("data/two_cubes.nsbmd", &model1, &model2);
    SDK_ASSERTMSG(model1, "model1 load failed");
    SDK_ASSERTMSG(model2, "model2 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(&object1, model1);
    NNS_G3dRenderObjInit(&object2, model2);

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

    G3DDemo_InitConsole();
    G3DDemo_Print(0,0, G3DDEMO_COLOR_YELLOW, "MULTI MODEL");

    for(;;)
    {
        OSTick time;

        SVC_WaitVBlankIntr();

        G3DDemo_PrintApplyToHW();
        G3DDemo_ReadGamePad();

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

        G3X_Reset();
        G3X_ResetMtxStack();

        SetCamera(&gCamera);

        {
            //
            // object 1 setup and drawing
            //
            VecFx32 scale = {FX32_ONE << 2, FX32_ONE << 2, FX32_ONE << 2};
            VecFx32 trans = {FX32_ONE * 4, 0, 0};

            // apply scaling and enlarge
            NNS_G3dGlbSetBaseScale(&scale);

            //  move slightly to the right
            NNS_G3dGlbSetBaseTrans(&trans);

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

            // Start time measurement
            time = OS_GetTick();
            {
                //
                // G3D: NNS_G3dDraw
                // Performs drawing for all instances by passing the
                // NNSG3dRenderObj that was set up.
                //
                NNS_G3dDraw(&object1);
            }
            // Time measurement result
            time = OS_GetTick() - time;
        }

        {
            //
            // object2 setup and drawing
            //
            VecFx32 trans = {-FX32_ONE * 4, 0, 0};
            // Makes scale the same as the setting when object1 was drawn.
            
            //  move a little to the left
            NNS_G3dGlbSetBaseTrans(&trans);

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

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

        {
            // drawing the ground
            // First, restores scale and trans.
            VecFx32 scale = {FX32_ONE, FX32_ONE, FX32_ONE};
            VecFx32 trans = {0, 0, 0};

            NNS_G3dGlbSetBaseScale(&scale);
            NNS_G3dGlbSetBaseTrans(&trans);
            //
            // 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);


        G3DDemo_Printf(16,0, G3DDEMO_COLOR_YELLOW, "TIME:%06d 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.
                    pModel1,pModel2: address of the pointer specifying the model

  Returns:      None.
   ------------------------------------------------------------------------- */
static void
LoadG3DModel(const char* path, NNSG3dResMdl** pModel1, NNSG3dResMdl** pModel2)
{
    BOOL status;
    NNSG3dResTex*        texture = NULL;
    NNSG3dResFileHeader* resFile = G3DDemo_LoadFile(path);

    SDK_NULL_ASSERT(resFile);

    // when calling the default initialization function and setting 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.
    // --------------------------------------------------------------------
    //  get two models from one binary
    //  texture differs only for the blue star and red circle part of die 1
    //  control amount of data by sharing
    *pModel1 = NNS_G3dGetMdlByIdx(NNS_G3dGetMdlSet(resFile), 0);
    *pModel2 = NNS_G3dGetMdlByIdx(NNS_G3dGetMdlSet(resFile), 1);
}


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

