/*
 * Converting no hs worlds to real binary worlds (with bsps).
 * No HS worlds are used in the generation process of worlds
 *
 * Copyright (c) 1998 Criterion Software Ltd.
 */

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

#include <string.h>            /* Needed for memset */

#include "rwcore.h"
#include "rpworld.h"
#include "rpdbgerr.h"

#include "nhssmplx.h"
#include "nhsbary.h"
#include "nhswing.h"

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

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

typedef struct RtWeldEdge RtWeldEdge;
typedef struct RtWeldEdge *RtWeldEdgePtr;
struct RtWeldEdge
{
    RtWingEdge          edge;
    RtWeldEdge         *child[4]; /*  maintained in Quad-tree  */
};

typedef struct RtWeldEdgeHash RtWeldEdgeHash;
struct RtWeldEdgeHash
{
    RtWeldEdgePtr      *last;
    RtWeldEdgePtr       root;
};

typedef struct RtWeldState RtWeldState;
typedef struct RtWeldState *RtWeldStatePtr;
struct RtWeldState
{
    RtWeldEdgeHash      hash[256];
    RwFreeList         *WeldFreeList;
    RwInt32             edgeDuplications;
    RtWingPoly         *WingPoly;
    RtWorldImportBuildSector *buildSector;
    RtWorldImport      *wpNoHS;
    RwInt32             cycles;
};

typedef struct RtCountState RtCountState;
typedef struct RtCountState *RtCountStatePtr;
struct RtCountState
{
    RwInt32             ExternalCount;
    RwInt32             InternalCount;
    RwInt32             CoplanarInternalCount;
    RwInt32             CoplanarPropertyCount;
    RwInt32             BridgingInternalCount;
};

typedef struct RtVertexCallBackState RtVertexCallBackState;
struct RtVertexCallBackState
{
    RtWorldImportVertexCallBack callBack;
    void               *state;
};

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

#if (defined(RWDEBUG))

static RtWingEdgeReference *
ImportWingEdgeValidateCount(RtWingEdgeReference * curr, void *state)
{
    RtWingEdgeReference *pFinal = (RtWingEdgeReference *) state;

    RWFUNCTION(RWSTRING("ImportWingEdgeValidateCount"));

    RWASSERT(NULL != curr);
    RWASSERT(NULL != pFinal);

    pFinal->sense = curr->sense;
    pFinal->pEdge = curr->pEdge;

    RWRETURN(curr);
}

static RtWingEdgeReference *
ImportWingEdgeValidateLinks(RtWingEdgeReference * curr, void *state)
{
    RtWingPoly         *WingPoly = (RtWingPoly *) state;
    RtWingEdgeReference *link = (RtWingEdgeReference *) NULL;
    RtWing             *pEdge = (RtWing *) NULL;

    RWFUNCTION(RWSTRING("ImportWingEdgeValidateLinks"));

    pEdge = curr->pEdge;
    RWASSERT(WingPoly == pEdge[curr->sense].WingPoly);

    link = &pEdge[curr->sense].Next;
    RWASSERT(pEdge == link->pEdge[link->sense].Prev.pEdge);
    RWASSERT(curr->sense == link->pEdge[link->sense].Prev.sense);

    link = &pEdge[curr->sense].Prev;
    RWASSERT(pEdge == link->pEdge[link->sense].Next.pEdge);
    RWASSERT(curr->sense == link->pEdge[link->sense].Next.sense);

    RWRETURN(curr);
}

static void
ImportValidateWingPoly(RtWingPoly * WingPoly)
{
    RtWingEdgeReference final;

    RWFUNCTION(RWSTRING("ImportValidateWingPoly"));
    RWASSERT(NULL != WingPoly);

    RWASSERT((0 == WingPoly->edgeCount)
             || (NULL != WingPoly->Start.pEdge));

    if (0 < WingPoly->edgeCount)
    {
        /*
         * Validate linking of winged edges
         */
        _rtImportWingPolyForAllEdges(WingPoly,
                                     ImportWingEdgeValidateLinks,
                                     WingPoly);

        /*
         * Validate numbder of winged edges
         */
        final.pEdge = (RtWing *) NULL;
        _rtImportWingPolyForAllEdges(WingPoly,
                                     ImportWingEdgeValidateCount,
                                     &final);
        RWASSERT(NULL != final.pEdge);

        RWASSERT(final.sense == WingPoly->Start.sense);
        RWASSERT(final.pEdge == WingPoly->Start.pEdge);
    }

    RWRETURNVOID();
}

#define VALIDATEWINGPOLY(ptr) ImportValidateWingPoly(ptr)

#define VALIDATEALLWINGPOLYS(weldState)                                 \
MACRO_START                                                             \
{                                                                       \
    RtWorldImportBuildSector *buildSector;                              \
    RwInt32             numPolygons;                                    \
    RwInt32             nI;                                             \
                                                                        \
    RWASSERT(NULL != weldState);                                        \
                                                                        \
    buildSector = weldState->buildSector;                               \
    RWASSERT(NULL != buildSector);                                      \
                                                                        \
    numPolygons = buildSector->numPolygons;                             \
    for (nI = 0; nI < numPolygons; nI++)                                \
    {                                                                   \
        RtWingPoly         *const WingPoly = &weldState->WingPoly[nI];  \
                                                                        \
        VALIDATEWINGPOLY(WingPoly);                                     \
                                                                        \
    }                                                                   \
}                                                                       \
MACRO_STOP

#endif /* (defined(RWDEBUG)) */

#if (!defined(VALIDATEWINGPOLY))
#define VALIDATEWINGPOLY(ptr)  /* No op */
#endif /* (!defined(VALIDATEWINGPOLY)) */

#if (!defined(VALIDATEALLWINGPOLYS))
#define VALIDATEALLWINGPOLYS(weldState) /* No op */
#endif /* (!defined(VALIDATEALLWINGPOLYS)) */

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

RtWingPoly         *
_rtImportWingPolyForAllEdges(RtWingPoly * WingPoly,
                             RtWingEdgeReferenceCallBack callBack,
                             void *state)
{
    RwInt32             edgeCount;
    RtWingEdgeReference start;
    RtWingEdgeReference curr;

    RWFUNCTION(RWSTRING("_rtImportWingPolyForAllEdges"));

    RWASSERT(NULL != WingPoly);

    edgeCount = WingPoly->edgeCount;

    start.sense = WingPoly->Start.sense;
    start.pEdge = WingPoly->Start.pEdge;

    RWASSERT((edgeCount == 0) || (NULL != start.pEdge));

    curr.sense = start.sense;
    curr.pEdge = start.pEdge;

    while (--edgeCount >= 0)
    {
        RtWingEdgeReference next;
        RtWingEdgeReference *const link = &curr.pEdge[curr.sense].Next;

        next.sense = link->sense;
        next.pEdge = link->pEdge;

        if (!callBack(link, state))
        {
            WingPoly = (RtWingPoly *) NULL;
            break;
        }

        curr.sense = next.sense;
        curr.pEdge = next.pEdge;
    }

    RWRETURN(WingPoly);
}

static RtWingEdgeReference *
ImportWingEdgeVertexFunc(RtWingEdgeReference * curr, void *state)
{
    RtVertexCallBackState *VertexCallBackState =
        (RtVertexCallBackState *) state;
    RtWorldImportVertexCallBack VertexFunc;
    void               *VertexState;
    RtWorldImportVertex *vpStop;

    RWFUNCTION(RWSTRING("ImportWingEdgeVertexFunc"));

    RWASSERT(NULL != curr);
    vpStop = curr->pEdge[curr->sense].Vert;

    RWASSERT(NULL != state);
    VertexFunc = VertexCallBackState->callBack;
    VertexState = VertexCallBackState->state;

    if (NULL == VertexFunc(vpStop, VertexState))
    {
        curr = (RtWingEdgeReference *) NULL;
    }

    RWRETURN(curr);
}

RtWingPoly         *
_rtImportWingPolyForAllVertices(RtWingPoly * WingPoly,
                                RtWorldImportVertexCallBack callBack,
                                void *state)
{
    RtVertexCallBackState VertexCallBackState;

    RWFUNCTION(RWSTRING("_rtImportWingPolyForAllVertices"));

    RWASSERT(NULL != WingPoly);

    VertexCallBackState.callBack = callBack;
    VertexCallBackState.state = state;

    WingPoly =
        _rtImportWingPolyForAllEdges(WingPoly,
                                     ImportWingEdgeVertexFunc,
                                     &VertexCallBackState);

    RWRETURN(WingPoly);
}

#define WingEdgeHashVal(_result, _v0, _v1)                      \
MACRO_START                                                     \
{                                                               \
    (_result) = ((RwUInt32) (_v0)) + ((RwUInt32) (_v1));        \
    (_result) = ((_result) >> 16) + (_result);                  \
    (_result) = ((_result) >> 8) + (_result);                   \
    (_result) = (RwUInt32) ((RwUInt8) (_result));               \
                                                                \
    RWASSERT(((_result)) < 256);                                \
}                                                               \
MACRO_STOP

#if (defined(_MSC_VER) && defined (RWVERBOSE) && defined(RWDEBUG))
static unsigned int wing_hash_hit[256];

#define WING_HASH_HIT(_hash_val)  wing_hash_hit[_hash_val]++;
#endif /* (defined(_MSC_VER) && defined (RWVERBOSE) && defined(RWDEBUG)) */

#if (!defined(WING_HASH_HIT))
#define WING_HASH_HIT(_hash_val) /* No op */
#endif /* (!defined(WING_HASH_HIT)) */

static RtWing      *
ImportFindWingEdge(RtWorldImportVertex * const v0,
                   RtWorldImportVertex * const v1, const RwInt32 sense,
                   RtWeldState * const weldState)
{
    RtWing             *result = (RtWing *) NULL;
    RtWeldEdgePtr       candidate;
    RtWorldImportVertex *const vlo = (sense) ? (v0) : (v1);
    RtWorldImportVertex *const vhi = (sense) ? (v1) : (v0);
    RtWeldEdgePtr      *parent;
    RwUInt32            hash_val;
    RtWeldEdgeHash     *hash;

    RWFUNCTION(RWSTRING("ImportFindWingEdge"));

    RWASSERT(NULL != (weldState));

    WingEdgeHashVal(hash_val, (v0), (v1));
    hash = &(weldState)->hash[hash_val];

    WING_HASH_HIT(hash_val);

    parent = hash->last;

    if ((!parent) ||
        (!(candidate = *parent)) ||
        (vlo != candidate->edge[0].Vert) ||
        (vhi != candidate->edge[1].Vert) ||
        (NULL != candidate->edge[(sense)].WingPoly))
    {
        parent = &hash->root;

        /* Sort into Quad-tree */
        for (candidate = *parent; candidate; candidate = *parent)
        {
            RtWing             *const Wing = candidate->edge;
            const RwInt32       difference_lo = vlo - Wing[0].Vert;
            const RwInt32       difference_hi = vhi - Wing[1].Vert;
            RwInt32             index;

            if (!(difference_lo | difference_hi))
            {
                if (NULL == Wing[(sense)].WingPoly)
                    break;
                else
                    (weldState)->edgeDuplications++;
            }

            index = (((difference_lo >= 0) << 1) |
                     ((difference_hi >= 0) << 0));

            parent = &candidate->child[index];
        }
    }

    if (candidate)
    {
        (result) = candidate->edge;
    }
    else
    {
        /* New -- fresh vertex */
        RwFreeList         *const WeldFreeList =
            (weldState)->WeldFreeList;

        RWASSERT(NULL != WeldFreeList);
        candidate = (RtWeldEdgePtr) RwFreeListAlloc(WeldFreeList);
        RWASSERT(NULL != candidate);

        memset(candidate, 0, sizeof(RtWeldEdge));
        *parent = candidate;

        (result) = candidate->edge;
        (result)[0].Vert = vlo;
        (result)[1].Vert = vhi;
    }

    hash->last = parent;

    RWRETURN(result);
}

static void
ImportWingPolyTriangleConstructor(RtWingPoly * WingPoly,
                                  RtWorldImportVertex * v0,
                                  RtWorldImportVertex * v1,
                                  RtWorldImportVertex * v2,
                                  RtWeldState * weldState)
{
    RtDV4d             *mat;
    RwV3d              *c0;
    RwV3d              *c1;
    RwV3d              *c2;
    RwReal              area;
    RtWingEdgeReference WingEdge[3];
    RtWingEdgeReference *curr;
    RtWingEdgeReference *next;
    RtWingEdgeReference *prev;
    RtWingEdgeReference *link;
    RtBarySupport      *support;

    RWFUNCTION(RWSTRING("ImportWingPolyTriangleConstructor"));

    RWASSERT(NULL != WingPoly);

    support = &WingPoly->support;

    c0 = &v0->OC;
    c1 = &v1->OC;
    c2 = &v2->OC;

    support->base[0] = v0;
    support->base[1] = v1;
    support->base[2] = v2;

    RtWorldImportVertexSubtractMacro(&support->edge[0], v2, v1);
    RtWorldImportVertexSubtractMacro(&support->edge[1], v0, v2);
    RtWorldImportVertexSubtractMacro(&support->edge[2], v1, v0);

    mat = support->bary;

    WingPoly->hasBarySupport =
        _rtImportBarycentricFromCartesianMatrix(mat, &area, c0, c1, c2);

    WingPoly->support.area = area;

    WingEdge[0].sense = (v1 < v2);

    WingEdge[0].pEdge =
        ImportFindWingEdge(v1, v2, WingEdge[0].sense, weldState);
    RWASSERT(NULL != WingEdge[0].pEdge);
    RWASSERT(WingEdge[0].pEdge[0].Vert ==
             (WingEdge[0].sense ? v1 : v2));
    RWASSERT(WingEdge[0].pEdge[1].Vert ==
             (WingEdge[0].sense ? v2 : v1));
    RWASSERT(NULL == WingEdge[0].pEdge[WingEdge[0].sense].WingPoly);
    WingEdge[0].pEdge[WingEdge[0].sense].WingPoly = WingPoly;

    WingEdge[1].sense = (v2 < v0);

    WingEdge[1].pEdge =
        ImportFindWingEdge(v2, v0, WingEdge[1].sense, weldState);
    RWASSERT(NULL != WingEdge[1].pEdge);
    RWASSERT(WingEdge[1].pEdge[0].Vert ==
             (WingEdge[1].sense ? v2 : v0));
    RWASSERT(WingEdge[1].pEdge[1].Vert ==
             (WingEdge[1].sense ? v0 : v2));
    RWASSERT(NULL == WingEdge[1].pEdge[WingEdge[1].sense].WingPoly);
    WingEdge[1].pEdge[WingEdge[1].sense].WingPoly = WingPoly;

    WingEdge[2].sense = (v0 < v1);

    WingEdge[2].pEdge =
        ImportFindWingEdge(v0, v1, WingEdge[2].sense, weldState);
    RWASSERT(NULL != WingEdge[2].pEdge);
    RWASSERT(WingEdge[2].pEdge[0].Vert ==
             (WingEdge[2].sense ? v0 : v1));
    RWASSERT(WingEdge[2].pEdge[1].Vert ==
             (WingEdge[2].sense ? v1 : v0));
    RWASSERT(NULL == WingEdge[2].pEdge[WingEdge[2].sense].WingPoly);
    WingEdge[2].pEdge[WingEdge[2].sense].WingPoly = WingPoly;

    curr = &WingEdge[0];
    next = &WingEdge[1];
    prev = &WingEdge[2];

    link = &curr->pEdge[curr->sense].Next;
    link->sense = next->sense;
    link->pEdge = next->pEdge;
    link = &curr->pEdge[curr->sense].Prev;
    link->sense = prev->sense;
    link->pEdge = prev->pEdge;
    link = curr;
    curr = next;
    next = prev;
    prev = link;

    link = &curr->pEdge[curr->sense].Next;
    link->sense = next->sense;
    link->pEdge = next->pEdge;
    link = &curr->pEdge[curr->sense].Prev;
    link->sense = prev->sense;
    link->pEdge = prev->pEdge;
    link = curr;
    curr = next;
    next = prev;
    prev = link;

    link = &curr->pEdge[curr->sense].Next;
    link->sense = next->sense;
    link->pEdge = next->pEdge;
    link = &curr->pEdge[curr->sense].Prev;
    link->sense = prev->sense;
    link->pEdge = prev->pEdge;
    link = curr;
    curr = next;
    next = prev;
    prev = link;

    WingPoly->Start.pEdge = curr->pEdge;
    WingPoly->Start.sense = curr->sense;

    WingPoly->edgeCount = 3;
    WingPoly->phase = 0;

    RWRETURNVOID();
}

#define   _TEX_EPSILON (((RwReal)1)/((RwReal)(1<<4)))
#define _PLANE_EPSILON (((RwReal)1)/((RwReal)(1<<3)))

#if (0 && defined(RWDEBUG) && defined(RWVERBOSE))
static RtWorldImportVertex *
ImportInPlaneAssert(RtWorldImportVertex * vpVert, RtDV4d normal)
{
    RwV3d              *pos;
    RwDReal             offset;

    RWFUNCTION(RWSTRING("ImportInPlaneAssert"));

    RWASSERT(vpVert);
    RWASSERT(normal);

    pos = &vpVert->OC;

    offset = (normal[0] * normal[0] +
              normal[1] * normal[1] +
              normal[2] * normal[2]) - (RwReal) 1;

    RWASSERT(WithinEpsilonOfZero(offset, _PLANE_EPSILON));

    offset = (normal[0] * pos->x +
              normal[1] * pos->y + normal[2] * pos->z + normal[3]);

    RWASSERT(WithinEpsilonOfZero(offset, _PLANE_EPSILON));

    RWRETURN(vpVert);

}
#endif /* (0 && defined(RWDEBUG) && defined(RWVERBOSE)) */

#define _PLANAR_NORMAL_EPSILON (((RwReal)1)/((RwReal)(1<<4)))
#define _PLANAR_OFFSET_EPSILON (((RwReal)1)/((RwReal)(1<<4)))

static              RwBool
ImportCoPlanarGeometryWingPolys(RtWingPoly * poly0, RtWingPoly * poly1)
{
    RwBool              coplanar = FALSE;
    RwReal              poly0Area;
    RwReal              poly1Area;
    RtWingPoly         *more;
    RtWingPoly         *less;

    RWFUNCTION(RWSTRING("ImportCoPlanarGeometryWingPolys"));

    RWASSERT(NULL != poly0);
    RWASSERT(NULL != poly1);

    poly0Area = poly0->support.area;
    poly1Area = poly1->support.area;

    if (poly0Area < poly1Area)
    {
        more = poly1;
        less = poly0;
    }
    else
    {
        more = poly0;
        less = poly1;
    }

    coplanar = (more->hasBarySupport);

    if (coplanar)
    {

#if (0 && defined(PROPAGATIONOVERZEROAREA))
        coplanar = !(less->hasBarySupport);

        if (coplanar)
        {
            less->support = more->support;
            less->hasBarySupport = TRUE;
        }
        else
#else /* (0 && defined(PROPAGATIONOVERZEROAREA)) */
        coplanar = (less->hasBarySupport);
        if (coplanar)
#endif /* (0 && defined(PROPAGATIONOVERZEROAREA)) */
        {

            RwDReal             dot_normal;
            RtDV4d             *const moreBary = &more->support.bary[0];
            RtDV4d             *const lessBary = &less->support.bary[0];

            dot_normal = ((moreBary[0][3] * lessBary[0][3]) +
                          (moreBary[1][3] * lessBary[1][3]) +
                          (moreBary[2][3] * lessBary[2][3]));

            coplanar =
                (dot_normal >= ((RwReal) 1) - _PLANAR_NORMAL_EPSILON);

#if (1 || defined(WINGSMIGHTNOTSHAREEDGE))
            if (coplanar)
            {
                RwDReal             diff_offset;

                diff_offset = moreBary[3][3] - lessBary[3][3];

                coplanar = WithinEpsilonOfZero(diff_offset,
                                               _PLANAR_OFFSET_EPSILON);
            }
#endif /* (1 || defined(WINGSMIGHTNOTSHAREEDGE)) */

        }
    }

    RWRETURN(coplanar);
}

static              RwBool
ImportCoPlanarPropertyWingPolys(RtWingPoly * source, RtWingPoly * match)
{
    RwBool              coplanar = TRUE;
    RtDV4d             *bary;
    RtWorldImportVertex **base;
    RtWorldImportVertex *edge;
    RtWorldImportVertex extrap;
    RwReal              sourceArea;
    RwReal              matchArea;
    RtWingPoly         *more;
    RtWingPoly         *less;
    RwUInt32            i;

    RWFUNCTION(RWSTRING("ImportCoPlanarPropertyWingPolys"));

    RWASSERT(NULL != source);
    RWASSERT(NULL != match);

    RWASSERT(ImportCoPlanarGeometryWingPolys(source, match));

    sourceArea = source->support.area;
    matchArea = match->support.area;

    if (sourceArea < matchArea)
    {
        more = match;
        less = source;
    }
    else
    {
        more = source;
        less = match;
    }

    bary = &more->support.bary[0];
    base = &more->support.base[0];
    edge = &less->support.edge[0];

    for (i = 0; i < 3; i++)
    {
        RtDV4d              weight;
        RwReal              rDelta;

        _rtImportWorldBaryFromEdge(weight, bary, &edge[i].OC);

#if (0 && defined(RWDEBUG) && defined(RWVERBOSE))
        /*
         * assert that the point lines in the triangles plane ...
         */

        RWASSERT(WithinEpsilonOfZero(weight[3], _PLANAR_EPSILON));
#endif /* (0 && defined(RWDEBUG) && defined(RWVERBOSE)) */

#if (0)
        extrap.OC.x = (weight[0] * base[0]->OC.x +
                       weight[1] * base[1]->OC.x +
                       weight[2] * base[2]->OC.x);
        extrap.OC.y = (weight[0] * base[0]->OC.y +
                       weight[1] * base[1]->OC.y +
                       weight[2] * base[2]->OC.y);
        extrap.OC.z = (weight[0] * base[0]->OC.z +
                       weight[1] * base[1]->OC.z +
                       weight[2] * base[2]->OC.z);
#endif /* (0) */

        extrap.texCoords[0].u = (RwReal)
            (weight[0] * base[0]->texCoords[0].u +
             weight[1] * base[1]->texCoords[0].u +
             weight[2] * base[2]->texCoords[0].u);

        rDelta = (edge[i].texCoords[0].u - extrap.texCoords[0].u);
        coplanar = (WithinEpsilonOfZero(rDelta, _TEX_EPSILON));
        if (!coplanar)
        {
#if (0 && (defined(RWDEBUG) && defined(RWVERBOSE)))
            RWMESSAGE(("U Texture coordinate not coplanar: %f == rDelta", rDelta));
#endif /* (0 && defined(RWDEBUG) && defined(RWVERBOSE))) */

            break;
        }

        extrap.texCoords[0].v = (RwReal)
            (weight[0] * base[0]->texCoords[0].v +
             weight[1] * base[1]->texCoords[0].v +
             weight[2] * base[2]->texCoords[0].v);

        rDelta = (edge[i].texCoords[0].v - extrap.texCoords[0].v);
        coplanar = (WithinEpsilonOfZero(rDelta, _TEX_EPSILON));
        if (!coplanar)
        {
#if (0 && (defined(RWDEBUG) && defined(RWVERBOSE)))
            RWMESSAGE(("V Texture coordinate not coplanar: %f == rDelta", rDelta));
#endif /* (0 && defined(RWDEBUG) && defined(RWVERBOSE))) */

            break;
        }
#if (0)

        extrap.texCoords[1].u = (weight[0] * base[0]->texCoords[1].u +
                               weight[1] * base[1]->texCoords[1].u +
                               weight[2] * base[2]->texCoords[1].u);
        extrap.texCoords[1].v = (weight[0] * base[0]->texCoords[1].v +
                               weight[1] * base[1]->texCoords[1].v +
                               weight[2] * base[2]->texCoords[1].v);
#endif /* (0) */
    }

#if (0)
    {

        RtWingPoly         *status =
            _rtImportWingPolyForAllVertices(more,
                                            ImportCoPlanarProperties,
                                            less);

        coplanar = (NULL != status);
    }

    RWMONITOR(("coplanar %d", coplanar));

#endif /* (0) */

    RWRETURN(coplanar);
}

static RtWingEdgeReference *
ImportWingEdgeSetWingPoly(RtWingEdgeReference * curr, void *state)
{
    RtWingPoly         *WingPoly = (RtWingPoly *) state;

    RWFUNCTION(RWSTRING("ImportWingEdgeSetWingPoly"));
    RWASSERT(NULL != curr);
    RWASSERT(NULL != WingPoly);

    curr->pEdge[curr->sense].WingPoly = WingPoly;

    RWRETURN(curr);
}

static RtWingPoly  *
ImportWingEdgeUnionPolys(RtWing * Wing)
{
    RtWingPoly         *more;
    RtWingPoly         *less;
    RtWingEdgeReference *backwardNext;
    RtWingEdgeReference *backwardPrev;
    RtWingEdgeReference *forewardNext;
    RtWingEdgeReference *forewardPrev;
    RwReal              backwardArea;
    RwReal              forewardArea;

    RWFUNCTION(RWSTRING("ImportWingEdgeUnionPolys"));
    RWASSERT(NULL != Wing);
    RWASSERT(ImportCoPlanarPropertyWingPolys(Wing[rwBACKWARD].WingPoly,
                                             Wing[rwFOREWARD].
                                             WingPoly));

    backwardArea = Wing[rwBACKWARD].WingPoly->support.area;
    forewardArea = Wing[rwFOREWARD].WingPoly->support.area;

    if (backwardArea < forewardArea)
    {
        more = Wing[rwFOREWARD].WingPoly;
        less = Wing[rwBACKWARD].WingPoly;
    }
    else
    {
        more = Wing[rwBACKWARD].WingPoly;
        less = Wing[rwFOREWARD].WingPoly;
    }

    RWASSERT((NULL != more) && (NULL != less) && (more != less));

    RWASSERT((more->pinfo.matIndex == less->pinfo.matIndex));

    /*
     * Merge Smaller Poly loop into Bigger Poly loop
     */

    /*
     * Remove all records of Smaller Poly
     */
    _rtImportWingPolyForAllEdges(less, ImportWingEdgeSetWingPoly, more);

    /*
     * Cache links from pivot edge
     */
    backwardPrev = &Wing[rwBACKWARD].Prev;
    forewardNext = &Wing[rwFOREWARD].Next;

    backwardNext = &Wing[rwBACKWARD].Next;
    forewardPrev = &Wing[rwFOREWARD].Prev;

    /*
     * Link Forward loop into Backward loop
     */

    backwardPrev->pEdge[backwardPrev->sense].Next = *forewardNext;
    forewardNext->pEdge[forewardNext->sense].Prev = *backwardPrev;
    backwardNext->pEdge[backwardNext->sense].Prev = *forewardPrev;
    forewardPrev->pEdge[forewardPrev->sense].Next = *backwardNext;

    /*
     * Update start edge reference for Bigger Poly
     */

    more->Start = *backwardNext;
    more->edgeCount = (more->edgeCount + less->edgeCount - 2);

    VALIDATEWINGPOLY(more);

    /*
     * Leave Smaller Poly loop as 2 edge degenerate
     */

    backwardNext->pEdge = Wing;
    backwardNext->sense = rwFOREWARD;
    forewardPrev->pEdge = Wing;
    forewardPrev->sense = rwBACKWARD;

    forewardNext->pEdge = Wing;
    forewardNext->sense = rwBACKWARD;
    backwardPrev->pEdge = Wing;
    backwardPrev->sense = rwFOREWARD;

    less->Start.pEdge = Wing;
    less->Start.sense = rwFOREWARD;
    less->edgeCount = 2;

    /*
     * Reset records for less
     */
    _rtImportWingPolyForAllEdges(less, ImportWingEdgeSetWingPoly, less);

    VALIDATEWINGPOLY(less);

    RWRETURN(more);
}

static void
ImportMergeWings(RtWeldEdgePtr candidate,
                 RtWeldState * __RWUNUSED__ weldState,
                 RtCountState * countState)
{
    RwBool              geometryPlanar;
    RtWing             *Wing;
    RtWingPoly         *Poly0;
    RtWingPoly         *Poly1;

    RWFUNCTION(RWSTRING("ImportMergeWings"));

    Wing = candidate->edge;
    Poly0 = Wing[0].WingPoly;
    Poly1 = Wing[1].WingPoly;

    geometryPlanar = ImportCoPlanarGeometryWingPolys(Poly0, Poly1);

    if (geometryPlanar)
    {
        RwBool              propertyPlanar;

        countState->CoplanarInternalCount++;

        propertyPlanar = (Poly0->hasBarySupport &&
                          Poly1->hasBarySupport &&
                          ImportCoPlanarPropertyWingPolys(Poly0,
                                                          Poly1));

        if (propertyPlanar)
        {
            /* VALIDATEALLWINGPOLYS(weldState); */

#if (0 && (defined(RWDEBUG) && defined(RWVERBOSE)))
            if (weldState->cycles)
            {
                RWMESSAGE(("%d == weldState->cycles",
                           weldState->cycles));
                RWMESSAGE(("[ %f %f %f | %f ] Poly0 [ %f %f %f | %f ] Poly1", Poly0->bary[0][3], Poly0->bary[1][3], Poly0->bary[2][3], Poly0->bary[3][3], Poly1->bary[0][3], Poly1->bary[1][3], Poly1->bary[2][3], Poly1->bary[3][3]));
            }
#endif /* (0 && (defined(RWDEBUG) && defined(RWVERBOSE))) */

            ImportWingEdgeUnionPolys(Wing);

            /* VALIDATEALLWINGPOLYS(weldState); */
            countState->CoplanarPropertyCount++;
        }
        else
        {
            COLORNONPROPERTYPLANAREDGE(candidate);
        }
    }
    else
    {
        COLORNONGEOMETRYPLANAREDGE(candidate);
    }

    RWRETURNVOID();
}

static              RtWeldEdgePtr
ImportMergeEdges(RtWeldEdgePtr candidate,
                 RtWeldState * weldState, RtCountState * countState)
{
    RWFUNCTION(RWSTRING("ImportMergeEdges"));
    if (NULL != candidate)
    {
        RtWing             *const Wing = candidate->edge;
        RwInt32             i;

        RWASSERT(!RWORPHANEDGE(Wing));
        RWASSERT((!RWINTERNALEDGE(Wing)) ||
                 (Wing[0].WingPoly->pinfo.matIndex ==
                  Wing[1].WingPoly->pinfo.matIndex));

        /* Merge children */
        for (i = 0; i < 4; i++)
        {
            candidate->child[i] = ImportMergeEdges(candidate->child[i],
                                                   weldState,
                                                   countState);
        }

        if (!RWINTERNALEDGE(Wing))
        {
            countState->ExternalCount++;
            COLOREXTERNALEDGE(candidate);
        }
        else
        {
            countState->InternalCount++;
            if (RWBRIDGINGEDGE(Wing))
            {
                countState->BridgingInternalCount++;
                COLORBRIDGINGEDGE(candidate);
            }
            else
            {
                ImportMergeWings(candidate, weldState, countState);
            }
        }
    }

    RWRETURN(candidate);
}

static              RtWeldEdgePtr
ImportPurgeTree(RtWeldEdgePtr root, RwFreeList * WeldFreeList)
{
    RWFUNCTION(RWSTRING("ImportPurgeTree"));
    if (NULL != root)
    {
        RwInt32             i;

        /* Purge children */
        for (i = 0; i < 4; i++)
        {
            root->child[i] =
                ImportPurgeTree(root->child[i], WeldFreeList);
        }
#if (0)
        RWMONITOR(("%d == root->refCount", (int) root->refCount));
#endif /* (0) */
        RwFreeListFree(WeldFreeList, root);
        root = (RtWeldEdgePtr) NULL;
    }

    RWRETURN(root);
}

static RtWeldState *
ImportWingStructureDestroy(RtWeldState * weldState)
{
    RwInt32             nI;

    RWFUNCTION(RWSTRING("ImportWingStructureDestroy"));
    RWASSERT(NULL != weldState);

    for (nI = 0; nI < 256; nI++)
    {
        ImportPurgeTree(weldState->hash[nI].root,
                        weldState->WeldFreeList);
    }

    if (NULL != weldState->WingPoly)
    {
        RwFree(weldState->WingPoly);
        weldState->WingPoly = (RtWingPoly *) NULL;
    }

    RWRETURN(weldState);
}

static RtWeldState *
ImportWingStructureCreate(RtWeldState * weldState)
{
    RtWorldImportBuildVertex *boundaries;
    RtWorldImportBuildSector *buildSector;
    RwInt32             numPolygons;
    RwInt32             nI;

    RWFUNCTION(RWSTRING("ImportWingStructureCreate"));
    RWASSERT(NULL != weldState);
    buildSector = weldState->buildSector;
    numPolygons = buildSector->numPolygons;
    boundaries = buildSector->boundaries;
    weldState->WingPoly = (RtWingPoly *)
        RwMalloc(sizeof(RtWingPoly) * numPolygons);
    if (NULL != weldState->WingPoly)
    {
        for (nI = 0; nI < numPolygons; nI++)
        {
            RtWingPoly         *const WingPoly =
                &weldState->WingPoly[nI];
            RtWorldImportVertex *const v0 = (boundaries++)->mode.vpVert;
            RtWorldImportVertex *const v1 = (boundaries++)->mode.vpVert;
            RtWorldImportVertex *const v2 = (boundaries++)->mode.vpVert;

            RWASSERT(NULL == boundaries->mode.vpVert);
            ImportWingPolyTriangleConstructor(WingPoly, v0, v1,
                                              v2, weldState);
            VALIDATEWINGPOLY(WingPoly);
            WingPoly->pinfo = (boundaries++)->pinfo;
            WingPoly->nI = nI;
        }

    }
    else
    {
        weldState = (RtWeldState *) NULL;
    }

    RWRETURN(weldState);
}

static RtWingEdgeReference *
ImportWingPolyRemoveFirstSnipEdge(RtWingEdgeReference * curr,
                                  void *state)
{
    RtWingPoly         *WingPoly = (RtWingPoly *) state;
    RtWing             *currWing;

    RWFUNCTION(RWSTRING("ImportWingPolyRemoveFirstSnipEdge"));
    RWASSERT(NULL != WingPoly);
    RWASSERT(NULL != curr);
    currWing = curr->pEdge;
    RWASSERT(WingPoly == currWing[curr->sense].WingPoly);
    /*
     * Is it a bridging edge ?
     */
    if (RWBRIDGINGEDGE(currWing))
    {
        RtWingEdgeReference *next;
        RtWing             *nextWing;

        next = &currWing[curr->sense].Next;
        RWASSERT(NULL != next);
        nextWing = next->pEdge;
        RWASSERT(WingPoly == nextWing[next->sense].WingPoly);
        /*
         * Is it a snip edge ?
         */
        if (next->pEdge == curr->pEdge)
        {
            RtWingEdgeReference *before = &currWing[curr->sense].Prev;
            RtWing             *beforeWing = before->pEdge;
            RtWingEdgeReference *after = &nextWing[next->sense].Next;
            RtWing             *afterWing = after->pEdge;

            VALIDATEWINGPOLY(WingPoly);
#if (0)
            RWMONITOR(("Snip! [ %f %f %f ] [ %f %f %f ]",
                       currWing[0].Vert->OC.x,
                       currWing[0].Vert->OC.y,
                       currWing[0].Vert->OC.z,
                       currWing[1].Vert->OC.x,
                       currWing[1].Vert->OC.y, currWing[1].Vert->OC.z));
#endif /* (0) */
            /* Remove  snip */
            beforeWing[before->sense].Next.sense = after->sense;
            beforeWing[before->sense].Next.pEdge = after->pEdge;
            afterWing[after->sense].Prev.sense = before->sense;
            afterWing[after->sense].Prev.pEdge = before->pEdge;
            /* Decrement polygon edge count  */
            WingPoly->Start.sense = before->sense;
            WingPoly->Start.pEdge = before->pEdge;
            WingPoly->edgeCount = WingPoly->edgeCount - 2;
            /* Tag snip as orphanned */
            currWing[0].WingPoly = (RtWingPoly *) NULL;
            currWing[1].WingPoly = (RtWingPoly *) NULL;
            /*
             * Flag snip removal
             */
            curr = (RtWingEdgeReference *) NULL;
            VALIDATEWINGPOLY(WingPoly);
        }
    }

    RWRETURN(curr);
}

static RtWeldState *
ImportWingStructureRemoveSnips(RtWeldState * weldState)
{
    RtWorldImportBuildSector *buildSector;
    RwInt32             numPolygons;
    RwInt32             nI;

    RWFUNCTION(RWSTRING("ImportWingStructureRemoveSnips"));
    RWASSERT(NULL != weldState);
    buildSector = weldState->buildSector;
    RWASSERT(NULL != buildSector);
    numPolygons = buildSector->numPolygons;
    for (nI = 0; nI < numPolygons; nI++)
    {
        RtWingPoly         *const WingPoly = &weldState->WingPoly[nI];

        while (2 < WingPoly->edgeCount)
        {
            if (_rtImportWingPolyForAllEdges(WingPoly,
                                             ImportWingPolyRemoveFirstSnipEdge,
                                             WingPoly))
                break;
        }
    }

    RWRETURN(weldState);
}

static RtWeldState *
ImportWingStructureMergePolygons(RtWeldState * weldState)
{
    RwInt32             nI;
    RtCountState        countState;

    RWFUNCTION(RWSTRING("ImportWingStructureMergePolygons"));
    RWASSERT(NULL != weldState);
    countState.ExternalCount = 0;
    countState.InternalCount = 0;
    countState.CoplanarInternalCount = 0;
    countState.CoplanarPropertyCount = 0;
    countState.BridgingInternalCount = 0;
    RWMONITOR((" - "));

    for (nI = 0; nI < 256; nI++)
    {
        ImportMergeEdges(weldState->hash[nI].root, weldState,
                         &countState);
    }

    RWMONITOR(("%d == countState.ExternalCount",
               (int) countState.ExternalCount));
    RWMONITOR(("%d == countState.InternalCount",
               (int) countState.InternalCount));
    RWMONITOR(("%d == countState.CoplanarInternalCount",
               (int) countState.CoplanarInternalCount));
    RWMONITOR(("%d == countState.CoplanarPropertyCount",
               (int) countState.CoplanarPropertyCount));
    RWMONITOR(("%d == countState.BridgingInternalCount",
               (int) countState.BridgingInternalCount));
#if (0 || defined(SECOND_MERGE_PASS))
    /*
     * Second pass shouldn't find any more mergeable edges;
     * these should all have become bridging edges
     */
    countState.ExternalCount = 0;
    countState.InternalCount = 0;
    countState.CoplanarInternalCount = 0;
    countState.CoplanarPropertyCount = 0;
    countState.BridgingInternalCount = 0;
    RWMONITOR((" - "));

    for (nI = 0; nI < 256; nI++)
    {
        ImportMergeEdges(weldState->root[nI], weldState, &countState);
    }

    RWMONITOR(("%d == countState.ExternalCount",
               countState.ExternalCount));
    RWMONITOR(("%d == countState.InternalCount",
               countState.InternalCount));
    RWMONITOR(("%d == countState.CoplanarInternalCount",
               countState.CoplanarInternalCount));
    RWMONITOR(("%d == countState.CoplanarPropertyCount",
               countState.CoplanarPropertyCount));
    RWMONITOR(("%d == countState.BridgingInternalCount",
               countState.BridgingInternalCount));
    RWASSERT(0 == countState.CoplanarPropertyCount);
#endif /* (0 || defined(SECOND_MERGE_PASS)) */
    RWRETURN(weldState);
}

static RtWeldState *
ImportWingStructureTriangulate(RtWeldState * weldState)
{
    RtWorldImportBuildVertex *boundaries;
    RtWorldImportBuildSector *buildSector;
    RwInt32             numBoundaries;
    RwInt32             numPolygons;
    RwInt32             numFaces = 0;
    RwInt32             nI;
    RtSimplexState      simplexState;
    RwInt32             edgeMax = 0;

    RWFUNCTION(RWSTRING("ImportWingStructureTriangulate"));
    RWASSERT(NULL != weldState);
    buildSector = weldState->buildSector;
    RWASSERT(NULL != buildSector);
    simplexState.wpNoHS = weldState->wpNoHS;
    boundaries = buildSector->boundaries;
    numPolygons = 0;
    for (nI = 0; nI < buildSector->numPolygons; nI++)
    {
        RtWingPoly         *const WingPoly = &weldState->WingPoly[nI];
        RwInt32             edgeCount = WingPoly->edgeCount;

        VALIDATEWINGPOLY(WingPoly);
        if (edgeMax < edgeCount)
            edgeMax = edgeCount;
        if (edgeCount < 3)
        {
#if (1 && 0)
            RWMONITOR(("Skipping WingPoly %p edgeCount %d", WingPoly,
                       edgeCount));
#endif /* (1 && 0) */
            continue;
        }

        numFaces++;
        numPolygons += (edgeCount - 2);
    }

    RWMONITOR(("%d == numFaces; %d == Expected triangles",
               (int) numFaces, (int) numPolygons));
    numBoundaries = numPolygons * 4;
    simplexState.simplexBuffer =
        (RtSimplexVertex *) RwMalloc(sizeof(RtSimplexVertex) * edgeMax);
    RWASSERT(NULL != simplexState.simplexBuffer);
    boundaries =
        (RtWorldImportBuildVertex *)
        RwMalloc(sizeof(RtWorldImportBuildVertex) *
                 (numBoundaries + 1));
    RWASSERT(NULL != boundaries);
    boundaries[numBoundaries].mode.vpVert =
        ((RtWorldImportVertex *) 0xDEADBEEF);
    simplexState.boundaries = boundaries;
    simplexState.numPolygons = 0;
    simplexState.numBoundaries = 0;
    for (nI = 0; nI < buildSector->numPolygons; nI++)
    {
        RtWingPoly         *const WingPoly = &weldState->WingPoly[nI];
        RwInt32             edgeCount = WingPoly->edgeCount;

        VALIDATEWINGPOLY(WingPoly);
        if (edgeCount < 3)
        {
#if (0)
            RWMONITOR((" >> SHRUNK WingPoly %p edgeCount %d", WingPoly,
                       edgeCount));
#endif /* (0) */
            continue;
        }

#if (0)
        RWMONITOR(("WingPoly %p edgeCount %d", WingPoly, edgeCount));
#endif /* (0) */
        _rtImportWingPolyTriangulate(&simplexState, WingPoly);
    }

#if (0)
    RWMONITOR(("simplexState.numBoundaries %d numBoundaries %d",
               simplexState.numBoundaries, numBoundaries));
    RWMONITOR(("simplexState.numPolygons %d numPolygons %d",
               simplexState.numPolygons, numPolygons));
#endif /* (0) */
    RWASSERT(simplexState.numBoundaries <= numBoundaries);
    RWASSERT(simplexState.numPolygons <= numPolygons);
    /* blow away old boundaries */
    RwFree(buildSector->boundaries);
    /* install new merged boundaries */
    buildSector->boundaries = boundaries;
    RWASSERT((simplexState.numPolygons * 4) ==
             (simplexState.numBoundaries));
    buildSector->numBoundaries = simplexState.numBoundaries;
    buildSector->numPolygons = simplexState.numPolygons;
    RWASSERT(((RtWorldImportVertex *) 0xDEADBEEF)
             == boundaries[numBoundaries].mode.vpVert);
    RwFree(simplexState.simplexBuffer);
    RWRETURN(weldState);
}

void
_rtImportBuildSectorWeldPolygons(RtWorldImportBuildSector * buildSector,
                                 RtWorldImport * wpNoHS,
                                 RwInt32 weldCycles)
{
    RwInt32             nI;
    RwInt32             PreNumPolygons;
    RtWeldState         weldState;

    RWFUNCTION(RWSTRING("_rtImportBuildSectorWeldPolygons"));
    RWASSERT(NULL != buildSector);

#if (defined(_MSC_VER))
#if (_MSC_VER>=1000)
    {
        RwInt32             Weld_Off = 0;

        RWGETWINREGDWORD(Weld_Off, _T("Weld_Off"));
        RWMONITOR(("%d == Weld_Off", Weld_Off));
        if (Weld_Off)
            RWRETURNVOID();
    }
#endif /*  (_MSC_VER>=1000) */
#endif /* (defined(_MSC_VER)) */

    PreNumPolygons = buildSector->numPolygons;
    RWMONITOR(("%d == PreNumPolygons", (int) PreNumPolygons));

    for (nI = 0; nI < 256; nI++)
    {
        weldState.hash[nI].last = (RtWeldEdgePtr *) NULL;
        weldState.hash[nI].root = (RtWeldEdgePtr) NULL;
    }

    weldState.WeldFreeList =
        RwFreeListCreate(sizeof(RtWeldEdge), 20, 0);
    weldState.edgeDuplications = 0;
    weldState.WingPoly = (RtWingPoly *) NULL;
    weldState.buildSector = buildSector;
    weldState.wpNoHS = wpNoHS;
    weldState.cycles = weldCycles;
    ImportWingStructureCreate(&weldState);
    RWMONITOR(("%d Duplicate edges in duplicate sense",
               (int) weldState.edgeDuplications));
    ImportWingStructureMergePolygons(&weldState);
    ImportWingStructureRemoveSnips(&weldState);
    ImportWingStructureTriangulate(&weldState);
    ImportWingStructureDestroy(&weldState);
    if (NULL != weldState.WeldFreeList)
    {
        RwFreeListDestroy(weldState.WeldFreeList);
        weldState.WeldFreeList = (RwFreeList *) NULL;
    }
    RWMONITOR(("%d == Post buildSector->numPolygons",
               (int) buildSector->numPolygons));
    RWMONITOR(("%d == delta [%d%%]\n",
               (int) (PreNumPolygons - buildSector->numPolygons),
               PreNumPolygons
               ? ((100 *
                   (int) (PreNumPolygons -
                          buildSector->numPolygons)) /
                  (int) PreNumPolygons) : 0));

#if (defined(_MSC_VER) && defined (RWVERBOSE) && defined(RWDEBUG))
    {
        FILE               *fp;
        RwInt32             nI;

        fp = fopen("winghash.plt", "w");

        fprintf(fp, "set title \"Find Wing Edge Hash hits\"\n");
        fprintf(fp, "set xtics 0,32,256\n");
        fprintf(fp, "plot \"winghash.dat\" notitle with points, ");
        fprintf(fp, " \"winghash.dat\" title 'hash' with steps\n");
        fprintf(fp, "\n");
        fprintf(fp, "pause -1 \"Press return to continue\"\n");
        fprintf(fp, "reset\n");

        fclose(fp);

        fp = fopen("winghash.dat", "w");

        for (nI = 0; nI < 256; nI++)
        {
            fprintf(fp, " %3d %12d\n", nI, wing_hash_hit[nI]);
        }

        fclose(fp);
    }
#endif /* (defined(_MSC_VER) && defined (RWVERBOSE) && defined(RWDEBUG)) */

    RWRETURNVOID();
}

/* *INDENT-OFF* */

#if (0)

static RtWorldImportVertex *
ImportCoPlanarProperties(RtWorldImportVertex * vpVert, void *state)
{
    RtWingPoly         *WingPoly = (RtWingPoly *) state;
    RtDV4d             *bary;
    RtWorldImportVertex **base;
    RtDV4d              weight;
    RwDReal             texCoord;
    RwTexCoords         texCoords;
    RwTexCoords         texDelta;

    /* RwRGBA                  preLitCol;            */
    RWFUNCTION(RWSTRING("ImportCoPlanarProperties"));

    RWASSERT(NULL != WingPoly);

    bary = &WingPoly->bary[0];
    base = &WingPoly->base[0];

    _rtImportWorldBaryFromCart(weight, bary, &vpVert->OC);

    /*
     * assert that the point lines in the triangles plane ...
     */
    RWASSERT(WithinEpsilonOfZero(weight[3], _PLANE_EPSILON));

    texCoord = (weight[0] * base[0]->texCoords[0].u +
                weight[1] * base[1]->texCoords[0].u +
                weight[2] * base[2]->texCoords[0].u);

    texCoords.u = (RwReal) texCoord;
    texDelta.u = (RwReal) (vpVert->texCoords[0].u - texCoord);

#if (0 && (defined(RWDEBUG) && defined(RWVERBOSE)))
    RWMESSAGE(("%f == texDelta.u", texDelta.u));
#endif /* (0 && (defined(RWDEBUG) && defined(RWVERBOSE))) */

    if (!WithinEpsilonOfZero(texDelta.u, _TEX_EPSILON))
    {
#if (0 && (defined(RWDEBUG) && defined(RWVERBOSE)))
        RWMESSAGE(("U Texture coordinate not coplanar: %f == texDelta.u",
                   texDelta.u));
#endif /* (0 && defined(RWDEBUG) && defined(RWVERBOSE))) */

        vpVert = (RtWorldImportVertex *) NULL;
    }
    else
    {
        texCoord = (weight[0] * base[0]->texCoords[0].v +
                    weight[1] * base[1]->texCoords[0].v +
                    weight[2] * base[2]->texCoords[0].v);

        texCoords.v = (RwReal) texCoord;
        texDelta.v = (RwReal) (vpVert->texCoords[0].v - texCoord);

#if (0 && (defined(RWDEBUG) && defined(RWVERBOSE)))
        RWMESSAGE(("%f == texDelta.v", texDelta.v));
#endif /* (0 && ( defined(RWDEBUG) && defined(RWVERBOSE))) */

        if (!WithinEpsilonOfZero(texDelta.v, _TEX_EPSILON))
        {
#if (0 && (defined(RWDEBUG) && defined(RWVERBOSE)))
            RWMESSAGE(("V Texture coordinate not coplanar: %f == texDelta.v",
                       texDelta.v));
#endif /* (0 && ( defined(RWDEBUG) && defined(RWVERBOSE))) */
            vpVert = (RtWorldImportVertex *) NULL;
        }
    }

    RWRETURN(vpVert);
}

#endif /* (0) */

/* *INDENT-ON* */
