/*
 *
 * VRML to RW converter plug-in
 */
/****************************************************************************
 *                                                                         
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1997 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : Primitives.c
 *                                                                         
 * Purpose : Convertes Vrml's geometry primitives nodes.
 *          Currnetly supports cone, box, sphere & cylinder
 *                                                                      
 ****************************************************************************/

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

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

#include "rpplugin.h"
#include "primitives.h"
#include "builder.h"
#include "appearance.h"
#include "material.h"
#include "rpvrmlanim.h"
#include "rpvrml.h"
#include "converter.h"

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

extern float        GscaleFactor;

static RwV3d        GboxVertices[8] = {
    {-1.0f, 1.0f, -1.0f},      /* 0 */
    {1.0f, 1.0f, -1.0f},       /* 1 */
    {-1.0f, -1.0f, -1.0f},     /* 2 */
    {1.0f, -1.0f, -1.0f},      /* 3 */
    {-1.0f, 1.0f, 1.0f},       /* 4 */
    {1.0f, 1.0f, 1.0f},        /* 5 */
    {-1.0f, -1.0f, 1.0f},      /* 6 */
    {1.0f, -1.0f, 1.0f}        /* 7 */
};

static RwV3d        GboxNormals[6] = {
    {0.0f, 1.0f, 0.0f},        /* top */
    {0.0f, -1.0f, 0.0f},       /* bottom */
    {0.0f, 0.0f, -1.0f},       /* front */
    {0.0f, 0.0f, 1.0f},        /* back */
    {-1.0f, 0.0f, 0.0f},       /* left */
    {1.0f, 0.0f, 0.0f}         /* right */
};

static RwTexCoords  GboxTexCoords[4] = {
    {0.0f, 0.0f},
    {0.0f, 1.0f},
    {1.0f, 1.0f},
    {1.0f, 0.0f}
};

static RwUInt16     GboxIndices[6][4] = {
    {4, 0, 1, 5},              /* top */
    {2, 6, 7, 3},              /* bottom */
    {0, 2, 3, 1},              /* front */
    {6, 4, 5, 7},              /* back */
    {6, 2, 0, 4},              /* left */
    {5, 1, 3, 7}               /* right */
};

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

/***** cone *****/

/* clump */
static RpGeometry  *Cone_Geometry(RwReal height, RwReal radius,
                                  RwBool bottom, RwBool side);
static RwBool       Cone_SetVertexInfo(RpGeometry * geometry,
                                       RwBool bottom, RwBool side);
static RwBool       Cone_SetTriangles(RpGeometry * geometry,
                                      RpTriangle * triangles,
                                      RwBool bottom, RwBool side);
static RwBool       Cone_SetMorphTarget(RpGeometry * geometry,
                                        RwReal height, RwReal radius,
                                        RwBool bottom, RwBool side);

/* world */
static RwBool       Cone_SetWorldVertices(RwReal height, RwReal radius,
                                          RwBool bottom, RwBool side);
static RwBool       Cone_SetWorldFaces(RwBool bottom, RwBool side);

/***** box *****/

/* clump */
static RpGeometry  *Box_Geometry(sfvec3f * box);
static RwBool       Box_SetTriangles(RpGeometry * geometry,
                                     RpTriangle * triangles);
static RwBool       Box_SetMorphTarget(RpGeometry * geometry,
                                       sfvec3f * box);
static RwBool       Box_SetVertexInfo(RpGeometry * geometry);

/* world */
static RwBool       Box_SetWorldVertices(sfvec3f * box);
static RwBool       Box_SetWorldFaces(void);

/***** sphere *****/

/* clump */
static RpGeometry  *Sphere_Geometry(RwReal radius);
static RwBool       Sphere_SetTriangles(RpGeometry * geometry,
                                        RpTriangle * triangles);
static RwBool       Sphere_SetMorphTarget(RpGeometry * geometry,
                                          RwReal radius);
static RwBool       Sphere_SetVertexInfo(RpGeometry * geometry);

/* world */
static RwBool       Sphere_SetWorldVertices(RwReal radius);
static RwBool       Sphere_SetWorldFaces(void);

/***** cylinder *****/

/* clump */
static RpGeometry  *Cylinder_Geometry(RwReal height, RwReal radius,
                                      RwBool top, RwBool bottom,
                                      RwBool side);
static RwBool       Cylinder_SetMorphTarget(RpGeometry * geometry,
                                            RwReal height,
                                            RwReal radius, RwBool top,
                                            RwBool bottom, RwBool side);
static RwBool       Cylinder_SetTriangles(RpGeometry * geometry,
                                          RpTriangle * triangles,
                                          RwBool top, RwBool bottom,
                                          RwBool side);
static RwBool       Cylinder_SetVertexInfo(RpGeometry * geometry,
                                           RwBool top, RwBool bottom,
                                           RwBool side);

/* world */
static RwBool       Cylinder_SetWorldVertices(RwReal height,
                                              RwReal radius, RwBool top,
                                              RwBool bottom,
                                              RwBool side);
static RwBool       Cylinder_SetWorldFaces(RwBool top, RwBool bottom,
                                           RwBool side);

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

/* number of segments in a primitve */

/* must be an even number greater than 2 */
#define SEGMENTS (32)

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

   Cone methods

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

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

    if (an)
    {
        RpGeometry         *geometry;
        AbstractField      *af;
        sffloat             radius = (sffloat) 0;
        sffloat             height = (sffloat) 0;
        sfbool              bottom = (sfbool) 0;
        sfbool              side = (sfbool) 0;
        RwChar             *nodeName;

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

        /* get radius */
        if ((af =
             AbstractNode_GetAbstractField(an,
                                           RWSTRING("bottomRadius"))))
        {
            Field              *field;

            field = AbstractField_GetField(af);
            radius = *FieldSffloat_GetValue(field);
        }

        /* get height */
        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("height"))))
        {
            Field              *field;

            field = AbstractField_GetField(af);
            height = *FieldSffloat_GetValue(field);
        }

        /* get bottom */
        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("bottom"))))
        {
            Field              *field;

            field = AbstractField_GetField(af);
            bottom = *FieldSfbool_GetValue(field);
        }

        /* get side */
        if ((af = AbstractNode_GetAbstractField(an, RWSTRING("side"))))
        {
            Field              *field;

            field = AbstractField_GetField(af);
            side = *FieldSfbool_GetValue(field);
        }

        if (Build_GetType() == CLUMP)
        {
            geometry = Cone_Geometry(height, radius, bottom, side);
            if (!Build_AddGeometry(geometry))
            {
                RWRETURN(FALSE);
            }
        }
        else                   /* WORLD */
        {
            RwInt32             flags;

            flags = rpWORLDNORMALS | rpWORLDLIGHT;
            if (Material_IsCurrentTextured())
            {
                flags |= rpWORLDTEXTURED;
            }
            Build_WorldAccumulateFlags(flags);

            if (!Cone_SetWorldVertices(height, radius, bottom, side))
            {
                RWRETURN(FALSE);
            }
            if (!Cone_SetWorldFaces(bottom, side))
            {
                RWRETURN(FALSE);
            }
        }

        RWRETURN(TRUE);
    }

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

static RpGeometry  *
Cone_Geometry(RwReal height, RwReal radius, RwBool bottom, RwBool side)
{
    RpGeometry         *geometry = (RpGeometry *)NULL;
    RwInt32             numVertices = 0;
    RwInt32             numFaces = 0;
    RwInt32             flags =
        rpGEOMETRYLIGHT | rpGEOMETRYNORMALS |
        rpGEOMETRYMODULATEMATERIALCOLOR;

    RWFUNCTION(RWSTRING("Cone_Geometry"));

    if (Material_IsCurrentTextured())
    {
        flags |= rpGEOMETRYTEXTURED;
    }

    if (side)
    {
        numVertices += 2 * (SEGMENTS + 1);
        numFaces += SEGMENTS;
    }
    if (bottom)
    {
        numVertices += SEGMENTS + 2;
        numFaces += SEGMENTS;
    }

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

    RpGeometryLock(geometry, rpGEOMETRYLOCKALL);
    Cone_SetTriangles(geometry, RpGeometryGetTriangles(geometry),
                      bottom, side);
    Cone_SetMorphTarget(geometry, height, radius, bottom, side);
    Cone_SetVertexInfo(geometry, bottom, side);
    RpGeometryUnlock(geometry);

    RWRETURN(geometry);
}

static              RwBool
Cone_SetVertexInfo(RpGeometry * geometry, RwBool bottom, RwBool side)
{
    RWFUNCTION(RWSTRING("Cone_SetVertexInfo"));
    RWASSERT(geometry);

    if (geometry)
    {
        RwTexCoords        *vertexTexCoords =
            RpGeometryGetVertexTexCoords(geometry, rwTEXTURECOORDINATEINDEX0);

        if (vertexTexCoords)
        {
            RwInt32             segNum;
            RwReal              u, v, uInc;
            RwReal              angle, angleInc;

            u = (RwReal) (0);
            v = (RwReal) (0);
            uInc = (RwReal) ((RwReal) (1) / SEGMENTS);
            angle = (RwReal) (0);
            angleInc = (RwReal) ((2 * rwPI) / SEGMENTS);

            if (side)
            {
                u = (RwReal) (0);
                for (segNum = 0; segNum <= SEGMENTS; segNum++)
                {
                    vertexTexCoords->u = u;
                    vertexTexCoords->v = (RwReal) (0);
                    u += uInc;
                    vertexTexCoords++;
                }

                u = (RwReal) (0);
                for (segNum = 0; segNum <= SEGMENTS; segNum++)
                {
                    vertexTexCoords->u = (RwReal) (u);
                    vertexTexCoords->v = (RwReal) (1);
                    u += uInc;
                    vertexTexCoords++;
                }
            }

            if (bottom)
            {
                angle = (RwReal) (0);
                vertexTexCoords->u = (RwReal) (0.5);
                vertexTexCoords->v = (RwReal) (0.5);
                vertexTexCoords++;
                for (segNum = 0; segNum <= SEGMENTS; segNum++)
                {
                    u = (RwReal) ((1 + RwCos(angle)) / 2);
                    v = (RwReal) ((1 + RwSin(angle)) / 2);
                    vertexTexCoords->u = u;
                    vertexTexCoords->v = v;
                    vertexTexCoords++;
                    angle += angleInc;
                }
            }
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
Cone_SetTriangles(RpGeometry * geometry, RpTriangle * triangles,
                  RwBool bottom, RwBool side)
{
    RWFUNCTION(RWSTRING("Cone_SetTriangles"));
    RWASSERT(geometry);
    RWASSERT(triangles);

    if (geometry && triangles)
    {
        RpMaterial         *material;
        RwInt32             segNum;
        RwInt32             ind1, ind2, ind3;

        material = Material_GetCurrent();
        ind1 = ind2 = ind3 = -1;

        /* cone side */
        if (side)
        {
            ind1 = SEGMENTS;
            ind2 = -1;
            for (segNum = 0; segNum < SEGMENTS; segNum++)
            {
                ind1++;
                ind2++;
                ind3 = ind1 + 1;
                RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                                   (RwUInt16) ind1,
                                                   (RwUInt16) ind2,
                                                   (RwUInt16) ind3);
                RpGeometryTriangleSetMaterial(geometry, triangles,
                                              material);
                triangles++;
            }
        }

        /* cone bottom */
        if (bottom)
        {
            ind1 = ind3 + 1;
            ind2 = ind1;
            for (segNum = 0; segNum < SEGMENTS; segNum++)
            {
                ind1++;
                ind3 = ind1 + 1;
                RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                                   (RwUInt16) ind3,
                                                   (RwUInt16) ind2,
                                                   (RwUInt16) ind1);
                RpGeometryTriangleSetMaterial(geometry, triangles,
                                              material);
                triangles++;
            }
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
Cone_SetMorphTarget(RpGeometry * geometry, RwReal height, RwReal radius,
                    RwBool bottom, RwBool side)
{
    RWFUNCTION(RWSTRING("Cone_SetMorphTarget"));
    RWASSERT(geometry);

    if (geometry)
    {
        RpMorphTarget      *morphTarget;
        RwV3d              *vertices;
        RwV3d              *normals;
        RwInt32             segNum, vertNum = 0;
        RwReal              angle, angleInc;
        RwSphere            bSphere;

        angle = (RwReal) (0);
        angleInc = (RwReal) ((2 * rwPI) / SEGMENTS);
        morphTarget = RpGeometryGetMorphTarget(geometry, 0);
        vertices = RpMorphTargetGetVertices(morphTarget);
        normals = RpMorphTargetGetVertexNormals(morphTarget);

        radius *= GscaleFactor;
        height *= GscaleFactor;

        if (side)
        {
            RwReal              elevAng =
                (RwReal) RwATan(height / radius);
            RwReal              minRadius = (RwReal) RwCos(elevAng);

            angle = 0.0f;
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                vertices[vertNum].x = (RwReal) (0);
                vertices[vertNum].y = (RwReal) (height / 2);
                vertices[vertNum].z = (RwReal) (0);
                if (normals)
                {
                    normals[vertNum].x =
                        (RwReal) (minRadius * RwCos(angle));
                    normals[vertNum].y = (RwReal) (RwSin(elevAng));
                    normals[vertNum].z =
                        (RwReal) (minRadius * RwSin(angle));
                }
                vertNum++;
                angle += angleInc;
            }
            angle = 0.0f;
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                vertices[vertNum].x = (RwReal) (radius * RwCos(angle));
                vertices[vertNum].y = (RwReal) (-height / 2);
                vertices[vertNum].z = (RwReal) (radius * RwSin(angle));
                if (normals)
                {
                    normals[vertNum].x =
                        (RwReal) (minRadius * RwCos(angle));
                    normals[vertNum].y = (RwReal) (RwSin(elevAng));
                    normals[vertNum].z =
                        (RwReal) (minRadius * RwSin(angle));
                }
                vertNum++;
                angle += angleInc;
            }
        }

        if (bottom)
        {
            vertices[vertNum].x = 0.0f;
            vertices[vertNum].y = (RwReal) (-height / 2);
            vertices[vertNum].z = 0.0f;
            if (normals)
            {
                normals[vertNum].x = 0.0f;
                normals[vertNum].y = -1.0f;
                normals[vertNum].z = 0.0f;
            }
            vertNum++;
            angle = 0.0f;
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                vertices[vertNum].x = (RwReal) (radius * RwCos(angle));
                vertices[vertNum].y = (RwReal) ((-height / 2));
                vertices[vertNum].z = (RwReal) (radius * RwSin(angle));
                if (normals)
                {
                    normals[vertNum].x = 0.0f;
                    normals[vertNum].y = -1.0f;
                    normals[vertNum].z = 0.0f;
                }
                vertNum++;
                angle += angleInc;
            }
        }

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

        RWRETURN(TRUE);
    }

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

static              RwBool
Cone_SetWorldVertices(RwReal height, RwReal radius, RwBool bottom,
                      RwBool side)
{
    RtWorldImportVertex *vertices;
    RwInt32             numVertices = 0;

    RWFUNCTION(RWSTRING("Cone_SetWorldVertices"));

    if (side)
    {
        numVertices += 2 * (SEGMENTS + 1);
    }
    if (bottom)
    {
        numVertices += SEGMENTS + 2;
    }

    radius *= GscaleFactor;
    height *= GscaleFactor;

    /* allocate additional world vertices */
    vertices = Build_WorldAddMoreVertices(numVertices);
    if (vertices)
    {
        RwReal              angle, angleInc;
        RwInt32             segNum, vertNum;
        RwMatrix           *currentLTM;
        RwReal              u, v, uInc;

        angle = (RwReal) (0);
        angleInc = (RwReal) ((2 * rwPI) / SEGMENTS);

        currentLTM = Build_GetCurrentLTM();

        uInc = (RwReal) ((RwReal) (1) / SEGMENTS);
        vertNum = 0;

        if (side)
        {

            RwReal              elevAng;
            RwReal              minRadius;

            angle = (RwReal) (0);
            elevAng = (RwReal) RwATan(height / radius);
            minRadius = (RwReal) RwCos(elevAng);
            u = (RwReal) (0);
            v = (RwReal) (0);

            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                /* vertices */
                vertices[vertNum].OC.x = (RwReal) (0);
                vertices[vertNum].OC.y = (RwReal) (height / 2);
                vertices[vertNum].OC.z = (RwReal) (0);
                RwV3dTransformPoints(&vertices[vertNum].OC,
                                     &vertices[vertNum].OC, 1,
                                     currentLTM);

                /* normals */
                vertices[vertNum].normal.x =
                    (RwReal) (minRadius * RwCos(angle));
                vertices[vertNum].normal.y = (RwReal) (RwSin(elevAng));
                vertices[vertNum].normal.z =
                    (RwReal) (minRadius * RwSin(angle));
                RwV3dTransformVectors(&vertices[vertNum].normal,
                                      &vertices[vertNum].normal, 1,
                                      currentLTM);

                /* textures */
                vertices[vertNum].texCoords[0].u = u;
                vertices[vertNum].texCoords[0].v = (RwReal) (0);

                vertNum++;
                angle += angleInc;
                u += uInc;
            }

            angle = (RwReal) (0);
            u = (RwReal) (0);

            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                /* vertices */
                vertices[vertNum].OC.x =
                    (RwReal) (radius * RwCos(angle));
                vertices[vertNum].OC.y = (RwReal) (-height / 2);
                vertices[vertNum].OC.z =
                    (RwReal) (radius * RwSin(angle));
                RwV3dTransformPoints(&vertices[vertNum].OC,
                                     &vertices[vertNum].OC, 1,
                                     currentLTM);

                /* normals */
                vertices[vertNum].normal.x =
                    (RwReal) (minRadius * RwCos(angle));
                vertices[vertNum].normal.y = (RwReal) (RwSin(elevAng));
                vertices[vertNum].normal.z =
                    (RwReal) (minRadius * RwSin(angle));
                RwV3dTransformVectors(&vertices[vertNum].normal,
                                      &vertices[vertNum].normal, 1,
                                      currentLTM);

                /* textures */
                vertices[vertNum].texCoords[0].u = u;
                vertices[vertNum].texCoords[0].v = (RwReal) (1);

                vertNum++;
                angle += angleInc;
                u += uInc;
            }
        }

        if (bottom)
        {
            angle = (RwReal) (0);

            /* vertices */
            vertices[vertNum].OC.x = (RwReal) (0);
            vertices[vertNum].OC.y = (RwReal) (-height / 2);
            vertices[vertNum].OC.z = (RwReal) (0);
            RwV3dTransformPoints(&vertices[vertNum].OC,
                                 &vertices[vertNum].OC, 1, currentLTM);

            /* normals */
            vertices[vertNum].normal.x = (RwReal) (0);
            vertices[vertNum].normal.y = (RwReal) (-1);
            vertices[vertNum].normal.z = (RwReal) (0);
            RwV3dTransformVectors(&vertices[vertNum].normal,
                                  &vertices[vertNum].normal, 1,
                                  currentLTM);

            /* textures */
            vertices[vertNum].texCoords[0].u = (RwReal) (0.5f);
            vertices[vertNum].texCoords[0].v = (RwReal) (0.5f);

            vertNum++;
            angle = (RwReal) (0);
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                /* vertices */
                vertices[vertNum].OC.x =
                    (RwReal) (radius * RwCos(angle));
                vertices[vertNum].OC.y = (RwReal) ((-height / 2));
                vertices[vertNum].OC.z =
                    (RwReal) (radius * RwSin(angle));
                RwV3dTransformPoints(&vertices[vertNum].OC,
                                     &vertices[vertNum].OC, 1,
                                     currentLTM);

                /* normals */
                vertices[vertNum].normal.x = 0.0f;
                vertices[vertNum].normal.y = -1.0f;
                vertices[vertNum].normal.z = 0.0f;
                RwV3dTransformVectors(&vertices[vertNum].normal,
                                      &vertices[vertNum].normal, 1,
                                      currentLTM);

                /* textures */
                u = (RwReal) ((1 + RwCos(angle)) / 2);
                v = (RwReal) ((1 + RwSin(angle)) / 2);
                vertices[vertNum].texCoords[0].u = u;
                vertices[vertNum].texCoords[0].v = v;

                vertNum++;
                angle += angleInc;
            }
        }

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

static              RwBool
Cone_SetWorldFaces(RwBool bottom, RwBool side)
{
    RtWorldImportTriangle *triangles;
    RwInt32             numFaces = 0;

    RWFUNCTION(RWSTRING("Cone_SetWorldFaces"));

    if (side)
    {
        numFaces += SEGMENTS;
    }
    if (bottom)
    {
        numFaces += SEGMENTS;
    }

    /* allocate additional world triangles */
    triangles = Build_WorldAddMoreTriangles(numFaces);
    if (triangles)
    {
        RwInt32             matInd;
        RwInt32             ind1, ind2, ind3;
        RwInt32             segNum;
        RwInt32             vertexIndOffset;

        vertexIndOffset = Build_WorldGetVertexIndOffset();

        /* add current material to world & get its index */
        matInd = Material_GetCurrentIndex();
        ind1 = ind2 = ind3 = -1;

        /* cone side */
        if (side)
        {
            ind1 = SEGMENTS;
            ind2 = -1;

            for (segNum = 0; segNum < SEGMENTS; segNum++)
            {
                ind1++;
                ind2++;
                ind3 = ind1 + 1;

                triangles->vertIndex[0] = ind1 + vertexIndOffset;
                triangles->vertIndex[1] = ind2 + vertexIndOffset;
                triangles->vertIndex[2] = ind3 + vertexIndOffset;
                triangles->matIndex = matInd;
                triangles++;
            }
        }

        /* cone bottom */
        if (bottom)
        {
            ind1 = ind3 + 1;
            ind2 = ind1;

            for (segNum = 0; segNum < SEGMENTS; segNum++)
            {
                ind1++;
                ind3 = ind1 + 1;
                triangles->vertIndex[2] = ind1 + vertexIndOffset;
                triangles->vertIndex[1] = ind2 + vertexIndOffset;
                triangles->vertIndex[0] = ind3 + vertexIndOffset;
                triangles->matIndex = matInd;
                triangles++;
            }
        }

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

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

   Box methods

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

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

    if (an)
    {
        RpGeometry         *geometry;
        AbstractField      *af;
        sfvec3f             box;
        RwChar             *nodeName;

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

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

            field = AbstractField_GetField(af);
            box = *FieldSfvec3f_GetValue(field);
        }

        if (Build_GetType() == CLUMP)
        {
            geometry = Box_Geometry(&box);
            if (!Build_AddGeometry(geometry))
            {
                RWRETURN(FALSE);
            }
        }
        else                   /* WORLD */
        {
            RwInt32             flags;

            flags = rpWORLDNORMALS | rpWORLDLIGHT;
            if (Material_IsCurrentTextured())
            {
                flags |= rpWORLDTEXTURED;
            }
            Build_WorldAccumulateFlags(flags);

            if (!Box_SetWorldVertices(&box))
            {
                RWRETURN(FALSE);
            }
            if (!Box_SetWorldFaces())
            {
                RWRETURN(FALSE);
            }
        }

        RWRETURN(TRUE);
    }

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

static RpGeometry  *
Box_Geometry(sfvec3f * box)
{
    RWFUNCTION(RWSTRING("Box_Geometry"));
    RWASSERT(box);

    if (box)
    {
        RpGeometry         *geometry;
        RwInt32             numVertices = 24;
        RwInt32             numPolys = 12;
        RwInt32             flags =
            rpGEOMETRYLIGHT | rpGEOMETRYNORMALS |
            rpGEOMETRYMODULATEMATERIALCOLOR;

        if (Material_IsCurrentTextured())
        {
            flags |= rpGEOMETRYTEXTURED;
        }

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

        RpGeometryLock(geometry, rpGEOMETRYLOCKALL);
        Box_SetTriangles(geometry, RpGeometryGetTriangles(geometry));
        Box_SetMorphTarget(geometry, box);
        Box_SetVertexInfo(geometry);
        RpGeometryUnlock(geometry);

        RWRETURN(geometry);
    }

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

static              RwBool
Box_SetTriangles(RpGeometry * geometry, RpTriangle * triangles)
{
    RWFUNCTION(RWSTRING("Box_SetTriangles"));
    RWASSERT(geometry);
    RWASSERT(triangles);

    if (geometry && triangles)
    {
        RpMaterial         *material;
        RwInt32             numFaces;
        RwInt32             faceNum;
        RwUInt16            indBase;

        numFaces = 6;
        indBase = 0;
        material = Material_GetCurrent();

        for (faceNum = 0; faceNum < numFaces; faceNum++)
        {
            RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                               (RwUInt16) (indBase + 2),
                                               (RwUInt16) (indBase + 1),
                                               indBase);
            RpGeometryTriangleSetMaterial(geometry, triangles,
                                          material);
            triangles++;

            RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                               indBase,
                                               (RwUInt16) (indBase + 3),
                                               (RwUInt16) (indBase +
                                                           2));
            RpGeometryTriangleSetMaterial(geometry, triangles,
                                          material);
            triangles++;

            indBase += 4;
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
Box_SetMorphTarget(RpGeometry * geometry, sfvec3f * box)
{
    RWFUNCTION(RWSTRING("Box_SetMorphTarget"));
    RWASSERT(geometry);
    RWASSERT(box);

    if (geometry && box)
    {
        RpMorphTarget      *morphTarget;
        RwV3d              *vertices;
        RwV3d              *normals;
        RwInt32             vertInd;
        RwInt32             faceNum;
        RwInt32             vertNum;
        RwReal              w, h, l;
        RwSphere            bSphere;

        w = (RwReal) ((box->x * GscaleFactor) / 2);
        h = (RwReal) ((box->y * GscaleFactor) / 2);
        l = (RwReal) ((box->z * GscaleFactor) / 2);

        morphTarget = RpGeometryGetMorphTarget(geometry, 0);
        vertices = RpMorphTargetGetVertices(morphTarget);
        normals = RpMorphTargetGetVertexNormals(morphTarget);

        vertInd = 0;
        for (faceNum = 0; faceNum < 6; faceNum++)
        {
            for (vertNum = 0; vertNum < 4; vertNum++)
            {
                vertInd = GboxIndices[faceNum][vertNum];

                vertices->x = GboxVertices[vertInd].x * w;
                vertices->y = GboxVertices[vertInd].y * h;
                vertices->z = GboxVertices[vertInd].z * l;

                vertices++;

                if (normals)
                {
                    *normals = GboxNormals[faceNum];

                    normals++;
                }
            }
        }

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

        RWRETURN(TRUE);
    }

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

static              RwBool
Box_SetVertexInfo(RpGeometry * geometry)
{
    RWFUNCTION(RWSTRING("Box_SetVertexInfo"));
    RWASSERT(geometry);

    if (geometry)
    {
        RwTexCoords        *vertexTexCoords;

        vertexTexCoords = RpGeometryGetVertexTexCoords(geometry, rwTEXTURECOORDINATEINDEX0);
        if (vertexTexCoords)
        {
            RwInt32             faceNum;
            RwInt32             vertNum;

            for (faceNum = 0; faceNum < 6; faceNum++)
            {
                for (vertNum = 0; vertNum < 4; vertNum++)
                {
                    *vertexTexCoords = GboxTexCoords[vertNum];

                    vertexTexCoords++;
                }
            }
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
Box_SetWorldVertices(sfvec3f * box)
{
    RWFUNCTION(RWSTRING("Box_SetWorldVertices"));
    RWASSERT(box);

    if (box)
    {
        RtWorldImportVertex *vertices;
        RwMatrix           *currentLTM;
        RwReal              w;
        RwReal              h;
        RwReal              l;
        RwInt32             numVertices = 24;

        w = (box->x * GscaleFactor) / 2;
        h = (box->y * GscaleFactor) / 2;
        l = (box->z * GscaleFactor) / 2;

        currentLTM = Build_GetCurrentLTM();

        /* allocate additional world vertices */
        vertices = Build_WorldAddMoreVertices(numVertices);
        if (vertices)
        {
            RwInt32             faceNum, vertNum;
            RwInt32             vertInd = 0;

            for (faceNum = 0; faceNum < 6; faceNum++)
            {
                for (vertNum = 0; vertNum < 4; vertNum++)
                {
                    vertInd = GboxIndices[faceNum][vertNum];

                    /* vertices */
                    vertices->OC.x = GboxVertices[vertInd].x * w;
                    vertices->OC.y = GboxVertices[vertInd].y * h;
                    vertices->OC.z = GboxVertices[vertInd].z * l;
                    RwV3dTransformPoints(&vertices->OC, &vertices->OC,
                                         1, currentLTM);

                    /* normals */
                    vertices->normal = GboxNormals[faceNum];
                    RwV3dTransformVectors(&vertices->normal,
                                          &vertices->normal, 1,
                                          currentLTM);

                    /* textures */
                    vertices->texCoords[0] = GboxTexCoords[vertNum];

                    vertices++;
                }
            }

            RWRETURN(TRUE);
        }

        RWRETURN(FALSE);
    }

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

static              RwBool
Box_SetWorldFaces(void)
{
    RtWorldImportTriangle *triangles;
    RwInt32             numFaces = 12;

    RWFUNCTION(RWSTRING("Box_SetWorldFaces"));

    /* allocate additional world triangles */
    triangles = Build_WorldAddMoreTriangles(numFaces);
    if (triangles)
    {
        RwInt32             matInd;
        RwInt32             numFaces;
        RwInt32             faceNum;
        RwInt32             indBase;
        RwInt32             vertexIndOffset;

        /* add current material to world & get its index */
        matInd = Material_GetCurrentIndex();
        vertexIndOffset = Build_WorldGetVertexIndOffset();
        indBase = 0;
        numFaces = 6;

        for (faceNum = 0; faceNum < numFaces; faceNum++)
        {
            triangles->vertIndex[0] = indBase + 2 + vertexIndOffset;
            triangles->vertIndex[1] = indBase + 1 + vertexIndOffset;
            triangles->vertIndex[2] = indBase + vertexIndOffset;
            triangles->matIndex = matInd;
            triangles++;

            triangles->vertIndex[0] = indBase + vertexIndOffset;
            triangles->vertIndex[1] = indBase + 3 + vertexIndOffset;
            triangles->vertIndex[2] = indBase + 2 + vertexIndOffset;
            triangles->matIndex = matInd;
            triangles++;

            indBase += 4;
        }

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

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

   Sphere methods

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

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

    if (an)
    {
        RpGeometry         *geometry;
        AbstractField      *af;
        sffloat             radius = (sffloat) 0;
        RwChar             *nodeName;

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

        /* get radius */
        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("radius"))))
        {
            Field              *field;

            field = AbstractField_GetField(af);
            radius = *FieldSffloat_GetValue(field);
        }

        if (Build_GetType() == CLUMP)
        {
            geometry = Sphere_Geometry(radius);
            if (!Build_AddGeometry(geometry))
            {
                RWRETURN(FALSE);
            }
        }
        else                   /* WORLD */
        {
            RwInt32             flags;

            flags = rpWORLDNORMALS | rpWORLDLIGHT;
            if (Material_IsCurrentTextured())
            {
                flags |= rpWORLDTEXTURED;
            }
            Build_WorldAccumulateFlags(flags);

            if (!Sphere_SetWorldVertices(radius))
            {
                RWRETURN(FALSE);
            }
            if (!Sphere_SetWorldFaces())
            {
                RWRETURN(FALSE);
            }
        }

        RWRETURN(TRUE);
    }

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

static RpGeometry  *
Sphere_Geometry(RwReal radius)
{
    RpGeometry         *geometry;
    RwInt32             numVertices, numFaces;
    RwInt32             flags =
        rpGEOMETRYLIGHT | rpGEOMETRYNORMALS |
        rpGEOMETRYMODULATEMATERIALCOLOR;

    RWFUNCTION(RWSTRING("Sphere_Geometry"));

    if (Material_IsCurrentTextured())
    {
        flags |= rpGEOMETRYTEXTURED;
    }

    numVertices = (SEGMENTS + 1) * ((SEGMENTS / 2) + 1);
    numFaces = SEGMENTS * (SEGMENTS - 2);

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

    RpGeometryLock(geometry, rpGEOMETRYLOCKALL);
    Sphere_SetTriangles(geometry, RpGeometryGetTriangles(geometry));
    Sphere_SetMorphTarget(geometry, radius);
    Sphere_SetVertexInfo(geometry);
    RpGeometryUnlock(geometry);

    RWRETURN(geometry);
}

static              RwBool
Sphere_SetTriangles(RpGeometry * geometry, RpTriangle * triangles)
{
    RWFUNCTION(RWSTRING("Sphere_SetTriangles"));
    RWASSERT(geometry);
    RWASSERT(triangles);

    if (geometry && triangles)
    {
        RpMaterial         *material;
        RwInt32             latFace, longFace;
        RwUInt16            ind1, ind2, ind3;

        material = Material_GetCurrent();

        /* top cap */
        ind1 = SEGMENTS;
        ind2 = ~0;
        for (longFace = 0; longFace < SEGMENTS; longFace++)
        {
            ind1++;
            ind2++;
            ind3 = ind1 + 1;
            RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                               ind3, ind2, ind1);
            RpGeometryTriangleSetMaterial(geometry, triangles,
                                          material);
            triangles++;
        }

        /* middle */
        for (latFace = 1; latFace < ((SEGMENTS / 2) - 1); latFace++)
        {
            ind1 += 1;
            ind2 += 1;
            for (longFace = 0; longFace < SEGMENTS; longFace++)
            {
                ind1++;
                ind2++;
                ind3 = ind1 + 1;

                RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                                   ind3, ind2, ind1);
                RpGeometryTriangleSetMaterial(geometry, triangles,
                                              material);
                triangles++;

                RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                                   (RwUInt16) (ind2 +
                                                               1), ind2,
                                                   ind3);
                RpGeometryTriangleSetMaterial(geometry, triangles,
                                              material);
                triangles++;
            }
        }

        /* bottom cap */
        ind1++;
        ind2++;
        for (longFace = 0; longFace < SEGMENTS; longFace++)
        {
            ind1++;
            ind2++;
            ind3 = ind2 + 1;
            RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                               ind3, ind2, ind1);
            RpGeometryTriangleSetMaterial(geometry, triangles,
                                          material);
            triangles++;
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
Sphere_SetMorphTarget(RpGeometry * geometry, RwReal radius)
{
    RWFUNCTION(RWSTRING("Sphere_SetMorphTarget"));
    RWASSERT(geometry);

    if (geometry)
    {
        RpMorphTarget      *morphTarget;
        RwV3d              *vertices;
        RwV3d              *normals;
        RwReal              angInc = (RwReal) ((2 * rwPI) / SEGMENTS);
        RwReal              latAng = (RwReal) (0), longAng =
            (RwReal) (0);
        RwInt32             latSeg, longSeg;
        RwSphere            bSphere;

#if (0)
        RwInt32             vertNum = 0;
#endif /* (0) */

        morphTarget = RpGeometryGetMorphTarget(geometry, 0);
        vertices = RpMorphTargetGetVertices(morphTarget);
        normals = RpMorphTargetGetVertexNormals(morphTarget);

        radius *= GscaleFactor;

        for (latSeg = 0; latSeg <= (SEGMENTS / 2); latSeg++)
        {
            longAng = (RwReal) (0);
            for (longSeg = 0; longSeg <= SEGMENTS; longSeg++)
            {
                RwReal              latRadius =
                    (RwReal) (radius * RwSin(latAng));

                vertices->x = (RwReal) (latRadius * RwSin(longAng));
                vertices->y = (RwReal) (radius * RwCos(latAng));
                vertices->z = (RwReal) (latRadius * RwCos(longAng));
                if (normals)
                {
                    normals->x = vertices->x / radius;
                    normals->y = vertices->y / radius;
                    normals->z = vertices->z / radius;
                    normals++;
                }
                vertices++;
                longAng += angInc;
            }
            latAng += angInc;
        }

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

        RWRETURN(TRUE);
    }

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

static              RwBool
Sphere_SetVertexInfo(RpGeometry * geometry)
{
    RWFUNCTION(RWSTRING("Sphere_SetVertexInfo"));
    RWASSERT(geometry);

    if (geometry)
    {
        RwTexCoords        *vertexTexCoords =
            RpGeometryGetVertexTexCoords(geometry, rwTEXTURECOORDINATEINDEX0);

        if (vertexTexCoords)
        {
            RwInt32             longSeg, latSeg;
            RwReal              u = (RwReal) (0), v = (RwReal) (0);
            RwReal              uInc = (RwReal) (1.0f / SEGMENTS);
            RwReal              vInc = (RwReal) (2.0f / SEGMENTS);

            for (longSeg = 0; longSeg <= (SEGMENTS / 2); longSeg++)
            {
                for (latSeg = 0; latSeg <= SEGMENTS; latSeg++)
                {
                    vertexTexCoords->u = u;
                    vertexTexCoords->v = v;
                    vertexTexCoords++;
                    u += uInc;
                }
                u = (RwReal) (0);
                v += vInc;
            }
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
Sphere_SetWorldVertices(RwReal radius)
{
    RtWorldImportVertex *vertices;
    RwInt32             numVertices;

    RWFUNCTION(RWSTRING("Sphere_SetWorldVertices"));

    /* allocate additional world vertices */
    numVertices = (SEGMENTS + 1) * ((SEGMENTS / 2) + 1);
    vertices = Build_WorldAddMoreVertices(numVertices);
    if (vertices)
    {
        RwReal              latAng = (RwReal) (0), longAng =
            (RwReal) (0);
        RwReal              angInc = (RwReal) ((2 * rwPI) / SEGMENTS);
        RwInt32             latSeg, longSeg;
        RwMatrix           *currentLTM = Build_GetCurrentLTM();
        RwReal              u = (RwReal) (0), v = (RwReal) (0);
        RwReal              uInc = (RwReal) (1.0f / SEGMENTS);
        RwReal              vInc = (RwReal) (2.0f / SEGMENTS);

        radius *= GscaleFactor;

        for (latSeg = 0; latSeg <= (SEGMENTS / 2); latSeg++)
        {
            longAng = (RwReal) (0);
            for (longSeg = 0; longSeg <= SEGMENTS; longSeg++)
            {
                RwReal              latRadius =
                    (RwReal) (radius * RwSin(latAng));

                /* vertices */
                vertices->OC.x = (RwReal) (latRadius * RwSin(longAng));
                vertices->OC.y = (RwReal) (radius * RwCos(latAng));
                vertices->OC.z = (RwReal) (latRadius * RwCos(longAng));
                RwV3dTransformPoints(&vertices->OC, &vertices->OC, 1,
                                     currentLTM);

                /* normals */
                vertices->normal.x = vertices->OC.x / radius;
                vertices->normal.y = vertices->OC.y / radius;
                vertices->normal.z = vertices->OC.z / radius;
                RwV3dTransformVectors(&vertices->normal,
                                      &vertices->normal, 1, currentLTM);

                /* textures */
                vertices->texCoords[0].u = u;
                vertices->texCoords[0].v = v;

                vertices++;
                longAng += angInc;
                u += uInc;
            }
            latAng += angInc;
            u = (RwReal) (0);
            v += vInc;
        }

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

static              RwBool
Sphere_SetWorldFaces(void)
{
    RtWorldImportTriangle *triangles;
    RwInt32             numFaces;

    RWFUNCTION(RWSTRING("Sphere_SetWorldFaces"));

    /* allocate additional world triangles */
    numFaces = SEGMENTS * (SEGMENTS - 2);
    triangles = Build_WorldAddMoreTriangles(numFaces);
    if (triangles)
    {
        RwInt32             matInd;
        RwInt32             vertexIndOffset =
            Build_WorldGetVertexIndOffset();
        RwInt32             latFace, longFace;
        RwInt32             ind1 = 0, ind2 = 0, ind3 = 0;

        /* add current material to world & get its index */
        matInd = Material_GetCurrentIndex();

        /* top cap */
        ind1 = SEGMENTS;
        ind2 = -1;
        for (longFace = 0; longFace < SEGMENTS; longFace++)
        {
            ind1++;
            ind2++;
            ind3 = ind1 + 1;

            triangles->vertIndex[0] = ind3 + vertexIndOffset;
            triangles->vertIndex[1] = ind2 + vertexIndOffset;
            triangles->vertIndex[2] = ind1 + vertexIndOffset;
            triangles->matIndex = matInd;
            triangles++;
        }

        /* middle */
        for (latFace = 1; latFace < ((SEGMENTS / 2) - 1); latFace++)
        {
            ind1 += 1;
            ind2 += 1;
            for (longFace = 0; longFace < SEGMENTS; longFace++)
            {
                ind1++;
                ind2++;
                ind3 = ind1 + 1;

                triangles->vertIndex[0] = ind3 + vertexIndOffset;
                triangles->vertIndex[1] = ind2 + vertexIndOffset;
                triangles->vertIndex[2] = ind1 + vertexIndOffset;
                triangles->matIndex = matInd;
                triangles++;

                triangles->vertIndex[0] = ind2 + 1 + vertexIndOffset;
                triangles->vertIndex[1] = ind2 + vertexIndOffset;
                triangles->vertIndex[2] = ind3 + vertexIndOffset;
                triangles->matIndex = matInd;
                triangles++;
            }
        }

        /* bottom cap */
        ind1++;
        ind2++;
        for (longFace = 0; longFace < SEGMENTS; longFace++)
        {
            ind1++;
            ind2++;
            ind3 = ind2 + 1;
            triangles->vertIndex[0] = ind3 + vertexIndOffset;
            triangles->vertIndex[1] = ind2 + vertexIndOffset;
            triangles->vertIndex[2] = ind1 + vertexIndOffset;
            triangles->matIndex = matInd;
            triangles++;
        }

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

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

   Cylinder methods

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

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

    if (an)
    {
        RpGeometry         *geometry;
        AbstractField      *af;
        sffloat             radius = (sffloat) 0;
        sffloat             height = (sffloat) 0;
        sfbool              bottom = (sfbool) 0;
        sfbool              top = (sfbool) 0;
        sfbool              side = (sfbool) 0;
        RwChar             *nodeName;

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

        /* get radius */
        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("radius"))))
        {
            Field              *field;

            field = AbstractField_GetField(af);
            radius = *FieldSffloat_GetValue(field);
        }

        /* get height */
        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("height"))))
        {
            Field              *field;

            field = AbstractField_GetField(af);
            height = *FieldSffloat_GetValue(field);
        }

        /* get top */
        if ((af = AbstractNode_GetAbstractField(an, RWSTRING("top"))))
        {
            Field              *field;

            field = AbstractField_GetField(af);
            bottom = *FieldSfbool_GetValue(field);
        }

        /* get bottom */
        if ((af = AbstractNode_GetAbstractField(an, RWSTRING("top"))))
        {
            Field              *field;

            field = AbstractField_GetField(af);
            top = *FieldSfbool_GetValue(field);
        }

        /* get side */
        if ((af = AbstractNode_GetAbstractField(an, RWSTRING("side"))))
        {
            Field              *field;

            field = AbstractField_GetField(af);
            side = *FieldSfbool_GetValue(field);
        }

        if (Build_GetType() == CLUMP)
        {
            geometry =
                Cylinder_Geometry(height, radius, top, bottom, side);
            if (!Build_AddGeometry(geometry))
            {
                RWRETURN(FALSE);
            }
        }
        else                   /* WORLD */
        {
            RwInt32             flags;

            flags = rpWORLDNORMALS | rpWORLDLIGHT;
            if (Material_IsCurrentTextured())
            {
                flags |= rpWORLDTEXTURED;
            }
            Build_WorldAccumulateFlags(flags);

            if (!Cylinder_SetWorldVertices
                (height, radius, top, bottom, side))
            {
                RWRETURN(FALSE);
            }
            if (!Cylinder_SetWorldFaces(top, bottom, side))
            {
                RWRETURN(FALSE);
            }
        }

        RWRETURN(TRUE);
    }

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

static RpGeometry  *
Cylinder_Geometry(RwReal height, RwReal radius, RwBool top,
                  RwBool bottom, RwBool side)
{
    RpGeometry         *geometry;
    RwInt32             numVertices = 0, numFaces = 0;
    RwInt32             flags =
        rpGEOMETRYLIGHT | rpGEOMETRYNORMALS |
        rpGEOMETRYMODULATEMATERIALCOLOR;

    RWFUNCTION(RWSTRING("Cylinder_Geometry"));

    if (Material_IsCurrentTextured())
    {
        flags |= rpGEOMETRYTEXTURED;
    }

    if (top)
    {
        numVertices += (SEGMENTS + 2);
        numFaces += SEGMENTS;
    }
    if (bottom)
    {
        numVertices += (SEGMENTS + 2);
        numFaces += SEGMENTS;
    }
    if (side)
    {
        numVertices += (2 * (SEGMENTS + 1));
        numFaces += (2 * SEGMENTS);
    }

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

    RpGeometryLock(geometry, rpGEOMETRYLOCKALL);
    Cylinder_SetTriangles(geometry, RpGeometryGetTriangles(geometry),
                          top, bottom, side);
    Cylinder_SetMorphTarget(geometry, height, radius, top, bottom,
                            side);
    Cylinder_SetVertexInfo(geometry, top, bottom, side);
    RpGeometryUnlock(geometry);

    RWRETURN(geometry);
}

static              RwBool
Cylinder_SetMorphTarget(RpGeometry * geometry, RwReal height,
                        RwReal radius, RwBool top, RwBool bottom,
                        RwBool side)
{
    RWFUNCTION(RWSTRING("Cylinder_SetMorphTarget"));
    RWASSERT(geometry);

    if (geometry)
    {
        RpMorphTarget      *morphTarget;
        RwV3d              *vertices;
        RwV3d              *normals;
        RwInt32             segNum;
        RwReal              angle, angleInc =
            (RwReal) ((2 * rwPI) / SEGMENTS);
        RwSphere            bSphere;

        morphTarget = RpGeometryGetMorphTarget(geometry, 0);
        vertices = RpMorphTargetGetVertices(morphTarget);
        normals = RpMorphTargetGetVertexNormals(morphTarget);

        radius *= GscaleFactor;
        height *= GscaleFactor;

        if (top)
        {
            vertices->x = (RwReal) (0);
            vertices->y = (RwReal) (height / 2);
            vertices->z = (RwReal) (0);
            vertices++;
            if (normals)
            {
                normals->x = (RwReal) (0);
                normals->y = (RwReal) (1);
                normals->z = (RwReal) (0);
                normals++;
            }
            angle = (RwReal) (0);
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                vertices->x = (RwReal) (radius * RwCos(angle));
                vertices->y = (RwReal) (height / 2);
                vertices->z = (RwReal) (radius * RwSin(angle));
                angle += angleInc;
                vertices++;
                if (normals)
                {
                    normals->x = (RwReal) (0);
                    normals->y = (RwReal) (1);
                    normals->z = (RwReal) (0);
                    normals++;
                }
            }
        }

        if (bottom)
        {
            vertices->x = (RwReal) (0);
            vertices->y = (RwReal) (-height / 2);
            vertices->z = (RwReal) (0);
            vertices++;
            if (normals)
            {
                normals->x = (RwReal) (0);
                normals->y = (RwReal) (-1);
                normals->z = (RwReal) (0);
                normals++;
            }
            angle = (RwReal) (0);
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                vertices->x = (RwReal) (radius * RwCos(angle));
                vertices->y = (RwReal) (-height / 2);
                vertices->z = (RwReal) (radius * RwSin(angle));
                angle += angleInc;
                vertices++;
                if (normals)
                {
                    normals->x = (RwReal) (0);
                    normals->y = (RwReal) (-1);
                    normals->z = (RwReal) (0);
                    normals++;
                }
            }
        }

        if (side)
        {
            angle = (RwReal) (0);
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                vertices->x = (RwReal) (radius * RwCos(angle));
                vertices->y = (RwReal) (height / 2);
                vertices->z = (RwReal) (radius * RwSin(angle));
                angle += angleInc;
                vertices++;
                if (normals)
                {
                    normals->x = (RwReal) (RwCos(angle));
                    normals->y = (RwReal) (0);
                    normals->z = (RwReal) (RwSin(angle));
                    normals++;
                }

            }
            angle = (RwReal) (0);
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                vertices->x = (RwReal) (radius * RwCos(angle));
                vertices->y = (RwReal) (-height / 2);
                vertices->z = (RwReal) (radius * RwSin(angle));
                angle += angleInc;
                vertices++;
                if (normals)
                {
                    normals->x = (RwReal) (RwCos(angle));
                    normals->y = (RwReal) (0);
                    normals->z = (RwReal) (RwSin(angle));
                    normals++;
                }
            }
        }

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

        RWRETURN(TRUE);
    }

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

static              RwBool
Cylinder_SetTriangles(RpGeometry * geometry, RpTriangle * triangles,
                      RwBool top, RwBool bottom, RwBool side)
{
    RWFUNCTION(RWSTRING("Cylinder_SetTriangles"));
    RWASSERT(geometry);
    RWASSERT(triangles);

    if (geometry && triangles)
    {
        RpMaterial         *material;
        RwInt32             segNum;
        RwUInt16            ind1, ind2;
        RwUInt16            baseInd;

        /* Set up the part base */
        baseInd = 0;
        material = Material_GetCurrent();

        if (top)
        {
            /* baseInd is the center of the disc */
            ind1 = baseInd + 1;
            for (segNum = 0; segNum < SEGMENTS; segNum++)
            {
                RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                                   ind1, baseInd,
                                                   (RwUInt16) (ind1 +
                                                               1));
                RpGeometryTriangleSetMaterial(geometry, triangles,
                                              material);
                triangles++;
                ind1++;
            }

            /* Skip to next part */
            baseInd += (SEGMENTS + 2);
        }

        if (bottom)
        {
            /* baseInd is the center of the disc */
            ind1 = baseInd + 1;
            for (segNum = 0; segNum < SEGMENTS; segNum++)
            {
                RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                                   (RwUInt16) (ind1 +
                                                               1),
                                                   baseInd, ind1);
                RpGeometryTriangleSetMaterial(geometry, triangles,
                                              material);
                triangles++;
                ind1++;
            }

            /* Skip to next part */
            baseInd += (SEGMENTS + 2);
        }

        if (side)
        {
            ind1 = baseInd;
            ind2 = baseInd + SEGMENTS + 1;
            for (segNum = 0; segNum < SEGMENTS; segNum++)
            {
                RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                                   ind1,
                                                   (RwUInt16) (ind2 +
                                                               1),
                                                   ind2);
                RpGeometryTriangleSetMaterial(geometry, triangles,
                                              material);
                triangles++;

                RpGeometryTriangleSetVertexIndices(geometry, triangles,
                                                   ind1,
                                                   (RwUInt16) (ind1 +
                                                               1),
                                                   (RwUInt16) (ind2 +
                                                               1));
                RpGeometryTriangleSetMaterial(geometry, triangles,
                                              material);
                triangles++;

                ind1++;
                ind2++;
            }
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
Cylinder_SetVertexInfo(RpGeometry * geometry, RwBool top, RwBool bottom,
                       RwBool side)
{
    RWFUNCTION(RWSTRING("Cylinder_SetVertexInfo"));
    RWASSERT(geometry);

    if (geometry)
    {
        RwTexCoords        *vertexTexCoords =
            RpGeometryGetVertexTexCoords(geometry, rwTEXTURECOORDINATEINDEX0);

        if (vertexTexCoords)
        {
            RwInt32             segNum;
            RwReal              u, v;
            RwReal              uInc = (RwReal) (1.0f / SEGMENTS);
            RwReal              angle, angleInc =
                (RwReal) ((2 * rwPI) / SEGMENTS);

            if (top)
            {
                vertexTexCoords->u = (RwReal) (0.5);
                vertexTexCoords->v = (RwReal) (0.5);
                vertexTexCoords++;
                angle = (RwReal) (0);
                for (segNum = 0; segNum <= SEGMENTS; segNum++)
                {
                    u = (RwReal) ((1 + RwCos(angle)) / 2);
                    v = (RwReal) ((1 + RwSin(angle)) / 2);
                    vertexTexCoords->u = u;
                    vertexTexCoords->v = v;
                    vertexTexCoords++;
                    angle += angleInc;
                }
            }
            if (bottom)
            {
                vertexTexCoords->u = (RwReal) (0.5);
                vertexTexCoords->v = (RwReal) (0.5);
                vertexTexCoords++;
                angle = (RwReal) (0);
                for (segNum = 0; segNum <= SEGMENTS; segNum++)
                {
                    u = (RwReal) ((1 + RwCos(angle)) / 2);
                    v = (RwReal) ((1 + RwSin(angle)) / 2);
                    vertexTexCoords->u = u;
                    vertexTexCoords->v = v;
                    vertexTexCoords++;
                    angle += angleInc;
                }
            }
            if (side)
            {
                u = (RwReal) (0);
                v = (RwReal) (0);
                for (segNum = 0; segNum <= SEGMENTS; segNum++)
                {
                    vertexTexCoords->u = u;
                    vertexTexCoords->v = v;
                    vertexTexCoords++;
                    u += uInc;
                }
                u = (RwReal) (0);
                v = (RwReal) (1);
                for (segNum = 0; segNum <= SEGMENTS; segNum++)
                {
                    vertexTexCoords->u = u;
                    vertexTexCoords->v = v;
                    vertexTexCoords++;
                    u += uInc;
                }
            }
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
Cylinder_SetWorldVertices(RwReal height, RwReal radius, RwBool top,
                          RwBool bottom, RwBool side)
{
    RtWorldImportVertex *vertices;
    RwInt32             numVertices = 0;

    RWFUNCTION(RWSTRING("Cylinder_SetWorldVertices"));

    if (top)
    {
        numVertices += (SEGMENTS + 2);
    }
    if (bottom)
    {
        numVertices += (SEGMENTS + 2);
    }
    if (side)
    {
        numVertices += (2 * (SEGMENTS + 1));
    }

    radius *= GscaleFactor;
    height *= GscaleFactor;

    /* allocate additional world vertices */
    vertices = Build_WorldAddMoreVertices(numVertices);
    if (vertices)
    {
        RwInt32             segNum;
        RwReal              u, v;
        RwReal              uInc = (RwReal) (1.0f / SEGMENTS);
        RwReal              angle, angleInc =
            (RwReal) ((2 * rwPI) / SEGMENTS);
        RwMatrix           *currentLTM = Build_GetCurrentLTM();

        if (top)
        {
            /* vertices */
            vertices->OC.x = (RwReal) (0);
            vertices->OC.y = (RwReal) (height / 2);
            vertices->OC.z = (RwReal) (0);
            RwV3dTransformPoints(&vertices->OC, &vertices->OC, 1,
                                 currentLTM);

            /* normals */
            vertices->normal.x = (RwReal) (0);
            vertices->normal.y = (RwReal) (1);
            vertices->normal.z = (RwReal) (0);
            RwV3dTransformVectors(&vertices->normal, &vertices->normal,
                                  1, currentLTM);

            /* textures */
            vertices->texCoords[0].u = (RwReal) (0.5);
            vertices->texCoords[0].v = (RwReal) (0.5);

            vertices++;

            angle = (RwReal) (0);
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                /* vertices */
                vertices->OC.x = (RwReal) (radius * RwCos(angle));
                vertices->OC.y = (RwReal) (height / 2);
                vertices->OC.z = (RwReal) (radius * RwSin(angle));
                RwV3dTransformPoints(&vertices->OC, &vertices->OC, 1,
                                     currentLTM);

                /* normals */
                vertices->normal.x = (RwReal) (0);
                vertices->normal.y = (RwReal) (1);
                vertices->normal.z = (RwReal) (0);
                RwV3dTransformVectors(&vertices->normal,
                                      &vertices->normal, 1, currentLTM);

                /* textures */
                u = (RwReal) ((1 + RwCos(angle)) / 2);
                v = (RwReal) ((1 + RwSin(angle)) / 2);
                vertices->texCoords[0].u = u;
                vertices->texCoords[0].v = v;

                vertices++;
                angle += angleInc;
            }
        }
        if (bottom)
        {
            /* vertices */
            vertices->OC.x = (RwReal) (0);
            vertices->OC.y = (RwReal) (-height / 2);
            vertices->OC.z = (RwReal) (0);
            RwV3dTransformPoints(&vertices->OC, &vertices->OC, 1,
                                 currentLTM);

            /* normals */
            vertices->normal.x = (RwReal) (0);
            vertices->normal.y = (RwReal) (-1);
            vertices->normal.z = (RwReal) (0);
            RwV3dTransformVectors(&vertices->normal, &vertices->normal,
                                  1, currentLTM);

            /* textures */
            vertices->texCoords[0].u = (RwReal) (0.5);
            vertices->texCoords[0].v = (RwReal) (0.5);

            vertices++;

            angle = 0.0f;
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                /* vertices */
                vertices->OC.x = (RwReal) (radius * RwCos(angle));
                vertices->OC.y = (RwReal) (-height / 2);
                vertices->OC.z = (RwReal) (radius * RwSin(angle));
                RwV3dTransformPoints(&vertices->OC, &vertices->OC, 1,
                                     currentLTM);

                /* normals */
                vertices->normal.x = (RwReal) (0);
                vertices->normal.y = (RwReal) (-1);
                vertices->normal.z = (RwReal) (0);
                RwV3dTransformVectors(&vertices->normal,
                                      &vertices->normal, 1, currentLTM);

                /* textures */
                u = (RwReal) ((1 + RwCos(angle)) / 2);
                v = (RwReal) ((1 + RwSin(angle)) / 2);
                vertices->texCoords[0].u = u;
                vertices->texCoords[0].v = v;

                vertices++;
                angle += angleInc;
            }
        }
        if (side)
        {
            angle = (RwReal) (0);
            u = (RwReal) (0);
            v = (RwReal) (0);
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                /* vertices */
                vertices->OC.x = (RwReal) (radius * RwCos(angle));
                vertices->OC.y = (RwReal) (height / 2);
                vertices->OC.z = (RwReal) (radius * RwSin(angle));
                RwV3dTransformPoints(&vertices->OC, &vertices->OC, 1,
                                     currentLTM);

                /* normals */
                vertices->normal.x = (RwReal) (RwCos(angle));
                vertices->normal.y = (RwReal) (0);
                vertices->normal.z = (RwReal) (RwSin(angle));
                RwV3dTransformVectors(&vertices->normal,
                                      &vertices->normal, 1, currentLTM);

                /* textures */
                vertices->texCoords[0].u = u;
                vertices->texCoords[0].v = v;

                vertices++;
                angle += angleInc;
                u += uInc;
            }
            angle = (RwReal) (0);
            u = (RwReal) (0);
            v = (RwReal) (1);
            for (segNum = 0; segNum <= SEGMENTS; segNum++)
            {
                /* vertices */
                vertices->OC.x = (RwReal) (radius * RwCos(angle));
                vertices->OC.y = (RwReal) (-height / 2);
                vertices->OC.z = (RwReal) (radius * RwSin(angle));
                RwV3dTransformPoints(&vertices->OC, &vertices->OC, 1,
                                     currentLTM);

                /* normals */
                vertices->normal.x = (RwReal) (RwCos(angle));
                vertices->normal.y = (RwReal) (0);
                vertices->normal.z = (RwReal) (RwSin(angle));
                RwV3dTransformVectors(&vertices->normal,
                                      &vertices->normal, 1, currentLTM);

                /* textures */
                vertices->texCoords[0].u = u;
                vertices->texCoords[0].v = v;

                vertices++;
                angle += angleInc;
                u += uInc;
            }
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
Cylinder_SetWorldFaces(RwBool top, RwBool bottom, RwBool side)
{
    RtWorldImportTriangle *triangles;
    RwInt32             numFaces = 0;

    RWFUNCTION(RWSTRING("Cylinder_SetWorldFaces"));

    if (top)
    {
        numFaces += SEGMENTS;
    }
    if (bottom)
    {
        numFaces += SEGMENTS;
    }
    if (side)
    {
        numFaces += (2 * SEGMENTS);
    }

    /* allocate additional world triangles */
    triangles = Build_WorldAddMoreTriangles(numFaces);
    if (triangles)
    {
        RwInt32             matInd;
        RwInt32             vertexIndOffset =
            Build_WorldGetVertexIndOffset();
        RwInt32             ind1 = 0;
        RwInt32             ind2 = 0;
        RwInt32             baseInd;
        RwInt32             segNum;

#if (0)
        RwInt32             ind3 = 0;
#endif /* (0) */

        /* add current material to world & get its index */
        matInd = Material_GetCurrentIndex();
        baseInd = 0;

        if (top)
        {
            ind1 = baseInd + 1;
            for (segNum = 0; segNum < SEGMENTS; segNum++)
            {
                triangles->vertIndex[0] = ind1 + vertexIndOffset;
                triangles->vertIndex[1] = baseInd + vertexIndOffset;
                triangles->vertIndex[2] = ind1 + 1 + vertexIndOffset;
                triangles->matIndex = matInd;
                triangles++;
                ind1++;
            }
            baseInd += (SEGMENTS + 2);
        }
        if (bottom)
        {
            ind1 = baseInd + 1;
            for (segNum = 0; segNum < SEGMENTS; segNum++)
            {
                triangles->vertIndex[0] = ind1 + 1 + vertexIndOffset;
                triangles->vertIndex[1] = baseInd + vertexIndOffset;
                triangles->vertIndex[2] = ind1 + vertexIndOffset;
                triangles->matIndex = matInd;
                triangles++;
                ind1++;
            }
            baseInd += (SEGMENTS + 2);
        }
        if (side)
        {
            ind1 = baseInd;
            ind2 = baseInd + SEGMENTS + 1;
            for (segNum = 0; segNum < SEGMENTS; segNum++)
            {
                triangles->vertIndex[0] = ind1 + vertexIndOffset;
                triangles->vertIndex[1] = ind2 + 1 + vertexIndOffset;
                triangles->vertIndex[2] = ind2 + vertexIndOffset;
                triangles->matIndex = matInd;
                triangles++;

                triangles->vertIndex[0] = ind1 + vertexIndOffset;
                triangles->vertIndex[1] = ind1 + 1 + vertexIndOffset;
                triangles->vertIndex[2] = ind2 + 1 + vertexIndOffset;
                triangles->matIndex = matInd;
                triangles++;

                ind1++;
                ind2++;
            }
        }

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

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

   stub functions for later implementation

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

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

    if (an)
    {
        RwChar             *nodeName;

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

        RWERROR((E_RP_VRML_INDEXEDLINESET, an->lineNum));

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

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

    if (an)
    {
        RwChar             *nodeName;

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

        RWERROR((E_RP_VRML_POINTSET, an->lineNum));

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

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

    if (an)
    {
        RwChar             *nodeName;

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

        RWERROR((E_RP_VRML_TEXT, an->lineNum));

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}
