
/****************************************************************************
 *
 * This file is a product of Criterion Software Ltd.
 *
 * This file is provided as is with no warranties of any kind and is
 * provided without any obligation on Criterion Software Ltd.
 * or Canon Inc. to assist in its use or modification.
 *
 * Criterion Software Ltd. and Canon Inc. will not, under any
 * circumstances, be liable for any lost revenue or other damages
 * arising from the use of this file.
 *
 * Copyright (c) 2000 Criterion Software Ltd.
 * All Rights Reserved.
 *
 */

/****************************************************************************
 *                                                                         
 * main.c
 *
 * Copyright (C) 2000 Criterion Technologies.
 *
 * Original author: Nicolas Vale.
 * Reviewed by: John Irwin.
 *                                                                         
 * Purpose: Example to illustrate the correlation between different image 
 *          types and the textures that are generated from them. It also shows
 *          how the texture's raster type can vary across different platforms.
 *                         
 ****************************************************************************/

#include "rwcore.h"

#ifdef RWLOGO
#include "rplogo.h"
#endif

#include "rtcharse.h"
#include "rtpng.h"
#include "rtbmp.h"

#include "skeleton.h"
#include "menu.h"
#include "events.h"
#include "camera.h"

#ifdef RWMETRICS
#include "metrics.h"
#endif

#define DEFAULT_SCREEN_WIDTH (640)
#define DEFAULT_SCREEN_HEIGHT (480)

#ifdef WIDE_SCREEN
#define DEFAULT_ASPECTRATIO (16.0f/9.0f)
#else
#define DEFAULT_ASPECTRATIO (4.0f/3.0f)
#endif

#define DEFAULT_VIEWWINDOW (0.5f)

static RwBool FPSOn = FALSE;

static RwInt32 FrameCounter = 0;
static RwInt32 FramesPerSecond = 0;

static RwRGBA ForegroundColor = {200, 200, 200, 255};
static RwRGBA BackgroundColor = { 64,  64,  64,   0};

static RwCamera *Camera = NULL;
static RtCharset *Charset = NULL;

static RwTexture *Texture = NULL;
static RwImage *Image = NULL;
static RwInt32 CurrentImage = 0;
static RwReal Scale = 1.0f;

/*
 * All the possible raster pixel formats...
 */
typedef struct RasterPixelFormat RasterPixelFormat;
struct RasterPixelFormat
{
    RwRasterFormat pixelFormat;
    RwChar description[16];
};

static  RasterPixelFormat RasterPixelFormats[] =
{
    /* 
     * 16 bits per pixel, 1-bit for the alpha channel,
     * 5 for the red, 5 for the green and 5 for blue...
     */
    { rwRASTERFORMAT1555, RWSTRING("1555") },
    
    /* 
     * 16 bits per pixel, 5 red, 6 green, and 5 blue...
     */
    { rwRASTERFORMAT565, RWSTRING("565") },
    
    /* 
     * 16 bits per pixel, 4 alpha, 4 red, 4 green, and 4 blue...
     */
    { rwRASTERFORMAT4444, RWSTRING("4444") },
    
    /* 
     * 8 bits per pixel, gray scale...
     */
    { rwRASTERFORMATLUM8, RWSTRING("LUM8") },
    
    /* 
     * 32 bits per pixel, 8 alpha, 8 red, 8 green, and 8 blue...
     */
    { rwRASTERFORMAT8888, RWSTRING("8888") },
    
    /* 
     * 24 bits per pixel, 8 red, 8 green, and 8 blue...
     */
    { rwRASTERFORMAT888, RWSTRING("888") },
    
    /* 
     * 16 bits per pixel, undefined. Possible use as a Z-buffer...
     */
    { rwRASTERFORMAT16, RWSTRING("16-bit") },
    
    /* 
     * 24 bits per pixel, undefined. Possible use as a Z-buffer...
     */
    { rwRASTERFORMAT24, RWSTRING("24-bit") },
    
    /* 
     * 32 bits per pixel, undefined. Possible use as a Z-buffer...
     */
    { rwRASTERFORMAT32, RWSTRING("32-bit") },
    
    /* 
     * 16 bits per pixel, 5 red, 5 green, and 5 blue...
     */
    { rwRASTERFORMAT555, RWSTRING("555") }
};

#define NUM_RASTER_PIXEL_FORMATS \
    (sizeof(RasterPixelFormats)/sizeof(RasterPixelFormats[0]))

/* 
 * All the possible raster masks...
 */
typedef struct RasterFormatMask RasterFormatMask;
struct RasterFormatMask
{
    RwRasterFormat mask;
    RwChar description[16];
};


static RasterFormatMask RasterFormatMasks[] =
{
    /* 
     * A mask to determine if raster is using mipmaps
     * generated by RenderWare...
     */
    { rwRASTERFORMATAUTOMIPMAP, RWSTRING("AUTOMIPMAP") },
    
    /* 
     * A mask to determine if raster is using 8 bits per pixel 
     * and a palette (color lookup table)... 
     */
    { rwRASTERFORMATPAL8, RWSTRING("PAL8") },
    
    /* 
     * A mask to determine if raster is using 4 bits per pixel 
     * and a palette (color lookup table)...
     */
    { rwRASTERFORMATPAL4, RWSTRING("PAL4") },
    
    /* 
     * A mask to determine if raster is using mipmaps...
     */
    { rwRASTERFORMATMIPMAP, RWSTRING("MIPMAP") }
};

#define NUM_RASTER_MASKS  \
    (sizeof(RasterFormatMasks)/sizeof(RasterFormatMasks[0]))

/* 
 * The set of test images and masks...
 */
#define NUM_IMAGES (9)

static const RwChar *ImageFileNames[NUM_IMAGES * 2] = 
{
    RWSTRING("img888.png"),   RWSTRING(""),
    RWSTRING("img8888.png"),  RWSTRING(""),
    RWSTRING("imggray.png"),  RWSTRING(""),

    RWSTRING("imgpal4.png"),  RWSTRING(""),
    RWSTRING("imgpal8.png"),  RWSTRING(""),
    RWSTRING("imgpal1.bmp"),  RWSTRING("imgpal1m.bmp"),
    RWSTRING("imgpal2m.png"), RWSTRING(""),
    RWSTRING("imgmask.png"),  RWSTRING("imgmaskm.png"),

    RWSTRING("imgdffsz.png"), RWSTRING("")
};


/*
 *****************************************************************************
 */
static RwCamera *
CreateCamera(void)
{
    return CameraCreate(RsGlobal.maximumWidth, RsGlobal.maximumHeight, TRUE);
}


/*
 *****************************************************************************
 */
static RwBool
Initialize(void)
{
    if( RsInitialize() )
    {
        if( !RsGlobal.maximumWidth )
        {
            RsGlobal.maximumWidth = DEFAULT_SCREEN_WIDTH;
        }

        if( !RsGlobal.maximumHeight )
        {
            RsGlobal.maximumHeight = DEFAULT_SCREEN_HEIGHT;
        }

        RsGlobal.appName = RWSTRING("RW3 Imagetex Example");

        RsGlobal.maxFPS = 120;

        return TRUE;
    }

    return FALSE;
}


/*
 *****************************************************************************
 */
static void
UpdateTextureDisplayScale(void)
{
    /*
     * If texture is larger than the screen then it needs to be scaled...
     */
    RwReal crw, crh, trw, trh;

    crw = (RwReal) RwRasterGetWidth(RwCameraGetRaster(Camera));
    crh = (RwReal) RwRasterGetHeight(RwCameraGetRaster(Camera));

    trw = (RwReal) RwRasterGetWidth(RwTextureGetRaster(Texture));
    trh = (RwReal) RwRasterGetHeight(RwTextureGetRaster(Texture));

    /*
     * Factor-in a 20% margin...
     */
    trw *= 1.2f;
    trh *= 1.2f;

    if( (trw > crw) && (trh > crh) )
    {
        /*
         * Both dimensions are larger than the screen, pick the
         * smallest scale factor...
         */
        Scale = (crw / trw) < (crh / trh) ? (crw / trw) : (crh / trh);
    }
    else if( trw > crw )
    {
        /*
         * Only the width of the texture is greater than the screen...
         */
        Scale = crw / trw;
    }
    else if( trh > crh )
    {
        /*
         * Only the height of the texture is greater than the screen...
         */
        Scale = crh / trh;
    }
    else
    {
        /*
         * The texture can fit on the screen without scaling...
         */
        Scale = 1.0f;
    }

    return;
}


/*
 *****************************************************************************
 */
static RwBool
ImageLoad(RwInt32 imageNum)
{
    RwBool loaded = FALSE;
    RwChar *path;
    RwChar path1[256], path2[256];

    if( Image )
    {
        /* 
         * Destroy old image...
         */
        RwImageDestroy(Image);

        Image = NULL;
    }

    path = RsPathnameCreate(RWSTRING("./textures/"));
    RwImageSetPath(path);
    RsPathnameDestroy(path);

    rwstrcpy(path1, RWSTRING(ImageFileNames[imageNum * 2]));
    rwstrcpy(path2, RWSTRING(ImageFileNames[imageNum * 2 + 1]));

    /* 
     * Load new image...
     */
    Image = RwImageRead(path1);

    if( Image )
    {
        /* 
         * Once we have the image, strip the extension...
         */
        RwChar *ext = rwstrstr(path1, RWSTRING("."));

        *ext = '\0';

        ext = rwstrstr(path2, RWSTRING("."));
        if( ext )
        {
            *ext = '\0';
        }

        if( Texture )
        {
            /* 
             * Destroy old texture...
             */
            RwTextureDestroy(Texture);
        }

        Texture = RwTextureRead(path1, path2);
        if( Texture )
        {
            UpdateTextureDisplayScale();

            loaded = TRUE;
        }
    }

    return loaded;
}


/*
 *****************************************************************************
 */
static RwBool
CurrentImageCallback(RwBool testEnable)
{
    if( testEnable )
    {
        return TRUE;
    }

    ImageLoad(CurrentImage);

    return TRUE;
}


static RwBool
InitializeMenu(void)
{
    static RwChar imageLabel[] = RWSTRING("Image_I");
    static RwChar fpsLabel[] = RWSTRING("FPS_F");

    if( MenuOpen(TRUE, &ForegroundColor, &BackgroundColor) )
    {
        MenuAddEntryInt(imageLabel, &CurrentImage,
            CurrentImageCallback, 0, NUM_IMAGES - 1, 1, NULL);

        MenuAddSeparator();

        MenuAddEntryBool(fpsLabel, &FPSOn, NULL);

        return TRUE;
    }

    return FALSE;
}


/*
 *****************************************************************************
 */
static RwBool
Initialize3D(void *param)
{
    if( !RsRwInitialize(param) )
    {
        RsErrorMessage(RWSTRING("Error initializing RenderWare."));

        return FALSE;
    }

    Charset = RtCharsetCreate(&ForegroundColor, &BackgroundColor);
    if( Charset == NULL )
    {
        RsErrorMessage(RWSTRING("Cannot create raster charset."));

        return FALSE;
    }

    Camera = CreateCamera();
    if( Camera == NULL )
    {
        RsErrorMessage(RWSTRING("Cannot create camera."));

        return FALSE;
    }

    if( !InitializeMenu() )
    {
        RsErrorMessage(RWSTRING("Error initializing menu."));

        return FALSE;
    }

#ifdef RWMETRICS
    RsMetricsOpen(Camera);
#endif

    /*
     * Load the initial image...
     */
    ImageLoad(CurrentImage);

    return TRUE;
}


/*
 *****************************************************************************
 */
static void
Terminate3D(void)
{
#ifdef RWMETRICS
    RsMetricsClose();
#endif

    MenuClose();

    if( Image )
    {
        RwImageDestroy(Image);
    }

    if( Texture )
    {
        RwTextureDestroy(Texture);
    }

    if( Camera )
    {
        CameraDestroy(Camera);
    }

    if( Charset )
    {
        RwRasterDestroy(Charset);
    }

    RsRwTerminate();

    return;
}


/*
 *****************************************************************************
 */
static RwBool
AttachPlugins(void)
{
#ifdef RWLOGO
    /* 
     * Attach logo plug-in...
     */
    if( !RpLogoPluginAttach() )
    {
        return FALSE;
    }
#endif

    return TRUE;
}


/*
 *****************************************************************************
 */
static RwBool
RegisterImageFormats(void)
{
    if( !RwImageRegisterImageFormat(RWSTRING("bmp"), RtBMPImageRead, 0) )
    {
        return FALSE;
    }

    if( !RwImageRegisterImageFormat(RWSTRING("png"), RtPNGImageRead, 0) )
    {
        return FALSE;
    }

    return TRUE;
}


/*
 *****************************************************************************
 */
static void
TextureRender(void)
{
    RwRaster *camRas;
    RwIm2DVertex vertex[4];
    RwReal crw, crh, trw, trh;
    RwReal recipCameraZ;
    RwReal minX, minY, maxX, maxY;

    camRas = RwCameraGetRaster(Camera);

    crw = (RwReal) RwRasterGetWidth(camRas);
    crh = (RwReal) RwRasterGetHeight(camRas);

    trw = (RwReal) RwRasterGetWidth(RwTextureGetRaster(Texture));
    trh = (RwReal) RwRasterGetHeight(RwTextureGetRaster(Texture));

    trw *= Scale;
    trh *= Scale;

    /*
     * Display texture at the center of the screen...
     */
    minX = (crw - trw) / 2.0f;
    minY = (crh - trh) / 2.0f;
    maxX = (crw + trw) / 2.0f;
    maxY = (crh + trh) / 2.0f;

    recipCameraZ = 1.0f / RwCameraGetNearClipPlane(Camera);

    RwIm2DVertexSetScreenX(&vertex[0], minX);
    RwIm2DVertexSetScreenY(&vertex[0], maxY);
    RwIm2DVertexSetScreenZ(&vertex[0], RwIm2DGetNearScreenZ());
    RwIm2DVertexSetRecipCameraZ(&vertex[0], recipCameraZ);
    RwIm2DVertexSetIntRGBA(&vertex[0], 255, 255, 255, 255);
    RwIm2DVertexSetU(&vertex[0], 0.0f, recipCameraZ);
    RwIm2DVertexSetV(&vertex[0], 1.0f, recipCameraZ);

    RwIm2DVertexSetScreenX(&vertex[1], maxX);
    RwIm2DVertexSetScreenY(&vertex[1], maxY);
    RwIm2DVertexSetScreenZ(&vertex[1], RwIm2DGetNearScreenZ());
    RwIm2DVertexSetRecipCameraZ(&vertex[1], recipCameraZ);
    RwIm2DVertexSetIntRGBA(&vertex[1], 255, 255, 255, 255);
    RwIm2DVertexSetU(&vertex[1], 1.0f, recipCameraZ);
    RwIm2DVertexSetV(&vertex[1], 1.0f, recipCameraZ);

    RwIm2DVertexSetScreenX(&vertex[2], minX);
    RwIm2DVertexSetScreenY(&vertex[2], minY);
    RwIm2DVertexSetScreenZ(&vertex[2], RwIm2DGetNearScreenZ());
    RwIm2DVertexSetRecipCameraZ(&vertex[2], recipCameraZ);
    RwIm2DVertexSetIntRGBA(&vertex[2], 255, 255, 255, 255);
    RwIm2DVertexSetU(&vertex[2], 0.0f, recipCameraZ);
    RwIm2DVertexSetV(&vertex[2], 0.0f, recipCameraZ);

    RwIm2DVertexSetScreenX(&vertex[3], maxX);
    RwIm2DVertexSetScreenY(&vertex[3], minY);
    RwIm2DVertexSetScreenZ(&vertex[3], RwIm2DGetNearScreenZ());
    RwIm2DVertexSetRecipCameraZ(&vertex[3], recipCameraZ);
    RwIm2DVertexSetIntRGBA(&vertex[3], 255, 255, 255, 255);
    RwIm2DVertexSetU(&vertex[3], 1.0f, recipCameraZ);
    RwIm2DVertexSetV(&vertex[3], 0.0f, recipCameraZ);

    RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS,
                     (void *) rwTEXTUREADDRESSCLAMP);

    RwRenderStateSet(rwRENDERSTATETEXTURERASTER,
                     (void *) RwTextureGetRaster(Texture));

    RwRenderStateSet(rwRENDERSTATESHADEMODE,
                     (void *) rwSHADEMODEGOURAUD);

    RwIm2DRenderPrimitive(rwPRIMTYPETRISTRIP, vertex, 4);

    return;
}


/*
 *****************************************************************************
 */
static void
DisplayOnScreenInfo(RwCamera *camera)
{
    RwChar caption[256];
    RtCharsetDesc charsetDesc;
    RwInt32 crw, crh;

    RtCharsetGetDesc(Charset, &charsetDesc);

    crw = RwRasterGetWidth(RwCameraGetRaster(camera));
    crh = RwRasterGetHeight(RwCameraGetRaster(camera));

    if( FPSOn )
    {
        rwsprintf(caption, RWSTRING("FPS: %03d"), FramesPerSecond);

        RtCharsetPrint(Charset, caption,
            (crw - charsetDesc.width * (rwstrlen(caption) + MARGIN_RIGHT) ),
            charsetDesc.height * MARGIN_TOP );
    }

    if( Image && Texture )
    {
        /*
         * Display Image and Texture information (from bottom to top) ...
         */

        RwRaster *textureRaster;
        RwRasterFormat textureFormat;
        RwInt32 captionWidth = 20;
        RwInt32 captionMargin = MARGIN_RIGHT;
        RwInt32 headerMargin = 5;
        RwInt32 lineNumber = 1 + MARGIN_BOTTOM;
        RwInt32 i;
        RwChar value[16];

        textureRaster = RwTextureGetRaster(Texture);
        textureFormat = (RwRasterFormat)RwRasterGetFormat(textureRaster);

        /* 
         * Texture palette...
         */
        rwsprintf(value, RWSTRING("no"));

        for(i=0; i<(RwInt32)NUM_RASTER_MASKS; i++)
        {
            if( textureFormat & RasterFormatMasks[i].mask )
            {
                rwsprintf(value, RWSTRING("%s"),
                          RasterFormatMasks[i].description);

                break;
            }
        }

        rwsprintf(caption, RWSTRING("Palette .............."));
        caption[captionWidth - rwstrlen(value)] = RWSTRING('\0');
        rwsprintf(caption, RWSTRING("%s %s"), caption, value);

        RtCharsetPrint(Charset, caption,
            crw - charsetDesc.width * (captionWidth + captionMargin),
            crh - charsetDesc.height * lineNumber++);

        /*
         * Texture depth...
         */
        rwsprintf(value, RWSTRING("%d"),
                  RwRasterGetDepth(textureRaster));
        rwsprintf(caption, RWSTRING("Depth .............."));
        caption[captionWidth - rwstrlen(value)] = RWSTRING('\0');
        rwsprintf(caption, RWSTRING("%s %s"), caption, value);

        RtCharsetPrint(Charset, caption,
            crw - charsetDesc.width * (captionWidth + captionMargin),
            crh - charsetDesc.height * lineNumber++);

        /*
         * Texture size...
         */
        rwsprintf(value, RWSTRING("%d x %d"),
                  RwRasterGetWidth(textureRaster),
                  RwRasterGetHeight(textureRaster));
        rwsprintf(caption, RWSTRING("Size .............."));
        caption[captionWidth - rwstrlen(value)] = RWSTRING('\0');
        rwsprintf(caption, RWSTRING("%s %s"), caption, value);

        RtCharsetPrint(Charset, caption,
            crw - charsetDesc.width * (captionWidth + captionMargin),
            crh - charsetDesc.height * lineNumber++);

        /* 
         * Texture format...
         */
        for(i=0; i<(RwInt32)NUM_RASTER_PIXEL_FORMATS; i++)
        {
            RwRasterFormat pixelFormat;

            pixelFormat = (RwRasterFormat)
                (textureFormat & rwRASTERFORMATPIXELFORMATMASK);

            if( pixelFormat == RasterPixelFormats[i].pixelFormat )
            {
                rwsprintf(value, RWSTRING("%s"),
                          RasterPixelFormats[i].description);

                break;
            }
        }

        rwsprintf(caption, RWSTRING("Format .............."));
        caption[captionWidth - rwstrlen(value)] = RWSTRING('\0');
        rwsprintf(caption, RWSTRING("%s %s"), caption, value);

        RtCharsetPrint(Charset, caption,
            crw - charsetDesc.width * (captionWidth + captionMargin),
            crh - charsetDesc.height * lineNumber++);

        /*
         * Texture display header...
         */
        rwsprintf(caption, RWSTRING("Texture:"));
        RtCharsetPrint(Charset, caption,
            crw - charsetDesc.width * (captionWidth + headerMargin),
            crh - charsetDesc.height * lineNumber++);

        /* 
         * Image palette...
         */
        lineNumber++;

        if( RwImageGetPalette(Image) )
        {
            rwsprintf(value, RWSTRING("yes"));
        }
        else
        {
            rwsprintf(value, RWSTRING("no"));
        }

        rwsprintf(caption, RWSTRING("Palette .............."));
        caption[captionWidth - rwstrlen(value)] = RWSTRING('\0');
        rwsprintf(caption, RWSTRING("%s %s"), caption, value);

        RtCharsetPrint(Charset, caption,
            crw - charsetDesc.width * (captionWidth + captionMargin),
            crh - charsetDesc.height * lineNumber++);

        /*
         * Image depth...
         */
        rwsprintf(value, RWSTRING("%d"), RwImageGetDepth(Image));
        rwsprintf(caption, RWSTRING("Depth .............."));
        caption[captionWidth - rwstrlen(value)] = RWSTRING('\0');
        rwsprintf(caption, RWSTRING("%s %s"), caption, value);

        RtCharsetPrint(Charset, caption,
            crw - charsetDesc.width * (captionWidth + captionMargin),
            crh - charsetDesc.height * lineNumber++);

        /*
         * Image size...
         */
        rwsprintf(value, RWSTRING("%d x %d"),
                  RwImageGetWidth(Image), RwImageGetHeight(Image));
        rwsprintf(caption, RWSTRING("Size .............."));
        caption[captionWidth - rwstrlen(value)] = RWSTRING('\0');
        rwsprintf(caption, RWSTRING("%s %s"), caption, value);

        RtCharsetPrint(Charset, caption,
        crw - charsetDesc.width * (captionWidth + captionMargin),
        crh - charsetDesc.height * lineNumber++);

        /* 
         * Image mask name...
         */
        rwsprintf(value, RWSTRING("%s"), RwTextureGetMaskName(Texture));
        if( rwstrlen(value) <= 0 )
        {
            rwsprintf(value, RWSTRING("none"));
        }
        rwsprintf(caption, RWSTRING("Mask ....................."));
        caption[captionWidth - rwstrlen(value)] = RWSTRING('\0');
        rwsprintf(caption, RWSTRING("%s %s"), caption, value);

        RtCharsetPrint(Charset, caption,
            crw - charsetDesc.width * (captionWidth + captionMargin),
            crh - charsetDesc.height * lineNumber++);

        /*
         * Image name...
         */
        rwsprintf(value, RWSTRING("%s"), RwTextureGetName(Texture));
        rwsprintf(caption, RWSTRING("Name ....................."));
        caption[captionWidth - rwstrlen(value)] = RWSTRING('\0');
        rwsprintf(caption, RWSTRING("%s %s"), caption, value);

        RtCharsetPrint(Charset, caption,
            crw - charsetDesc.width * (captionWidth + captionMargin),
            crh - charsetDesc.height * lineNumber++);

        /*
         * Image display header...
         */
        rwsprintf(caption, RWSTRING("Image:"));
        RtCharsetPrint(Charset, caption,
            crw - charsetDesc.width * (captionWidth + headerMargin),
            crh - charsetDesc.height * lineNumber++);
    }

    return;
}


/*
 *****************************************************************************
 */
static void
Render(void)
{
    RwCameraClear(Camera, &BackgroundColor, 
        rwCAMERACLEARZ | rwCAMERACLEARIMAGE);

    if( RwCameraBeginUpdate(Camera) )
    {
        if( MenuGetStatus() != HELPMODE )
        {
            if( Texture )
            {
                TextureRender();
            }

            DisplayOnScreenInfo(Camera);
        }

        MenuRender(Camera, NULL);

#ifdef RWMETRICS
        RsMetricsRender();
#endif

        RwCameraEndUpdate(Camera);
    }

    /* 
     * Display camera's raster...
     */
    RsCameraShowRaster(Camera);

    FrameCounter++;

    return;
}


/*
 *****************************************************************************
 */
static void 
Idle(void)
{
    RwUInt32 thisTime;

    static RwBool firstCall = TRUE;
    static RwUInt32 lastFrameTime;

    if( firstCall )
    {
        lastFrameTime = RsTimer();

        firstCall = FALSE;
    }

    thisTime = RsTimer();

    /* 
     * Has a second elapsed since we last updated the FPS...
     */
    if( thisTime > (lastFrameTime + 1000) )
    {
        /* 
         * Capture the frame counter...
         */
        FramesPerSecond = FrameCounter;
        
        /*
         * ...and reset...
         */
        FrameCounter = 0;
        
        lastFrameTime = thisTime;
    }

    Render();

    return;
}


/*
 *****************************************************************************
 */
RsEventStatus
AppEventHandler(RsEvent event, void *param)
{
    switch( event )
    {
        case rsINITIALIZE:
        {
            return Initialize()? rsEVENTPROCESSED : rsEVENTERROR;
        }

        case rsCAMERASIZE:
        {
            CameraSize(Camera, (RwRect *) param,
                       DEFAULT_VIEWWINDOW, DEFAULT_ASPECTRATIO);

            if( Texture )
            {
                UpdateTextureDisplayScale();
            }

            return rsEVENTPROCESSED;
        }

        case rsRWINITIALIZE:
        {
            return Initialize3D(param) ? rsEVENTPROCESSED : rsEVENTERROR;
        }

        case rsRWTERMINATE:
        {
            Terminate3D();

            return rsEVENTPROCESSED;
        }

        case rsPLUGINATTACH:
        {
            return AttachPlugins()? rsEVENTPROCESSED : rsEVENTERROR;
        }

        case rsINPUTDEVICEATTACH:
        {
            AttachInputDevices();

            return rsEVENTPROCESSED;
        }

        case rsREGISTERIMAGELOADER:
        {
            return RegisterImageFormats()? rsEVENTPROCESSED :
                rsEVENTERROR;
        }

        case rsIDLE:
        {
            Idle();

            return rsEVENTPROCESSED;
        }

        default:
        {
            return rsEVENTNOTPROCESSED;
        }
    }
}

/*
 *****************************************************************************
 */
