/*---------------------------------------------------------------------------*
  Project:   NITRO-System - build - demos - g2d - samples - RendererCore2
  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  06/29/2005 05:59:56  kitani_toshikazu
  Cleaned up source code and added comments.

  Revision 1.3  05/31/2005 05:27:31  kitani_toshikazu
  Supported build in ProDG environment.

  Revision 1.2  11/15/2004 05:55:32  kitani_toshikazu
  Revised comments, revised debug messages

  Revision 1.1  11/11/2004 05:26:56  kitani_toshikazu
  (none)

  Revision 1.2  11/11/2004 05:22:06  kitani_toshikazu
  Discontinued use of NNS_G2dSetRndCoreOamRegisterFuncEx() function.

  Revision 1.1  11/10/2004 09:26:58  kitani_toshikazu
  Registered initial issue

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

// ============================================================================
//  Explanation of the demo:
//      Sample of cell animation rendering using the RendererCore module
//      
//      This sample demo has almost the exact same contents as RendererCore1.
//
//      This demo illustrates a case where the RendererCore module API is used as necessary while using the Renderer module.
//        
//       (Operations of the type described above are possible because the Renderer module uses the RendererCore module internally.)
//         
//  
//      Renderer module API
//       Renderer Core module API
//       Compares the performance of other renderer APIs.
//
//  Operation Method
//      START button: Switches the cell output method between OBJ and Software Sprite.
//                      A button:
//                      In the case of SoftwareSprite drawing with MakeCellToOams is not done.
//      R Button        Switches the cell displayed. (Cell composed of a normal cell and a single image)
//      L Button        Changes the drawing method.
//      +Control Pad: Moves the cell. (Changes the flip status of the renderer at the same time.)
//                                          This is not reflected with the MakeCellToOams function.))
//
// 
//  With software sprite drawing, the renderer core carries out drawing using a drawing function
//  specialized for cells composed of single images.
//  Therefore, when normal cells are drawn, checking for problems with incorrect drawing can be done.
//  For this situation, use the R Button and try to change  the display cell to a cell composed from a single image.
// ============================================================================

#include <nitro.h>
#include <nnsys/g2d.h>

#include "g2d_demolib.h"


#define SCREEN_WIDTH                256                     // screen width
#define SCREEN_HEIGHT               192                     // screen height

#define CELL_INIT_POS_X             (60 << FX32_SHIFT)     // Initial X position of the cell
#define CELL_INIT_POS_Y             (60 << FX32_SHIFT)     // Initial Y position of the cell

#define INIT_OUTPUT_TYPE            NNS_G2D_SURFACETYPE_MAIN2D   // Initial value for output method

#define NUM_OF_OAM                  128                 // Number of OAMs allocated to OAM manager

#define CHARA_BASE                  0x0                 // Character image base address
#define PLTT_BASE                   0x0                 // Palette base address



//------------------------------------------------------------------------------
// Constants for drawn cell
// NUM_CELL_X * NUM_CELL_Y is the drawn cell number, but if the total OBJ number
// exceeds 128, processing will not stop and an accurate performance comparison
// will not be possible unless MyDrawMethod == MYDRAWMETHOD_CELLTOOAM.
// 
#define NUM_CELL_X 5        
#define NUM_CELL_Y 5
#define STEP_CELL_X 24
#define STEP_CELL_Y 24
  
//------------------------------------------------------------------------------
// structure definitions

typedef struct CellInfo
{
    NNSG2dFVec2                 scale;     // Scale
    NNSG2dFVec2                 pos;       // Position of cell rendering
    NNSG2dCellAnimation*        pCellAnim; // cell animation
} CellInfo;

typedef enum 
{
    MYDRAWMETHOD_RENDERER,
    MYDRAWMETHOD_RENDERERCORE,
    MYDRAWMETHOD_CELLTOOAM,
    MYDRAWMETHOD_MAX

}MyDrawMethod;

const char*         drawTypeStrTbl[MYDRAWMETHOD_MAX]=
{
    "MYDRAWMETHOD_RENDERER    ",
    "MYDRAWMETHOD_RENDERERCORE",
    "MYDRAWMETHOD_CELLTOOAM   ",

};

//------------------------------------------------------------------------------
// Global Variables

static NNSG2dImageProxy             sImageProxy;    // Character/texture proxy for cell
static NNSG2dImagePaletteProxy      sPaletteProxy;  // Palette proxy for cell

static NNSG2dOamManagerInstance*    spCurrentOam;   // Manager of OAM subject to callback processing

static NNSG2dAnimBankData*          spAnimBank;     // Animation Bank
static NNSG2dCellAnimation*         spCellAnim;     // Cell animation


//------------------------------------------------------------------------------
// Prototype declarations

void VBlankIntr(void);
static NNSG2dCellDataBank* LoadResources(void);
static void ProcessInput(CellInfo* pCellInfo, NNSG2dSurfaceType* pOutputType, u16 numCells);
static void MakeAffineMatrix(MtxFx22* pMtx, MtxFx22* pMtx2D, const CellInfo* pCellInfo);
static BOOL CallBackAddOam( const GXOamAttr* pOam, u16 affineIndex, BOOL bDoubleAffine );
static u16 CallBackAddAffine( const MtxFx22* mtx );
static void InitOamManager(NNSG2dOamManagerInstance*   pObjOamManager);
static void InitRenderer(
                NNSG2dRendererInstance* pRenderer,
                NNSG2dRenderSurface*    pSurface);


/*---------------------------------------------------------------------------*
  Name:         CallBackAddOam

  Description:  Function called internally by NNS_G2dDraw* functions to add an OAM .

  Arguments:    pOam :           Pointer to OAM to be added
                affineIndex :    Affine index used by this OAM.
                                May be a value of 32 or larger.
                                If affine is not used,
                                NNS_G2D_OAM_AFFINE_IDX_NONE is specified.
                bDoubleAffine :  Whether in double affine mode.

  Returns:      Returns TRUE if successful; returns FALSE otherwise.
 *---------------------------------------------------------------------------*/
static BOOL CallBackAddOam( const GXOamAttr* pOam, u16 affineIndex, BOOL bDoubleAffine )
{
#pragma unused( bDoubleAffine )

    SDK_NULL_ASSERT( spCurrentOam );
    SDK_NULL_ASSERT( pOam );

    return NNS_G2dEntryOamManagerOamWithAffineIdx( spCurrentOam, pOam, affineIndex );
}

/*---------------------------------------------------------------------------*
  Name:         CallBackAddAffine

  Description:  This function is called to add affine parameters in the
                NNS_G2dDraw* functions.

  Arguments:    mtx :        Pointer to affine transform matrix to be added.

  Returns:      Returns the AffineIndex for referencing the affine parameters
                added.
 *---------------------------------------------------------------------------*/
static u16 CallBackAddAffine( const MtxFx22* mtx )
{
    SDK_NULL_ASSERT( spCurrentOam );
    SDK_NULL_ASSERT( mtx );
    return NNS_G2dEntryOamManagerAffine( spCurrentOam, mtx );
}

/*---------------------------------------------------------------------------*
  Name:         CallBackAddAffine

  Description:  This function is called inside the rendering functions in the Renderer Core in order to add OAM.
                 
                Registers multiple OAMs together.
                It is called once with the timing of EndRendering.

  Arguments:    pOam :           Pointer to the OAM columns to be added.
                num:            Number of OAMs to be added.

  Returns:      Returns TRUE if successful; returns FALSE otherwise.
 *---------------------------------------------------------------------------*/
static BOOL CallBackAddOamBlock( const GXOamAttr* pOam, u16 num )
{
    return NNS_G2dEntryOamManagerOam( spCurrentOam, pOam, num );
}


/*---------------------------------------------------------------------------*
  Name:         InitOamManager

  Description:  This function carries out the initialization of the OAM Manager
                System and one OAM Manager.

  Arguments:    pOamManager:    Pointer to the OAM manager to be initialized.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void InitOamManager(NNSG2dOamManagerInstance*   pOamManager)
{
    BOOL result;
    SDK_NULL_ASSERT( pOamManager );

    NNS_G2dInitOamManagerModule();

	result = NNS_G2dGetNewOamManagerInstanceAsFastTransferMode(
		        pOamManager, 0, NUM_OF_OAM, NNS_G2D_OAMTYPE_MAIN );
    SDK_ASSERT( result );
}

//------------------------------------------------------------------------------
static void ResetRendererSurface( NNSG2dRenderSurface*    pSurface )
{
    SDK_NULL_ASSERT( pSurface );
    {
        NNSG2dViewRect* pRect = &(pSurface->viewRect);
        
        pRect->posTopLeft.x = FX32_ONE * 0;
        pRect->posTopLeft.y = FX32_ONE * 0;
        pRect->sizeView.x = FX32_ONE * SCREEN_WIDTH;
        pRect->sizeView.y = FX32_ONE * SCREEN_HEIGHT;

        pSurface->pFuncOamRegister       = CallBackAddOam;
        pSurface->pFuncOamAffineRegister = CallBackAddAffine;
        
        pSurface->type                   = NNS_G2D_SURFACETYPE_MAIN2D;
    }
    
}

/*---------------------------------------------------------------------------*
  Name:         InitRenderer

  Description:  Initializes Renderer and Surface.

  Arguments:    pRenderer:  Pointer to Renderer to be initialized.
                pSurface:   Pointer to Surface to be initialized.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void InitRenderer(
                NNSG2dRendererInstance* pRenderer,
                NNSG2dRenderSurface*    pSurface )
{
    NNSG2dViewRect* pRect = &(pSurface->viewRect);
    SDK_NULL_ASSERT( pRenderer );
    SDK_NULL_ASSERT( pSurface );

    NNS_G2dInitRenderer( pRenderer );
    
    NNS_G2dInitRenderSurface( pSurface );
    
    ResetRendererSurface( pSurface );
    
    NNS_G2dAddRendererTargetSurface( pRenderer, pSurface );
    NNS_G2dSetRendererImageProxy( pRenderer, &sImageProxy, &sPaletteProxy );
    
}


/*---------------------------------------------------------------------------*
  Name:         LoadResource

  Description:  Reads the animation data, cell bank, character data, and
                palette data from the file. It loads the character data and
                palette data to the VRAM.

  Arguments:    None.

  Returns:      Pointer to the cell bank that was read.
 *---------------------------------------------------------------------------*/
static NNSG2dCellDataBank* LoadResources(void)
{
    NNSG2dCellDataBank* pCellBank;
    void* pBuf;

    NNS_G2dInitImageProxy( &sImageProxy );
    NNS_G2dInitImagePaletteProxy( &sPaletteProxy );

    //------------------------------------------------------------------------------
    // load cell data
    {
        pBuf = G2DDemo_LoadNCER( &pCellBank, "data/RendererCore.NCER" );
        SDK_NULL_ASSERT( pBuf );
        // Because the cell bank is used in main memory until the end,
        // this pBuf is not freed.
    }

    //------------------------------------------------------------------------------
    // load animation data
    {
        // Because the cell animation data are used in main memory until the end,
        // this pBuf is not freed.
        pBuf = G2DDemo_LoadNANR( &spAnimBank, "data/RendererCore.NANR" );
        SDK_NULL_ASSERT( pBuf );

        //
        // Initialize cell animation entity
        //
        {
            spCellAnim = G2DDemo_GetNewCellAnimation(1);
            SDK_NULL_ASSERT( spCellAnim );

            SDK_NULL_ASSERT( NNS_G2dGetAnimSequenceByIdx(spAnimBank, 0) );

            NNS_G2dInitCellAnimation(
                spCellAnim,
                NNS_G2dGetAnimSequenceByIdx(spAnimBank, 0),
                pCellBank );
        }
    }

    //------------------------------------------------------------------------------
    // load character data for 2D
    {
        NNSG2dCharacterData* pCharData;

        pBuf = G2DDemo_LoadNCGR( &pCharData, "data/RendererCore.NCGR" );
        SDK_NULL_ASSERT( pBuf );

        // Loading For 2D Graphics Engine.
        NNS_G2dLoadImage1DMapping(
            pCharData,
            CHARA_BASE,
            NNS_G2D_VRAM_TYPE_2DMAIN,
            &sImageProxy );

        // Character data were copied into VRAM,
        // this pBuf is freed. Same below.
        G2DDemo_Free( pBuf );
    }

    // load character data for 3D (software sprite)
    {
        NNSG2dCharacterData* pCharData;

        pBuf = G2DDemo_LoadNCGR( &pCharData, "data/RendererCore.NCBR" );
        SDK_NULL_ASSERT( pBuf );

        // Loading For 3D Graphics Engine.
        NNS_G2dLoadImage1DMapping(
            pCharData,
            CHARA_BASE,
            NNS_G2D_VRAM_TYPE_3DMAIN,
            &sImageProxy );

        G2DDemo_Free( pBuf );
    }

    //------------------------------------------------------------------------------
    // load palette data
    {
        NNSG2dPaletteData* pPlttData;

        pBuf = G2DDemo_LoadNCLR( &pPlttData, "data/RendererCore.NCLR" );
        SDK_NULL_ASSERT( pBuf );

        // Loading For 2D Graphics Engine.
        NNS_G2dLoadPalette(
            pPlttData,
            PLTT_BASE,
            NNS_G2D_VRAM_TYPE_2DMAIN,
            &sPaletteProxy);

        // Loading For 3D Graphics Engine.
        NNS_G2dLoadPalette(
            pPlttData,
            PLTT_BASE,
            NNS_G2D_VRAM_TYPE_3DMAIN,
            &sPaletteProxy);

        G2DDemo_Free( pBuf );
    }

    return pCellBank;
}

//------------------------------------------------------------------------------
static void MtxSetIdentity( MtxFx32* pM )
{
    pM->_00 = FX32_ONE;
    pM->_01 = 0;
    pM->_10 = 0;
    pM->_11 = FX32_ONE;
    pM->_20 = 0;
    pM->_21 = 0;
}

//------------------------------------------------------------------------------
// Carries out drawing using the renderer module.
static void DrawByRenderer
( 
    NNSG2dRendererInstance*     pRdr, 
    const CellInfo*             pCellInf 
)
{
    int                         i,j;
    //
    // Uses NNS_G2dBeginRenderingEx, which specifies the optimization option,
    // instead of NNS_G2dBeginRendering().
    //
    NNS_G2dBeginRenderingEx( pRdr, NNS_G2D_RDR_OPZHINT_NOT_SR | 
                                   NNS_G2D_RDR_OPZHINT_LOCK_PARAMS );
                                   
    for( i = 0; i < NUM_CELL_X; i++ )
    {
        for( j = 0; j < NUM_CELL_Y; j++ )
        {
            //
            // With this demo, Push Pop is not needed.
            // If this is the case, using the following description provides more efficient processing.
            //
            NNS_G2dSetTrans( pCellInf->pos.x + FX32_ONE * STEP_CELL_X*i, 
                             pCellInf->pos.y + FX32_ONE * STEP_CELL_Y*j, 
                                  0 );
            NNS_G2dDrawCellAnimation( pCellInf->pCellAnim );
        }
    }
    NNS_G2dEndRendering();
}          
  


//------------------------------------------------------------------------------
// Carries out drawing using the renderer core module API.
// The renderer core module is a module that carries out the drawing processing for the renderer module.
// If the renderer core module is to be used directly,
// it needs to carry out the matrix stack control, parameter rewrite, etc. carried out by the renderer module.
static void DrawByRndCore
( 
    NNSG2dRendererInstance*     pRdr, 
    const CellInfo*             pCellInf 
)
{
    NNSG2dRndCoreInstance*    pRndCore = &pRdr->rendererCore;
    NNSG2dRenderSurface*      pSurface = pRdr->pTargetSurfaceList;
    MtxFx32                   mtx;
    int                       i,j;
    
    MtxSetIdentity( &mtx );
    
    
    //
    // perform surface settings in render core
    //
    NNS_G2dSetRndCoreSurface( pRndCore, &pSurface->coreSurface );
    
    //
    // If drawing to a 2D surface...
    //
    if( pSurface->type != NNS_G2D_SURFACETYPE_MAIN3D )
    {
        //
        // settings of registration function for 2D drawing
        //
        NNS_G2dSetRndCoreOamRegisterFunc  ( pRndCore, 
                                            CallBackAddOam,
                                            CallBackAddAffine );

        NNS_G2dRndCoreBeginRendering( pRndCore );
        for( i = 0; i < NUM_CELL_X; i++ )
        {
            for( j = 0; j < NUM_CELL_Y; j++ )
            {
                // Set the rendering position for the matrix set for the RendererCore.
                mtx._20 = pCellInf->pos.x + FX32_ONE * STEP_CELL_X*i;
                mtx._21 = pCellInf->pos.y + FX32_ONE * STEP_CELL_Y*j;
                //
                // Sets matrix cache in renderer.
                // (Z value must be previously set.))
                //
                NNS_G2dSetRndCoreCurrentMtx2D( &mtx, NULL );
                //
                // Performs rendering using the RenderCore API.
                //
                NNS_G2dRndCoreDrawCellFast2D( NNS_G2dGetCellAnimationCurrentCell( pCellInf->pCellAnim ) );
            }
        }
        NNS_G2dRndCoreEndRendering( );
    }else{
        //
        // transfers drawing process to render core
        //
        NNS_G2dSetRndCore3DSoftSpriteZvalue( pRndCore, -1 );
        NNS_G2dRndCoreBeginRendering( pRndCore );
        NNS_G2dSetRndCoreCellCloneSource3D( NNS_G2dGetCellAnimationCurrentCell( pCellInf->pCellAnim ) );
        for( i = 0; i < NUM_CELL_X; i++ )
        {
            for( j = 0; j < NUM_CELL_Y; j++ )
            {
                // Set the rendering position for the matrix set for the RendererCore.
                mtx._20 = pCellInf->pos.x + FX32_ONE * STEP_CELL_X*i;
                mtx._21 = pCellInf->pos.y + FX32_ONE * STEP_CELL_Y*j;
                //
                // Sets matrix cache in renderer.
                // (Z value must be previously set.))
                //
                NNS_G2dSetRndCoreCurrentMtx3D( &mtx );
                //
                // Performs rendering using RenderCore API.
                // The API is used to draw the same sprite at high speed.
                //
                NNS_G2dRndCoreDrawCellClone3D( NNS_G2dGetCellAnimationCurrentCell( pCellInf->pCellAnim ) );
            }
        }
        NNS_G2dRndCoreEndRendering( );
    }
}

//------------------------------------------------------------------------------
// Carries out drawing using NNS_G2dMakeCellToOams.
// This operates the fastest, but supplemental features are limited.
static void DrawByMakeOamsFunc
( 
    NNSG2dOamManagerInstance*   pOamMgr, 
    const CellInfo*             pCellInf 
)
{   
    static GXOamAttr          temp[NUM_OF_OAM];
    u16                       numOamBuffer = NUM_OF_OAM;                  // number of remaining OAMs
    u16                       numDrawn;
    GXOamAttr*                pBuffer = temp;
    NNSG2dFVec2               trans;
    int                       i,j;
    
    
    
    
    for( i = 0; i < NUM_CELL_X; i++ )
    {
        for( j = 0; j < NUM_CELL_Y; j++ )
        {
            trans.x = pCellInf->pos.x + FX32_ONE * STEP_CELL_X*i;
            trans.y = pCellInf->pos.y + FX32_ONE * STEP_CELL_Y*j;
            
            if( numOamBuffer <= 0 )
            {
                return;
            }
            //
            // Writes an OBJ list equivalent to MultiCell to temporary.
            //
            numDrawn = NNS_G2dMakeCellToOams(
                                pBuffer,           // output destination Oam buffer
                                numOamBuffer,   // output destination buffer length
                                NNS_G2dGetCellAnimationCurrentCell( pCellInf->pCellAnim ), // output Cell
                                NULL,           // affine conversion
                                &trans,         // offset position
                                NULL,           // Affine Index
                                FALSE );        // double affine?
            
            numOamBuffer    -= numDrawn;
            pBuffer         += numDrawn;
            
            SDK_ASSERT(numOamBuffer < NUM_OF_OAM);
        }
    }
    // Registers OBJ list to OAM Manager
    (void)NNS_G2dEntryOamManagerOam(
            pOamMgr,
            temp,
            (u16)( NUM_OF_OAM - numOamBuffer ) );
}


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

  Description:  This is the main function.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void NitroMain(void)
{
    NNSG2dRendererInstance      render;         // Renderer for rendering
    NNSG2dRenderSurface         surface;        // Main screen surface
    NNSG2dCellDataBank*         pCellBank;      // Cell data
    NNSG2dOamManagerInstance    oamManager;     // OAM manager for 2D rendering
    BOOL                        bDrawByCore = FALSE;
    MyDrawMethod                drawMethod = MYDRAWMETHOD_RENDERERCORE;
    
    OSTick                      time;
    u16                         anmSeqIdx = 0;
    
    // Initilizing App.
    {
        G2DDemo_CommonInit();
        G2DDemo_CameraSetup();
        G2DDemo_MaterialSetup();
        G2DDemo_PrintInit();
        InitOamManager( &oamManager );
        InitRenderer( &render, &surface );
        pCellBank = LoadResources();
        
        spCurrentOam = &oamManager;
    }

    // start display
    {
        SVC_WaitVBlankIntr();
        GX_DispOn();
        GXS_DispOn();
    }

    // Main loop
    while( TRUE )
    {
        static CellInfo cellInfo = {
            {FX32_ONE, FX32_ONE},               // Scale
            {CELL_INIT_POS_X, CELL_INIT_POS_Y},  // Position of cell rendering
            NULL
        };

        cellInfo.pCellAnim = spCellAnim;
        //------------------------------------------------------------------------------
        // Update display information and output method for cell for which input is read
        {
            G2DDemo_ReadGamePad();
            
            // Move Cell
            {
                if ( G2DDEMO_IS_PRESS(PAD_KEY_UP) )
                {
                    cellInfo.pos.y -= FX32_ONE;
                }
                if ( G2DDEMO_IS_PRESS(PAD_KEY_DOWN) )
                {
                    cellInfo.pos.y += FX32_ONE;
                }
                if ( G2DDEMO_IS_PRESS(PAD_KEY_LEFT) )
                {
                    cellInfo.pos.x -= FX32_ONE;
                    NNS_G2dSetRendererFlipMode( &render, FALSE, FALSE );
                }
                if ( G2DDEMO_IS_PRESS(PAD_KEY_RIGHT) )
                {
                    cellInfo.pos.x += FX32_ONE;
                    NNS_G2dSetRendererFlipMode( &render, TRUE, FALSE );
                }
            }

            // Reset
            if( G2DDEMO_IS_TRIGGER(PAD_BUTTON_X) )
            {
                cellInfo.pos = (NNSG2dFVec2){CELL_INIT_POS_X, CELL_INIT_POS_Y};
            }

            // Toggle Output Type
            if( G2DDEMO_IS_TRIGGER(PAD_BUTTON_START) )
            {
                surface.type = (surface.type == NNS_G2D_SURFACETYPE_MAIN2D) ?
                                    NNS_G2D_SURFACETYPE_MAIN3D:
                                    NNS_G2D_SURFACETYPE_MAIN2D;
            }
            
        
            // Drawing method change
            if( G2DDEMO_IS_TRIGGER(PAD_BUTTON_L) )
            {
                drawMethod = (MyDrawMethod)(++drawMethod % MYDRAWMETHOD_MAX);
                //
                // Carries out initialization of the surface.
                // This is for resetting the callbacks for render core set up in the renderer module.
                //
                {
                    if( drawMethod == MYDRAWMETHOD_RENDERER )
                    {
                        NNS_G2dInitRenderSurface( &surface );
                        ResetRendererSurface( &surface );
                        
                    }else if( drawMethod == MYDRAWMETHOD_RENDERERCORE ){
                        
                        NNS_G2dInitRndCoreSurface( &surface.coreSurface );
                        ResetRendererSurface( &surface );
                    }
                }
            }
        
            //
            // Animation switch
            //
            if( G2DDEMO_IS_TRIGGER(PAD_BUTTON_R) )
            {
                anmSeqIdx++;
                anmSeqIdx = (u16)(anmSeqIdx % NNS_G2dGetNumAnimSequence( spAnimBank ));
                
                NNS_G2dInitCellAnimation( spCellAnim, 
                                          NNS_G2dGetAnimSequenceByIdx(spAnimBank, anmSeqIdx ),
                                          pCellBank );
            }
        }
        
        
        //------------------------------------------------------------------------------
        // Drawing
        //
        // Render cells using various rendering APIs.
        //
        time = OS_GetTick();   
        {
            switch( drawMethod )
            {
            case MYDRAWMETHOD_RENDERER:
                //
                // drawing with renderer
                //                
                DrawByRenderer( &render, &cellInfo );break;            
            case MYDRAWMETHOD_RENDERERCORE:
                //
                // drawing with renderer core
                //
                DrawByRndCore( &render, &cellInfo );break;            
            case MYDRAWMETHOD_CELLTOOAM:
                //
                // Drawing using NNS_G2dMakeCellToOams().
                //     Processing is not done with software sprite drawing.
                if( surface.type != NNS_G2D_SURFACETYPE_MAIN3D )
                {
                    DrawByMakeOamsFunc( &oamManager, &cellInfo );break;
                }
            }
        }
        time = OS_GetTick() - time;
        
        //------------------------------------------------------------------------------
        // Output display information
        {
            G2DDemo_PrintOutf(0, 0, "pos:   x=%3d     y=%3d",
                cellInfo.pos.x >> FX32_SHIFT, cellInfo.pos.y >> FX32_SHIFT);   
            G2DDemo_PrintOut(0, 1, drawTypeStrTbl[drawMethod] );
            G2DDemo_PrintOut(0, 2, (surface.type == NNS_G2D_SURFACETYPE_MAIN2D) ?
                   "OBJ           ": "SoftwareSprite"); 
            
            G2DDemo_PrintOutf( 0, 22, "OBJ_CNT:%04ld", 
                cellInfo.pCellAnim->pCurrentCell->numOAMAttrs * NUM_CELL_X * NUM_CELL_Y );
            G2DDemo_PrintOutf( 0, 23, "TIME   :%06ld usec\n", OS_TicksToMicroSeconds(time) );
            
            // Software sprite drawing is not carried out during NNS_G2dMakeSimpleMultiCellToOams() drawing.
            if( surface.type == NNS_G2D_SURFACETYPE_MAIN3D && drawMethod == MYDRAWMETHOD_CELLTOOAM )
            {
                G2DDemo_PrintOutf( 5, 10, "Not supported." );
            }else{
                G2DDemo_PrintOutf( 5, 10, "              " );
            }
        }

        // Wait for V-Blank
        G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_Z);
        SVC_WaitVBlankIntr();


        //
        // Write buffer contents to HW
        //
        {
            // Display information
            G2DDemo_PrintApplyToHW();

            // Cell
            NNS_G2dApplyOamManagerToHW( &oamManager );
            NNS_G2dResetOamManagerBuffer( &oamManager );
        }

        NNS_G2dTickCellAnimation( spCellAnim, FX32_ONE );
    }
}

/*---------------------------------------------------------------------------*
  Name:         VBlankIntr

  Description:  Handles V-blank interrupts.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void VBlankIntr(void)
{
    OS_SetIrqCheckFlag( OS_IE_V_BLANK );                   // checking VBlank interrupt
}

