/*---------------------------------------------------------------------------*
  Project:  TWL-System - demos - g3d - samples - ScreenUtil
  File:     main.c

  Copyright 2004-2009 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.

  $Revision: 1155 $
 *---------------------------------------------------------------------------*/

//---------------------------------------------------------------------------
//
// This sample uses the G3D coordinate conversion utility APIs.
//
// By converting the background screen coordinates, you can determine where world coordinates correspond to points on the background screen, and which line in world coordinates correspond to points in background screen coordinates.
// 
// 
//
// Sample Operation
// Displays the background screen coordinates corresponding the origin in the world coordinate system, and the background screen coordinates corresponding to the position of each joint for the running person.
// 
// 
// Touching the Touch Panel displays the background screen coordinates for the location you touched as well as the corresponding point on the near and far planes in world coordinates.
// 
//
// HOWTO:
// 1: The NNS_G3dWorldPosToScrPos function can be used to convert a point in world coordinates to a point in background screen coordinates.
//    
// 2: The NNS_G3dLocalOriginToScrPos function can be used to convert the origin in the local coordinate system to a point in background screen coordinates.
//    
// 3: The NNS_G3dScrPosToWorldLine function can be used to convert a point in background screen coordinates to a line in world coordinates.
//    A line is returned as two points, one of which is in the near clipping plane, the other, the far clipping 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];

//
// Get 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 Screen because this sample uses the Touch Screen
    //
    {
        BOOL result;
        TPCalibrateParam calibrate;

        // Initialize the Touch Screen API
        TP_Init();

        // Take calibration parameters from the flash memory
        result = TP_GetUserInfo( &calibrate );

        if (!result)
        {
            // If valid values have not been entered, the program ends with a request to the user to calibrate.
            // 
            OS_Printf("Calibration please\n");
            return;
        }

        // Set the calibration parameters in the Touch Screen API.
        TP_SetCalibrateParam( &calibrate );
    }

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

    // Make the manager able to manage 4 slots' worth of texture image slots, and set it as the default manager.
    // 
    NNS_GfdInitFrmTexVramManager(4, TRUE);

    // Make the manager able to manage 32 KB of palettes, and set it as 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 a single model, and every type of information related to the animations 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. (But it must be set, not NULL, 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 it to rendering object
            // 
            // 
            // Allocate and initialize the animation object (NNSG3dAnmObj) that acts as an intermediary for the loaded animation file and the NNSG3dRenderObj structure.
            // All animations are set up with the same code.
            //

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

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

            // Allocate the necessary amount of memory. Initialization must be done separately.
            pAnmObj = NNS_G3dAllocAnmObj(&allocator, // Specify the allocator to use
                                         pAnmRes,    // Specify the animation resource
                                         pModel);    // Specify the 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 animations)

            //
            // 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 the 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 states set with G3dGlbXXXX.
            // Camera and projection matrix, and the like, are set.
            //
            NNS_G3dGlbFlushP();

            //  Start measuring the time
            time = OS_GetTick();
            {
                //
                // G3D: NNS_G3dDraw
                // Passing the configured NNSG3dRenderObj structure can perform rendering in any situation.
                // 
                //
                NNS_G3dDraw(&object);
            }
            // Time measurement result
            time = OS_GetTick() - time;
        }

        {
            VecFx32 scale = {FX32_ONE, FX32_ONE, FX32_ONE};
            NNS_G3dGlbSetBaseScale(&scale);
            //
            // G3D: Normally, when any of the G3_XXX functions are called, you have to call the NNS_G3dGeComFlushBuffer function before that and synchronize.
            //      
            //
            NNS_G3dGlbFlushP();
            NNS_G3dGeFlushBuffer();
            G3DDemo_DrawGround(&gGround);
        }

        /* Swap memory 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:  Loads 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);

    // 
    // To use DMA transfers inside functions such as NNS_G3dResDefaultSetup and NNS_G3dDraw, all model resources have to be stored in memory before calling those functions.
    // After a certain size, for large resources it is faster to call DC_StoreAll.
    // For information on DC_StoreRange and DC_StoreAll, see the NITRO-SDK Function Reference Manual.
    DC_StoreRange(resFile, resFile->fileSize);

    status = NNS_G3dResDefaultSetup(resFile);
    NNS_G3D_ASSERTMSG(status, "NNS_G3dResDefaultSetup failed");

    //
    // G3D: Gets model
    // Because nsbmd can include multiple models, gets the pointer to a single model by specifying its index. When there is only one model, use 0.
    // 
    // --------------------------------------------------------------------
    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);
}
