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

/**
 * \ingroup rppatch
 * \page rppatchoverview RpPatch Plugin Overview
 *
 * The RpPatch plugin extends the RenderWare Graphics 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 over
 * a regular triangular mesh because of some their inherent 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 allows an
 *    improvement in performance and visual quality.
 *
 * The disadvantage of using patches is that 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.
 */

/*===========================================================================*
 *--- Include files ---------------------------------------------------------*
 *===========================================================================*/
#include <rwcore.h>
#include <rpworld.h>

#include "rpplugin.h"
#include "rpdbgerr.h"

#include "rpmatfx.h"
#include "rppatch.h"


#include "patchatomic.h"
#include "patchgeometry.h"
#include "patch.h"

/*===========================================================================*
 *--- Private Types ---------------------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Private Global Variables ----------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Private Defines -------------------------------------------------------*
 *===========================================================================*/
#define rpPATCHENTRIESPERBLOCK 20
#define rpPATCHALIGNMENT 0

/*===========================================================================*
 *--- Local Types -----------------------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Local Global Variables ------------------------------------------------*
 *===========================================================================*/
#if (!defined(DXOYGEN))
static const char rcsid[] __RWUNUSED__ =
    "@@@@(#)$Id: rppatch.c,v 1.7 2001/09/25 09:38:47 johns Exp $";
#endif /* (!defined(DXOYGEN)) */

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

/*===========================================================================*
 *--- Local Defines ---------------------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Local functions -------------------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Private functions -----------------------------------------------------*
 *===========================================================================*/

/*===========================================================================*
 *--- Plugin Engine Functions -----------------------------------------------*
 *===========================================================================*/

/*****************************************************************************
 - PatchEngineOpen
 - Creates the patch pipelines and the global free list.
 - Inputs :   instance - Pointer to engine instance.
 - Outputs:   void *   - Pointer to engine instance - on success.
 */
static void *
PatchEngineOpen( void *instance,
                 RwInt32 offset __RWUNUSED__,
                 RwInt32 size   __RWUNUSED__ )
{
    RpPatchLODRange lodRange;

    RWFUNCTION(RWSTRING("PatchEngineOpen"));
    RWASSERT(NULL != instance);

    /* Create the patch pipelines, only once. */
    if(_rpPatchGlobals.module.numInstances == 0)
    {
        RwUInt32 pipes;
        RwInt32 offset;
        RwBool success;

        /* Assume we always want the generic pipe. */
        pipes = rpPATCHPIPELINEGENERIC;

        /* Is the skin plugin attached? */
        offset = RwEngineGetPluginOffset(rwID_SKINPLUGIN);
        if(-1 != offset)
        {
            pipes |= rpPATCHPIPELINESKINNED;
        }

        /* Is the matfx plugin attached? */
        offset = RwEngineGetPluginOffset(rwID_MATERIALEFFECTSPLUGIN);
        if(-1 != offset)
        {
            pipes |= rpPATCHPIPELINEMATFX;
        }

        /* Create the pipelines. */
        success = _rpPatchPipelinesCreate(pipes);
        RWASSERT(FALSE != success);

        /* Create a PatchAtomicData freelist. */
        _rpPatchGlobals.atomicFreeList =
            RwFreeListCreate( sizeof(PatchAtomicData),
                              rpPATCHENTRIESPERBLOCK,
                              rpPATCHALIGNMENT );
        RWASSERT(NULL != _rpPatchGlobals.atomicFreeList);

        /* Create a PatchGeometryData freelist. */
        _rpPatchGlobals.geometryFreeList =
            RwFreeListCreate( sizeof(PatchGeometryData),
                              rpPATCHENTRIESPERBLOCK,
                              rpPATCHALIGNMENT );
        RWASSERT(NULL != _rpPatchGlobals.geometryFreeList);
    }

    /* Another instance. */
    _rpPatchGlobals.module.numInstances++;

    /* Setup the default LOD. */
    lodRange.minLod = rpPATCHDEFAULTMINLOD;
    lodRange.maxLod = rpPATCHDEFAULTMAXLOD;
    lodRange.minRange = rpPATCHDEFAULTMINRANGE;
    lodRange.maxRange = rpPATCHDEFAULTMAXRANGE;

    RpPatchSetDefaultLODCallBackRange(&lodRange);

    RWRETURN(instance);
}

/*****************************************************************************
 - PatchEngineClose
 - Destroys the patch pipelines and the global free list.
 - Inputs :   instance - Pointer to engine instance.
 - Outputs:   void *   - Pointer to engine instance - on success.
 */
static void *
PatchEngineClose( void *instance,
                  RwInt32 offset __RWUNUSED__,
                  RwInt32 size   __RWUNUSED__ )
{
    RWFUNCTION(RWSTRING("PatchEngineClose"));
    RWASSERT(NULL != instance);
    RWASSERT(0 < _rpPatchGlobals.module.numInstances);

    /* One less module instance. */
    _rpPatchGlobals.module.numInstances--;

    if(0 == _rpPatchGlobals.module.numInstances)
    {
        RwBool success;

        /* Destroy the patching pipelines. */
        success = _rpPatchPipelinesDestroy();
        RWASSERT(FALSE != success);

        /* Destory the patch atomic free list. */
        RWASSERT(NULL != _rpPatchGlobals.atomicFreeList);
        RwFreeListDestroy(_rpPatchGlobals.atomicFreeList);
        _rpPatchGlobals.atomicFreeList = (RwFreeList *)NULL;

        /* Destory the patch geometry free list. */
        RWASSERT(NULL != _rpPatchGlobals.geometryFreeList);
        RwFreeListDestroy(_rpPatchGlobals.geometryFreeList);
        _rpPatchGlobals.geometryFreeList = (RwFreeList *)NULL;
    }

    RWRETURN(instance);
}

/*===========================================================================*
 *--- Plugin API Functions --------------------------------------------------*
 *===========================================================================*/

/**
 * \ingroup rppatch
 * \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 \ref RwEngineInit and opening it with \ref RwEngineOpen.
 *
 * Note that the include file rppatch.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 success;

    RWAPIFUNCTION(RWSTRING("RpPatchPluginAttach"));

    /* Register the plugin. */
    _rpPatchGlobals.engineOffset =
        RwEngineRegisterPlugin( sizeof(PatchEngineData),
                                rwID_PATCHPLUGIN,
                                PatchEngineOpen,
                                PatchEngineClose );
    RWASSERT(0 < _rpPatchGlobals.engineOffset);

    /* Extend atomic to hold patch atomic data. */
    _rpPatchGlobals.atomicOffset =
        RpAtomicRegisterPlugin( sizeof(PatchAtomicData),
                                rwID_PATCHPLUGIN,
                                _rpPatchAtomicConstructor,
                                _rpPatchAtomicDestructor,
                                _rpPatchAtomicCopy );
    RWASSERT(0 < _rpPatchGlobals.atomicOffset);

    /* Attach the atomic stream handling functions. */
    success = RpAtomicRegisterPluginStream( rwID_PATCHPLUGIN,
                                            _rpPatchAtomicRead,
                                            _rpPatchAtomicWrite,
                                            _rpPatchAtomicGetSize );
    RWASSERT(0 < success);

    /* Attach an always callback streaming function. */
    success = RpAtomicSetStreamAlwaysCallBack( rwID_PATCHPLUGIN,
                                               _rpPatchAtomicAlways );
    RWASSERT(0 < success);

    /* Attach a rights callback streaming function. */
    success = RpAtomicSetStreamRightsCallBack( rwID_PATCHPLUGIN,
                                               _rpPatchAtomicAlways );
    RWASSERT(0 < success);

    /* Extend geometry to hold patch geometry data. */
    _rpPatchGlobals.geometryOffset =
        RpGeometryRegisterPlugin( sizeof(PatchGeometryData),
                                  rwID_PATCHPLUGIN,
                                  _rpPatchGeometryConstructor,
                                  _rpPatchGeometryDestructor,
                                  _rpPatchGeometryCopy );
    RWASSERT(0 < _rpPatchGlobals.geometryOffset);

    /* Attach the stream handling functions */
    success = RpGeometryRegisterPluginStream( rwID_PATCHPLUGIN,
                                              _rpPatchGeometryRead,
                                              _rpPatchGeometryWrite,
                                              _rpPatchGeometryGetSize );
    RWASSERT(0 < success);

    /* Attach an always callback streaming function. */
    success = RpGeometrySetStreamAlwaysCallBack( rwID_PATCHPLUGIN,
                                                 _rpPatchGeometryAlways );
    RWASSERT(0 < success);

    RWRETURN(TRUE);
}
