
/****************************************************************************
 *
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1998 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : Elevation.h
 *                                                                         
 * Purpose : Convertes elevationGrid nodes
 *                                                                      
 ****************************************************************************/

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

#include <string.h>

#include "rpplugin.h"
#include <rwcore.h>
#include <rtworld.h>

#include "elevation.h"
#include "material.h"
#include "builder.h"
#include "rpvrmlanim.h"
#include "rpvrml.h"
#include "texturetransform.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: elevation.c,v 1.25 2001/02/14 12:38:53 Markj Exp $";

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

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

typedef struct ElevationInfo ElevationInfo;
struct ElevationInfo
{
    sfbool              ccw;
    sfbool              solid;
    sfbool              colorPerVertex;
    sfbool              normalPerVertex;
    sffloat             creaseAngle;
    sffloat             xSpacing;
    sffloat             zSpacing;
    sfint32             xDimension;
    sfint32             zDimension;
    Field              *colors;
    Field              *texCoords;
    Field              *normals;
    Field              *heights;
};

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

   Elevation  methods

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

static ElevationInfo *
ElevationInfo_Create(void)
{
    ElevationInfo      *ei;

    RWFUNCTION(RWSTRING("ElevationInfo_Create"));

    ei = (ElevationInfo *) RwMalloc(sizeof(ElevationInfo));
    if (!ei)
    {
        RWRETURN((ElevationInfo *)NULL);
    }

    ei->ccw = FALSE;
    ei->solid = FALSE;
    ei->colorPerVertex = 0;
    ei->normalPerVertex = 0;
    ei->creaseAngle = 0.0f;
    ei->xSpacing = 0.0f;
    ei->zSpacing = 0.0f;
    ei->xDimension = 0;
    ei->zDimension = 0;
    ei->colors = (Field *)NULL;
    ei->texCoords = (Field *)NULL;
    ei->normals = (Field *)NULL;
    ei->heights = (Field *)NULL;

    RWRETURN(ei);
}

static              RwBool
ElevationInfo_Destroy(ElevationInfo * ei)
{
    RWFUNCTION(RWSTRING("ElevationInfo_Destroy"));
    RWASSERT(ei);

    if (ei)
    {
        RwFree(ei);

        RWRETURN(TRUE);
    }

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

static ElevationInfo *
ElevationInfo_Setup(ElevationInfo * ei, AbstractNode * an)
{
    RWFUNCTION(RWSTRING("ElevationInfo_Setup"));
    RWASSERT(ei);
    RWASSERT(an);

    if (ei && an)
    {
        AbstractNode       *anChild;
        AbstractField      *af;
        Field              *field;

        /* ccw */
        af = AbstractNode_GetAbstractField(an, "ccw");
        if (af)
        {
            field = AbstractField_GetField(af);
            ei->ccw = *FieldSfbool_GetValue(field);
        }

        /* solid */
        af = AbstractNode_GetAbstractField(an, "solid");
        if (af)
        {
            field = AbstractField_GetField(af);
            ei->solid = *FieldSfbool_GetValue(field);
        }

        /* colorPerVertex */
        af = AbstractNode_GetAbstractField(an, "colorPerVertex");
        if (af)
        {
            field = AbstractField_GetField(af);
            ei->colorPerVertex = *FieldSfbool_GetValue(field);
        }

        /* normalPerVertex */
        af = AbstractNode_GetAbstractField(an, "normalPerVertex");
        if (af)
        {
            field = AbstractField_GetField(af);
            ei->normalPerVertex = *FieldSfbool_GetValue(field);
        }

        /* creaseAngle */
        af = AbstractNode_GetAbstractField(an, "creaseAngle");
        if (af)
        {
            field = AbstractField_GetField(af);
            ei->creaseAngle = *FieldSffloat_GetValue(field);
        }

        /* xSpacing */
        af = AbstractNode_GetAbstractField(an, "xSpacing");
        if (af)
        {
            field = AbstractField_GetField(af);
            ei->xSpacing = *FieldSffloat_GetValue(field);
        }

        /* zSpacing */
        af = AbstractNode_GetAbstractField(an, "zSpacing");
        if (af)
        {
            field = AbstractField_GetField(af);
            ei->zSpacing = *FieldSffloat_GetValue(field);
        }

        /* xDimesion */
        af = AbstractNode_GetAbstractField(an, "xDimension");
        if (af)
        {
            field = AbstractField_GetField(af);
            ei->xDimension = *FieldSfint32_GetValue(field);
        }

        /* zDimension */
        af = AbstractNode_GetAbstractField(an, "zDimension");
        if (af)
        {
            field = AbstractField_GetField(af);
            ei->zDimension = *FieldSfint32_GetValue(field);
        }

        /* colors */
        anChild = AbstractNode_GetChildNode(an, "color");
        if (anChild)
        {
            af = AbstractNode_GetAbstractField(anChild, "Color");
            if (af)
            {
                field = AbstractField_GetField(af);
                ei->colors = field;
            }
        }

        /* texCoords */
        anChild = AbstractNode_GetChildNode(an, "texCoords");
        if (anChild)
        {
            af = AbstractNode_GetAbstractField(anChild,
                                               "TextureCoordinate");
            if (af)
            {
                field = AbstractField_GetField(af);
                ei->texCoords = field;
            }
        }

        /* normals */
        anChild = AbstractNode_GetChildNode(an, "normal");
        if (anChild)
        {
            af = AbstractNode_GetAbstractField(anChild, "Normal");
            if (af)
            {
                field = AbstractField_GetField(af);
                ei->normals = field;
            }
        }

        /* heights */
        af = AbstractNode_GetAbstractField(an, "height");
        if (af)
        {
            field = AbstractField_GetField(af);
            ei->heights = field;
        }

        RWRETURN(ei);
    }

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

static              RwBool
ElevationInfo_CheckValid(ElevationInfo * __RWUNUSED__ ei)
{
    RWFUNCTION(RWSTRING("ElevationInfo_CheckValid"));
    RWASSERT(ei);

    if (ei)
    {
        /* for now just return true */
        RWRETURN(TRUE);
    }

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

static RpGeometry  *
ElevationGrid_SetTriangles(RpGeometry * geometry, ElevationInfo * ei)
{
    RWFUNCTION(RWSTRING("ElevationGrid_SetTriangles"));
    RWASSERT(geometry);
    RWASSERT(ei);

    if (geometry && ei)
    {
        RpTriangle         *triangles;
        RpMaterial         *material;
        RwInt32             xDim, zDim;
        RwInt32             x, z;

        triangles = RpGeometryGetTriangles(geometry);
        material = Material_GetCurrent();

        xDim = ei->xDimension;
        zDim = ei->zDimension;

        for (z = 1; z < zDim; z++)
        {
            RwUInt16            baseInd;

            baseInd = (RwUInt16) ((z - 1) * xDim);

            for (x = 1; x < xDim; x++)
            {
                RwUInt16            ind1, ind2, ind3;

                /* upper triangle */
                ind1 = (RwUInt16) (baseInd + x - 1);
                ind2 = (RwUInt16) (ind1 + 1);
                ind3 = (RwUInt16) (ind2 + xDim);

                if (ei->ccw)
                {
                    RpGeometryTriangleSetVertexIndices(geometry,
                                                       triangles, ind3,
                                                       ind2, ind1);
                }
                else
                {
                    RpGeometryTriangleSetVertexIndices(geometry,
                                                       triangles, ind1,
                                                       ind2, ind3);
                }
                RpGeometryTriangleSetMaterial(geometry, triangles,
                                              material);

                /* next triangle */
                triangles++;

                /* lower triangle */
                ind2 = ind3;
                ind3 = (RwUInt16) (ind1 + xDim);

                if (ei->ccw)
                {
                    RpGeometryTriangleSetVertexIndices(geometry,
                                                       triangles, ind3,
                                                       ind2, ind1);
                }
                else
                {
                    RpGeometryTriangleSetVertexIndices(geometry,
                                                       triangles, ind1,
                                                       ind2, ind3);
                }
                RpGeometryTriangleSetMaterial(geometry, triangles,
                                              material);

                /* next triangle */
                triangles++;
            }
        }

        RWRETURN(geometry);
    }

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

static RpGeometry  *
ElevationGrid_SetKeyFrame(RpGeometry * geometry, ElevationInfo * ei)
{
    RWFUNCTION(RWSTRING("ElevationGrid_SetKeyFrame"));
    RWASSERT(geometry);
    RWASSERT(ei);

    if (geometry && ei)
    {
        RpMorphTarget      *morphTarget;
        RwV3d              *vertices;
        RwInt32             x, z, hCnt;
        RwInt32             xDim, zDim;
        RwReal              xInc, zInc, height;
        RwReal              scaleFactor;
        RwV3d               vert;

        /* get the one and only morph target */
        morphTarget = RpGeometryGetMorphTarget(geometry, 0);
        if (!morphTarget)
        {
            RWRETURN((RpGeometry *)NULL);
        }

        /* get the vertices */
        vertices = RpMorphTargetGetVertices(morphTarget);

        /* initialize the current vertex */
        vert.x = vert.y = vert.z = (RwReal) (0);

        /* setup the spacing values */
        scaleFactor = Build_GetScaleFactor();
        xInc = ei->xSpacing * scaleFactor;
        zInc = ei->zSpacing * scaleFactor;

        xDim = ei->xDimension;
        zDim = ei->zDimension;
        hCnt = 0;
        for (z = 0; z < zDim; z++)
        {
            vert.x = (RwReal) (0);
            for (x = 0; x < xDim; x++)
            {
                sffloat            *sfs;

                /* work out the height */
                sfs = FieldMffloat_GetValue(ei->heights, hCnt);
                height = (RwReal) (*sfs);
                height = ((height) * ((RwReal) (scaleFactor)));
                vert.y = height;

                *vertices = vert;

                vert.x = ((vert.x) + (xInc));
                hCnt++;
                vertices++;
            }

            vert.z = ((vert.z) + (zInc));
        }

        /* set the BoundingSphere */
        {
            RwSphere            bSphere;

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

        RWRETURN(geometry);
    }

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

static RwTexCoords *
ElevationGrid_SetDefualtTexCoords(RwTexCoords * texCoords,
                                  ElevationInfo * ei)
{
    RWFUNCTION(RWSTRING("ElevationGrid_SetDefualtTexCoords"));
    RWASSERT(texCoords);
    RWASSERT(ei);

    if (texCoords && ei)
    {
        RwInt32             x, z;
        RwInt32             xDim, zDim;
        RwReal              u, v;
        RwReal              uInc, vInc;
        RwTexCoords        *texCoord;

        texCoord = texCoords;

        xDim = ei->xDimension;
        zDim = ei->zDimension;

        uInc = (((RwReal) (1)) / ((RwReal) (xDim - 1)));
        vInc = (((RwReal) (1)) / ((RwReal) (zDim - 1)));

        v = (RwReal) (0);

        for (z = 0; z < zDim; z++)
        {
            u = (RwReal) (0);

            for (x = 0; x < xDim; x++)
            {
                sfvec2f             texTrans;

                /* apply any texture transformation */
                texTrans.x = u;
                texTrans.y = v;
                TextureTransform_Apply(&texTrans, &texTrans);

                /* set the values */
                texCoord->u = (RwReal) (texTrans.x);
                texCoord->v = (RwReal) (texTrans.y + 1.0f);

                /* add the u inc */
                u = ((u) + (uInc));

                /* next texture */
                texCoord++;
            }

            /* add the v inc */
            v = ((v) + (vInc));
        }

        RWRETURN(texCoords);
    }

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

static RpGeometry  *
ElevationGrid_SetVertexInfo(RpGeometry * geometry, ElevationInfo * ei)
{
    RWFUNCTION(RWSTRING("ElevationGrid_SetVertexInfo"));
    RWASSERT(geometry);
    RWASSERT(ei);

    if (geometry && ei)
    {
        RwInt32             flags;

        flags = RpGeometryGetFlags(geometry);
        if (flags & rpGEOMETRYTEXTURED)
        {
            RwTexCoords        *texCoords;

            texCoords = RpGeometryGetVertexTexCoords(geometry, rwTEXTURECOORDINATEINDEX0);

            if (ei->texCoords)
            {
            }
            else
            {
                ElevationGrid_SetDefualtTexCoords(texCoords, ei);
            }
        }

        RWRETURN(geometry);
    }

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

static RpGeometry  *
ElevationGrid_Geometry(ElevationInfo * ei)
{
    RWFUNCTION(RWSTRING("ElevationGrid_Geometry"));
    RWASSERT(ei);

    if (ei)
    {
        RpGeometry         *geometry;
        RwInt32             flags;
        RwInt32             numVertices, numTriangles;

        numVertices = ei->xDimension * ei->zDimension;
        numTriangles = (ei->xDimension - 1) * (ei->zDimension - 1) * 2;

        flags =
            rpGEOMETRYLIGHT | rpGEOMETRYNORMALS |
            rpGEOMETRYMODULATEMATERIALCOLOR;
        if (Material_IsCurrentTextured())
        {
            flags |= rpGEOMETRYTEXTURED;
        }

        geometry = RpGeometryCreate(numVertices, numTriangles, flags);
        if (!geometry)
        {
            RWRETURN((RpGeometry *)NULL);
        }

        if (RpGeometryLock(geometry, rpGEOMETRYLOCKALL))
        {
            ElevationGrid_SetTriangles(geometry, ei);
            ElevationGrid_SetKeyFrame(geometry, ei);
            ElevationGrid_SetVertexInfo(geometry, ei);
            if (!ei->normals)
            {
                RtGeometryCalculateVertexNormals(geometry);
            }
            RpGeometryUnlock(geometry);
        }

        RWRETURN(geometry);
    }

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

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

    if (an)
    {
        RwChar             *nodeName;
        ElevationInfo      *ei;

        nodeName = AbstractNode_GetBaseName(an);
        /*RWASSERT(rwstrcmp(nodeName, "ElevationGrid") == 0); */

        ei = ElevationInfo_Create();
        if (!ei)
        {
            RWRETURN(FALSE);
        }

        if (!ElevationInfo_Setup(ei, an))
        {
            ElevationInfo_Destroy(ei);

            RWRETURN(FALSE);
        }

        if (!ElevationInfo_CheckValid(ei))
        {
            ElevationInfo_Destroy(ei);

            RWRETURN(FALSE);
        }

        if (Build_GetType() == CLUMP)
        {
            RpGeometry         *geometry;

            geometry = ElevationGrid_Geometry(ei);
            if (!geometry)
            {
                ElevationInfo_Destroy(ei);

                RWRETURN(FALSE);
            }

            if (!Build_AddGeometry(geometry))
            {
                ElevationInfo_Destroy(ei);

                RWRETURN(FALSE);
            }
        }
        else                   /* WORLD */
        {

        }

        ElevationInfo_Destroy(ei);

#if (0)
        RWERROR((E_RP_VRML_ELEVATIONGRID, an->lineNum));
#endif /* (0) */

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}
