/****************************************************************************
 *                                                                          *
 *  Module  :   rppatch.c                                                   *
 *                                                                          *
 *  Purpose :   General patch handling.                                     *
 *                                                                          *
 ****************************************************************************/

/**
 * \ingroup rppatch310
 * \page rppatch310overview RpPatch310 Plugin Overview
 *
 * The RpPatch310 plugin extends the RenderWare3 API to support patches.
 * Quadric and Triangular BiCubic patches are supported using 16 and 10 control
 * points respectively.
 *
 * A patch provides several benefits for representing curve surfaces rather
 * than a regular triangular mesh because of some their inherin properties.
 *
 * The advantages of using patches rather than triangular meshes are:
 *
 *    Data compactness. A single surface can be represented by 10 or 16
 *    control points. This gives a saving in memory usage.
 *
 *    A patch has LOD, (Level Of Detail). Because a patch can be refined to
 *    any level of detail. A coarse refinement can be set for distant patches
 *    and a finer refinement set for close patches. This results in a possible
 *    improvement in performance and visual quality.
 *
 * The disadvantage of using patches is:
 *
 *    Mesh generation. A patch is not directly renderable. A triangular mesh
 *    must be generated from the patch control points. This can quite expensive
 *    and may be required for each frame.
 *
 * To avoid regenerating the triangular mesh per frame, the current mesh is cached
 * into a RwResEntry (a block of memory in the resources arena). This mesh is
 * reused until the mesh needs to be refined. This typically occurs when the LOD
 * has changed, forcing a regeneration.
 *
 *
 *
 *
 *
 *
 *
 */

/****************************************************************************
 Includes
 */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

/* RW includes */
#include "rpplugin.h"
#include <rpdbgerr.h>
#include <rwcore.h>
#include <rpworld.h>

#include "rppatch310.h"
#include "patchvar.h"
#include "patchgen.h"

/* SKY2 Multipass */
#ifdef SKY2_DRVMODEL_H

#include "patgemsky.h"

#endif /* SKY2_DRVMODEL_H */

#if (!defined(DOXYGEN))
static const char   rcsid[] __RWUNUSED__ =
    "@@@@(#)$Id: rppatch310.c,v 1.11 2001/09/25 15:37:27 iestynb Exp $";
#endif /* (!defined(DOXYGEN)) */

/****************************************************************************
 Local Defines
 */

/****************************************************************************
 Globals (across program)
 */

#if (defined(RWDEBUG))
long                rpPatchStackDepth = 0;
#endif /* (defined(RWDEBUG)) */

/*
 *
 *
 *
 *
 *
 */

RpPatchGlobalVars   rpPatchGlobals;

/*
 * CameraBeginUpdate callback to capture the camera's frame
 * and inverted.
 */
static RwCamera    *
PatchCameraBeginUpdate(RwCamera * camera)
{
    RwFrame            *frame;
    RwCamera           *result;
    RpPatchCameraExt   *data;

    RWFUNCTION(RWSTRING("PatchCameraBeginUpdate"));

    /* Sanity check. */
    RWASSERT(camera != NULL);

    data = (RpPatchCameraExt *)
        RPPATCHPROP(camera, rpPatchGlobals.cameraExtOffset);

    result = data->oldBeginUpdate(camera);

    /* Grab a copy of the camera's frame and invert. */
    frame = RwCameraGetFrame(camera);
    /* RwMatrixInvert(&rpPatchGlobals.invCamMatrix, RwFrameGetLTM(frame)); */

    rpPatchGlobals.camFrame = frame;

    RWRETURN(result);
}

/*
 * Default select LOD callback.
 */
static              RwInt32
PatchAtomicSelectLOD(RpAtomic * atomic)
{
    RwInt32             result;
    RwMatrix            xForm, *mpLocalToWorld, *viewMatrix;

    RWFUNCTION(RWSTRING("PatchAtomicSelectLOD"));

    /* Sanity check. */
    RWASSERT(atomic != NULL);

    {
        RwReal              z;

        mpLocalToWorld =
            RwFrameGetLTM((RwFrame *) rwObjectGetParent(atomic));
        viewMatrix =
            &(((RwCamera *) RWSRCGLOBAL(curCamera))->viewMatrix);

        RwMatrixMultiply(&xForm, mpLocalToWorld, viewMatrix);

        z = RwMatrixGetPos(&xForm)->z;

        result =
            (z >=
             rpPatchGlobals.maxAtomicRange) ? rpPatchGlobals.
            minAtomicLOD : (z <=
                            rpPatchGlobals.
                            minAtomicRange) ? rpPatchGlobals.
            maxAtomicLOD
            : (RwInt32) ((rpPatchGlobals.deltaAtomicLOD *
                          (z - rpPatchGlobals.minAtomicRange)) +
                         rpPatchGlobals.maxAtomicLOD);
    }

    result = (result > RPPATCHMINLOD) ? result : RPPATCHMINLOD;
    result = (result < RPPATCHMAXLOD) ? result : RPPATCHMAXLOD;

    RWRETURN(result);
}

/*
 *
 *
 *
 *
 *
 */

static void        *
PatchEngineOpen(void *ptr,
                RwInt32 offset __RWUNUSED__, RwInt32 size __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("PatchEngineOpen"));

    if ((rpPatchGlobals.patchFreeList =
         RwFreeListCreate(sizeof(RpPatch), 25, 0)) == NULL)
        RWRETURN((NULL));

    if ((rpPatchGlobals.patchNodeFreeList =
         RwFreeListCreate(sizeof(RpPatchNode), 25, 0)) == NULL)
        RWRETURN((NULL));

    if ((rpPatchGlobals.patchMaterialFreeList =
         RwFreeListCreate(sizeof(RpPatchMaterial), 25, 0)) == NULL)
        RWRETURN((NULL));

    rpPatchGlobals.atomicObjectPipe = _rpPatchPipelineCreate();
    rpPatchGlobals.atomicGEMObjectPipe = (RxPipeline *) NULL;

#ifdef SKY2_DRVMODEL_H

    /* SKY2 Multipass */
    rpPatchGlobals.atomicGEMObjectPipe = _rpPatchGEMPipelineCreate();

#endif /* SKY2_DRVMODEL_H */

    rpPatchGlobals.atomicLODCallBack = PatchAtomicSelectLOD;

    rpPatchGlobals.maxAtomicLOD = RPPATCHDEFAULTMAXLOD;
    rpPatchGlobals.minAtomicLOD = RPPATCHDEFAULTMINLOD;

    rpPatchGlobals.minAtomicRange = RPPATCHDEFAULTMINRANGE;
    rpPatchGlobals.maxAtomicRange = RPPATCHDEFAULTMAXRANGE;

    RpPatchSetAtomicLODRange(RPPATCHDEFAULTMINRANGE,
                             RPPATCHDEFAULTMAXRANGE);

    /* Env map parameters. Offset and scale. */
    rpPatchGlobals.emapParams[0] = (RwReal) 1.0;
    rpPatchGlobals.emapParams[1] = (RwReal) 1.0;
    rpPatchGlobals.emapParams[2] = (RwReal) 0.5;
    rpPatchGlobals.emapParams[3] = (RwReal) 0.5;

    rpPatchGlobals.vertPosTol = (RwReal) RPPATCHVERTPOSTOL;
    rpPatchGlobals.vertNrmTol = (RwReal) RPPATCHVERTNRMTOL;

    RWRETURN((ptr));
}

static void        *
PatchEngineClose(void *ptr,
                 RwInt32 offset __RWUNUSED__, RwInt32 size __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("PatchEngineClose"));

    if (rpPatchGlobals.patchFreeList != NULL)
        RwFreeListDestroy(rpPatchGlobals.patchFreeList);

    if (rpPatchGlobals.patchNodeFreeList != NULL)
        RwFreeListDestroy(rpPatchGlobals.patchNodeFreeList);

    if (rpPatchGlobals.patchMaterialFreeList != NULL)
        RwFreeListDestroy(rpPatchGlobals.patchMaterialFreeList);

    if (rpPatchGlobals.atomicObjectPipe != NULL)
        RxPipelineDestroy(rpPatchGlobals.atomicObjectPipe);

    if (rpPatchGlobals.atomicGEMObjectPipe != NULL)
        RxPipelineDestroy(rpPatchGlobals.atomicGEMObjectPipe);

    rpPatchGlobals.atomicObjectPipe = (RxPipeline *) NULL;
    rpPatchGlobals.atomicGEMObjectPipe = (RxPipeline *) NULL;

    RWRETURN(ptr);
}

/*
 *
 *
 *
 *
 */
static void        *
PatchGeomExtConstructor(void *obj,
                        RwInt32 offset, RwInt32 size __RWUNUSED__)
{
    RpPatchGeomExt     *data;

    RWFUNCTION(RWSTRING("PatchGeomExtConstructor"));

    /* Sanity check. */
    RWASSERT(obj != NULL);

    data = (RpPatchGeomExt *) RPPATCHPROP(obj, offset);

    data->version = RPPATCHVERSION;
    data->flag = 0;
    data->patch = (RpPatch *) NULL;

    RWRETURN((obj));
}

static void        *
PatchGeomExtDestructor(void *obj,
                       RwInt32 offset, RwInt32 size __RWUNUSED__)
{
    RpPatchGeomExt     *data;

    RWFUNCTION(RWSTRING("PatchGeomExtDestructor"));

    data = (RpPatchGeomExt *) RPPATCHPROP(obj, offset);

    data->version = RPPATCHVERSION;
    data->flag = 0;
    data->patch = (RpPatch *) NULL;

    RWRETURN((obj));
}

static void        *
PatchGeomExtCopy(void *dstObj, const void *srcObj __RWUNUSED__,
                 RwInt32 offset, RwInt32 size __RWUNUSED__)
{
    const RpPatchGeomExt *srcData;
    RpPatchGeomExt     *dstData;

    RWFUNCTION(RWSTRING("PatchGeomExtCopy"));

    /* Sanity check. */
    RWASSERT(dstObj != NULL);
    RWASSERT(srcObj != NULL);

    srcData = (const RpPatchGeomExt *) RPPATCHPROPCONST(srcObj, offset);
    dstData = (RpPatchGeomExt *) RPPATCHPROP(dstObj, offset);

    memcpy(dstData, srcData, sizeof(RpPatchGeomExt));

    RWRETURN((dstObj));
}

/*
 *
 *
 *
 *
 */
static              RwInt32
PatchGeomExtGetChunkSize(const RpGeometry * geom,
                         RwInt32 offset, RwInt32 __RWUNUSED__ size)
{
    const RpPatchGeomExt *data;

    RWFUNCTION(RWSTRING("PatchGeomExtGetChunkSize"));

    /* Sanity check. */
    RWASSERT(geom != NULL);

    data = (const RpPatchGeomExt *) RPPATCHPROPCONST(geom, offset);

    if (data->flag & RPPATCHFLAGQUAD)
    {
        RWRETURN(sizeof(RpPatchGeomExt));
    }

    RWRETURN(0);
}

static RwStream    *
PatchGeomExtReadChunk(RwStream * s,
                      RwInt32 len,
                      RpGeometry * geom,
                      RwInt32 offset, RwInt32 __RWUNUSED__ size)
{
    RpPatchGeomExt     *data;

    RWFUNCTION(RWSTRING("PatchGeomExtReadChunk"));

    /* Sanity check. */
    RWASSERT(geom != NULL);
    RWASSERT(s != NULL);

    data = (RpPatchGeomExt *) RPPATCHPROP(geom, offset);

    RwStreamRead(s, &data->version, sizeof(RwInt32));
    RwMemNative(&data->version, sizeof(RwInt32));

    RwStreamRead(s, &data->flag, len - sizeof(RwInt32));
    RwMemNative(&data->version, len - sizeof(RwInt32));

    RWRETURN(s);
}

static RwStream    *
PatchGeomExtWriteChunk(RwStream * s,
                       RwInt32 len,
                       const RpGeometry * geom,
                       RwInt32 offset, RwInt32 __RWUNUSED__ size)
{
    const RpPatchGeomExt *data;
    RpPatchGeomExt      tmp;

    RWFUNCTION(RWSTRING("PatchGeomExtWriteChunk"));

    /* Sanity check. */
    RWASSERT(geom != NULL);
    RWASSERT(s != NULL);

    data = (const RpPatchGeomExt *) RPPATCHPROPCONST(geom, offset);

    tmp = *data;
    RwMemLittleEndian(&tmp, sizeof(RpPatchGeomExt));
    RwStreamWrite(s, &tmp, len);

    RWRETURN(s);
}

/*
 *
 *
 *
 *
 */
static void        *
PatchAtomicExtConstructor(void *obj,
                          RwInt32 offset, RwInt32 size __RWUNUSED__)
{
    RpPatchAtomicExt   *data;

    RWFUNCTION(RWSTRING("PatchAtomicExtConstructor"));

    /* Sanity check. */
    RWASSERT(obj != NULL);

    data = (RpPatchAtomicExt *) RPPATCHPROP(obj, offset);

    data->version = RPPATCHVERSION;
    data->flag = 0;
    data->patch = (RpPatch *) NULL;

    data->numQuadPatch = 0;
    data->numTriPatch = 0;

    RWRETURN((obj));
}

static void        *
PatchAtomicExtDestructor(void *obj,
                         RwInt32 offset, RwInt32 size __RWUNUSED__)
{
    RpPatchAtomicExt   *data;

    RWFUNCTION(RWSTRING("PatchAtomicExtDestructor"));

    /* Sanity check. */
    RWASSERT(obj != NULL);

    data = (RpPatchAtomicExt *) RPPATCHPROP(obj, offset);

    data->version = RPPATCHVERSION;
    data->flag = 0;
    data->patch = (RpPatch *) NULL;

    data->numQuadPatch = 0;
    data->numTriPatch = 0;

    RWRETURN((obj));
}

static void        *
PatchAtomicExtCopy(void *dstObj, const void *srcObj __RWUNUSED__,
                   RwInt32 offset, RwInt32 size __RWUNUSED__)
{
    const RpPatchAtomicExt *srcData;
    RpPatchAtomicExt   *dstData;

    RWFUNCTION(RWSTRING("PatchAtomicExtCopy"));

    /* Sanity check. */
    RWASSERT(dstObj != NULL);
    RWASSERT(srcObj != NULL);

    srcData =
        (const RpPatchAtomicExt *) RPPATCHPROPCONST(srcObj, offset);
    dstData = (RpPatchAtomicExt *) RPPATCHPROP(dstObj, offset);

    memcpy(dstData, srcData, sizeof(RpPatchAtomicExt));

    RWRETURN((dstObj));
}

/*
 *
 *
 *
 *
 */

static              RwInt32
PatchAtomicExtGetChunkSize(const RpAtomic * atomic,
                           RwInt32 offset, RwInt32 __RWUNUSED__ size)
{
    const RpPatchAtomicExt *data;

    RWFUNCTION(RWSTRING("PatchAtomicExtGetChunkSize"));

    /* Sanity check. */
    RWASSERT(atomic != NULL);

    data = (const RpPatchAtomicExt *) RPPATCHPROPCONST(atomic, offset);

    if (data->flag & RPPATCHATOMICFLAGPATCH)
    {
        RWRETURN(sizeof(RpPatchAtomicExt));
    }

    RWRETURN(0);
}

static RwStream    *
PatchAtomicExtReadChunk(RwStream * s,
                        RwInt32 len,
                        RpAtomic * atomic,
                        RwInt32 offset, RwInt32 __RWUNUSED__ size)
{
    RpPatchAtomicExt   *data;

    RWFUNCTION(RWSTRING("PatchAtomicExtReadChunk"));

    /* Sanity check. */
    RWASSERT(atomic != NULL);
    RWASSERT(s != NULL);

    data = (RpPatchAtomicExt *) RPPATCHPROP(atomic, offset);

    RwStreamRead(s, &data->version, sizeof(RwInt32));
    RwMemNative(&data->version, sizeof(RwInt32));

    RwStreamRead(s, &data->flag, len - sizeof(RwInt32));
    RwMemNative(&data->version, len - sizeof(RwInt32));

    /* Set up the patch pipe. */
    if (data->flag & RPPATCHATOMICFLAGPATCH)
    {
        RpAtomicSetPipeline(atomic, rpPatchGlobals.atomicObjectPipe);
    }

    RWRETURN(s);
}

static RwStream    *
PatchAtomicExtWriteChunk(RwStream * s,
                         RwInt32 len,
                         const RpAtomic * atomic,
                         RwInt32 offset, RwInt32 __RWUNUSED__ size)
{
    const RpPatchAtomicExt *data;
    RpPatchAtomicExt    tmp;

    RWFUNCTION(RWSTRING("PatchAtomicExtWriteChunk"));

    /* Sanity check. */
    RWASSERT(atomic != NULL);
    RWASSERT(s != NULL);

    data = (const RpPatchAtomicExt *) RPPATCHPROPCONST(atomic, offset);

    tmp = *data;
    RwMemLittleEndian(&tmp, sizeof(RpPatchAtomicExt));
    RwStreamWrite(s, &tmp, len);

    RWRETURN(s);
}

/*
 *
 *
 *
 *
 */
static void        *
PatchCameraExtConstructor(void *obj,
                          RwInt32 offset, RwInt32 size __RWUNUSED__)
{
    RwCamera           *camera;
    RpPatchCameraExt   *data;

    RWFUNCTION(RWSTRING("PatchCameraExtConstructor"));

    /* Sanity check. */
    RWASSERT(obj != NULL);

    camera = (RwCamera *) obj;

    data = (RpPatchCameraExt *) RPPATCHPROP(obj, offset);

    data->oldBeginUpdate = camera->beginUpdate;
    data->oldEndUpdate = camera->endUpdate;

    camera->beginUpdate = PatchCameraBeginUpdate;

    RWRETURN((obj));
}

static void        *
PatchCameraExtDestructor(void *obj,
                         RwInt32 offset, RwInt32 size __RWUNUSED__)
{
    RpPatchCameraExt   *data;

    RWFUNCTION(RWSTRING("PatchCameraExtDestructor"));

    data = (RpPatchCameraExt *) RPPATCHPROP(obj, offset);

    RWRETURN((obj));
}

static void        *
PatchCameraExtCopy(void *dstObj, const void *srcObj __RWUNUSED__,
                   RwInt32 offset, RwInt32 size __RWUNUSED__)
{
    const RpPatchCameraExt *srcData;
    RpPatchCameraExt   *dstData;

    RWFUNCTION(RWSTRING("PatchCameraExtCopy"));

    /* Sanity check. */
    RWASSERT(dstObj != NULL);
    RWASSERT(srcObj != NULL);

    srcData =
        (const RpPatchCameraExt *) RPPATCHPROPCONST(srcObj, offset);
    dstData = (RpPatchCameraExt *) RPPATCHPROP(dstObj, offset);

    memcpy(dstData, srcData, sizeof(RpPatchCameraExt));

    RWRETURN((dstObj));
}

/*
 *
 *
 *
 *
 */
static              RwInt32
PatchCameraExtGetChunkSize(const RwCamera * cam __RWUNUSED__,
                           RwInt32 offset __RWUNUSED__,
                           RwInt32 size __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("PatchCameraExtGetChunkSize"));

    RWRETURN(0);
}

/*
 *
 *
 *
 *
 */

/**
 * \ingroup rppatch310
 * \ref RpPatchSetNumControlPoints is used to set the total number of unique control
 * points in a patch.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param patch              The patch.
 * \param numControlPoints   The total number of control points for the patch.
 *
 * \return The patch if successful, NULL otherwise.
 *
 * \see RpLPatchSetControlPoints.
 * \see RpPatchSetNormals.
 * \see RpPatchSetTexCoords.
 * \see RpPatchSetPreLit.
 * \see RpPatchCreateGeometry.
 */

RpPatch            *
RpPatchSetNumControlPoints(RpPatch * patch, RwInt32 numControlPoints)
{
    RWAPIFUNCTION(RWSTRING("RpPatchSetNumControlPoints"));

    /* Sanity check. */
    RWASSERT(patch != NULL);

    patch->numControlPoints = numControlPoints;

    RWRETURN(patch);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchSetControlPoints is used to attach the control points for the patch.
 *
 * The size of the control points array is determined by \ref RpPatchSetNumControlPoints.
 * Only the reference to the array is stored, not the actual array. This array is used
 * when building a RpGeometry from the patch. So it is important the array is not destroyed
 * before \ref RpPatchCreateGeometry.
 *
 * This is a mandantory requirement and must be present during creation.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param patch  The patch.
 * \param cpt    The array of control points.
 *
 * \return The patch if successful, NULL otherwise.
 *
 * \see RpPatchSetNumControlPoints.
 * \see RpPatchSetNormals.
 * \see RpPatchSetTexCoords.
 * \see RpPatchSetPreLit.
 * \see RpPatchCreateGeometry.
 */

RpPatch            *
RpPatchSetControlPoints(RpPatch * patch, RwV3d * cpt)
{
    RWAPIFUNCTION(RWSTRING("RpPatchSetControlPoints"));

    /* Sanity check. */
    RWASSERT(patch != NULL);

    patch->cpt = cpt;

    RWRETURN(patch);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchSetNormals is used to attach the normals for the patch.
 *
 * The size of the normals array is determined by \ref RpPatchSetNumControlPoints.
 * Only the reference to the array is stored, not the actual array. This array is used
 * when building a RpGeometry from the patch. So it is important the array is not destroyed
 * before \ref RpPatchCreateGeometry.
 *
 * This is a mandantory requirement and must be present during geometry creation.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param patch   The patch.
 * \param nrm     The array of normals.
 *
 * \return The patch if successful, NULL otherwise.
 *
 * \see RpPatchSetNumControlPoints.
 * \see RpPatchSetControlPoints.
 * \see RpPatchSetTexCoords.
 * \see RpPatchSetPreLit.
 * \see RpPatchCreateGeometry.
 */

RpPatch            *
RpPatchSetNormals(RpPatch * patch, RwV3d * nrm)
{
    RWAPIFUNCTION(RWSTRING("RpPatchSetNormals"));

    /* Sanity check. */
    RWASSERT(patch != NULL);

    patch->nrm = nrm;

    RWRETURN(patch);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchSetTexCoords is used to attach the texture coordindats for the patch.
 *
 * The size of the texture coordinates array is determined by \ref RpPatchSetNumControlPoints.
 * Only the reference to the array is stored, not the actual array. This array is used
 * when building a RpGeometry from the patch. So it is important the array is not destroyed
 * before \ref RpPatchCreateGeometry.
 *
 * Texture coordinates are optional and may have more than one set for multipass.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param patch         The patch.
 * \param texcoords     The array of texture coordinates.
 * \param index         The texture coordinates set id.
 *
 * \return The patch if successful, NULL otherwise.
 *
 * \see RpPatchSetNumControlPoints.
 * \see RpPatchSetControlPoints.
 * \see RpPatchSetNormals.
 * \see RpPatchSetPreLit.
 * \see RpPatchCreateGeometry.
 */

RpPatch            *
RpPatchSetTexCoords(RpPatch * patch, RwTexCoords * texcoords,
                    RwInt32 index)
{
    RWAPIFUNCTION(RWSTRING("RpPatchSetTexCoords"));

    /* Sanity check. */
    RWASSERT(patch != NULL);
    RWASSERT(index >= 0);

    patch->texCoords[index] = texcoords;

    RWRETURN(patch);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchSetPreLit is used to attach the pre lit colour intensity for the patch.
 *
 * The size of the pre-lit array is determined by \ref RpPatchSetNumControlPoints.
 * Only the reference to the array is stored, not the actual array. This array is used
 * when building a RpGeometry from the patch. So it is important the array is not destroyed
 * before \ref RpPatchCreateGeometry.
 *
 * Prelit values are optional.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param patch     The patch.
 * \param prelit    The array of prelit values.
 *
 * \return The patch if successful, NULL otherwise.
 *
 * \see RpPatchSetNumControlPoints.
 * \see RpPatchSetControlPoints.
 * \see RpPatchSetNormals.
 * \see RpPatchSetTexCoords.
 * \see RpPatchCreateGeometry.
 */

RpPatch            *
RpPatchSetPreLit(RpPatch * patch, RwRGBA * prelit)
{
    RWAPIFUNCTION(RWSTRING("RpPatchSetPreLit"));

    /* Sanity check. */
    RWASSERT(patch != NULL);

    patch->preLit = prelit;

    RWRETURN(patch);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchCreate is used created a new empty RpPatch.
 *
 * The patch plugin must be attached before using this function.
 *
 * \return The Patch if successful, NULL otherwise.
 *
 * \see RpPatchDestroy.
 */

RpPatch            *
RpPatchCreate(void)
{
    RpPatch            *patch;

    RWAPIFUNCTION(RWSTRING("RpPatchCreate"));

    patch = (RpPatch *) RwFreeListAlloc(rpPatchGlobals.patchFreeList);

    if (patch != NULL)
        memset((void *) patch, 0, sizeof(RpPatch));

    RWRETURN(patch);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchDestroy is used to destroy a RpPatch.
 *
 * This destroys the patch plus any privately allocated data structures. Any attached
 * data, such as control points, normals etc are not destroyed.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param patch     The patch.
 *
 * \return TRUE if successful, NULL otherwise.
 *
 * \see RpPatchCreate.
 */

RwBool
RpPatchDestroy(RpPatch * patch)
{
    RWAPIFUNCTION(RWSTRING("RpPatchDestroy"));

    /* Sanity check. */
    RWASSERT(patch != NULL);

    {
        RpPatchMaterial    *matList, *matListNext;
        RpPatchNode        *patchNode, *patchNodeNext;

        matList = patch->patchMat;

        while (matList != NULL)
        {
            matListNext = matList->next;

            patchNode = matList->quadPatch;

            while (patchNode != NULL)
            {
                patchNodeNext = patchNode->next;

                RwFreeListFree(rpPatchGlobals.patchNodeFreeList,
                               (void *) patchNode);

                patchNode = patchNodeNext;
            }

            patchNode = matList->triPatch;

            while (patchNode != NULL)
            {
                patchNodeNext = patchNode->next;

                RwFreeListFree(rpPatchGlobals.patchNodeFreeList,
                               (void *) patchNode);

                patchNode = patchNodeNext;
            }

            matList->next = (RpPatchMaterial *) NULL;
            matList->quadPatch = (RpPatchNode *) NULL;
            matList->triPatch = (RpPatchNode *) NULL;

            RwFreeListFree(rpPatchGlobals.patchMaterialFreeList,
                           (void *) matList);

            matList = matListNext;
        }

        RwFreeListFree(rpPatchGlobals.patchFreeList, (void *) patch);
    }

    RWRETURN(TRUE);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchPluginAttach is used to attach the patch plugin to the
 * RenderWare system to enable the use of patch modelling for
 * atomics. The patch plugin must be attached between initializing the
 * system with RwEngineInit and opening it with RwEngineOpen.
 *
 * Note that the include file rppatch310.h is required and must be included
 * by an application wishing to use this plugin.  The patch plugin library is
 * contained in the file rppatch.lib.
 *
 * \return TRUE on success, FALSE otherwise
 */

RwBool
RpPatchPluginAttach(void)
{
    RwInt32             offset;

    RWAPIFUNCTION(RWSTRING("RpPatchPluginAttach"));

    rpPatchGlobals.patchFreeList = (RwFreeList *) NULL;
    rpPatchGlobals.patchNodeFreeList = (RwFreeList *) NULL;
    rpPatchGlobals.patchMaterialFreeList = (RwFreeList *) NULL;

    rpPatchGlobals.geomExtOffset = -1;
    rpPatchGlobals.atomicExtOffset = -1;

    rpPatchGlobals.engineOffset =
        RwEngineRegisterPlugin(0,
                               rwID_PATCHPLUGIN,
                               (RwPluginObjectConstructor)
                               PatchEngineOpen,
                               (RwPluginObjectDestructor)
                               PatchEngineClose);

    /*
     *
     *
     *
     */
    rpPatchGlobals.geomExtOffset =
        RpGeometryRegisterPlugin(sizeof(RpPatchGeomExt),
                                 rwID_PATCHPLUGIN,
                                 PatchGeomExtConstructor,
                                 PatchGeomExtDestructor,
                                 PatchGeomExtCopy);

    offset =
        RpGeometryRegisterPluginStream(rwID_PATCHPLUGIN,
                                       (RwPluginDataChunkReadCallBack)
                                       PatchGeomExtReadChunk,
                                       (RwPluginDataChunkWriteCallBack)
                                       PatchGeomExtWriteChunk,
                                       (RwPluginDataChunkGetSizeCallBack)
                                       PatchGeomExtGetChunkSize);

    /*
     *
     *
     *
     */
    rpPatchGlobals.atomicExtOffset =
        RpAtomicRegisterPlugin(sizeof(RpPatchAtomicExt),
                               rwID_PATCHPLUGIN,
                               PatchAtomicExtConstructor,
                               PatchAtomicExtDestructor,
                               PatchAtomicExtCopy);

    offset =
        RpAtomicRegisterPluginStream(rwID_PATCHPLUGIN,
                                     (RwPluginDataChunkReadCallBack)
                                     PatchAtomicExtReadChunk,
                                     (RwPluginDataChunkWriteCallBack)
                                     PatchAtomicExtWriteChunk,
                                     (RwPluginDataChunkGetSizeCallBack)
                                     PatchAtomicExtGetChunkSize);

    /*
     *
     *
     *
     */
    rpPatchGlobals.cameraExtOffset =
        RwCameraRegisterPlugin(sizeof(RpPatchCameraExt),
                               rwID_PATCHPLUGIN,
                               PatchCameraExtConstructor,
                               PatchCameraExtDestructor,
                               PatchCameraExtCopy);

    RWRETURN((TRUE));
}

/**
 * \ingroup rppatch310
 * \ref RpPatchSetAtomicLODCallBack is used to define a callback function
 * that determines which LOD should be used to facet the specified atomic.
 * This allows a custom LOD selection defined by the application.
 *
 * A default callback is defined for all atomics that calculates the LOD
 * level using linear interpolation between a min and far range.
 *
 * Values beyond the range are clamped to use the max and min LOD respectively.
 *
 * The format of the callback function is:
 *
 *    RwInt32 (*RpPatchAtomicLODCallBack) (RpAtoimc *atomic);
 *
 * where the return value is the index of the LOD.
 *
 * Setting the callback to NULL will revert to using the default callback.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param callback  The callback function.
 *
 * \return TRUE if successful. FALSE otherwise.
 * \see RpPatchSetAtomicLODRange
 * \see RpPatchSetAtomicLOD
 */

RwBool
RpPatchSetAtomicLODCallBack(RpPatchAtomicLODCallBack callback)
{
    RWAPIFUNCTION(RWSTRING("RpPatchSetAtomicLODCallBack"));

    if (callback == NULL)
    {
        rpPatchGlobals.atomicLODCallBack = PatchAtomicSelectLOD;
    }
    else
    {
        rpPatchGlobals.atomicLODCallBack = callback;
    }

    RWRETURN(TRUE);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchSetAtomicLODRange is used to set min and max range of the LOD
 * selection.
 *
 * These values are used by the default patch atomic LOD callback. The
 * min range defines distance when the patch is at the maximum LOD.
 * Atomic closer than this distance is facetted at maximum LOD.
 *
 * The far defines the distance when the patch is minimum LOD. Atomic
 * further than this distanve is facetted at the minimum LOD.
 *
 * Atomics within the min and max range will have a LOD according to
 * its distance from the viewer.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param minRange    The minimum distance.
 * \param maxRange    The maximum distance.
 *
 * \return TRUE if successful. FALSE otherwise.
 * \see RpPatchSetAtomicLODCallBack
 * \see RpPatchSetAtomicLOD
 * \see RpPatchGetAtomicLOD
 * \see RpPatchGetAtomicLODRange
 */

RwBool
RpPatchSetAtomicLODRange(RwReal minRange, RwReal maxRange)
{
    RwBool              result;

    RWAPIFUNCTION(RWSTRING("RpPatchSetAtomicLODRange"));

    /* Check we have something sensible. */
    RWASSERT(minRange > 0);
    RWASSERT(maxRange > 0);

    result = FALSE;

    if (minRange < maxRange)
    {
        rpPatchGlobals.minAtomicRange = minRange;
        rpPatchGlobals.maxAtomicRange = maxRange;

        rpPatchGlobals.recipAtomicRange = maxRange - minRange;

        if (rpPatchGlobals.recipAtomicRange > (RwReal) 0.0)
        {
            rpPatchGlobals.recipAtomicRange =
                ((RwReal) 1.0) / rpPatchGlobals.recipAtomicRange;

            rpPatchGlobals.deltaAtomicLOD =
                (rpPatchGlobals.minAtomicLOD -
                 rpPatchGlobals.maxAtomicLOD +
                 1) * (rpPatchGlobals.recipAtomicRange);

        }
        else
        {
            rpPatchGlobals.deltaAtomicLOD = (RwReal) 0.0;
            rpPatchGlobals.recipAtomicRange = (RwReal) - 1.0;
        }

        result = TRUE;
    }

    RWRETURN(result);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchGetAtomicLODRange is used to query the min and max range of the LOD
 * selection.
 *
 * These values are used by the default patch atomic LOD callback. The
 * min range defines distance when the patch is at the maximum LOD.
 * Atomic closer than this distance is facetted at maximum LOD.
 *
 * The far defines the distance when the patch is minimum LOD. Atomic
 * further than this distance is facetted at the minimum LOD.
 *
 * Atomics within the min and max range will have a LOD according to
 * its distance from the viewer.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param minRange    The returned minimum distance.
 * \param maxRange    The returned maximum distance.
 *
 * \return TRUE if successful. FALSE otherwise.
 * \see RpPatchSetAtomicLODCallBack
 * \see RpPatchSetAtomicLOD
 * \see RpPatchGetAtomicLOD
 * \see RpPatchGetAtomicLODRange
 */

RwBool
RpPatchGetAtomicLODRange(RwReal * minRange, RwReal * maxRange)
{
    RwBool              result;

    RWAPIFUNCTION(RWSTRING("RpPatchGetAtomicLODRange"));

    result = TRUE;

    *minRange = rpPatchGlobals.minAtomicRange;
    *maxRange = rpPatchGlobals.maxAtomicRange;

    RWRETURN(result);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchSetAtomicLOD is used to set min and max LOD.
 *
 * These values are used by the default patch atomic LOD callback when
 * calculating the LOD for the atomic. The min and max values sets the
 * min and max LOD possible.
 *
 * Atomics closer than the minimum distance will be facetted to the
 * max LOD anf atomics beyond the maximum distance will be facetted to
 * the minimum LOD.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param minLOD    The minimum LOD.
 * \param maxLOD    The maximum LOD.
 *
 * \return TRUE if successful. FALSE otherwise.
 * \see RpPatchSetAtomicLODCallBack
 * \see RpPatchGetAtomicLOD
 * \see RpPatchSetAtomicLODRange
 * \see RpPatchGetAtomicLODRange
 */

RwBool
RpPatchSetAtomicLOD(RwInt32 minLOD, RwInt32 maxLOD)
{
    RwBool              result;

    RWAPIFUNCTION(RWSTRING("RpPatchSetAtomicLOD"));

    RWASSERT(minLOD > 0);
    RWASSERT(maxLOD > 0);

    result = FALSE;

    if (maxLOD >= minLOD)
    {
        rpPatchGlobals.minAtomicLOD = minLOD;
        rpPatchGlobals.maxAtomicLOD = maxLOD;

        rpPatchGlobals.deltaAtomicLOD =
            (rpPatchGlobals.minAtomicLOD - rpPatchGlobals.maxAtomicLOD +
             1) * (rpPatchGlobals.recipAtomicRange);

        result = TRUE;
    }

    RWRETURN(result);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchGetAtomicLOD is used to query the min and max LOD.
 *
 * These values are used by the default patch atomic LOD callback when
 * calculating the LOD for the atomic. The min and max values sets the
 * min and max LOD possible.
 *
 * Atomics closer than the minimum distance will be facetted to the
 * max LOD anf atomics beyond the maximum distance will be facetted to
 * the minimum LOD.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param minLOD    The returned minimum LOD.
 * \param maxLOD    The returned maximum LOD.
 *
 * \return TRUE if successful. FALSE otherwise.
 * \see RpPatchSetAtomicLODCallBack
 * \see RpPatchSetAtomicLOD
 * \see RpPatchSetAtomicLODRange
 * \see RpPatchGetAtomicLODRange
 */

RwBool
RpPatchGetAtomicLOD(RwInt32 * minLOD, RwInt32 * maxLOD)
{
    RwBool              result;

    RWAPIFUNCTION(RWSTRING("RpPatchGetAtomicLOD"));

    result = TRUE;

    *minLOD = rpPatchGlobals.minAtomicLOD;
    *maxLOD = rpPatchGlobals.maxAtomicLOD;

    RWRETURN(result);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchAtomicSetFlags is used to set the patch's property flag.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param atomic    The patch atomic.
 * \param flag      The flag.
 *
 * \return the atomic if successful. NULL otherwise.
 * \see RpPatchGetFlags
 */

RpAtomic           *
RpPatchAtomicSetFlags(RpAtomic * atomic, RwInt32 flag)
{
    RpPatchAtomicExt   *data;

    RWAPIFUNCTION(RWSTRING("RpPatchAtomicSetFlags"));

    RWASSERT(atomic);

    data = (RpPatchAtomicExt *)
        RPPATCHPROP(atomic, rpPatchGlobals.atomicExtOffset);

    data->flag = flag;

    /* Select the appropiate pipe. */
    if (flag & RPPATCHATOMICFLAGMATFX)
    {
        RpAtomicSetPipeline(atomic, rpPatchGlobals.atomicGEMObjectPipe);
    }
    else
    {
        RpAtomicSetPipeline(atomic, rpPatchGlobals.atomicObjectPipe);
    }

    RWRETURN(atomic);
}

/**
 * \ingroup rppatch310
 * \ref RpPatchAtomicGetFlags is used to query the patch's property flag.
 *
 * The patch plugin must be attached before using this function.
 *
 * \param atomic    The patch atomic.
 *
 * \return THe property flag.
 *
 * \see RpPatchSetFlags
 */

RwInt32
RpPatchAtomicGetFlags(RpAtomic * atomic)
{
    RpPatchAtomicExt   *data;

    RWAPIFUNCTION(RWSTRING("RpPatchAtomicGetFlags"));

    RWASSERT(atomic);

    data = (RpPatchAtomicExt *)
        RPPATCHPROP(atomic, rpPatchGlobals.atomicExtOffset);

    RWRETURN(data->flag);
}
