/*
 *
 * VRML to RW converter plug-in
 */
/****************************************************************************
 * 
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1998 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : PreWorldInfo.c
 *                                                                         
 * Purpose : Used to create an intermediate representation 
 *           of Abstract FaceSet nodes
 *
 *          (Name is very misleading since it is also used for clumps)
 *                                                                         
 ****************************************************************************/

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

#include <stdlib.h>
#include <assert.h>

#include "rpplugin.h"
#include "preworldinfo.h"
#include "rpvrmlanim.h"
#include "rpvrml.h"
#include "logfile.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: preworldinfo.c,v 1.36 2001/02/08 12:17:06 johns Exp $";

/****************************************************************************
 Local Types
 */

/* prapper the remapper */
typedef struct PreWorldReMapper PreWorldReMapper;
struct PreWorldReMapper
{
    RwInt32             vertexIndex;
    RwInt32             textureIndex;
    RwInt32             colourIndex;
    RwInt32             normalIndex;
    RwInt32             faceIndex;
    RwUInt8             triIndex;
};

typedef             RwBool(*compareFunc) (const void *, const void *);

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

static faceLoop    *PreWorldInfo_CreateTriFaces(Field * triFacesList,
                                                RwInt32 numTriFaces,
                                                RwBool ccw);
static faceLoop    *PreWorldInfo_CreateSolidFaces(Field * faceList,
                                                  faceLoop * triFaces,
                                                  RwInt32 numTriFaces);
static faceLoop    *PreWorldInfo_CreateDefaultFaces(faceLoop * triFaces,
                                                    RwInt32
                                                    numTriFaces);
static faceLoop    *PreWorldInfo_CreateDefaultSolidFaces(faceLoop *
                                                         triFaces,
                                                         RwInt32
                                                         numTriFaces);
faceLoop           *CreateFaceLoopArray(RwInt32 numItems);
static RwInt32      CheckTriangleFaces(Field * faceList,
                                       RwInt32 numVerts,
                                       RwInt32 lineNum);
static RwBool       CheckSolidFaces(Field * indList, RwInt32 numCoords);
static RwBool       CheckDefaultSolidFaces(RwInt32 numFaces,
                                           RwInt32 numCoords);

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

   Exposed PreWorldInfo methods

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

PreWorldInfo       *
PreWorldInfo_Create(void)
{
    PreWorldInfo       *worldInfo;

    RWFUNCTION(RWSTRING("PreWorldInfo_Create"));

    worldInfo = (PreWorldInfo *) RwMalloc(sizeof(PreWorldInfo));
    if (!worldInfo)
    {
        RWRETURN((PreWorldInfo *)NULL);
    }

    /* flags */
    worldInfo->ccw = TRUE;
    worldInfo->coloursPerVertex = TRUE;
    worldInfo->normalsPerVertex = TRUE;

    /* vertices */
    worldInfo->numVertices = 0;
    worldInfo->numTriFaces = 0;
    worldInfo->vertices = (sfvec3f **)NULL;
    worldInfo->triFaces = (faceLoop *)NULL;

    /* morph target keyframes */
    worldInfo->numMorphTargets = 0;
    worldInfo->morphTargets = (float *)NULL;
    worldInfo->morphTargetsValues = (sfvec3f **)NULL;

    /* textures */
    worldInfo->numTextures = 0;
    worldInfo->numTextureFaces = 0;
    worldInfo->textures = (sfvec2f **)NULL;
    worldInfo->texFaces = (faceLoop *)NULL;

    /* colours */
    worldInfo->numColours = 0;
    worldInfo->numColourFaces = 0;
    worldInfo->colours = (sfcolor **)NULL;
    worldInfo->colourFaces = (faceLoop *)NULL;

    /* normals */
    worldInfo->numNormals = 0;
    worldInfo->numNormalFaces = 0;
    worldInfo->normals = (sfvec3f **)NULL;
    worldInfo->normalFaces = (faceLoop *)NULL;

    /* final remapped list */
    worldInfo->numFinalVertices = 0;
    worldInfo->finalVertices = (PreWorldVertex *)NULL;

    RWRETURN(worldInfo);
}

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

    if (worldInfo)
    {
        /* vertices */
        if (worldInfo->vertices)
        {
            RwFree(worldInfo->vertices);
            worldInfo->vertices = (sfvec3f **)NULL;
        }
        if (worldInfo->triFaces)
        {
            RwFree(worldInfo->triFaces);
            worldInfo->triFaces = (faceLoop *)NULL;
        }

        /* keyframes */
        if (worldInfo->morphTargets)
        {
            RwFree(worldInfo->morphTargets);
            worldInfo->morphTargets = (float *)NULL;
        }
        if (worldInfo->morphTargetsValues)
        {
            RwFree(worldInfo->morphTargetsValues);
            worldInfo->morphTargetsValues = (sfvec3f **)NULL;
        }

        /* textures */
        if (worldInfo->textures)
        {
            RwFree(worldInfo->textures);
            worldInfo->textures = (sfvec2f **)NULL;
        }
        if (worldInfo->texFaces)
        {
            RwFree(worldInfo->texFaces);
            worldInfo->texFaces = (faceLoop *)NULL;
        }

        /* colours */
        if (worldInfo->colours)
        {
            RwFree(worldInfo->colours);
            worldInfo->colours = (sfcolor **)NULL;
        }
        if (worldInfo->colourFaces)
        {
            RwFree(worldInfo->colourFaces);
            worldInfo->colourFaces = (faceLoop *)NULL;
        }

        /* normals */
        if (worldInfo->normals)
        {
            RwFree(worldInfo->normals);
            worldInfo->normals = (sfvec3f **)NULL;
        }
        if (worldInfo->normalFaces)
        {
            RwFree(worldInfo->normalFaces);
            worldInfo->normalFaces = (faceLoop *)NULL;
        }

        /* final remapped list */
        if (worldInfo->finalVertices)
        {
            RwFree(worldInfo->finalVertices);
            worldInfo->finalVertices = (PreWorldVertex *)NULL;
        }

        RwFree(worldInfo);

        RWRETURN(TRUE);
    }

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

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

   tri faces

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

RwBool
PreWorldInfo_SetVertices(PreWorldInfo * worldInfo, Field * verts)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_SetVertices"));
    RWASSERT(worldInfo);
    RWASSERT(verts);

    if (worldInfo && verts)
    {
        RwInt32             numVerts;

        numVerts = Field_NumElements(verts);
        worldInfo->numVertices = numVerts;
        if (worldInfo->numVertices)
        {
            worldInfo->vertices =
                (sfvec3f **) RwMalloc(sizeof(sfvec3f *) * numVerts);
            if (worldInfo->vertices)
            {
                sfvec3f           **vertices = worldInfo->vertices;
                RwInt32             i;

                for (i = 0; i < numVerts; i++)
                {
                    *vertices = FieldMfvec3f_GetValue(verts, i);
                    vertices++;
                }

                RWRETURN(TRUE);
            }
        }
    }

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

RwBool
PreWorldInfo_SetTriFaces(PreWorldInfo * worldInfo, Field * triFaces,
                         RwInt32 numVerts, RwInt32 lineNum)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_SetTriFaces"));
    RWASSERT(worldInfo);
    RWASSERT(triFaces);

    if (worldInfo && triFaces)
    {
        if (worldInfo->triFaces)
        {
            RwFree(worldInfo->triFaces);
        }

        /* set num world faces */
        worldInfo->numTriFaces =
            CheckTriangleFaces(triFaces, numVerts, lineNum);
        if (worldInfo->numTriFaces == 0)
        {
            RWRETURN(FALSE);
        }

        worldInfo->triFaces = PreWorldInfo_CreateTriFaces(triFaces,
                                                          worldInfo->
                                                          numTriFaces,
                                                          worldInfo->
                                                          ccw);
        if (!worldInfo->triFaces)
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

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

/* morphTargets */
RwBool
PreWorldInfo_SetMorphTargets(PreWorldInfo * worldInfo, LLinkList * keys,
                             LLinkList * keyValues)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_SetMorphTargets"));
    RWASSERT(worldInfo);
    RWASSERT(keys);
    RWASSERT(keyValues);

    if (worldInfo && keys && keyValues)
    {
        RwInt32             numMorphTargets;
        RwInt32             numKeyVertices;
        RwInt32             keyOffset = 0;
        RwInt32             keyValueOffset = 0;
        sffloat            *sfkey, *morphTarget;
        sfvec3f            *sfv, **morphTargetValue;

        numMorphTargets = LLinkList_NumItems(keys);
        numKeyVertices = LLinkList_NumItems(keyValues);

        if ((worldInfo->numVertices * numMorphTargets) !=
            numKeyVertices)
        {
            /* send error msg */
            RWRETURN(FALSE);
        }

        if (!worldInfo->morphTargets)
        {
            RwInt32             size =
                sizeof(sffloat) * numMorphTargets;

            keyOffset = 0;
            morphTarget = (sffloat *) RwMalloc(size + keyOffset);
        }
        else
        {
            RwInt32             size =
                sizeof(sffloat) * numMorphTargets;

            keyOffset = sizeof(sffloat) * worldInfo->numMorphTargets;

            morphTarget = (sffloat *) RwRealloc(worldInfo->morphTargets,
                                                size + keyOffset);
        }

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

        worldInfo->morphTargets = morphTarget;
        morphTarget += worldInfo->numMorphTargets;

        if (!worldInfo->morphTargetsValues)
        {
            RwInt32             size =
                sizeof(sfvec3f *) * numKeyVertices;

            keyValueOffset = 0;
            morphTargetValue =
                (sfvec3f **) RwMalloc(size + keyValueOffset);
        }
        else
        {
            RwInt32             size =
                sizeof(sfvec3f *) * numKeyVertices;

            keyValueOffset =
                sizeof(sfvec3f *) * worldInfo->numVertices *
                worldInfo->numMorphTargets;

            morphTargetValue =
                (sfvec3f **) RwRealloc(worldInfo->morphTargetsValues,
                                       size + keyValueOffset);
        }

        if (!morphTargetValue)
        {
            RwFree(morphTarget);

            RWRETURN(FALSE);
        }

        worldInfo->morphTargetsValues = morphTargetValue;
        morphTargetValue +=
            worldInfo->numVertices * worldInfo->numMorphTargets;

        worldInfo->numMorphTargets += numMorphTargets;

        LLinkList_IteratorReset(keys);
        while ((sfkey = (sffloat *) LLinkList_IteratorPrev(keys)))
        {
#if (0)
            *morphTarget++ = *sfkey;
            +value offset;
#endif /* (0) */
        }

        LLinkList_IteratorReset(keyValues);
        while ((sfv = (sfvec3f *) LLinkList_IteratorPrev(keyValues)))
        {
            *morphTargetValue++ = sfv;
        }

        RWRETURN(TRUE);
    }

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

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

   texture faces

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

RwBool
PreWorldInfo_SetTextures(PreWorldInfo * worldInfo, Field * textureList)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_SetTextures"));
    RWASSERT(worldInfo);
    RWASSERT(textureList);

    if (worldInfo && textureList)
    {
        RwInt32             numTextures;

        numTextures = Field_NumElements(textureList);
        worldInfo->numTextures = numTextures;
        if (numTextures)
        {
            worldInfo->textures =
                (sfvec2f **) RwMalloc(sizeof(sfvec2f *) * numTextures);
            if (worldInfo->vertices)
            {
                RwInt32             i;
                sfvec2f           **textures = worldInfo->textures;

                for (i = 0; i < numTextures; i++)
                {
                    *textures = FieldMfvec2f_GetValue(textureList, i);
                    textures++;
                }

                RWRETURN(TRUE);
            }
        }

        RWRETURN(FALSE);
    }

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

RwBool
PreWorldInfo_SetTexFaces(PreWorldInfo * worldInfo, Field * texFaceList,
                         RwInt32 numVerts, RwInt32 lineNum)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_SetTexFaces"));
    RWASSERT(worldInfo);
    RWASSERT(texFaceList);

    if (worldInfo && texFaceList)
    {
        if (worldInfo->texFaces)
        {
            RwFree(worldInfo->texFaces);
        }

        worldInfo->numTextureFaces = CheckTriangleFaces(texFaceList,
                                                        numVerts,
                                                        lineNum);
        if (worldInfo->numTextureFaces == 0)
        {
            RWRETURN(FALSE);
        }

        worldInfo->texFaces = PreWorldInfo_CreateTriFaces(texFaceList,
                                                          worldInfo->
                                                          numTextureFaces,
                                                          worldInfo->
                                                          ccw);
        if (!worldInfo->texFaces)
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

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

    if (worldInfo)
    {
        RwInt32             numTextureFaces;
        faceLoop           *triFaces;

        if (worldInfo->numTextures != worldInfo->numVertices)
        {
            RWRETURN(FALSE);
        }

        numTextureFaces = worldInfo->numTriFaces;
        triFaces = worldInfo->triFaces;
        worldInfo->texFaces =
            PreWorldInfo_CreateDefaultFaces(triFaces, numTextureFaces);
        if (!worldInfo->texFaces)
        {
            RWRETURN(FALSE);
        }
        worldInfo->numTextureFaces = numTextureFaces;

        RWRETURN(TRUE);
    }

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

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

   colour faces

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

RwBool
PreWorldInfo_SetColours(PreWorldInfo * worldInfo, Field * colourList)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_SetColours"));
    RWASSERT(worldInfo);
    RWASSERT(colourList);

    if (worldInfo && colourList)
    {
        RwInt32             numColours;

        numColours = Field_NumElements(colourList);
        worldInfo->numColours = numColours;
        if (numColours)
        {
            worldInfo->colours =
                (sfcolor **) RwMalloc(sizeof(sfcolor *) * numColours);
            if (worldInfo->vertices)
            {
                RwInt32             i;
                sfcolor           **colours = worldInfo->colours;

                for (i = 0; i < numColours; i++)
                {
                    *colours = FieldMfcolor_GetValue(colourList, i);
                    colours++;
                }

                RWRETURN(TRUE);
            }
        }
    }

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

RwBool
PreWorldInfo_SetColFaces(PreWorldInfo * worldInfo, Field * colFaceList,
                         RwInt32 numVerts, RwInt32 lineNum)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_SetColFaces"));
    RWASSERT(worldInfo);
    RWASSERT(colFaceList);

    if (worldInfo && colFaceList)
    {
        if (worldInfo->colourFaces)
        {
            RwFree(worldInfo->colourFaces);
        }

        worldInfo->numColourFaces = CheckTriangleFaces(colFaceList,
                                                       numVerts,
                                                       lineNum);
        if (worldInfo->numColourFaces == 0)
        {
            RwFree(FALSE);
        }

        worldInfo->colourFaces =
            PreWorldInfo_CreateTriFaces(colFaceList,
                                        worldInfo->numColourFaces,
                                        worldInfo->ccw);
        if (!worldInfo->colourFaces)
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

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

RwBool
PreWorldInfo_SetSolidColFaces(PreWorldInfo * worldInfo,
                              Field * colFaceList)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_SetSolidColFaces"));
    RWASSERT(worldInfo);
    RWASSERT(colFaceList);

    if (worldInfo && colFaceList)
    {
        RwInt32             numColourFaces;
        RwInt32             numColours;
        faceLoop           *triFaces;

        numColours = worldInfo->numColours;
        if (!CheckSolidFaces(colFaceList, numColours))
        {
            RWRETURN(FALSE);
        }

        numColourFaces = worldInfo->numTriFaces;
        triFaces = worldInfo->triFaces;
        worldInfo->colourFaces =
            PreWorldInfo_CreateSolidFaces(colFaceList,
                                          triFaces, numColourFaces);
        if (!worldInfo->colourFaces)
        {
            RWRETURN(FALSE);
        }
        worldInfo->numColourFaces = numColourFaces;

        RWRETURN(TRUE);
    }

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

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

    if (worldInfo)
    {
        RwInt32             numColourFaces;
        faceLoop           *triFaces;

        numColourFaces = worldInfo->numTriFaces;
        triFaces = worldInfo->triFaces;
        worldInfo->colourFaces =
            PreWorldInfo_CreateDefaultFaces(triFaces, numColourFaces);
        if (!worldInfo->colourFaces)
        {
            RWRETURN(FALSE);
        }
        worldInfo->numColourFaces = numColourFaces;

        RWRETURN(TRUE);
    }

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

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

    if (worldInfo)
    {
        RwInt32             numColourFaces;
        RwInt32             numColours;
        faceLoop           *triFaces;

        numColourFaces = worldInfo->numTriFaces;
        numColours = worldInfo->numColours;
        if (!CheckDefaultSolidFaces(numColourFaces, numColours))
        {
            RWRETURN(FALSE);
        }

        triFaces = worldInfo->triFaces;
        worldInfo->colourFaces =
            PreWorldInfo_CreateDefaultSolidFaces(triFaces,
                                                 numColourFaces);
        if (!worldInfo->colourFaces)
        {
            RWRETURN(FALSE);
        }
        worldInfo->numColourFaces = numColourFaces;

        RWRETURN(TRUE);
    }

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

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

   normal faces

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

RwBool
PreWorldInfo_SetNormals(PreWorldInfo * worldInfo, Field * normalList)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_SetNormals"));
    RWASSERT(worldInfo);
    RWASSERT(normalList);

    if (worldInfo && normalList)
    {
        RwInt32             numNormals;

        numNormals = Field_NumElements(normalList);
        worldInfo->numNormals = numNormals;
        if (numNormals)
        {
            worldInfo->normals =
                (sfvec3f **) RwMalloc(sizeof(sfvec3f *) * numNormals);
            if (worldInfo->vertices)
            {
                RwInt32             i;
                sfvec3f           **normals = worldInfo->normals;

                for (i = 0; i < numNormals; i++)
                {
                    *normals = FieldMfvec3f_GetValue(normalList, i);
                    normals++;
                }

                RWRETURN(TRUE);
            }
        }
    }

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

RwBool
PreWorldInfo_SetNormalFaces(PreWorldInfo * worldInfo,
                            Field * normFaceList, RwInt32 numVerts,
                            RwInt32 lineNum)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_SetNormalFaces"));
    RWASSERT(worldInfo);
    RWASSERT(normFaceList);

    if (worldInfo && normFaceList)
    {
        if (worldInfo->normalFaces)
        {
            RwFree(worldInfo->normalFaces);
        }

        worldInfo->numNormalFaces = CheckTriangleFaces(normFaceList,
                                                       numVerts,
                                                       lineNum);
        if (worldInfo->numNormalFaces == 0)
        {
            RWRETURN(FALSE);
        }

        worldInfo->normalFaces =
            PreWorldInfo_CreateTriFaces(normFaceList,
                                        worldInfo->numNormalFaces,
                                        worldInfo->ccw);
        if (!worldInfo->normalFaces)
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

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

RwBool
PreWorldInfo_SetSolidNormalFaces(PreWorldInfo * worldInfo,
                                 Field * normalFaceList)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_SetSolidNormalFaces"));
    RWASSERT(worldInfo);
    RWASSERT(normalFaceList);

    if (worldInfo && normalFaceList)
    {
        RwInt32             numNormalFaces;
        RwInt32             numNormals;
        faceLoop           *triFaces;

        numNormals = worldInfo->numNormals;
        if (!CheckSolidFaces(normalFaceList, numNormals))
        {
            RWRETURN(FALSE);
        }

        numNormalFaces = worldInfo->numTriFaces;
        triFaces = worldInfo->triFaces;
        worldInfo->normalFaces =
            PreWorldInfo_CreateSolidFaces(normalFaceList, triFaces,
                                          numNormalFaces);
        if (!worldInfo->normalFaces)
        {
            RWRETURN(FALSE);
        }
        worldInfo->numNormalFaces = numNormalFaces;

        RWRETURN(FALSE);
    }

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

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

    if (worldInfo)
    {
        RwInt32             numNormalFaces;
        faceLoop           *triFaces;

        numNormalFaces = worldInfo->numTriFaces;
        triFaces = worldInfo->triFaces;
        worldInfo->normalFaces =
            PreWorldInfo_CreateDefaultFaces(triFaces, numNormalFaces);
        if (!worldInfo->normalFaces)
        {
            RWRETURN(FALSE);
        }
        worldInfo->numNormalFaces = numNormalFaces;

        RWRETURN(TRUE);
    }

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

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

    if (worldInfo)
    {
        RwInt32             numNormalFaces;
        RwInt32             numNormals;
        faceLoop           *triFaces;

        numNormalFaces = worldInfo->numTriFaces;
        numNormals = worldInfo->numNormals;
        if (!CheckDefaultSolidFaces(numNormalFaces, numNormals))
        {
            RWRETURN(FALSE);
        }

        triFaces = worldInfo->triFaces;
        worldInfo->normalFaces =
            PreWorldInfo_CreateDefaultSolidFaces(triFaces,
                                                 numNormalFaces);
        if (!worldInfo->normalFaces)
        {
            RWRETURN(FALSE);
        }
        worldInfo->numNormalFaces = numNormalFaces;

        RWRETURN(TRUE);
    }

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

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

    remapping

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

static int
#ifdef _WINDOWS
                    __cdecl
#endif                          /* _WINDOWS */
PreWorldReMapper_Compare(const void *pA, const void *pB)
{
    const PreWorldReMapper *mapA = (const PreWorldReMapper *) pA;
    const PreWorldReMapper *mapB = (const PreWorldReMapper *) pB;

    RWFUNCTION(RWSTRING("PreWorldReMapper_Compare"));
    RWASSERT(mapA);
    RWASSERT(mapB);

    /* can we sort on vertexIndex? */
    if (mapA->vertexIndex > mapB->vertexIndex)
    {
        RWRETURN(1);
    }
    else if (mapA->vertexIndex < mapB->vertexIndex)
    {
        RWRETURN(-1);
    }

    /* can we sort on textureIndex? */
    if (mapA->textureIndex > mapB->textureIndex)
    {
        RWRETURN(1);
    }
    else if (mapA->textureIndex < mapB->textureIndex)
    {
        RWRETURN(-1);
    }

    /* can we sort on colourIndex? */
    if (mapA->colourIndex > mapB->colourIndex)
    {
        RWRETURN(1);
    }
    else if (mapA->colourIndex < mapB->colourIndex)
    {
        RWRETURN(-1);
    }

    /* can we sort on normalIndex? */
    if (mapA->normalIndex > mapB->normalIndex)
    {
        RWRETURN(1);
    }
    else if (mapA->normalIndex < mapB->normalIndex)
    {
        RWRETURN(-1);
    }

    /* must be equal then */
    RWRETURN(0);
}

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

    if (worldInfo)
    {
        PreWorldReMapper   *remapper;
        PreWorldReMapper   *currentMap;
        RwInt32             faceNum, numFaces;

        numFaces = worldInfo->numTriFaces;
        remapper =
            (PreWorldReMapper *) RwMalloc(sizeof(PreWorldReMapper) *
                                          numFaces * 3);
        if (!remapper)
        {
            RWRETURN(FALSE);
        }

        currentMap = remapper;
        for (faceNum = 0; faceNum < numFaces; faceNum++)
        {
            RwUInt8             j;

            for (j = 0; j < 3; j++)
            {
                currentMap->faceIndex = faceNum;
                currentMap->triIndex = j;

                if (worldInfo->triFaces)
                {
                    currentMap->vertexIndex =
                        worldInfo->triFaces[faceNum].index[j];
                }
                else
                {
                    currentMap->vertexIndex = -1;
                }

                if (worldInfo->texFaces)
                {
                    currentMap->textureIndex =
                        worldInfo->texFaces[faceNum].index[j];
                }
                else
                {
                    currentMap->textureIndex = -1;
                }

                if (worldInfo->colourFaces)
                {
                    currentMap->colourIndex =
                        worldInfo->colourFaces[faceNum].index[j];
                }
                else
                {
                    currentMap->colourIndex = -1;
                }

                if (worldInfo->normalFaces)
                {
                    currentMap->normalIndex =
                        worldInfo->normalFaces[faceNum].index[j];
                }
                else
                {
                    currentMap->normalIndex = -1;
                }

                currentMap++;
            }
        }

        /* sort on vertexIndex->textureIndex->colorIndex->normalIndex */
        qsort(remapper, numFaces * 3, sizeof(PreWorldReMapper),
              PreWorldReMapper_Compare);

        /* now remap the vertex face triangles */
        {
            RwInt32             mapNum;
            RwInt32             numFinalVertices = 0;
            RwInt32             currentVertexIndex = -1;

            for (mapNum = 0; mapNum < numFaces * 3; mapNum++)
            {
                int                 faceIndex, triIndex;

                /* do we increament the vertex index? */
                if ((mapNum == 0) ||
                    (PreWorldReMapper_Compare(&remapper[mapNum - 1],
                                              &remapper[mapNum]) != 0))
                {
                    currentVertexIndex++;
                }

                /* remap its assoiacted face */
                faceIndex = remapper[mapNum].faceIndex;
                triIndex = remapper[mapNum].triIndex;
                worldInfo->triFaces[faceIndex].index[triIndex] =
                    currentVertexIndex;
            }

            numFinalVertices = currentVertexIndex + 1;
            worldInfo->finalVertices =
                (PreWorldVertex *) RwMalloc(sizeof(PreWorldVertex) *
                                            numFinalVertices);
            worldInfo->numFinalVertices = numFinalVertices;
            if (!worldInfo->finalVertices)
            {
                RwFree(remapper);

                RWRETURN(FALSE);
            }

            currentVertexIndex = -1;
            for (mapNum = 0; mapNum < numFaces * 3; mapNum++)
            {
                if ((mapNum == 0) ||
                    (PreWorldReMapper_Compare(&remapper[mapNum - 1],
                                              &remapper[mapNum]) != 0))
                {
                    int                 vertexIndex, textureIndex,
                        colourIndex, normalIndex;

                    currentVertexIndex++;

                    /* get vertexIndex */
                    vertexIndex = remapper[mapNum].vertexIndex;
                    worldInfo->finalVertices[currentVertexIndex].
                        vertexIndex = vertexIndex;

                    /* get textureIndex */
                    textureIndex = remapper[mapNum].textureIndex;
                    worldInfo->finalVertices[currentVertexIndex].
                        textureIndex = textureIndex;

                    /* get colourIndex */
                    colourIndex = remapper[mapNum].colourIndex;
                    worldInfo->finalVertices[currentVertexIndex].
                        colourIndex = colourIndex;

                    /* get normalIndex */
                    normalIndex = remapper[mapNum].normalIndex;
                    worldInfo->finalVertices[currentVertexIndex].
                        normalIndex = normalIndex;
                }
            }
        }

        /* done its job */
        RwFree(remapper);

        /* all done beautifully */
        RWRETURN(TRUE);
    }

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

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

   helper methods

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

static faceLoop    *
PreWorldInfo_CreateTriFaces(Field * triFacesList, RwInt32 numTriFaces,
                            RwBool ccw)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_CreateTriFaces"));
    RWASSERT(triFacesList);

    if (triFacesList)
    {
        faceLoop           *triFaces, *currentTriFace;
        sfint32             triIndices[3];
        sfint32            *pointInd = (sfint32 *)NULL;
        RwInt32             k = 2, lastInd = -1;
        RwInt32             faceNum = -1;
        RwInt32             i, numItems;

        /* 
         *  setup faces loops
         *  Takes a list of vertex indicies describing convex polygons
         *  and fans into triangles (concave polygons not handled yet)
         */

        currentTriFace = CreateFaceLoopArray(numTriFaces);
        if (!currentTriFace)
        {
            RWRETURN((faceLoop *)NULL);
        }
        triFaces = currentTriFace;

        numItems = Field_NumElements(triFacesList);

        for (i = 0; i < numItems; i++)
        {
            pointInd = FieldMfint32_GetValue(triFacesList, i);

            if (*pointInd != -1)
            {
                if (lastInd == -1)
                {
                    triIndices[0] = *pointInd;
                    faceNum++;
                    k = 1;
                }
                else
                {
                    if (k == 1)
                    {
                        triIndices[2] = *pointInd;
                        k = 2;
                    }
                    else
                    {
                        triIndices[1] = triIndices[2];
                        triIndices[2] = *pointInd;

                        if (ccw)
                        {
                            currentTriFace->index[0] = triIndices[0];
                            currentTriFace->index[1] = triIndices[1];
                            currentTriFace->index[2] = triIndices[2];
                        }
                        else
                        {
                            currentTriFace->index[0] = triIndices[2];
                            currentTriFace->index[1] = triIndices[1];
                            currentTriFace->index[2] = triIndices[0];

                        }
                        currentTriFace->faceNum = faceNum;
                        currentTriFace++;
                    }
                }
            }
            lastInd = *pointInd;
        }

        RWRETURN(triFaces);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((faceLoop *)NULL);
}

static faceLoop    *
PreWorldInfo_CreateSolidFaces(Field * faceList, faceLoop * triFaces,
                              RwInt32 numTriFaces)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_CreateSolidFaces"));
    RWASSERT(faceList);
    RWASSERT(triFaces);
    RWASSERT(numTriFaces);

    if (faceList && triFaces && numTriFaces)
    {
        RwInt32             prevFaceNum;
        faceLoop           *solidTriFaces;
        faceLoop           *currentTriFace;
        RwInt32             numItems, i;

        solidTriFaces = CreateFaceLoopArray(numTriFaces);
        if (!solidTriFaces)
        {
            RWRETURN((faceLoop *)NULL);
        }

        currentTriFace = solidTriFaces;
        prevFaceNum = 0;
        numItems = Field_NumElements(faceList);
        for (i = 0; i < numItems; i++)
        {
            while (prevFaceNum == triFaces->faceNum)
            {
                sfint32             index;

                index = *FieldMfint32_GetValue(faceList, i);
                currentTriFace->index[0] = index;
                currentTriFace->index[1] = index;
                currentTriFace->index[2] = index;
                currentTriFace->faceNum = triFaces->faceNum;
                currentTriFace++;
                triFaces++;
            }
            prevFaceNum = triFaces->faceNum;
        }

        RWRETURN(solidTriFaces);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((faceLoop *)NULL);
}

static faceLoop    *
PreWorldInfo_CreateDefaultFaces(faceLoop * triFaces,
                                RwInt32 numTriFaces)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_CreateDefaultFaces"));
    RWASSERT(triFaces);
    RWASSERT(numTriFaces);

    if (triFaces && numTriFaces)
    {
        RwInt32             triFaceIndex;
        faceLoop           *defaultFaces;
        faceLoop           *currentDefaultFace;

        defaultFaces = CreateFaceLoopArray(numTriFaces);
        if (!defaultFaces)
        {
            RWRETURN((faceLoop *)NULL);
        }

        currentDefaultFace = defaultFaces;
        for (triFaceIndex = 0; triFaceIndex < numTriFaces;
             triFaceIndex++)
        {
            currentDefaultFace->index[0] = triFaces->index[0];
            currentDefaultFace->index[1] = triFaces->index[1];
            currentDefaultFace->index[2] = triFaces->index[2];
            currentDefaultFace->faceNum = triFaces->faceNum;
            currentDefaultFace++;
            triFaces++;
        }

        RWRETURN(defaultFaces);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((faceLoop *)NULL);
}

static faceLoop    *
PreWorldInfo_CreateDefaultSolidFaces(faceLoop * triFaces,
                                     RwInt32 numTriFaces)
{
    RWFUNCTION(RWSTRING("PreWorldInfo_CreateDefaultSolidFaces"));
    RWASSERT(triFaces);
    RWASSERT(numTriFaces);

    if (triFaces && numTriFaces)
    {
        RwInt32             triFaceIndex;
        faceLoop           *defaultFaces;
        faceLoop           *currentDefaultFace;

        defaultFaces = CreateFaceLoopArray(numTriFaces);
        if (!defaultFaces)
        {
            RWRETURN((faceLoop *)NULL);
        }

        currentDefaultFace = defaultFaces;
        for (triFaceIndex = 0; triFaceIndex < numTriFaces;
             triFaceIndex++)
        {
            currentDefaultFace->index[0] = triFaces->faceNum;
            currentDefaultFace->index[1] = triFaces->faceNum;
            currentDefaultFace->index[2] = triFaces->faceNum;
            currentDefaultFace++;
            triFaces++;
        }

        RWRETURN(defaultFaces);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((faceLoop *)NULL);
}

faceLoop           *
CreateFaceLoopArray(RwInt32 numItems)
{
    faceLoop           *faceLoopArray;

    RWFUNCTION(RWSTRING("CreateFaceLoopArray"));

    faceLoopArray = (faceLoop *) RwMalloc(sizeof(faceLoop) * numItems);

    RWRETURN(faceLoopArray);
}

static              RwInt32
CheckTriangleFaces(Field * faceList, RwInt32 numVerts, RwInt32 lineNum)
{
    RWFUNCTION(RWSTRING("CheckTriangleFaces"));

    if (faceList)
    {
        RwInt32             nonDegenTriangles = 0;
        RwInt32             currentElement = 0;
        RwInt32             i, numItems, indCntr = 0;
        sfint32            *currentInd, prevInd;

        numItems = Field_NumElements(faceList);
        prevInd = -1;
        for (i = 0; i < numItems; i++)
        {
            RwInt32             startingElement =
                currentElement - indCntr;

            currentInd = FieldMfint32_GetValue(faceList, i);

            if (*currentInd < -1)
            {
                RWRETURN(0);
            }

            if (*currentInd > numVerts)
            {
                RWERROR((E_RP_VRML_COORDIND, lineNum, numVerts,
                         *currentInd, startingElement));
                RWRETURN(0);
            }

            if (*currentInd == -1)
            {
                if (indCntr < 3)
                {
                    RWERROR((E_RP_VRML_INVFACELOOP, lineNum,
                             startingElement));
                    RWRETURN(0);
                }
                else
                {
                    nonDegenTriangles += (indCntr - 2);
                }
                indCntr = 0;
            }
            else
            {
                indCntr++;
            }
            currentElement++;
            prevInd = *currentInd;
        }

        /* catch case where there is no -1 at end of loop */
        if (prevInd != -1)
        {
            if (indCntr < 3)
            {
                RwInt32             startingElement =
                    currentElement - indCntr;

                RWERROR((E_RP_VRML_INVFACELOOP, lineNum,
                         startingElement));
            }
            else
            {
                nonDegenTriangles += (indCntr - 2);
            }
        }

        RWRETURN(nonDegenTriangles);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(-1);
}

static              RwBool
CheckSolidFaces(Field * indList, RwInt32 numCoords)
{
    RWFUNCTION(RWSTRING("CheckSolidFaces"));
    RWASSERT(indList);

    if (indList)
    {
        RwInt32             numItems, i;

        numItems = Field_NumElements(indList);
        for (i = 0; i < numItems; i++)
        {
            sfint32            *currentInd;

            currentInd = FieldMfint32_GetValue(indList, i);
            if (*currentInd < 0)
            {
                /* error msg */
                RWRETURN(FALSE);
            }

            if (*currentInd > numCoords - 1)
            {
                /* error msg */
                RWRETURN(FALSE);
            }
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
CheckDefaultSolidFaces(RwInt32 numFaces, RwInt32 numCoords)
{
    RWFUNCTION(RWSTRING("CheckDefaultSolidFaces"));

    if (numFaces != numCoords)
    {
        /* error msg */
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}
