/**********************************************************************
 *
 * File :     object.c
 *
 * Abstract : Generic object handler. This module handles the depth
 *            sorting and general control flow for the objects in
 *            the street.
 *
 **********************************************************************
 *
 * 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. will not, under any
 * circumstances, be liable for any lost revenue or other damages arising
 * from the use of this file.
 *
 * Copyright (c) 1995 Criterion Software Ltd.
 * All Rights Reserved.
 *
 * RenderWare is a trademark of Canon Inc.
 *
 ************************************************************************/

/*--- Include files ---*/

#include "global.h"     /* Application general includes */

/*--- Structure Definitions ---*/

/* All Objects: A single global variable of this type is declared and used
 *  to hold the data for all of the objects currently defined
 */

typedef struct
{
    Object **oppObjects;
    int nEndObject;
    int nAmoObjects;
    RwV3d vDeletePos;
    int nFirst;             /* 0-2 : 0 x 1 y 2 z */
    int nSecond;
    int nThird;
    int (*fpCompare)(const void *,const void *);
} AllObjects;

AllObjects aoGAll;

/*--- Macor and Magic Number definitions */

#define MAX_NUM     20   /* The maximum depth for an object in cyberstreet
                            any objects that are further away from the camera
                            than this will automatically be deleted */


/* Patch for floating point */

#ifdef RWFLOAT
#define RETURNINT(A) ((int)((A)*65536.0))
#else
#define RETURNINT(A) (A)
#endif

#define INITIAL_OBJECTS  20  /* Initial number of objects */
#define OBJ_INCREASE 5       /* Allocate space for this many objects
                                at a time */

/************************************************************************
 *
 *      Function:       DefaultRender()
 *
 *      Description:    The default object render function. This function
 *                      will be used to render all objects that don't
 *                      explicitly define their own render function
 *
 *      Parameters:     opObj - the object to render
 *
 *      Return Value:   None
 *
 ************************************************************************/
static void DefaultRender(Object *opObj)
{
    if (opObj->cpClump)
    {
        /* The default render action is just to translate the clump to
           its final position and render it */

        RwTranslateMatrix(RwScratchMatrix(),
                          opObj->vPos.x,
                          opObj->vPos.y,
                          opObj->vPos.z, rwREPLACE);

        RwTransformClump(opObj->cpClump, RwScratchMatrix(), rwREPLACE);
        RwRenderClump(opObj->cpClump);
    }
}

/************************************************************************
 *
 *      Function:       DefaultUpdate()
 *
 *      Description:    The default object update function. This function
 *                      will be used to update all objects that don't
 *                      explicitly define their own update function
 *
 *      Parameters:     opObj - the object to update
 *
 *      Return Value:   None
 *
 ************************************************************************/
static void
DefaultUpdate(Object *opObj)
{
  /* Don't do anything for the default object */
  opObj = opObj;
}

/************************************************************************
 *
 *      Function:       DefaultDestroy()
 *
 *      Description:    The default object destroy function. This function
 *                      will be used to destroy all objects that don't
 *                      explicitly define their own destroy function
 *
 *      Parameters:     opObj - the object to destroy
 *
 *      Return Value:   None
 *
 ************************************************************************/
static void
DefaultDestroy(Object *opObj)
{
    /* Destroy the RenderWare clump */
    RwDestroyClump(opObj->cpClump);

    /* free up the object data */
    free(opObj);
}

/************************************************************************
 * Object Depth Sorting:
 * We make the following assumptions about the objects in the cyberstreet scene.
 * 1. If 2 objects occupy the same space then they will be drawn in an
 *    arbitrary order. We don't want to Z buffer them.
 * 2. The objects are small and self contained, therefore a simple depth sort
 *    using the objects origins is sufficient.
 * For these reasons we choose to implement our own scene sorting which because
 * of its specific nature will be quicker than the more general RenderWare
 * scene sorting which will always try to render an accurate result.
 *
 * The algorithm that is used is a simple depth sort, where objects are sorted
 * such that those furthest from the camera are drawn first. We use the standard
 * C library function 'qsort' to perform the sorting.
 *
 * There are 2 mechanisms that are used to determine how best to sort the scene.
 * the implementation of these mechanism varies with camera position.
 * 1. We sort the scene by the most significant axis first down to least
 *    least significant. ie if the camera is looking along the X axis, then
 *    the x component of an objects position is more important than the y
 *    component in determining correct ordering. The order of sorting is
 *    assigned as first, second, third
 *
 * 2. Depending on the direction of the camera's at vector, the comparison
 *    mechanism varies. ie A camera looking along the positive X axis would
 *    sort from maximum X (furthest) to minimum Z (nearest). If the camera
 *    was looking along the negative X axis then the sort order would be from
 *    minimum X (furthest) to maximum X (nearest). Given this, we need 8
 *    comparison functions for qsort which implement all combinations of
 *    camera orientation in the 3 axis X, Y, and Z
 *
 *************************************************************************/

/************************************************************************
 *
 *      Function:       FMaxSMaxTMax()
 *
 *      Description:    qsort comparison function.
 *
 *      Parameters:     oppF - first object
 *                      oppS - second object
 *
 *      Return Value:   -ve if 2nd object is further away than the first
 *                      0 if they occupy the same location
 *                      +ve if the 2nd object is nearer that the first
 *
 ************************************************************************/
static int
FMaxSMaxTMax(const Object **oppF, const Object **oppS)
{
    RwReal nVal;

    /* Compare the position of the 2 objects in the Most Significant Axis */

    if (nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nFirst] +
                ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nFirst] )
    {
        return RETURNINT(nVal);
    }

    /* Most significant Axis comarison was zero so try the next one */
    if (nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nSecond] +
                ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nSecond] )
    {
        return RETURNINT(nVal);
    }

    /* Try the least significant axis */

    nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nThird] +
            ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nThird];

    return RETURNINT(nVal);
}

/************************************************************************
 *
 *      Function:       FMinSMaxTMax()
 *                      
 *      Description:    qsort comparison function. 
 *
 *      Parameters:     oppF - first object
 *                      oppS - second object
 *
 *      Return Value:   -ve if 2nd object is further away than the first
 *                      0 if they occupy the same location
 *                      +ve if the 2nd object is nearer that the first
 *
 ************************************************************************/
static int 
FMinSMaxTMax(const Object **oppF, const Object **oppS)
{
    RwReal nVal;

    /* Compare the position of the 2 objects in the Most Significant Axis */

    if (nVal = ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nFirst] -
               ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nFirst] )
    {
        return RETURNINT(nVal);
    }

    /* Most significant Axis comarison was zero so try the next one */

    if (nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nSecond] +
                ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nSecond] )
    {
        return RETURNINT(nVal);
    }

    /* Try the least significant axis */

    nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nThird] +
            ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nThird];
    
    return RETURNINT(nVal);
}

/************************************************************************
 *
 *      Function:       FMinSMinTMax()
 *                      
 *      Description:    qsort comparison function. 
 *
 *      Parameters:     oppF - first object
 *                      oppS - second object
 *
 *      Return Value:   -ve if 2nd object is further away than the first
 *                      0 if they occupy the same location
 *                      +ve if the 2nd object is nearer that the first
 *
 ************************************************************************/
static int FMinSMinTMax(const Object **oppF,const Object **oppS)
{
    RwReal nVal;

    /* Compare the position of the 2 objects in the Most Significant Axis */

    if (nVal =  ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nFirst] -
                ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nFirst] )
    {
        return RETURNINT(nVal);
    }

    /* Most significant Axis comarison was zero so try the next one */

    if (nVal =  ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nSecond] -
                ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nSecond] ) 
    {
        return RETURNINT(nVal);
    }

    /* Try the least significant axis */

     nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nThird] +
             ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nThird];
    
    return RETURNINT(nVal);
}

/************************************************************************
 *
 *      Function:       FMaxSMinTMax()
 *                      
 *      Description:    qsort comparison function. 
 *
 *      Parameters:     oppF - first object
 *                      oppS - second object
 *
 *      Return Value:   -ve if 2nd object is further away than the first
 *                      0 if they occupy the same location
 *                      +ve if the 2nd object is nearer that the first
 *
 ************************************************************************/
static int 
FMaxSMinTMax(const Object **oppF, const Object **oppS)
{
    RwReal nVal;

    /* Compare the position of the 2 objects in the Most Significant Axis */

    if (nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nFirst] +
                ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nFirst] )
    {
        return RETURNINT(nVal);
    }

    /* Most significant Axis comarison was zero so try the next one */

    if (nVal =  ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nSecond] -
                ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nSecond] )
    {
        return RETURNINT(nVal);
    }

    /* Try the least significant axis */

    nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nThird] +
            ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nThird];
  
    return RETURNINT(nVal);
}

/************************************************************************
 *
 *      Function:       FMaxSMinTMin()
 *                      
 *      Description:    qsort comparison function. 
 *
 *      Parameters:     oppF - first object
 *                      oppS - second object
 *
 *      Return Value:   -ve if 2nd object is further away than the first
 *                      0 if they occupy the same location
 *                      +ve if the 2nd object is nearer that the first
 *
 ************************************************************************/
int FMaxSMinTMin(const Object **oppF,const Object **oppS)
{
    RwReal nVal;

    /* Compare the position of the 2 objects in the Most Significant Axis */

    if (nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nFirst] +
                ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nFirst] )
    {
        return RETURNINT(nVal);
    }

    /* Most significant Axis comarison was zero so try the next one */

    if (nVal = ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nSecond] -
               ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nSecond] )
    {
        return RETURNINT(nVal);
    }

    /* Try the least significant axis */

    nVal = ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nThird] -
           ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nThird];
    
    return RETURNINT(nVal);
}


/************************************************************************
 *
 *      Function:       FMinSMinTMin()
 *                      
 *      Description:    qsort comparison function. 
 *
 *      Parameters:     oppF - first object
 *                      oppS - second object
 *
 *      Return Value:   -ve if 2nd object is further away than the first
 *                      0 if they occupy the same location
 *                      +ve if the 2nd object is nearer that the first
 *
 ************************************************************************/
static int 
FMinSMinTMin(const Object **oppF, const Object **oppS)
{
    RwReal nVal;

    /* Compare the position of the 2 objects in the Most Significant Axis */

    if (nVal = ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nFirst] -
               ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nFirst] )
    {
        return RETURNINT(nVal);
    }

    /* Most significant Axis comarison was zero so try the next one */

    if (nVal = ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nSecond] -
               ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nSecond] ) 
    {
        return RETURNINT(nVal);
    }

    /* Try the least significant axis */

    nVal = ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nThird] -
           ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nThird];
  
    return RETURNINT(nVal);
}

/************************************************************************
 *
 *      Function:       FMinSMaxTMin()
 *                      
 *      Description:    qsort comparison function. 
 *
 *      Parameters:     oppF - first object
 *                      oppS - second object
 *
 *      Return Value:   -ve if 2nd object is further away than the first
 *                      0 if they occupy the same location
 *                      +ve if the 2nd object is nearer that the first
 *
 ************************************************************************/
static int 
FMinSMaxTMin(const Object **oppF, const Object **oppS)
{
    RwReal nVal;

    /* Compare the position of the 2 objects in the Most Significant Axis */

    if (nVal = ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nFirst] -
               ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nFirst] ) 
    {
        return RETURNINT(nVal);
    }

    /* Most significant Axis comarison was zero so try the next one */

    if (nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nSecond] +
                ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nSecond] )
    {
        return RETURNINT(nVal);
    }

    /* Try the least significant axis */

    nVal = ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nThird] -
           ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nThird];
    
    return RETURNINT(nVal);
}

/************************************************************************
 *
 *      Function:       FMaxSMaxTMin()
 *                      
 *      Description:    qsort comparison function. 
 *
 *      Parameters:     oppF - first object
 *                      oppS - second object
 *
 *      Return Value:   -ve if 2nd object is further away than the first
 *                      0 if they occupy the same location
 *                      +ve if the 2nd object is nearer that the first
 *
 ************************************************************************/
static int 
FMaxSMaxTMin(const Object **oppF, const Object **oppS)
{
    RwReal nVal;

    /* Compare the position of the 2 objects in the Most Significant Axis */

    if (nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nFirst] +
                ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nFirst] )
    {
        return RETURNINT(nVal);
    }

    /* Most significant Axis comarison was zero so try the next one */

    if (nVal = -((RwReal *)(&((*oppF)->vPos)))[aoGAll.nSecond] +
                ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nSecond] )
    {
        return RETURNINT(nVal);
    }

    /* Try the least significant axis */

    nVal = ((RwReal *)(&((*oppF)->vPos)))[aoGAll.nThird] -
           ((RwReal *)(&((*oppS)->vPos)))[aoGAll.nThird];
  
    return RETURNINT(nVal);
}


/************************************************************************
 *
 *      Function:       ObjectDelete()
 *                      
 *      Description:    Delete and object from the cyberstreet scene. Rather
 *                      than remove it from it current place in the scene and 
 *                      have to shuffle the list about, we just move it to a
 *                      distant point and then delete all of the objects at 
 *                      this point once the list has been reordered on the 
 *                      next render. 
 *
 *      Parameters:     opObj - object to delete
 *
 *      Return Value:   None
 *
 ************************************************************************/
void ObjectDelete(Object *opObj)
{
    opObj->vPos = aoGAll.vDeletePos;
}

/************************************************************************
 *
 *      Function:       ObjectSetup()
 *                      
 *      Description:    Initialise an objects data structure. 
 *
 *      Parameters:     opObj - object to set up
 *                      x, y, z - Initial position for the object
 *                      cpClump - The RenderWare clump data for the object
 *
 *      Return Value:   None
 *
 ************************************************************************/
void 
ObjectSetup(Object *opObj, RwReal x, RwReal y, RwReal z, RwClump *cpClump)
{
    /* define the functions that will be used to render, update and
     * destroy the object. Not these functions may be overloaded with
     * an object specific routine
     */
    opObj->fpRender = DefaultRender;
    opObj->fpUpdate = DefaultUpdate;
    opObj->fpDestroy = DefaultDestroy;

    /* Initialise the objects position
     */
    opObj->vPos.x = x;
    opObj->vPos.y = y;
    opObj->vPos.z = z;

    /* The object has no extra information initially. This value may
     * be overloaded later
     */
    opObj->pExtra = NULL;

    /* Define the objects clump
     */
    opObj->cpClump = cpClump;
}

/************************************************************************
 *
 *      Function:       AllObjectsUpdate()
 *                      
 *      Description:    Update the position of all of the objects
 *
 *      Parameters:     None
 *
 *      Return Value:   None
 *
 ************************************************************************/
void 
AllObjectsUpdate(void)
{
    int nCount;         /* Temporary loop control variable */
    Object **oppObj;    /* Object pointer used in loop iteration */
    RwV3d vAt;          /* The camera look at vector */
    RwV3d vSize;        /* The absolute x, y, and z for the camera look at 
                           vector */

    /* Get the camera's look at vector */

    RwGetCameraLookAt(cpGCamera,&vAt);

    /* based on the camera's look at vector, determine the position that 
     * will be used as the 'delete position' for this frame. Any objects
     * that are moved to this position will be deleted after the scene
     * has been sorted.
     */

    if (vAt.x < CREAL(0.0)) 
    {
        aoGAll.vDeletePos.x = CREAL(+MAX_NUM);
        vSize.x = -vAt.x;
    } 
    else 
    {
        aoGAll.vDeletePos.x = CREAL(-MAX_NUM);
        vSize.x = +vAt.x;
    }

    if (vAt.y < CREAL(0.0)) 
    {
        aoGAll.vDeletePos.y = CREAL(+MAX_NUM);
        vSize.y = -vAt.y;
    } 
    else 
    {
        aoGAll.vDeletePos.y = CREAL(-MAX_NUM);
        vSize.y = +vAt.y;
    }

    if (vAt.z < CREAL(0.0)) 
    {
        aoGAll.vDeletePos.z = CREAL(+MAX_NUM);
        vSize.z = -vAt.z;
    }
    else 
    {
        aoGAll.vDeletePos.z = CREAL(-MAX_NUM);
        vSize.z = +vAt.z;
    }

    /* Determine the order that the axis will be compared in when performing
     * the depth sort. 
     */

    if (vSize.x > vSize.y) 
    {
        if (vSize.y > vSize.z) 
        {
            /* x > y > z */
            aoGAll.nFirst = 0;
            aoGAll.nSecond = 1;
            aoGAll.nThird = 2;
        } 
        else 
        {
            /* y smallest */
            aoGAll.nThird = 1;
            if (vSize.x > vSize.z) 
            {
                /* x > z > y */
                aoGAll.nFirst = 0;
                aoGAll.nSecond = 2;
            } 
            else 
            {
                /* z > x > y */
                aoGAll.nFirst = 2;
                aoGAll.nSecond = 0;
            }
        }
    }
    else 
    {
        /* y > x */
        if (vSize.y > vSize.z) 
        {
            /* y largest */
            aoGAll.nFirst = 1;
            if (vSize.x > vSize.z) 
            {
                /* y > x > z */
                aoGAll.nSecond = 0;
                aoGAll.nThird = 2;
            }
            else 
            {
                /* y > z > x */
                aoGAll.nSecond = 2;
                aoGAll.nThird = 0;
            }
        } 
        else 
        {
            /* z > y > x */
            aoGAll.nFirst = 2;
            aoGAll.nSecond = 1;
            aoGAll.nThird = 0;
        }
    }

    /* Determine the sort function that we will use based on the cameras
     * at vector
     */

    if (((RwReal *)&vAt)[aoGAll.nFirst] > CREAL(0.0)) 
    {
        /* Max1 */
        if (((RwReal *)&vAt)[aoGAll.nSecond]>CREAL(0.0)) 
        {
            /* Max2 */
            if (((RwReal *)&vAt)[aoGAll.nThird]>CREAL(0.0)) 
            {
                /* Max 3*/
                aoGAll.fpCompare = FMaxSMaxTMax;
            } 
            else 
            {
                /* Min 3 */
                aoGAll.fpCompare = FMaxSMaxTMin;
            }
        } 
        else 
        {
            /* Min2 */
            if (((RwReal *)&vAt)[aoGAll.nThird] > CREAL(0.0)) 
            {
                /* Max 3*/
                aoGAll.fpCompare = FMaxSMinTMax;
            } 
            else 
            {
                /* Min 3 */
                aoGAll.fpCompare = FMaxSMinTMin;
            }
        }
    } 
    else 
    {
        /* Min 1 */
        if (((RwReal *)&vAt)[aoGAll.nSecond]>CREAL(0.0)) 
        {
            /* Max2 */
            if (((RwReal *)&vAt)[aoGAll.nThird]>CREAL(0.0)) 
            {
                /* Max 3*/
                aoGAll.fpCompare = FMinSMaxTMax;
            } 
            else 
            {
                /* Min 3 */
                aoGAll.fpCompare = FMinSMaxTMin;
            }
        } 
        else 
        {
            /* Min2 */
            if (((RwReal *)&vAt)[aoGAll.nThird]>CREAL(0.0)) 
            {
                /* Max 3*/
                aoGAll.fpCompare = FMinSMinTMax;
            } 
            else 
            {
                /* Min 3 */
                aoGAll.fpCompare = FMinSMinTMin;
            }
        }
    }

    /* Call the object update function for all of the objects in the street */

    oppObj = aoGAll.oppObjects;

    for (nCount=0; nCount < aoGAll.nEndObject; nCount++, oppObj++)
    {
        (*oppObj)->fpUpdate(*oppObj);
    }
}


/************************************************************************
 *
 *      Function:       AllObjectsSetup()
 *                      
 *      Description:    Initialise the global object data structure
 *
 *      Parameters:     None
 *
 *      Return Value:   None
 *
 ************************************************************************/
int AllObjectsSetup(void)
{
    aoGAll.oppObjects = (Object **) malloc(sizeof(Object *)*INITIAL_OBJECTS);
    aoGAll.nAmoObjects = INITIAL_OBJECTS;
    aoGAll.nEndObject = 0;

  return TRUE;
}

/************************************************************************
 *
 *      Function:       AllObjectsAddObject()
 *                      
 *      Description:    Add an object to the list of global objects
 *
 *      Parameters:     opObj - the object to add to the global list
 *
 *      Return Value:   None
 *
 ************************************************************************/
void AllObjectsAddObject(Object *opObj)
{
    if (aoGAll.nEndObject == aoGAll.nAmoObjects) 
    {
        /* The object list is full. Grow it a bit */
        aoGAll.oppObjects = realloc(aoGAll.oppObjects,
                                    sizeof(Object *) * (aoGAll.nAmoObjects + OBJ_INCREASE));
        aoGAll.nAmoObjects += OBJ_INCREASE;
    }

    /* Add the object to the list */
    aoGAll.oppObjects[aoGAll.nEndObject++] = opObj;
}

/************************************************************************
 *
 *      Function:       AllObjectsDestroy()
 *                      
 *      Description:    Destroy the global object data structure
 *
 *      Parameters:     None
 *
 *      Return Value:   None
 *
 ************************************************************************/
void 
AllObjectsDestroy(void)
{
    int nCount;
    Object **oppObj;

    /* Destroy all of the objects first */

    oppObj = aoGAll.oppObjects;

    for (nCount=0; nCount < aoGAll.nEndObject; nCount++, oppObj++)
    {
        (*oppObj)->fpDestroy(*oppObj);
    }

    /* Destroy the All Object structure */

    free(aoGAll.oppObjects);
}

/************************************************************************
 *
 *      Function:       AllObjectsRender()
 *                      
 *      Description:    Sort the objects in the scene and then render them
 *                      from furthest to nearest
 *
 *      Parameters:     None
 *
 *      Return Value:   None
 *
 ************************************************************************/
void 
AllObjectsRender(void)
{
    int nCount;     /* temporary loop control variable */
    int nNewEnd;    /* the last object that was rendered */
    Object **oppObj;

    /* Order all of the objects */

    qsort(aoGAll.oppObjects, aoGAll.nEndObject, sizeof(Object *),
         aoGAll.fpCompare);

    /* Render each object using its own render function. We don't 
     * attempt to render objects that are at the delete position
     */

    nCount = 0;
    oppObj = aoGAll.oppObjects;

    while ((nCount < aoGAll.nEndObject) &&
           ((*oppObj)->vPos.x != CREAL(MAX_NUM)) &&
           ((*oppObj)->vPos.x != CREAL(-MAX_NUM)) )
    {
        (*oppObj)->fpRender(*oppObj);

        oppObj++;
        nCount++;
    }

    /* Delete all the unwanted objects */

    nNewEnd = nCount;

    while (nCount < aoGAll.nEndObject)
    {
        (*oppObj)->fpDestroy(*oppObj);

        oppObj++;
        nCount++;
    }

    /* This is the last object now */
    aoGAll.nEndObject = nNewEnd;
}

/************************************************************************
 *
 *      Function:       AddStaticObject()
 *                      
 *      Description:    Read a clump and position it at a fixed location
 *                      in the scene
 *
 *      Parameters:     x, y, z - fixed position for the object
 *                      name - the RenderWare script file name
 *
 *      Return Value:   None
 *
 ************************************************************************/
int 
AddStaticObject(RwReal x, RwReal y, RwReal z, char *name)
{
    Object *opObj;
    RwClump *cpClump;

    if (!(cpClump = DoRwReadShape(name))) 
    {
        return FALSE;
    }
    
    /* Create the object data structure */
    opObj = malloc(sizeof(Object));

    /* Setup the object and add it to the list of global objects */

    ObjectSetup(opObj, x, y, z, cpClump);
    AllObjectsAddObject(opObj);

    return TRUE;
}

