
/****************************************************************************
 *                                                                          *
 *  Module  :   patch.c                                                     *
 *                                                                          *
 *  Purpose :                                                               *
 *                                                                          *
 ****************************************************************************/

/****************************************************************************
 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 <rtbezpat.h>

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

#include "patchwin.h"

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

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

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

/****************************************************************************
 Functions
 */

/*****************************************************************************
 * Generate the trig refinememt index.
 */
static RxVertexIndex *
PatchGenerateTriIndex(RxVertexIndex * indices, RwInt32 base,
                      RwInt32 res)
{
    RwInt32             odd, step, idx_buf_stride, i, j;
    RxVertexIndex      *idx, *idx_buf;

    RWFUNCTION(RWSTRING("PatchGenerateTriIndex"));

    /* This is a bit messy, but we need to generate vertex index depending
     * suitable for a long strip.
     *
     * So this mean we will be writing the buffer alternating left to right
     * and right to left for each scan line.
     *
     * Assuming a 4 x 4 refinement we would have the following indices
     *
     *   0   1   2   3
     *   4   5   6
     *   7   8
     *   9
     *
     * This will give a strip index of
     *
     *   0   4   1   5   2   6   3
     *   6   8   5   7   4
     *   7   9   8
     *
     * For even lines, write left to right
     * For odd lines, write right to left.
     */
    idx = indices;
    idx_buf = indices;

    odd = 1;
    step = 1;

    for (i = 0; i < res; i++)
    {
        for (j = 0; j < (res - i); j++)
        {
            *idx = (RxVertexIndex) base;
            idx += step;

            *idx = (RxVertexIndex) (base + res + 1 - i);
            idx += step;

            base++;
        }

        *idx = (RxVertexIndex) base;
        idx += step;
        base++;

        idx_buf_stride = (res - i) * 2 + 1;

        idx_buf += idx_buf_stride;
        idx = idx_buf + (odd * (idx_buf_stride - 2 - step));

        odd = !odd;
        step *= -1;
    }

    /* If odd, then duplicate the last index. This preserve the winding order for the
     * start of the next patch.
     */
    if (res & 0x01)
        idx[0] = idx[-1];

    RWRETURN(indices);
}

/*****************************************************************************
 * Generate the trig refinememt.
 */
static RxObjSpace3DVertex *
PatchRefineTri(RxObjSpace3DVertex * vert,
               RtBezierMatrix cptPos,
               RtBezierMatrix cptNrm,
               RtBezierMatrix cptCol,
               RwTexCoords * cptTexCoords, 
               RwInt32 res)
{

    RtBezierMatrix      diffPos, diffNrm, diffCol;
    RtBezierRow         rowPos, rowNrm, rowCol;
    RwInt32             i, j, numIndices, l2;
    RwReal              u0, u1, u2, v0, v1, v2, t0, t1, t2;
    RwReal              recipRes;
    RxObjSpace3DVertex *pt;

    RWFUNCTION(RWSTRING("PatchRefineTri"));

    recipRes = (RwReal) 1.0 / res;

    RtBezierQuadFromTriangle(quad, *cptPos);
    RtBezierQuadMatrixWeightSetup(weight, quad);
    RtBezierQuadMatrixDifferenceSetup(diffPos, weight,
                                      recipRes, recipRes);

    RtBezierQuadFromTriangle(quad, *cptNrm);
    RtBezierQuadMatrixWeightSetup(weight, quad);
    RtBezierQuadMatrixDifferenceSetup(diffNrm, weight,
                                      recipRes, recipRes);


    if (cptCol)
    {
        RtBezierQuadFromTriangle(quad, *cptCol);
        RtBezierQuadMatrixWeightSetup(weight, quad);
        RtBezierQuadMatrixDifferenceSetup(diffCol, weight,
                                          recipRes, recipRes);
    }

    u0 = cptTexCoords[0].u;
    v0 = cptTexCoords[0].v;

    u1 = cptTexCoords[1].u;
    v1 = cptTexCoords[1].v;

    u2 = cptTexCoords[2].u;
    v2 = cptTexCoords[2].v;

    pt = vert;

    for (j = 0; j <= res; j++)
    {
        t0 = recipRes * (RwReal) j;

        /* Pos */
        rowPos[0].x = diffPos[0][0].x;
        rowPos[0].y = diffPos[0][0].y;
        rowPos[0].z = diffPos[0][0].z;

        rowPos[1].x = diffPos[0][1].x;
        rowPos[1].y = diffPos[0][1].y;
        rowPos[1].z = diffPos[0][1].z;

        rowPos[2].x = diffPos[0][2].x;
        rowPos[2].y = diffPos[0][2].y;
        rowPos[2].z = diffPos[0][2].z;

        rowPos[3].x = diffPos[0][3].x;
        rowPos[3].y = diffPos[0][3].y;
        rowPos[3].z = diffPos[0][3].z;

        /* Nrm */
        rowNrm[0].x = diffNrm[0][0].x;
        rowNrm[0].y = diffNrm[0][0].y;
        rowNrm[0].z = diffNrm[0][0].z;

        rowNrm[1].x = diffNrm[0][1].x;
        rowNrm[1].y = diffNrm[0][1].y;
        rowNrm[1].z = diffNrm[0][1].z;

        rowNrm[2].x = diffNrm[0][2].x;
        rowNrm[2].y = diffNrm[0][2].y;
        rowNrm[2].z = diffNrm[0][2].z;

        rowNrm[3].x = diffNrm[0][3].x;
        rowNrm[3].y = diffNrm[0][3].y;
        rowNrm[3].z = diffNrm[0][3].z;

        /* Col */
        if (cptCol)
        {
            rowCol[0].x = diffCol[0][0].x;
            rowCol[0].y = diffCol[0][0].y;
            rowCol[0].z = diffCol[0][0].z;
            rowCol[0].w = diffCol[0][0].w;

            rowCol[1].x = diffCol[0][1].x;
            rowCol[1].y = diffCol[0][1].y;
            rowCol[1].z = diffCol[0][1].z;
            rowCol[1].w = diffCol[0][1].w;

            rowCol[2].x = diffCol[0][2].x;
            rowCol[2].y = diffCol[0][2].y;
            rowCol[2].z = diffCol[0][2].z;
            rowCol[2].w = diffCol[0][2].w;

            rowCol[3].x = diffCol[0][3].x;
            rowCol[3].y = diffCol[0][3].y;
            rowCol[3].z = diffCol[0][3].z;
            rowCol[3].w = diffCol[0][3].w;
        }

        for (i = 0; i <= (res - j); i++)
        {
            /* compute barycentric coords */
            l2 = res - j - i;

            t1 = recipRes * (RwReal) l2;
            t2 = (RwReal) 1.0 - (t0 + t1);

            /* Write the point into the buffer. */
            pt->objVertex.x = rowPos[0].x;
            pt->objVertex.y = rowPos[0].y;
            pt->objVertex.z = rowPos[0].z;

            pt->objNormal.x = rowNrm[0].x;
            pt->objNormal.y = rowNrm[0].y;
            pt->objNormal.z = rowNrm[0].z;

            pt->color = 0;

            pt->u = t1 * u0 + t0 * u1 + t2 * u2;
            pt->v = t1 * v0 + t0 * v1 + t2 * v2;

            RtBezierQuadDifferenceStepU(rowPos);
            RtBezierQuadDifferenceStepU(rowNrm);

            if (cptCol)
            {
                pt->color =
                    (((RwUInt32) (rowCol[0].w)) << 24) |
                    (((RwUInt32) (rowCol[0].x)) << 16) |
                    (((RwUInt32) (rowCol[0].y)) << 8) |
                    (((RwUInt32) (rowCol[0].z)));

                RtBezierQuadDifferenceStepU(rowCol);
            }

            pt++;
        }

        RtBezierQuadDifferenceStepV(diffPos);
        RtBezierQuadDifferenceStepV(diffNrm);

        if (cptCol)
        {
            RtBezierQuadDifferenceStepV(diffCol);
        }
    }

    /* Generate the vertex index. */

    RWRETURN(vert);
}

/*****************************************************************************
 * Generate the quad refinememt index.
 */
static RxVertexIndex *
PatchGenerateQuadIndex(RxVertexIndex * indices, RwInt32 base,
                       RwInt32 res)
{
    RwInt32             odd, step, idx_buf_stride, i, j;
    RxVertexIndex      *idx, *idx_buf;

    RWFUNCTION(RWSTRING("PatchGenerateQuadIndex"));

    /* This is a bit messy, but we need to generate vertex index depending
     * suitable for a long strip.
     *
     * So this mean we will be writing the buffer alternating left to right
     * and right to left for each scan line.
     *
     * Assuming a 4 x 4 refinement we would have the following indices
     *
     *   0   1   2   3
     *   4   5   6   7
     *   8   9  10  11
     *  12  13  14  15
     *
     * This will give a strip index of
     *
     *   0   4   1   5   2   6   3   7
     *  11   7   6  10   5   9   4   8
     *   8  12   9  13  10  14  11  15
     *
     * For even lines, write left to right
     * For odd lines, write right to left.
     */
    idx = indices;
    idx_buf = indices;
    idx_buf_stride = (res + 1) * 2;

    odd = 1;
    step = 1;

    for (i = 0; i < res; i++)
    {
        for (j = 0; j <= res; j++)
        {
            *idx = (RxVertexIndex) base;
            idx += step;

            *idx = (RxVertexIndex) (base + res + 1);
            idx += step;

            base++;
        }

        idx_buf += idx_buf_stride;
        idx = idx_buf + (odd * (idx_buf_stride - step));

        odd = !odd;
        step *= -1;

    }

    RWRETURN(indices);
}

/*****************************************************************************
 * Generate the quad refinememt.
 */
static RxObjSpace3DVertex *
PatchRefineQuad(RxObjSpace3DVertex * vert,
                RtBezierMatrix cptPos,
                RtBezierMatrix cptNrm,
                RtBezierMatrix cptCol,
                RwTexCoords * cptTexCoords, 
                RwInt32 res)
{

    RtBezierMatrix      weight;
    RtBezierMatrix      diffCol;
    RtBezierMatrix      diffNrm;
    RtBezierMatrix      diffPos;
    RtBezierRow         rowCol;
    RtBezierRow         rowNrm;
    RtBezierRow         rowPos;
    RwInt32             i;
    RwInt32             j;
    RwInt32             numIndices;
    RwReal              du0;
    RwReal              du1;
    RwReal              du;
    RwReal              dv0;
    RwReal              dv1;
    RwReal              dv;
    RwReal              recipRes;
    RwReal              u0;
    RwReal              u1;
    RwReal              u;
    RwReal              v0;
    RwReal              v1;
    RwReal              v;
    RxObjSpace3DVertex *pt;

    RWFUNCTION(RWSTRING("PatchRefineQuad"));

    recipRes = (RwReal) 1.0 / res;

    RtBezierQuadMatrixWeightSetup(weight, *cptPos);
    RtBezierQuadMatrixDifferenceSetup(diffPos, weight,
                                      recipRes, recipRes);

    RtBezierQuadMatrixWeightSetup(weight, *cptNrm);
    RtBezierQuadMatrixDifferenceSetup(diffNrm, weight,
                                      recipRes, recipRes);

    if (cptCol)
    {
        RtBezierQuadMatrixWeightSetup(weight, *cptCol);
        RtBezierQuadMatrixDifferenceSetup(diffCol, weight,
                                          recipRes, recipRes);

    }

    dv0 = (cptTexCoords[2].v - cptTexCoords[0].v) * recipRes;
    dv1 = (cptTexCoords[3].v - cptTexCoords[1].v) * recipRes;

    du0 = (cptTexCoords[2].u - cptTexCoords[0].u) * recipRes;
    du1 = (cptTexCoords[3].u - cptTexCoords[1].u) * recipRes;

    pt = vert;

    for (j = 0; j <= res; j++)
    {
        v = ((RwReal) j) * recipRes;

        /* Interpolate the left edge. */
        u0 = cptTexCoords[0].u + ((RwReal) j * du0);
        v0 = cptTexCoords[0].v + ((RwReal) j * dv0);

        /* Interpolate the right edge. */
        u1 = cptTexCoords[1].u + ((RwReal) j * du1);
        v1 = cptTexCoords[1].v + ((RwReal) j * dv1);

        dv = (v1 - v0) * recipRes;
        du = (u1 - u0) * recipRes;

        /* Pos */
        rowPos[0].x = diffPos[0][0].x;
        rowPos[0].y = diffPos[0][0].y;
        rowPos[0].z = diffPos[0][0].z;

        rowPos[1].x = diffPos[0][1].x;
        rowPos[1].y = diffPos[0][1].y;
        rowPos[1].z = diffPos[0][1].z;

        rowPos[2].x = diffPos[0][2].x;
        rowPos[2].y = diffPos[0][2].y;
        rowPos[2].z = diffPos[0][2].z;

        rowPos[3].x = diffPos[0][3].x;
        rowPos[3].y = diffPos[0][3].y;
        rowPos[3].z = diffPos[0][3].z;

        /* Nrm */
        rowNrm[0].x = diffNrm[0][0].x;
        rowNrm[0].y = diffNrm[0][0].y;
        rowNrm[0].z = diffNrm[0][0].z;

        rowNrm[1].x = diffNrm[0][1].x;
        rowNrm[1].y = diffNrm[0][1].y;
        rowNrm[1].z = diffNrm[0][1].z;

        rowNrm[2].x = diffNrm[0][2].x;
        rowNrm[2].y = diffNrm[0][2].y;
        rowNrm[2].z = diffNrm[0][2].z;

        rowNrm[3].x = diffNrm[0][3].x;
        rowNrm[3].y = diffNrm[0][3].y;
        rowNrm[3].z = diffNrm[0][3].z;

        /* Col */
        if (cptCol)
        {
            rowCol[0].x = diffCol[0][0].x;
            rowCol[0].y = diffCol[0][0].y;
            rowCol[0].z = diffCol[0][0].z;
            rowCol[0].w = diffCol[0][0].w;

            rowCol[1].x = diffCol[0][1].x;
            rowCol[1].y = diffCol[0][1].y;
            rowCol[1].z = diffCol[0][1].z;
            rowCol[1].w = diffCol[0][1].w;

            rowCol[2].x = diffCol[0][2].x;
            rowCol[2].y = diffCol[0][2].y;
            rowCol[2].z = diffCol[0][2].z;
            rowCol[2].w = diffCol[0][2].w;

            rowCol[3].x = diffCol[0][3].x;
            rowCol[3].y = diffCol[0][3].y;
            rowCol[3].z = diffCol[0][3].z;
            rowCol[3].w = diffCol[0][3].w;
        }

        for (i = 0; i <= res; i++)
        {
            u = ((RwReal) i) * recipRes;

            /* Write the point into the buffer. */
            pt->objVertex.x = rowPos[0].x;
            pt->objVertex.y = rowPos[0].y;
            pt->objVertex.z = rowPos[0].z;

            pt->objNormal.x = rowNrm[0].x;
            pt->objNormal.y = rowNrm[0].y;
            pt->objNormal.z = rowNrm[0].z;

            pt->color = 0;

            pt->u = (RwReal) u0 + ((RwReal) i * du);
            pt->v = (RwReal) v0 + ((RwReal) i * dv);

            RtBezierQuadDifferenceStepU(rowPos);
            RtBezierQuadDifferenceStepU(rowNrm);

            if (cptCol)
            {
                pt->color =
                    (((RwUInt32) (rowCol[0].w)) << 24) |
                    (((RwUInt32) (rowCol[0].x)) << 16) |
                    (((RwUInt32) (rowCol[0].y)) << 8) |
                    (((RwUInt32) (rowCol[0].z)));

                RtBezierQuadDifferenceStepU(rowCol);
            }

            pt++;
        }

        RtBezierQuadDifferenceStepV(diffPos);
        RtBezierQuadDifferenceStepV(diffNrm);

        if (cptCol)
            RtBezierQuadDifferenceStepV(diffCol);
    }

    RWRETURN(vert);
}

/*****************************************************************************
 * Generate the atomic refinememt.
 */
RwBool
_rpPatchInstanceAtomic(RpPatchInstanceAtomic * instAtomic,
                       RpAtomic * atomic, RwInt32 res)
{
    RwInt32             numMesh, i, j, k, l, minVert, base, firstpatch,
        numCtrlIndices,
        numQuadIndices, numQuadVerts, numTriIndices, numTriVerts;
    RwV3d              *vert, *nrm;
    RwRGBA             *preLit, defaultPreLit;
    RwTexCoords        *texCoords, cptTexCoords[4];
    RpGeometry         *geom;
    RpMeshHeader       *meshHeader;
    RpMesh             *mesh;
    RtBezierMatrix      cptPos, cptNrm, cptPreLit, *cptPreLitPtr;
    RxObjSpace3DVertex *pt;
    RxVertexIndex      *idx, *cptIdx;
    RpPatchInstanceMesh *instMesh;

    RWFUNCTION(RWSTRING("_rpPatchInstanceAtomic"));

    defaultPreLit.red = 0;
    defaultPreLit.green = 0;
    defaultPreLit.blue = 0;
    defaultPreLit.alpha = 255;

    geom = RpAtomicGetGeometry(atomic);
    vert = geom->morphTarget[0].verts;
    nrm = geom->morphTarget[0].normals;

    preLit = geom->preLitLum;
    texCoords = geom->texCoords[0];

    if (preLit)
        cptPreLitPtr = &cptPreLit;
    else
        cptPreLitPtr = NULL;

    meshHeader = geom->mesh;
    mesh = (RpMesh *)
        ((RwUInt8 *) (meshHeader + 1) + meshHeader->firstMeshOffset);

    numMesh = meshHeader->numMeshes;

    pt = instAtomic->vertices;
    idx = (RxVertexIndex *) & instAtomic->meshes[numMesh];
    instMesh = instAtomic->meshes;

    numQuadIndices = RPPATCHQUADNUMINDEX((res + 1));
    numQuadVerts = RPPATCHQUADNUMVERT((res + 1));

    numTriIndices = RPPATCHTRINUMINDEX((res + 1));
    numTriVerts = RPPATCHTRINUMVERT((res + 1));

    minVert = 0;

    /*
     * We write an index for tri patches for odd number of patches.
     * This is to preserve the winding order.
     */
    if (res & 0x01)
        numTriIndices++;

    while (numMesh)
    {
        firstpatch = 1;
        base = 0;

        instMesh->indices = idx;
        instMesh->material = mesh->material;
        instMesh->mesh = mesh;

        instMesh->numIndices = 0;
        instMesh->minVert = minVert;
        instMesh->numVerts = 0;

        numCtrlIndices = mesh->numIndices;
        cptIdx = mesh->indices;

        /* Extract the quad control points from the mesh index. */
        while (numCtrlIndices > 0)
        {
            /* Duplicate the last index of the previous and the first
             * index of the next patch. This connects the two together.
             */
            if (!firstpatch)
            {
                idx[0] = idx[-1];
                idx[1] = (RxVertexIndex) base;

                idx += 2;
                instMesh->numIndices += 2;
            }

            /* We use the fifth texCoords to flag if its a quad or tri
             * patch
             */
            if (texCoords[cptIdx[5]].u != (RwReal) 0.0)
            {
                /* Tri patch. */

                /* Set up the control point array. */
                l = 0;

                for (j = 0; j < 4; j++)
                {
                    for (i = 0; i < (4 - j); i++)
                    {
                        k = cptIdx[l];

                        cptPos[i][j].x = vert[k].x;
                        cptPos[i][j].y = vert[k].y;
                        cptPos[i][j].z = vert[k].z;

                        cptNrm[i][j].x = nrm[k].x;
                        cptNrm[i][j].y = nrm[k].y;
                        cptNrm[i][j].z = nrm[k].z;

                        l++;
                    }

                    for (; i < 4; i++)
                    {
                        cptPos[i][j].x = (RwReal) 0.0;
                        cptPos[i][j].y = (RwReal) 0.0;
                        cptPos[i][j].z = (RwReal) 0.0;

                        cptNrm[i][j].x = (RwReal) 0.0;
                        cptNrm[i][j].y = (RwReal) 0.0;
                        cptNrm[i][j].z = (RwReal) 0.0;
                    }
                }

                /* Set up the prelit. */
                if (preLit)
                {
                    for (i = 0; i < 4; i++)
                    {
                        for (j = 0; j < (4 - i); j++)
                        {
                            k = cptIdx[(j << 2) + i];

                            cptPreLit[i][j].z = preLit[k].red;
                            cptPreLit[i][j].y = preLit[k].green;
                            cptPreLit[i][j].z = preLit[k].blue;
                            cptPreLit[i][j].w = preLit[k].alpha;
                        }

                        for (; i < 4; i++)
                        {
                            cptPreLit[i][j].x = (RwReal) 0.0;
                            cptPreLit[i][j].y = (RwReal) 0.0;
                            cptPreLit[i][j].z = (RwReal) 0.0;
                            cptPreLit[i][j].w = (RwReal) 0.0;
                        }
                    }
                }

                /* Set up the texccords. */
                cptTexCoords[0] = texCoords[cptIdx[0]];
                cptTexCoords[1] = texCoords[cptIdx[9]];
                cptTexCoords[2] = texCoords[cptIdx[3]];

                PatchRefineTri(pt, &cptPos, &cptNrm, cptPreLitPtr,
                               cptTexCoords, res);
                PatchGenerateTriIndex(idx, base, res);

                base += numTriVerts;
                pt += numTriVerts;
                idx += numTriIndices;

                instMesh->numVerts += numTriVerts;
                instMesh->numIndices += numTriIndices;

                minVert += numTriVerts;

                firstpatch = 0;
            }
            else
            {
                /* Quad patch. */

                /* Set up the control point array. */
                for (i = 0; i < 4; i++)
                {
                    for (j = 0; j < 4; j++)
                    {
                        k = cptIdx[(j << 2) + i];

                        cptPos[i][j].x = vert[k].x;
                        cptPos[i][j].y = vert[k].y;
                        cptPos[i][j].z = vert[k].z;

                        cptNrm[i][j].x = nrm[k].x;
                        cptNrm[i][j].y = nrm[k].y;
                        cptNrm[i][j].z = nrm[k].z;
                    }
                }

                /* Set up the prelit. */
                if (preLit)
                {
                    for (i = 0; i < 4; i++)
                    {
                        for (j = 0; j < 4; j++)
                        {
                            k = cptIdx[(j << 2) + i];

                            cptPreLit[i][j].z = preLit[k].red;
                            cptPreLit[i][j].y = preLit[k].green;
                            cptPreLit[i][j].z = preLit[k].blue;
                            cptPreLit[i][j].w = preLit[k].alpha;
                        }
                    }
                }

                /* Set up the texccords. */
                cptTexCoords[0] = texCoords[cptIdx[0]];
                cptTexCoords[1] = texCoords[cptIdx[3]];
                cptTexCoords[2] = texCoords[cptIdx[12]];
                cptTexCoords[3] = texCoords[cptIdx[15]];

                PatchRefineQuad(pt, &cptPos, &cptNrm, cptPreLitPtr,
                                cptTexCoords, res);
                PatchGenerateQuadIndex(idx, base, res);

                base += numQuadVerts;
                pt += numQuadVerts;
                idx += numQuadIndices;

                instMesh->numVerts += numQuadVerts;
                instMesh->numIndices += numQuadIndices;

                minVert += numQuadVerts;

                firstpatch = 0;
            }

            numCtrlIndices -= RPPATCHNUMCONTROLINDICES;
            cptIdx += RPPATCHNUMCONTROLINDICES;
        }

        instMesh++;
        mesh++;
        numMesh--;
    }

    RWRETURN(TRUE);
}

/*
 */

RxPipeline         *
_rpPatchPipelineCreate(void)
{
    RwInt32             i;
    RxPipeline         *pipe;

    RWFUNCTION(RWSTRING("_rpPatchPipelineCreate"));

    pipe = RxPipelineCreate();
    if (pipe)
    {
        RxLockedPipe       *lpipe;

        pipe->pluginId = rwID_PATCHPLUGIN;

        if ((lpipe = RxPipelineLock(pipe)) != NULL)
        {
            RxPipelineNode     *node;

            lpipe = RxLockedPipeAddFragment(lpipe, NULL,
                                            RxNodeDefinitionGetDX7PatchAtomicInstance
                                            (),
                                            RxNodeDefinitionGetAtomicEnumerateLights
                                            (),
                                            RxNodeDefinitionGetMaterialScatter
                                            (), NULL);

            /* Make sure indices and D3D7 specific data clusters propagate to
             * the end of the pipeline, such that they can be passed to the
             * subsequent material pipelines.
             */
            node =
                RxPipelineFindNodeByName(lpipe,
                                         RxNodeDefinitionGetMaterialScatter
                                         ()->name, NULL, NULL);
            RxPipelineNodeRequestCluster(lpipe, node, &RxClIndices);
            RxPipelineNodeRequestCluster(lpipe, node,
                                         &RxClDX7VertexBufferInfo);

            lpipe = RxLockedPipeUnlock(lpipe);

            RWASSERT(pipe == (RxPipeline *) lpipe);
        }
    }

    RWRETURN(pipe);
}
