#if 0
//<copyright>
// 
// Copyright (c) 1992,93,94,95
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
// 
//</copyright>
#endif

/*
 * File:     ge3d_gl.c
 *
 * Authors:  Michael Hofer (until May 92) and Michael Pichler
 *
 * Created:   1 Apr 92
 *
 * Changed:  14 Nov 95
 *
 * $Id: ge3d_gl.C,v 1.5 1996/01/30 13:40:30 mpichler Exp $
 *
 */


/*
 * Implementation of Graphics Engine 3D
 *
 * for SGI's GL graphics library (IrisGL).
 *
 * note: this code gets no longer upgraded by new features,
 * just the prototypes are upgraded where needed to be able to comple this library.
 * Use the OpenGL implementation instead to get the full range of features.
 *
 */


#include "ge3drc.h"
const char* what_ge3d_1_0_4_8 =
  "@(#)[HG-LIB] ge3d\t1.0 [3D Graphics Engine for IrisGL] [Michael Pichler]";


#include "ge3d.h"



/* switch between ANSI C function prototypes and old C function declarations */


#ifdef GE3D_PROTOTYPES

#  define PARMS1(t1,p1)                                 (t1 p1)
#  define PARMS2(t1,p1,t2,p2)                           (t1 p1, t2 p2)
#  define PARMS3(t1,p1,t2,p2,t3,p3)                     (t1 p1, t2 p2, t3 p3)
#  define PARMS4(t1,p1,t2,p2,t3,p3,t4,p4)               (t1 p1, t2 p2, t3 p3, t4 p4)
#  define PARMS5(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5)         (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5)
#  define PARMS6(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5,t6,p6) \
   (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6)
#  define PARMS7(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5,t6,p6,t7,p7) \
   (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7)
#  define PARMS8(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5,t6,p6,t7,p7,t8,p8) \
   (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8)
#  define PARMS9(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5,t6,p6,t7,p7,t8,p8,t9,p9) \
   (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8, t9 p9)
#  define PARMS10(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5,t6,p6,t7,p7,t8,p8,t9,p9,t10,p10) \
   (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7, t8 p8, t9 p9, t10 p10)

#else

#  define PARMS1(t1,p1)                                 (p1) t1 p1;
#  define PARMS2(t1,p1,t2,p2)                           (p1, p2) t1 p1; t2 p2;
#  define PARMS3(t1,p1,t2,p2,t3,p3)                     (p1, p2, p3) t1 p1; t2 p2; t3 p3;
#  define PARMS4(t1,p1,t2,p2,t3,p3,t4,p4)               (p1, p2, p3, p4) t1 p1; t2 p2; t3 p3; t4 p4;
#  define PARMS5(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5)         (p1, p2, p3, p4, p5) t1 p1; t2 p2; t3 p3; t4 p4; t5 p5;
#  define PARMS6(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5,t6,p6) \
   (p1, p2, p3, p4, p5, p6) t1 p1; t2 p2; t3 p3; t4 p4; t5 p5; t6 p6;
#  define PARMS7(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5,t6,p6,t7,p7) \
   (p1, p2, p3, p4, p5, p6, p7) t1 p1; t2 p2; t3 p3; t4 p4; t5 p5; t6 p6; t7 p7;
#  define PARMS8(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5,t6,p6,t7,p7,t8,p8) \
   (p1, p2, p3, p4, p5, p6, p7, p8) t1 p1; t2 p2; t3 p3; t4 p4; t5 p5; t6 p6; t7 p7; t8 p8;
#  define PARMS9(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5,t6,p6,t7,p7,t8,p8,t9,p9) \
   (p1, p2, p3, p4, p5, p6, p7, p8, p9) t1 p1; t2 p2; t3 p3; t4 p4; t5 p5; t6 p6; t7 p7; t8 p8; t9 p9;
#  define PARMS10(t1,p1,t2,p2,t3,p3,t4,p4,t5,p5,t6,p6,t7,p7,t8,p8,t9,p9,t10,p10) \
   (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) t1 p1; t2 p2; t3 p3; t4 p4; t5 p5; t6 p6; t7 p7; t8 p8; t9 p9; t10 p10;

#endif









#include <gl/gl.h>

#include <stdio.h>
#include <math.h>
#include <unistd.h>



static int ge3d_mode = ge3d_wireframe;   /* default drawing mode: wireframe */
static long ge3d_overlay_drawmode = NORMALDRAW;  /* no overlays by default */
static int ge3d_depthbuffering = 1;  /* use Z-Buffer for hidden-line/surface-elimination */
static int ge3d_backfaceculling = 1;  /* do backface culling */

Matrix ge3d_tm;                           /* transformation matrix */

/* static float rgb_black [3] = { 0.0, 0.0, 0.0 }; */
/* static float rgb_gray [3] = { 0.3, 0.3, 0.3 }; */
static float fill_color [3] = { 1.0, 1.0, 1.0 };
static float line_color [3] = { 1.0, 1.0, 1.0 };
static float backg_color [3] = { 0.0, 0.0, 0.0 };
static int samelfcolor = 1;  /* same line and fill color */
static float null_array [] = { LMNULL };
static float twosided_lmodel [] = { TWOSIDE, 1.0, LMNULL };

static Matrix ge3d_identitymat = 
{ { 1.0, 0.0, 0.0, 0.0 },
  { 0.0, 1.0, 0.0, 0.0 },
  { 0.0, 0.0, 1.0, 0.0 },
  { 0.0, 0.0, 0.0, 1.0 }
};

/* #define HL_OFFSET 0.0025 */
/* offset for pseudo hidden line; 0.002 to 0.003 seems fine */

#define HL_FACTOR 1.007
/* translation factor for pseudo hidden line; somewhere between 1.001 and 1.01 */


#define assigncolor( c, r, g, b ) \
(c) [0] = (r),  (c) [1] = (g),  (c) [2] = b

/* modify z translation for pseudo hidden line */
/* macro needs a float variable oldztran to save the old value */

#define BEGINPOLYGONOFFSET(oldztran)  \
  mmode (MPROJECTION);  \
  getmatrix (ge3d_tm);  \
  oldztran = ge3d_tm [3][2];  \
  ge3d_tm [3][2] *= HL_FACTOR;  \
  loadmatrix (ge3d_tm);  \
  mmode (MVIEWING);


#define ENDPOLYGONOFFSET(oldztran)  \
  mmode (MPROJECTION);  \
  ge3d_tm [3][2] = oldztran;  \
  loadmatrix (ge3d_tm);  \
  mmode (MVIEWING);



void ge3d_openwindow ()
{
  /* open gl */
  prefsize (800, 600);  /* prefposition (100, 900, 100, 700); */
  winopen ("gl");

  RGBmode ();  /* RGB color mode */
  doublebuffer ();  /* double buffering, drawing on invisible page, ge3d_swapbuffers displays picture */
  gconfig ();

  c3f (backg_color);  clear ();
  swapbuffers ();  
  c3f (backg_color);  clear ();  /* clear both visible and active page */

  /* set default camera */
  mmode (MPROJECTION);
  perspective (460, 1.33333, 1, 10000);
  lookat (0, 0, 25, 0, 0, 15, 0);
  mmode (MVIEWING);

  ge3d_init_ ();

} /* ge3d_openwindow */



void ge3d_init_ ()
{
  mmode (MVIEWING);  /* multi matrix mode */

  c3f (fill_color);  /* default color */

  ge3d_mode = ge3d_wireframe;
  shademodel (FLAT);                                 /* no smoothing */
  backface (FALSE);                                  /* no backface removal */
  zfunction (ZF_LEQUAL);                             /* default under GL, but not under OpenGL */
  zbuffer (FALSE);                                   /* no hidden surface elimination */

  /* define default material and lightmodel for shading */
  lmdef (DEFMATERIAL, 1, 1, null_array);
  lmdef (DEFMATERIAL, 2, 1, null_array);  /* for ge3dDefaultMaterial */
  lmdef (DEFLMODEL, 1, 1, null_array);  /* single-sided lighting */
  lmdef (DEFLMODEL, 2, 3, twosided_lmodel);  /* double-sided lighting */
  lmbind (LMODEL, 1);  /* single-sided */
  lmbind (MATERIAL, 0);
  /* lighting calculations are switched on/off with lmbind (MATERIAL, 1 or 0); */

} /* ge3d_init_ */


/* ge3dHint */
/* flags that allow modification of ge3d behaviour */

void ge3dHint PARMS2 (
  int, flag,
  int, value
)
{
  switch (flag)
  {
    case hint_depthbuffer:
      /* must be set before activating a drawing mode to be effective */
      ge3d_depthbuffering = value;
    break;

    case hint_backfaceculling:
      /* may be changed any time */
      if (ge3d_backfaceculling != value)
      {
        ge3d_backfaceculling = value;
        if (value && ge3d_mode != ge3d_wireframe)
        { backface (TRUE);  /* backface removal */
          lmbind (LMODEL, 1);  /* single-sided */
        }
        else
        { backface (FALSE);  /* no backface removal */
          lmbind (LMODEL, 2);  /* double-sided */
        }
      }
    break;

    /* TODO: hint_lighting */
  }
}



void ge3d_clearscreen ()
{
  c3f (backg_color);  clear ();
  if ((ge3d_mode != ge3d_wireframe) && ge3d_depthbuffering)
    zclear ();
}



void ge3d_swapbuffers ()
{
  swapbuffers ();  /* display actual picture, draw next invisible */
  /* causes no error if double buffering is not used */
}



void ge3d_moveto PARMS3(
float, x,
float, y,
float, z
)
{
  move (x, y, z);
}


void ge3dMoveTo PARMS1(
const point3D*, p
)
{
  move (p->x, p->y, p->z);
}



void ge3d_lineto PARMS3(
float, x,
float, y,
float, z
)
{
  draw (x, y, z);
}


void ge3dLineTo PARMS1(
const point3D*, p
)
{
  draw (p->x, p->y, p->z);
}


void ge3d_line PARMS6(  /* draw a line */
  float, x0,
  float, y0,
  float, z0,
  float, x1,
  float, y1,
  float, z1
)
{
  move (x0, y0, z0);
  draw (x1, y1, z1);
}


void ge3dLine PARMS2(  /* draw a line */
  const point3D*, p,
  const point3D*, q
)
{
  move (p->x, p->y, p->z);
  draw (q->x, q->y, q->z);
}


/* wirecube: wireframe of an aligned cube */
/* x0, x1 etc. need not be ordered */

void ge3d_wirecube PARMS6(
float, x0,
float, y0,
float, z0,
float, x1,
float, y1,
float, z1
)
{
  /* bottom */
  move (x0, y0, z0);
  draw (x0, y1, z0);
  draw (x1, y1, z0);
  draw (x1, y0, z0);
  draw (x0, y0, z0);
  /* top */
  draw (x0, y0, z1);
  draw (x0, y1, z1);
  draw (x1, y1, z1);
  draw (x1, y0, z1);
  draw (x0, y0, z1);
  /* remaining edges */
  move (x0, y1, z0);  draw (x0, y1, z1);
  move (x1, y1, z0);  draw (x1, y1, z1);
  move (x1, y0, z0);  draw (x1, y0, z1);
}



/* ge3dPolyLines2D (float* p)
   draws a sequence of (open) polylines,
   i.e. arbitrary many polylines in sequence,
   terminated with (float) 0 (!);
   each polyline begins with the number of points, followed
   by that many pairs of (x, y) coordinates;
   since the polyline is not automatically closed,
   only lines with two or more points make sense;
   all line attributes apply -
   line color should be set immediately before call
*/


void ge3dPolyLines2D (float* p)
{
  register int n;

  while ((n = (int) *p++) != 0)
  {
    bgnline ();
    while (n--)  /* n points */
    { v2f (p);
      p += 2;
    }
    endline ();  /* line is not closed */
  }
}



/* cube: solid cube, arguments: two opposite vertices */

void ge3dCube PARMS2(
const point3D*, p,  /* two opposite vertices, orderd (p.x <= q.x) etc. */
const point3D*, q   /* otherwise wrong normal vectors */
)
{
  point3D vertex [8];  /* 8 vertices */
  point3D* vptr;
  int vindex [4];  /* 4 vertices per face */
  vector3D normal;
  register unsigned int i;

  if (ge3d_mode == ge3d_wireframe)
  {
    ge3dWireCube (p, q);
    return;
  }

  /* compute vertices */
  for (i = 0, vptr = vertex;  i < 8;  i++, vptr++)
    init3D (*vptr, (i & 4) ? q->x : p->x, (i & 2) ? q->y : p->y, (i & 1) ? q->z : p->z);

  /* somewhat inefficient, because each polygon is drawn per se */
#define cubeface(nx, ny, nz, v0, v1, v2, v3)  \
  init3D (normal, nx, ny, nz);  \
  vindex [0] = v0,  vindex [1] = v1,  vindex [2] = v2, vindex [3] = v3;  \
  ge3d_polygon (vertex, 4, vindex, 0, 0, 0, &normal)
  
  /* 6 faces (provide face but no vertex normals) */
  /* for each face normal vector and vertex indices */
  cubeface ( 1,  0,  0,  4, 6, 7, 5);
  cubeface (-1,  0,  0,  0, 1, 3, 2);
  cubeface ( 0,  1,  0,  2, 3, 7, 6);
  cubeface ( 0, -1,  0,  0, 4, 5, 1);
  cubeface ( 0,  0,  1,  1, 5, 7, 3);
  cubeface ( 0,  0, -1,  0, 2, 6, 4);
#undef cubeface
} /* ge3dCube */



/* ShadedPolygon:
   polygon with color for each vertex,
   the polygon is filled (regardless of current mode),
   fillcolor and linecolor have no influence;
   switches to gouraud shading
*/

void ge3dShadedPolygon PARMS3(
  int, nverts,
  const point3D*, vertexlist,
  const colorRGB*, colorlist
)
{
  shademodel (GOURAUD);
  bgnpolygon ();
  while (nverts--)
  { c3f ((float*) colorlist++);
    v3f ((float*) vertexlist++);
  }
  endpolygon ();
}



/* polygon */

void ge3d_polygon PARMS7(
  point3D*, vertexlist,
  int, nverts,
  int*, vertexindexlist, 
  vector3D*, normallist,
  int, nnormals,
  int*, normalindexlist,
  vector3D*, f_normal
)
{
  register int i, *v_ptr, *n_ptr;


  if (ge3d_mode == ge3d_wireframe)
  {
    c3f (line_color);
    bgnclosedline ();
    v_ptr = vertexindexlist;
    for (i = 0;  i < nverts;  i++, v_ptr++)
      v3f ((float*) (vertexlist + *v_ptr));
    endclosedline ();
    return;
  }
  else if (ge3d_mode == ge3d_hidden_line)
  {
    /* Personal Iris does not support polymodes */
    /* draw polygon in background color and line around */
    c3f (backg_color);
    bgnpolygon ();
    v_ptr = vertexindexlist;
    for (i = 0;  i < nverts;  i++, v_ptr++)
      v3f ((float*) (vertexlist + *v_ptr));
    endpolygon ();
  }
  else /* if (ge3d_mode == ge3d_flat_shading || ge3d_mode == ge3d_smooth_shading) */
  {
    bgnpolygon ();
    c3f (fill_color);
    v_ptr = vertexindexlist;
    n_ptr = normalindexlist;
    if (ge3d_mode == ge3d_flat_shading || nnormals < nverts)
    {
      if (f_normal)
        n3f ((float*) f_normal);
      for (i = 0;  i < nverts;  i++, v_ptr++, n_ptr++)
        v3f ((float*) (vertexlist + *v_ptr));
    }
    else  /* send vertices and normals */
      for (i = 0;  i < nverts;  i++, v_ptr++, n_ptr++)
      { n3f ((float*) (normallist + *n_ptr));
        v3f ((float*) (vertexlist + *v_ptr));
      }
    endpolygon ();
  }

  if (ge3d_mode == ge3d_hidden_line || !samelfcolor)  /* draw edges in linecolor */
  {
    float oldztran;

    BEGINPOLYGONOFFSET (oldztran)

    /* (no backface culling here) */
    c3f (line_color);
    bgnclosedline ();
    v_ptr = vertexindexlist;
    for (i = 0;  i < nverts;  i++, v_ptr++)
      v3f ((float*) (vertexlist + *v_ptr));
    endclosedline ();

    ENDPOLYGONOFFSET (oldztran)

    /* linewidth (1); */
  }

} /* ge3d_polygon */



/* note: GL version of ge3d does not support texturing */

void ge3dPolyhedron PARMS5(
  point3D*, vertexlist,
  vector3D*, normallist,
  point2D*, texvertlist,
  int, numfaces,
  face*, facelist
)
{
  register unsigned i;
  register face* faceptr;

  /* this loop would work always, but for efficiency drawing for each mode is done below */
/*
  for (i = 0, faceptr = facelist;  i < numfaces;  i++, faceptr++)
  {
    ge3d_polygon (vertexlist, faceptr->num_faceverts, faceptr->facevert,
                  normallist, faceptr->num_facenormals, faceptr->facenormal,
                  &faceptr->normal);
  }
  return;
*/

  if (ge3d_mode == ge3d_wireframe)
  { ge3d_wirepolyhedron (vertexlist, normallist, numfaces, facelist);
    return;
  }
  else if (ge3d_mode == ge3d_hidden_line)
  {
    register unsigned j;
    register int nverts, * v_ptr;

    c3f (backg_color);
    for (i = 0, faceptr = facelist;  i < numfaces;  i++, faceptr++)
    { nverts = faceptr->num_faceverts;
      v_ptr = faceptr->facevert;
      bgnpolygon ();
      for (j = 0;  j < nverts;  j++, v_ptr++)
	v3f ((float*) (vertexlist + *v_ptr));
      endpolygon ();
    }
  }
  else /* if (ge3d_mode == ge3d_flat_shading || ge3d_mode == ge3d_smooth_shading) */
  {
    register unsigned j;
    register int nverts, nnormals, * v_ptr, * n_ptr;

    c3f (fill_color);

    for (i = 0, faceptr = facelist;  i < numfaces;  i++, faceptr++)
    {
      nverts = faceptr->num_faceverts;
      v_ptr = faceptr->facevert;
      nnormals = faceptr->num_facenormals;
      n_ptr = faceptr->facenormal;

      bgnpolygon ();
      if (ge3d_mode == ge3d_flat_shading || nnormals < nverts)
      {
        n3f ((float*) &faceptr->normal);
	for (j = 0;  j < nverts;  j++, v_ptr++, n_ptr++)
	  v3f ((float*) (vertexlist + *v_ptr));
      }
      else  /* send vertices and normals */
	for (j = 0;  j < nverts;  j++, v_ptr++, n_ptr++)
	{ n3f ((float*) (normallist + *n_ptr));
	  v3f ((float*) (vertexlist + *v_ptr));
	}
      endpolygon ();
    }
  }


  if (ge3d_mode == ge3d_hidden_line || !samelfcolor)  /* draw edges in linecolor */
  {
    float oldztran;

    BEGINPOLYGONOFFSET (oldztran)
    /* sometimes (in rare circumstances) switching to MPROJECTION causes change of lighting (GL bug?) */

    c3f (line_color);
    ge3d_wirepolyhedron (vertexlist, normallist, numfaces, facelist);

    ENDPOLYGONOFFSET (oldztran)
  }

} /* ge3d_polyhedron */



/* ge3d_wirepolyhedron */
/* always draws a polyhedron in wireframe (independent of current mode) */

void ge3d_wirepolyhedron PARMS4(
  point3D*, vertexlist,
  vector3D*, normallist,  /* unused */
  int, numfaces,
  face*, facelist
)
{
  register unsigned i, j;
  register face* faceptr;
  register int nverts, * v_ptr;

  for (i = 0, faceptr = facelist;  i < numfaces;  i++, faceptr++)
  {
    nverts = faceptr->num_faceverts;
    v_ptr = faceptr->facevert;  /* vertexindexlist */
    bgnclosedline ();
    for (j = 0;  j < nverts;  j++, v_ptr++)
      v3f ((float*) (vertexlist + *v_ptr));
    endclosedline ();
  }

} /* ge3d_wirepolyhedron */



/* ge3dFaceSet -- under construction */
/* note: material bindings not yet ported to IrisGL (TODO) */
/* note: texturing not supported under IrisGL */

void ge3dFaceSet
#ifdef GE3D_PROTOTYPES
(
  const point3D* vertexlist,           /* array of vertices */
  int numcoordind,                     /* number of vertex indices */
  const int* coordindex,               /* indices into vertexlist, -1 for closing face */
  const materialsGE3D* materials,      /* material definitions */
  int matbinding,                      /* material binding (ge3d_matbinding_t) */
  int nummatind,                       /* number of material indices */
  const int* materindex,               /* indices into arrays of materlist */
  const vector3D* normallist,          /* array of vertex normal vectors (0 allowed) */
/*int normalbinding,*/
  int numnormalind,                    /* number of vertex normal indices (0 allowed) */
  const int* normalindex,              /* indices into normallist (corresponding to coordindex) */
  const vector3D* facenormals,         /* numcoordind face normals (necessary for flat shading) */
  const point2D* texvertlist,          /* array of texture vertices */
  int numtexind,                       /* number of texture vertex indices (0 allowed) */
  const int* texvertindex              /* indices into texvertlist */
)
#else
( vertexlist, numcoordind, coordindex,
  materials, matbinding, nummatind, materindex,
  normallist, numnormalind, normalindex, facenormals,
  texvertlist, numtexind, texvertindex
)
  const point3D* vertexlist;  int numcoordind;  const int* coordindex;
  const materialsGE3D* materials;  int matbinding, nummatind;  const int* materindex;
  const vector3D* normallist;  int numnormalind;  const int* normalindex;  const vector3D* facenormals;
  const point2D* texvertlist;  int numtexind;  const int* texvertindex
#endif
{
  register int cindex;

/* TODO: facenormals */
/* TODO: material binding */
/* TODO: texturing */

  if (!numcoordind)
    return;

  if (coordindex [numcoordind - 1] < 0)
    numcoordind--;  /* avoid empty bgn.../end... for last primitive (automatically closed) */

  /*fprintf (stderr, "ge3dFaceSet with %d face indices.\n", numcoordind);*/
  if (ge3d_mode == ge3d_wireframe)
  {
    bgnclosedline ();  /* closed polylines */
    while (numcoordind--)
    {
      cindex = *coordindex++;
      /*fprintf (stderr, "[%d] ", cindex);*/
      if (cindex < 0)
      { endclosedline ();
        bgnclosedline ();
      }
      else
        v3f ((float*) (vertexlist + cindex));
    }
    endclosedline ();  /* end of last polyline */
    /*fprintf (stderr, "\n");*/
  }
  else if (ge3d_mode == ge3d_hidden_line)
  {
    register int ncind = numcoordind;
    register const int* crdind = coordindex;
    float oldztran;

    c3f (backg_color);
    bgnpolygon ();
    while (ncind--)
    {
      cindex = *crdind++;
      if (cindex < 0)
      { endpolygon ();
        bgnpolygon ();
      }
      else
        v3f ((float*) (vertexlist + cindex));
    }
    endpolygon ();  /* end of last polygon */

    /* currently always same line and fill color for ge3dFaceSet */
    /* (if not do this code also on !samelfcolor at the end; take care of didlighting) */

    BEGINPOLYGONOFFSET (oldztran)

    c3f (line_color);
    bgnclosedline ();
    while (numcoordind--)
    {
      cindex = *coordindex++;
      if (cindex < 0)
      { endclosedline ();
        bgnclosedline ();
      }
      else
        v3f ((float*) (vertexlist + cindex));
    }
    endclosedline ();  /* end of last polyline */

    ENDPOLYGONOFFSET (oldztran)
  }
  else  /* if (ge3d_mode == ge3d_flat_shading || ge3d_mode == ge3d_smooth_shading) */
  {
    c3f (fill_color);
    bgnpolygon ();

    if (ge3d_mode == ge3d_flat_shading || numnormalind < numcoordind)
    { /* insufficient vertex normals or flat shading */
      /* TODO: one face normal for flat shading - binding per face */
      while (numcoordind--)
      {
        cindex = *coordindex++;
        if (cindex < 0)
        { endpolygon ();
          bgnpolygon ();
        }
        else
          v3f ((float*) (vertexlist + cindex));
      }
    }
    else /* numnormalind == numcoordind */
    { /* smooth shading: normal for each vertex */
      while (numcoordind--)
      {
        cindex = *coordindex++;
        /*fprintf (stderr, "[%d/%d] ", cindex, *normalindex);*/
        /* nindex = *normalindex++; */
        if (cindex < 0)
        { normalindex++;
          endpolygon ();
          bgnpolygon ();
        }
        else
        { n3f ((float*) (normallist + *normalindex++));
          v3f ((float*) (vertexlist + cindex));
        }
      }
      /*fprintf (stderr, "\n");*/
    }

    endpolygon ();  /* end of last polygon */
  } /* flat/smooth shading */

} /* ge3dFaceSet */



/* ge3dLineSet -- under construction */

void ge3dLineSet PARMS3(
  const point3D*, vertexlist,
  int, numcoordind,
  const int*, coordindex
/*,
  const void*, materlist,
  int, nummatind,
  const int*, materindex,
*/
)
{
  register int cindex;
  /*fprintf (stderr, "ge3dLineSet with %d polyline indices.\n", numcoordind);*/

  bgnline ();  /* open polyline */
  while (numcoordind--)
  {
    cindex = *coordindex++;
    /*fprintf (stderr, "[%d] ", cindex);*/
    if (cindex < 0)
    { endline ();
      bgnline ();
    }
    else
      v3f ((float*) (vertexlist + cindex));
  }
  endline ();  /* end of last polyline */

} /* ge3dLineSet */



/* ge3dPointSet -- under construction */

void ge3dPointSet PARMS2(
  const point3D*, points,
  int, numpts
/*,
  const void*, materlist,
  int, nummatind,
  const int*, materindex,
*/
)
{
  /* TODO: use small rectangles, 1-pixel-points are too small */
  bgnpoint ();

  while (numpts--)
    v3f ((float*) points++);

  endpoint ();

} /* ge3dPointSet */


void ge3dSphere PARMS1(
  float, radius
)
{
  /* TODO - gl has no quadric primitives; program it by hand */
#define QU_STACKS 6
#define QU_SLICES 12

  static int firstcall = 1;
  static float sinrho [QU_STACKS+1], cosrho [QU_STACKS+1];
  static float sintheta [QU_SLICES+1], costheta [QU_SLICES+1];

  int i, n = QU_STACKS;
  register int j;
  point3D vertex;

  if (firstcall)
  {
    /* compute angles on first call */
    float drho = M_PI / QU_STACKS;  /* elevation */
    float dtheta = 2.0 * M_PI / QU_SLICES;  /* azimuth */
    float angle;

    firstcall = 0;
    for (j = 0;  j <= QU_STACKS;  j++)
    { angle = j * drho - (M_PI / 2.0);
      sinrho [j] = sin (angle);
      cosrho [j] = cos (angle);
    }
    for (j = 0;  j <= QU_SLICES;  j++)
    { angle = j * dtheta;
      sintheta [j] = sin (angle);
      costheta [j] = cos (angle);
    }
  } /* precalculations */


  if (ge3d_mode == ge3d_wireframe
   || ge3d_mode == ge3d_hidden_line)  /* TODO: hidden line circle */
  {
    for (i = 1;  i < QU_STACKS;  i++)  /* horicontal circles */
    {
      bgnclosedline ();
      for (j = 0;  j <= QU_SLICES;  j++)
      { init3D (vertex, costheta [j] * cosrho [i], sinrho [i], sintheta [j] * cosrho [i]);
        scl3D (vertex, radius);
        v3f ((float*) &vertex);
      }
      endclosedline ();
    }

    for (j = 0;  j < QU_SLICES;  j++)  /* vertical half circles */
    {
      bgnline ();
      for (i = 0;  i <= QU_STACKS;  i++)
      { init3D (vertex, costheta [j] * cosrho [i], sinrho [i], sintheta [j] * cosrho [i]);
        scl3D (vertex, radius);
        v3f ((float*) &vertex);
      }
      endline ();
    }
  } /* wireframe */
  else  /* flat and smooth shading */
  {
    while (n--)  /* stack loop */
    {
      bgnqstrip ();
      for (j = 0;  j <= QU_SLICES;  j++)
      {
        init3D (vertex, costheta [j] * cosrho [n+1], sinrho [n+1], sintheta [j] * cosrho [n+1]);
        n3f ((float*) &vertex);
        scl3D (vertex, radius);
        v3f ((float*) &vertex);

        init3D (vertex, costheta [j] * cosrho [n], sinrho [n], sintheta [j] * cosrho [n]);
        n3f ((float*) &vertex);
        scl3D (vertex, radius);
        v3f ((float*) &vertex);
      }
      endqstrip ();
    }
  }

#undef QU_STACKS
#undef QU_SLICES
} /* ge3dSphere */



void ge3dCylinder PARMS5(
  float, botradius,
  float, topradius,             /* use same as botradius for a cylinder, or 0 for a cone */
  float, bottom,
  float, height,
  int, parts                    /* cyl_all or combination of cyl_sides/bottom/top */
)
{
  /* TODO - gl has no quadric primitives; program it by hand */
}



/* ge3d_setmode */
/* changes the drawing mode */

void ge3d_setmode PARMS1( 
int, newmode 
)
{
  if (ge3d_mode == newmode)
    return;

  switch (newmode)
  {
    case ge3d_wireframe:
      shademodel (FLAT);                                 /* no smoothing */
      /* polymode (PYM_LINE); ... not available */
      backface (FALSE);                                  /* no backface removal */
      zbuffer (FALSE);                                   /* no hidden surface elimination */
      lmbind (MATERIAL, 0);                              /* no light calculations */
    break;  /* wireframe */

    case ge3d_hidden_line:
      if (ge3d_backfaceculling)
        backface (TRUE);                                 /* backface removal */
      if (ge3d_depthbuffering)
      { zbuffer (TRUE);                                  /* hidden line elimination */
        zclear ();
      }
      lmbind (MATERIAL, 0);                              /* no light calculations */
    break;  /* hidden line */

    case ge3d_flat_shading:
      shademodel (FLAT);                                 /* constant shading */
      if (ge3d_backfaceculling)
        backface (TRUE);                                 /* backface removal */
      if (ge3d_depthbuffering)
      { zbuffer (TRUE);                                  /* hidden surface elimination */
        zclear ();
      }
      lmbind (MATERIAL, 1);                              /* enable light calculations */
    break;  /* flat shading */

    case ge3d_smooth_shading:
    case ge3d_texturing:                                 /* treat texturing as smooth_shading */
      shademodel (GOURAUD);                              /* smooth shading */
      if (ge3d_backfaceculling)
        backface (TRUE);                                 /* backface removal */
      if (ge3d_depthbuffering)
      { zbuffer (TRUE);                                  /* hidden surface elimination */
        zclear ();
      }
      lmbind (MATERIAL, 1);                              /* enable light calculations */
    break;  /* smooth shading */
  }

  ge3d_mode = newmode;

} /* ge3d_setmode */



void ge3d_setlinecolor PARMS3(
float, r,
float, g,
float, b
)
{ 
  assigncolor (line_color, r, g, b);
  c3f (line_color);
  samelfcolor = 0;
}



void ge3dLineColor PARMS1(
const colorRGB*, c
)
{
  assigncolor (line_color, c->R, c->G, c->B);
  c3f (line_color);
  samelfcolor = 0;
}



void ge3d_setlinestyle PARMS1(
short, pattern
)
{
  if (pattern == -1)
    setlinestyle (0);  /* solid line */
  else
  { deflinestyle (1, pattern);  /* 16 bit pattern */
    setlinestyle (1);
  }
}



void ge3d_setlinewidth PARMS1(
short, width
)
{
  linewidth (width);
}


/* no support for anti-aliasing in GL version of ge3d */
int ge3dAntialiasingSupport ()
{ return 0;
}

void ge3dAntialiasing PARMS1(int, flags)  { }


void ge3d_text PARMS4(
float, x,
float, y,
float, z,
const char*, s
)
{
  cmov (x, y, z);
  charstr ((const String) s);
}


void ge3dText PARMS2(
const point3D*, p,
const char*, s
)
{
  cmov (p->x, p->y, p->z);
  charstr ((const String) s);
}



void ge3d_rect PARMS4(  /* rectangle (outline) */
float, x1,
float, y1,
float, x2,
float, y2
)
{
  rect (x1, y1, x2, y2);
}


void ge3d_rectf PARMS4(  /* filled rectangle */
float, x1,
float, y1,
float, x2,
float, y2
)
{
  rectf (x1, y1, x2, y2);
}


void ge3dFilledRect PARMS5(  /* shaded, filled rectangle */
float, x1,
float, y1,
float, x2,
float, y2,
float, z
)
{
  static float normal [3] = { 0, 0, 1 };

  /* on some architectures v3f et.al. only work for non-changing data */
  point3D a, b, c, d;
  init3D (a, x1, y1, z);
  init3D (b, x2, y1, z);
  init3D (c, x2, y2, z);
  init3D (d, x1, y2, z);

  bgnpolygon ();
    n3f (normal);
    v3f ((float*) &a);
    v3f ((float*) &b);
    v3f ((float*) &c);
    v3f ((float*) &d);
  endpolygon ();
}


void ge3d_circle PARMS3(  /* circle (outline) */
float, x,
float, y,
float, r
)
{
  circ (x, y, r);
}


void ge3d_circf PARMS3(  /* filled circle */
float, x,
float, y,
float, r
)
{
  circf (x, y, r);
}



void ge3d_arc PARMS5(  /* arc (outline) */
float, x,
float, y,
float, radius,
float, startangle,     /* counterclockwise from positive x-axis, */
float, endangle        /* in degrees */
)
{
  arc (x, y, radius, (int) (startangle*10), (int) (endangle*10));
}



void ge3d_setfillcolor PARMS3(
float, r,
float, g,
float, b
)
{ 
  float mat [5];  /* material */

  assigncolor (fill_color, r, g, b);
  assigncolor (line_color, r, g, b);  /* set also linecolor */
  c3f (fill_color);
  samelfcolor = 1;

  if (ge3d_mode == ge3d_wireframe || ge3d_mode == ge3d_hidden_line)
    return;

  /* many architectures only support diffuse hardware lighting */
  mat [0] = DIFFUSE;
  mat [1] = r;
  mat [2] = g;
  mat [3] = b;
  mat [4] = LMNULL;

  lmdef (DEFMATERIAL, 1, 5, mat);
  lmbind (MATERIAL, 1);
}


void ge3dMaterial PARMS2(
  int, scope,
  const materialsGE3D*, materials
)
{
  /* TODO: scope (front/backface material; although in VRML always front and back) */
  /* TODO: transparency (alpha value) */
  float mat [64];  /* TODO: range check */
  int np = 0;
  float* matptr = mat;
  const float* c;

  if (materials->num_base)
  { /* base color as fillcolor */
    const colorRGB* basecol = materials->rgb_base;
    assigncolor (fill_color, basecol->R, basecol->G, basecol->B);
    assigncolor (line_color, basecol->R, basecol->G, basecol->B);
    c3f (fill_color);
    samelfcolor = 1;
  }

  if (ge3d_mode < ge3d_flat_shading)
    return;

  if (materials->num_ambient)
  { c = (const float*) materials->rgb_ambient;
    *matptr++ = AMBIENT;  *matptr++ = *c++;  *matptr++ = *c++;  *matptr++ = *c;
    np += 4;
  }
  if (materials->num_diffuse)
  { c = (const float*) materials->rgb_diffuse;
    *matptr++ = DIFFUSE;  *matptr++ = *c++;  *matptr++ = *c++;  *matptr++ = *c;
    np += 4;
  }
  if (materials->num_specular)
  { c = (const float*) materials->rgb_specular;
    *matptr++ = SPECULAR;  *matptr++ = *c++;  *matptr++ = *c++;  *matptr++ = *c;
    np += 4;
  }
  if (materials->num_emissive)
  { c = (const float*) materials->rgb_emissive;
    *matptr++ = EMISSION;  *matptr++ = *c++;  *matptr++ = *c++;  *matptr++ = *c;
    np += 4;
  }
  if (materials->num_shininess)  /* VRML/Inventor/ge3d: 0.0 to 1.0; GL: 0.0 to 128.0 */
  {
    *matptr++ = SHININESS;  *matptr++ = *materials->val_shininess * 128.0;
    np += 2;
  }
  *matptr = LMNULL;
  np++;
  /*fprintf (stderr, "material definition with %d entries (incl. LMNULL)\n", np);*/

  lmdef (DEFMATERIAL, 1, np, mat);
  lmbind (MATERIAL, 1);

} /* ge3dMaterial */


void ge3dDefaultMaterial ()
{
  if (ge3d_mode >= ge3d_flat_shading)
    lmbind (MATERIAL, 2);  /* default properties from ge3d_init_ */
}


void ge3d_setbackgcolor PARMS3(
float, r,
float, g,
float, b
)
{ 
  assigncolor (backg_color, r, g, b);
}



void ge3dBackgroundColor PARMS1(
const colorRGB*, c
)
{
  assigncolor (backg_color, c->R, c->G, c->B);
}



/* ge3d_set_light_source */
/* private function that sets up a light source (positional or
   directional) in camera coordinates
*/

static void ge3d_set_light_source PARMS4(
int, index,
const colorRGB*, color,
const point3D*, pos,
float, positional
)
{
  float light [10];

  light [0] = LCOLOR;
  light [1] = color->R;
  light [2] = color->G;
  light [3] = color->B;
  light [4] = POSITION;  /* no separate flag DIRECTION */
  light [5] = pos->x;
  light [6] = pos->y;
  light [7] = pos->z;
  light [8] = positional;  /* 1.0 or 0.0 */
  light [9] = LMNULL;
  /* TODO: undo spotlight settings here when ge3dSpotLight is implemented */

  /* fprintf (stderr, "setting light at position (%f, %f, %f) with intensities (%f, %f, %f)\n",
     pos->x, pos->y, pos->z, color->R, color->G, color->B);
  */

  lmdef (DEFLIGHT, index+1, 10, light);  /* user index may run from 0 (reserved under GL) */
}


/* ge3dSetLightSource */
/* arguments: index of light source, light color,
   position or direction (in world coordinates),
   positional flag (1.0 = pos., 0.0 = dir.);
   directions must be given *towards* light source,
   positions and directions are relative to camera
   (use of ge3d_setlightsource is discouraged; ge3dLightSource
   has a more flexible interface) */
/* ge3d_setlightsource no longer exists; call ge3dSetLightSource with flags
   for positional and camera light source or ge3dLightSource providing a
   transformation matrix
*/


void ge3dSetLightSource PARMS5(
int, index,
const colorRGB*, color,
const point3D*, pos,
float, positional,
int, camlgt
)
{
  point3D trfpos;

  if (!camlgt)
  {
    if (positional)
      ge3dTransformMcWc (pos, &trfpos);
    else
      ge3dTransformVectorMcWc (pos, &trfpos);
    pos = &trfpos;  /* use transformed position */
  }

  ge3d_set_light_source (index, color, pos, positional);
}

/* ge3dLightSource */
/* similar to ge3dSetLightSource, but position/orientation is not given
   as point/vector, but a transformation matrix that transforms the
   default position (0, 0, 0) or orientation (-1, 0, 0).
*/
/* note light sources must be defined and switched on again
   after each change of camera */
/* for directional light sources the default direction is
   from negative x axis towards origin ('from left') - GL
   expects a vector *towards* the light source, which is
   (-1, 0, 0) for the default direction
*/

void ge3dLightSource PARMS5(
int, index,
const colorRGB*, color,
const matrix4D, mat,
float, positional,
int, camlgt
)
{
  point3D pos;

  pushmatrix ();

  if (camlgt)  /* light position relative to current camera */
    loadmatrix (mat);
  else         /* light position in world coordinates */
    multmatrix (mat);  /* necessary for correct lighting */

  getmatrix (ge3d_tm);  

  if (positional)
  { /* ge3d_transform_mc_wc ((float) 0, (float) 0, (float) 0, &pos.x, &pos.y, &pos.z); */
    init3D (pos, ge3d_tm [3][0], ge3d_tm [3][1], ge3d_tm [3][2]);
  }
  else
  { /* ge3d_transformvector_mc_wc ((float) -1, (float) 0, (float) 0, &pos.x, &pos.y, &pos.z); */
    init3D (pos, - ge3d_tm [0][0], - ge3d_tm [0][1], - ge3d_tm [0][2]);
  }

  popmatrix ();

  ge3d_set_light_source (index, color, &pos, positional);
  /* already transformed to camera coordinates */
} /* ge3dLightSource */


void ge3dSpotLight PARMS6(
  int, index,                           /* light index */
  const colorRGB*, color,               /* color */
  const point3D*, pos,                  /* light position (in object coordinates) */
  const vector3D*, dir,                 /* spot direction - from light source away */
  float, dropoff,                       /* linear (?) attenuation */
  float, cutoffangle                    /* spread angle, degrees, range [0, 90] or 180 */
)
{
  /* TODO !!! */

} /* ge3dSpotLight */


void ge3d_switchlight PARMS2(
int, index,
int, state
)
{ 
  static int lighttarget [8] = { LIGHT0, LIGHT1, LIGHT2, LIGHT3, LIGHT4, LIGHT5, LIGHT6, LIGHT7 };

  if (index > 8)
  { fprintf (stderr, "ge3d. error: invalid light index %d.\n", index);
    return;
  }

  pushmatrix ();
  loadmatrix (ge3d_identitymat);
  lmbind (lighttarget [index], state ? index+1 : 0);  /* lmdef used index+1 */
  popmatrix ();  
}



void ge3d_transform_mc_wc PARMS6(
float, inx,
float, iny,
float, inz,
float*, outx,
float*, outy,
float*, outz
)
{
  getmatrix (ge3d_tm);  
  *outx = inx * ge3d_tm [0][0] + iny * ge3d_tm [1][0] + inz * ge3d_tm [2][0] + ge3d_tm [3][0];
  *outy = inx * ge3d_tm [0][1] + iny * ge3d_tm [1][1] + inz * ge3d_tm [2][1] + ge3d_tm [3][1];
  *outz = inx * ge3d_tm [0][2] + iny * ge3d_tm [1][2] + inz * ge3d_tm [2][2] + ge3d_tm [3][2];
}


void ge3dTransformMcWc PARMS2(
const point3D*, in,
point3D*, out
)
{
  float x = in->x, y = in->y, z = in->z;  /* beware of transform (p, p)! */
  getmatrix (ge3d_tm);  
  out->x = x * ge3d_tm [0][0] + y * ge3d_tm [1][0] + z * ge3d_tm [2][0] + ge3d_tm [3][0];
  out->y = x * ge3d_tm [0][1] + y * ge3d_tm [1][1] + z * ge3d_tm [2][1] + ge3d_tm [3][1];
  out->z = x * ge3d_tm [0][2] + y * ge3d_tm [1][2] + z * ge3d_tm [2][2] + ge3d_tm [3][2];
}



void ge3d_transformvector_mc_wc PARMS6(
float, inx,
float, iny,
float, inz,
float*, outx,
float*, outy,
float*, outz
)
{
  getmatrix (ge3d_tm);
  *outx = inx * ge3d_tm [0][0] + iny * ge3d_tm [1][0] + inz * ge3d_tm [2][0];
  *outy = inx * ge3d_tm [0][1] + iny * ge3d_tm [1][1] + inz * ge3d_tm [2][1];
  *outz = inx * ge3d_tm [0][2] + iny * ge3d_tm [1][2] + inz * ge3d_tm [2][2];
}


void ge3dTransformVectorMcWc PARMS2(
const vector3D*, in,
vector3D*, out
)
{
  float x = in->x, y = in->y, z = in->z;  /* beware of transform (v, v)! */
  getmatrix (ge3d_tm);
  out->x = x * ge3d_tm [0][0] + y * ge3d_tm [1][0] + z * ge3d_tm [2][0];
  out->y = x * ge3d_tm [0][1] + y * ge3d_tm [1][1] + z * ge3d_tm [2][1];
  out->z = x * ge3d_tm [0][2] + y * ge3d_tm [1][2] + z * ge3d_tm [2][2];
}



void ge3d_setcamera PARMS8(     /* set up a perspective camera */
const point3D*, eye,            /* camera position */
const point3D*, ref,            /* reference (lookat) point */
const vector3D*, up,            /* up vector (defaults to (0, 1, 0) on NULL) */
float, aper,			/* window height ... */
float, focal,			/* ... at distance focal */
float, aspect,			/* ratio width / height */
float, hither,			/* near and ... */
float, yon			/* ... far clipping plane (both from eye; positive) */
)
{
  float alpha;

  alpha = 2 * atan2 (aper, 2 * focal);  /* in radians */

  mmode (MPROJECTION);
  perspective ((int) (alpha * 1800 / M_PI), aspect, hither, yon);

  mmode (MVIEWING);  /* load matrix of lookat on viewing matrix stack - otherwise lighting is incorrect */
  loadmatrix (ge3d_identitymat);  /* replace old camera transformation */
  /* TODO: calculate twist in case up is given */
  lookat (eye->x, eye->y, eye->z, ref->x, ref->y, ref->z, 0);
  /* mmode (MVIEWING); */
}


void ge3dCamera PARMS8(                 /* camera (perspective/orthographic; new style) */
  int, type,                            /* cam_perspective/orthographic | cam_absolute/relative */
  const point3D*, pos,                  /* camera position */
  float, rotangle,                      /* angle of rotation (degrees) ... */
  const vector3D*, rotaxis,             /* ... around axis */
  float, height,        /* perspective: total vertical viewing angle, degrees; orthographic: window height */
  float, aspect,                        /* ratio width / height */
  float, hither,                        /* near and ... */
  float, yon                            /* ... far clipping plane (both from eye; positive) */
)                                       /* perspective: both clipping planes must be positive */
{
  if (type & cam_perspective)
  {
    mmode (MPROJECTION);  /* replace perspective matrix */
    perspective ((int) (height * 10) /*angle*/, aspect, hither, yon);
  }
  else if (type & cam_orthographic)
  {
    mmode (MPROJECTION);  /* replace perspective matrix */
    height /= 2.0;     /* half height */
    aspect *= height;  /* half width */
    ortho (-aspect, aspect, -height, height, hither, yon);
  } /* may have to place gluOrtho onto modelview matrix stack for correct lighting (?) */
  /* otherwise no change of projection matrix */

  /* we have to translate, then rotate the camera, */
  /* so apply the inverse rotation and translation to the scene */
  mmode (MVIEWING);
  if (type & cam_absolute)
    loadmatrix (ge3d_identitymat);  /* replace old camera transformation */
  if (type & (cam_absolute | cam_relative))
  { ge3dRotate (rotaxis, - rotangle * (M_PI / 180));
    translate (- pos->x, - pos->y, - pos->z);
  }
  /* otherwise no change of modelview matrix */
  /* MVIEWING */
}


void ge3d_ortho_cam PARMS7(     /* set up an orthogonal camera */
const point3D*, eye,            /* camera position */
const point3D*, ref,            /* reference (lookat) point */
const vector3D*, up,            /* up vector (defaults to (0, 1, 0) on NULL) */
float, width,			/* window width */
float, height,			/* window height */
float, hither,			/* near and ... */
float, yon			/* ... far clipping plane (both from eye; neg. allowed) */
)
{
  width /= 2;
  height /= 2;
  mmode (MPROJECTION);
  ortho (-width, width, -height, height, hither, yon);

  mmode (MVIEWING);  /* load matrix of lookat on viewing matrix stack - otherwise lighting is incorrect */
  loadmatrix (ge3d_identitymat);  /* replace old camera transformation */
  /* TODO: calculate twist in case up is given */
  lookat (eye->x, eye->y, eye->z, ref->x, ref->y, ref->z, 0);
  /* mmode (MVIEWING); */
}



void ge3d_push_matrix ()
{
  pushmatrix ();  
}


void ge3dPushIdentity ()
{
  /* push identity matrix onto the stack */
  pushmatrix ();
  loadmatrix (ge3d_identitymat);
}


void ge3dLoadIdentity ()
{
  /* replace current transformation matrix with identity matrix */
  loadmatrix (ge3d_identitymat);
}


void ge3d_push_this_matrix
#ifdef GE3D_PROTOTYPES
(const float mat [4][4])
#else
(mat)  const float mat [4][4];
#endif
{
  pushmatrix ();
  multmatrix (mat);  /* concatenate */
}


void ge3d_push_new_matrix
#ifdef GE3D_PROTOTYPES
(const float mat [4][4])
#else
(mat)  const float mat [4][4];
#endif
{
  pushmatrix ();
  loadmatrix (mat);  /* new */
}


void ge3dMultMatrix
#ifdef GE3D_PROTOTYPES
(const float mat [4][4])
#else
(mat)  const float mat [4][4];
#endif
{
  multmatrix (mat);  /* concatenate */
  /* OpenGL wants column major matrices to multiply with column vectors */
  /* IrisGL wants row major matrices to multiply with row vectors */
  /* watch the float array as you like - its the same */
  /* e.g. elements [12, 13, 14] do the translation part */
}


void ge3d_print_cur_matrix ()
{
  int i;

  getmatrix (ge3d_tm);
  for (i = 0;  i < 4;  i++)
    printf ("%13f %12f %12f %12f\n", ge3d_tm [i][0], ge3d_tm [i][1], ge3d_tm [i][2], ge3d_tm [i][3]);
}


void ge3d_get_and_pop_matrix PARMS1(
matrix4D, mat
)
{
  getmatrix (mat);
  popmatrix ();
}



void ge3d_pop_matrix ()
{
  popmatrix ();
}



void ge3d_rotate_axis PARMS2(
char, axis,
float, angle  /* in degrees */
)
{
  rot (angle, axis);
}


void ge3dRotate PARMS2(
  const vector3D*, axis,  /* rotation axis vector */
  float, angle            /* in radians, righthand */
)
{
  /* for rotation matrix see the OpenGL programming guide, App. G */
  double n;
  float x, y, z, xx, xy, xz, yy, yz, zz;
  float c, c1, s;

  x = axis->x;
  y = axis->y;
  z = axis->z;

  n = norm3D (*axis);  /* norm */
  if (!n)
    return;

  /* normalize rotation vector */
  x /= n;
  y /= n;
  z /= n;

  xx = x * x;
  xy = x * y;
  xz = x * z;
  yy = y * y;
  yz = y * z;
  zz = z * z;

  c = cos (angle);
  s = sin (angle);
  c1 = 1.0 - c;

  ge3d_tm [0][0] = c1 * xx + c;
  ge3d_tm [0][1] = c1 * xy + s * z;
  ge3d_tm [0][2] = c1 * xz - s * y;
  ge3d_tm [1][0] = c1 * xy - s * z;
  ge3d_tm [1][1] = c1 * yy + c;
  ge3d_tm [1][2] = c1 * yz + s * x;
  ge3d_tm [2][0] = c1 * xz + s * y;
  ge3d_tm [2][1] = c1 * yz - s * x;
  ge3d_tm [2][2] = c1 * zz + c;
  ge3d_tm [0][3] = ge3d_tm [1][3] = ge3d_tm [2][3] = 0.0;
  ge3d_tm [3][0] = ge3d_tm [3][1] = ge3d_tm [3][2] = 0.0;
  ge3d_tm [3][3] = 1.0;

  multmatrix (ge3d_tm);

} /* ge3dRotate */


void ge3d_translate PARMS3(
float, x,
float, y,
float, z
)
{
  translate (x, y, z);
}


void ge3dTranslate PARMS1(
const vector3D*, v
)
{
  translate (v->x, v->y, v->z);
}



void ge3d_scale PARMS4(
float, x,
float, y,
float, z,
float, all
)
{
  scale (x * all, y * all, z * all);
}


void ge3dScale PARMS1(
const float*, s    /* scaling by a point or vector makes no sense; just 3 float values */
)
{
  scale (s [0], s [1], s [2]);
}


/* texturing routines not implemented for Iris-GL */
/* dummy functions */

int ge3dTexturingSupport ()
{ return 0;  /* no texturing support */
}

int ge3dCreateTexture PARMS4(
  int, width,
  int, height,
  const void*, data,
  int, bmpformat
)
{ return 0;  /* could not create texture */
}

void ge3dFreeTexture PARMS1(int, index)  { }

void ge3dDoTexturing PARMS1(int, on)  { }

void ge3dApplyTexture PARMS1(int, index)  { }

void ge3dTexturedPolygon PARMS4(
  int, nverts,
  const point3D*, vertexlist,
  const point2D*, texturelist,
  int, index
)
{
  /* draw wireframe polygon instead, texturelist/index ignored */
  bgnclosedline ();
  while (nverts--)
    v3f ((float*) vertexlist++);
  endclosedline ();
}


/* overlay routines */

/* the following routines are NOT allowed for mixed-model programs:
   ge3dRequestOverlay, ge3dBitplanes, ge3dClearOverlay, ge3dMapColori,
   ge3dMapColorRGB (thus only ge3dColorIndex is allowed);

   the according functionality must be obtained with X-routines.
*/

/* ge3dRequestOverlay ();

   call this function once right after initialization to request
   overlay planes.
   return value: no. of bitplanes got (currently at least 2)

   when true overlay bitplanes are not available (only on 8bit entry
   systems), use pop up planes (which should normally be reserved to
   the window manager).
*/

int ge3dRequestOverlay ()
{
  int avail = (int) getgdesc (GD_BITS_OVER_SNG_CMODE);

  if (avail < 2)  /* no overlay planes - use popup planes */
  { ge3d_overlay_drawmode = PUPDRAW;
    return 2;     /* popup planes need not be configured */
  }

  /* overlay planes available */
  ge3d_overlay_drawmode = OVERDRAW;
  overlay (avail);
  gconfig ();
  return avail;
}

/* ge3dBitplanes (mode);

   determines active bitplanes that are affected by following
   commands.
   argument: (currently) either ge3d_normal_planes or ge3d_overlay_planes
*/

void ge3dBitplanes (int mode)
{
  if (mode == ge3d_overlay_planes)
    drawmode (ge3d_overlay_drawmode);  /* usually OVERDRAW */
  else
    drawmode (NORMALDRAW);
}

/* ge3dClearOverlay ();

   clears the overlay bitplanes (to the invisible color);
   also activates overlay bitplanes for following commands.
*/

void ge3dClearOverlay ()
{
  drawmode (ge3d_overlay_drawmode);
  color (0);
  clear ();
  /* overlay bitplanes remain active at this point */
}

/* ge3dMapColori (int index, short r, short g, short b);
   ge3dMapColorRGB (int index, const RGBColor*);

   define a color map entry. (r, g, b) are in range 0 to 255,
   RGB color components are defined in range 0.0 to 1.0.
   With n bitplanes color indices 0 to (1<<n)-1 are available.
   For overlay planes index 0 is reserved for "invisible".

   Note that currently normal bitplanes are always in RGB mode and
   overlay bitplanes olways in colormap (color indexed) mode. Note
   also that the functions ge3d_polygon, ge3d_polyhedron, and
   ge3dShadedPolygon may not be called in in colormap mode.
*/

void ge3dMapColori PARMS4(
int, index,
short, r,
short, g,
short, b
)
{
  mapcolor (index, r, g, b);
}

void ge3dMapColorRGB PARMS2(
int, index,
const colorRGB*, c
)
{
  mapcolor (index, (int) (c->R * 255.0), (int) (c->G * 255.0), (int) (c->B * 255.0));
}

/* ge3dColorIndex (int index);

   activate a color from the colormap. Only allowed in colormap
   mode (i.e. currently only for overlay bitplanes).
*/

void ge3dColorIndex PARMS1(
int, index
)
{
  color (index);
}


/* ge3d_close */

void ge3d_close ()
{
  sleep (10000);
  gexit ();
}
