R E T I R E D






/*
 * @doc nodePS2ObjEnumMeshes
 * @topic A PS2-specific node to enumerate the meshes in an object and dispatch each mesh down the appropriate material pipeline |
 * @index | nodePS2ObjEnumMeshes
 * @normal Copyright (c) Criterion Software Limited
 */
/****************************************************************************
 *                                                                          *
 * module : nodePS2ObjEnumMeshes.c                                          *
 *                                                                          *
 * purpose: yawn...                                                         *
 *                                                                          *
 ****************************************************************************/

/****************************************************************************
 includes
 */

#include <math.h>
#include <eekernel.h> /* SyncDCache(), etc. */
#include "rwcore.h"
#include "baworld.h"
#include "bapipew.h"
#include "p2stdclsw.h"
#include "skyisms.h"
#include "matputil.h"
#include "nodePS2ObjEnumMeshes.h"

static const char rcsid[] __RWUNUSED__ = "@@(#)$Id: nodePS2ObjEnumMeshes.c,v 1.34 2000/09/13 19:24:15 iestynb Exp $";


/****************************************************************************
 local types
 */

/****************************************************************************
 local (static) prototypes
 */

/****************************************************************************
 local defines
 */

/****************************************************************************
 local (static) globals
 */

/****************************************************************************
 functions
 */

/****************************************************************************
 _rwCreateMeshPacket()
 */

RxPacket *
_rwCreateMeshPacket(RxPipelineNodeInstance *self,
                    RxPS2DMASessionRecord *DMASessionRecord,
                    const RpMesh *mesh,
                    RwResEntry **cacheEntryRef)
{
    RxPacket *result = NULL;
    RxPacket *pk;

    RWFUNCTION(RWSTRING("_rwCreateMeshPacket"));

    if ( (pk = RxPacketCreate(self)) != NULL )
    {
        RxCluster *clDMASessionRecord;
        RxCluster *clMesh;

        clDMASessionRecord = RxClusterLockWrite(pk, 0 /* RxClPS2DMASessionRecord */, self);
        clMesh             = RxClusterLockWrite(pk, 1 /* RxClPS2Mesh */, self);

        if ( clDMASessionRecord && clMesh )
        {
            /* so... all packets share the same physical DMASessionRecord - owned by object packet,
               externally referenced by mesh packet - cunning, eh? */

            RxClusterSetExternalData(clDMASessionRecord, DMASessionRecord, sizeof(RxPS2DMASessionRecord), 1);

            if ( RxClusterInitialiseData(clMesh, 1, sizeof(RxPS2Mesh)) )
            {
                RxPS2Mesh *Mesh;

                Mesh = RxClusterGetCursorData(clMesh, RxPS2Mesh);

                Mesh->mesh = mesh;
                Mesh->cacheEntryRef = cacheEntryRef;

                result = pk; /* success */
            }
        }

        RxClusterUnlock(clMesh);
        RxClusterUnlock(clDMASessionRecord);
    }

    RWRETURN(result);
}

/****************************************************************************
 _rwPS2ObjEnumMeshesNodeBody()

 interesting node: takes in (& passes through) PS2DMASessionRecord packets;
 side effect is to spew out PS2Mesh packets to material pipelines
 */

static RwBool
_rwPS2ObjEnumMeshesNodeBody(RxPipelineNodeInstance *self,
                            RxMemoryArena *arena __RWUNUSED__,
                            void *data __RWUNUSED__)
{
    RxPacket *pk;

    RWFUNCTION(RWSTRING("_rwPS2ObjEnumMeshesNodeBody"));

    while ( (pk = RxPacketFetch(self)) != NULL )
    {
        RxCluster *clDMASessionRecord;
        RxPS2DMASessionRecord *DMASessionRecord;

        /* iterate over meshes, create mesh packet and send down material pipelines */

        clDMASessionRecord = RxClusterLockRead(pk, 0 /* RxClPS2DMASessionRecord */);
        RWASSERT( clDMASessionRecord != NULL && (clDMASessionRecord->flags & rxCLFLAGS_CLUSTERVALID) );

        DMASessionRecord = RxClusterGetCursorData(clDMASessionRecord, RxPS2DMASessionRecord);
        RWASSERT( DMASessionRecord != NULL );

        {
            RpMeshHeader *meshHeader = NULL;
            RwMeshCache *meshCache = NULL;
            const RpMesh *mesh;
            RwUInt32 n;

            RWASSERT( (rxOBJTYPE_WORLDSECTOR == DMASessionRecord->objType) ||
                      (rxOBJTYPE_ATOMIC == DMASessionRecord->objType) );
            
            switch ( DMASessionRecord->objType )
            {
                case rxOBJTYPE_WORLDSECTOR:
                    {
                        
                        meshHeader = 
                            DMASessionRecord->sourceObject.worldSector->mesh;

                        meshCache =
                            rpWorldSectorGetMeshCache(DMASessionRecord->sourceObject.worldSector,
                                                      meshHeader->numMeshes);
                    }
                    break;

                case rxOBJTYPE_ATOMIC:
                    {
                        RpAtomic   *atomic =
                            DMASessionRecord->sourceObject.atomic;
                        RpGeometry *geom = 
                            RpAtomicGetGeometry(atomic);

                        RWASSERT( geom != NULL );

                        meshHeader = geom->mesh;

                        meshCache =
                            rpAtomicGetMeshCache(atomic,
                                                 meshHeader->numMeshes);
                    }
                    break;
            }

            mesh = (const RpMesh *) (meshHeader + 1);

            for (n = 0; n < meshHeader->numMeshes; n++)
            {
                RxPacket *pkmesh =
                    _rwCreateMeshPacket(self, DMASessionRecord, mesh, rwMeshCacheGetEntryRef(meshCache, n));
                RWASSERT( pkmesh != NULL ); /* notice the healthy misuse of asserts in this
                                               function, substituting for robustness */

                RxPacketDispatchToPipeline( pkmesh,
                                            mesh->material->pipeline ?
                                                mesh->material->pipeline :
                                                RpMaterialGetDefaultRenderPipeline(),
                                            self);

                mesh++;
            }
        }

        RxPacketDispatch(pk, 0 /* DefaultOutput */, self);
    }

    RWRETURN(TRUE);
}

/**********************************************************************
 * @func <f RxNodeDefinitionGetPS2ObjEnumMeshes> returns a pointer to the
 * "PS2ObjEnumMeshes.csl" node definition.
 *
 * A basic PS2 pipeline organisation is as follows:
 *
 * WorldSectorInstancingPipeline - "PS2ObjWorldSectorOpen.csl", "PS2ObjEnumMeshes.csl", "PS2ObjClose.csl"
 *
 * AtomicInstancingPipeline - "PS2ObjAtomicOpen.csl", "PS2ObjEnumMeshes.csl", "PS2ObjClose.csl"
 *
 * MaterialPipeline - "PS2MatInstance.csl", "PS2MatBridge.csl"
 *
 * Heading up their respective instancing pipelines, the PS2ObjXXXOpen nodes receive a reference to the object via
 * their data pointer and create a packet for the object containing an RxPS2DMASessionRecord. The nodes open
 * a DMA session, and add data to the DMA chain for the object-scoped state (matrix, lights, ...).
 *
 * PS2ObjEnumMeshes receives the object packet output by PS2ObjXXXOpen and generates a new packet for each mesh
 * within the object, each packet containing a reference to the object's RxPS2DMASessionRecord and a unique RxPS2Mesh
 * record, which conveys a pointer to the mesh. Mesh packets are then vectored to material pipelines, either
 * the pipeline assigned to the mesh's material, or in the absence of a specific assignment, to the default material
 * pipeline. Within the material pipeline, the mesh packets receive processing from PS2MatInstance and PS2MatBridge.
 *
 * Once all material pipelines have been executed, flow of control returns to the instancing pipe, which executes
 * node PS2ObjClose to complete object processing.
 *
 * Note the difference between PS2 and "generic" pipelines: PS2 pipelines defer instancing
 * (conversion of compressed atomic or worldsector data into a format suitable for rendering) to the material pipeline,
 * as against generic pipelines which instance at the object level (in the instancing pipeline). This enables PS2
 * pipelines to select instanced data format at mesh granularity, dependent on rendering requirements.
 *
 * @xref <f RxNodeDefinitionGetPS2MatBridge>
 * @xref <f RxNodeDefinitionGetPS2MatInstance>
 * @xref <f RxNodeDefinitionGetPS2ObjClose>
 * @xref <f RxNodeDefinitionGetPS2ObjOpen>
 */

/****************************************************************************
 RxNodeDefinitionGetPS2ObjEnumMeshes()
 */

RxNodeDefinition *
RxNodeDefinitionGetPS2ObjEnumMeshes(void)
{
    /***********************************************/
    /**                                           **/
    /**  PS2OBJENUMMESHES.CSL NODE SPECIFICATION  **/
    /**                                           **/
    /***********************************************/

    static RxClusterRef nodeClusters[] =
    {
        { &RxClPS2DMASessionRecord, rxCLALLOWABSENT,  rxCLRESERVED }, 
        { &RxClPS2Mesh,             rxCLFORCEPRESENT, rxCLRESERVED }
    };

    #define NUMCLUSTERSOFINTEREST \
        ((sizeof(nodeClusters))/(sizeof(nodeClusters[0])))


    /* input requirements (this array parallel to ClusterRefs) */
    static RxClusterValidityReq nodeReqs[NUMCLUSTERSOFINTEREST] =
    {
          rxCLREQ_REQUIRED              ,   rxCLREQ_DONTWANT
    };

    /* output state (this array parallel to ClusterRefs) */
    static RxClusterValid nodeOut1[NUMCLUSTERSOFINTEREST] =
    {
          rxCLVALID_VALID               ,   rxCLVALID_INVALID
    };

    static RwChar _DefaultOutput[] = RWSTRING("DefaultOutput");

    static RxOutputSpec nodeOuts[] =
    {
        {
            _DefaultOutput,              // Name
            nodeOut1,                    // OutputClusters
            rxCLVALID_NOCHANGE            // AllOtherClusters
        }
    };

    #define NUMOUTPUTS \
        ((sizeof(nodeOuts))/(sizeof(nodeOuts[0])))

    static RwChar _PS2ObjEnumMeshes_csl[] = RWSTRING("PS2ObjEnumMeshes.csl");

    static RxNodeDefinition nodePS2ObjEnumMeshesCSL =
    {
        _PS2ObjEnumMeshes_csl,                 // Name
        {                                      // nodemethods
            _rwPS2ObjEnumMeshesNodeBody,       // +-- nodebody
            (RxNodeInitFn)NULL,                // +-- nodeinit
            (RxNodeTermFn)NULL,                // +-- nodeterm
            (RxPipelineNodeInitFn) NULL,       // +-- pipelinenodeinit
            (RxPipelineNodeTermFn) NULL,       // +-- pipelineNodeTerm
            (RxPipelineNodeConfigFn) NULL,     // +--  pipelineNodeConfig
            (RxConfigMsgHandlerFn) NULL        // +--  configMsgHandler
        },
        {                                      // Io
            NUMCLUSTERSOFINTEREST,             // +-- NumClustersOfInterest
            nodeClusters,                      // +-- ClustersOfInterest
            nodeReqs,                          // +-- InputRequirements
            NUMOUTPUTS,                        // +-- NumOutputs
            nodeOuts                           // +-- Outputs
        },
        (RwUInt32)0,                           // PipelineNodePrivateDataSize
        FALSE,                                 // editable
        (RwInt32)0                             // inPipes
    };

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

    RxNodeDefinition *result = &nodePS2ObjEnumMeshesCSL;

    RWAPIFUNCTION(RWSTRING("RxNodeDefinitionGetPS2ObjEnumMeshes"));

    /*RWMESSAGE((RWSTRING("Pipeline II node")));*/

    RWRETURN(result);
}
