/*---------------------------------------------------------------------------*
  Project:	NITRO-System - build - demos - g3d - samples - ScreenUtil
  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.4  02/25/2005 03:11:12  takano_makoto
  Warning measures for signed<->unsigned casts

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

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

  Revision 1.1  11/02/2004 07:55:25  nishida_kenji
  Initial check in.

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

//---------------------------------------------------------------------------
//
// Sample that uses the G3D coordinate conversion utility API
//
// By performing the coordinate conversion with the BG screen coordinates,
// it is possible to find which point in the world coordinate correspond to the point in BG screen
// or which point in the BG screen coordinates corresponds to a line in the world coordinate.
//
// Sample Operation
// Displays the coordinate point that corresponds to the origin of the world coordinate on the BG screen, and
// displays the coordinate points that corresponds to each of the joints of the runner on the BG screen .
// Touch the panel to display the coordinates of the touched point on the BG screen and
// The corresponding point on the near plane and point on the far plane are displayed in world coordinates.
//   
//
// HOWTO:
// 1: Use the NNS_G3dWorldPosToScrPos function to convert the point in the world coordinate
//    to a point in the BG screen coordinate system.
// 2: Use the NNS_G3dLocalOriginToScrPos function to convert the origin in the local coordinate
//    system to the BG screen coordinate system.
// 3: Use the NNS_G3dScrPosToWorldLine function to convert the point in the BG screen coordinate
//    system to a line in the world coordinate. A line is returned as two points, which are the point
//    on the NEAR clip plane and FAR clip plane.
// 
//---------------------------------------------------------------------------

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


#define NUMJNT 24
struct
{
    BOOL isOnScreen;
    int  x;
    int  y;
}
ScrPosJnt[NUMJNT];

//
// Obtain the location of each joint on the BG screen
//
static void
getScrPos(NNSG3dRS* rs)
{
    int jntID;
    int x, y, r;

    jntID = NNS_G3dRSGetCurrentNodeDescID(rs);
    r = NNS_G3dLocalOriginToScrPos(&x, &y);
    ScrPosJnt[jntID].isOnScreen = (!r);
    ScrPosJnt[jntID].x = x;
    ScrPosJnt[jntID].y = y;
}

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

  Description:  Sample main.

  Arguments:    None.

  Returns:      None.
   ------------------------------------------------------------------------- */
void
NitroMain(void)
{
    NNSG3dRenderObj object;
    NNSG3dResMdl*   pModel;
    void*           pAnmRes;
    NNSFndAllocator allocator;
    NNSG3dAnmObj*   pAnmObj;

    G3DDemo_InitSystem();
    G3DDemo_InitMemory();
    G3DDemo_InitVRAM();

    InitG3DLib();
    G3DDemo_InitDisplay();

    //
    // Initialize the touch panel because this sample uses the touch panel
    //
    {
        BOOL result;
        TPCalibrateParam calibrate;

        // Initialize the touch panel API
        TP_Init();

        // Takes calibration parameter from the flash memory
        result = TP_GetUserInfo( &calibrate );

        if (!result)
        {
            // If the valid value was not entered, ask the user to calibrate and
            // quit the program.
            OS_Printf("Calibration please\n");
            return;
        }

        // Set the calibration parameter to the touch panel API.
        TP_SetCalibrateParam( &calibrate );
    }

    //
    // create allocator with 4-byte alignment
    //
    NNS_FndInitAllocatorForExpHeap(&allocator, G3DDemo_AppHeap, 4);

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

    pModel = LoadG3DModel("data/human_run_t.nsbmd");
    SDK_ASSERTMSG(pModel, "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, pModel);
    
    //
    // Load the joint animation and try making associations.
    //
    {
        const u8* pFile = G3DDemo_LoadFile("data/human_run_t.nsbca");
        SDK_ASSERTMSG( pFile, "anm load failed" );
        {
            // initialize animation, add to rendering object
            // 
            // Allocate and initialize the animation object (NNSG3dAnmObj) that goes between
            // the loaded animation file and NNSG3dRenderObj.
            // All animation is set up with the same code.
            //

            //
            // Allocates from the allocator the memory necessary for the animation object.
            // You must specify and allocate model resources and animation resources.
            //

            // specify index #0 animation
            pAnmRes = NNS_G3dGetAnmByIdx(pFile, 0);
            SDK_NULL_ASSERT(pAnmRes);

            // Allocates the necessary amount of memory. Initialize must be separate.
            pAnmObj = NNS_G3dAllocAnmObj(&allocator, // specify the allocator to use
                                         pAnmRes,    // specify animation resource
                                         pModel);    // specify model resource
            SDK_NULL_ASSERT(pAnmObj);

            //
            // Initialize AnmObj. Same for others that are not joint animation
            //
            NNS_G3dAnmObjInit(pAnmObj, // pointer to animation object
                              pAnmRes, // pointer to animation resource
                              pModel,  // pointer to NNSG3dResMdl
                              NULL );  // pointer to NNSG3dResTex (NULL is OK except for texture pattern animation)

            //
            // Add AnmObj to RenderObj. Same for others that are not joint animation
            //
            NNS_G3dRenderObjAddAnmObj( &object, pAnmObj );

            //
            // Set the callback
            //
            NNS_G3dRenderObjSetCallBack(&object,
                                        &getScrPos,
                                        NULL,
                                        NNS_G3D_SBC_NODEDESC,
                                        NNS_G3D_SBC_CALLBACK_TIMING_C);
        }
    }
    
    //
    // demo common initialization
    //
    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, "ScreenUtil");

    for(;;)
    {
        OSTick time;

        SVC_WaitVBlankIntr();

        G3DDemo_PrintApplyToHW();
        G3DDemo_ReadGamePad();

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

        G3X_Reset();
        G3X_ResetMtxStack();

        SetCamera(&gCamera);

        //
        // Update animation frame.
        //
        pAnmObj->frame += FX32_ONE;
        if( pAnmObj->frame >= NNS_G3dAnmObjGetNumFrame(pAnmObj))
        {
            pAnmObj->frame = 0;
        }

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

            //  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 result
            time = OS_GetTick() - time;
        }

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

        {
            int x, y, r;
            VecFx32 worldOrig = {0, 0, 0};
            r = NNS_G3dWorldPosToScrPos(&worldOrig, &x, &y);
            if (!r)
            {
                G3DDemo_Printf(0, 2, G3DDEMO_COLOR_GREEN, "World Origin -> (%3d,%3d)", x, y);
            }
            else
            {
                G3DDemo_Printf(0, 2, G3DDEMO_COLOR_GREEN, "World Origin -> ---------");
            }
        }

        {
            TPData tp;
            if (!TP_RequestCalibratedSampling(&tp) &&
                tp.touch                           &&
                tp.validity == TP_VALIDITY_VALID)
            {
                VecFx32 near, far;
                int x, y;
                x = tp.x;
                y = tp.y;
                G3DDemo_Printf(0, 5, G3DDEMO_COLOR_GREEN, "Touch = (%3d, %3d)", x, y);

                (void)NNS_G3dScrPosToWorldLine(x, y, &near, &far);
                G3DDemo_Printf(0, 6, G3DDEMO_COLOR_GREEN, "Point on near plane(World)");
                G3DDemo_Printf(0, 7, G3DDEMO_COLOR_GREEN, "  (%7.2f, %7.2f, %7.2f)", near.x / 4096.f, near.y / 4096.f, near.z / 4096.f);
                
                G3DDemo_Printf(0, 8, G3DDEMO_COLOR_GREEN, "Point on far plane(World)");
                G3DDemo_Printf(0, 9, G3DDEMO_COLOR_GREEN, "  (%7.2f, %7.2f, %7.2f)", far.x / 4096.f, far.y / 4096.f, far.z / 4096.f);
            }
            else
            {
                G3DDemo_Printf(0, 5, G3DDEMO_COLOR_GREEN, "No touch          ");
                G3DDemo_Printf(0, 6, G3DDEMO_COLOR_GREEN, "                          ");
                G3DDemo_Printf(0, 7, G3DDEMO_COLOR_GREEN, "                                ");
                G3DDemo_Printf(0, 8, G3DDEMO_COLOR_GREEN, "                          ");
                G3DDemo_Printf(0, 9, G3DDEMO_COLOR_GREEN, "                                ");
            }
        }

        {
            //
            // Display the location of each joint on the screen
            //
            int i;
            for (i = 0; i < NUMJNT / 2; i++)
            {
                if (ScrPosJnt[2*i].isOnScreen)
                    G3DDemo_Printf( 0, i + 11, G3DDEMO_COLOR_GREEN, "Jnt%02d (%3d,%3d)", 2*i, ScrPosJnt[2*i].x, ScrPosJnt[2*i].y);
                else
                    G3DDemo_Printf( 0, i + 11, G3DDEMO_COLOR_GREEN, "Jnt%02d ---------", 2*i);

                if (ScrPosJnt[2*i+1].isOnScreen)
                    G3DDemo_Printf(16, i + 11, G3DDEMO_COLOR_GREEN, "Jnt%02d (%3d,%3d)", 2*i+1, ScrPosJnt[2*i+1].x, ScrPosJnt[2*i+1].y);
                else
                    G3DDemo_Printf(16, i + 11, G3DDEMO_COLOR_GREEN, "Jnt%02d ---------", 2*i+1);
            }
        }

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

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

