/*---------------------------------------------------------------------------*
  Project:  NITRO-System - build - demos - g3d - samples - callback1
  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.15  02/25/2005 03:08:52  takano_makoto
  Warning measures for signed<->unsigned casts

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

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

  Revision 1.12  10/21/2004 02:16:28  nishida_kenji
  Use NNS_G3dRSGetCurrentNodeDescID.

  Revision 1.11  10/20/2004 01:39:07  nishida_kenji
  Use accessors for NNSG3dRS and NNSG3dRenderObj.

  Revision 1.10  09/27/2004 09:58:37  nishida_kenji
  Use accessors for NNSG3dRS.

  Revision 1.9  09/15/2004 05:34:08  nishida_kenji
  reset rs->cbFunc at the end of callback if operation is over.

  Revision 1.8  09/10/2004 09:26:07  nishida_kenji
  rename NNS_G3dGlbFlush to NNS_G3dGlbFlushP.

  Revision 1.7  08/31/2004 05:11:40  nishida_kenji
  small fix

  Revision 1.6  08/05/2004 00:11:32  nishida_kenji
  time NNS_G3dDraw only.

  Revision 1.5  08/02/2004 08:47:14  nishida_kenji
  suppress warnings.

  Revision 1.4  08/02/2004 01:04:36  nishida_kenji
  add introduction.

  Revision 1.3  07/29/2004 04:33:58  nishida_kenji
  revised

  Revision 1.2  2004/07/26 06:47:04  nakajima_souichi
  (none)

  Revision 1.1  07/21/2004 06:45:57  nishida_kenji
  Initial check in.

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

//---------------------------------------------------------------------------
// Sample that customizes G3D drawing operations using callback (Part 1)
//
// You can use NNS_G3dRenderObjSetCallBack to set the callback function
// called during rendering. The NNSG3dRS structure, which is also a callback argument,
// stores the renderer state during rendering.
// It is possible to customize G3D rendering operations by manipulating this structure.
//  
// 
// Sample Operation
// By pushing the X button --> becomes translucent or wireframe depending on the material.
// By pushing the Y button --> the robot's chest area becomes invisible.
// By pushing the A button --> the robot's left arm becomes twice as large.
//
// HOWTO:
// 1: Sets, using the NNS_G3dRenderObjSetCallback function, the callback function
//    set in NNSG3dRenderObj, the type and address of the SBC instruction, and the call  timing in the specified SBC instruction.
// 2: The callback function takes the pointer to the NNSG3dRS structure in the argument, and has no return value.
// 3: It is possible to customize G3D rendering operations by accessing the NNSG3dRS structure inside the callback function.
//      
// 3a: With the NNS_G3D_SBC_CALLBACK_TIMING_B of the NNS_G3D_SBC_NODE, you can control whether the shape
//     belonging to the node is visible or invisible (visibility) by manipulating the contents of the NNSG3dVisAnmResult structure.
//      
// 3b: With the NNS_G3D_SBC_CALLBACK_TIMING_B of the NNS_G3D_SBC_MAT, you can change the material characteristics
//     by managing the contents of the NNSG3dMatAnmResult structure.
// 3c: With the NNS_G3D_SBC_CALLBACK_TIMING_B of the NNS_G3D_SBC_NODEDESC, you can change the joint position, direction,
//     and scale by manipulating the contents of the NNSG3dJntAnmResult structure.
//
//---------------------------------------------------------------------------


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


/* -------------------------------------------------------------------------
    eraseWaist

    This callback is put in the SBC's NODE command (the NNSi_G3dFuncSbc_NODE function).
    By using NODE commands register callbacks with timing A, timing B you can
    delete or change the visibility calculation results.
    Use NNS_G3dRSGetVisAnmResult(rs)n to access the NNSG3dVisAnmResul structure which
    stores the visibility calculation results.

    When called, will not display is the rendering node name if "chest."
 ------------------------------------------------------------------------- */
static void
eraseWaist(NNSG3dRS* rs)
{
    int jntID;
    NNS_G3D_GET_JNTID(NNS_G3dRenderObjGetResMdl(NNS_G3dRSGetRenderObj(rs)),
                      &jntID,
                      "chest");
    SDK_ASSERT(jntID >= 0);

    if (NNS_G3dRSGetCurrentNodeID(rs) == jntID)
    {
        NNSG3dVisAnmResult* visResult = NNS_G3dRSGetVisAnmResult(rs);
        visResult->isVisible = FALSE;
        //
        // If the callback does not need to be used for anything other than
        // the above, the impact on performance can be reduced somewhat by resetting.
        //
        NNS_G3dRSResetCallBack(rs, NNS_G3D_SBC_NODE);
    }
}


/* -------------------------------------------------------------------------
    getTranslucent

    This callback is put in the SBC's MAT command (the NNSi_G3dFuncSbc_MAT function).
    By using MAT commands to register callbacks with timing A, timing B you can
    delete or change the material calculation results.
    Use NNS_G3dRSGetMatAnmResult(rs) to access the NNSG3dMatAnmResult structure which
    stores the material calculation results.

    Displays as translucent if the render material name is "lambert2," otherwise
    displays as wireframe.
 ------------------------------------------------------------------------- */
static void
getTranslucent(NNSG3dRS* rs)
{
    int matID;
    NNSG3dMatAnmResult* matResult;
    NNS_G3D_GET_MATID(NNS_G3dRenderObjGetResMdl(NNS_G3dRSGetRenderObj(rs)),
                      &matID,
                      "lambert2");
    SDK_ASSERT(matID >= 0);

    matResult = NNS_G3dRSGetMatAnmResult(rs);

    if (NNS_G3dRSGetCurrentMatID(rs) == matID)
    {
        matResult->prmPolygonAttr =
            (matResult->prmPolygonAttr & ~REG_G3_POLYGON_ATTR_ALPHA_MASK) |
            (16 << REG_G3_POLYGON_ATTR_ALPHA_SHIFT);
    }
    else
    {
        matResult->flag |= NNS_G3D_MATANM_RESULTFLAG_WIREFRAME;
    }
}


/* -------------------------------------------------------------------------
    scaleArm

    This callback is put in the SBC's NODEDESC command (the NNSi_G3dFuncSbc_NODEDESC function).
    With the NODEDESC command, you can create and change the joint calculation results
    by registering the callback in timing A and timing B.
    Use NNS_G3dRSGetJntAnmResult(rs) to access the NNSG3dJntAnmResult structure which
    stores the joint calculation results.

    The first argument of the NODEDESC command is the joint ID calculated by the NODEDESC command.
    The scale is 2x only when calculating left_upper_arm.
 ------------------------------------------------------------------------- */
static void
scaleArm(NNSG3dRS* rs)
{
    int jntID;
    
    NNS_G3D_GET_JNTID(NNS_G3dRenderObjGetResMdl(NNS_G3dRSGetRenderObj(rs)),
                      &jntID,
                      "left_upper_arm");
    SDK_ASSERT(jntID >= 0);

    // Get the first argument of the NODEDESC command
    if (NNS_G3dRSGetCurrentNodeDescID(rs) == jntID)
    {
        NNSG3dJntAnmResult* jntResult;
        jntResult = NNS_G3dRSGetJntAnmResult(rs);

        jntResult->flag &= ~NNS_G3D_JNTANM_RESULTFLAG_SCALE_ONE;
        jntResult->scale.x = 2 * FX32_ONE;
        jntResult->scale.y = 2 * FX32_ONE;
        jntResult->scale.z = 2 * FX32_ONE;

        //
        // If the callback does not need to be used for anything other than
        // the above, the impact on performance can be reduced somewhat by  resetting.
        //
        NNS_G3dRSResetCallBack(rs, NNS_G3D_SBC_NODEDESC);
    }
}


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

  Description:  Sample main.

  Arguments:    None.

  Returns:      None.
   ------------------------------------------------------------------------- */
void
NitroMain(void)
{
    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/robot_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, 0, 5*FX32_ONE);
    G3DDemo_InitGround(&gGround, (fx32)(1.5*FX32_ONE));

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

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

            //
            // Set the callback funtion that is set by the button that is pressed,
            // the SBC command that enters the callback, and the command's internal timing.
            //
            if (G3DDEMO_IS_PAD_PRESS(PAD_BUTTON_Y))
            {
                // If the Y Button is pressed, the waist nodes will be hidden.
                // Invoke the callback with the NODE command's timing B.
                NNS_G3dRenderObjSetCallBack(&object,
                                            &eraseWaist,
                                            NULL,
                                            NNS_G3D_SBC_NODE,
                                            NNS_G3D_SBC_CALLBACK_TIMING_B);
            }
            else if (G3DDEMO_IS_PAD_PRESS(PAD_BUTTON_X))
            {
                // If the X Button is pressed, the material will become translucent and show the wireframe.
                // Invoke the callback with the MAT command's timing B.
                NNS_G3dRenderObjSetCallBack(&object,
                                            &getTranslucent,
                                            NULL,
                                            NNS_G3D_SBC_MAT,
                                            NNS_G3D_SBC_CALLBACK_TIMING_B);
            }
            else if (G3DDEMO_IS_PAD_PRESS(PAD_BUTTON_A))
            {
                // If the A Button is pressed, the left arm will double in size.
                // Invoke the callback with NODEDESC command's timing B.
                NNS_G3dRenderObjSetCallBack(&object,
                                            &scaleArm,
                                            NULL,
                                            NNS_G3D_SBC_NODEDESC,
                                            NNS_G3D_SBC_CALLBACK_TIMING_B);
            }
            else
            {
                // Reset callback settings if no button gets pressed
                NNS_G3dRenderObjResetCallBack(&object);
            }

            //
            // 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 measurement.
            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);


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

