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

/****************************************************************************
 *
 * clmprndr.c
 *
 * Copyright (C) 2001 Criterion Technologies.
 *
 * Original author: Alexandre Hadjadj
 * Reviewed by:
 *
 * Purpose: Hierarchie, WireFrame, Normals and Bounding Boxes
 *          rendering call backs
 *
 ****************************************************************************/

#include "rwcore.h"
#include "rpworld.h"
#include "rprandom.h"

#include "skeleton.h"

#include "main.h"

#include "clmpview.h"
#include "clmppick.h"
#include "clmprndr.h"

/*
 * Number of immediate mode vertices to transform and render at any one time...
 */
#define DISPATCHSIZE (1000)

typedef struct _TriStripData
{
    RwMatrix *matrix;
    RwV3d *vertices;
    RwBool lengths;
} TriStripData;

RwIm3DVertex *Im3DVertices = (RxObjSpace3DVertex *)NULL;

static RwRGBA Red = { 255, 0, 0, 255 };
static RwRGBA Cyan = { 0, 255, 255, 255 };
static RwRGBA Green = { 0, 255, 0, 255 };
static RwRGBA Blue = { 0, 0, 255, 255 };
static RwRGBA Yellow = { 255, 255, 0, 255 };

static RwImVertexIndex AtomicBBoxIndices[24] = {
    0, 1, 1, 3, 3, 2, 2, 0, 4, 5, 5, 7,
    7, 6, 6, 4, 0, 4, 1, 5, 2, 6, 3, 7
};

static RwIm3DVertex *imVertex;
static RwRGBA Color;

/*
 *****************************************************************************
 */
static RwFrame *
HierarchyRender(RwFrame *frame, void *data)
{
    RwMatrix *mat[2];

    mat[0] = RwFrameGetLTM(RwFrameGetParent(frame));
    mat[1] = RwFrameGetLTM(frame);

    RwIm3DVertexSetPos(imVertex,
                       (RwMatrixGetPos (mat[0]))->x,
                       (RwMatrixGetPos (mat[0]))->y,
                       (RwMatrixGetPos (mat[0]))->z);

    RwIm3DVertexSetRGBA(imVertex,
                        Color.red, Color.green, Color.blue,
                        Color.alpha);

    imVertex++;

    RwIm3DVertexSetPos(imVertex,
                       (RwMatrixGetPos (mat[1]))->x,
                       (RwMatrixGetPos (mat[1]))->y,
                       (RwMatrixGetPos (mat[1]))->z);

    RwIm3DVertexSetRGBA(imVertex,
                        Color.red, Color.green, Color.blue,
                        Color.alpha);

    imVertex++;

    return RwFrameForAllChildren(frame, HierarchyRender, NULL);

}


/*
 *****************************************************************************
 */
RpAtomic *
AtomicRenderSkeleton(RpAtomic *atomic, void *data)
{
    RpGeometry *geometry;
    RwMatrix *LTM;

    geometry = RpAtomicGetGeometry(atomic);

    if( geometry )
    {
        RwFrame *frame = RwFrameGetRoot(RpAtomicGetFrame(atomic));
        RwInt32 frameNbr = RwFrameCount(frame);

        if( frame != NULL && frameNbr > 1 )
        {
            imVertex = Im3DVertices;

            if( AtomicSelected == atomic )
            {
                Color = Cyan;
            }
            else
            {
                Color = Red;
            }
            RwFrameForAllChildren(frame, HierarchyRender, NULL);

            RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);
            RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE);
            RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE);

            LTM = RwFrameGetLTM(RpAtomicGetFrame(atomic));

            if( RwIm3DTransform(Im3DVertices, (frameNbr - 1) << 1, 
                                (RwMatrix *)NULL, 0) )
            {
                RwIm3DRenderPrimitive(rwPRIMTYPELINELIST);

                RwIm3DEnd();
            }

        }
    }

    return atomic;
}


/*
 *****************************************************************************
 */
RpAtomic *
AtomicRenderWireMesh(RpAtomic *atomic, void *data)
{
    RpGeometry *geometry;
    RwRaster *raster;
    static RwBool stateSet = FALSE;

    if( !stateSet )
    {
        /*
         * Render state is persistent - only need to set it once...
         */
        RwRenderStateSet(rwRENDERSTATESHADEMODE, (void *)rwSHADEMODEGOURAUD);

        RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE);
        RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE);

        RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE);
        RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO);

        stateSet = TRUE;
    }

    /*
     * ...except for texture raster...
     */
    RwRenderStateGet(rwRENDERSTATETEXTURERASTER, (void *)&raster);
    RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);

    geometry = RpAtomicGetGeometry(atomic);

    if( geometry )
    {
        RwReal interpPos = 1.0f, invInterpPos = 0.0f;
        RpMorphTarget *morphTarget;
        RwInt32 nkf;
        RpTriangle *triangle;
        RwV3d *vertPosStart = (RwV3d *)NULL;
        RwV3d *vertPosEnd = (RwV3d *)NULL;
        RwMatrix *LTM;
        RwInt32 numTri, numImmVert, i;

        nkf = RpGeometryGetNumMorphTargets(geometry);

        if( nkf > 1 )
        {
            RpInterpolator *interp;
            RwInt32 startMorphTarget, endMorphTarget;

            interp = RpAtomicGetInterpolator(atomic);

            interpPos =
                RpInterpolatorGetValue(interp) /
                RpInterpolatorGetScale(interp);

            invInterpPos = 1.0f - interpPos;

            startMorphTarget = RpInterpolatorGetStartMorphTarget(interp);
            endMorphTarget = RpInterpolatorGetEndMorphTarget(interp);

            morphTarget = RpGeometryGetMorphTarget(geometry, startMorphTarget);
            vertPosStart = RpMorphTargetGetVertices(morphTarget);

            morphTarget = RpGeometryGetMorphTarget(geometry, endMorphTarget);
            vertPosEnd = RpMorphTargetGetVertices(morphTarget);
        }
        else
        {
            morphTarget = RpGeometryGetMorphTarget(geometry, 0);
            vertPosStart = RpMorphTargetGetVertices(morphTarget);
        }

        imVertex = Im3DVertices;

        if( AtomicSelected == atomic )
        {
            Color = Red;
        }
        else
        {
            Color = Cyan;
        }

        numTri = RpGeometryGetNumTriangles(geometry);
        triangle = RpGeometryGetTriangles(geometry);

        for( i = 0; i < numTri; i++ )
        {
            RwUInt16 vert0, vert1, vert2;
            RwV3d vertPos[3];

            RpGeometryTriangleGetVertexIndices(geometry, 
                triangle, &vert0, &vert1, &vert2);

            if( nkf > 1 )
            {
                RwV3d tempVec1, tempVec2;

                RwV3dScale(&tempVec1, &vertPosStart[vert0], invInterpPos);
                RwV3dScale(&tempVec2, &vertPosEnd[vert0], interpPos);
                RwV3dAdd(&vertPos[0], &tempVec1, &tempVec2);

                RwV3dScale(&tempVec1, &vertPosStart[vert1], invInterpPos);
                RwV3dScale(&tempVec2, &vertPosEnd[vert1], interpPos);
                RwV3dAdd(&vertPos[1], &tempVec1, &tempVec2);

                RwV3dScale(&tempVec1, &vertPosStart[vert2], invInterpPos);
                RwV3dScale(&tempVec2, &vertPosEnd[vert2], interpPos);
                RwV3dAdd(&vertPos[2], &tempVec1, &tempVec2);

            }
            else
            {
                vertPos[0] = vertPosStart[vert0];
                vertPos[1] = vertPosStart[vert1];
                vertPos[2] = vertPosStart[vert2];
            }

            RwIm3DVertexSetPos(imVertex,
                vertPos[0].x, vertPos[0].y, vertPos[0].z);
            RwIm3DVertexSetRGBA (imVertex, 
                Color.red, Color.green, Color.blue, Color.alpha);
            imVertex++;

            RwIm3DVertexSetPos(imVertex, 
                vertPos[1].x, vertPos[1].y, vertPos[1].z);
            RwIm3DVertexSetRGBA(imVertex, 
                Color.red, Color.green, Color.blue, Color.alpha);
            imVertex++;

            RwIm3DVertexSetPos(imVertex,
                vertPos[1].x, vertPos[1].y, vertPos[1].z);
            RwIm3DVertexSetRGBA(imVertex,
                Color.red, Color.green, Color.blue, Color.alpha);
            imVertex++;

            RwIm3DVertexSetPos(imVertex,
                vertPos[2].x, vertPos[2].y, vertPos[2].z);
            RwIm3DVertexSetRGBA(imVertex,
                Color.red, Color.green, Color.blue, Color.alpha);
            imVertex++;

            RwIm3DVertexSetPos(imVertex,
                vertPos[2].x, vertPos[2].y, vertPos[2].z);
            RwIm3DVertexSetRGBA(imVertex,
                Color.red, Color.green, Color.blue, Color.alpha);
            imVertex++;

            RwIm3DVertexSetPos(imVertex,
                vertPos[0].x, vertPos[0].y, vertPos[0].z);
            RwIm3DVertexSetRGBA(imVertex,
                Color.red, Color.green, Color.blue, Color.alpha);
            imVertex++;

            triangle++;
        }

        LTM = RwFrameGetLTM(RpAtomicGetFrame(atomic));

        RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);
        RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE);
        RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE);
        RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE);
        RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO);

        numImmVert = numTri * 6;

        for( i = 0; i < numImmVert; i += DISPATCHSIZE )
        {
            RwInt32 size;

            size =
                ((numImmVert - i) >
                 DISPATCHSIZE) ? DISPATCHSIZE : (numImmVert - i);

            if( RwIm3DTransform(&Im3DVertices[i], size, LTM, 0 ) )
            {
                RwIm3DRenderPrimitive(rwPRIMTYPELINELIST);

                RwIm3DEnd();
            }
        }
    }

    RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)raster);

    return atomic;
}


/*
 *****************************************************************************
 */
RpAtomic *
AtomicRenderVertexNormals(RpAtomic *atomic, void *data)
{
    RpGeometry *geometry;

    geometry = RpAtomicGetGeometry(atomic);

    if( geometry && (RpGeometryGetFlags(geometry) & rpGEOMETRYNORMALS) )
    {
        RpMorphTarget *morphTarget;
        RwInt32 numVert, nkf, numImmVert;
        RwV3d *vertPosStart = (RwV3d *)NULL;
        RwV3d *vertPosEnd = (RwV3d *)NULL;
        RwV3d *vertNormalStart = (RwV3d *)NULL;
        RwV3d *vertNormalEnd = (RwV3d *)NULL;
        RwReal interpPos = 1.0f, invInterpPos = 0.0f;
        RwMatrix *LTM;
        RwInt32 i;

        nkf = RpGeometryGetNumMorphTargets(geometry);

        if( nkf > 1 )
        {
            RpInterpolator *interp;
            RwInt32 startMorphTarget, endMorphTarget;

            interp = RpAtomicGetInterpolator(atomic);

            interpPos =
                RpInterpolatorGetValue (interp) /
                RpInterpolatorGetScale (interp);

            invInterpPos = 1.0f - interpPos;

            startMorphTarget = RpInterpolatorGetStartMorphTarget(interp);
            endMorphTarget = RpInterpolatorGetEndMorphTarget(interp);

            morphTarget = RpGeometryGetMorphTarget(geometry, startMorphTarget);
            vertPosStart = RpMorphTargetGetVertices(morphTarget);
            vertNormalStart = RpMorphTargetGetVertexNormals(morphTarget);

            morphTarget = RpGeometryGetMorphTarget(geometry, endMorphTarget);
            vertPosEnd = RpMorphTargetGetVertices(morphTarget);
            vertNormalEnd = RpMorphTargetGetVertexNormals(morphTarget);
        }
        else
        {
            morphTarget = RpGeometryGetMorphTarget(geometry, 0);
            vertPosStart = RpMorphTargetGetVertices(morphTarget);
            vertNormalStart = RpMorphTargetGetVertexNormals(morphTarget);
        }

        numVert = RpGeometryGetNumVertices(geometry);

        imVertex = Im3DVertices;

        if( AtomicSelected == atomic )
        {
            Color = Blue;
        }
        else
        {
            Color = Green;
        }

        for( i = 0; i < numVert; i++ )
        {
            RwV3d vertPos, vertNormal;

            if( nkf > 1 )
            {
                RwV3d tempVec1, tempVec2;

                RwV3dScale(&tempVec1, &vertPosStart[i], invInterpPos);
                RwV3dScale(&tempVec2, &vertPosEnd[i], interpPos);
                RwV3dAdd(&vertPos, &tempVec1, &tempVec2);

                RwV3dScale(&tempVec1, &vertNormalStart[i], invInterpPos);
                RwV3dScale(&tempVec2, &vertNormalEnd[i], interpPos);
                RwV3dAdd(&vertNormal, &tempVec1, &tempVec2);
            }
            else
            {
                vertPos = vertPosStart[i];
                vertNormal = vertNormalStart[i];
            }

            RwIm3DVertexSetPos (imVertex,
                vertPos.x, vertPos.y, vertPos.z);
            RwIm3DVertexSetRGBA (imVertex,
                Color.red, Color.green, Color.blue, Color.alpha);
            imVertex++;

            RwIm3DVertexSetPos (imVertex,
                vertPos.x + NormalsScaleFactor * vertNormal.x,
                vertPos.y + NormalsScaleFactor * vertNormal.y,
                vertPos.z + NormalsScaleFactor * vertNormal.z);

            RwIm3DVertexSetRGBA (imVertex,
                Color.red, Color.green, Color.blue, Color.alpha);
            imVertex++;
        }

        LTM = RwFrameGetLTM(RpAtomicGetFrame(atomic));

        RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);
        RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE);
        RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE);

        numImmVert = numVert << 1;

        for( i = 0; i < numImmVert; i += DISPATCHSIZE )
        {
            RwInt32 size;

            size =
                ((numImmVert - i) >
                 DISPATCHSIZE) ? DISPATCHSIZE : (numImmVert - i);

            if( RwIm3DTransform(&Im3DVertices[i], size, LTM, 0) )
            {
                RwIm3DRenderPrimitive(rwPRIMTYPELINELIST);

                RwIm3DEnd();
            }
        }
    }

    return atomic;
}

/*
 *****************************************************************************
 */
static RpMesh *
MeshRenderTriStrip(RpMesh * mesh, RpMeshHeader * meshHeader, void * data)
{
    RwMatrix        *matrix       = ((TriStripData *)data)->matrix;
    RwV3d           *vertices     = ((TriStripData *)data)->vertices;
    RwBool           lengths      = ((TriStripData *)data)->lengths;
    RwImVertexIndex *currentIndex = mesh->indices;
    RwUInt32         vertCounter  = 0;
    
    RwIm3DVertex    *TriStrip;
    RwUInt32         i, j;

    TriStrip = (RwIm3DVertex *)malloc(mesh->numIndices * sizeof(RwIm3DVertex));
    
    Color.red   = 0;
    Color.green = 0;
    Color.blue  = 0;
    Color.alpha = 255;

    if(!lengths)
    {
        RpRandomSeed(mesh->numIndices);
    }

    /* Process the index array, test for duplicate vertices */
    for(i = 0; i < mesh->numIndices; i++)
    {
        RwIm3DVertexSetPos( &TriStrip[i], 
                            vertices[*currentIndex].x,
                            vertices[*currentIndex].y,
                            vertices[*currentIndex].z );

        /* Check indices for join-strip degenerates */
        if((i > 0) && ((*currentIndex) == (*(currentIndex-1))))
        {
            /* This is degenerate - start a new strip if we've got a triangle */
            if(vertCounter >= 3)
            {
                if(lengths)
                {
                    if((vertCounter-3)<NumTriStripAngles)
                    {
                        Color.red = 
                            (RwUInt8)(255 - (255*(vertCounter-3))/NumTriStripAngles);
                    }
                    else
                    {
                        Color.red = 0;
                    }
                }
                else
                {
                    Color.red   = (RwUInt8)(RpRandom() % 200) + 50;
                    Color.green = (RwUInt8)(RpRandom() % 200) + 50;
                    Color.blue  = (RwUInt8)(RpRandom() % 200) + 50;
                }

                /* Colour my verts */
                for(j = 0; j < vertCounter; j++)
                {
                    RwIm3DVertexSetRGBA( &TriStrip[i - (j+1)],
                                         Color.red,
                                         Color.green,
                                         Color.blue,
                                         Color.alpha );
                }

                /* Zero counter */
                vertCounter = 0;
            }
        }
        else
        {
            /* count vertices. Need 3 to make a triangle. */
            vertCounter++;
        }

        /* Point to the next source vertex */
        currentIndex++;
    }

    /* final geometry */
    currentIndex--;
    if(vertCounter >= 3)
    {
        if(lengths)
        {
            if((vertCounter-3)<NumTriStripAngles)
            {
                Color.red = 
                    (RwUInt8)(255 - (255*(vertCounter-3))/NumTriStripAngles);
            }
            else
            {
                Color.red = 0;
            }
        }
        else
        {
            Color.red   = (RwUInt8)(RpRandom() % 200) + 50;
            Color.green = (RwUInt8)(RpRandom() % 200) + 50;
            Color.blue  = (RwUInt8)(RpRandom() % 200) + 50;
        }

        /* Colour my verts */
        for(j = 0; j < vertCounter; j++)
        {
            RwIm3DVertexSetRGBA( &TriStrip[i - (j+1)],
                                 Color.red,
                                 Color.green,
                                 Color.blue,
                                 Color.alpha );
        }
    }

    RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);
    RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE);
    RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE);
    RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE);
    RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO);

    if( RwIm3DTransform(TriStrip, mesh->numIndices, matrix, 0) )
    {                         
        RwIm3DRenderPrimitive(rwPRIMTYPETRISTRIP); 
        RwIm3DEnd();
    }   

    free(TriStrip);

    return mesh;
}


/*
 *****************************************************************************
 */
static RpMesh *
MeshRenderMeshes(RpMesh * mesh, RpMeshHeader * meshHeader, void * data)
{
    RwMatrix        *matrix       = ((TriStripData *)data)->matrix;
    RwV3d           *vertices     = ((TriStripData *)data)->vertices;
    RwImVertexIndex *currentIndex = mesh->indices;
    
    RwIm3DVertex    *TriStrip;
    RwUInt32         i;

    TriStrip = (RwIm3DVertex *)malloc(mesh->numIndices * sizeof(RwIm3DVertex));
    

    RpRandomSeed(mesh->numIndices);
    Color.red   = (RwUInt8)(RpRandom() % 200) + 50;
    Color.green = (RwUInt8)(RpRandom() % 200) + 50;
    Color.blue  = (RwUInt8)(RpRandom() % 200) + 50;
    Color.alpha = 255;

    /* Process the index array, test for duplicate vertices */
    for(i = 0; i < mesh->numIndices; i++, currentIndex++)
    {
        RwIm3DVertexSetPos( &TriStrip[i], 
                            vertices[*currentIndex].x,
                            vertices[*currentIndex].y,
                            vertices[*currentIndex].z );

        RwIm3DVertexSetRGBA( &TriStrip[i],
                             Color.red,
                             Color.green,
                             Color.blue,
                             Color.alpha );
    }

    RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);
    RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE);
    RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE);
    RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE);
    RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO);

    if( RwIm3DTransform(TriStrip, mesh->numIndices, matrix, 0) )
    {                         
        RwIm3DRenderPrimitive(rwPRIMTYPETRISTRIP); 
        RwIm3DEnd();
    }   

    free(TriStrip);

    return mesh;
}

/*
 *****************************************************************************
 */
RpAtomic *
AtomicRenderTriStrip(RpAtomic * atomic, void * data)
{
    RpGeometry *geometry = RpAtomicGetGeometry(atomic);
    TriStripData info = 
    {
        RwFrameGetLTM(RpAtomicGetFrame(atomic)),
        RpMorphTargetGetVertices(RpGeometryGetMorphTarget(geometry, 0)),
        (RwBool)data
    };

    /* Is this tristripped? */
    if(RpGeometryGetFlags(geometry) & rpGEOMETRYTRISTRIP)
    {
        /* Enumerate all meshes */
        RpGeometryForAllMeshes(geometry, MeshRenderTriStrip, &info);
    }
    else
    {
        /* Just render it. */
        RpAtomicRender(atomic);
    }

    return atomic;
}


/*
 *****************************************************************************
 */
RpAtomic *
AtomicRenderMeshes(RpAtomic * atomic, void * data)
{
    RpGeometry *geometry = RpAtomicGetGeometry(atomic);
    TriStripData info = 
    {
        RwFrameGetLTM(RpAtomicGetFrame(atomic)),
        RpMorphTargetGetVertices(RpGeometryGetMorphTarget(geometry, 0)),
        FALSE
    };

    /* Is this tristripped? */
    if(RpGeometryGetFlags(geometry) & rpGEOMETRYTRISTRIP)
    {
        /* Enumerate all meshes */
        RpGeometryForAllMeshes(geometry, MeshRenderMeshes, &info);
    }
    else
    {
        /* Just render it. */
        RpAtomicRender(atomic);
    }

    return atomic;
}


/*
 *****************************************************************************
 */
void
AtomicRenderBoundingBox(RpAtomic * atomic, RwBBox * bbox)
{
    RwInt32 i;
    RwMatrix *LTM;

    imVertex = Im3DVertices;
    Color = Yellow;

    for( i = 0; i < 8; i++ )
    {
        RwIm3DVertexSetPos(imVertex,
            i & 1 ? bbox->sup.x : bbox->inf.x,
            i & 2 ? bbox->sup.y : bbox->inf.y,
            i & 4 ? bbox->sup.z : bbox->inf.z);

        RwIm3DVertexSetRGBA(imVertex,
            Color.red, Color.green, Color.blue, Color.alpha);
        imVertex++;
    }

    RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);
    RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE);
    RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE);

    LTM = RwFrameGetLTM(RpAtomicGetFrame(atomic));

    if( RwIm3DTransform(Im3DVertices, 8, LTM, 0) )
    {
        RwIm3DRenderIndexedPrimitive(rwPRIMTYPELINELIST, AtomicBBoxIndices, 24);

        RwIm3DEnd();
    }

    return;

}


/*
 *****************************************************************************
 */
void
ResizeIm3DVertexArray(void)
{
    RwUInt32 numImmVert, temp;
    RwMemoryFunctions *memFuncs;
    RwIm3DVertex *immVertices;

    /*
     * For the selected atomic bounding box...
     */
    numImmVert = 8;

    if( LodRoot )
    {
        RwInt32 i;

        for( i = 0; i < numLods + 1; i++ )
        {
            /*
             * For the triangle mesh...
             */
            temp = LODClumpStats[i].totalTriangles * 6;

            if( temp > numImmVert )
            {
                numImmVert = temp;
            }

            /*
             * For the vertex normals...
             */
            temp = LODClumpStats[i].totalVertices * 2;

            if( temp > numImmVert )
            {
                numImmVert = temp;
            }
        }
    }
    else
    {
        /*
         * For the triangle mesh...
         */
        temp = ClumpStats.totalTriangles * 6;

        if( temp > numImmVert )
        {
            numImmVert = temp;
        }

        /*
         * For the vertex normals...
         */
        temp = ClumpStats.totalVertices * 2;

        if( temp > numImmVert )
        {
            numImmVert = temp;
        }
    }

    memFuncs = RwOsGetMemoryInterface();

    immVertices = (RxObjSpace3DVertex *)
        memFuncs->rwrealloc(Im3DVertices, 
                            numImmVert * sizeof(RwIm3DVertex));

    if( immVertices == NULL )
    {
        RsErrorMessage(RWSTRING("Memory allocation error."));

        RsEventHandler(rsQUITAPP, NULL);
    }
    else
    {
        Im3DVertices = immVertices;
    }

    return;

}

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

