
/****************************************************************************
 *
 * 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) 1999 Criterion Software Ltd.
 * All Rights Reserved.
 *
 */

/****************************************************************************
 *
 * paint2d.c
 *
 * Copyright (C) 1999 Criterion Technologies.
 *
 * Original author: Adam Billyard.
 *
 * Purpose: RenderWare 3.0 demo.
 *
 ****************************************************************************/

#include <stdio.h>

#if defined(__KATANA__)
#if defined(RW_USE_SPF)
#include <mathf.h>
#else  /* RW_USE_SPF */
#include <math.h>
#endif /* RW_USE_SPF */
#endif /* __KATANA__ */

#if !(defined(__KATANA__))
#include <math.h>
#endif

#include "rwcore.h"
#include "rt2d.h"

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

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

#ifdef RWMOUSE
#include "mouse.h"
#endif

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

#include "main.h"
#include "paint2d.h"

static RwChar _mandrill[] = RWSTRING("mandrill");
static RwChar _chilli[] = RWSTRING("chilli");
static RwChar _grid[] = RWSTRING("grid");

static RwChar    *TextureNames[] =
{
    _mandrill,
    _chilli,
    _grid
};

#define NUMTEXTURES (sizeof(TextureNames)/sizeof(TextureNames[0]))

static RwTexture *Textures[NUMTEXTURES];

RwRGBA FGNDColor = {200, 200, 200, 255};
RwRGBA BGNDColor = {  0,   0,   0, 255};

static Rt2dBrush *Brush;
static Rt2dPath *Circle;
static Rt2dPath *Echar;
static Rt2dPath *Hand;

static Rt2dFont *Font[2];
static Rt2dFont *Sans[5];
static Rt2dFont *Serif[9];
static Rt2dFont *Term;

RwInt32 TermLines = 0;
RwInt32 DTermLines = 0;

RwInt32 PageNumber = 1;


/*
 *****************************************************************************
 */
void
PageSet(RwInt32 page)
{
    PageNumber = page;

    Rt2dCTMSetIdentity();

    return;
}

/*
 *****************************************************************************
 */
RwInt32
PageGet(void)
{
    return (PageNumber);
}

/*
 *****************************************************************************
 */
static void
RenderPage1(RwReal animTime)
{
    Rt2dPath *path;
    Rt2dBBox bbox;
    RwReal h;
    RwRGBA col[] = {
        {255,   0,   0, 255}, /* 0: Red */
        {255, 128,   0, 255}, /* 1: Orange */
        {  0, 128, 160, 255}, /* 2: Dark cyan */
        {255, 255, 255, 255}, /* 3: White */
        {  0,   0,  32, 255}, /* 4: Dark blue */
        {  0,   0, 160, 255}, /* 5: Deep blue */
        {255, 255, 255, 255}, /* 6: White */
        {  0, 255,   0, 255}, /* 7: Green */
        {255, 255,   0, 255}, /* 8: Yellow */
        {255,   0, 255,   0}  /* 9: Magenta */
    };

    RwV2d uv[] = {
        {  0.0f, 1.0f },
        {  1.0f, 1.0f },
        {  1.0f, 0.0f },
        {  0.0f, 0.0f },
        {  0.0f, 0.0f },
        { (RwReal)25.0, 0.0f },
        { (RwReal)25.0, 1.0f },
        {  0.0f, 1.0f }
    };

    /*
     * Background fill (static)...
     */
    Rt2dCTMPush();
    Rt2dCTMSetIdentity();
    {
        static RwChar _paths_strokes_fills[] =
            RWSTRING("Paths, Strokes & Fills");
        RwV2d xstep, ystep, origin;

        path = Rt2dPathCreate();

        Rt2dDeviceGetClippath(path);
        Rt2dBrushSetRGBA(Brush, &col[5], &col[5], &col[4], &col[4]);

        Rt2dPathFill(path, Brush);

        /* And we'll put a border around it too of exactly 5 pixels */
        Rt2dDeviceGetStep(&xstep, &ystep, &origin);

        Rt2dPathInset(path, RwV2dLength(&xstep) * 2.5f);
        Rt2dBrushSetWidth(Brush, RwV2dLength(&xstep) * 5.0f);
        Rt2dBrushSetRGBA(Brush, &col[0], &col[0], &col[8], &col[8]);
        Rt2dPathStroke(path, Brush);

        Rt2dPathDestroy(path);

        /* and a title */
        h = Rt2dFontGetHeight(Sans[3]);
        Rt2dBrushSetRGBA(Brush, &col[6], &col[6], &col[6], &col[6]);
        bbox.x = 0.0f;
        bbox.y = WinBBox.h-h;
        bbox.w = WinBBox.w;
        bbox.h = h;
        Rt2dFontFlow(Sans[3], _paths_strokes_fills,
                     h, &bbox, rt2dJUSTIFYCENTER, Brush);
    }
    Rt2dCTMPop();

    if (Rt2dVisible(WinBBox.x+0.05f, WinBBox.y+0.05f, WinBBox.w-0.1f, WinBBox.h-0.1f))
    {
        /*
         * Rectangle...
         */
        Rt2dCTMPush();
        Rt2dCTMTranslate(WinBBox.w * 0.2f, WinBBox.h * 0.7f);
        {
            path = Rt2dPathCreate();
            Rt2dPathRect(path, -0.1f, -0.1f, 0.2f, 0.2f);
            Rt2dBrushSetRGBA(Brush, &col[6], &col[6], &col[2], &col[2]);
            Rt2dBrushSetWidth(Brush, 0.03f);
            Rt2dPathStroke(path, Brush);
            Rt2dPathDestroy(path);
        }
        Rt2dCTMPop();

        /*
         * Rounded rectangle...
         */
        Rt2dCTMPush();
        Rt2dCTMTranslate(WinBBox.w * 0.5f, WinBBox.h * 0.7f);
        {
            path = Rt2dPathCreate();
            Rt2dPathRoundRect(path, -0.1f, -0.1f, 0.2f, 0.2f, 0.05f);
            Rt2dBrushSetRGBA(Brush, &col[0], &col[0], &col[1], &col[1]);
            Rt2dBrushSetWidth(Brush, 0.025f);
            Rt2dPathStroke(path, Brush);
            Rt2dPathDestroy(path);
        }
        Rt2dCTMPop();

        /*
         * Filled rounded rectangle...
         */
        Rt2dCTMPush();
        Rt2dCTMTranslate(WinBBox.w * 0.8f, WinBBox.h * 0.7f);
        {
            path = Rt2dPathCreate();
            Rt2dPathRoundRect(path, -0.1f, -0.1f, 0.2f, 0.2f, 0.05f);
            Rt2dBrushSetRGBA(Brush, &col[0], &col[0], &col[0], &col[0]);
            Rt2dBrushSetWidth(Brush, 0.01f);
            Rt2dPathStroke(path, Brush);
            Rt2dPathInset(path, 0.005f);
            Rt2dBrushSetRGBA(Brush, &col[7], &col[8], &col[8], &col[7]);
            Rt2dBrushSetWidth(Brush, 0.025f);
            Rt2dPathFill(path, Brush);
            Rt2dPathDestroy(path);
        }
        Rt2dCTMPop();

        /*
         * Closed curved path, textured fill...
         */
        Rt2dCTMPush();
        Rt2dCTMTranslate(WinBBox.w * 0.33f, WinBBox.h * 0.2f);
        Rt2dCTMScale(0.3f, 0.3f);
        {
            path = Rt2dPathCreate();
            Rt2dPathMoveto(path, 0.0f, 0.0f);
            Rt2dPathCurveto(path, 1.0f, 0.2f, 1.0f, 1.0f, 0.0f, 0.8f);
            Rt2dPathCurveto(path, -1.0f, 0.75f, -0.6f, -0.1f, 0.0f, 0.0f);
            Rt2dPathClose(path);

            Rt2dBrushSetUV(Brush, &uv[0], &uv[1], &uv[2], &uv[3]);
            Rt2dBrushSetRGBA(Brush, &col[6], &col[6], &col[6], &col[6]);
            Rt2dBrushSetTexture(Brush, Textures[0]);
            Rt2dPathFill(path, Brush);

            Rt2dPathInset(path, -0.05f);
            Rt2dBrushSetRGBA(Brush, &col[9], &col[9], &col[8], &col[8]);
            Rt2dBrushSetTexture(Brush, NULL);
            Rt2dBrushSetWidth(Brush, 0.1f);
            Rt2dPathStroke(path, Brush);

            Rt2dPathDestroy(path);
        }
        Rt2dCTMPop();

        /*
         * Zig-zag line...
         */
        Rt2dCTMPush();
        Rt2dCTMTranslate(WinBBox.w * 0.66f, WinBBox.h * 0.3f);
        Rt2dCTMScale(1.5f, 1.5f);
        {
            path = Rt2dPathCreate();
            Rt2dPathMoveto(path, -0.15f, -0.15f);
            Rt2dPathLineto(path, 0.0f, 0.1f);
            Rt2dPathLineto(path, 0.05f, -0.05f);
            Rt2dPathLineto(path, 0.15f, 0.1f);
            Rt2dBrushSetRGBA(Brush, &col[8], &col[8], &col[2], &col[2]);
            Rt2dBrushSetWidth(Brush, 0.03f);
            Rt2dPathStroke(path, Brush);
            Rt2dPathDestroy(path);
        }
        Rt2dCTMPop();
    }

    /*
     * Now normalize an area of the display for page stuff...
     */
    Rt2dCTMPush();
    Rt2dCTMTranslate((WinBBox.w-1.0f)*0.5f, -1.75f);
    if (Rt2dVisible(0.0f, 0.0f, 1.0f, 1.414f))
    {
        /*
         * Draw page boundary...
         */
        {
            Rt2dPath *path;
            RwRGBA col[] = {
                {240, 240, 240, 255},
                {120, 128, 128, 255},
                {240, 240, 240, 255},
                {240, 240, 240, 255}
            };

            path = Rt2dPathCreate();
            Rt2dPathMoveto(path, 0.0f, 0.0f);
            Rt2dPathLineto(path, 1.0f, 0.0f);
            Rt2dPathLineto(path, 1.0f, 1.414f);
            Rt2dPathLineto(path, 0.0f, 1.414f);
            Rt2dPathClose(path);

            Rt2dBrushSetRGBA(Brush, &col[0], &col[1], &col[2], &col[3]);
            Rt2dPathFill(path, Brush);

            Rt2dPathDestroy(path);
        }

        /*
         * Stroke and fill some paths...
         */
        {
            Rt2dPath *path, *insetpath;
            RwInt32 i;
            RwReal bias;
            RwRGBA col[] = {
                {255,   0, 255, 255},
                {255,   0, 255, 128},
                {  0,   0, 255, 255},
                {255, 255,   0, 128},
                {255,   0,   0, 255},
                {  0,   0, 255, 255},
                {  0, 255,   0, 255},
                {  0,   0,   0, 255},
                {255, 255, 255, 255}
            };

            RwV2d uv[] = {
                { 0.0f, 0.0f},
                { 1.0f, 0.0f},
                { 1.0f, 1.0f},
                { 0.0f, 1.0f},
                { 0.0f, 0.0f},
                {25.0f, 0.0f},
                {25.0f, 1.0f},
                { 0.0f, 1.0f}
            };

            /*
             * Curly animated line...
             */
            path = Rt2dPathCreate();
            Rt2dPathMoveto(path, 0.3f, 0.2f);

            bias = (RwReal)RwSin(animTime * 4.0f) * 0.9f;
            Rt2dPathCurveto(path, 0.8f + bias, 0.4f, 0.4f, 0.8f, 0.6f, 1.0f);
            Rt2dPathCurveto(path, 0.8f, 1.2f, 0.8f, 1.3f, 0.2f, 1.3f);

            Rt2dBrushSetWidth(Brush, 0.05f);
            Rt2dBrushSetRGBA(Brush, &col[8], &col[8], &col[8], &col[8]);
            Rt2dBrushSetUV(Brush, &uv[4], &uv[5], &uv[6], &uv[7]);
            Rt2dBrushSetTexture(Brush, Textures[1]);

            Rt2dPathStroke(path, Brush);
            Rt2dBrushSetTexture(Brush, NULL);

            Rt2dPathDestroy(path);

            /*
             * Zigzag line...
             */
            path = Rt2dPathCreate();
            Rt2dPathMoveto(path, 0.1f, 0.1f);
            Rt2dPathLineto(path, 0.2f, 0.9f);
            Rt2dPathLineto(path, 0.4f, 0.4f);
            Rt2dPathLineto(path, 0.8f, 0.8f);

            Rt2dBrushSetWidth(Brush, 0.08f);
            Rt2dBrushSetRGBA(Brush, &col[7], &col[7], &col[7], &col[7]);

            Rt2dPathStroke(path, Brush);

            Rt2dPathDestroy(path);

            /*
             * Some shapes
             */
            path = Rt2dPathCreate();
            Rt2dPathMoveto(path, 0.05f, 0.05f);
            Rt2dPathLineto(path, 0.95f, 0.95f);
            Rt2dPathLineto(path, 0.05f, 0.95f);
            Rt2dPathClose(path);

            Rt2dCTMTranslate(0.5f, 0.5f);

            Rt2dCTMPush();
            Rt2dCTMRotate(animTime * 20);
            Rt2dCTMScale(0.35f, 0.35f);
            {
                Rt2dBrushSetWidth(Brush, 0.05f);
                Rt2dBrushSetRGBA(Brush, &col[1], &col[1], &col[0],&col[0]);
                Rt2dPathStroke(path, Brush);

                Rt2dPathInset(path, Rt2dBrushGetWidth(Brush) * 0.5f);

                Rt2dBrushSetRGBA(Brush, &col[0], &col[0], &col[0], &col[0]);

                Rt2dPathFill(path, Brush);
            }
            Rt2dCTMPop();

            Rt2dPathDestroy(path);

            path = Rt2dPathCreate();
            Rt2dPathMoveto(path, -0.2f, -0.2f);
            Rt2dPathLineto(path, 0.5f, -0.2f);
            Rt2dPathLineto(path, 0.2f, 0.2f);
            Rt2dPathLineto(path, -0.2f, 0.2f);
            Rt2dPathClose(path);

            Rt2dCTMPush();
            Rt2dCTMRotate(animTime * -30.0f);
            Rt2dCTMScale(0.5f, 0.5f);
            {
                Rt2dBrushSetWidth(Brush, 0.05f);
                Rt2dBrushSetRGBA(Brush, &col[2], &col[2], &col[3], &col[3]);
                Rt2dPathStroke(path, Brush);

                Rt2dBrushSetRGBA(Brush, &col[3], &col[3], &col[3], &col[3]);
                Rt2dPathInset(path, Rt2dBrushGetWidth(Brush) * 0.5f);

                Rt2dPathFill(path, Brush);
            }
            Rt2dCTMPop();

            Rt2dPathDestroy(path);

            Rt2dCTMPush();
            Rt2dCTMTranslate(-0.15f, 0.5f);
            Rt2dCTMScale(0.25f, 0.25f);
            {
                Rt2dBrushSetWidth(Brush, 0.2f);
                Rt2dBrushSetRGBA(Brush, &col[4], &col[4], &col[5], &col[5]);
                Rt2dPathStroke(Circle, Brush);

                insetpath = Rt2dPathCreate();
                Rt2dPathCopy(insetpath, Circle);
                Rt2dPathInset(insetpath, Rt2dBrushGetWidth(Brush) * 0.5f);
                Rt2dBrushSetRGBA(Brush, &col[8], &col[8], &col[8], &col[8]);
                Rt2dBrushSetUV(Brush, &uv[0], &uv[1], &uv[2], &uv[3]);
                Rt2dBrushSetTexture(Brush, Textures[2]);

                Rt2dPathFill(insetpath, Brush);

                Rt2dBrushSetTexture(Brush, NULL);

                Rt2dPathDestroy(insetpath);

                /*
                 * Dial ticks...
                 */
                Rt2dCTMPush();
                {
                    path = Rt2dPathCreate();
                    Rt2dPathMoveto(path, 0.8f, 0.0f);
                    Rt2dPathLineto(path, 0.9f, 0.0f);
                    Rt2dBrushSetWidth(Brush, 0.02f);
                    Rt2dBrushSetRGBA(Brush, &col[4], &col[4], &col[4], &col[4]);

                    for (i=0; i<12; i++)
                    {
                        Rt2dPathStroke(path, Brush);

                        Rt2dCTMRotate(30.0f);
                    }

                    Rt2dPathDestroy(path);
                }
                Rt2dCTMPop();

                Rt2dBrushSetRGBA(Brush, &col[3], &col[4], &col[4], &col[3]);
                Rt2dCTMRotate(animTime * 180.0f);

                Rt2dPathFill(Hand, Brush);

                Rt2dCTMRotate(animTime * -270.0f);
                Rt2dCTMScale(0.75f, 0.75f);

                Rt2dPathFill(Hand, Brush);
            }
            Rt2dCTMPop();
        }
    }
    Rt2dCTMPop();

    return;


}


/*
 *****************************************************************************
 */
static void
hsvtorgb(RwReal h, RwReal s, RwReal v, RwRGBA * col)
{
    RwReal f, p1, p2, p3;
    RwInt32 i;

    if (h < 0.0f)
    {
        h = 0.0f;
    }
    else if (h > 1.0f)
    {
        h = h - (RwReal)floor(h);
    }

    h *= 6.0f;
    i = (RwInt32)(h);
    f = h - (RwReal)i;
    p1 = (v * (1.0f - s));
    p2 = (v * (1.0f - (s * f)));
    p3 = (v * (1.0f - (s * (1.0f - f))));

    switch (i)
    {
        case 0:
            col->red   = (RwUInt8) ((v * 255.0f));
            col->green = (RwUInt8) ((p3 * 255.0f));
            col->blue  = (RwUInt8) ((p1 * 255.0f));
            break;

        case 1:
            col->red   = (RwUInt8) ((p2 * 255.0f));
            col->green = (RwUInt8) ((v * 255.0f));
            col->blue  = (RwUInt8) ((p1 * 255.0f));
            break;

        case 2:
            col->red   = (RwUInt8) ((p1 * 255.0f));
            col->green = (RwUInt8) ((v * 255.0f));
            col->blue  = (RwUInt8) ((p3 * 255.0f));
            break;

        case 3:
            col->red   = (RwUInt8) ((p1 * 255.0f));
            col->green = (RwUInt8) ((p2 * 255.0f));
            col->blue  = (RwUInt8) ((v * 255.0f));
            break;

        case 4:
            col->red   = (RwUInt8) ((p3 * 255.0f));
            col->green = (RwUInt8) ((p1 * 255.0f));
            col->blue  = (RwUInt8) ((v * 255.0f));
            break;

        case 5:
            col->red   = (RwUInt8) ((v * 255.0f));
            col->green = (RwUInt8) ((p1 * 255.0f));
            col->blue  = (RwUInt8) ((p2 * 255.0f));
            break;
    }

    return;

}



/*
 *****************************************************************************
 */
static void
RenderPage2(RwReal dTime, RwReal animTime)
{
    Rt2dPath *path;
    Rt2dBBox bbox;
    RwV2d anchor;
    RwReal h;
    RwInt32 i;
    RwRGBA col[] = {
        {  0,   0,   0, 255},
        {255, 255,   0, 255},
        {255, 255, 255, 255},
        {255,   0, 255, 255},
        {128, 255,   0, 255},
        {  0, 255, 255, 255},
        { 64,  64,  64, 255}
    };

    /*
     * Background fill (static)...
     */
    Rt2dCTMPush();
    Rt2dCTMSetIdentity();
    {
        static RwChar _fonts_layout[] =
            RWSTRING("Fonts & Layout");
        path = Rt2dPathCreate();
        Rt2dDeviceGetClippath(path);
        Rt2dBrushSetRGBA(Brush, &col[0], &col[0], &col[0], &col[0]);
        Rt2dPathFill(path, Brush);
        Rt2dPathDestroy(path);

        /* and a title */
        h = Rt2dFontGetHeight(Sans[3]);
        Rt2dBrushSetRGBA(Brush, &col[1], &col[1], &col[1], &col[1]);
        bbox.x = 0.0f;
        bbox.y = WinBBox.h-h;
        bbox.w = WinBBox.w;
        bbox.h = h;
        Rt2dFontFlow(Sans[3], _fonts_layout, h, &bbox, rt2dJUSTIFYCENTER, Brush);
    }
    Rt2dCTMPop();

    if (Rt2dVisible(WinBBox.x+0.05f, WinBBox.y+0.05f, WinBBox.w-0.1f, WinBBox.h-0.1f))
    {
        char upper[] = RWSTRING("ABCDEFGHIJKLMNOPQRSTUVWXYZ,.0123456789");
        char lower[] = RWSTRING("abcdefghijklmnopqrstuvwxyz,.!£$%^&()");

        Rt2dBrushSetRGBA(Brush, &col[2], &col[2], &col[2], &col[2]);

        anchor.y = WinBBox.h - h - h;
        for (i=0; i<4 && anchor.y > 0.1f; i++)
        {
            anchor.x = 0.1f;
            Rt2dFontShow(Sans[i], upper, Rt2dFontGetHeight(Sans[i]), &anchor, Brush);
            anchor.y -= Rt2dFontGetHeight(Sans[i]);

            anchor.x = 0.1f;
            Rt2dFontShow(Sans[i], lower, Rt2dFontGetHeight(Sans[i]), &anchor, Brush);
            anchor.y -= Rt2dFontGetHeight(Sans[i]) * 1.25f;
        }

        anchor.y -= Rt2dFontGetHeight(Sans[2]);
        for (i=0; i<4 && anchor.y > 0.1f; i++)
        {
            anchor.x = 0.1f;
            Rt2dFontShow(Serif[i], upper, Rt2dFontGetHeight(Serif[i]), &anchor, Brush);
            anchor.y -= Rt2dFontGetHeight(Serif[i]);

            anchor.x = 0.1f;
            Rt2dFontShow(Serif[i], lower, Rt2dFontGetHeight(Serif[i]), &anchor, Brush);
            anchor.y -= Rt2dFontGetHeight(Serif[i]) * 1.25f;
        }
    }


    Rt2dCTMPush();
    Rt2dCTMTranslate(0.0f, -WinBBox.h);
    if (Rt2dVisible(WinBBox.x+0.05f, WinBBox.y+0.05f, WinBBox.w-0.1f, WinBBox.h-0.1f))
    {
        /*
         * Animated strings
         */
        Rt2dCTMPush();
        {
            RwReal h;

            Rt2dCTMTranslate(0.1f * WinBBox.w, 0.5f * WinBBox.h);

            h = Rt2dFontGetHeight(Font[0]);
            Rt2dCTMScale(1.0f, 1.0f + RwRealAbs((RwReal)(RwSin(animTime * 2.5f))) * 1.0f);

            Rt2dBrushSetRGBA(Brush, &col[6], &col[6], &col[6], &col[6]);

            anchor.x = 0.0f;
            anchor.y = 0.0f;
            Rt2dFontShow(Font[0], RWSTRING("BOUNCING GLYPHS"), h, &anchor, Brush);
        }
        Rt2dCTMPop();

        /*
         *  Logo Mandela in the middle
         */
        Rt2dCTMPush();
        {
            RwRGBA inner, outer;

            Rt2dCTMTranslate(0.5f * WinBBox.w, 0.5f * WinBBox.h);
            Rt2dCTMRotate(animTime * 20.0f);

            hsvtorgb(animTime, 1.0f, 1.0f, &inner);
            inner.alpha = 255;
            hsvtorgb(animTime + 0.25f, 1.0f, 0.75f, &outer);
            outer.alpha = 255;

            Rt2dBrushSetRGBA(Brush, &inner, &outer, &outer, &inner);
            h = Rt2dFontGetHeight(Sans[1]);
            for (i=0; i<12; i++)
            {
                anchor.x = 0.0f;
                anchor.y = 0.0f;

                Rt2dCTMPush();
                {
                    Rt2dCTMTranslate(h*1.1f, h * -0.5f);
                    Rt2dFontShow(Sans[1], RWSTRING("RenderWare 3.0"), h, &anchor, Brush);
                }
                Rt2dCTMPop();

                Rt2dCTMRotate(30.0f);
            }
        }
        Rt2dCTMPop();

        /*
         * Smooth string fade-in
         */
        Rt2dCTMPush();
        {
                static RwReal readlocf[3], dreadlocf[3];
                static RwChar *readloc[3];
                static RwChar msg[] =
                    RWSTRING("In Xamdu did Cublai Can build a stately Palace, encompassing sixteene miles");

                RwChar savech;
                RwReal h;
                RwInt32 ccount;
                RwRGBA tcol[] = {
                    {  0, 255, 0, 255},
                    {255, 255, 0, 255},
                    {255,   0, 0, 255},
                    {255,   0, 0, 255},
                    {  0, 255, 0,   0},
                    {255, 255, 0,   0},
                };

                Rt2dCTMTranslate(0.05f, 0.05f);

                h = Rt2dFontGetHeight(Sans[0]);
                anchor.x = 0.0f;
                anchor.y = 0.0f;

                /* need a reset? */
                if (!readloc[0] || *readloc[0] == '\0')
                {
                    readlocf[0] = -30.0f;
                    dreadlocf[0] = 30.0f;
                    readloc[0] = msg;

                    readlocf[1] = -20.0f;
                    dreadlocf[1] = 30.0f;
                    readloc[1] = msg;

                    readlocf[2] = 0.0f;
                    dreadlocf[2] = 30.0f;
                    readloc[2] = msg;
                }

                /* update string pointers */
                for (i=0; i<3; i++)
                {
                    readlocf[i] += dTime * dreadlocf[i];
                    ccount = (RwInt32) readlocf[i];

                    if (ccount > 0)
                    {
                        /* leave only fractional part */
                        readlocf[i] -= (RwReal) ccount;

                        /* make sure we stay within string boundary */
                        while(ccount--)
                        {
                            readloc[i]++;

                            if (*readloc[i] == '\0')
                            {
                                /* stop future increments */
                                dreadlocf[i] = 0.0f;
                                break;
                            }
                        }
                    }
                }

                /* regular string */
                Rt2dBrushSetRGBA(Brush, &tcol[0], &tcol[0], &tcol[1], &tcol[1]);
                savech = *readloc[0];
                *readloc[0] = '\0';
                Rt2dFontShow(Sans[0], &msg[0], h, &anchor, Brush);
                *readloc[0] = savech;

                /* up to bright part */
                Rt2dBrushSetRGBA(Brush, &tcol[0], &tcol[2], &tcol[3], &tcol[1]);
                savech = *readloc[1];
                *readloc[1] = '\0';
                Rt2dFontShow(Sans[0], readloc[0], h, &anchor, Brush);
                *readloc[1] = savech;

                /* down to fade-in part */
                Rt2dBrushSetRGBA(Brush, &tcol[2], &tcol[4], &tcol[5], &tcol[3]);
                savech = *readloc[2];
                *readloc[2] = '\0';
                Rt2dFontShow(Sans[0], readloc[1], h, &anchor, Brush);
                *readloc[2] = savech;
        }
        Rt2dCTMPop();
    }
    Rt2dCTMPop();

    /*
     * Flowing text in a box
     */
    Rt2dCTMPush();
    Rt2dCTMTranslate(0.0f, WinBBox.h);
    if (Rt2dVisible(WinBBox.x+0.05f, WinBBox.y+0.05f, WinBBox.w-0.1f, WinBBox.h-0.1f))
    {
        static RwChar bodytext[] =
            RWSTRING("Changing the view window is analogous ")
            RWSTRING("to changing the setting on a zoom ")
            RWSTRING("lens in a real camera. Wide-angle ")
            RWSTRING("views, encompassing a large proportion ")
            RWSTRING("of the world, can be simulated by ")
            RWSTRING("using a large view window. However, the ")
            RWSTRING("effect is to make objects in the ")
            RWSTRING("world appear smaller. Conversely, telephoto ")
            RWSTRING("views are achieved by using a small ")
            RWSTRING("view window. Objects in the world ")
            RWSTRING("will appear larger, i.e. magnified, ")
            RWSTRING("though a smaller proportion of the world ")
            RWSTRING("will appear in the view window. The ")
            RWSTRING("camera’s view window has a default ")
            RWSTRING("width and height of 2.0 units, giving ")
            RWSTRING("roughly a normal eye-view. (See API ")
            RWSTRING("function RwCameraSetViewWindow.) ");

        /*
         * Right...
         */
        bbox.x = 0.00f;
        bbox.y = 0.02f;
        bbox.w = WinBBox.w * 0.333f;
        bbox.h = WinBBox.h * 0.90f;
        h = Rt2dFontGetHeight(Sans[0]);
        Rt2dBrushSetRGBA(Brush, &col[3], &col[4], &col[4], &col[3]);
        Rt2dFontFlow(Sans[0], bodytext, h, &bbox, rt2dJUSTIFYRIGHT, Brush);

        path = Rt2dPathCreate();
        Rt2dPathRect(path, bbox.x, bbox.y, bbox.w, bbox.h);
        Rt2dBrushSetWidth(Brush, 0.005f);
        Rt2dBrushSetRGBA(Brush, &col[2], &col[2], &col[2], &col[2]);
        Rt2dPathStroke(path, Brush);
        Rt2dPathDestroy(path);

        /*
         * Centre...
         */
        bbox.x = WinBBox.w * 0.333f;
        bbox.y = 0.02f;
        bbox.w = WinBBox.w * 0.333f;
        bbox.h = WinBBox.h * 0.90f;
        h = Rt2dFontGetHeight(Sans[1]);
        Rt2dBrushSetRGBA(Brush, &col[4], &col[5], &col[5], &col[4]);
        Rt2dFontFlow(Sans[1], bodytext, h, &bbox, rt2dJUSTIFYCENTER, Brush);

        path = Rt2dPathCreate();
        Rt2dPathRect(path, bbox.x, bbox.y, bbox.w, bbox.h);
        Rt2dBrushSetWidth(Brush, 0.005f);
        Rt2dBrushSetRGBA(Brush, &col[2], &col[2], &col[2], &col[2]);
        Rt2dPathStroke(path, Brush);
        Rt2dPathDestroy(path);

        /*
         * Left...
         */
        bbox.x = WinBBox.w * 0.666f;
        bbox.y = 0.02f;
        bbox.w = WinBBox.w * 0.333f;
        bbox.h = WinBBox.h * 0.90f;
        h = Rt2dFontGetHeight(Serif[0]);
        Rt2dBrushSetRGBA(Brush, &col[5], &col[3], &col[3], &col[5]);
        Rt2dFontFlow(Serif[0], bodytext, h, &bbox, rt2dJUSTIFYLEFT,Brush);

        path = Rt2dPathCreate();
        Rt2dPathRect(path, bbox.x, bbox.y, bbox.w, bbox.h);
        Rt2dBrushSetWidth(Brush, 0.005f);
        Rt2dBrushSetRGBA(Brush, &col[2], &col[2], &col[2], &col[2]);
        Rt2dPathStroke(path, Brush);
        Rt2dPathDestroy(path);
    }
    Rt2dCTMPop();

    /*
     * Outline fonts...
     */
    Rt2dCTMPush();
    Rt2dCTMTranslate(-WinBBox.w, 0.0f);
    if (Rt2dVisible(WinBBox.x+0.05f, WinBBox.y+0.05f, WinBBox.w-0.1f, WinBBox.h-0.1f))
    {
        Rt2dCTMPush();
        Rt2dCTMTranslate(0.0f, WinBBox.h*0.4f);

        Rt2dBrushSetRGBA(Brush, &col[1], &col[1], &col[5], &col[5]);
        Rt2dBrushSetWidth(Brush, 0.01f);

        if (Rt2dVisible(0.0f, 0.0f, WinBBox.w, 0.1f))
        {
            anchor.x = WinBBox.w * 0.3f;
            anchor.y = 0.0f;
            Rt2dFontShow(Serif[4], RWSTRING("Times-Roman"), 0.1f, &anchor, Brush);
        }
        Rt2dCTMTranslate(0.0f, 0.1f);

        if (Rt2dVisible(0.0f, 0.0f, WinBBox.w, 0.1f))
        {
            anchor.x = WinBBox.w * 0.3f;
            anchor.y = 0.0f;
            Rt2dFontShow(Serif[8], RWSTRING("Courier"), 0.1f, &anchor, Brush);
        }
        Rt2dCTMTranslate(0.0f, 0.1f);

        if (Rt2dVisible(0.0f, 0.0f, WinBBox.w, 0.1f))
        {
            anchor.x = WinBBox.w * 0.3f;
            anchor.y = 0.0f;
            Rt2dFontShow(Sans[4], RWSTRING("Helvetica"), 0.1f, &anchor, Brush);
        }
        Rt2dCTMTranslate(0.0f, 0.1f);

        if (Rt2dVisible(0.0f, 0.0f, WinBBox.w, 0.1f))
        {
            anchor.x = WinBBox.w * 0.3f;
            anchor.y = 0.0f;
            Rt2dFontShow(Font[1], RWSTRING("Hiragana"), 0.1f, &anchor, Brush);
        }
        Rt2dCTMTranslate(0.0f, 0.1f);

        Rt2dCTMPop();
    }
    Rt2dCTMPop();

    return;

}



/*
 *****************************************************************************
 */
static void
RenderPage3(RwReal animTime)
{
    Rt2dPath *path;
    RwRGBA col[] = {
        {160,   0,   0, 255},
        {160, 128,   0, 255},
        { 32, 128,   0, 255},
        {  0, 255, 255, 255},
        {255, 255, 255, 255},
        {128, 255, 255,  16}
    };

    /*
     * A border around screen area...
     */
    Rt2dCTMPush();
    {
        Rt2dCTMSetIdentity();

        path = Rt2dPathCreate();
        Rt2dDeviceGetClippath(path);
        Rt2dBrushSetRGBA(Brush, &col[1], &col[1], &col[0], &col[0]);
        Rt2dPathFill(path, Brush);
        Rt2dPathDestroy(path);
    }
    Rt2dCTMPop();


    Rt2dCTMPush();
    {
        /* quick'n'dirty method of getting 'pixel' coordinates */
        Rt2dCTMScale(WinAspect / WinWidth, 1.0f / WinHeight);

        /* logo */
        path = Rt2dPathCreate();
        Rt2dPathMoveto(path, 0.0f, WinHeight*0.75f);
        Rt2dPathRLineto(path, 280.0f, 0.0f);
        Rt2dPathRCurveto(path, 20.0f, 0.0f, 20.0f, 30.0f, 40.0f, 30.0f);

        Rt2dPathRLineto(path, 300.0f, 0.0f);
        Rt2dPathRCurveto(path, 19.0f, 0.0f, 19.0f, 20.0f, 0.0f, 20.0f);

        Rt2dPathRLineto(path, -320.0f, 0.0f);
        Rt2dPathRCurveto(path, -20.0f, 0.0f, -20.0f, -10.0f, -40.0f, -10.0f);

        Rt2dPathRLineto(path, -260.0f, 0.0f);
        Rt2dPathClose(path);

        Rt2dBrushSetWidth(Brush, 4.0f);
        Rt2dBrushSetRGBA(Brush, &col[2], &col[2], &col[3], &col[3]);
        Rt2dPathStroke(path, Brush);
        Rt2dPathDestroy(path);
    }
    Rt2dCTMPop();

    /*
     *  Draw some floating 'E's
     */
    Rt2dCTMPush();
    {
        RwRGBA ecol[] =
        {
            {255,255,0,128},
            {0,255,255,128},
            {255,0,255,128}
        };
        RwV2d pos[] =
        {
            {0.2f,0.7f},
            {0.65f,0.5f},
            {0.75f,0.3f}
        };
        RwReal phase;
        RwInt32 i;

        phase = animTime * 0.25f;
        phase = phase - (RwReal)((RwInt32)phase);

        for (i=0; i<3; i++)
        {
            RwReal scale;

            scale = phase > 0.5f ? 1.0f - phase : phase;
            scale = 0.5f + scale * 4.5f;
            Rt2dCTMPush();
            Rt2dCTMTranslate(WinBBox.w*pos[i].x,WinBBox.h*pos[i].y);
            Rt2dCTMTranslate(0.25f,0.25f);
            Rt2dCTMRotate(phase * 360.0f);
            Rt2dCTMTranslate(-0.25f,-0.25f);
            Rt2dCTMScale(scale, scale);
            Rt2dBrushSetRGBA(Brush, &ecol[i], &ecol[i], &ecol[i], &ecol[i]);
            Rt2dPathFill(Echar, Brush);
            Rt2dCTMPop();

            phase += 0.25f;
            if (phase > 1.0f)
            {
                phase -= 1.0f;
            }
        }
    }
    Rt2dCTMPop();

    /*
     * Draw some abstract circles
     */
    Rt2dCTMPush();
    {
        RwV2d pos[] =
        {
            {0.15f,0.3f},
            {0.65f,0.7f},
            {0.75f,0.5f}
        };
        RwReal phase;
        RwInt32 i;

        path = Rt2dPathCreate();
        Rt2dPathOval(path, -1.0f, -1.0f, 2.0f, 2.0f);
        Rt2dBrushSetRGBA(Brush, &col[4], &col[4], &col[5], &col[5]);

        phase = animTime * 0.2f;
        phase = phase - (RwReal)((RwInt32)phase);
        for (i=0; i<3; i++)
        {
            Rt2dCTMPush();
            Rt2dCTMTranslate(WinBBox.w*pos[i].x,WinBBox.h*pos[i].y);
            Rt2dCTMScale(0.1f + phase, 0.1f + phase);

            Rt2dBrushSetWidth(Brush, 0.1f+(phase * 0.2f));
            Rt2dPathStroke(path, Brush);
            Rt2dCTMPop();

            phase += 0.3f;
            if (phase > 1.0f)
            {
                phase -= 1.0f;
            }
        }
        Rt2dPathDestroy(path);
    }
    Rt2dCTMPop();

    return;

}


/*
 *****************************************************************************
 */
RwCamera *
RenderFrame(RwCamera *camera,
            RwInt32 x __RWUNUSED__,
            RwInt32 y __RWUNUSED__,
            void *data  __RWUNUSED__)
{
    /*
     * Renders a single frame to the given device...
     */

    RwCameraClear(camera, &BGNDColor, rwCAMERACLEARIMAGE);

    if (RwCameraBeginUpdate(camera))
    {
        if( MenuGetStatus() != HELPMODE )
        {
            RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *) FALSE);
            RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *) FALSE);
            RwRenderStateSet(rwRENDERSTATESHADEMODE, (void *) rwSHADEMODEGOURAUD);
            RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void *) rwFILTERLINEAR);
            RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *) TRUE);

            switch (PageNumber)
            {
                case 1:
                    RenderPage1(AnimationTime);
                    break;

                case 2:
                    RenderPage2(DeltaTime, AnimationTime);
                    break;

                case 3:
                    RenderPage3(AnimationTime);
                    break;

                default:
                    break;
            }

            /*
             * Render terminal with Rt2d tools...
             */
            if (DisplayConsole)
            {
                RwV2d anchor;
                RwRGBA col[] = {{255, 128, 0, 255}, {192, 0, 0, 255}};
                RwReal h;
                RwInt32 i;
                RwChar *cur;

                Rt2dCTMPush();
                Rt2dCTMSetIdentity();

                {
                    RwV2d xstep,ystep,origin;

                    Rt2dDeviceGetStep(&xstep,&ystep,&origin);
                    Rt2dCTMTranslate(xstep.x*0.5f,xstep.y*0.5f);
                    Rt2dCTMTranslate(ystep.x*0.5f,ystep.y*0.5f); 
                }


                h = Rt2dFontGetHeight(Term);
                Rt2dBrushSetRGBA(Brush, &col[0], &col[0], &col[1], &col[1]);

                cur = RsGlobal.terminal->screen;
                anchor.y = 1.0f + h * (TermLines + 1);

                for (i = 0; i < RsGlobal.terminal->height; i++)
                {
                    anchor.x = 0.0f;

                    Rt2dFontShow(Term, cur, h, &anchor, Brush);

                    cur += RsGlobal.terminal->width;

                    anchor.y -= h;
                }

                Rt2dCTMPop();
            }
        }

        MenuRender( camera, NULL );

#ifdef RWMETRICS
            RsMetricsRender();
#endif /* RWMETRICS */

        RwCameraEndUpdate(camera);
    }

    /*
     * Copy the rendering to the output device...
     * Only if we are not doing tile rendering.
     */
    if (!TileRender)
    {
        RsCameraShowRaster(camera);
    }

    return camera;
}

/*
 *****************************************************************************
 */
RwBool
Initialize2D(RwCamera *camera)
{
    RwChar tempStr[256];
    RwUInt32 i;
    RwChar *path;

    Rt2dOpen(camera);

    Rt2dDeviceSetFlat((RwReal)(0.5));

    Brush = Rt2dBrushCreate();

    rwsprintf(tempStr, RWSTRING("fonts%cmisc%c"), 
              RsPathGetSeparator(), RsPathGetSeparator());
    path = RsPathnameCreate(tempStr);
    Rt2dFontSetPath(path);
    RsPathnameDestroy(path);
    Font[0] = Rt2dFontRead(RWSTRING("fbigtxt"));
    Font[1] = Rt2dFontRead(RWSTRING("ch"));
    Sans[4] = Rt2dFontRead(RWSTRING("helv"));
    Serif[4] = Rt2dFontRead(RWSTRING("tr"));
    Serif[8] = Rt2dFontRead(RWSTRING("courier"));

    rwsprintf(tempStr, RWSTRING("fonts%ct%c"),
              RsPathGetSeparator(), RsPathGetSeparator());
    path = RsPathnameCreate(tempStr);
    Rt2dFontSetPath(path);
    RsPathnameDestroy(path);
    Sans[0] = Rt2dFontRead(RWSTRING("t10"));
    Sans[1] = Rt2dFontRead(RWSTRING("t12"));
    Sans[2] = Rt2dFontRead(RWSTRING("t14"));
    Sans[3] = Rt2dFontRead(RWSTRING("t16"));

    rwsprintf(tempStr, RWSTRING("fonts%ctnr%c"),
              RsPathGetSeparator(), RsPathGetSeparator());
    path = RsPathnameCreate(tempStr);
    Rt2dFontSetPath(path);
    RsPathnameDestroy(path);
    Serif[0] = Rt2dFontRead(RWSTRING("tnr10"));
    Serif[1] = Rt2dFontRead(RWSTRING("tnr12"));
    Serif[2] = Rt2dFontRead(RWSTRING("tnr14"));
    Serif[3] = Rt2dFontRead(RWSTRING("tnr16"));

    rwsprintf(tempStr, RWSTRING("fonts%ccn%c"), 
              RsPathGetSeparator(), RsPathGetSeparator());
    path = RsPathnameCreate(tempStr);
    Rt2dFontSetPath(path);
    RsPathnameDestroy(path);
    Serif[5] = Rt2dFontRead(RWSTRING("cn12"));
    Serif[6] = Rt2dFontRead(RWSTRING("cn14"));
    Serif[7] = Rt2dFontRead(RWSTRING("cn16"));

    /*
     * Font used to display the terminal...
     */
    Term = Rt2dFontRead(RWSTRING("cn8"));

    Circle = Rt2dPathCreate();
    Rt2dPathOval(Circle, (RwReal)(-1.0), (RwReal)(-1.0), (RwReal)(2.0), (RwReal)(2.0));
    Rt2dDeviceSetFlat(5.0f);
    Rt2dPathFlatten(Circle);
    Rt2dDeviceSetFlat(0.5f);

    Echar = Rt2dPathCreate();
    Rt2dPathMoveto(Echar, 0.418f, 0.159f);
    Rt2dPathCurveto(Echar, 0.395f,0.09f,0.348f,0.0540063f,0.281f,0.0540063f);
    Rt2dPathCurveto(Echar, 0.23f,0.0540063f,0.186f,0.077004f,0.157f,0.118f);
    Rt2dPathCurveto(Echar, 0.136f,0.149f,0.128f,0.18f,0.127f,0.234f);
    Rt2dPathLineto(Echar, 0.127f,0.254f);
    Rt2dPathLineto(Echar, 0.127f,0.274f);
    Rt2dPathLineto(Echar, 0.127f,0.302006f);
    Rt2dPathCurveto(Echar, 0.136f,0.399002f,0.195f,0.462f,0.279f,0.462f);
    Rt2dPathCurveto(Echar, 0.364f,0.462f,0.423f,0.396002f,0.423f,0.302006f);

    Rt2dPathLineto(Echar, 0.127f,0.302006f);
    Rt2dPathLineto(Echar, 0.127f,0.234f);
    Rt2dPathLineto(Echar, 0.513f,0.234f);

    Rt2dPathCurveto(Echar, 0.513f,0.309f,0.508f,0.357f,0.494f,0.394f);
    Rt2dPathCurveto(Echar, 0.463f,0.484f,0.381f,0.539006f,0.28f,0.539006f);
    Rt2dPathCurveto(Echar, 0.134f,0.539006f,0.04f,0.428004f,0.04f,0.255f);
    Rt2dPathCurveto(Echar, 0.04f,0.082f,0.13f,-0.023f,0.278f,-0.023f);
    Rt2dPathCurveto(Echar, 0.398f,-0.023f,0.481f,0.045f,0.502f,0.159f);

    Rt2dPathClose(Echar);

#if (defined (RWTARGET_sky) || defined (RWTARGET_sky2))  /* On PS2 */
    Rt2dDeviceSetFlat(0.5f);
#else
    Rt2dDeviceSetFlat(1.0f);
#endif

    Rt2dPathFlatten(Echar);
    Rt2dDeviceSetFlat(0.5f);

    Hand = Rt2dPathCreate();
    Rt2dPathMoveto(Hand, (RwReal)(0.0), (RwReal)(0.0));
    Rt2dPathLineto(Hand, (RwReal)(0.7), (RwReal)(-0.1));
    Rt2dPathLineto(Hand, (RwReal)(1.0), (RwReal)(0.0));
    Rt2dPathLineto(Hand, (RwReal)(0.7), (RwReal)(0.1));
    Rt2dPathClose(Hand);

    /*
     * Load the textures...
     */
    rwsprintf(tempStr, RWSTRING("textures%c"), RsPathGetSeparator());
    path = RsPathnameCreate(tempStr);
    RwImageSetPath(path);
    RsPathnameDestroy(path);

    for (i = 0; i < NUMTEXTURES; i++)
    {
        Textures[i] = RwTextureRead(TextureNames[i], NULL);
        if (Textures[i])
        {
            RwTextureSetFilterMode(Textures[i], rwFILTERLINEAR);
            RwTextureSetAddressing(Textures[i], rwTEXTUREADDRESSWRAP);
        }
    }

    TermLines = 14;
    DTermLines = 0;

    return TRUE;
}



/*
 *****************************************************************************
 */
void
Terminate2D(void)
{
    RwInt32 i;

    for (i=2; i>=0; i--)
    {
        RwTextureDestroy(Textures[i]);
    }

    if (Hand)
    {
        Rt2dPathDestroy(Hand);
        Hand = NULL;
    }

    if (Echar)
    {
        Rt2dPathDestroy(Echar);
        Echar = NULL;
    }

    if (Circle)
    {
        Rt2dPathDestroy(Circle);
        Circle = NULL;
    }

    if (Term)
    {
        Rt2dFontDestroy(Term);
        Term = NULL;
    }

    for (i=8; i>=0; i--)
    {
        if (Serif[i])
        {
            Rt2dFontDestroy(Serif[i]);
            Serif[i] = NULL;
        }
    }

    for (i=4; i>=0; i--)
    {
        if (Sans[i])
        {
            Rt2dFontDestroy(Sans[i]);
            Sans[i] = NULL;
        }
    }

    if (Font[1])
    {
        Rt2dFontDestroy(Font[1]);
        Font[1] = NULL;
    }

    if (Font[0])
    {
        Rt2dFontDestroy(Font[0]);
        Font[0] = NULL;
    }

    if (Brush)
    {
        Rt2dBrushDestroy(Brush);
        Brush = NULL;
    }

    Rt2dClose();

    return;

}

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