/*---------------------------------------------------------------------------*
  Project:  NITRO-System - build - demos - g3d - samples - ModifyNsbmd
  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  06/02/2005 10:41:39  nishida
  Corrected to enable build in ProDG.

  Revision 1.10  02/25/2005 03:10:19  takano_makoto
  Warning measures for signed<->unsigned casts

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

  Revision 1.8  11/04/2004 10:33:25  nishida
  Revised function header.

  Revision 1.7  09/10/2004 09:48:51  nishida_kenji
  rename NNS_G3dGlbFlush to NNS_G3dGlbFlushP.

  Revision 1.6  08/05/2004 00:12:07  nishida_kenji
  time NNS_G3dDraw only.

  Revision 1.5  08/02/2004 07:20:15  nishida_kenji
  add introduction.

  Revision 1.4  07/29/2004 02:43:48  nishida_kenji
  revised

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

//---------------------------------------------------------------------------
//
// Sample of rewriting data in model resource
//
// In G3D library, the data in model resource is only rewritten during initialization and termination.
// Although not overwritten, it can be directly overwritten if the user explicitly writes the code.
//  In this sample, functions declared in model.h for G3D are used to access each attribute in a material.
//  
//
// Sample Operation
// Material data in model resource changes with each frame.
// Move the menu (each material attribute) up and down with the B and X buttons.
// With the A button in material data, set whether to use the global information
// set with the NNSG3dGlbXXXX function instead of the material data in model resource.
// Push the button to switch.
//
// HOWTO:
// * Use NNS_G3dMdlUseMdlXXXX to reflect in the drawing the material data in model resource (default).
// * Use NNS_G3dMdlUseGlbXXXX to reflect in the drawing the material data set with the NSG3dGlbXXXX function.
// * Use NNS_G3dMdlSetMdlXXXX to rewrite the material data in the model resource.
// * Use NNS_G3dMdlGetMdlXXXX to get the material data in model resource.
//
//---------------------------------------------------------------------------

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

//  timer for time changing model material
static int mdlTimer = 0;
static void changeMdlMat(NNSG3dResMdl* pMdl);
static GXRgb calcNextColor(GXRgb rgb);

#define	NUM_ITEM	9


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

  Description:  Sample main.
                select item with X and B; switch between Glb and Mdl with A

  Arguments:    None.

  Returns:      None.
   ------------------------------------------------------------------------- */
void
NitroMain(void)
{
    //  variable definition of item relation
//  const int NumItem = 9;

    int itemNo = 0;
    const char* ItemName[NUM_ITEM][2] = {
        {"MdlDiff","GlbDiff"},
        {"MdlAmb","GlbAmb"},
        {"MdlSpec","GlbSpec"},
        {"MdlEmi","GlbEmi"},
        {"MdlLightFlag","GlbLightFlag"},
        {"MdlPolyMode","GlbPolyMode"},
        {"MdlCullMode","GlbCullMode"},
        {"MdlPolyID","GlbPolyID"},
        {"MdlAlpha","GlbAlpha"}
    };
    int itemFlag[NUM_ITEM] = {
        0,0,0,0,0,0,0,0,0
    };
    

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

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

    for(;;)
    {
        OSTick time;

        SVC_WaitVBlankIntr();

        G3DDemo_PrintApplyToHW();
        G3DDemo_ReadGamePad();
        
        //  Sets whether to use global parameters or model parameters.
        //  use function declared by model.h
        if (itemFlag[0] == 0)
            NNS_G3dMdlUseMdlDiff(model);
        else
            NNS_G3dMdlUseGlbDiff(model);

        if (itemFlag[1] == 0)
            NNS_G3dMdlUseMdlAmb(model);
        else
            NNS_G3dMdlUseGlbAmb(model);

        if (itemFlag[2] == 0)
            NNS_G3dMdlUseMdlSpec(model);
        else
            NNS_G3dMdlUseGlbSpec(model);

        if (itemFlag[3] == 0)
            NNS_G3dMdlUseMdlEmi(model);
        else
            NNS_G3dMdlUseGlbEmi(model);

        if (itemFlag[4] == 0)
            NNS_G3dMdlUseMdlLightEnableFlag(model);
        else
            NNS_G3dMdlUseGlbLightEnableFlag(model);

        if (itemFlag[5] == 0)
            NNS_G3dMdlUseMdlPolygonMode(model);
        else
            NNS_G3dMdlUseGlbPolygonMode(model);

        if (itemFlag[6] == 0)
            NNS_G3dMdlUseMdlCullMode(model);
        else
            NNS_G3dMdlUseGlbCullMode(model);

        if (itemFlag[7] == 0)
            NNS_G3dMdlUseMdlPolygonID(model);
        else
            NNS_G3dMdlUseGlbPolygonID(model);

        if (itemFlag[8] == 0)
            NNS_G3dMdlUseMdlAlpha(model);
        else
            NNS_G3dMdlUseGlbAlpha(model);

        //  time convert the parameters of model material
        changeMdlMat(model);

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

        G3X_Reset();
        G3X_ResetMtxStack();

        SetCamera(&gCamera);

        {
            VecFx32 scale = {FX32_ONE >> 2, FX32_ONE >> 2, FX32_ONE >> 2};
            VecFx32 trans = {FX32_ONE * 4, 0, 0};

            //
            // set model base scale and base translation
            //
            NNS_G3dGlbSetBaseScale(&scale);
            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(&object);
            }
            // Time measurement results screen display.
            time = OS_GetTick() - time;
        }

        // restore scaling
        {
            VecFx32 scale = {FX32_ONE, FX32_ONE, FX32_ONE};
            NNS_G3dGlbSetBaseScale(&scale);
        }
        //  also restore trans
        {
            VecFx32 trans = {0, 0, 0};
            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);
        
        
        //  switch selected item with X and B; use button to change
        {
            if(G3DDEMO_IS_PAD_TRIGGER(PAD_BUTTON_X))
            {
                itemNo --;
                if(itemNo < 0)
                {
                    itemNo = NUM_ITEM - 1;
                }
            }
            if(G3DDEMO_IS_PAD_TRIGGER(PAD_BUTTON_B))
            {
                itemNo ++;
                if(itemNo >= NUM_ITEM)
                {
                    itemNo = 0;
                }
            }
            if(G3DDEMO_IS_PAD_TRIGGER(PAD_BUTTON_A))
            {
                itemFlag[itemNo] = 1 - itemFlag[itemNo];
            }
        }
        
        
        //  display item's current state
        {
            int item;
            G3DDemo_Printf(0,2, G3DDEMO_COLOR_GREEN, "select:X,B");
            G3DDemo_Printf(0,3, G3DDEMO_COLOR_GREEN, "change:A");
            for(item = 0;item < NUM_ITEM;item ++)
            {
                int color = G3DDEMO_COLOR_WHITE;
                if(item == itemNo)
                {
                    color = G3DDEMO_COLOR_RED;
                }
                G3DDemo_Printf(0,5 + item, color, "%s", ItemName[item][itemFlag[item]]);
            }
        }

        G3DDemo_Printf(16,0, G3DDEMO_COLOR_YELLOW, "TIME:%06d usec", OS_TicksToMicroSeconds(time));
    }
}


/* -------------------------------------------------------------------------
  Name:         changeMdlMat

  Description:  time converts the settings of model material

  Arguments:    pointer to model:NNSG3dResMdl* pMdl

  Returns:      None.
   ------------------------------------------------------------------------- */
void
changeMdlMat(NNSG3dResMdl* pMdl)
{
    u32 MatID;

    mdlTimer ++;
    if(mdlTimer >= 32 * 32 * 32)
    {
        mdlTimer = 0;
    }

    for (MatID = 0; MatID < pMdl->info.numMat; ++MatID)
    {
        //
        // Changes model material color with each frame.
        //
        NNS_G3dMdlSetMdlDiff(pMdl,
                             MatID,
                             calcNextColor(NNS_G3dMdlGetMdlDiff(pMdl, MatID)));

        NNS_G3dMdlSetMdlAmb(pMdl,
                            MatID,
                            calcNextColor(NNS_G3dMdlGetMdlAmb(pMdl, MatID)));

        NNS_G3dMdlSetMdlSpec(pMdl,
                             MatID,
                             calcNextColor(NNS_G3dMdlGetMdlSpec(pMdl, MatID)));

        NNS_G3dMdlSetMdlEmi(pMdl,
                            MatID,
                            calcNextColor(NNS_G3dMdlGetMdlEmi(pMdl, MatID)));

        //
        // Changes the model light ON/OFF.
        //
        NNS_G3dMdlSetMdlLightEnableFlag(pMdl,
                                        MatID,
                                        mdlTimer / 55 % 16);

        //
        // Changes polygon mode of model.
        //
        NNS_G3dMdlSetMdlPolygonMode(pMdl,
                                    MatID,
                                    (GXPolygonMode)(mdlTimer / 60 % 2));

        //
        // Changes model cull mode.
        //
        NNS_G3dMdlSetMdlCullMode(pMdl,
                                 MatID,
                                 (GXCull)(mdlTimer / 73 % 2 + 1));

        //
        // Sets model polygon ID.
        //
        NNS_G3dMdlSetMdlPolygonID(pMdl,
                                  MatID,
                                  31);

        //
        // Changes model alpha.
        //
        NNS_G3dMdlSetMdlAlpha(pMdl,
                              MatID,
                              mdlTimer / 7 % 32);
    }
}


/* -------------------------------------------------------------------------
  Name:         calcNextColor

  Description:  calculates the next color from the current color in order to change the material color

  Arguments:    current color:GXRgb rgb

  Returns:      next color:GXRgb
   ------------------------------------------------------------------------- */
GXRgb
calcNextColor(GXRgb rgb) {
    //  extraction of each component
    u16 R = (u16)((rgb & GX_RGB_R_MASK) >> GX_RGB_R_SHIFT);
    u16 G = (u16)((rgb & GX_RGB_G_MASK) >> GX_RGB_G_SHIFT);
    u16 B = (u16)((rgb & GX_RGB_B_MASK) >> GX_RGB_B_SHIFT);
    
    const int ChangeSpeed = 2;
    
    R += ChangeSpeed;
    if(R > 31)
    {
        R -= 31;
        G += ChangeSpeed;
        if(G > 31)
        {
            G -= 31;
            B += ChangeSpeed;
            if(B > 31)
            {
                B -= ChangeSpeed;
            }
        }
    }

    return GX_RGB(R, G, B);
}


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

  Description:  Initializes G3D library.

  Arguments:    None.

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

    //
    // G3D:
    // global light settings
    // ------------------------------------------------------------------------
    NNS_G3dGlbLightVector(GX_LIGHTID_0, -FX16_SQRT1_3, -FX16_SQRT1_3, -FX16_SQRT1_3);
    NNS_G3dGlbLightVector(GX_LIGHTID_1,  FX16_SQRT1_3,  FX16_SQRT1_3,  FX16_SQRT1_3);
    NNS_G3dGlbLightVector(GX_LIGHTID_2,             0,   -FX16_ONE  ,             0);
    NNS_G3dGlbLightVector(GX_LIGHTID_3,             0,    FX16_ONE-1,             0);

    NNS_G3dGlbLightColor(GX_LIGHTID_0, GX_RGB(31, 31, 31));
    NNS_G3dGlbLightColor(GX_LIGHTID_1, GX_RGB(10, 10, 10));
    NNS_G3dGlbLightColor(GX_LIGHTID_2, GX_RGB(16,  0,  0));
    NNS_G3dGlbLightColor(GX_LIGHTID_3, GX_RGB(16, 16,  0));

    NNS_G3dGlbMaterialColorDiffAmb(GX_RGB(20, 10, 10), GX_RGB(6, 6, 6), FALSE);
    NNS_G3dGlbMaterialColorSpecEmi(GX_RGB(10, 16, 0), GX_RGB(6, 6, 14), FALSE);
    
    //  global polygon attribute settings
    NNS_G3dGlbPolygonAttr(
            0x000f,                                   // all lights ON
            GX_POLYGONMODE_MODULATE,
            GX_CULL_BACK,
            0,
            28,
            GX_POLYGON_ATTR_MISC_XLU_DEPTH_UPDATE
    );
}


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

  Description:  Reads model data from file.

  Arguments:    path: Model data path name.

  Returns:      Pointer to model.
   ------------------------------------------------------------------------- */
static NNSG3dResMdl*
LoadG3DModel(const char* path)
{
    BOOL status;
    NNSG3dResMdl* model = NULL;
    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.
    // --------------------------------------------------------------------
    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);
}

