
/****************************************************************************
 * 
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) MCMXCVIII Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : Faceset.c
 *                                                                         
 * Purpose : Used to convert IndexedFaceSet nodes
 *                                                                         
 ****************************************************************************/

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

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

#include "rpplugin.h"
#include "faceset.h"
#include "builder.h"
#include "primitives.h"
#include "appearance.h"
#include "converter.h"
#include "material.h"
#include "preworldinfo.h"
#include "route.h"
#include "texturetransform.h"
#include "groupnodes.h"
#include "rpvrmlanim.h"
#include "rpvrml.h"
#include "utilities.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: faceset.c,v 1.74 2001/08/28 10:58:01 mattt Exp $";

extern RwReal       GscaleFactor;

/****************************************************************************
 Local (Static) Prototypes
 */

static RwBool       FaceSet_SetPreWorldInfo(AbstractNode * an,
                                            PreWorldInfo * worldInfo);

/* worlds */
static RwBool       FaceSet_World(PreWorldInfo * worldInfo);
static RwBool       FaceSet_SetWorldVertices(PreWorldInfo * worldInfo);
static RwBool       FaceSet_SetWorldFaces(PreWorldInfo * worldInfo);

/* clumps */
static RwBool       FaceSet_Clump(PreWorldInfo * worldInfo);
static RwBool       FaceSet_SetClumpVertexInfo(RpGeometry * geometry,
                                               PreWorldInfo *
                                               worldInfo);
static RwBool       FaceSet_SetClumpTriangles(RpGeometry * geometry,
                                              PreWorldInfo * worldInfo);
static RwBool       FaceSet_SetClumpMorphTarget(RpGeometry * geometry,
                                                PreWorldInfo *
                                                worldInfo);

static RwBool       CalcTextureOffsets(sfvec2f ** textureList,
                                       RwInt32 numTextures,
                                       RwReal * uOffSet,
                                       RwReal * vOffSet);

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Exposed FaceSet methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RwBool
FaceSet_Create(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("FaceSet_Create"));
    RWASSERT(an);

    if (an)
    {
        PreWorldInfo       *worldInfo;

        worldInfo = PreWorldInfo_Create();
        if (!worldInfo)
        {
            RWRETURN(FALSE);
        }

        if (!FaceSet_SetPreWorldInfo(an, worldInfo))
        {
            PreWorldInfo_Destroy(worldInfo);

            RWRETURN(FALSE);
        }

        if (Build_GetType() == CLUMP)
        {
            if (!FaceSet_Clump(worldInfo))
            {
                PreWorldInfo_Destroy(worldInfo);

                RWRETURN(FALSE);
            }
        }
        else                   /* WORLD */
        {
            if (!FaceSet_World(worldInfo))
            {
                PreWorldInfo_Destroy(worldInfo);

                RWRETURN(FALSE);
            }
        }

        PreWorldInfo_Destroy(worldInfo);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static              RwBool
FaceSet_SetPreWorldInfo(AbstractNode * an, PreWorldInfo * worldInfo)
{
    RWFUNCTION(RWSTRING("FaceSet_SetPreWorldInfo"));
    RWASSERT(an);
    RWASSERT(worldInfo);

    /* builds intermediate faceset representation */

    if (an && worldInfo)
    {
        AbstractNode       *anChild;
        AbstractField      *af;
        Field              *triFacesList = (Field *) NULL;
        int                 numTriFaceIndices;
        int                 triFaceLineNum = 0;

        /* get face loop order */

        if ((af = AbstractNode_GetAbstractField(an, RWSTRING("ccw"))))
        {
            Field              *field;
            sfbool             *ccw;

            field = AbstractField_GetField(af);
            ccw = FieldSfbool_GetValue(field);
            if (!ccw)
            {
                RWRETURN(FALSE);
            }
            worldInfo->ccw = *ccw;
        }

        /* get the crease angle */

        if ((af =
             AbstractNode_GetAbstractField(an,
                                           RWSTRING("creaseAngle"))))
        {
            Field              *field;
            sffloat            *creaseAngle;

            field = AbstractField_GetField(af);
            creaseAngle = FieldSffloat_GetValue(field);
            if (!creaseAngle)
            {
                RWRETURN(FALSE);
            }
            worldInfo->creaseAngle = *creaseAngle;
        }

        /* get face index lists */

        if ((anChild =
             AbstractNode_GetChildNode(an, RWSTRING("coord"))))
        {
            RwInt32             numVerts = 0;

            /* values */
            if ((af =
                 AbstractNode_GetAbstractField(anChild,
                                               RWSTRING("point"))))
            {
                Field              *field;

                field = AbstractField_GetField(af);
                numVerts = Field_NumElements(field);
                PreWorldInfo_SetVertices(worldInfo, field);
            }
            /* indices */
            if ((af =
                 AbstractNode_GetAbstractField(an,
                                               RWSTRING("coordIndex"))))
            {

                triFaceLineNum = af->lineNum;
                numTriFaceIndices = af->Items;
                triFacesList = AbstractField_GetField(af);
                PreWorldInfo_SetTriFaces(worldInfo, triFacesList,
                                         numVerts, triFaceLineNum);
            }

            /* key frames */
            {
                AbstractNode       *coordInterpNode;

                coordInterpNode =
                    Route_GetFirstCoordInterpolator(anChild);
#if 0
                while (coordInterpNode)
                {
                    sffloat            *keysList;
                    RwInt32             numKeys;
                    sfvec3f            *keyValuesList;
                    RwInt32             numKeyValues;

                    af = AbstractNode_GetAbstractField(coordInterpNode,
                                                       RWSTRING("key"));
                    keysList = (sffloat *) af->fieldValueArray;
                    numKeys = af->Items;
                    af = AbstractNode_GetAbstractField(coordInterpNode,
                                                       RWSTRING
                                                       ("keyValue"));
                    keyValuesList = (sfvec3f *) af->fieldValueArray;
                    numKeyValues = af->Items;
                    PreWorldInfo_SetMorphTargetsII(worldInfo, keysList,
                                                   numKeys,
                                                   keyValuesList,
                                                   numKeyValues);
                    /* get the next */
                    coordInterpNode =
                        Route_GetNextCoordInterpolator(anChild);
                }
#endif /* 0 */
            }
        }

        /* no faces defined so bail out */
        if (worldInfo->numTriFaces == 0)
        {
            RWRETURN(FALSE);
        }

        /* get texture coords index lists */
        if ((anChild =
             AbstractNode_GetChildNode(an, RWSTRING("texCoord"))))
        {
            RwInt32             numVerts = 0;

            /* values */
            if ((af =
                 AbstractNode_GetAbstractField(anChild,
                                               RWSTRING("point"))))
            {
                Field              *field;

                numVerts = af->Items;
                field = AbstractField_GetField(af);
                PreWorldInfo_SetTextures(worldInfo, field);
            }
            /* indices */
            if ((af =
                 AbstractNode_GetAbstractField(an,
                                               RWSTRING
                                               ("texCoordIndex"))))
            {
                Field              *field;
                RwInt32             lineNum;

                lineNum = af->lineNum;
                field = AbstractField_GetField(af);
                PreWorldInfo_SetTexFaces(worldInfo, field, numVerts,
                                         lineNum);
            }
            else
            {

                PreWorldInfo_SetTexFaces(worldInfo, triFacesList,
                                         numVerts, triFaceLineNum);
            }
        }

        /* get color coords index lists */
#ifdef USE_COLOUREDVERTS
        if ((anChild = AbstractNode_GetChildNode(an, "color")))
        {
            RwInt32             numVerts = 0;

            /* values */
            if ((af = AbstractNode_GetAbstractField(anChild, "color")))
            {
                Field              *field;

                numVerts = af->Items;
                field = AbstractField_GetField(af);
                PreWorldInfo_SetColours(worldInfo, field);
            }
            /* per vertex? */
            if ((af =
                 AbstractNode_GetAbstractField(an, "colorPerVertex")))
            {
                Field              *field;
                sfbool              colorsPerVertex;

                field = AbstractField_GetField(af);
                colorsPerVertex = *FieldSfbool_GetValue(field);
                worldInfo->coloursPerVertex = colorsPerVertex;
            }
            /* indices */
            if ((af = AbstractNode_GetAbstractField(an, "colorIndex")))
            {
                Field              *field;
                RwInt32             lineNum;

                lineNum = af->lineNum;

                field = AbstractField_GetField(af);
                if (worldInfo->coloursPerVertex)
                {
                    PreWorldInfo_SetColFaces(worldInfo, field,
                                             numVerts, lineNum);
                }
                else
                {
                    PreWorldInfo_SetSolidColFaces(worldInfo, field);
                }
            }
            else
                /* use defaults instead */
            {
                if (worldInfo->coloursPerVertex)
                {
                    PreWorldInfo_SetColFaces(worldInfo, triFacesList,
                                             numVerts, triFaceLineNum);
                }
                else
                {
                    PreWorldInfo_SetDefaultSolidColFaces(worldInfo);
                }
            }
        }
#endif /* USE_COLOUREDVERTS */

        /* I'm a gonna get me some normals */
        if ((anChild = AbstractNode_GetChildNode(an, "normal")))
        {
            RwInt32             numVerts = 0;

            /* values */
            if ((af = AbstractNode_GetAbstractField(anChild, "vector")))
            {
                Field              *field;

                field = AbstractField_GetField(af);
                numVerts = Field_NumElements(field);
                PreWorldInfo_SetNormals(worldInfo, field);
            }
            /* per vertex? */
            if ((af =
                 AbstractNode_GetAbstractField(an, "normalPerVertex")))
            {
                Field              *field;
                sfbool              normalPerVertex;

                field = AbstractField_GetField(af);
                normalPerVertex = *FieldSfbool_GetValue(field);
                worldInfo->normalsPerVertex = normalPerVertex;
            }
            /* indices */
            if ((af = AbstractNode_GetAbstractField(an, "normalIndex")))
            {
                Field              *field;
                RwInt32             lineNum;

                lineNum = af->lineNum;
                field = AbstractField_GetField(af);

                if (worldInfo->normalsPerVertex)
                {
                    PreWorldInfo_SetNormalFaces(worldInfo, field,
                                                numVerts, lineNum);
                }
                else
                {
                    PreWorldInfo_SetSolidNormalFaces(worldInfo, field);
                }
            }
            else
                /* use defaults instead */
            {
                if (worldInfo->normalsPerVertex)
                {
                    PreWorldInfo_SetNormalFaces(worldInfo,
                                                triFacesList,
                                                numVerts,
                                                triFaceLineNum);
                }
                else
                {
                    PreWorldInfo_SetDefaultSolidNormalFaces(worldInfo);
                }
            }
        }

        /* set final mappig */
        PreWorldInfo_SetFinalMapping(worldInfo);

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Private clump FaceSet methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

static              RwBool
FaceSet_Clump(PreWorldInfo * worldInfo)
{
    RWFUNCTION(RWSTRING("FaceSet_Clump"));
    RWASSERT(worldInfo);

    if (worldInfo)
    {
        RwInt32             flags =
            rpGEOMETRYLIGHT | rpGEOMETRYNORMALS |
            rpGEOMETRYMODULATEMATERIALCOLOR;
        RwInt32             numVertices = worldInfo->numFinalVertices;
        RwInt32             numFaces = worldInfo->numTriFaces;
        RpGeometry         *geometry;

        if (worldInfo->textures && worldInfo->texFaces)
        {
            flags |= rpGEOMETRYTEXTURED;
        }

#ifdef USE_COLOUREDVERTS
        if (worldInfo->colours && worldInfo->colourFaces)
        {
            flags |= rpGEOMETRYCOLORED;
        }
#endif /* USE_COLOUREDVERTS */

        /* temp hack */
        flags |= rpGEOMETRYPRELIT;

        if ((numFaces == 0) || (numVertices == 0))
        {
            RWRETURN(FALSE);
        }

        if (!
            (geometry = RpGeometryCreate(numVertices, numFaces, flags)))
        {
            RWRETURN(FALSE);
        }

        if (RpGeometryLock(geometry, rpGEOMETRYLOCKALL))
        {
            FaceSet_SetClumpVertexInfo(geometry, worldInfo);
            FaceSet_SetClumpTriangles(geometry, worldInfo);
            FaceSet_SetClumpMorphTarget(geometry, worldInfo);

            /* setup normals if required */
            if (!(worldInfo->normals && worldInfo->normalFaces))
            {
#if 0
                RpGeometry         *newGeometry;

                newGeometry =
                    _rpVrmlGeometryAddCreaseNormals(geometry,
                                                    worldInfo->
                                                    creaseAngle);
                if (newGeometry)
                {
                    RpGeometryDestroy(geometry);

                    geometry = newGeometry;
                }
#endif /* 0 */
                RtGeometryCalculateVertexNormals(geometry);
            }

            RpGeometryUnlock(geometry);
        }

        /* add to hierarchy */
        if (!Build_AddGeometry(geometry))
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static              RwBool
FaceSet_SetClumpTriangles(RpGeometry * geometry,
                          PreWorldInfo * worldInfo)
{
    RWFUNCTION(RWSTRING("FaceSet_SetClumpTriangles"));
    RWASSERT(geometry);
    RWASSERT(worldInfo);

    if (geometry && worldInfo)
    {
        RpTriangle         *triangles =
            RpGeometryGetTriangles(geometry);
        faceLoop           *triFace = worldInfo->triFaces;

#if (0)
        faceLoop           *texFace = worldInfo->texFaces;
#endif /* (0) */

        if (triangles && triFace)
        {
            RpMaterial         *material;
            RwInt32             numFaces = worldInfo->numTriFaces;
            RwInt32             faceIndex;

            /* get the material */
            if (worldInfo->colours || worldInfo->textures)
            {
                /* prelit so use a white material */
                material = Material_GetWhite();
            }
            else
            {
                /* use the current node material */
                material = Material_GetCurrent();
            }

            /* set up the faces */
            for (faceIndex = 0; faceIndex < numFaces; faceIndex++)
            {
                RpGeometryTriangleSetVertexIndices(geometry,
                                                   triangles,
                                                   (RwUInt16)
                                                   (triFace->index[0]),
                                                   (RwUInt16)
                                                   (triFace->index[1]),
                                                   (RwUInt16)
                                                   (triFace->index[2]));
                RpGeometryTriangleSetMaterial(geometry, triangles,
                                              material);
                triangles++;
                triFace++;
            }
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static              RwBool
FaceSet_SetClumpMorphTarget(RpGeometry * geometry,
                            PreWorldInfo * worldInfo)
{
    RWFUNCTION(RWSTRING("FaceSet_SetClumpMorphTarget"));
    RWASSERT(geometry);
    RWASSERT(worldInfo);

    if (geometry && worldInfo)
    {
        RwInt32             numFrames = worldInfo->numMorphTargets;
        RwInt32             frameNum;
        sfvec3f           **points;
        sfvec3f            *offSet = Build_GetPivotOffset();

        if (numFrames == 0)
        {
            points = worldInfo->vertices;
            numFrames = 1;
        }
        else
        {
            points = worldInfo->morphTargetsValues;
        }

        for (frameNum = 0; frameNum < numFrames; frameNum++)
        {
            RpMorphTarget      *morphTarget;

            if (frameNum > 0)
            {
                RpGeometryAddMorphTarget(geometry);
            }

            morphTarget = RpGeometryGetMorphTarget(geometry, frameNum);
            if (morphTarget)
            {
                RwV3d              *vertices =
                    RpMorphTargetGetVertices(morphTarget);
                RwV3d              *normals =
                    RpMorphTargetGetVertexNormals(morphTarget);
                RwInt32             vertexNum;
                RwInt32             numVertices =
                    worldInfo->numFinalVertices;

                for (vertexNum = 0; vertexNum < numVertices;
                     vertexNum++)
                {
                    RwInt32             vertexOffSet =
                        worldInfo->numVertices * frameNum;
                    RwInt32             vertexIndex;
                    RwInt32             normalIndex;

                    /* vertex */
                    vertexIndex =
                        worldInfo->finalVertices[vertexNum].vertexIndex;
                    if (vertexIndex != -1)
                    {
                        sfvec3f            *sfv3 =
                            points[vertexIndex + vertexOffSet];

                        if (sfv3)
                        {
                            vertices->x =
                                (RwReal) ((sfv3->x + offSet->x) *
                                          GscaleFactor);
                            vertices->y =
                                (RwReal) ((sfv3->y + offSet->y) *
                                          GscaleFactor);
                            vertices->z =
                                (RwReal) ((sfv3->z + offSet->z) *
                                          GscaleFactor);
                        }
                        vertices++;
                    }

                    /* normal */
                    if (frameNum == 0)
                    {
                        normalIndex =
                            worldInfo->finalVertices[vertexNum].
                            normalIndex;
                        if (normalIndex != -1)
                        {
                            sfvec3f            *sfv3 =
                                worldInfo->normals[normalIndex];

                            if (sfv3)
                            {
                                normals->x = (RwReal) (sfv3->x);
                                normals->y = (RwReal) (sfv3->y);
                                normals->z = (RwReal) (sfv3->z);
                            }
                            normals++;
                        }
                    }
                }
                {
                    RwSphere           *bSphere =
                        &morphTarget->boundingSphere;

                    RpMorphTargetCalcBoundingSphere(morphTarget,
                                                    bSphere);
                    RpMorphTargetSetBoundingSphere(morphTarget,
                                                   bSphere);
                }
            }
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static              RwBool
FaceSet_SetClumpVertexInfo(RpGeometry * geometry,
                           PreWorldInfo * worldInfo)
{
    RWFUNCTION(RWSTRING("FaceSet_SetClumpVertexInfo"));
    RWASSERT(geometry);
    RWASSERT(worldInfo);

    /* just sets preLight colours */
    if (geometry && worldInfo)
    {
#ifdef USE_COLOUREDVERTS
        RwRGBA             *colours;
#endif /* USE_COLOUREDVERTS */
        RwTexCoords        *textures;
        RwInt32             numVertices;

        numVertices = worldInfo->numFinalVertices;

#ifdef USE_COLOUREDVERTS
        colours = RpGeometryGetVertexColors(geometry);
        if (colours)
        {
            RwInt32             vertexNum;

            for (vertexNum = 0; vertexNum < numVertices; vertexNum++)
            {
                RwInt32             colourIndex;

                colourIndex =
                    worldInfo->finalVertices[vertexNum].colourIndex;
                if (colourIndex != -1)
                {
                    sfcolor            *sfc;

                    sfc = worldInfo->colours[colourIndex];
                    if (sfc)
                    {
                        RwRGBAReal          nColour;
                        RwRGBA              cColour;

                        nColour.red = (RwReal) (sfc->r);
                        nColour.green = (RwReal) (sfc->g);
                        nColour.blue = (RwReal) (sfc->b);
                        nColour.alpha = (RwReal) (1);

                        RwRGBAFromRwRGBAReal(&cColour, &nColour);
                        *colours = cColour;

                        colours++;
                    }
                }
            }
        }

#endif /* USE_COLOUREDVERTS */

        textures = RpGeometryGetVertexTexCoords(geometry, rwTEXTURECOORDINATEINDEX0);
        if (textures)
        {
            RwInt32             vertexNum;
            RwReal              uOffSet, vOffSet;

            if (worldInfo->textures)
            {
                CalcTextureOffsets(worldInfo->textures,
                                   worldInfo->numTextures, &uOffSet,
                                   &vOffSet);
            }

            for (vertexNum = 0; vertexNum < numVertices; vertexNum++)
            {
                RwInt32             textureIndex;

                textureIndex =
                    worldInfo->finalVertices[vertexNum].textureIndex;
                if (textureIndex != -1)
                {
                    sfvec2f            *sfv2, newTexCoord;

                    sfv2 = worldInfo->textures[textureIndex];
                    if (sfv2)
                    {
                        TextureTransform_Apply(&newTexCoord, sfv2);
                        textures->u =
                            (RwReal) (uOffSet + newTexCoord.x);
                        textures->v =
                            (RwReal) (vOffSet - newTexCoord.y);

                        textures++;
                    }
                }
            }
        }
        /* set the lumunaces to a default value */
        {
            RwRGBA             *colours =
                RpGeometryGetPreLightColors(geometry);

            if (colours)
            {
                RwInt32             numVertices =
                    worldInfo->numFinalVertices;
                RwInt32             vertexNum;
                RwRGBAReal          nColour;
                RwRGBA              cColour;

                /* get the pre-light color */
                if (Material_GetMatNodeFlag() /*Material_IsSet() */ )
                {
                    sfcolor            *sfc;

                    sfc = Material_GetCurrentEmissiveColor();

                    nColour.red = (RwReal) (sfc->r);
                    nColour.green = (RwReal) (sfc->g);
                    nColour.blue = (RwReal) (sfc->b);
                    nColour.alpha = (RwReal) (1);

                    RwRGBAFromRwRGBAReal(&cColour, &nColour);
                }
                else
                {
                    /* otherwise use white */
                    nColour.red = (RwReal) (1);
                    nColour.green = (RwReal) (1);
                    nColour.blue = (RwReal) (1);
                    nColour.alpha = (RwReal) (1);

                    RwRGBAFromRwRGBAReal(&cColour, &nColour);
                }

                RwRGBAFromRwRGBAReal(&cColour, &nColour);

                for (vertexNum = 0; vertexNum < numVertices;
                     vertexNum++)
                {
                    *colours = cColour;

                    colours++;
                }
            }
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Private world FaceSet methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

static              RwBool
FaceSet_World(PreWorldInfo * worldInfo)
{
    RWFUNCTION(RWSTRING("FaceSet_World"));
    RWASSERT(worldInfo);

    if (worldInfo)
    {
        RwInt32             flags = 0;

        /* work out the world flags */
        flags = rpWORLDNORMALS | rpWORLDLIGHT;

        if (worldInfo->textures)
        {
            flags |= rpWORLDTEXTURED;
        }

#ifdef USE_COLOUREDVERTS
        if (worldInfo->colours)
        {
            flags |= rpWORLDCOLORED;
        }
#endif /* USE_COLOUREDVERTS */

        Build_WorldAccumulateFlags(flags);

        /* create the NoHsWorld */
        if (!FaceSet_SetWorldVertices(worldInfo))
        {
            RWRETURN(FALSE);
        }

        if (!FaceSet_SetWorldFaces(worldInfo))
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static              RwBool
FaceSet_SetWorldVertices(PreWorldInfo * worldInfo)
{
    RWFUNCTION(RWSTRING("FaceSet_SetWorldVertices"));
    RWASSERT(worldInfo);

    if (worldInfo)
    {
        RtWorldImportVertex *vertices;
        RwInt32             numVertices = worldInfo->numFinalVertices;
        RwMatrix           *currentLTM = Build_GetCurrentLTM();

        /* allocate additional world vertices */
        vertices = Build_WorldAddMoreVertices(numVertices);
        if (vertices)
        {
            RwInt32             vertexNum;
            RwReal              uOffSet, vOffSet;

            /* calculate any texture offsets */
            if (worldInfo->textures)
            {
                CalcTextureOffsets(worldInfo->textures,
                                   worldInfo->numTextures, &uOffSet,
                                   &vOffSet);
            }

            for (vertexNum = 0; vertexNum < numVertices; vertexNum++)
            {
                RwInt32             vertexIndex, textureIndex;

#ifdef USE_COLOUREDVERTS
                RwInt32             colourIndex;
#endif /* USE_COLOUREDVERTS */

                /* vertex */
                vertexIndex =
                    worldInfo->finalVertices[vertexNum].vertexIndex;
                if (vertexIndex != -1)
                {
                    sfvec3f            *sfv3 =
                        worldInfo->vertices[vertexIndex];

                    if (sfv3)
                    {
                        vertices->OC.x = sfv3->x * GscaleFactor;
                        vertices->OC.y = sfv3->y * GscaleFactor;
                        vertices->OC.z = sfv3->z * GscaleFactor;
                        RwV3dTransformPoints(&vertices->OC,
                                             &vertices->OC, 1,
                                             currentLTM);
                    }
                }

                /* texture */
                textureIndex =
                    worldInfo->finalVertices[vertexNum].textureIndex;
                if (textureIndex != -1)
                {
                    sfvec2f            *sfv2 =
                        worldInfo->textures[textureIndex];
                    sfvec2f             newTexCoord;

                    if (sfv2)
                    {
                        TextureTransform_Apply(&newTexCoord, sfv2);
                        vertices->texCoords[0].u =
                            (RwReal) (uOffSet + newTexCoord.x);
                        vertices->texCoords[0].v =
                            (RwReal) (vOffSet - newTexCoord.y);
                    }
                }

                /* colour */
#ifdef USE_COLOUREDVERTS
                colourIndex =
                    worldInfo->finalVertices[vertexNum].colourIndex;
                if (colourIndex != -1)
                {
                    sfcolor            *sfc =
                        worldInfo->colours[colourIndex];

                    if (sfc)
                    {
                        RwRGBAReal          nColour;
                        RwRGBA              cColour;

                        nColour.red = (RwReal) (sfc->r);
                        nColour.green = (RwReal) (sfc->g);
                        nColour.blue = (RwReal) (sfc->b);
                        nColour.alpha = (RwReal) (1);

                        RwRGBAFromRwRGBAReal(&cColour, &nColour);
                        vertices->color = cColour;
                    }
                }
                else
                {
                    RpMaterial         *material;
                    RwRGBA              cColour;

                    material = Material_GetCurrent();
                    if (material)
                    {
                        cColour = *RpMaterialGetColor(material);
                    }
                    else
                    {
                        cColour.red = 0;
                        cColour.green = 0;
                        cColour.blue = 0;
                        cColour.alpha = 0;
                    }

                    vertices->color = cColour;
                }
#endif /* USE_COLOUREDVERTS */

                /* set defualt values for prelight */
                if (Material_GetMatNodeFlag() /*Material_IsSet() */ )
                {
                    RwRGBAReal          nColour;
                    RwRGBA              cColour;

                    sfcolor            *sfc;

                    sfc = Material_GetCurrentEmissiveColor();

                    nColour.red = (RwReal) (sfc->r);
                    nColour.green = (RwReal) (sfc->g);
                    nColour.blue = (RwReal) (sfc->b);
                    nColour.alpha = (RwReal) (1);
                    RwRGBAFromRwRGBAReal(&cColour, &nColour);

                    vertices->preLitCol = cColour;
                }
                else
                {
                    RwRGBA              cColour;

                    cColour.red = cColour.green = cColour.blue = 255;
                    cColour.alpha = 255;

                    vertices->preLitCol = cColour;
                }

                vertices++;
            }

            RWRETURN(TRUE);
        }

        RWRETURN(FALSE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static              RwBool
FaceSet_SetWorldFaces(PreWorldInfo * worldInfo)
{
    RWFUNCTION(RWSTRING("FaceSet_SetWorldFaces"));
    RWASSERT(worldInfo);

    if (worldInfo)
    {
        RtWorldImportTriangle *triangles;
        RwInt32             numFaces = worldInfo->numTriFaces;

        /* allocate additional world triangles */
        triangles = Build_WorldAddMoreTriangles(numFaces);
        if (triangles)
        {
            RwInt32             matInd;
            RwInt32             vertexIndOffset =
                Build_WorldGetVertexIndOffset();
            RwInt32             faceIndex;
            faceLoop           *triFace = worldInfo->triFaces;

            if (!triFace)
            {
                RWRETURN(FALSE);
            }

            /* setup the material index */
            if (worldInfo->colours || worldInfo->textures)
            {
                matInd = Material_GetWhiteIndex();
            }
            else
            {
                matInd = Material_GetCurrentIndex();
            }

            for (faceIndex = 0; faceIndex < numFaces; faceIndex++)
            {
                RwInt32             j;

                for (j = 0; j < 3; j++)
                {
                    triangles->vertIndex[j] =
                        triFace->index[j] + vertexIndOffset;
                }

                triangles->matIndex = matInd;
                triangles++;
                triFace++;
            }

            RWRETURN(TRUE);
        }

        RWRETURN(FALSE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Private FaceSet helper functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RwBool
CalcTextureOffsets(sfvec2f ** textureList, RwInt32 numTextures,
                   RwReal * uOffSet, RwReal * vOffSet)
{
    RWFUNCTION(RWSTRING("CalcTextureOffsets"));
    RWASSERT(textureList);
    RWASSERT(uOffSet);
    RWASSERT(vOffSet);

    if (textureList && uOffSet && vOffSet)
    {
        RwInt32             texIndex;
        RwReal              uMin, vMax;
        sfvec2f            *texCoord;

        /* make sure they're in a valid range */
        uMin = FLT_MAX;
        vMax = FLT_MIN;

        for (texIndex = 0; texIndex < numTextures; texIndex++)
        {
            sfvec2f             newTexCoord;

            texCoord = textureList[texIndex];
            TextureTransform_Apply(&newTexCoord, texCoord);

            if (newTexCoord.x < uMin)
            {
                uMin = newTexCoord.x;
            }

            if (newTexCoord.y > vMax)
            {
                vMax = newTexCoord.y;
            }
        }

        *uOffSet = (RwReal) (-RwFloor(uMin));
        *vOffSet = (RwReal) (vMax + (1 - (vMax - RwFloor(vMax))));

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

#if 0
static void
FaceSet_SetClumpMorphTarget(RpGeometry * geometry,
                            PreWorldInfo * worldInfo)
{
    RWFUNCTION(RWSTRING("FaceSet_SetClumpMorphTarget"));

    assert(geometry);
    assert(worldInfo);

    if (geometry && worldInfo)
    {
        RpMorphTarget      *morphTarget =
            RpGeometryGetMorphTarget(geometry, 0);
        RwV3d              *vertices =
            RpMorphTargetGetVertices(morphTarget);
        RwV3d              *normals =
            RpMorphTargetGetVertexNormals(morphTarget);
        int                 vertexNum;
        int                 numVertices = worldInfo->numFinalVertices;

        for (vertexNum = 0; vertexNum < numVertices; vertexNum++)
        {
            int                 vertexIndex;
            int                 normalIndex;

            /* vertex */
            vertexIndex =
                worldInfo->finalVertices[vertexNum].vertexIndex;
            if (vertexIndex != -1)
            {
                sfvec3f            *sfv3 =
                    worldInfo->vertices[vertexIndex];

                if (sfv3)
                {
                    vertices->x =
                        (RwReal) ((sfv3->x + offSet->x) * GscaleFactor);
                    vertices->y =
                        (RwReal) ((sfv3->y + offSet->y) * GscaleFactor);
                    vertices->z =
                        (RwReal) ((sfv3->z + offSet->z) * GscaleFactor);
                }
                vertices++;
            }

            /* normal */
            normalIndex =
                worldInfo->finalVertices[vertexNum].normalIndex;
            if (normalIndex != -1)
            {
                sfvec3f            *sfv3 =
                    worldInfo->normals[normalIndex];

                if (sfv3)
                {
                    normals->x = (RwReal) (sfv3->x);
                    normals->y = (RwReal) (sfv3->y);
                    normals->z = (RwReal) (sfv3->z);
                }
                normals++;
            }
        }
        {
            RwSphere           *bSphere = &morphTarget->boundingSphere;

            RpMorphTargetFindBoundingSphere(morphTarget, bSphere);
            RpMorphTargetSetBoundingSphere(morphTarget, bSphere);
        }
    }

    RWRETURNVOID();
}

#endif /* 0 */
