File: cpw_primitives.c

/***************************************************************************/
/*                                                                         */
/*  cpw_primitives.c                                                       */
/*                                                                         */
/*    Primitives drawing interface.                                        */
/*                                                                         */
/*  Copyright 2001-2002 by                                                 */
/*  Jim Mathies, Silicon Graphics                                          */
/*                                                                         */
/*  Some of this code is copyright Silicon Graphics Inc. See the SGI       */
/*  copyright for more info. SGI code is plainly marked as such.           */
/*                                                                         */
/*  This file is part of the Cpw project, and may only be used,            */
/*  modified, and distributed under the terms of the Cpw project           */
/*  license.  By continuing to use, modify, or distribute this file        */
/*  you indicate that you have read the license and understand and         */
/*  accept it fully.                                                       */
/*                                                                         */
/*  File Platform: cross                                                   */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/*  (c) Copyright 1993, Silicon Graphics, Inc.                             */
/*                                                                         */
/*  ALL RIGHTS RESERVED                                                    */
/*                                                                         */
/*  Permission to use, copy, modify, and distribute this software          */
/*  for any purpose and without fee is hereby granted, provided            */
/*  that the above copyright notice appear in all copies and that          */
/*  both the copyright notice and this permission notice appear in         */
/*  supporting documentation, and that the name of Silicon                 */
/*  Graphics, Inc. not be used in advertising or publicity                 */
/*  pertaining to distribution of the software without specific,           */
/*  written prior permission.                                              */
/*                                                                         */
/*  THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU              */
/*  "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR          */
/*  OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF               */
/*  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  IN NO            */
/*  EVENT SHALL SILICON GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE         */
/*  ELSE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT OR                  */
/*  CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER,          */
/*  INCLUDING WITHOUT LIMITATION, LOSS OF PROFIT, LOSS OF USE,             */
/*  SAVINGS OR REVENUE, OR THE CLAIMS OF THIRD PARTIES, WHETHER OR         */
/*  NOT SILICON GRAPHICS, INC.  HAS BEEN ADVISED OF THE POSSIBILITY        */
/*  OF SUCH LOSS, HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,           */
/*  ARISING OUT OF OR IN CONNECTION WITH THE POSSESSION, USE OR            */
/*  PERFORMANCE OF THIS SOFTWARE.                                          */
/*                                                                         */
/*  US Government Users Restricted Rights                                  */
/*                                                                         */
/*  Use, duplication, or disclosure by the Government is subject to        */
/*  restrictions set forth in FAR 52.227.19(c)(2) or subparagraph          */
/*  (c)(1)(ii) of the Rights in Technical Data and Computer                */
/*  Software clause at DFARS 252.227-7013 and/or in similar or             */
/*  successor clauses in the FAR or the DOD or NASA FAR                    */
/*  Supplement.  Unpublished-- rights reserved under the copyright         */
/*  laws of the United States.  Contractor/manufacturer is Silicon         */
/*  Graphics, Inc., 2011 N.  Shoreline Blvd., Mountain View, CA            */
/*  94039-7311.                                                            */
/*                                                                         */
/*  OpenGL(TM) is a trademark of Silicon Graphics, Inc.                    */
/*                                                                         */
/***************************************************************************/

#include "cpw_primitives.h"
#include "cpw_primdata.h"
#include "cpw_error.h"

/*************************************************************************/
/*                                                                       */
/*   init and exit.                                                      */
/*                                                                       */
/*************************************************************************/

bool cpw_primitives_init( pCpw cpw ) 
{
    /* allocate space for the prims context */

    pCpwPrim ctx = (pCpwPrim)cpwmalloc( sizeof( CpwPrimitivesState ) );

    if ( ctx == 0 ) {
        cpw_error_set( cpw, cpw_error_outofmemory );
        return false;
    }
    memset( ctx, 0, sizeof( CpwPrimitivesState ) );

    /* various inits */

    ctx->quadObj = gluNewQuadric();
    ctx->scale = 1;
    cpw->ctx_primitives = (pVoid) ctx;
    return true;
}

void cpw_primitives_exit( pCpw cpw ) 
{
    if ( cpw->ctx_primitives )
    cpwfree( cpw->ctx_primitives );
}

/*************************************************************************/
/*                                                                       */
/*   set or reset a drawing option                                       */
/*                                                                       */
/*************************************************************************/

/* Instead of passing the parameters in when you draw a specific primitive,
   Cpw stores settings for use in all primitive drawing. Different options
   apply to each primitive. See the .h for more details */

bool cpwSetPrimitiveOpt( pCpw cpw, uint_32 option, float_32 value )
{
    pCpwPrim ctx = (pCpwPrim)cpw->ctx_primitives;
    
    switch( option ) {

        /* reset: destroys and recreates the glu quadratic used by some
           primitives, and resets all values to 0. */

        case CPW_PRIMOPT_RESET:
            gluDeleteQuadric( ctx->quadObj );
            memset( ctx, 0, sizeof( CpwPrimitivesState ) );
            ctx->quadObj = gluNewQuadric();
            ctx->scale = 1;

        /* values */

        case CPW_PRIMOPT_SIZE:
        ctx->size = value;            
        break;

        case CPW_PRIMOPT_RADIUS:
        ctx->radius = value;
        break;

        case CPW_PRIMOPT_INNERRADIUS:
        ctx->innerradius = value;
        break;

        case CPW_PRIMOPT_OUTERRADIUS:
        ctx->outerradius = value;
        break;

        case CPW_PRIMOPT_BASERADIUS:
        ctx->baseradius = value;
        break;

        case CPW_PRIMOPT_TOPRADIUS:
        ctx->topradius = value;
        break;

        case CPW_PRIMOPT_WIDTH:
        ctx->width = value;
        break;

        case CPW_PRIMOPT_HEIGHT:
        ctx->height = value;
        break;

        case CPW_PRIMOPT_SLICES:
        ctx->slices = (uint_32)value;
        break;

        case CPW_PRIMOPT_STACKS:
        ctx->stacks = (uint_32)value;
        break;

        case CPW_PRIMOPT_SCALE:
        ctx->scale = (float_32)value;
        break;

        case CPW_PRIMOPT_TEXTURE:
        if ( value == 1 ) {
          ctx->texture = true;
          gluQuadricTexture( ctx->quadObj, GL_TRUE );
        } else {
          ctx->texture = false;
          gluQuadricTexture( ctx->quadObj, GL_FALSE );
        }
        break;

        default:
        cpw_error_set( cpw, cpw_error_invalidparameter );
        return false;
    }
    return true;
}

/*************************************************************************/
/*                                                                       */
/*   draw primitive helper functions                                     */
/*                                                                       */
/*************************************************************************/

/*************************************************************************/
/*   3D primitives                                                       */
/*************************************************************************/

bool cpw_primitives_3d_wire_sphere( pCpw cpw )
{
    pCpwPrim ctx = (pCpwPrim)cpw->ctx_primitives;
    gluQuadricDrawStyle( ctx->quadObj, GLU_LINE );
    gluQuadricNormals( ctx->quadObj, GLU_SMOOTH );
    gluSphere( ctx->quadObj, ctx->radius, ctx->slices, ctx->stacks );
    return true;
}

bool cpw_primitives_3d_solid_sphere( pCpw cpw )
{
    pCpwPrim ctx = (pCpwPrim)cpw->ctx_primitives;
    gluQuadricDrawStyle( ctx->quadObj, GLU_FILL );
    gluQuadricNormals( ctx->quadObj, GLU_SMOOTH );
    gluSphere( ctx->quadObj, ctx->radius, ctx->slices, ctx->stacks );
    return true;
}

bool cpw_primitives_3d_wire_cone( pCpw cpw )
{
    pCpwPrim ctx = (pCpwPrim)cpw->ctx_primitives;
    gluQuadricDrawStyle( ctx->quadObj, GLU_LINE );
    gluQuadricNormals( ctx->quadObj, GLU_SMOOTH );
    gluCylinder( ctx->quadObj, ctx->baseradius, ctx->topradius, ctx->height, ctx->slices, 
                 ctx->stacks );
    return true;
}

bool cpw_primitives_3d_solid_cone( pCpw cpw )
{
    pCpwPrim ctx = (pCpwPrim)cpw->ctx_primitives;
    gluQuadricDrawStyle( ctx->quadObj, GLU_FILL );
    gluQuadricNormals( ctx->quadObj, GLU_SMOOTH );
    gluCylinder( ctx->quadObj, ctx->baseradius, ctx->topradius, ctx->height, ctx->slices, 
                 ctx->stacks );
    return true;
}

bool cpw_primitives_3d_wire_disk( pCpw cpw )
{
    pCpwPrim ctx = (pCpwPrim)cpw->ctx_primitives;
    gluQuadricDrawStyle( ctx->quadObj, GLU_LINE );
    gluQuadricNormals( ctx->quadObj, GLU_SMOOTH );
    gluDisk( ctx->quadObj, ctx->innerradius, ctx->outerradius, ctx->slices, ctx->stacks );
    return true;
}

bool cpw_primitives_3d_solid_disk( pCpw cpw )
{
    pCpwPrim ctx = (pCpwPrim)cpw->ctx_primitives;
    gluQuadricDrawStyle( ctx->quadObj, GLU_FILL );
    gluQuadricNormals( ctx->quadObj, GLU_SMOOTH );
    gluDisk( ctx->quadObj, ctx->innerradius, ctx->outerradius, ctx->slices, ctx->stacks );
    return true;
}

/* Silicon Graphics data */

static GLfloat n[6][3] =
{
    {-1.0, 0.0, 0.0},
    {0.0, 1.0, 0.0},
    {1.0, 0.0, 0.0},
    {0.0, -1.0, 0.0},
    {0.0, 0.0, 1.0},
    {0.0, 0.0, -1.0}
};
static GLint faces[6][4] =
{
    {0, 1, 2, 3},
    {3, 2, 6, 7},
    {7, 6, 5, 4},
    {4, 5, 1, 0},
    {5, 6, 2, 1},
    {7, 4, 0, 3}
};

/* Silicon Graphics function */

void cpw_primitives_3d_drawbox( GLfloat size, GLenum type )
{
    GLfloat v[8][3];
    GLint i;

    v[0][0] = v[1][0] = v[2][0] = v[3][0] = -size / 2;
    v[4][0] = v[5][0] = v[6][0] = v[7][0] = size / 2;
    v[0][1] = v[1][1] = v[4][1] = v[5][1] = -size / 2;
    v[2][1] = v[3][1] = v[6][1] = v[7][1] = size / 2;
    v[0][2] = v[3][2] = v[4][2] = v[7][2] = -size / 2;
    v[1][2] = v[2][2] = v[5][2] = v[6][2] = size / 2;

    for (i = 5; i >= 0; i--) {
        glBegin(type);
        glNormal3fv(&n[i][0]);
        glTexCoord2d(0.0, 0.0);
        glVertex3fv(&v[faces[i][0]][0]);
        glTexCoord2d(1.0, 0.0);
        glVertex3fv(&v[faces[i][1]][0]);
        glTexCoord2d(1.0, 1.0);
        glVertex3fv(&v[faces[i][2]][0]);
        glTexCoord2d(0.0, 1.0);
        glVertex3fv(&v[faces[i][3]][0]);
        glEnd();
    }
}

bool cpw_primitives_3d_wire_cube( pCpw cpw )
{
    pCpwPrim ctx = (pCpwPrim)cpw->ctx_primitives;
    cpw_primitives_3d_drawbox( ctx->size, GL_LINE_LOOP );
    return true;
}

bool cpw_primitives_3d_solid_cube( pCpw cpw )
{
    pCpwPrim ctx = (pCpwPrim)cpw->ctx_primitives;
    cpw_primitives_3d_drawbox( ctx->size, GL_QUADS );
    return true;
}

/* Silicon Graphics function */

void cpw_primitives_3d_teapot( GLint grid, float_32 scale, GLenum type )
{
    float p[4][4][3], q[4][4][3], r[4][4][3], s[4][4][3];
    long i, j, k, l;

    glPushAttrib(GL_ENABLE_BIT | GL_EVAL_BIT);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);
    glEnable(GL_MAP2_VERTEX_3);
    glEnable(GL_MAP2_TEXTURE_COORD_2);
    glPushMatrix();
    glRotatef(270.0, 1.0, 0.0, 0.0);
    glScalef((float)0.5 * scale, (float)0.5 * scale, (float)0.5 * scale);
    glTranslatef(0.0, 0.0, -1.5);

    for (i = 0; i < 10; i++) 
    {
        for (j = 0; j < 4; j++) 
        {
            for (k = 0; k < 4; k++) 
            {
                for (l = 0; l < 3; l++) 
                {
                    p[j][k][l] = (float)cpdata[patchdata[i][j * 4 + k]][l];
                    q[j][k][l] = (float)cpdata[patchdata[i][j * 4 + (3 - k)]][l];

                    if (l == 1)
                        q[j][k][l] *= -1.0;

                    if (i < 6) {
                        r[j][k][l] = (float)
                        cpdata[patchdata[i][j * 4 + (3 - k)]][l];
                        if (l == 0)
                            r[j][k][l] *= -1.0;
                        s[j][k][l] = (float)cpdata[patchdata[i][j * 4 + k]][l];
                        if (l == 0)
                            s[j][k][l] *= -1.0;
                        if (l == 1)
                            s[j][k][l] *= -1.0;
                    }
                }
            }
        }

        glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1, 4, 2, &tex[0][0][0]);
        glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &p[0][0][0]);
        glMapGrid2f(grid, 0.0, 1.0, grid, 0.0, 1.0);
        glEvalMesh2(type, 0, grid, 0, grid);
        glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &q[0][0][0]);
        glEvalMesh2(type, 0, grid, 0, grid);

        if (i < 6) {
            glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &r[0][0][0]);
            glEvalMesh2(type, 0, grid, 0, grid);
            glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &s[0][0][0]);
            glEvalMesh2(type, 0, grid, 0, grid);
        }
    }
    glPopMatrix();
    glPopAttrib();
}

/* Silicon Graphics function */

void doughnut( GLfloat r, GLfloat R, GLint nsides, GLint rings )
{
    int i, j;
    GLfloat theta, phi, theta1;
    GLfloat cosTheta, sinTheta;
    GLfloat cosTheta1, sinTheta1;
    GLfloat ringDelta, sideDelta;

    ringDelta = (float)2.0 * M_PI / rings;
    sideDelta = (float)2.0 * M_PI / nsides;

    theta = (float)0.0;
    cosTheta = (float)1.0;
    sinTheta = (float)0.0;

    for (i = rings - 1; i >= 0; i--) {
        theta1 = theta + ringDelta;
        cosTheta1 = (float)cos(theta1);
        sinTheta1 = (float)sin(theta1);
        glBegin(GL_QUAD_STRIP);
        phi = 0.0;
        for (j = nsides; j >= 0; j--) {
            GLfloat cosPhi, sinPhi, dist;

            phi += sideDelta;
            cosPhi = (float)cos(phi);
            sinPhi = (float)sin(phi);
            dist = R + r * cosPhi;

            glNormal3f(cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi);
            glVertex3f(cosTheta1 * dist, -sinTheta1 * dist, r * sinPhi);
            glNormal3f(cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi);
            glVertex3f(cosTheta * dist, -sinTheta * dist,  r * sinPhi);
        }
        glEnd();
        theta = theta1;
        cosTheta = cosTheta1;
        sinTheta = sinTheta1;
    }
}

void cpw_primitives_3d_wiretorus( GLfloat ir, GLfloat or, GLint sides, GLint rings )
{
  glPushAttrib( GL_POLYGON_BIT );
  glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
  doughnut( ir, or, sides, rings );
  glPopAttrib();
}

void cpw_primitives_3d_solidtorus( GLfloat ir, GLfloat or, GLint sides, GLint rings )
{
  doughnut( ir, or, sides, rings );
}

/*************************************************************************/
/*   2D primitives                                                       */
/*************************************************************************/

bool cpw_primitives_2d_square( pCpw cpw, float_32 scale, GLenum type )
{
    glPushMatrix();

    glScalef( (float)scale, (float)scale, (float)1.0 );

    /* GL_LINE_LOOP/GL_QUADS */

    glBegin( type );

    glTexCoord2d(0.0, 0.0);    
    glVertex3f( 0.0, 0.0, 0.0 );
    glTexCoord2d(1.0, 0.0);
    glVertex3f( 1.0, 0.0, 0.0 );
    glTexCoord2d(1.0, 1.0);
    glVertex3f( 1.0, 1.0, 0.0 );
    glTexCoord2d(0.0, 1.0);
    glVertex3f( 0.0, 1.0, 0.0 );

    glEnd();

    glPopMatrix();
    return true;
}

bool cpw_primitives_2d_rectangle( pCpw cpw, float_32 scale, float_32 width, float_32 height, GLenum type )
{
    glPushMatrix();

    glScalef( (float)scale, (float)scale, (float)1.0 );

    /* GL_LINE_LOOP/GL_QUADS */

    glBegin( type );
    
    glTexCoord2d(0.0, 0.0);    
    glVertex3f( 0.0, 0.0, 0.0 );
    glTexCoord2d(1.0, 0.0);
    glVertex3f( width, 0.0, 0.0 );
    glTexCoord2d(1.0, 1.0);
    glVertex3f( width, height, 0.0 );
    glTexCoord2d(0.0, 1.0);
    glVertex3f( 0.0, height, 0.0 );

    glEnd();

    glPopMatrix();
    return true;
}

bool cpw_primitives_2d_line( pCpw cpw, float_32 length )
{
    glPushMatrix();

    glBegin( GL_LINE );
    
    glVertex3f( 0, 0.0, 0.0 );
    glVertex3f( length, 0.0, 0.0 );

    glEnd();

    glPopMatrix();
    return true;
}

/*************************************************************************/
/*                                                                       */
/*   draw a primitive                                                    */
/*                                                                       */
/*************************************************************************/

bool cpwDrawPrimitive( pCpw cpw, uint_32 primitive )
{
    pCpwPrim ctx = (pCpwPrim)cpw->ctx_primitives;
    switch( primitive ) {

        /* 3D primitives */

        case CPW_PRIM_3D_WIRESPHERE:
        cpw_primitives_3d_wire_sphere( cpw );
        break;

        case CPW_PRIM_3D_SOLIDSPHERE:
        cpw_primitives_3d_solid_sphere( cpw );
        break;

        case CPW_PRIM_3D_WIRECONE:
        cpw_primitives_3d_wire_cone( cpw );
        break;

        case CPW_PRIM_3D_SOLIDCONE:
        cpw_primitives_3d_solid_cone( cpw );
        break;

        case CPW_PRIM_3D_WIRECUBE:
        cpw_primitives_3d_wire_cube( cpw );
        break;

        case CPW_PRIM_3D_SOLIDCUBE:
        cpw_primitives_3d_solid_cube( cpw );
        break;

        case CPW_PRIM_3D_WIRETORUS:
        case CPW_PRIM_3D_WIREDOUGHNUT:
        cpw_primitives_3d_wiretorus( ctx->innerradius, ctx->outerradius, ctx->slices, ctx->stacks );
        break;

        case CPW_PRIM_3D_SOLIDTORUS:
        case CPW_PRIM_3D_SOLIDDOUGHNUT:
        cpw_primitives_3d_solidtorus( ctx->innerradius, ctx->outerradius, ctx->slices, ctx->stacks );
        break;

        case CPW_PRIM_3D_WIRETEAPOT:
        cpw_primitives_3d_teapot( 7, ctx->scale, GL_LINE );
        break;

        case CPW_PRIM_3D_SOLIDTEAPOT:
        case CPW_PRIM_3D_TEAPOTAHEDRON: /* http://www.sjbaker.org/teapot/ */
        cpw_primitives_3d_teapot( 10, ctx->scale, GL_FILL );
        break;

        case CPW_PRIM_3D_WIREDISK:
        cpw_primitives_3d_wire_disk( cpw );
        break;

        case CPW_PRIM_3D_SOLIDDISK:
        cpw_primitives_3d_solid_disk( cpw );
        break;

        /* 2D primitives */

        case CPW_PRIM_2D_SQUARE:
        cpw_primitives_2d_square( cpw, ctx->scale, GL_LINE_LOOP );
        break;

        case CPW_PRIM_2D_FILLEDSQUARE:
        cpw_primitives_2d_square( cpw, ctx->scale, GL_QUADS );
        break;

        case CPW_PRIM_2D_RECTANGLE:
        cpw_primitives_2d_rectangle( cpw, ctx->scale, ctx->width, ctx->height, GL_LINE_LOOP );
        break;

        case CPW_PRIM_2D_FILLEDRECTANGLE:
        cpw_primitives_2d_rectangle( cpw, ctx->scale, ctx->width, ctx->height, GL_QUADS );
        break;

        case CPW_PRIM_2D_LINE:
        cpw_primitives_2d_line( cpw, ctx->width );
        break;

        default:
        cpw_error_set( cpw, cpw_error_invalidparameter );
        return false;
    }
    return true;
}