Txtr_Geometry.c

/*
 *  Txtr_Geometry.c
 *
 *  This is a simple library illustrating trigrid and box geometries. The trigrids
 *  represented are Square, Torus, Wavey Torus, Splash, Sphere, Cone, Pipe, Steps,
 *  and Spring.
 *
 *  NewLibraryTriGrid returns a trigrid geometry when kGeometryLibraryRange_Simple
 *  is added to the trigrid number (0 to 8) or a trigrid with shading uv attributes
 *  when kGeometryLibraryRange_UVGeoAttributes or kGeometryLibraryRange_UVFaceAttributes
 *  is added to the trigrid number.
 *
 *  Note: The calculation of uv's may be flipping the texture.
 *
 *
 *  Robert Dierkes
 *  © 1995 Apple Computer, Inc.
 *
 *   03/22/95   RDD     Created.
 *   09/20/95   RDD     Cleanup.
 *   11/13/95   RDD     Changed macro to DebugStr.
 */
 
/*------------------*/
/*  Include Files   */
/*------------------*/
#include <Memory.h>
#include <math.h>
 
#include "QD3D.h"
#include "QD3DGeometry.h"
#include "QD3DMath.h"
#include "QD3DSet.h"
 
#include "Txtr_Geometry.h"
#include "Txtr_Math.h"
#include "Txtr_Error.h"
 
 
/*----------------------*/
/*  Local Prototypes    */
/*----------------------*/
static
void AddVertexNormals(
    unsigned long   geoNum,
    TQ3TriGridData  *pTriData);
 
 
 
unsigned long GetLibraryMaxSimpleBox(void)
{
    return kGeometryLibrary_BoxMaxSimple;
}
 
 
unsigned long GetLibraryMaxSimpleTriGrid(void)
{
    return kGeometryLibrary_TriGridMaxSimple;
}
 
 
 
TQ3GeometryObject NewLibraryBox(
            unsigned long       num)
{
    TQ3GeometryObject   geometryObject = NULL;
    TQ3BoxData          boxData;
 
    switch (num)
    {
    case kGeometryLibraryRange_Simple+0:    /* Box */
        Q3Point3D_Set(&boxData.origin,      -0.5,-0.5,-0.5);
        Q3Vector3D_Set(&boxData.orientation, 0.0, 1.0, 0.0);
        Q3Vector3D_Set(&boxData.majorAxis,   0.0, 0.0, 1.0);
        Q3Vector3D_Set(&boxData.minorAxis,   1.0, 0.0, 0.0);
        boxData.boxAttributeSet  = NULL;
        boxData.faceAttributeSet = NULL;
 
        geometryObject = Q3Box_New(&boxData);
        break;
 
    case kGeometryLibraryRange_Simple+1:    /* Skewed Box */
        Q3Point3D_Set(&boxData.origin,      -0.75,-0.5,-0.75);
        Q3Vector3D_Set(&boxData.orientation, 0.5,  1.0, 0.5);
        Q3Vector3D_Set(&boxData.majorAxis,   0.0,  0.0, 1.0);
        Q3Vector3D_Set(&boxData.minorAxis,   1.0,  0.0, 0.0);
        boxData.boxAttributeSet  = NULL;
        boxData.faceAttributeSet = NULL;
 
        geometryObject = Q3Box_New(&boxData);
        break;
 
    default:
        DebugStr("\pWarning:  NewLibraryBox ; unknown box index");
        return NULL;
        break;
    }
 
    return geometryObject;
}
 
 
/*
 *  NewLibraryTriGrid
 *
 * There are two methods below for creating trigrid vertices. If the trigrid is:
 *
 *  1. planar (relatively) then the vvValue (an x-coordinate) increases from vvMin to vvMax.
 *  2. a cross section revolved about the Y axis then the vvValue (an angle) decreases from
 *      vvMax to vvMin in a counter-clockwise manner about the axis. This puts the correct
 *      side of the trigrid facing outward for texture uvs.
 *
 * In both cases, uuValue decreases from uuMax to uuMin from postive to negative Y.
 *
 *  TriGrid vertex index numbering
 *
 *     (i)
 *      8       9       10      11
 *      4       5       6       7
 *      0       1       2       3
 *
 *
 *  UVs for an upright texture continuous across the trigrid
 *
 *     (U,V)
 *     (x,y)
 *
 *      ^ +     0.0, 1.0    0.33, 1.0   0.66, 1.0   1.0, 1.0
 *      |       0.0, 0.5    0.33, 0.5   0.66, 0.5   1.0, 0.5
 *      V -     0.0, 0.0    0.33, 0.0   0.66, 0.0   1.0, 0.0
 *
 *              U -->
 *              -   +
 *
 *  UVs for an upright repeated texture
 *
 *     (U,V)
 *     (x,y)
 *
 *      ^ +     0,2     1,2     2,2     3,2
 *      |       0,1     1,1     2,1     3,1
 *      V -     0,0     1,0     2,0     3,0
 *
 *              U -->
 *              -   +
 */
TQ3GeometryObject NewLibraryTriGrid(
            unsigned long       num)
{
    TQ3GeometryObject   geometryObject = NULL;
    TQ3TriGridData      data;
    TQ3Vertex3D         *vertices;
    unsigned long       i;
    float               uuValue, vvValue,
                        uuMin, uuMax, uuStep,
                        vvMin, vvMax, vvStep,
                        radius;
    TQ3Boolean          hasUVAttributes;
    TQ3Param2D          param2D;
    TQ3Boolean          addNormals;
 
    hasUVAttributes = (TQ3Boolean) (mHasUVGeoAttributes(num)  ||  mHasUVFaceAttributes(num));
 
    switch (num)
    {
        case kGeometryLibraryRange_Simple+0:        /* Flat 5x5 */
        case kGeometryLibraryRange_UVGeoAttributes+0:
        case kGeometryLibraryRange_UVFaceAttributes+0:
        case kGeometryLibraryRange_Simple+3:        /* Splash */
        case kGeometryLibraryRange_UVGeoAttributes+3:
        case kGeometryLibraryRange_UVFaceAttributes+3:
        case kGeometryLibraryRange_Simple+7:        /* Steps */
        case kGeometryLibraryRange_UVGeoAttributes+7:
        case kGeometryLibraryRange_UVFaceAttributes+7:
        {
            addNormals = kQ3False;
 
            /* Setup min, max and step values trigrid vertex u & v values */
            switch (num)
            {
            /**
             ** Flat 5x5
             **/
            case kGeometryLibraryRange_Simple+0:
            case kGeometryLibraryRange_UVGeoAttributes+0:
            case kGeometryLibraryRange_UVFaceAttributes+0:
 
                /*
                 * uuValue is an x-coordinate
                 * vvValue is a  y-coordinate
                 */
                uuMin = -0.5;
                uuMax =  0.5;
                uuStep = 0.25;
 
                vvMin = -0.5;
                vvMax =  0.5;
                vvStep = 0.25;
            break;
 
            /**
             ** Splash
             **/
            case kGeometryLibraryRange_Simple+3:
            case kGeometryLibraryRange_UVGeoAttributes+3:
            case kGeometryLibraryRange_UVFaceAttributes+3:
 
                addNormals = kQ3True;
 
                /*
                 * uuValue is an x-coordinate
                 * vvValue is a  y-coordinate
                 */
                uuMin = -3.5;
                uuMax =  3.5;
                vvMin = -3.5;
                vvMax =  3.5;
                if (hasUVAttributes) {
                    uuStep = 0.35;
                    vvStep = 0.35;
                } else {
                    uuStep = 0.25;
                    vvStep = 0.25;
                }
                break;
 
            /**
             ** Steps
             **/
            case kGeometryLibraryRange_Simple+7:
            case kGeometryLibraryRange_UVGeoAttributes+7:
            case kGeometryLibraryRange_UVFaceAttributes+7:
 
                /*
                 * uuValue is an x-coordinate
                 * vvValue is a  y-coordinate
                 */
                #define kStepScale   0.2
 
                uuMin = -4.5;
                uuMax =  4.5;
                uuStep = 0.5;
 
                vvMin = -4.5;
                vvMax =  4.5;
                vvStep = 0.5;
                break;
 
            default:
                DebugStr("\pWarning:  NewLibraryTriGrid ; unknown flat trigrid index");
                return NULL;
                break;
            }
 
 
            /* Setup TQ3TriGridData */
            data.numRows        = (unsigned long) ((vvMax - vvMin) / vvStep) + 1;
            data.numColumns     = (unsigned long) ((uuMax - uuMin) / uuStep) + 1;
            data.facetAttributeSet  = NULL;
            data.triGridAttributeSet= NULL;
 
            data.vertices = (TQ3Vertex3D *) NewPtrClear (data.numRows * data.numColumns * sizeof(TQ3Vertex3D));
            if (data.vertices == NULL)
            {
                ERROR_DEBUG_STR("Error:  NewLibraryTriGrid ; Out of memory for triGrid vertices, geometry library number");
                return NULL;
            }
 
            /* Set trigrid vertices and shading UVs, if it hasUVAttributes */
            vertices = data.vertices;
            i = 0;
            for (vvValue = vvMin; vvValue <= vvMax; vvValue += vvStep)
            {
                for (uuValue = uuMin; uuValue <= uuMax; uuValue += uuStep)
                {
                    switch (num)
                    {
                    /* Flat 5x5 */
                    case kGeometryLibraryRange_Simple+0:
                    case kGeometryLibraryRange_UVGeoAttributes+0:
                    case kGeometryLibraryRange_UVFaceAttributes+0:
                        vertices[i].point.x = uuValue;
                        vertices[i].point.y = vvValue;
                        vertices[i].point.z = 0;
                    break;
 
                    /* Splash */
                    case kGeometryLibraryRange_Simple+3:
                    case kGeometryLibraryRange_UVGeoAttributes+3:
                    case kGeometryLibraryRange_UVFaceAttributes+3:
                        vertices[i].point.x = uuValue;
                        vertices[i].point.y = vvValue;
                        vertices[i].point.z = 1.5 * uMath_Sin_Deg(48.0 * uuValue * vvValue) *
                                                    uMath_Cos_Deg(48.0 * uuValue * vvValue);
                    break;
 
                    /* Steps */
                    case kGeometryLibraryRange_Simple+7:
                    case kGeometryLibraryRange_UVGeoAttributes+7:
                    case kGeometryLibraryRange_UVFaceAttributes+7:
                        vertices[i].point.x = uuValue;
                        vertices[i].point.y = vvValue;
                        vertices[i].point.z = kStepScale * (((long) uuValue) % ((data.numRows-1)/2)) *
                                                           (((long) vvValue) % ((data.numColumns-1)/2));
                    break;
                    }
 
                    if (hasUVAttributes)
                    {
                        if (mHasUVGeoAttributes(num))
                        {
                            param2D.u = (uuValue - uuMin) / (uuMax - uuMin);
                            param2D.v = (vvValue - vvMin) / (vvMax - vvMin);
                        }
                        else /* mHasUVFaceAttributes */
                        {
                            param2D.u = i % data.numColumns;
                            param2D.v = i / data.numColumns;
                        }
 
                        vertices[i].attributeSet = Q3AttributeSet_New();
                        if (vertices[i].attributeSet != NULL)
                            Q3AttributeSet_Add(vertices[i].attributeSet, kQ3AttributeTypeShadingUV, &param2D);
                    }
                    else
                        vertices[i].attributeSet = NULL;
 
                    i++;
                }
            }
 
            if (addNormals)
                AddVertexNormals(num, &data);
 
            geometryObject = Q3TriGrid_New (&data);
 
            if (hasUVAttributes)
            {
                for (i = 0; i < data.numRows * data.numColumns; i++)
                {
                    if (vertices[i].attributeSet != NULL)
                        Q3Object_Dispose(vertices[i].attributeSet);
                }
            }
            DisposePtr ((void *) vertices);
        }
        break;
 
        case kGeometryLibraryRange_Simple+1:        /* Torus */
        case kGeometryLibraryRange_UVGeoAttributes+1:
        case kGeometryLibraryRange_UVFaceAttributes+1:
        case kGeometryLibraryRange_Simple+2:        /* Wavey Torus */
        case kGeometryLibraryRange_UVGeoAttributes+2:
        case kGeometryLibraryRange_UVFaceAttributes+2:
        case kGeometryLibraryRange_Simple+4:        /* Sphere */
        case kGeometryLibraryRange_UVGeoAttributes+4:
        case kGeometryLibraryRange_UVFaceAttributes+4:
        case kGeometryLibraryRange_Simple+5:        /* Cone */
        case kGeometryLibraryRange_UVGeoAttributes+5:
        case kGeometryLibraryRange_UVFaceAttributes+5:
        case kGeometryLibraryRange_Simple+6:        /* Pipe */
        case kGeometryLibraryRange_UVGeoAttributes+6:
        case kGeometryLibraryRange_UVFaceAttributes+6:
        case kGeometryLibraryRange_Simple+8:        /* Spring */
        case kGeometryLibraryRange_UVGeoAttributes+8:
        case kGeometryLibraryRange_UVFaceAttributes+8:
        {
            /* Setup min, max and step values trigrid vertex u & v values */
            switch (num)
            {
            /**
             ** Torus
             **/
            case kGeometryLibraryRange_Simple+1:
            case kGeometryLibraryRange_UVGeoAttributes+1:
            case kGeometryLibraryRange_UVFaceAttributes+1:
            /**
             ** Wavey Torus
             **/
            case kGeometryLibraryRange_Simple+2:
            case kGeometryLibraryRange_UVGeoAttributes+2:
            case kGeometryLibraryRange_UVFaceAttributes+2:
 
                addNormals = kQ3True;
 
                /*
                 * uuValue is used to generate each circular cross section about the y axis
                 * vvValue is used to revolve the circular cross section about the torus' center
                 */
                #define kTorusCrossRadius    0.5
                #define kTorusInnerRadius    1.0
                #define kTorusWaveyAmpRadius 5.0
 
                uuMin =    0.0;
                uuMax =  360.0;
                vvMin = -180.0;
                vvMax =  180.0;
                if (hasUVAttributes) {
                    uuStep = 12.0;  /* 10.0; */
                    vvStep = 12.0;  /* 10.0; */
                } else {
                    uuStep = 10.0;
                    vvStep = 10.0;
                }
                break;
 
            /**
             ** Sphere
             **/
            case kGeometryLibraryRange_Simple+4:
            case kGeometryLibraryRange_UVGeoAttributes+4:
            case kGeometryLibraryRange_UVFaceAttributes+4:
 
                addNormals = kQ3True;
 
                /*
                 * uuValue is an angle used to revolve the cross section about the y axis
                 * vvValue is an angle used to generate each half circular cross section
                 */
                uuMin =    0.0;
                uuMax =  360.0;
                vvMin = - 90.0;
                vvMax =   90.0;
                if (hasUVAttributes) {
                    uuStep =  12.0; /* 10.0; */
                    vvStep =  12.0; /* 10.0; */
                } else {
                    uuStep =   9.0;
                    vvStep =   9.0;
                }
                radius = 1.0;
                break;
 
            /**
             ** Cone
             **/
            case kGeometryLibraryRange_Simple+5:
            case kGeometryLibraryRange_UVGeoAttributes+5:
            case kGeometryLibraryRange_UVFaceAttributes+5:
 
                addNormals = kQ3True;
 
                /*
                 * vvValue is the length of the cone side from point to the edge
                 * uuValue is an angle used to revolve the cross section about the y axis
                 */
                if (hasUVAttributes) {
                    uuStep = 24.0;  /* 15.0; */
                    vvStep =  0.25;
                } else {
                    uuStep = 10.0;
                    vvStep =  0.25;
                }
                uuMin =   0.0;
                uuMax = 360.0;
                vvMin = -vvStep;
                vvMax =   2.0;
 
                radius =  0.5;
                break;
 
            /**
             ** Pipe
             **/
            case kGeometryLibraryRange_Simple+6:
            case kGeometryLibraryRange_UVGeoAttributes+6:
            case kGeometryLibraryRange_UVFaceAttributes+6:
 
                addNormals = kQ3True;
 
                /*
                 * uuValue is used to revolve the line cross section about the y axis
                 * vvValue is used to generate the length of the pipe
                 */
                if (hasUVAttributes) {
                    uuStep = 20.0;  /* 10.0; */
                    vvStep =  0.5;
                } else {
                    uuStep = 10.0;  /* 15.0; */
                    vvStep =  0.3;
                }
                uuMin =   0.0;
                uuMax = 360.0;
                vvMin = - 1.5;
                vvMax =   1.5;
#ifdef  FINISH_THIS
                vvMin = - 1.5 - vvStep;
                vvMax =   1.5 + vvStep;
#else   /* FINISH_THIS */
                vvMin = - 1.5;
                vvMax =   1.5;
#endif  /* FINISH_THIS */
                radius =  1.0;
                break;
 
            /**
             ** Spring
             **/
            case kGeometryLibraryRange_Simple+8:
            case kGeometryLibraryRange_UVGeoAttributes+8:
            case kGeometryLibraryRange_UVFaceAttributes+8:
 
                addNormals = kQ3True;
 
                /*
                 * uuValue is used to revolve the circular cross section about the springs' axis
                 * vvValue is used to generate each circular cross section
                 */
                #define kSpringCrossRadius   0.25
                #define kSpringInnerRadius   1.0
                #define kSpringSpacing       3.5*kSpringCrossRadius
 
                uuMin = - 90.0;
                uuMax =  810.0;
                vvMin = -180.0;
                vvMax =  180.0;
                if (hasUVAttributes) {
                    uuStep = 15.0;  /* 30.0; */
                    vvStep = 30.0;
                } else {
                    uuStep = 15.0;  /* 20.0; */
                    vvStep = 15.0;
                }
                break;
 
            default:
                DebugStr("\pWarning: NewLibraryTriGrid ; unknown revolved trigrid index");
                return NULL;
                break;
            }
 
 
            /* Setup TQ3TriGridData */
            data.numRows        = (unsigned long) ((vvMax - vvMin) / vvStep) + 1;
            data.numColumns     = (unsigned long) ((uuMax - uuMin) / uuStep) + 1;
            data.facetAttributeSet  = NULL;
            data.triGridAttributeSet= NULL;
 
            data.vertices = (TQ3Vertex3D *) NewPtrClear (data.numRows * data.numColumns * sizeof(TQ3Vertex3D));
            if (data.vertices == NULL)
            {
                ERROR_DEBUG_STR("Error:  NewLibraryTriGrid ; Out of memory for triGrid vertices.");
                return NULL;
            }
 
            /* Set trigrid vertices and shading UVs, if it hasUVAttributes */
            vertices = data.vertices;
            i = 0;
            for (vvValue = vvMin; vvValue <= vvMax; vvValue += vvStep)
            {
                for (uuValue = uuMax; uuValue >= uuMin; uuValue -= uuStep)
                {
                    switch (num)
                    {
                    /* Torus */
                    case kGeometryLibraryRange_Simple+1:
                    case kGeometryLibraryRange_UVGeoAttributes+1:
                    case kGeometryLibraryRange_UVFaceAttributes+1:
                        vertices[i].point.x = (uMath_Cos_Deg(vvValue) * kTorusCrossRadius + kTorusInnerRadius) * uMath_Cos_Deg(uuValue);
                        vertices[i].point.y =  uMath_Sin_Deg(vvValue) * kTorusCrossRadius;
                        vertices[i].point.z = (uMath_Cos_Deg(vvValue) * kTorusCrossRadius + kTorusInnerRadius) * uMath_Sin_Deg(uuValue);
                    break;
 
                    /* Wavey Torus */
                    case kGeometryLibraryRange_Simple+2:
                    case kGeometryLibraryRange_UVGeoAttributes+2:
                    case kGeometryLibraryRange_UVFaceAttributes+2:
                        radius = kTorusCrossRadius / kTorusWaveyAmpRadius * uMath_Sin_Deg(kTorusWaveyAmpRadius * uuValue) + kTorusCrossRadius;
                        vertices[i].point.x = (uMath_Cos_Deg(vvValue) * radius + kTorusInnerRadius) * uMath_Cos_Deg(uuValue);
                        vertices[i].point.y =  uMath_Sin_Deg(vvValue) * radius;
                        vertices[i].point.z = (uMath_Cos_Deg(vvValue) * radius + kTorusInnerRadius) * uMath_Sin_Deg(uuValue);
                    break;
 
                    /* Sphere */
                    case kGeometryLibraryRange_Simple+4:
                    case kGeometryLibraryRange_UVGeoAttributes+4:
                    case kGeometryLibraryRange_UVFaceAttributes+4:
                        vertices[i].point.x = uMath_Cos_Deg(vvValue) * uMath_Cos_Deg(uuValue) * radius;
                        vertices[i].point.y = uMath_Sin_Deg(vvValue) * radius;
                        vertices[i].point.z = uMath_Cos_Deg(vvValue) * uMath_Sin_Deg(uuValue) * radius;
                    break;
 
                    /* Cone */
                    case kGeometryLibraryRange_Simple+5:
                    case kGeometryLibraryRange_UVGeoAttributes+5:
                    case kGeometryLibraryRange_UVFaceAttributes+5:
                        if (vvValue < (vvMin + vvStep))
                        {
                            vertices[i].point.x = 0.0;
                            vertices[i].point.y = vvMin + vvStep;
                            vertices[i].point.z = 0.0;
                        }
                        else
                        {
                            vertices[i].point.x = (vvMax - vvValue) * uMath_Cos_Deg(uuValue) * radius;
                            vertices[i].point.y =  vvValue;
                            vertices[i].point.z = (vvMax - vvValue) * uMath_Sin_Deg(uuValue) * radius;
                        }
                    break;
 
                    /* Pipe */
                    case kGeometryLibraryRange_Simple+6:
                    case kGeometryLibraryRange_UVGeoAttributes+6:
                    case kGeometryLibraryRange_UVFaceAttributes+6:
#ifdef  FINISH_THIS
                        if (vvValue < (vvMin + vvStep))
                        {
                            vertices[i].point.x = 0.0;
                            vertices[i].point.y = vvMin + vvStep;
                            vertices[i].point.z = 0.0;
                        }
                        else
                        if (vvValue > (vvMax - vvStep))
                        {
                            vertices[i].point.x = 0.0;
                            vertices[i].point.y = vvMax - vvStep;
                            vertices[i].point.z = 0.0;
                        }
                        else
                        {
                            vertices[i].point.x = uMath_Cos_Deg(uuValue) * radius;
                            vertices[i].point.y = vvValue;
                            vertices[i].point.z = uMath_Sin_Deg(uuValue) * radius;
                        }
#else   /* FINISH_THIS */
                        vertices[i].point.x = radius * uMath_Cos_Deg(uuValue);
                        vertices[i].point.y = vvValue;
                        vertices[i].point.z = radius * uMath_Sin_Deg(uuValue);
#endif  /* FINISH_THIS */
                    break;
 
                    /* Spring */
                    case kGeometryLibraryRange_Simple+8:
                    case kGeometryLibraryRange_UVGeoAttributes+8:
                    case kGeometryLibraryRange_UVFaceAttributes+8:
                        vertices[i].point.x = (uMath_Cos_Deg(vvValue) * kSpringCrossRadius + kSpringInnerRadius) * uMath_Cos_Deg(uuValue);
                        vertices[i].point.y =  uMath_Sin_Deg(vvValue) * kSpringCrossRadius + kSpringSpacing * uuValue/360.0;
                        vertices[i].point.z = (uMath_Cos_Deg(vvValue) * kSpringCrossRadius + kSpringInnerRadius) * uMath_Sin_Deg(uuValue);
                    break;
                    }
 
                    if (hasUVAttributes)
                    {
                        if (mHasUVGeoAttributes(num))
                        {
                            param2D.u = 1.0 - ((uuValue - uuMin) / (uuMax - uuMin));
                            param2D.v = (vvValue - vvMin) / (vvMax - vvMin);
                        }
                        else /* mHasUVFaceAttributes */
                        {
                            param2D.u = i % data.numColumns;
                            param2D.v = i / data.numColumns;
                        }
 
                        vertices[i].attributeSet = Q3AttributeSet_New();
                        if (vertices[i].attributeSet != NULL)
                            Q3AttributeSet_Add(vertices[i].attributeSet, kQ3AttributeTypeShadingUV, &param2D);
                    }
                    else
                        vertices[i].attributeSet = NULL;
 
                    i++;
                }
            }
 
            if (addNormals)
                AddVertexNormals(num, &data);
 
            geometryObject = Q3TriGrid_New (&data);
 
            if (hasUVAttributes)
            {
                for (i = 0; i < data.numRows * data.numColumns; i++)
                {
                    if (vertices[i].attributeSet != NULL)
                        Q3Object_Dispose(vertices[i].attributeSet);
                }
            }
            DisposePtr ((void *) vertices);
        }
        break;
 
        default:
            DebugStr("\pWarning:  NewLibraryTriGrid ; index unimplemented ");
            break;
    }
 
    return geometryObject;
}
 
 
void AddVertexNormals(
    unsigned long   geoNum,
    TQ3TriGridData  *pTriData)
{
    TQ3Vector3D     vector3D;
    TQ3Point3D      point3D;
    TQ3Vertex3D     *pVertices;
    unsigned long   i,
                    j,
                    k;
 
    pVertices = pTriData->vertices;
 
    if (
        /* Torus */
        geoNum == kGeometryLibraryRange_Simple+1            ||
        geoNum == kGeometryLibraryRange_UVGeoAttributes+1   ||
        geoNum == kGeometryLibraryRange_UVFaceAttributes+1  ||
        /* Wavey Torus */
        geoNum == kGeometryLibraryRange_Simple+2            ||
        geoNum == kGeometryLibraryRange_UVGeoAttributes+2   ||
        geoNum == kGeometryLibraryRange_UVFaceAttributes+2  ||
        /* Spring */
        geoNum == kGeometryLibraryRange_Simple+8            ||
        geoNum == kGeometryLibraryRange_UVGeoAttributes+8   ||
        geoNum == kGeometryLibraryRange_UVFaceAttributes+8
        )
    {
        unsigned long   i2;
 
        i2 = pTriData->numColumns * (pTriData->numRows/2);
 
        #define p1  pVertices[i].point
        #define p2  pVertices[i2].point
 
        i = 0;
        for (k = 0; k < pTriData->numRows / 2; k++)
        {
            for (j = 0; j < pTriData->numColumns; j++)
            {
                /* Create an attribute set for non-textured geometry */
                if (pVertices[i ].attributeSet == NULL)
                    pVertices[i ].attributeSet = Q3AttributeSet_New();
                if (pVertices[i2].attributeSet == NULL)
                    pVertices[i2].attributeSet = Q3AttributeSet_New();
 
                switch (geoNum)
                {
                /* Torus */
                case kGeometryLibraryRange_Simple+1:
                case kGeometryLibraryRange_UVGeoAttributes+1:
                case kGeometryLibraryRange_UVFaceAttributes+1:
                /* Wavey Torus */
                case kGeometryLibraryRange_Simple+2:
                case kGeometryLibraryRange_UVGeoAttributes+2:
                case kGeometryLibraryRange_UVFaceAttributes+2:
                /* Spring */
                case kGeometryLibraryRange_Simple+8:
                case kGeometryLibraryRange_UVGeoAttributes+8:
                case kGeometryLibraryRange_UVFaceAttributes+8:
                    /* point3D is midpoint */
                    Q3Point3D_Set (&point3D,
                        (uMath_Fabs(p2.x - p1.x) / 2) + uMath_Min(p1.x, p2.x),
                        (uMath_Fabs(p2.y - p1.y) / 2) + uMath_Min(p1.y, p2.y),
                        (uMath_Fabs(p2.z - p1.z) / 2) + uMath_Min(p1.z, p2.z)
                        );
                    Q3Point3D_Subtract(&pVertices[i].point, &point3D, &vector3D);
                break;
                }
 
                if ((i % pTriData->numColumns) != (pTriData->numRows / 2))
                {
                    Q3Vector3D_Normalize(&vector3D, &vector3D);
                    Q3AttributeSet_Add(pVertices[i].attributeSet, kQ3AttributeTypeNormal, &vector3D);
                }
 
                Q3Vector3D_Negate(&vector3D, &vector3D);
                Q3Vector3D_Normalize(&vector3D, &vector3D);
                Q3AttributeSet_Add(pVertices[i2].attributeSet, kQ3AttributeTypeNormal, &vector3D);
                i++;
                i2++;
            }
        }
 
        #undef  p1
        #undef  p2
 
        return;
    }
    else
    if (
        /* Splash */
        geoNum == kGeometryLibraryRange_Simple+3            ||
        geoNum == kGeometryLibraryRange_UVGeoAttributes+3   ||
        geoNum == kGeometryLibraryRange_UVFaceAttributes+3
        )
    {
        TQ3Point3D      point3Dc,
                        point3Dr;
 
        /*  C = numColumns
         *  i = vertex index
         *
         *      i+C
         *
         *  i-1  i  i+1
         *
         *      i-C
         */
        #define pL  pVertices[i-1].point
        #define pR  pVertices[i+1].point
        #define pT  pVertices[i+pTriData->numColumns].point
        #define pB  pVertices[i-pTriData->numColumns].point
        #define pH  point3Dc
        #define pV  point3Dr
 
        i = pTriData->numColumns + 1;
        for (k  = 1;
             k <= pTriData->numRows - 2;
             k++)
        {
            for (j  = 1;
                 j <= pTriData->numColumns - 2;
                 j++)
            {
                /* Create an attribute set for non-textured geometry */
                if (pVertices[i].attributeSet == NULL)
                    pVertices[i].attributeSet = Q3AttributeSet_New();
 
                /* point3Dc, point3Dr, point3D are midpoints */
 
                /* i-1 and i+1 */
                Q3Point3D_Set (&point3Dc,
                    (uMath_Fabs(pR.x - pL.x) / 2) + uMath_Min(pL.x, pR.x),
                    (uMath_Fabs(pR.y - pL.y) / 2) + uMath_Min(pL.y, pR.y),
                    (uMath_Fabs(pR.z - pL.z) / 2) + uMath_Min(pL.z, pR.z)
                    );
 
                /* i-C and i+C */
                Q3Point3D_Set (&point3Dr,
                    (uMath_Fabs(pT.x - pB.x) / 2) + uMath_Min(pB.x, pT.x),
                    (uMath_Fabs(pT.y - pB.y) / 2) + uMath_Min(pB.y, pT.y),
                    (uMath_Fabs(pT.z - pB.z) / 2) + uMath_Min(pB.z, pT.z)
                    );
 
                /* point3Dc and point3Dr */
                Q3Point3D_Set (&point3D,
                    (uMath_Fabs(pH.x - pV.x) / 2) + uMath_Min(pV.x, pH.x),
                    (uMath_Fabs(pH.y - pV.y) / 2) + uMath_Min(pV.y, pH.y),
                    (uMath_Fabs(pH.z - pV.z) / 2) + uMath_Min(pV.z, pH.z)
                    );
 
                Q3Point3D_Subtract(&pVertices[i].point, &point3D, &vector3D);
                Q3Vector3D_Normalize(&vector3D, &vector3D);
                Q3AttributeSet_Add(pVertices[i].attributeSet, kQ3AttributeTypeNormal, &vector3D);
 
                i += ((i+2) % pTriData->numColumns == 0) ? 3 : 1;
            }
        }
 
        #undef  pL
        #undef  pR
        #undef  pT
        #undef  pB
        #undef  pH
        #undef  pV
 
        return;
    }
 
 
    /*
     *  Sphere
     *  Cone
     *  Pipe
     */
    i = 0;
    for (k = 0; k < pTriData->numRows; k++)
    {
        for (j = 0; j < pTriData->numColumns; j++)
        {
            /* Create an attribute set for non-textured geometry */
            if (pVertices[i].attributeSet == NULL)
                pVertices[i].attributeSet = Q3AttributeSet_New();
 
            switch (geoNum)
            {
            /* Sphere */
            case kGeometryLibraryRange_Simple+4:
            case kGeometryLibraryRange_UVGeoAttributes+4:
            case kGeometryLibraryRange_UVFaceAttributes+4:
                Q3Point3D_Set (&point3D, 0.0, 0.0, 0.0);
                Q3Point3D_Subtract(&pVertices[i].point, &point3D, &vector3D);
            break;
 
            /* Cone */
            case kGeometryLibraryRange_Simple+5:
            case kGeometryLibraryRange_UVGeoAttributes+5:
            case kGeometryLibraryRange_UVFaceAttributes+5:
                Q3Point3D_Set ( &point3D,
                                0.0,
/**/                            ((pVertices[i].point.y > kQ3RealZero)  ||
                                 (pVertices[i].point.y == kQ3RealZero  &&
                                  Q3Vector3D_Length((TQ3Vector3D *) &pVertices[i].point) == 0.5))
                                    ? pVertices[i].point.y
                                    : 100.0,
                                0.0);
                Q3Point3D_Subtract(&pVertices[i].point, &point3D, &vector3D);
            break;
 
            /* Pipe */
            case kGeometryLibraryRange_Simple+6:
            case kGeometryLibraryRange_UVGeoAttributes+6:
            case kGeometryLibraryRange_UVFaceAttributes+6:
                Q3Point3D_Set (&point3D, 0.0, pVertices[i].point.y, 0.0);
                Q3Point3D_Subtract(&pVertices[i].point, &point3D, &vector3D);
            break;
            }
 
            Q3Vector3D_Normalize(&vector3D, &vector3D);
            Q3AttributeSet_Add(pVertices[i].attributeSet, kQ3AttributeTypeNormal, &vector3D);
            i++;
        }   /* for j */
    }   /* for k */
}