/****************************************************************************
 *                                                                          *
 *  Module  :   patchop .c                                                  *
 *                                                                          *
 *  Purpose :   Misc patch operations.                                      *
 *                                                                          *
 ****************************************************************************/

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

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

/* RW includes */
#include "rpplugin.h"
#include <rpdbgerr.h>
#include <rwcore.h>
#include <rpworld.h>
#include <rtbezpat.h>

#include "rppatch310.h"

#include "patchvar.h"
#include "patchgen.h"
#include "patchop.h"

#if (!defined(DOXYGEN))
static const char   rcsid[] __RWUNUSED__ =
    "@@@@(#)$Id: patchop.c,v 1.5 2001/09/24 12:39:23 johns Exp $";
#endif /* (!defined(DOXYGEN)) */

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

static RpPatchIdxTable *
PatchIdxTableCreate(RwInt32 size)
{
    RpPatchIdxTable    *idxTable;
    RwInt32             i;

    RWFUNCTION(RWSTRING("PatchIdxTableCreate"));

    RWASSERT(size > 0);

    idxTable =
        (RpPatchIdxTable *) RwMalloc(size * sizeof(RpPatchIdxTable));

    if (idxTable != NULL)
    {
        /* Init the idx table. */
        RWASSERT(idxTable != NULL);

        for (i = 0; i < size; i++)
        {
            idxTable[i].count = 0;
            idxTable[i].size = 0;
            idxTable[i].idx = (RxVertexIndex *) NULL;
        }
    }

    RWRETURN(idxTable);
}

static              RwBool
PatchIdxTableDestroy(RpPatchIdxTable * idxTable, RwInt32 size)
{
    RwInt32             i;

    RWFUNCTION(RWSTRING("PatchIdxTableDestroy"));

    RWASSERT(size > 0);
    RWASSERT(idxTable != NULL);

    for (i = 0; i < size; i++)
    {
        if (idxTable[i].idx != NULL)
            RwFree(idxTable[i].idx);

        idxTable[i].idx = (RxVertexIndex *) NULL;
        idxTable[i].count = 0;
        idxTable[i].size = 0;
    }

    RwFree(idxTable);

    RWRETURN(TRUE);
}

static RpPatchIdxTable *
PatchIdxTableAddIdx(RpPatchIdxTable * idxTable, RwInt32 i, RwInt32 j)
{
    RpPatchIdxTable    *result;
    RwInt32             newSize;
    RxVertexIndex      *newIdx, *oldIdx;

    RWFUNCTION(RWSTRING("PatchIdxTableAddIdx"));

    RWASSERT(idxTable != NULL);

    result = idxTable;

    if (idxTable[i].count == idxTable[i].size)
    {
        oldIdx = idxTable[i].idx;

        newSize = idxTable[i].size + RPPATCHIDXTABLESIZE;
        newIdx =
            (RxVertexIndex *) RwMalloc(newSize * sizeof(RxVertexIndex));

        if (newIdx != NULL)
        {
            if (oldIdx != NULL)
            {
                memcpy(newIdx, oldIdx,
                       idxTable[i].count * sizeof(RxVertexIndex));

                RwFree(oldIdx);
            }

            idxTable[i].size = newSize;
            idxTable[i].idx = newIdx;
        }
        else
        {
            result = (RpPatchIdxTable *) NULL;
        }
    }

    if (result != NULL)
    {
        idxTable[i].idx[idxTable[i].count] = (RxVertexIndex) j;
        idxTable[i].count++;
    }

    RWRETURN(result);
}

static RpPatch     *
PatchBuildVertIdxTable(RpPatch * patch, RpPatchIdxTable * idxTable)
{
    RwInt32             i, j, numVerts;
    RwV3d              *pos1, *pos2;
    RwReal              vertPosTol, dx, dy, dz;

    RWFUNCTION(RWSTRING("PatchBuildVertIdxTable"));

    RWASSERT(patch != NULL);
    RWASSERT(idxTable != NULL);

    vertPosTol = rpPatchGlobals.vertPosTol;

    pos1 = patch->cpt;
    numVerts = patch->numControlPoints;

    for (i = 0; i < numVerts; i++)
    {
        pos2 = pos1 + 1;

        for (j = (i + 1); j < numVerts; j++)
        {
            dx = pos1->x - pos2->x;
            dy = pos1->y - pos2->y;
            dz = pos1->z - pos2->z;

            if (((dx >= -vertPosTol) && (dx <= vertPosTol)) &&
                ((dy >= -vertPosTol) && (dy <= vertPosTol)) &&
                ((dz >= -vertPosTol) && (dz <= vertPosTol)))
            {
                PatchIdxTableAddIdx(idxTable, i, j);
                PatchIdxTableAddIdx(idxTable, j, i);
            }

            pos2++;
        }

        pos1++;
    }

    RWRETURN(patch);
}

static RpPatch     *
PatchIdxTableSmoothNormal(RpPatch * patch, RpPatchIdxTable * idxTable,
                          RwV3d * smnrm)
{
    RwInt32             i, j, numVerts, count;
    RwV3d               avgnrm, *nrm;
    RwReal              l, dot, vertNrmTol;
    RxVertexIndex      *idx;

    RWFUNCTION(RWSTRING("PatchIdxTableSmoothNormal"));

    RWASSERT(patch != NULL);
    RWASSERT(idxTable != NULL);

    vertNrmTol = rpPatchGlobals.vertNrmTol;

    nrm = patch->nrm;
    numVerts = patch->numControlPoints;

    for (i = 0; i < numVerts; i++)
    {
        smnrm[i].x = nrm[i].x;
        smnrm[i].y = nrm[i].y;
        smnrm[i].z = nrm[i].z;

        count = idxTable[i].count;

        if (count > 0)
        {
            avgnrm.x = nrm[i].x;
            avgnrm.y = nrm[i].y;
            avgnrm.z = nrm[i].z;

            idx = idxTable[i].idx;

            for (j = 0; j < count; j++)
            {
                dot = RwV3dDotProduct(&nrm[i], &nrm[*idx]);

                if (dot >= rpPatchGlobals.vertNrmTol)
                {
                    avgnrm.x += nrm[*idx].x;
                    avgnrm.y += nrm[*idx].y;
                    avgnrm.z += nrm[*idx].z;
                }

                idx++;
            }

            l = RwV3dLength(&avgnrm);

            if (l > (RwReal) 0.0)
                RwV3dScale(&smnrm[i], &avgnrm, ((RwReal) 1.0 / l));
        }
    }

    RWRETURN(patch);
}

/*
 *
 *
 *
 */
RpPatch            *
_rpPatchSmoothNormal(RpPatch * patch, RwV3d * smnrm)
{
    RpPatch            *result;
    RwInt32             numVerts;
    RpPatchIdxTable    *idxTable;

    RWFUNCTION(RWSTRING("_rpPatchSmoothNormal"));

    RWASSERT(patch != NULL);

    result = patch;

    /* First build an index array of co-incident points. */
    numVerts = patch->numControlPoints;

    idxTable = PatchIdxTableCreate(numVerts);

    if (idxTable != NULL)
    {
        /* Init the idx table. */
        RWASSERT(idxTable != NULL);

        /* Collect co-incident points. */
        PatchBuildVertIdxTable(patch, idxTable);

        /* Smooth the normals. */
        PatchIdxTableSmoothNormal(patch, idxTable, smnrm);
    }

    if (idxTable != NULL)
    {
        PatchIdxTableDestroy(idxTable, numVerts);
    }

    RWRETURN(result);
}
