Sommaire

BONJOUR
Musique "Invention BWV784" J.S. BACH 1685-1750 ...... Sound ANNULE ......

Tutoriel et Help

Style & CSS


README.TXT

Windows NT OpenGL(R) Release Notes

1.  OpenGL Functionality Notes
2.  Compiler warnings and include files
3.  Redistributable components for Windows 95
4.  Sample Applications

1.  Functionality Notes
-----------------------

The OpenGL API is supported on a variety of graphics hardware; the
software in this release provides support for graphics hardware including
basic emulation on any video adapter that is supported with the operating
system. This release of OpenGL will run on all supported hardware under
Windows NT 4.00 and Windows 95, including VGA and Super VGA 16 color mode
displays, and, for WIndows NT, including all RISC platforms.

The runtime libraries for Windows NT are also available bundled with the
NT workstation and server products.  The runtime libraries for Windows 95
are not bundled with the Windows 95 operating system, but application
developers may freely redistribute from this SDK along with their applications
to other Windows95 systems.  In addition, the Windows 95 libraries have been
bundled with the Windows95 operating system in the OEM system release 2, so
OEM Windows 95 systems shipping later in 1996 will begin appearing with the
OpenGL runtime libraries included.

To achieve good shaded rendering with OpenGL applications, you should use a
color graphics mode with 256 or more colors.  Reasonable shading is
possible for most OpenGL applications with 256 colors.

The Windows NT 4.0 and Windows 95 releases of OpenGL includes new functionality
and performance enhancements.  These include:

1) A complete implementation of OpenGL 1.1.  OpenGL 1.1 contains several
functions, including vertex array, polygon offset, logic ops, and several new
functions for handling textures.  The vertex array and texture calls are
particularly significant, as they may enable order of magnitude performance
improvements in some applications.

2) Overlay planes extensions.  These Microsoft OpenGL extensions permit
applications to manage and render into overlay planes where supported
in the graphics hardware.  This permits applications to bring up dialog boxes
and other UI features without overwriting 3D renderings.

3) Extended metafile support.  Applications may encapsulate OpenGL calls and
data in GDI extended metafiles.  This, together with NT 4.0 print spooler
enhancements for remote metafile rendering, makes it possible to print OpenGL
graphics at high resolution on the print server.  This feature is limited to
the Windows NT release.

4) Performance.  The software renderer has been tuned for this release.
Performance tuning has been carried out for the front end of the OpenGL
pipeline as well as for rendering particular primitives, especially
anti-aliased lines and texturing.  Software rendering is generally 2-4x
faster.

2.  Compiler warnings and include files
---------------------------------------

When developing or porting OpenGL source code for Windows NT, you may
notice certain compiler warning messages related to floating point type
conversions.

Microsoft has recognized "acceptable" type-conversion warnings and has
provided a means to ignore them in the provided OpenGL sample source files.
See the included "glos.h" header file.  When using this file, be certain
to include it at an analogous location in your source file.

3. Redistributable components for Windows 95
--------------------------------------------

The OpenGL redistributable components for Windows 95 are in the
\MSTOOLS\OPENGL95 directory.  Under this are the following subdirectories:

- DLL contains the runtime dynamic-link libraries for OpenGL and GLU.
  We recommend either of two methods for redistributing these libraries
  with your application on Windows 95 (for Windows NT, the libraries are
  bundled with the operating system and should not be redistributed):
  1) In your setup program, install these libraries in the
     application directory along with your application.  This gives you
     greater control over the version of OpenGL that your application
     will link to (an issue if other applications install other versions
     of the library), but also gives you greater responsibility for
     updating your customers' libraries if and when that is required
     to address defects, add functionality, etc.
  2) In your setup program, install these libraries in the windows
     system directory.  If you do this, you should use the Win32 setup
     API call VerInstallFile to help prevent installing an older version
     of the libraries over another application's installation of a more
     recent version of the libraries.

- MGA contains the binaries for Matrox MGA Impression Plus graphics card.
  It includes 3D DDI support for enhanced OpenGL performance in 1024x768
  at 256 color mode or 800x600 at 16bpp mode on WIndows 95.  (3D DDI is
  not supported in this release of Windows NT.)  Note that 4MB of video
  memory is required for these modes.  No 32bpp accelerated modes are
  supported at this time.  (In addition, index color mode fails buffer
  invariance conformance tests.)

- SCRSAVE contains 4 screen savers for Windows95 systems.  These should
  be copied into the windows system directory of your system.

4.  Sample Applications
-----------------------

a)  Samples included

    BOOKSample code from "OpenGL Programming Guide"

    DEMOSAUXDEMO  -  AUX Library demonstration.

BACKTRCE -  BackTrace sample ported from Silicon Graphics Irix/OpenGL.

GENGL -  A Generic Windows NT OpenGL sample application.  This is
    a great starting point for new programmers learning OpenGL.

                PGONOFFS -  Polygon offsets for efficient translation of objects.

STONEHNG -  Stonehenge application ported from SGI workstation.

                VASPHERE -  Using vertex arrays for improved performance.

    GLAUX       Source code for our AUXLIB implementation.

    SAMPLESOpenGL Sample code from SGI.  See SAMPLES\ABOUT.TXT for details.

    SCRSAVESource code for new OpenGL screen savers.

b)  Disclaimer for Sample Applications that contain SGI copyright notice

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(R) is a trademark of Silicon Graphics, Inc.
Windows is a registered trademark of Microsoft Corporation.


ACCANTI.C

/*
 * (c) Copyright 1993, Silicon Graphics, Inc.
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  accanti.c
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include "jitter.h"

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Initialize lighting and other values.
 */
void myinit(void)
{
    GLfloat mat_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_position[] = { 0.0, 0.0, 10.0, 1.0 };
    GLfloat lm_ambient[] = { 0.2, 0.2, 0.2, 1.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialf(GL_FRONT, GL_SHININESS, 50.0);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lm_ambient);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glShadeModel (GL_FLAT);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClearAccum(0.0, 0.0, 0.0, 0.0);
}

void displayObjects(void)
{
    GLfloat torus_diffuse[] = { 0.7, 0.7, 0.0, 1.0 };
    GLfloat cube_diffuse[] = { 0.0, 0.7, 0.7, 1.0 };
    GLfloat sphere_diffuse[] = { 0.7, 0.0, 0.7, 1.0 };
    GLfloat octa_diffuse[] = { 0.7, 0.4, 0.4, 1.0 };

    glPushMatrix ();
    glRotatef (30.0, 1.0, 0.0, 0.0);

    glPushMatrix ();
    glTranslatef (-0.80, 0.35, 0.0);
    glRotatef (100.0, 1.0, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, torus_diffuse);
    auxSolidTorus (0.275, 0.85);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (-0.75, -0.50, 0.0);
    glRotatef (45.0, 0.0, 0.0, 1.0);
    glRotatef (45.0, 1.0, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, cube_diffuse);
    auxSolidCube (1.5);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (0.75, 0.60, 0.0);
    glRotatef (30.0, 1.0, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, sphere_diffuse);
    auxSolidSphere (1.0);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (0.70, -0.90, 0.25);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, octa_diffuse);
    auxSolidOctahedron (1.0);
    glPopMatrix ();

    glPopMatrix ();
}

#define ACSIZE  8

void CALLBACK display(void)
{
    GLint viewport[4];
    int jitter;

    glGetIntegerv (GL_VIEWPORT, viewport);

    glClear(GL_ACCUM_BUFFER_BIT);
    for (jitter = 0; jitter < ACSIZE; jitter++) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glPushMatrix ();
/*  Note that 4.5 is the distance in world space between
 *  left and right and bottom and top.
 *  This formula converts fractional pixel movement to
 *  world coordinates.
 */
        glTranslatef (j8[jitter].x*4.5/viewport[2],
            j8[jitter].y*4.5/viewport[3], 0.0);
        displayObjects ();
        glPopMatrix ();
        glAccum(GL_ACCUM, 1.0/ACSIZE);
        glFlush();
        auxSwapBuffers();
    }
    glAccum (GL_RETURN, 1.0);
    glFlush();
    auxSwapBuffers();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-2.25, 2.25, -2.25*h/w, 2.25*h/w, -10.0, 10.0);
    else
    glOrtho (-2.25*w/h, 2.25*w/h, -2.25, 2.25, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_DOUBLE | AUX_RGB
            | AUX_ACCUM | AUX_DEPTH16);
    auxInitPosition (0, 0, 250, 250);
    auxInitWindow ("Jittering - Orthographic");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


ACCNOT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  accnot.c
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Initialize lighting and other values.
 */

void myinit(void)
{
    GLfloat mat_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_position[] = { 0.0, 0.0, 10.0, 1.0 };
    GLfloat lm_ambient[] = { 0.2, 0.2, 0.2, 1.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialf(GL_FRONT, GL_SHININESS, 50.0);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lm_ambient);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

void CALLBACK display(void)
{
    GLfloat torus_diffuse[] = { 0.7, 0.7, 0.0, 1.0 };
    GLfloat cube_diffuse[] = { 0.0, 0.7, 0.7, 1.0 };
    GLfloat sphere_diffuse[] = { 0.7, 0.0, 0.7, 1.0 };
    GLfloat octa_diffuse[] = { 0.7, 0.4, 0.4, 1.0 };

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glShadeModel (GL_FLAT);
    glPushMatrix ();
    glRotatef (30.0, 1.0, 0.0, 0.0);

    glPushMatrix ();
    glTranslatef (-0.80, 0.35, 0.0);
    glRotatef (100.0, 1.0, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, torus_diffuse);
    auxSolidTorus (0.275, 0.85);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (-0.75, -0.50, 0.0);
    glRotatef (45.0, 0.0, 0.0, 1.0);
    glRotatef (45.0, 1.0, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, cube_diffuse);
    auxSolidCube (1.5);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (0.75, 0.60, 0.0);
    glRotatef (30.0, 1.0, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, sphere_diffuse);
    auxSolidSphere (1.0);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (0.70, -0.90, 0.25);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, octa_diffuse);
    auxSolidOctahedron (1.0);
    glPopMatrix ();

    glPopMatrix ();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if(!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho (-2.25, 2.25, -2.25*h/w, 2.25*h/w, -10.0, 10.0);
    else
        glOrtho (-2.25*w/h, 2.25*w/h, -2.25, 2.25, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB
            | AUX_ACCUM | AUX_DEPTH16);
    auxInitPosition (0, 0, 250, 250);
    auxInitWindow ("Auxillary 3-D Models");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


ACCPERSP.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  accpersp.c
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
#include <GL/glaux.h>
#include "jitter.h"

void myinit(void);
void accFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top,
    GLdouble znear, GLdouble zfar, GLdouble pixdx, GLdouble pixdy,
    GLdouble eyedx, GLdouble eyedy, GLdouble focus);
void accPerspective(GLdouble fovy, GLdouble aspect,
    GLdouble znear, GLdouble zfar, GLdouble pixdx, GLdouble pixdy,
    GLdouble eyedx, GLdouble eyedy, GLdouble focus);
void displayObjects(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

#define PI_ 3.14159265358979323846

/*  accFrustum()
 *  The first 6 arguments are identical to the glFrustum() call.
 *
 *  pixdx and pixdy are anti-alias jitter in pixels.
 *  Set both equal to 0.0 for no anti-alias jitter.
 *  eyedx and eyedy are depth-of field jitter in pixels.
 *  Set both equal to 0.0 for no depth of field effects.
 *
 *  focus is distance from eye to plane in focus.
 *  focus must be greater than, but not equal to 0.0.
 *
 *  Note that accFrustum() calls glTranslatef().  You will
 *  probably want to insure that your ModelView matrix has been
 *  initialized to identity before calling accFrustum().
 */
void accFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top,
    GLdouble znear, GLdouble zfar, GLdouble pixdx, GLdouble pixdy,
    GLdouble eyedx, GLdouble eyedy, GLdouble focus)
{
    GLdouble xwsize, ywsize;
    GLdouble dx, dy;
    GLint viewport[4];

    glGetIntegerv (GL_VIEWPORT, viewport);

    xwsize = right - left;
    ywsize = top - bottom;

    dx = -(pixdx*xwsize/(GLdouble) viewport[2] + eyedx*znear/focus);
    dy = -(pixdy*ywsize/(GLdouble) viewport[3] + eyedy*znear/focus);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum (left + dx, right + dx, bottom + dy, top + dy, znear, zfar);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef (-eyedx, -eyedy, 0.0);
}

/*  accPerspective()
 *
 *  The first 4 arguments are identical to the gluPerspective() call.
 *  pixdx and pixdy are anti-alias jitter in pixels.
 *  Set both equal to 0.0 for no anti-alias jitter.
 *  eyedx and eyedy are depth-of field jitter in pixels.
 *  Set both equal to 0.0 for no depth of field effects.
 *
 *  focus is distance from eye to plane in focus.
 *  focus must be greater than, but not equal to 0.0.
 *
 *  Note that accPerspective() calls accFrustum().
 */
void accPerspective(GLdouble fovy, GLdouble aspect,
    GLdouble znear, GLdouble zfar, GLdouble pixdx, GLdouble pixdy,
    GLdouble eyedx, GLdouble eyedy, GLdouble focus)
{
    GLdouble fov2,left,right,bottom,top;

    fov2 = ((fovy*PI_) / 180.0) / 2.0;

    top = znear / (cos(fov2) / sin(fov2));
    bottom = -top;

    right = top * aspect;
    left = -right;

    accFrustum (left, right, bottom, top, znear, zfar,
    pixdx, pixdy, eyedx, eyedy, focus);
}

/*  Initialize lighting and other values.
 */
void myinit(void)
{
    GLfloat mat_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_position[] = { 0.0, 0.0, 10.0, 1.0 };
    GLfloat lm_ambient[] = { 0.2, 0.2, 0.2, 1.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialf(GL_FRONT, GL_SHININESS, 50.0);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lm_ambient);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glShadeModel (GL_FLAT);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClearAccum(0.0, 0.0, 0.0, 0.0);
}

void displayObjects(void)
{
    GLfloat torus_diffuse[] = { 0.7, 0.7, 0.0, 1.0 };
    GLfloat cube_diffuse[] = { 0.0, 0.7, 0.7, 1.0 };
    GLfloat sphere_diffuse[] = { 0.7, 0.0, 0.7, 1.0 };
    GLfloat octa_diffuse[] = { 0.7, 0.4, 0.4, 1.0 };

    glPushMatrix ();
    glTranslatef (0.0, 0.0, -5.0);
    glRotatef (30.0, 1.0, 0.0, 0.0);

    glPushMatrix ();
    glTranslatef (-0.80, 0.35, 0.0);
    glRotatef (100.0, 1.0, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, torus_diffuse);
    auxSolidTorus (0.275, 0.85);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (-0.75, -0.50, 0.0);
    glRotatef (45.0, 0.0, 0.0, 1.0);
    glRotatef (45.0, 1.0, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, cube_diffuse);
    auxSolidCube (1.5);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (0.75, 0.60, 0.0);
    glRotatef (30.0, 1.0, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, sphere_diffuse);
    auxSolidSphere (1.0);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (0.70, -0.90, 0.25);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, octa_diffuse);
    auxSolidOctahedron (1.0);
    glPopMatrix ();

    glPopMatrix ();
}

#define ACSIZE  8

void CALLBACK display(void)
{
    GLint viewport[4];
    int jitter;

    glGetIntegerv (GL_VIEWPORT, viewport);

    if(!viewport[3]) return;

    glClear(GL_ACCUM_BUFFER_BIT);
    for (jitter = 0; jitter < ACSIZE; jitter++) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        accPerspective (50.0,
            (GLdouble) viewport[2]/(GLdouble) viewport[3],
            1.0, 15.0, j8[jitter].x, j8[jitter].y,
            0.0, 0.0, 1.0);
        displayObjects ();
        glAccum(GL_ACCUM, 1.0/ACSIZE);
        glFlush();
        auxSwapBuffers();
    }
    glAccum (GL_RETURN, 1.0);
    glFlush();
    auxSwapBuffers();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_DOUBLE | AUX_RGB
            | AUX_ACCUM | AUX_DEPTH16);
    auxInitPosition (0, 0, 250, 250);
    auxInitWindow ("Scene Antialiasing");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


ACCUM.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  accum.c
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include <stdio.h>

void myinit(void);
void loaddxdy(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

GLsizei width, height;

void myinit(void)
{
    GLfloat ambient[] = { 0.4, 0.4, 0.4, 1.0 };
    GLfloat diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat position[] = { 0.0, 2.0, 2.0, 0.0 };
    GLfloat mat_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    GLfloat mat_diffuse[] = { 0.7, 0.7, 0.7, 1.0 };
    GLfloat mat_specular[] = { 0.9, 0.9, 0.9, 1.0 };
    GLfloat mat_shininess[] = { 50.0 };
    GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
    glLightfv(GL_LIGHT0, GL_POSITION, position);

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);

    glEnable(GL_CULL_FACE);
    glCullFace(GL_FRONT);

    glClearAccum(0.0, 0.0, 0.0, 0.0);
}

#define ACSIZE 16

GLfloat dx[ACSIZE], dy[ACSIZE];

GLfloat jitter3[3][2] = {
    {0.5, 0.5},
    {1.35899e-05, 0.230369},
    {0.000189185, 0.766878},
};

GLfloat jitter11[11][2] = {
    {0.5, 0.5}, {0.406537, 0.135858},
    {0.860325, 0.968558}, {0.680141, 0.232877},
    {0.775694, 0.584871}, {0.963354, 0.309056},
    {0.593493, 0.864072}, {0.224334, 0.415055},
    {0.0366643, 0.690884}, {0.139685, 0.0313988},
    {0.319861, 0.767097},
};

GLfloat jitter16[16][2] = {
    {0.4375, 0.4375}, {0.1875, 0.5625},
    {0.9375, 1.1875}, {0.4375, 0.9375-1},
    {0.6875, 0.5625}, {0.1875, 0.0625},
    {0.6875, 0.3125}, {0.1875, 0.3125},
    {0.4375, 0.1875}, {0.9375-1, 0.4375},
    {0.6875, 0.8125}, {0.4375, 0.6875},
    {0.6875, 0.0625}, {0.9375, 0.9375},
    {1.1875, 0.8125}, {0.9375, 0.6875},
};

GLfloat jitter29[29][2] = {
    {0.5, 0.5}, {0.498126, 0.141363},
    {0.217276, 0.651732}, {0.439503, 0.954859},
    {0.734171, 0.836294}, {0.912454, 0.79952},
    {0.406153, 0.671156}, {0.0163892, 0.631994},
    {0.298064, 0.843476}, {0.312025, 0.0990405},
    {0.98135, 0.965697}, {0.841999, 0.272378},
    {0.559348, 0.32727}, {0.809331, 0.638901},
    {0.632583, 0.994471}, {0.00588314, 0.146344},
    {0.713365, 0.437896}, {0.185173, 0.246584},
    {0.901735, 0.474544}, {0.366423, 0.296698},
    {0.687032, 0.188184}, {0.313256, 0.472999},
    {0.543195, 0.800044}, {0.629329, 0.631599},
    {0.818263, 0.0439354}, {0.163978, 0.00621497},
    {0.109533, 0.812811}, {0.131325, 0.471624},
    {0.0196755, 0.331813},
};

GLfloat jitter90[90][2] = {
    {0.5, 0.5}, {0.784289, 0.417355},
    {0.608691, 0.678948}, {0.546538, 0.976002},
    {0.972245, 0.270498}, {0.765121, 0.189392},
    {0.513193, 0.743827}, {0.123709, 0.874866},
    {0.991334, 0.745136}, {0.56342, 0.0925047},
    {0.662226, 0.143317}, {0.444563, 0.928535},
    {0.248017, 0.981655}, {0.100115, 0.771923},
    {0.593937, 0.559383}, {0.392095, 0.225932},
    {0.428776, 0.812094}, {0.510615, 0.633584},
    {0.836431, 0.00343328}, {0.494037, 0.391771},
    {0.617448, 0.792324}, {0.688599, 0.48914},
    {0.530421, 0.859206}, {0.0742278, 0.665344},
    {0.979388, 0.626835}, {0.183806, 0.479216},
    {0.151222, 0.0803998}, {0.476489, 0.157863},
    {0.792675, 0.653531}, {0.0990416, 0.267284},
    {0.776667, 0.303894}, {0.312904, 0.296018},
    {0.288777, 0.691008}, {0.460097, 0.0436075},
    {0.594323, 0.440751}, {0.876296, 0.472043},
    {0.0442623, 0.0693901}, {0.355476, 0.00442787},
    {0.391763, 0.361327}, {0.406994, 0.696053},
    {0.708393, 0.724992}, {0.925807, 0.933103},
    {0.850618, 0.11774}, {0.867486, 0.233677},
    {0.208805, 0.285484}, {0.572129, 0.211505},
    {0.172931, 0.180455}, {0.327574, 0.598031},
    {0.685187, 0.372379}, {0.23375, 0.878555},
    {0.960657, 0.409561}, {0.371005, 0.113866},
    {0.29471, 0.496941}, {0.748611, 0.0735321},
    {0.878643, 0.34504}, {0.210987, 0.778228},
    {0.692961, 0.606194}, {0.82152, 0.8893},
    {0.0982095, 0.563104}, {0.214514, 0.581197},
    {0.734262, 0.956545}, {0.881377, 0.583548},
    {0.0560485, 0.174277}, {0.0729515, 0.458003},
    {0.719604, 0.840564}, {0.325388, 0.7883},
    {0.26136, 0.0848927}, {0.393754, 0.467505},
    {0.425361, 0.577672}, {0.648594, 0.0248658},
    {0.983843, 0.521048}, {0.272936, 0.395127},
    {0.177695, 0.675733}, {0.89175, 0.700901},
    {0.632301, 0.908259}, {0.782859, 0.53611},
    {0.0141421, 0.855548}, {0.0437116, 0.351866},
    {0.939604, 0.0450863}, {0.0320883, 0.962943},
    {0.341155, 0.895317}, {0.952087, 0.158387},
    {0.908415, 0.820054}, {0.481435, 0.281195},
    {0.675525, 0.25699}, {0.585273, 0.324454},
    {0.156488, 0.376783}, {0.140434, 0.977416},
    {0.808155, 0.77305}, {0.282973, 0.188937},
};

void loaddxdy(void)
{
    long i;
    for (i = 0; i < ACSIZE; i++) {
        dx[i] = jitter16[i][0]*10/width;
        dy[i] = jitter16[i][1]*10/height;
    }
}

void CALLBACK display(void)
{
    int i;

    glClear(GL_ACCUM_BUFFER_BIT);
    loaddxdy();
    for (i = 0; i < (ACSIZE); i++) {
        //printf("Pass %d\n", i);
        glPushMatrix();
        glTranslatef(dx[i], dy[i], 0.0);
        glRotatef(45.0, 1.0, 1.0, 1.0);
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
        auxSolidTeapot(1.0);
        glPopMatrix();
        glAccum(GL_ACCUM, 1.0/(ACSIZE));
        glFlush();
        auxSwapBuffers();
    }
    //printf("final job\n");
    glAccum(GL_RETURN, 1.0);
    //printf("done\n");
    glFlush();
    auxSwapBuffers();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if(!h) return;
    width = w; height = h;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho(-3.0, 3.0, -3.0*(GLfloat) h/(GLfloat) w,
                3.0*(GLfloat) h/(GLfloat) w, -15.0, 15.0);
    else
        glOrtho(-3.0*(GLfloat) w/(GLfloat) h,
                3.0*(GLfloat) w/(GLfloat) h, -3.0, 3.0, -15.0, 15.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_DOUBLE | AUX_RGB
            | AUX_ACCUM | AUX_DEPTH16);
    auxInitPosition (0, 0, 300, 300);
    auxInitWindow ("Accumulation Buffer");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


AIM.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  aim.c
 *  This program calculates the fovy (field of view angle
 *  in the y direction), by using trigonometry, given the
 *  size of an object and its size.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
#include <GL/glaux.h>
#include <stdio.h>

void myinit(void);
GLdouble calculateAngle (double size, double distance);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

void myinit (void) {
    glShadeModel (GL_FLAT);
}

/*  Clear the screen.  Set the current color to white.
 *  Draw the wire frame cube and sphere.
 */
void CALLBACK display (void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f (1.0, 1.0, 1.0);

    glLoadIdentity ();
/*  glTranslatef() as viewing transformation    */
    glTranslatef (0.0, 0.0, -5.0);
    auxWireCube(2.0);
    auxWireSphere(1.0);
    glFlush();
}

#define PI  3.1415926535

/*  atan2 () is a system math routine which calculates
 *  the arctangent of an angle, given length of the
 *  opposite and adjacent sides of a right triangle.
 *  atan2 () is not an OpenGL routine.
 */
GLdouble calculateAngle (double size, double distance)
{
    GLdouble radtheta, degtheta;

    radtheta = 2.0 * atan2 (size/2.0, distance);
    degtheta = (180.0 * radtheta) / PI;
    //printf ("degtheta is %lf\n", degtheta);
    return ((GLdouble) degtheta);
}

/*  Called when the window is first opened and whenever
 *  the window is reconfigured (moved or resized).
 */
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    GLdouble theta;

    if(!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    theta = calculateAngle (2.0, 5.0);
    gluPerspective(theta, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Field of View Angle");
    myinit ();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


ALPHA.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  alpha.c
 *  This program draws several overlapping filled polygons
 *  to demonstrate the effect order has on alpha blending results.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Initialize alpha blending function.
 */
void myinit(void)
{
    glEnable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glShadeModel (GL_FLAT);
    glClearColor (0.0, 0.0, 0.0, 0.0);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    glColor4f (1.0, 1.0, 0.0, 0.75);
    glRectf (0.0, 0.0, 0.5, 1.0);

    glColor4f (0.0, 1.0, 1.0, 0.75);
    glRectf (0.0, 0.0, 1.0, 0.5);
/*  draw colored polygons in reverse order in upper right  */
    glColor4f (0.0, 1.0, 1.0, 0.75);
    glRectf (0.5, 0.5, 1.0, 1.0);

    glColor4f (1.0, 1.0, 0.0, 0.75);
    glRectf (0.5, 0.5, 1.0, 1.0);

    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if(!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    gluOrtho2D (0.0, 1.0, 0.0, 1.0*(GLfloat)h/(GLfloat)w);
    else
    gluOrtho2D (0.0, 1.0*(GLfloat)w/(GLfloat)h, 0.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Alpha Blending");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


ALPHA3D.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  alpha3D.c
 *  This program demonstrates how to intermix opaque and
 *  alpha blended polygons in the same scene, by using glDepthMask.
 *  Pressing the left mouse button toggles the eye position.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK toggleEye (AUX_EVENTREC *event);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

void myinit(void)
{
    GLfloat mat_ambient[] = { 0.0, 0.0, 0.0, 0.15 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 0.15 };
    GLfloat mat_shininess[] = { 15.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

    glEnable (GL_LIGHTING);
    glEnable (GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

GLboolean eyePosition = GL_FALSE;

void CALLBACK toggleEye (AUX_EVENTREC *event)
{
    if (eyePosition)
    eyePosition = GL_FALSE;
    else
    eyePosition = GL_TRUE;
}

void CALLBACK display(void)
{
    GLfloat position[] = { 0.0, 0.0, 1.0, 1.0 };
    GLfloat mat_torus[] = { 0.75, 0.75, 0.0, 1.0 };
    GLfloat mat_cylinder[] = { 0.0, 0.75, 0.75, 0.15 };

    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLightfv (GL_LIGHT0, GL_POSITION, position);
    glPushMatrix ();
    if (eyePosition)
        gluLookAt (0.0, 0.0, 9.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
    else
        gluLookAt (0.0, 0.0, -9.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
    glPushMatrix ();
    glTranslatef (0.0, 0.0, 1.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_torus);
    auxSolidTorus (0.275, 0.85);
    glPopMatrix ();

    glEnable (GL_BLEND);
    glDepthMask (GL_FALSE);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_cylinder);
    glTranslatef (0.0, 0.0, -1.0);
    auxSolidCylinder (1.0, 2.0);
    glDepthMask (GL_TRUE);
    glDisable (GL_BLEND);
    glPopMatrix ();

    glFlush ();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if(!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(30.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Intermixing Opaque and Alpha Blending");
    auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, toggleEye);
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


ANTI.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  anti.c
 *  This program draws antialiased lines in RGBA mode.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include <stdio.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Initialize antialiasing for RGBA mode, including alpha
 *  blending, hint, and line width.  Print out implementation
 *  specific info on line width granularity and width.
 */
void myinit(void)
{
    GLfloat values[2];
    glGetFloatv (GL_LINE_WIDTH_GRANULARITY, values);
    //printf ("GL_LINE_WIDTH_GRANULARITY value is %3.1f\n", values[0]);

    glGetFloatv (GL_LINE_WIDTH_RANGE, values);

    //printf ("GL_LINE_WIDTH_RANGE values are %3.1f %3.1f\n",
    //values[0], values[1]);

    glEnable (GL_LINE_SMOOTH);
    glEnable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glHint (GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
    glLineWidth (1.5);

    glShadeModel(GL_FLAT);
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

/*  display() draws an icosahedron with a large alpha value, 1.0.
 */
void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor4f (1.0, 1.0, 1.0, 1.0);
    auxWireIcosahedron(1.0);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (45.0, (GLfloat) w/(GLfloat) h, 3.0, 5.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity ();
    glTranslatef (0.0, 0.0, -4.0);  /*  move object into view   */
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 400, 400);
    auxInitWindow ("Antialiased Lines Using RGBA");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


ANTINDX.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  antiindex.c
 *  The program draws a wireframe icosahedron with
 *  antialiased lines, in color index mode.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

#define RAMPSIZE 16
#define RAMPSTART 32

/*  Initialize antialiasing for color index mode,
 *  including loading a grey color ramp starting
 *  at RAMPSTART, which must be a multiple of 16.
 */
void myinit(void)
{
    int i;

    for (i = 0; i < RAMPSIZE; i++) {
    GLfloat shade;
    shade = (GLfloat) i/(GLfloat) RAMPSIZE;
    auxSetOneColor(RAMPSTART+(GLint)i, shade, shade, shade);
    }

    glEnable (GL_LINE_SMOOTH);
    glHint (GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
    glLineWidth (1.5);

    glClearIndex ((GLfloat) RAMPSTART);
    glShadeModel(GL_FLAT);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

/*  display() draws an icosahedron.
 */
void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glIndexi(RAMPSTART);
    auxWireIcosahedron(1.0);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (45.0, (GLfloat) w/(GLfloat) h, 3.0, 5.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity ();
    glTranslatef (0.0, 0.0, -4.0);  /*  move object into view   */
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  color index display mode, depth buffer,
 *  and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_INDEX | AUX_DEPTH16);
    auxInitPosition (0, 0, 400, 400);
    auxInitWindow ("Antialiased Lines Using Color Index");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


ANTIPNDX.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  antipindex.c
 *  The program draws antialiased points,
 *  in color index mode.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

#define RAMPSIZE 16
#define RAMPSTART 32

/*  Initialize point antialiasing for color index mode,
 *  including loading a grey color ramp starting at
 *  RAMPSTART, which must be a multiple of 16.
 */
void myinit(void)
{
    int i;

    for (i = 0; i < RAMPSIZE; i++) {
    GLfloat shade;
    shade = (GLfloat) i/(GLfloat) RAMPSIZE;
    auxSetOneColor (RAMPSTART+(GLint)i, shade, shade, shade);
    }
    glEnable (GL_POINT_SMOOTH);
    glHint (GL_POINT_SMOOTH_HINT, GL_FASTEST);
    glPointSize (3.0);
    glClearIndex ((GLfloat) RAMPSTART);
}

/*  display() draws several points.  */

void CALLBACK display(void)
{
    int i;

    glClear(GL_COLOR_BUFFER_BIT);
    glIndexi (RAMPSTART);
    glBegin (GL_POINTS);
    for (i = 1; i < 10; i++) {
        glVertex2f ((GLfloat) i * 10.0, (GLfloat) i * 10.0);
    }
    glEnd ();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w < h)
    glOrtho (0.0, 100.0, 0.0,
        100.0*(GLfloat) h/(GLfloat) w, -1.0, 1.0);
    else
    glOrtho (0.0, 100.0*(GLfloat) w/(GLfloat) h,
        0.0, 100.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity ();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  color index display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_INDEX);
    auxInitPosition (0, 0, 100, 100);
    auxInitWindow ("Points");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


ANTIPNT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  antipoint.c
 *  The program draws antialiased points, in RGBA mode.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Initialize point anti-aliasing for RGBA mode, including alpha
 *  blending, hint, and point size.  These points are 3.0 pixels big.
 */
void myinit(void)
{
    glEnable (GL_POINT_SMOOTH);
    glEnable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glHint (GL_POINT_SMOOTH_HINT, GL_DONT_CARE);
    glPointSize (3.0);

    glClearColor(0.0, 0.0, 0.0, 0.0);
}

/*  display() draws several points.
 */
void CALLBACK display(void)
{
    int i;

    glClear (GL_COLOR_BUFFER_BIT);
    glColor4f (1.0, 1.0, 1.0, 1.0);
    glBegin (GL_POINTS);
    for (i = 1; i < 10; i++) {
        glVertex2f ((GLfloat) i * 10.0, (GLfloat) i * 10.0);
    }
    glEnd ();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w < h)
    glOrtho (0.0, 100.0,
        0.0, 100.0*(GLfloat) h/(GLfloat) w, -1.0, 1.0);
    else
    glOrtho (0.0, 100.0*(GLfloat) w/(GLfloat) h,
        0.0, 100.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity ();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 100, 100);
    auxInitWindow ("Points");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


ANTIPOLY.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  antipoly.c
 *  This program draws filled polygons with antialiased
 *  edges.  The special GL_SRC_ALPHA_SATURATE blending
 *  function is used.
 *  Pressing the left mouse button turns the antialiasing
 *  on and off.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK toggleSmooth (AUX_EVENTREC *event);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

GLboolean polySmooth;

void myinit(void)
{
    GLfloat mat_ambient[] = { 0.0, 0.0, 0.0, 1.00 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.00 };
    GLfloat mat_shininess[] = { 15.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

    glEnable (GL_LIGHTING);
    glEnable (GL_LIGHT0);
    glEnable (GL_BLEND);
    glCullFace (GL_BACK);
    glEnable (GL_CULL_FACE);
    glEnable (GL_POLYGON_SMOOTH);
    polySmooth = GL_TRUE;

    glClearColor (0.0, 0.0, 0.0, 0.0);
}

void CALLBACK toggleSmooth (AUX_EVENTREC *event)
{
    if (polySmooth) {
    polySmooth = GL_FALSE;
    glDisable (GL_BLEND);
    glDisable (GL_POLYGON_SMOOTH);
    glEnable (GL_DEPTH_TEST);
    }
    else {
    polySmooth = GL_TRUE;
    glEnable (GL_BLEND);
    glEnable (GL_POLYGON_SMOOTH);
    glDisable (GL_DEPTH_TEST);
    }
}

/*  Note:  polygons must be drawn from front to back
 *  for proper blending.
 */
void CALLBACK display(void)
{
    GLfloat position[] = { 0.0, 0.0, 1.0, 0.0 };
    GLfloat mat_cube1[] = { 0.75, 0.75, 0.0, 1.0 };
    GLfloat mat_cube2[] = { 0.0, 0.75, 0.75, 1.0 };

    if (polySmooth)
    glClear (GL_COLOR_BUFFER_BIT);
    else
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix ();
    glTranslatef (0.0, 0.0, -8.0);
    glLightfv (GL_LIGHT0, GL_POSITION, position);

    glBlendFunc (GL_SRC_ALPHA_SATURATE, GL_ONE);

    glPushMatrix ();
    glRotatef (30.0, 1.0, 0.0, 0.0);
    glRotatef (60.0, 0.0, 1.0, 0.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_cube1);
    auxSolidCube (1.0);
    glPopMatrix ();

    glTranslatef (0.0, 0.0, -2.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_cube2);
    glRotatef (30.0, 0.0, 1.0, 0.0);
    glRotatef (60.0, 1.0, 0.0, 0.0);
    auxSolidCube (1.0);

    glPopMatrix ();

    glFlush ();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    h = (h == 0) ? 1 : h;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(30.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_ALPHA | AUX_DEPTH16);
    auxInitPosition (0, 0, 200, 200);
    auxInitWindow ("Antialiased Edges");
    auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, toggleSmooth);
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


BEZCURVE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  bezcurve.c
 *  This program uses evaluators to draw a Bezier curve.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

GLfloat ctrlpoints[4][3] = {
    { -4.0, -4.0, 0.0}, { -2.0, 4.0, 0.0},
    {2.0, -4.0, 0.0}, {4.0, 4.0, 0.0}};

void myinit(void)
{
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &ctrlpoints[0][0]);
    glEnable(GL_MAP1_VERTEX_3);
    glShadeModel(GL_FLAT);
}

void CALLBACK display(void)
{
    int i;

    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    glBegin(GL_LINE_STRIP);
    for (i = 0; i <= 30; i++)
        glEvalCoord1f((GLfloat) i/30.0);
    glEnd();
    /* The following code displays the control points as dots. */
    glPointSize(5.0);
    glColor3f(1.0, 1.0, 0.0);
    glBegin(GL_POINTS);
    for (i = 0; i < 4; i++)
        glVertex3fv(&ctrlpoints[i][0]);
    glEnd();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho(-5.0, 5.0, -5.0*(GLfloat)h/(GLfloat)w,
        5.0*(GLfloat)h/(GLfloat)w, -5.0, 5.0);
    else
    glOrtho(-5.0*(GLfloat)w/(GLfloat)h,
        5.0*(GLfloat)w/(GLfloat)h, -5.0, 5.0, -5.0, 5.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Bezier Curves Using Evaluators");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


BEZMESH.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  bezsurf.c
 *  This program renders a lighted, filled Bezier surface,
 *  using two-dimensional evaluators.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void initlights(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

GLfloat ctrlpoints[4][4][3] = {
    {{-1.5, -1.5, 4.0}, {-0.5, -1.5, 2.0},
    {0.5, -1.5, -1.0}, {1.5, -1.5, 2.0}},
    {{-1.5, -0.5, 1.0}, {-0.5, -0.5, 3.0},
    {0.5, -0.5, 0.0}, {1.5, -0.5, -1.0}},
    {{-1.5, 0.5, 4.0}, {-0.5, 0.5, 0.0},
    {0.5, 0.5, 3.0}, {1.5, 0.5, 4.0}},
    {{-1.5, 1.5, -2.0}, {-0.5, 1.5, -2.0},
    {0.5, 1.5, 0.0}, {1.5, 1.5, -1.0}}
};

void initlights(void)
{
    GLfloat ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    GLfloat position[] = { 0.0, 0.0, 2.0, 1.0 };
    GLfloat mat_diffuse[] = { 0.6, 0.6, 0.6, 1.0 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 50.0 };

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_POSITION, position);

    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glRotatef(85.0, 1.0, 1.0, 1.0);
    glEvalMesh2(GL_FILL, 0, 20, 0, 20);
    glPopMatrix();
    glFlush();
}

void myinit(void)
{
    glClearColor (0.0, 0.0, 0.0, 1.0);
    glEnable (GL_DEPTH_TEST);
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4,
        0, 1, 12, 4, &ctrlpoints[0][0][0]);
    glEnable(GL_MAP2_VERTEX_3);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);
    glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
    initlights();   /* for lighted version only */
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho(-4.0, 4.0, -4.0*(GLfloat)h/(GLfloat)w,
        4.0*(GLfloat)h/(GLfloat)w, -4.0, 4.0);
    else
    glOrtho(-4.0*(GLfloat)w/(GLfloat)h,
        4.0*(GLfloat)w/(GLfloat)h, -4.0, 4.0, -4.0, 4.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Lighted and Filled Bezier Surface");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


BEZSURF.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  bezmesh.c
 *  This program renders a wireframe Bezier surface,
 *  using two-dimensional evaluators.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

GLfloat ctrlpoints[4][4][3] = {
    {{-1.5, -1.5, 4.0}, {-0.5, -1.5, 2.0},
{0.5, -1.5, -1.0}, {1.5, -1.5, 2.0}},
    {{-1.5, -0.5, 1.0}, {-0.5, -0.5, 3.0},
{0.5, -0.5, 0.0}, {1.5, -0.5, -1.0}},
    {{-1.5, 0.5, 4.0}, {-0.5, 0.5, 0.0},
{0.5, 0.5, 3.0}, {1.5, 0.5, 4.0}},
    {{-1.5, 1.5, -2.0}, {-0.5, 1.5, -2.0},
{0.5, 1.5, 0.0}, {1.5, 1.5, -1.0}}
};

void CALLBACK display(void)
{
    int i, j;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    glPushMatrix ();
    glRotatef(85.0, 1.0, 1.0, 1.0);
    for (j = 0; j <= 8; j++) {
glBegin(GL_LINE_STRIP);
        for (i = 0; i <= 30; i++)
    glEvalCoord2f((GLfloat)i/30.0, (GLfloat)j/8.0);
glEnd();
glBegin(GL_LINE_STRIP);
        for (i = 0; i <= 30; i++)
    glEvalCoord2f((GLfloat)j/8.0, (GLfloat)i/30.0);
glEnd();
    }
    glPopMatrix ();
    glFlush();
}

void myinit(void)
{
    glClearColor (0.0, 0.0, 0.0, 1.0);
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4,
    0, 1, 12, 4, &ctrlpoints[0][0][0]);
    glEnable(GL_MAP2_VERTEX_3);
    glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_FLAT);
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
glOrtho(-4.0, 4.0, -4.0*(GLfloat)h/(GLfloat)w,
    4.0*(GLfloat)h/(GLfloat)w, -4.0, 4.0);
    else
glOrtho(-4.0*(GLfloat)w/(GLfloat)h,
    4.0*(GLfloat)w/(GLfloat)h, -4.0, 4.0, -4.0, 4.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Wireframe Bezier Surface");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


CHECKER.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  checker.c
 *  This program texture maps a checkerboard image onto
 *  two rectangles.  This program clamps the texture, if
 *  the texture coordinates fall outside 0.0 and 1.0.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void makeCheckImage(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Create checkerboard texture */
#define checkImageWidth 64
#define checkImageHeight 64
GLubyte checkImage[checkImageWidth][checkImageHeight][3];

void makeCheckImage(void)
{
    int i, j, c;

    for (i = 0; i < checkImageWidth; i++) {
    for (j = 0; j < checkImageHeight; j++) {
        c = ((((i&0x8)==0)^((j&0x8))==0))*255;
        checkImage[i][j][0] = (GLubyte) c;
        checkImage[i][j][1] = (GLubyte) c;
        checkImage[i][j][2] = (GLubyte) c;
    }
    }
}

void myinit(void)
{
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    makeCheckImage();
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, checkImageWidth,
    checkImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE,
    &checkImage[0][0][0]);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    glEnable(GL_TEXTURE_2D);
    glShadeModel(GL_FLAT);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex3f(-2.0, -1.0, 0.0);
    glTexCoord2f(0.0, 1.0); glVertex3f(-2.0, 1.0, 0.0);
    glTexCoord2f(1.0, 1.0); glVertex3f(0.0, 1.0, 0.0);
    glTexCoord2f(1.0, 0.0); glVertex3f(0.0, -1.0, 0.0);

    glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 0.0);
    glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, 0.0);
    glTexCoord2f(1.0, 1.0); glVertex3f(2.41421, 1.0, -1.41421);
    glTexCoord2f(1.0, 0.0); glVertex3f(2.41421, -1.0, -1.41421);
    glEnd();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, 1.0*(GLfloat)w/(GLfloat)h, 1.0, 30.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -3.6);
}

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Texture Map");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


CHECKER2.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  checker2.c
 *  This program texture maps a checkerboard image onto
 *  two rectangles.  This program repeats the texture, if
 *  the texture coordinates fall outside 0.0 and 1.0.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void makeCheckImage(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Create checkerboard texture */
#define checkImageWidth 64
#define checkImageHeight 64
GLubyte checkImage[checkImageWidth][checkImageHeight][3];

void makeCheckImage(void)
{
    int i, j, c;

    for (i = 0; i < checkImageWidth; i++) {
    for (j = 0; j < checkImageHeight; j++) {
        c = ((((i&0x8)==0)^((j&0x8))==0))*255;
        checkImage[i][j][0] = (GLubyte) c;
        checkImage[i][j][1] = (GLubyte) c;
        checkImage[i][j][2] = (GLubyte) c;
    }
    }
}

void myinit(void)
{
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    makeCheckImage();
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, checkImageWidth,
    checkImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE,
    &checkImage[0][0][0]);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    glEnable(GL_TEXTURE_2D);
    glShadeModel(GL_FLAT);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex3f(-2.0, -1.0, 0.0);
    glTexCoord2f(0.0, 3.0); glVertex3f(-2.0, 1.0, 0.0);
    glTexCoord2f(3.0, 3.0); glVertex3f(0.0, 1.0, 0.0);
    glTexCoord2f(3.0, 0.0); glVertex3f(0.0, -1.0, 0.0);

    glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 0.0);
    glTexCoord2f(0.0, 3.0); glVertex3f(1.0, 1.0, 0.0);
    glTexCoord2f(3.0, 3.0); glVertex3f(2.41421, 1.0, -1.41421);
    glTexCoord2f(3.0, 0.0); glVertex3f(2.41421, -1.0, -1.41421);
    glEnd();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, 1.0*(GLfloat)w/(GLfloat)h, 1.0, 30.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -3.6);
}

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Texture Map");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


CHESS.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  chess.c
 *  This program texture maps a checkerboard image onto
 *  two rectangles.  The texture coordinates for the
 *  rectangles are 0.0 to 3.0.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include <math.h>

void myinit(void);
void makeCheckImage(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

#define checkImageWidth 64
#define checkImageHeight 64
GLubyte checkImage[checkImageWidth][checkImageHeight][3];

void makecheckimage(void)
{
    int i, j, c;

    for (i = 0; i < checkImageWidth; i++) {
    for (j = 0; j < checkImageHeight; j++) {
        c = ((((i&0x8)==0)^((j&0x8))==0))*255;
        checkImage[i][j][0] = (GLubyte) c;
        checkImage[i][j][1] = (GLubyte) c;
        checkImage[i][j][2] = (GLubyte) c;
    }
    }
}

void myinit(void)
{
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    makecheckimage();
    glTexImage2D(GL_TEXTURE_2D, 0, 3,
    checkImageWidth, checkImageHeight, 0,
    GL_RGB, GL_UNSIGNED_BYTE, &checkImage[0][0][0]);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    glEnable(GL_TEXTURE_2D);
    glShadeModel(GL_FLAT);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex3f(-2.0, -1.0, 0.0);
    glTexCoord2f(0.0, 3.0); glVertex3f(-2.0, 1.0, 0.0);
    glTexCoord2f(3.0, 3.0); glVertex3f(0.0, 1.0, 0.0);
    glTexCoord2f(3.0, 0.0); glVertex3f(0.0, -1.0, 0.0);

    glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 0.0);
    glTexCoord2f(0.0, 3.0); glVertex3f(1.0, 1.0, 0.0);
    glTexCoord2f(3.0, 3.0); glVertex3f(2.41421, 1.0, -1.41421);
    glTexCoord2f(3.0, 0.0); glVertex3f(2.41421, -1.0, -1.41421);
    glEnd();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, 1.0*(GLfloat)w/(GLfloat)h, 1.0, 30.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -3.6);
}

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Texture Map");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


CLIP.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  clip.c
 *  This program demonstrates arbitrary clipping planes.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

void CALLBACK display(void)
{
    GLdouble eqn[4] = {0.0, 1.0, 0.0, 0.0};
    GLdouble eqn2[4] = {1.0, 0.0, 0.0, 0.0};

    glClear(GL_COLOR_BUFFER_BIT);

    glColor3f (1.0, 1.0, 1.0);
    glPushMatrix();
    glTranslatef (0.0, 0.0, -5.0);

/*    clip lower half -- y < 0      */
    glClipPlane (GL_CLIP_PLANE0, eqn);
    glEnable (GL_CLIP_PLANE0);
/*    clip left half -- x < 0       */
    glClipPlane (GL_CLIP_PLANE1, eqn2);
    glEnable (GL_CLIP_PLANE1);

    glRotatef (90.0, 1.0, 0.0, 0.0);
    auxWireSphere(1.0);
    glPopMatrix();
    glFlush();
}

void myinit (void) {
    glShadeModel (GL_FLAT);
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Arbitrary Clipping Planes");
    myinit ();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


COLORMAT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  colormat.c
 *  After initialization, the program will be in
 *  ColorMaterial mode.  Pressing the mouse buttons
 *  will change the color of the diffuse reflection.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK changeRedDiffuse (void);
void CALLBACK changeGreenDiffuse (void);
void CALLBACK changeBlueDiffuse (void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

GLfloat diffuseMaterial[4] = { 0.5, 0.5, 0.5, 1.0 };

/*  Initialize values for material property, light source,
 *  lighting model, and depth buffer.
 */
void myinit(void)
{
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

    glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseMaterial);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialf(GL_FRONT, GL_SHININESS, 25.0);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);

    glColorMaterial(GL_FRONT, GL_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
}

void CALLBACK changeRedDiffuse (void)
{
    diffuseMaterial[0] += 0.1;
    if (diffuseMaterial[0] > 1.0)
    diffuseMaterial[0] = 0.0;
    glColor4fv(diffuseMaterial);
}

void CALLBACK changeGreenDiffuse (void)
{
    diffuseMaterial[1] += 0.1;
    if (diffuseMaterial[1] > 1.0)
    diffuseMaterial[1] = 0.0;
    glColor4fv(diffuseMaterial);
}

void CALLBACK changeBlueDiffuse (void)
{
    diffuseMaterial[2] += 0.1;
    if (diffuseMaterial[2] > 1.0)
    diffuseMaterial[2] = 0.0;
    glColor4fv(diffuseMaterial);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    auxSolidSphere(1.0);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w,
        1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
    glOrtho (-1.5*(GLfloat)w/(GLfloat)h,
        1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("ColorMaterial Mode");
    myinit();
    auxKeyFunc(AUX_R, changeRedDiffuse);
    auxKeyFunc(AUX_r, changeRedDiffuse);
    auxKeyFunc(AUX_G, changeGreenDiffuse);
    auxKeyFunc(AUX_g, changeGreenDiffuse);
    auxKeyFunc(AUX_B, changeBlueDiffuse);
    auxKeyFunc(AUX_b, changeBlueDiffuse);
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


CONE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 * cone.c
 * This program demonstrates the use of the GL lighting model.
 * A sphere is drawn using a grey material characteristic.
 * A single light source illuminates the object.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Initialize material property, light source, and lighting model.
 */
void myinit(void)
{
    GLfloat mat_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    GLfloat mat_diffuse[] = { 0.8, 0.8, 0.8, 1.0 };
/*  mat_specular and mat_shininess are NOT default values   */
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 50.0 };

    GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
/*  light_position is NOT default value */
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

    GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glTranslatef (0.0, -1.0, 0.0);
    glRotatef (250.0, 1.0, 0.0, 0.0);
    auxSolidCone(1.0, 2.0);
    glPopMatrix();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w,
                1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
        glOrtho (-1.5*(GLfloat)w/(GLfloat)h,
                1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Lighting");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


CUBE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  cube.c
 *  Draws a 3-D cube, viewed with perspective, stretched
 *  along the y-axis.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Clear the screen.  Set the current color to white.
 *  Draw the wire frame cube.
 */
void CALLBACK display (void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f (1.0, 1.0, 1.0);
    glLoadIdentity ();  /*  clear the matrix    */
    glTranslatef (0.0, 0.0, -5.0);  /*  viewing transformation  */
    glScalef (1.0, 2.0, 1.0);   /*  modeling transformation */
    auxWireCube(1.0);   /*  draw the cube   */
    glFlush();
}

void myinit (void) {
    glShadeModel (GL_FLAT);
}

/*  Called when the window is first opened and whenever
 *  the window is reconfigured (moved or resized).
 */
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    glMatrixMode (GL_PROJECTION);   /*  prepare for and then  */
    glLoadIdentity ();  /*  define the projection  */
    glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);    /*  transformation  */
    glMatrixMode (GL_MODELVIEW);    /*  back to modelview matrix    */
    glViewport (0, 0, w, h);    /*  define the viewport */
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Perspective 3-D Cube");
    myinit ();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


CURVE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  curve.c
 *  This program uses the Utility Library NURBS routines to
 *  draw a one-dimensional NURBS curve.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

GLUnurbsObj *theNurb;

void myinit(void)
{
    glShadeModel (GL_FLAT);
    theNurb = gluNewNurbsRenderer();
    gluNurbsProperty (theNurb, GLU_SAMPLING_TOLERANCE, 10.0);
}

/*  This routine draws a B-spline curve.  Try a different
 *  knot sequence for a Bezier curve.  For example,
    GLfloat knots[8] = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0};
 */

void CALLBACK display(void)
{
    GLfloat ctlpoints[4][3] = {{-.75, -.75, 0.0},
    {-.5, .75, 0.0}, {.5, .75, 0.0}, {.75, -.75, 0.0}};
    GLfloat knots[8] = {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0};

    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f (1.0, 1.0, 1.0);
    gluBeginCurve(theNurb);
    gluNurbsCurve(theNurb,
        8, knots,
        3,
        &ctlpoints[0][0],
        4,
        GL_MAP1_VERTEX_3);
    gluEndCurve(theNurb);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    gluOrtho2D (-1.0, 1.0, -1.0 * (GLfloat) h/(GLfloat) w,
        1.0 * (GLfloat) h/(GLfloat) w);
    else
    gluOrtho2D (-1.0 * (GLfloat) w/(GLfloat) h,
        1.0 * (GLfloat) w/(GLfloat) h, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("NURBS Curve");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


DEPTHCUE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  depthcue.c
 *  This program draws a wireframe model, which uses
 *  intensity (brightness) to give clues to distance.
 *  Fog is used to achieve this effect.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Initialize linear fog for depth cueing.
 */
void myinit(void)
{
    GLfloat fogColor[4] = {0.0, 0.0, 0.0, 1.0};

    glEnable(GL_FOG);
    glFogi (GL_FOG_MODE, GL_LINEAR);
    glHint (GL_FOG_HINT, GL_NICEST);  /*  per pixel   */
    glFogf (GL_FOG_START, 3.0);
    glFogf (GL_FOG_END, 5.0);
    glFogfv (GL_FOG_COLOR, fogColor);
    glClearColor(0.0, 0.0, 0.0, 1.0);

    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_FLAT);
}

/*  display() draws an icosahedron.
 */
void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f (1.0, 1.0, 1.0);
    auxWireIcosahedron(1.0);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (45.0, (GLfloat) w/(GLfloat) h, 3.0, 5.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity ();
    glTranslatef (0.0, 0.0, -4.0);  /*  move object into view   */
}

/*  Main Loop
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 400, 400);
    auxInitWindow ("Distance Using Fog");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


DISK.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  disk.c
 *  This program demonstrates the use of the quadrics
 *  Utility Library routines to draw circles and arcs.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

GLUquadricObj * quadObj;

/*  Clear the screen.  For each triangle, set the current
 *  color and modify the modelview matrix.
 */
void CALLBACK display(void)
{
    glClearColor (0.0, 0.0, 0.0, 1.0);
    glClear (GL_COLOR_BUFFER_BIT);

    glPushMatrix();
    gluQuadricDrawStyle (quadObj, GLU_FILL);
    glColor3f (1.0, 1.0, 1.0);
    glTranslatef (10.0, 10.0, 0.0);
    gluDisk (quadObj, 0.0, 5.0, 10, 2);
    glPopMatrix();

    glPushMatrix();
    glColor3f (1.0, 1.0, 0.0);
    glTranslatef (20.0, 20.0, 0.0);
    gluPartialDisk (quadObj, 0.0, 5.0, 10, 3, 30.0, 120.0);
    glPopMatrix();

    glPushMatrix();
    gluQuadricDrawStyle (quadObj, GLU_SILHOUETTE);
    glColor3f (0.0, 1.0, 1.0);
    glTranslatef (30.0, 30.0, 0.0);
    gluPartialDisk (quadObj, 0.0, 5.0, 10, 3, 135.0, 270.0);
    glPopMatrix();

    glPushMatrix();
    gluQuadricDrawStyle (quadObj, GLU_LINE);
    glColor3f (1.0, 0.0, 1.0);
    glTranslatef (40.0, 40.0, 0.0);
    gluDisk (quadObj, 2.0, 5.0, 10, 10);
    glPopMatrix();
    glFlush();
}

void myinit (void) {
    quadObj = gluNewQuadric ();
    glShadeModel(GL_FLAT);
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (0.0, 50.0,
        0.0, 50.0*(GLfloat)h/(GLfloat)w, -1.0, 1.0);
    else
    glOrtho (0.0, 50.0*(GLfloat)w/(GLfloat)h,
        0.0, 50.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity ();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Quadrics");
    myinit ();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


DOF.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  dof.c
 *  This program demonstrates use of the accumulation buffer to
 *  create an out-of-focus depth-of-field effect.  The teapots
 *  are drawn several times into the accumulation buffer.  The
 *  viewing volume is jittered, except at the focal point, where
 *  the viewing volume is at the same position, each time.  In
 *  this case, the gold teapot remains in focus.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
#include <GL/glaux.h>
#include "jitter.h"

void myinit(void);
void accFrustum(GLdouble left, GLdouble right, GLdouble bottom,
    GLdouble top, GLdouble znear, GLdouble zfar, GLdouble pixdx,
    GLdouble pixdy, GLdouble eyedx, GLdouble eyedy, GLdouble focus);
void accPerspective(GLdouble fovy, GLdouble aspect,
    GLdouble znear, GLdouble zfar, GLdouble pixdx, GLdouble pixdy,
    GLdouble eyedx, GLdouble eyedy, GLdouble focus);
void renderTeapot (GLfloat x, GLfloat y, GLfloat z,
    GLfloat ambr, GLfloat ambg, GLfloat ambb,
    GLfloat difr, GLfloat difg, GLfloat difb,
    GLfloat specr, GLfloat specg, GLfloat specb, GLfloat shine);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

#define PI_ 3.14159265358979323846

/*  accFrustum()
 *  The first 6 arguments are identical to the glFrustum() call.
 *
 *  pixdx and pixdy are anti-alias jitter in pixels.
 *  Set both equal to 0.0 for no anti-alias jitter.
 *  eyedx and eyedy are depth-of field jitter in pixels.
 *  Set both equal to 0.0 for no depth of field effects.
 *
 *  focus is distance from eye to plane in focus.
 *  focus must be greater than, but not equal to 0.0.
 *
 *  Note that accFrustum() calls glTranslatef().  You will
 *  probably want to insure that your ModelView matrix has been
 *  initialized to identity before calling accFrustum().
 */
void accFrustum(GLdouble left, GLdouble right, GLdouble bottom,
    GLdouble top, GLdouble znear, GLdouble zfar, GLdouble pixdx,
    GLdouble pixdy, GLdouble eyedx, GLdouble eyedy, GLdouble focus)
{
    GLdouble xwsize, ywsize;
    GLdouble dx, dy;
    GLint viewport[4];

    glGetIntegerv (GL_VIEWPORT, viewport);

    xwsize = right - left;
    ywsize = top - bottom;

    dx = -(pixdx*xwsize/(GLdouble) viewport[2] + eyedx*znear/focus);
    dy = -(pixdy*ywsize/(GLdouble) viewport[3] + eyedy*znear/focus);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum (left + dx, right + dx, bottom + dy, top + dy, znear, zfar);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef (-eyedx, -eyedy, 0.0);
}

/*  accPerspective()
 *
 *  The first 4 arguments are identical to the gluPerspective() call.
 *  pixdx and pixdy are anti-alias jitter in pixels.
 *  Set both equal to 0.0 for no anti-alias jitter.
 *  eyedx and eyedy are depth-of field jitter in pixels.
 *  Set both equal to 0.0 for no depth of field effects.
 *
 *  focus is distance from eye to plane in focus.
 *  focus must be greater than, but not equal to 0.0.
 *
 *  Note that accPerspective() calls accFrustum().
 */
void accPerspective(GLdouble fovy, GLdouble aspect,
    GLdouble znear, GLdouble zfar, GLdouble pixdx, GLdouble pixdy,
    GLdouble eyedx, GLdouble eyedy, GLdouble focus)
{
    GLdouble fov2,left,right,bottom,top;

    fov2 = ((fovy*PI_) / 180.0) / 2.0;

    top = znear / (cos(fov2) / sin(fov2));
    bottom = -top;

    right = top * aspect;
    left = -right;

    accFrustum (left, right, bottom, top, znear, zfar,
    pixdx, pixdy, eyedx, eyedy, focus);
}

void myinit(void)
{
    GLfloat ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat position[] = { 0.0, 3.0, 3.0, 0.0 };

    GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    GLfloat local_view[] = { 0.0 };

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);

    glFrontFace (GL_CW);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClearAccum(0.0, 0.0, 0.0, 0.0);
}

void renderTeapot (GLfloat x, GLfloat y, GLfloat z,
    GLfloat ambr, GLfloat ambg, GLfloat ambb,
    GLfloat difr, GLfloat difg, GLfloat difb,
    GLfloat specr, GLfloat specg, GLfloat specb, GLfloat shine)
{
    float mat[3];

    glPushMatrix();
    glTranslatef (x, y, z);
    mat[0] = ambr; mat[1] = ambg; mat[2] = ambb;
    glMaterialfv (GL_FRONT, GL_AMBIENT, mat);
    mat[0] = difr; mat[1] = difg; mat[2] = difb;
    glMaterialfv (GL_FRONT, GL_DIFFUSE, mat);
    mat[0] = specr; mat[1] = specg; mat[2] = specb;
    glMaterialfv (GL_FRONT, GL_SPECULAR, mat);
    glMaterialf (GL_FRONT, GL_SHININESS, shine*128.0);
    auxSolidTeapot(0.5);
    glPopMatrix();
}

/*  display() draws 5 teapots into the accumulation buffer
 *  several times; each time with a jittered perspective.
 *  The focal point is at z = 5.0, so the gold teapot will
 *  stay in focus.  The amount of jitter is adjusted by the
 *  magnitude of the accPerspective() jitter; in this example, 0.33.
 *  In this example, the teapots are drawn 8 times.  See jitter.h
 */
void CALLBACK display(void)
{
    int jitter;
    GLint viewport[4];

    glGetIntegerv (GL_VIEWPORT, viewport);
    if (!viewport[3]) return;
    glClear(GL_ACCUM_BUFFER_BIT);

    for (jitter = 0; jitter < 8; jitter++) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    accPerspective (45.0,
        (GLdouble) viewport[2]/(GLdouble) viewport[3],
        1.0, 15.0, 0.0, 0.0,
        0.33*j8[jitter].x, 0.33*j8[jitter].y, 5.0);
/*  ruby, gold, silver, emerald, and cyan teapots   */
    renderTeapot (-1.1, -0.5, -4.5, 0.1745, 0.01175, 0.01175,
        0.61424, 0.04136, 0.04136, 0.727811, 0.626959, 0.626959, 0.6);
    renderTeapot (-0.5, -0.5, -5.0, 0.24725, 0.1995, 0.0745,
        0.75164, 0.60648, 0.22648, 0.628281, 0.555802, 0.366065, 0.4);
    renderTeapot (0.2, -0.5, -5.5, 0.19225, 0.19225, 0.19225,
        0.50754, 0.50754, 0.50754, 0.508273, 0.508273, 0.508273, 0.4);
    renderTeapot (1.0, -0.5, -6.0, 0.0215, 0.1745, 0.0215,
        0.07568, 0.61424, 0.07568, 0.633, 0.727811, 0.633, 0.6);
    renderTeapot (1.8, -0.5, -6.5, 0.0, 0.1, 0.06, 0.0, 0.50980392,
        0.50980392, 0.50196078, 0.50196078, 0.50196078, .25);
    glAccum (GL_ACCUM, 0.125);
    glFlush();
    auxSwapBuffers();
    }

    glAccum (GL_RETURN, 1.0);
    glFlush();
    auxSwapBuffers();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, depth buffer, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_DOUBLE | AUX_RGB
            | AUX_ACCUM | AUX_DEPTH16);
    auxInitPosition (0, 0, 400, 400);
    auxInitWindow ("Depth-of-Field");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


DOFNOT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  dofnot.c
 *  This program demonstrates the same scene as dof.c, but
 *  without use of the accumulation buffer, so everything
 *  is in focus.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
#include <GL/glaux.h>

void myinit(void);
void renderTeapot (GLfloat x, GLfloat y, GLfloat z,
    GLfloat ambr, GLfloat ambg, GLfloat ambb,
    GLfloat difr, GLfloat difg, GLfloat difb,
    GLfloat specr, GLfloat specg, GLfloat specb, GLfloat shine);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

void myinit(void)
{
    GLfloat ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat position[] = { 0.0, 3.0, 3.0, 0.0 };

    GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    GLfloat local_view[] = { 0.0 };

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);

    glFrontFace (GL_CW);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);

    glClearColor(0.0, 0.0, 0.0, 0.0);
}

void renderTeapot (GLfloat x, GLfloat y, GLfloat z,
    GLfloat ambr, GLfloat ambg, GLfloat ambb,
    GLfloat difr, GLfloat difg, GLfloat difb,
    GLfloat specr, GLfloat specg, GLfloat specb, GLfloat shine)
{
    float mat[3];

    glPushMatrix();
    glTranslatef (x, y, z);
    mat[0] = ambr; mat[1] = ambg; mat[2] = ambb;
    glMaterialfv (GL_FRONT, GL_AMBIENT, mat);
    mat[0] = difr; mat[1] = difg; mat[2] = difb;
    glMaterialfv (GL_FRONT, GL_DIFFUSE, mat);
    mat[0] = specr; mat[1] = specg; mat[2] = specb;
    glMaterialfv (GL_FRONT, GL_SPECULAR, mat);
    glMaterialf (GL_FRONT, GL_SHININESS, shine*128.0);
    auxSolidTeapot(0.5);
    glPopMatrix();
}

/*  display() draws 5 teapots into the accumulation buffer
 *  several times; each time with a jittered perspective.
 *  The focal point is at z = 5.0, so the gold teapot will
 *  stay in focus.  The amount of jitter is adjusted by the
 *  magnitude of the accPerspective() jitter; in this example, 0.33.
 *  In this example, the teapots are drawn 8 times.  See jitter.h
 */
void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix ();
/*  ruby, gold, silver, emerald, and cyan teapots   */
    renderTeapot (-1.1, -0.5, -4.5, 0.1745, 0.01175, 0.01175,
        0.61424, 0.04136, 0.04136, 0.727811, 0.626959, 0.626959, 0.6);
    renderTeapot (-0.5, -0.5, -5.0, 0.24725, 0.1995, 0.0745,
        0.75164, 0.60648, 0.22648, 0.628281, 0.555802, 0.366065, 0.4);
    renderTeapot (0.2, -0.5, -5.5, 0.19225, 0.19225, 0.19225,
        0.50754, 0.50754, 0.50754, 0.508273, 0.508273, 0.508273, 0.4);
    renderTeapot (1.0, -0.5, -6.0, 0.0215, 0.1745, 0.0215,
        0.07568, 0.61424, 0.07568, 0.633, 0.727811, 0.633, 0.6);
    renderTeapot (1.8, -0.5, -6.5, 0.0, 0.1, 0.06, 0.0, 0.50980392,
        0.50980392, 0.50196078, 0.50196078, 0.50196078, .25);

    glPopMatrix ();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective (45.0, (GLfloat) w/(GLfloat) h,
        1.0, 15.0);
    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, depth buffer, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 400, 400);
    auxInitWindow ("Depth-of-Field");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


DOUBLE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  double.c
 *  This program demonstrates double buffering for
 *  flicker-free animation.  The left and middle mouse
 *  buttons start and stop the spinning motion of the square.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK spinDisplay (void);
void CALLBACK startIdleFunc (AUX_EVENTREC *event);
void CALLBACK stopIdleFunc (AUX_EVENTREC *event);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

static GLfloat spin = 0.0;

void CALLBACK display(void)
{
    glClear (GL_COLOR_BUFFER_BIT);

    glPushMatrix ();
    glRotatef (spin, 0.0, 0.0, 1.0);
    glRectf (-25.0, -25.0, 25.0, 25.0);
    glPopMatrix ();

    glFlush();
    auxSwapBuffers();
}

void CALLBACK spinDisplay (void)
{
    spin = spin + 2.0;
    if (spin > 360.0)
    spin = spin - 360.0;
    display();
}

void CALLBACK startIdleFunc (AUX_EVENTREC *event)
{
    auxIdleFunc(spinDisplay);
}

void CALLBACK stopIdleFunc (AUX_EVENTREC *event)
{
    auxIdleFunc(0);
}

void myinit (void)
{
    glClearColor (0.0, 0.0, 0.0, 1.0);
    glColor3f (1.0, 1.0, 1.0);
    glShadeModel (GL_FLAT);
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-50.0, 50.0, -50.0*(GLfloat)h/(GLfloat)w,
        50.0*(GLfloat)h/(GLfloat)w, -1.0, 1.0);
    else
    glOrtho (-50.0*(GLfloat)w/(GLfloat)h,
        50.0*(GLfloat)w/(GLfloat)h, -50.0, 50.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity ();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_DOUBLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Double Buffering");
    myinit ();
    auxReshapeFunc (myReshape);
    auxIdleFunc (spinDisplay);
    auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, startIdleFunc);
    auxMouseFunc (AUX_RIGHTBUTTON, AUX_MOUSEDOWN, stopIdleFunc);
    auxMainLoop(display);
    return(0);
}


DRAWF.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  drawf.c
 *  Draws the bitmapped letter F on the screen (several times).
 *  This demonstrates use of the glBitmap() call.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

GLubyte rasters[24] = {
    0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00,
    0xff, 0x00, 0xff, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00,
    0xff, 0xc0, 0xff, 0xc0};

void myinit(void)
{
    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
    glClearColor (0.0, 0.0, 0.0, 0.0);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f (1.0, 1.0, 1.0);
    glRasterPos2i (20.5, 20.5);
    glBitmap (10, 12, 0.0, 0.0, 12.0, 0.0, rasters);
    glBitmap (10, 12, 0.0, 0.0, 12.0, 0.0, rasters);
    glBitmap (10, 12, 0.0, 0.0, 12.0, 0.0, rasters);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho (0, w, 0, h, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Bitmap");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


FEEDBACK.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  feedback.c
 *  This program demonstrates use of OpenGL feedback.  First,
 *  a lighting environment is set up and a few lines are drawn.
 *  Then feedback mode is entered, and the same lines are
 *  drawn.  The results in the feedback buffer are printed.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include <stdio.h>

void myinit(void);
void drawGeometry (GLenum mode);
void print3DcolorVertex (GLint size, GLint *count, GLfloat *buffer);
void printBuffer(GLint size, GLfloat *buffer);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Initialize lighting.
 */
void myinit(void)
{
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
}

/*  Draw a few lines and two points, one of which will
 *  be clipped.  If in feedback mode, a passthrough token
 *  is issued between the each primitive.
 */
void drawGeometry (GLenum mode)
{
    glBegin (GL_LINE_STRIP);
    glNormal3f (0.0, 0.0, 1.0);
    glVertex3f (30.0, 30.0, 0.0);
    glVertex3f (50.0, 60.0, 0.0);
    glVertex3f (70.0, 40.0, 0.0);
    glEnd ();
    if (mode == GL_FEEDBACK)
    glPassThrough (1.0);
    glBegin (GL_POINTS);
    glVertex3f (-100.0, -100.0, -100.0);    /*  will be clipped  */
    glEnd ();
    if (mode == GL_FEEDBACK)
    glPassThrough (2.0);
    glBegin (GL_POINTS);
    glNormal3f (0.0, 0.0, 1.0);
    glVertex3f (50.0, 50.0, 0.0);
    glEnd ();
}

/*  Write contents of one vertex to stdout. */
void print3DcolorVertex (GLint size,
    GLint *count, GLfloat *buffer)
{
    int i;

    //printf ("  ");
    for (i = 0; i < 7; i++) {
    //printf ("%4.2f ", buffer[size-(*count)]);
    *count = *count - 1;
    }
    //printf ("\n");
}

/*  Write contents of entire buffer.  (Parse tokens!)   */
void printBuffer(GLint size, GLfloat *buffer)
{
    GLint count;
    GLfloat token;

    count = size;
    while (count) {
    token = buffer[size-count]; count--;
    if (token == GL_PASS_THROUGH_TOKEN) {
        //printf ("GL_PASS_THROUGH_TOKEN\n");
        //printf ("  %4.2f\n", buffer[size-count]);
        count--;
    }
    else if (token == GL_POINT_TOKEN) {
        //printf ("GL_POINT_TOKEN\n");
        print3DcolorVertex (size, &count, buffer);
    }
    else if (token == GL_LINE_TOKEN) {
        //printf ("GL_LINE_TOKEN\n");
        print3DcolorVertex (size, &count, buffer);
        print3DcolorVertex (size, &count, buffer);
    }
    else if (token == GL_LINE_RESET_TOKEN) {
        //printf ("GL_LINE_RESET_TOKEN\n");
        print3DcolorVertex (size, &count, buffer);
        print3DcolorVertex (size, &count, buffer);
    }
    }
}

void CALLBACK display(void)
{
    GLfloat feedBuffer[1024];
    GLint size;

    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    glOrtho (0.0, 100.0, 0.0, 100.0, 0.0, 1.0);

    glClearColor (0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    drawGeometry (GL_RENDER);

    glFeedbackBuffer (1024, GL_3D_COLOR, feedBuffer);
    (void) glRenderMode (GL_FEEDBACK);
    drawGeometry (GL_FEEDBACK);

    size = glRenderMode (GL_RENDER);
    printBuffer (size, feedBuffer);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 100, 100);
    auxInitWindow ("Feedback");
    myinit ();
    auxMainLoop(display);
    return(0);
}


FOG.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  fog.c
 *  This program draws 5 red teapots, each at a different
 *  z distance from the eye, in different types of fog.
 *  Pressing the left mouse button chooses between 3 types of
 *  fog:  exponential, exponential squared, and linear.
 *  In this program, there is a fixed density value, as well
 *  as fixed start and end values for the linear fog.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
#include <GL/glaux.h>
#include <stdio.h>

void myinit(void);
void renderRedTeapot (GLfloat x, GLfloat y, GLfloat z);
void CALLBACK cycleFog (AUX_EVENTREC *event);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

GLint fogMode;

void CALLBACK cycleFog (AUX_EVENTREC *event)
{
    if (fogMode == GL_EXP) {
    fogMode = GL_EXP2;
    //printf ("Fog mode is GL_EXP2\n");
    }
    else if (fogMode == GL_EXP2) {
    fogMode = GL_LINEAR;
    //printf ("Fog mode is GL_LINEAR\n");
    glFogf (GL_FOG_START, 1.0);
    glFogf (GL_FOG_END, 5.0);
    }
    else if (fogMode == GL_LINEAR) {
    fogMode = GL_EXP;
    //printf ("Fog mode is GL_EXP\n");
    }
    glFogi (GL_FOG_MODE, fogMode);
}

/*  Initialize z-buffer, projection matrix, light source,
 *  and lighting model.  Do not specify a material property here.
 */
void myinit(void)
{
    GLfloat position[] = { 0.0, 3.0, 3.0, 0.0 };
    GLfloat local_view[] = { 0.0 };

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);

    glFrontFace (GL_CW);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);
    glEnable(GL_FOG);
    {
    GLfloat fogColor[4] = {0.5, 0.5, 0.5, 1.0};

    fogMode = GL_EXP;
    glFogi (GL_FOG_MODE, fogMode);
    glFogfv (GL_FOG_COLOR, fogColor);
    glFogf (GL_FOG_DENSITY, 0.35);
    glHint (GL_FOG_HINT, GL_DONT_CARE);
    glClearColor(0.5, 0.5, 0.5, 1.0);
    }
}

void renderRedTeapot (GLfloat x, GLfloat y, GLfloat z)
{
    float mat[3];

    glPushMatrix();
    glTranslatef (x, y, z);
    mat[0] = 0.1745; mat[1] = 0.01175; mat[2] = 0.01175;
    glMaterialfv (GL_FRONT, GL_AMBIENT, mat);
    mat[0] = 0.61424; mat[1] = 0.04136; mat[2] = 0.04136;
    glMaterialfv (GL_FRONT, GL_DIFFUSE, mat);
    mat[0] = 0.727811; mat[1] = 0.626959; mat[2] = 0.626959;
    glMaterialfv (GL_FRONT, GL_SPECULAR, mat);
    glMaterialf (GL_FRONT, GL_SHININESS, 0.6*128.0);
    auxSolidTeapot(1.0);
    glPopMatrix();
}

/*  display() draws 5 teapots at different z positions.
 */
void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    renderRedTeapot (-4.0, -0.5, -1.0);
    renderRedTeapot (-2.0, -0.5, -2.0);
    renderRedTeapot (0.0, -0.5, -3.0);
    renderRedTeapot (2.0, -0.5, -4.0);
    renderRedTeapot (4.0, -0.5, -5.0);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= (h*3))
    glOrtho (-6.0, 6.0, -2.0*((GLfloat) h*3)/(GLfloat) w,
        2.0*((GLfloat) h*3)/(GLfloat) w, 0.0, 10.0);
    else
    glOrtho (-6.0*(GLfloat) w/((GLfloat) h*3),
        6.0*(GLfloat) w/((GLfloat) h*3), -2.0, 2.0, 0.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity ();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, depth buffer, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 450, 150);
    auxInitWindow ("Fog");
    auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, cycleFog);
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


FOGINDEX.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  fogindex.c
 *  This program demonstrates fog in color index mode.
 *  Three cones are drawn at different z values in a linear
 *  fog.  32 contiguous colors (from 16 to 47) are loaded
 *  with a color ramp.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Initialize color map and fog.  Set screen clear color
 *  to end of color ramp.
 */
#define NMCOLORS 32
#define RAMPSTART 16

void myinit(void)
{
    int i;

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    for (i = 0; i < NMCOLORS; i++) {
    GLfloat shade;
    shade = (GLfloat) (NMCOLORS-i)/(GLfloat) NMCOLORS;
    auxSetOneColor (16 + i, shade, shade, shade);
    }
    glEnable(GL_FOG);

    glFogi (GL_FOG_MODE, GL_LINEAR);
    glFogi (GL_FOG_INDEX, NMCOLORS);
    glFogf (GL_FOG_START, 0.0);
    glFogf (GL_FOG_END, 4.0);
    glHint (GL_FOG_HINT, GL_NICEST);
    glClearIndex((GLfloat) (NMCOLORS+RAMPSTART-1));
}

/*  display() renders 3 cones at different z positions.
 */
void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix ();
    glTranslatef (-1.0, -1.0, -1.0);
    glRotatef (-90.0, 1.0, 0.0, 0.0);
    glIndexi (RAMPSTART);
    auxSolidCone(1.0, 2.0);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (0.0, -1.0, -2.25);
    glRotatef (-90.0, 1.0, 0.0, 0.0);
    glIndexi (RAMPSTART);
    auxSolidCone(1.0, 2.0);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (1.0, -1.0, -3.5);
    glRotatef (-90.0, 1.0, 0.0, 0.0);
    glIndexi (RAMPSTART);
    auxSolidCone(1.0, 2.0);
    glPopMatrix ();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-2.0, 2.0, -2.0*(GLfloat)h/(GLfloat)w,
        2.0*(GLfloat)h/(GLfloat)w, 0.0, 10.0);
    else
    glOrtho (-2.0*(GLfloat)w/(GLfloat)h,
        2.0*(GLfloat)w/(GLfloat)h, -2.0, 2.0, 0.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity ();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, depth buffer, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_INDEX | AUX_DEPTH16);
    auxInitPosition (0, 0, 200, 200);
    auxInitWindow ("Fog Using Color Index");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


FONT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  font.c
 *
 *  Draws some text in a bitmapped font.  Uses glBitmap()
 *  and other pixel routines.  Also demonstrates use of
 *  display lists.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void makeRasterFont(void);
void printString(char *s);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

GLubyte rasters[][13] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36},
{0x00, 0x00, 0x00, 0x66, 0x66, 0xff, 0x66, 0x66, 0xff, 0x66, 0x66, 0x00, 0x00},
{0x00, 0x00, 0x18, 0x7e, 0xff, 0x1b, 0x1f, 0x7e, 0xf8, 0xd8, 0xff, 0x7e, 0x18},
{0x00, 0x00, 0x0e, 0x1b, 0xdb, 0x6e, 0x30, 0x18, 0x0c, 0x76, 0xdb, 0xd8, 0x70},
{0x00, 0x00, 0x7f, 0xc6, 0xcf, 0xd8, 0x70, 0x70, 0xd8, 0xcc, 0xcc, 0x6c, 0x38},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x0c, 0x0e},
{0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c},
{0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30},
{0x00, 0x00, 0x00, 0x00, 0x99, 0x5a, 0x3c, 0xff, 0x3c, 0x5a, 0x99, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00},
{0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03},
{0x00, 0x00, 0x3c, 0x66, 0xc3, 0xe3, 0xf3, 0xdb, 0xcf, 0xc7, 0xc3, 0x66, 0x3c},
{0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x38, 0x18},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0xe7, 0x7e},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0x07, 0x03, 0x03, 0xe7, 0x7e},
{0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x03, 0x03, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x03, 0x7f, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e},
{0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06},
{0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60},
{0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x06, 0x03, 0xc3, 0xc3, 0x7e},
{0x00, 0x00, 0x3f, 0x60, 0xcf, 0xdb, 0xd3, 0xdd, 0xc3, 0x7e, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18},
{0x00, 0x00, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe},
{0x00, 0x00, 0x7e, 0xe7, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0xfc, 0xce, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0xce, 0xfc},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xff},
{0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xcf, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e},
{0x00, 0x00, 0x7c, 0xee, 0xc6, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06},
{0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc3},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xff, 0xff, 0xe7, 0xc3},
{0x00, 0x00, 0xc7, 0xc7, 0xcf, 0xcf, 0xdf, 0xdb, 0xfb, 0xf3, 0xf3, 0xe3, 0xe3},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0x7e},
{0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe},
{0x00, 0x00, 0x3f, 0x6e, 0xdf, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c},
{0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0xe0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0xc3, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3},
{0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x7e, 0x0c, 0x06, 0x03, 0x03, 0xff},
{0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c},
{0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60},
{0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18},
{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x30, 0x70},
{0x00, 0x00, 0x7f, 0xc3, 0xc3, 0x7f, 0x03, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x03},
{0x00, 0x00, 0x7f, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x33, 0x1e},
{0x7e, 0xc3, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00},
{0x38, 0x6c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x00},
{0x00, 0x00, 0xc6, 0xcc, 0xf8, 0xf0, 0xd8, 0xcc, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78},
{0x00, 0x00, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xfe, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00},
{0xc0, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0x00, 0x00, 0x00, 0x00},
{0x03, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xfe, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xfe, 0x03, 0x03, 0x7e, 0xc0, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00},
{0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0xe7, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0xc0, 0x60, 0x60, 0x30, 0x18, 0x3c, 0x66, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xff, 0x60, 0x30, 0x18, 0x0c, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x0f, 0x18, 0x18, 0x18, 0x38, 0xf0, 0x38, 0x18, 0x18, 0x18, 0x0f},
{0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18},
{0x00, 0x00, 0xf0, 0x18, 0x18, 0x18, 0x1c, 0x0f, 0x1c, 0x18, 0x18, 0x18, 0xf0},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x8f, 0xf1, 0x60, 0x00, 0x00, 0x00}
};

GLuint fontOffset;

void makeRasterFont(void)
{
    GLuint i;
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    fontOffset = glGenLists (128);
    for (i = 32; i < 127; i++) {
    glNewList(i+fontOffset, GL_COMPILE);
    glBitmap(8, 13, 0.0, 2.0, 10.0, 0.0, rasters[i-32]);
    glEndList();
    }
}

void myinit(void)
{
    glShadeModel (GL_FLAT);
    makeRasterFont();
}

void printString(char *s)
{
    glPushAttrib (GL_LIST_BIT);
    glListBase(fontOffset);
    glCallLists(strlen(s), GL_UNSIGNED_BYTE, (GLubyte *) s);
    glPopAttrib ();
}

/* Everything above this line could be in a library that defines a font.
 * To make it work, you've got to call makeRasterFont() before you start
 * making calls to printString().
 */
void CALLBACK display(void)
{
    GLfloat white[3] = { 1.0, 1.0, 1.0 };
    int i, j;
    char teststring[33];

    glClear(GL_COLOR_BUFFER_BIT);
    glColor3fv(white);
    for (i = 32; i < 127; i += 32) {
    glRasterPos2i(20, 200 - 18*i/32);
    for (j = 0; j < 32; j++)
        teststring[j] = (char) (i+j);
    teststring[32] = 0;
    printString(teststring);
    }
    glRasterPos2i(20, 100);
    printString("The quick brown fox jumps");
    glRasterPos2i(20, 82);
    printString("over a lazy dog.");
    glFlush ();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho (0.0, w, 0.0, h, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Display Lists");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


GLOS.H

// GLOS.H
//
// This is an OS specific header file

#include <windows.h>

// disable data conversion warnings

#pragma warning(disable : 4244)     // MIPS
#pragma warning(disable : 4136)     // X86
#pragma warning(disable : 4051)     // ALPHA
#pragma warning(disable : 4305)


LIGHT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  light.c
 *  This program demonstrates the use of the OpenGL lighting
 *  model.  A sphere is drawn using a grey material characteristic.
 *  A single light source illuminates the object.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);

/*  Initialize material property, light source, lighting model,
 *  and depth buffer.
 */
void myinit(void)
{
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 50.0 };
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    auxSolidSphere(1.0);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w,
        1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
    glOrtho (-1.5*(GLfloat)w/(GLfloat)h,
        1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Lighting");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


LINELIST.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  linelist.c
 *  This program demonstrates using display lists to call
 *  different line stipples.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);

#define drawOneLine(x1,y1,x2,y2) glBegin(GL_LINES); \
    glVertex2f ((x1),(y1)); glVertex2f ((x2),(y2)); glEnd();

GLuint offset;

void myinit (void)
{
/*  background to be cleared to black   */
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glShadeModel (GL_FLAT);

    offset = glGenLists (3);
    glNewList (offset, GL_COMPILE);
    glDisable (GL_LINE_STIPPLE);
    glEndList ();
    glNewList (offset+1, GL_COMPILE);
    glEnable (GL_LINE_STIPPLE);
    glLineStipple (1, 0x0F0F);
    glEndList ();
    glNewList (offset+2, GL_COMPILE);
    glEnable (GL_LINE_STIPPLE);
    glLineStipple (1, 0x1111);
    glEndList ();
}

void CALLBACK display(void)
{
    glClear (GL_COLOR_BUFFER_BIT);

/*  draw all lines in white */
    glColor3f (1.0, 1.0, 1.0);

    glCallList (offset);
    drawOneLine (50.0, 125.0, 350.0, 125.0);
    glCallList (offset+1);
    drawOneLine (50.0, 100.0, 350.0, 100.0);
    glCallList (offset+2);
    drawOneLine (50.0, 75.0, 350.0, 75.0);
    glCallList (offset+1);
    drawOneLine (50.0, 50.0, 350.0, 50.0);
    glCallList (offset);
    drawOneLine (50.0, 25.0, 350.0, 25.0);
    glFlush ();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 400, 150);
    auxInitWindow ("Display Lists");
    myinit ();
    auxMainLoop(display);
    return(0);
}


LINES.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  lines.c
 *  This program demonstrates different line stipples and widths.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);

#define drawOneLine(x1,y1,x2,y2) glBegin(GL_LINES); \
    glVertex2f ((x1),(y1)); glVertex2f ((x2),(y2)); glEnd();

void myinit (void) {
    /*  background to be cleared to black   */
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glShadeModel (GL_FLAT);
}

void CALLBACK display(void)
{
    int i;

    glClear (GL_COLOR_BUFFER_BIT);
/*  draw all lines in white */
    glColor3f (1.0, 1.0, 1.0);

/*  in 1st row, 3 lines drawn, each with a different stipple    */
    glEnable (GL_LINE_STIPPLE);
    glLineStipple (1, 0x0101);  /*  dotted  */
    drawOneLine (50.0, 125.0, 150.0, 125.0);
    glLineStipple (1, 0x00FF);  /*  dashed  */
    drawOneLine (150.0, 125.0, 250.0, 125.0);
    glLineStipple (1, 0x1C47);  /*  dash/dot/dash   */
    drawOneLine (250.0, 125.0, 350.0, 125.0);

/*  in 2nd row, 3 wide lines drawn, each with different stipple */
    glLineWidth (5.0);
    glLineStipple (1, 0x0101);
    drawOneLine (50.0, 100.0, 150.0, 100.0);
    glLineStipple (1, 0x00FF);
    drawOneLine (150.0, 100.0, 250.0, 100.0);
    glLineStipple (1, 0x1C47);
    drawOneLine (250.0, 100.0, 350.0, 100.0);
    glLineWidth (1.0);

/*  in 3rd row, 6 lines drawn, with dash/dot/dash stipple,  */
/*  as part of a single connect line strip          */
    glLineStipple (1, 0x1C47);
    glBegin (GL_LINE_STRIP);
    for (i = 0; i < 7; i++)
    glVertex2f (50.0 + ((GLfloat) i * 50.0), 75.0);
    glEnd ();

/*  in 4th row, 6 independent lines drawn,  */
/*  with dash/dot/dash stipple          */
    for (i = 0; i < 6; i++) {
    drawOneLine (50.0 + ((GLfloat) i * 50.0),
        50.0, 50.0 + ((GLfloat)(i+1) * 50.0), 50.0);
    }

/*  in 5th row, 1 line drawn, with dash/dot/dash stipple    */
/*  and repeat factor of 5          */
    glLineStipple (5, 0x1C47);
    drawOneLine (50.0, 25.0, 350.0, 25.0);
    glFlush ();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 400, 150);
    auxInitWindow ("Lines");
    myinit ();
    auxMainLoop(display);
    return(0);
}


LIST.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  list.c
 *  This program demonstrates how to make and execute a
 *  display list.  Note that attributes, such as current
 *  color and matrix, are changed.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void drawLine (void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

GLuint listName = 1;

void myinit (void)
{
    glNewList (listName, GL_COMPILE);
    glColor3f (1.0, 0.0, 0.0);
    glBegin (GL_TRIANGLES);
    glVertex2f (0.0, 0.0);
    glVertex2f (1.0, 0.0);
    glVertex2f (0.0, 1.0);
    glEnd ();
    glTranslatef (1.5, 0.0, 0.0);
    glEndList ();
    glShadeModel (GL_FLAT);
}

void drawLine (void)
{
    glBegin (GL_LINES);
    glVertex2f (0.0, 0.5);
    glVertex2f (15.0, 0.5);
    glEnd ();
}

void CALLBACK display(void)
{
    GLuint i;

    glClear (GL_COLOR_BUFFER_BIT);
    glColor3f (0.0, 1.0, 0.0);
    glPushMatrix();
    for (i = 0; i < 10; i++)
        glCallList (listName);
    drawLine ();
    glPopMatrix();
    glFlush ();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        gluOrtho2D (0.0, 2.0, -0.5 * (GLfloat) h/(GLfloat) w,
                1.5 * (GLfloat) h/(GLfloat) w);
    else
        gluOrtho2D (0.0, 2.0 * (GLfloat) w/(GLfloat) h, -0.5, 1.5);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 400, 50);
    auxInitWindow ("Display List");
    myinit ();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


LIST2.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  list2.c
 *  This program demonstrates glGenList() and glPushAttrib().
 *  The matrix and color are restored, before the line is drawn.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void drawLine (void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

GLuint listName;

void myinit (void)
{
    GLfloat color_vector[3] = {1.0, 0.0, 0.0};

    listName = glGenLists (1);
    glNewList (listName, GL_COMPILE);
    glPushAttrib (GL_CURRENT_BIT);
    glColor3fv (color_vector);
    glBegin (GL_TRIANGLES);
    glVertex2f (0.0, 0.0);
    glVertex2f (1.0, 0.0);
    glVertex2f (0.0, 1.0);
    glEnd ();
    glTranslatef (1.5, 0.0, 0.0);
    glPopAttrib ();
    glEndList ();
    glShadeModel (GL_FLAT);
}

void drawLine (void)
{
    glBegin (GL_LINES);
    glVertex2f (0.0, 0.5);
    glVertex2f (15.0, 0.5);
    glEnd ();
}

void CALLBACK display(void)
{
    GLuint i;
    GLfloat new_color[3] = {0.0, 1.0, 0.0};

    glClear (GL_COLOR_BUFFER_BIT);
    glColor3fv (new_color);
    glPushMatrix ();
    for (i = 0; i < 10; i++)
    glCallList (listName);
    glPopMatrix ();
    drawLine ();
    glFlush ();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    gluOrtho2D (0.0, 2.0, -0.5 * (GLfloat) h/(GLfloat) w,
        1.5 * (GLfloat) h/(GLfloat) w);
    else
    gluOrtho2D (0.0, 2.0 * (GLfloat) w/(GLfloat) h, -0.5, 1.5);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 400, 50);
    auxInitWindow ("Display List");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


MAPLIGHT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  maplight.c
 *  This program demonstrates the use of the GL lighting model.
 *  A sphere is drawn using a magenta diffuse reflective and
 *  white specular material property.
 *  A single light source illuminates the object.  This program
 *  illustrates lighting in color map mode.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

/*  Initialize material property, light source, and lighting model.
 */
void myinit(void)
{
    GLint i;

    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
    GLfloat mat_colormap[] = { 16.0, 48.0, 79.0 };
    GLfloat mat_shininess[] = { 10.0 };

    glMaterialfv(GL_FRONT, GL_COLOR_INDEXES, mat_colormap);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);

    for (i = 0; i < 32; i++) {
    auxSetOneColor (16 + i, 1.0 * (i/32.0), 0.0, 1.0 * (i/32.0));
    auxSetOneColor (48 + i, 1.0, 1.0 * (i/32.0), 1.0);
    }
    glClearIndex(0);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    auxSolidSphere(1.0);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w,
        1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
    glOrtho (-1.5*(GLfloat)w/(GLfloat)h,
        1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 *  Open window with initial window size, title bar, color
 *  index display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_INDEX | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Lighting in Color Map Mode");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


MATERIAL.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 * material.c
 * This program demonstrates the use of the GL lighting model.
 * Several objects are drawn using different material characteristics.
 * A single light source illuminates the objects.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

/*  Initialize z-buffer, projection matrix, light source,
 *  and lighting model.  Do not specify a material property here.
 */
void myinit(void)
{
    GLfloat ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat position[] = { 0.0, 3.0, 2.0, 0.0 };
    GLfloat lmodel_ambient[] = { 0.4, 0.4, 0.4, 1.0 };
    GLfloat local_view[] = { 0.0 };

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glClearColor(0.0, 0.1, 0.1, 0.0);
}

/*  Draw twelve spheres in 3 rows with 4 columns.
 *  The spheres in the first row have materials with no ambient reflection.
 *  The second row has materials with significant ambient reflection.
 *  The third row has materials with colored ambient reflection.
 *
 *  The first column has materials with blue, diffuse reflection only.
 *  The second column has blue diffuse reflection, as well as specular
 *  reflection with a low shininess exponent.
 *  The third column has blue diffuse reflection, as well as specular
 *  reflection with a high shininess exponent (a more concentrated highlight).
 *  The fourth column has materials which also include an emissive component.
 *
 *  glTranslatef() is used to move spheres to their appropriate locations.
 */

void CALLBACK display(void)
{
    GLfloat no_mat[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat mat_ambient[] = { 0.7, 0.7, 0.7, 1.0 };
    GLfloat mat_ambient_color[] = { 0.8, 0.8, 0.2, 1.0 };
    GLfloat mat_diffuse[] = { 0.1, 0.5, 0.8, 1.0 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat no_shininess[] = { 0.0 };
    GLfloat low_shininess[] = { 5.0 };
    GLfloat high_shininess[] = { 100.0 };
    GLfloat mat_emission[] = {0.3, 0.2, 0.2, 0.0};

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

/*  draw sphere in first row, first column
 *  diffuse reflection only; no ambient or specular
 */
    glPushMatrix();
    glTranslatef (-3.75, 3.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
    glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    auxSolidSphere(1.0);
    glPopMatrix();

/*  draw sphere in first row, second column
 *  diffuse and specular reflection; low shininess; no ambient
 */
    glPushMatrix();
    glTranslatef (-1.25, 3.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    auxSolidSphere(1.0);
    glPopMatrix();

/*  draw sphere in first row, third column
 *  diffuse and specular reflection; high shininess; no ambient
 */
    glPushMatrix();
    glTranslatef (1.25, 3.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    auxSolidSphere(1.0);
    glPopMatrix();

/*  draw sphere in first row, fourth column
 *  diffuse reflection; emission; no ambient or specular reflection
 */
    glPushMatrix();
    glTranslatef (3.75, 3.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
    glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
    auxSolidSphere(1.0);
    glPopMatrix();

/*  draw sphere in second row, first column
 *  ambient and diffuse reflection; no specular
 */
    glPushMatrix();
    glTranslatef (-3.75, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
    glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    auxSolidSphere(1.0);
    glPopMatrix();

/*  draw sphere in second row, second column
 *  ambient, diffuse and specular reflection; low shininess
 */
    glPushMatrix();
    glTranslatef (-1.25, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    auxSolidSphere(1.0);
    glPopMatrix();

/*  draw sphere in second row, third column
 *  ambient, diffuse and specular reflection; high shininess
 */
    glPushMatrix();
    glTranslatef (1.25, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    auxSolidSphere(1.0);
    glPopMatrix();

/*  draw sphere in second row, fourth column
 *  ambient and diffuse reflection; emission; no specular
 */
    glPushMatrix();
    glTranslatef (3.75, 0.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
    glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
    auxSolidSphere(1.0);
    glPopMatrix();

/*  draw sphere in third row, first column
 *  colored ambient and diffuse reflection; no specular
 */
    glPushMatrix();
    glTranslatef (-3.75, -3.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
    glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    auxSolidSphere(1.0);
    glPopMatrix();

/*  draw sphere in third row, second column
 *  colored ambient, diffuse and specular reflection; low shininess
 */
    glPushMatrix();
    glTranslatef (-1.25, -3.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    auxSolidSphere(1.0);
    glPopMatrix();

/*  draw sphere in third row, third column
 *  colored ambient, diffuse and specular reflection; high shininess
 */
    glPushMatrix();
    glTranslatef (1.25, -3.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    auxSolidSphere(1.0);
    glPopMatrix();

/*  draw sphere in third row, fourth column
 *  colored ambient and diffuse reflection; emission; no specular
 */
    glPushMatrix();
    glTranslatef (3.75, -3.0, 0.0);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
    glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
    glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
    auxSolidSphere(1.0);
    glPopMatrix();

    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= (h * 2))
    glOrtho (-6.0, 6.0, -3.0*((GLfloat)h*2)/(GLfloat)w,
        3.0*((GLfloat)h*2)/(GLfloat)w, -10.0, 10.0);
    else
    glOrtho (-6.0*(GLfloat)w/((GLfloat)h*2),
        6.0*(GLfloat)w/((GLfloat)h*2), -3.0, 3.0, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 600, 450);
    auxInitWindow ("Lighting");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


MIPMAP.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  mipmap.c
 *  This program demonstrates using mipmaps for texture maps.
 *  To overtly show the effect of mipmaps, each mipmap reduction
 *  level has a solidly colored, contrasting texture image.
 *  Thus, the quadrilateral which is drawn is drawn with several
 *  different colors.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void makeImages(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

GLubyte mipmapImage32[32][32][3];
GLubyte mipmapImage16[16][16][3];
GLubyte mipmapImage8[8][8][3];
GLubyte mipmapImage4[4][4][3];
GLubyte mipmapImage2[2][2][3];
GLubyte mipmapImage1[1][1][3];

void makeImages(void)
{
    int i, j;

    for (i = 0; i < 32; i++) {
    for (j = 0; j < 32; j++) {
        mipmapImage32[i][j][0] = 255;
        mipmapImage32[i][j][1] = 255;
        mipmapImage32[i][j][2] = 0;
    }
    }
    for (i = 0; i < 16; i++) {
    for (j = 0; j < 16; j++) {
        mipmapImage16[i][j][0] = 255;
        mipmapImage16[i][j][1] = 0;
        mipmapImage16[i][j][2] = 255;
    }
    }
    for (i = 0; i < 8; i++) {
    for (j = 0; j < 8; j++) {
        mipmapImage8[i][j][0] = 255;
        mipmapImage8[i][j][1] = 0;
        mipmapImage8[i][j][2] = 0;
    }
    }
    for (i = 0; i < 4; i++) {
    for (j = 0; j < 4; j++) {
        mipmapImage4[i][j][0] = 0;
        mipmapImage4[i][j][1] = 255;
        mipmapImage4[i][j][2] = 0;
    }
    }
    for (i = 0; i < 2; i++) {
    for (j = 0; j < 2; j++) {
        mipmapImage2[i][j][0] = 0;
        mipmapImage2[i][j][1] = 0;
        mipmapImage2[i][j][2] = 255;
    }
    }
    mipmapImage1[0][0][0] = 255;
    mipmapImage1[0][0][1] = 255;
    mipmapImage1[0][0][2] = 255;
}

void myinit(void)
{
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    glShadeModel(GL_FLAT);

    glTranslatef(0.0, 0.0, -3.6);
    makeImages();
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, 32, 32, 0,
         GL_RGB, GL_UNSIGNED_BYTE, &mipmapImage32[0][0][0]);
    glTexImage2D(GL_TEXTURE_2D, 1, 3, 16, 16, 0,
         GL_RGB, GL_UNSIGNED_BYTE, &mipmapImage16[0][0][0]);
    glTexImage2D(GL_TEXTURE_2D, 2, 3, 8, 8, 0,
         GL_RGB, GL_UNSIGNED_BYTE, &mipmapImage8[0][0][0]);
    glTexImage2D(GL_TEXTURE_2D, 3, 3, 4, 4, 0,
         GL_RGB, GL_UNSIGNED_BYTE, &mipmapImage4[0][0][0]);
    glTexImage2D(GL_TEXTURE_2D, 4, 3, 2, 2, 0,
         GL_RGB, GL_UNSIGNED_BYTE, &mipmapImage2[0][0][0]);
    glTexImage2D(GL_TEXTURE_2D, 5, 3, 1, 1, 0,
         GL_RGB, GL_UNSIGNED_BYTE, &mipmapImage1[0][0][0]);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
    GL_NEAREST_MIPMAP_NEAREST);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    glEnable(GL_TEXTURE_2D);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex3f(-2.0, -1.0, 0.0);
    glTexCoord2f(0.0, 8.0); glVertex3f(-2.0, 1.0, 0.0);
    glTexCoord2f(8.0, 8.0); glVertex3f(2000.0, 1.0, -6000.0);
    glTexCoord2f(8.0, 0.0); glVertex3f(2000.0, -1.0, -6000.0);
    glEnd();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, 1.0*(GLfloat)w/(GLfloat)h, 1.0, 30000.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Texture Map Using Mipmap");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


MODEL.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  model.c
 *  This program demonstrates the use of OpenGL modeling
 *  transformations.  Four triangles are drawn, each with
 *  a different transformation.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void draw_triangle(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

void draw_triangle(void)
{
    glBegin(GL_LINE_LOOP);
    glVertex2f(0.0, 25.0);
    glVertex2f(25.0, -25.0);
    glVertex2f(-25.0, -25.0);
    glEnd();
}

/*  Clear the screen.  For each triangle, set the current
 *  color and modify the modelview matrix.
 */
void CALLBACK display(void)
{
    glClearColor (0.0, 0.0, 0.0, 1.0);
    glClear (GL_COLOR_BUFFER_BIT);

    glLoadIdentity ();
    glColor3f (1.0, 1.0, 1.0);
    draw_triangle ();

    glEnable (GL_LINE_STIPPLE);
    glLineStipple (1, 0xF0F0);
    glLoadIdentity ();
    glTranslatef (-20.0, 0.0, 0.0);
    draw_triangle ();

    glLineStipple (1, 0xF00F);
    glLoadIdentity ();
    glScalef (1.5, 0.5, 1.0);
    draw_triangle ();

    glLineStipple (1, 0x8888);
    glLoadIdentity ();
    glRotatef (90.0, 0.0, 0.0, 1.0);
    draw_triangle ();
    glDisable (GL_LINE_STIPPLE);

    glFlush();
}

void myinit (void)
{
    glShadeModel (GL_FLAT);
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-50.0, 50.0, -50.0*(GLfloat)h/(GLfloat)w,
        50.0*(GLfloat)h/(GLfloat)w, -1.0, 1.0);
    else
    glOrtho (-50.0*(GLfloat)w/(GLfloat)h,
        50.0*(GLfloat)w/(GLfloat)h, -50.0, 50.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Modeling Transformations");
    myinit ();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


MOVELGHT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  movelight.c
 *  This program demonstrates when to issue lighting and
 *  transformation commands to render a model with a light
 *  which is moved by a modeling transformation (rotate or
 *  translate).  The light position is reset after the modeling
 *  transformation is called.  The eye position does not change.
 *
 *  A sphere is drawn using a grey material characteristic.
 *  A single light source illuminates the object.
 *
 *  Interaction:  pressing the left or middle mouse button
 *  alters the modeling transformation (x rotation) by 30 degrees.
 *  The scene is then redrawn with the light in a new position.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK movelight (AUX_EVENTREC *event);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

static int spin = 0;

void CALLBACK movelight (AUX_EVENTREC *event)
{
    spin = (spin + 30) % 360;
}

void myinit (void)
{
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

/*  Here is where the light position is reset after the modeling
 *  transformation (glRotated) is called.  This places the
 *  light at a new position in world coordinates.  The cube
 *  represents the position of the light.
 */
void CALLBACK display(void)
{
    GLfloat position[] = { 0.0, 0.0, 1.5, 1.0 };

    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix ();
    glTranslatef (0.0, 0.0, -5.0);

    glPushMatrix ();
    glRotated ((GLdouble) spin, 1.0, 0.0, 0.0);
    glRotated (0.0, 1.0, 0.0, 0.0);
    glLightfv (GL_LIGHT0, GL_POSITION, position);

    glTranslated (0.0, 0.0, 1.5);
    glDisable (GL_LIGHTING);
    glColor3f (0.0, 1.0, 1.0);
    auxWireCube (0.1);
    glEnable (GL_LIGHTING);
    glPopMatrix ();

    auxSolidTorus (0.275, 0.85);
    glPopMatrix ();
    glFlush ();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(40.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Moving Light");
    myinit();
    auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, movelight);
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


NURBS.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  nurbs.c
 *  This program shows a NURBS (Non-uniform rational B-splines)
 *  surface, shaped like a heart.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

#define S_NUMPOINTS 13
#define S_ORDER     3
#define S_NUMKNOTS  (S_NUMPOINTS + S_ORDER)
#define T_NUMPOINTS 3
#define T_ORDER     3
#define T_NUMKNOTS  (T_NUMPOINTS + T_ORDER)
#define SQRT2    1.41421356237309504880

/* initialized local data */

GLfloat sknots[S_NUMKNOTS] =
    {-1.0, -1.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0,
      4.0,  5.0,  6.0, 7.0, 8.0, 9.0, 9.0, 9.0};
GLfloat tknots[T_NUMKNOTS] = {1.0, 1.0, 1.0, 2.0, 2.0, 2.0};

GLfloat ctlpoints[S_NUMPOINTS][T_NUMPOINTS][4] = {
{   {4.,2.,2.,1.},{4.,1.6,2.5,1.},{4.,2.,3.0,1.}    },
{   {5.,4.,2.,1.},{5.,4.,2.5,1.},{5.,4.,3.0,1.} },
{   {6.,5.,2.,1.},{6.,5.,2.5,1.},{6.,5.,3.0,1.} },
{   {SQRT2*6.,SQRT2*6.,SQRT2*2.,SQRT2},
    {SQRT2*6.,SQRT2*6.,SQRT2*2.5,SQRT2},
    {SQRT2*6.,SQRT2*6.,SQRT2*3.0,SQRT2}  },
{   {5.2,6.7,2.,1.},{5.2,6.7,2.5,1.},{5.2,6.7,3.0,1.}   },
{   {SQRT2*4.,SQRT2*6.,SQRT2*2.,SQRT2},
    {SQRT2*4.,SQRT2*6.,SQRT2*2.5,SQRT2},
    {SQRT2*4.,SQRT2*6.,SQRT2*3.0,SQRT2}  },
{   {4.,5.2,2.,1.},{4.,4.6,2.5,1.},{4.,5.2,3.0,1.}  },
{   {SQRT2*4.,SQRT2*6.,SQRT2*2.,SQRT2},
    {SQRT2*4.,SQRT2*6.,SQRT2*2.5,SQRT2},
    {SQRT2*4.,SQRT2*6.,SQRT2*3.0,SQRT2}  },
{   {2.8,6.7,2.,1.},{2.8,6.7,2.5,1.},{2.8,6.7,3.0,1.}   },
{   {SQRT2*2.,SQRT2*6.,SQRT2*2.,SQRT2},
    {SQRT2*2.,SQRT2*6.,SQRT2*2.5,SQRT2},
    {SQRT2*2.,SQRT2*6.,SQRT2*3.0,SQRT2}  },
{   {2.,5.,2.,1.},{2.,5.,2.5,1.},{2.,5.,3.0,1.} },
{   {3.,4.,2.,1.},{3.,4.,2.5,1.},{3.,4.,3.0,1.} },
{   {4.,2.,2.,1.},{4.,1.6,2.5,1.},{4.,2.,3.0,1.}    }
};

GLUnurbsObj *theNurb;

/*  Initialize material property, light source, lighting model,
 *  and depth buffer.
 */
void myinit(void)
{
    GLfloat mat_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_diffuse[] = { 1.0, 0.2, 1.0, 1.0 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 50.0 };

    GLfloat light0_position[] = { 1.0, 0.1, 1.0, 0.0 };
    GLfloat light1_position[] = { -1.0, 0.1, 1.0, 0.0 };

    GLfloat lmodel_ambient[] = { 0.3, 0.3, 0.3, 1.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
    glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHT1);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_AUTO_NORMAL);

    theNurb = gluNewNurbsRenderer();

    glNewList (1, GL_COMPILE);
    gluBeginSurface(theNurb);
    gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, GLU_FILL);
    gluNurbsSurface(theNurb,
        S_NUMKNOTS, sknots,
        T_NUMKNOTS, tknots,
        4 * T_NUMPOINTS,
        4,
        &ctlpoints[0][0][0],
        S_ORDER, T_ORDER,
        GL_MAP2_VERTEX_4);
    gluEndSurface(theNurb);
    glEndList ();

}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);

    glPushMatrix();
    glTranslatef (4., 4.5, 2.5);
    glRotatef (220.0, 1., 0., 0.);
    glRotatef (115.0, 0., 1., 0.);
    glTranslatef (-4., -4.5, -2.5);
    glCallList (1);
    glPopMatrix();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-2.0, 2.0, -2.0, 2.0, 0.8, 10.0);
    gluLookAt(7.0,4.5,4.0, 4.5,4.5,2.0, 6.0,-3.0,2.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("NURBS Surface");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


PICKDPTH.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  pickdepth.c
 *  Picking is demonstrated in this program.  In
 *  rendering mode, three overlapping rectangles are
 *  drawn.  When the left mouse button is pressed,
 *  selection mode is entered with the picking matrix.
 *  Rectangles which are drawn under the cursor position
 *  are "picked."  Pay special attention to the depth
 *  value range, which is returned.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include <stdio.h>

void myinit(void);
void drawRects(GLenum mode);
void processHits (GLint hits, GLuint buffer[]);
void CALLBACK pickRects(AUX_EVENTREC *event);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

void myinit(void)
{
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_FLAT);
    glDepthRange (0.0, 1.0);    /*  The default z mapping   */
}

/*  The three rectangles are drawn.  In selection mode,
 *  each rectangle is given the same name.  Note that
 *  each rectangle is drawn with a different z value.
 */
void drawRects(GLenum mode)
{
    if (mode == GL_SELECT)
    glLoadName (1);
    glBegin (GL_QUADS);
    glColor3f (1.0, 1.0, 0.0);
    glVertex3i (2, 0, 0);
    glVertex3i (2, 6, 0);
    glVertex3i (6, 6, 0);
    glVertex3i (6, 0, 0);
    glColor3f (0.0, 1.0, 1.0);
    glVertex3i (3, 2, -1);
    glVertex3i (3, 8, -1);
    glVertex3i (8, 8, -1);
    glVertex3i (8, 2, -1);
    glColor3f (1.0, 0.0, 1.0);
    glVertex3i (0, 2, -2);
    glVertex3i (0, 7, -2);
    glVertex3i (5, 7, -2);
    glVertex3i (5, 2, -2);
    glEnd ();
}

/*  processHits() prints out the contents of the
 *  selection array.
 */
void processHits (GLint hits, GLuint buffer[])
{
    int i;
    GLint names, *ptr;

    //printf ("hits = %d\n", hits);
    ptr = (GLuint *) buffer;
    for (i = 0; i < hits; i++) {    /*  for each hit  */
        names = *ptr;
        //printf (" number of names for hit = %d\n", names); ptr++;
        //printf ("  z1 is %u;", *ptr); ptr++;
        //printf (" z2 is %u\n", *ptr); ptr++;
        //printf ("   the name is ");
/*
        for (j = 0; j < names; j++) {
            printf ("%d ", *ptr); ptr++;
        }
*/
        //printf ("\n");
    }
}

/*  pickRects() sets up selection mode, name stack,
 *  and projection matrix for picking.  Then the objects
 *  are drawn.
 */
#define BUFSIZE 512

void CALLBACK pickRects(AUX_EVENTREC *event)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits;
    GLint viewport[4];
    int x, y;

    x = event->data[AUX_MOUSEX];
    y = event->data[AUX_MOUSEY];
    glGetIntegerv (GL_VIEWPORT, viewport);

    glSelectBuffer (BUFSIZE, selectBuf);
    (void) glRenderMode (GL_SELECT);

    glInitNames();
    glPushName((unsigned)-1);

    glMatrixMode (GL_PROJECTION);
    glPushMatrix ();
    glLoadIdentity ();
/*  create 5x5 pixel picking region near cursor location    */
    gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y),
    5.0, 5.0, viewport);
    glOrtho (0.0, 8.0, 0.0, 8.0, -0.5, 2.5);
    drawRects (GL_SELECT);
    glPopMatrix ();
    glFlush ();

    hits = glRenderMode (GL_RENDER);
    //processHits (hits, selectBuf);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    drawRects (GL_RENDER);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho (0.0, 8.0, 0.0, 8.0, 0.0, 2.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, depth buffer, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 100, 100);
    auxInitWindow ("Picking Rectangles");
    myinit ();
    auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, pickRects);
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


PICKLINE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  pickline.c
 *  Picking is demonstrated here.  Press the left mouse button
 *  is enter picking mode.  You get two hits if you press the
 *  mouse, while the cursor is where the lines intersect.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include <stdio.h>

void myinit(void);
void drawLine(GLenum mode);
void CALLBACK pickLine(AUX_EVENTREC *event);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

void drawLine(GLenum mode)
{
    if (mode == GL_SELECT)
    glLoadName (1);
    glBegin (GL_LINES);
    glColor3f (1.0, 1.0, 1.0);
    glVertex3f (30.0, 30.0, 0.0);
    glVertex3f (50.0, 60.0, 0.0);
    glEnd ();

    if (mode == GL_SELECT)
    glLoadName (2);
    glBegin (GL_LINES);
    glColor3f (1.0, 1.0, 1.0);
    glVertex3f (50.0, 60.0, 0.0);
    glVertex3f (70.0, 40.0, 0.0);
    glEnd ();
}

/*  pickline() is called when the mouse is pressed.
 *  The projection matrix is reloaded to include the
 *  picking matrix.  The line is "redrawn" during
 *  selection mode, and names are sent to the buffer.
 */
#define BUFSIZE 512

void CALLBACK pickLine(AUX_EVENTREC *event)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits;
    GLint viewport[4];
    int x, y;

    x = event->data[AUX_MOUSEX];
    y = event->data[AUX_MOUSEY];
    glGetIntegerv (GL_VIEWPORT, viewport);

    glSelectBuffer (BUFSIZE, selectBuf);
    (void) glRenderMode (GL_SELECT);

    glInitNames();
    glPushName((GLuint)-1);

    glMatrixMode (GL_PROJECTION);
    glPushMatrix ();
    glLoadIdentity ();
    gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y),
    5.0, 5.0, viewport);
    gluOrtho2D (0.0, (GLdouble) viewport[2],
    0.0, (GLdouble) viewport[3]);
    drawLine (GL_SELECT);
    glPopMatrix ();
    glFlush ();

    hits = glRenderMode (GL_RENDER);
    //printf ("hits is %d\n", hits);
}

void myinit (void)
{
    glShadeModel (GL_FLAT);
    glClearColor (0.0, 0.0, 0.0, 0.0);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    drawLine (GL_RENDER);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D (0.0, (GLdouble) w, 0.0, (GLdouble) h);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 100, 100);
    auxInitWindow ("Picking Lines");
    myinit ();
    auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, pickLine);
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


PICKSQR.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  picksquare.c
 *  Use of multiple names and picking are demonstrated.
 *  A 3x3 grid of squares is drawn.  When the left mouse
 *  button is pressed, all squares under the cursor position
 *  have their color changed.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include <stdio.h>

void myinit(void);
void processHits (GLint hits, GLuint buffer[]);
void CALLBACK pickSquares(AUX_EVENTREC *event);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

int board[3][3];    /*  amount of color for each square */

/*  Clear color value for every square on the board     */
void myinit(void)
{
    int i, j;
    for (i = 0; i < 3; i++)
    for (j = 0; j < 3; j ++)
        board[i][j] = 0;
    glClearColor (0.0, 0.0, 0.0, 0.0);
}

/*  The nine squares are drawn.  In selection mode, each
 *  square is given two names:  one for the row and the
 *  other for the column on the grid.  The color of each
 *  square is determined by its position on the grid, and
 *  the value in the board[][] array.
 */
void drawSquares(GLenum mode)
{
    GLuint i, j;
    for (i = 0; i < 3; i++) {
    if (mode == GL_SELECT)
        glLoadName (i);
    for (j = 0; j < 3; j ++) {
        if (mode == GL_SELECT)
        glPushName (j);
        glColor3f ((GLfloat) i/3.0, (GLfloat) j/3.0,
            (GLfloat) board[i][j]/3.0);
        glRecti (i, j, i+1, j+1);
        if (mode == GL_SELECT)
        glPopName ();
    }
    }
}

/*  processHits() prints out the contents of the
 *  selection array.
 */
void processHits (GLint hits, GLuint buffer[])
{
    int i, j;
    GLint ii, jj, names, *ptr;

    //printf ("hits = %d\n", hits);
    ptr = (GLuint *) buffer;
    for (i = 0; i < hits; i++) {    /*  for each hit  */
    names = *ptr;
    //printf (" number of names for this hit = %d\n", names);
    ptr++;
    //printf ("  z1 is %u;", *ptr);
    ptr++;
    //printf (" z2 is %u\n", *ptr);
    ptr++;
    //printf ("   names are ");
    for (j = 0; j < names; j++) {   /*  for each name */
        //printf ("%d ", *ptr);
        if (j == 0) /*  set row and column  */
        ii = *ptr;
        else if (j == 1)
        jj = *ptr;
        ptr++;
    }
    //printf ("\n");
    board[ii][jj] = (board[ii][jj] + 1) % 3;
    }
}

/*  pickSquares() sets up selection mode, name stack,
 *  and projection matrix for picking.  Then the
 *  objects are drawn.
 */
#define BUFSIZE 512

void CALLBACK pickSquares(AUX_EVENTREC *event)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits;
    GLint viewport[4];
    int x, y;

    x = event->data[AUX_MOUSEX];
    y = event->data[AUX_MOUSEY];
    glGetIntegerv (GL_VIEWPORT, viewport);

    glSelectBuffer (BUFSIZE, selectBuf);
    (void) glRenderMode (GL_SELECT);

    glInitNames();
    glPushName((unsigned)-1);

    glMatrixMode (GL_PROJECTION);
    glPushMatrix ();
    glLoadIdentity ();
/*  create 5x5 pixel picking region near cursor location    */
    gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y),
    5.0, 5.0, viewport);
    gluOrtho2D (0.0, 3.0, 0.0, 3.0);
    drawSquares (GL_SELECT);

    glMatrixMode (GL_PROJECTION);
    glPopMatrix ();
    glFlush ();

    hits = glRenderMode (GL_RENDER);
    processHits (hits, selectBuf);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    drawSquares (GL_RENDER);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D (0.0, 3.0, 0.0, 3.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 100, 100);
    auxInitWindow ("Picking Squares");
    myinit ();
    auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, pickSquares);
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


PLANE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  plane.c
 *  This program demonstrates the use of local versus
 *  infinite lighting on a flat plane.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void drawPlane(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

/*  Initialize material property, light source, and lighting model.
 */
void myinit(void)
{
    GLfloat mat_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
/*   mat_specular and mat_shininess are NOT default values  */
    GLfloat mat_diffuse[] = { 0.4, 0.4, 0.4, 1.0 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 15.0 };

    GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

void drawPlane(void)
{
    glBegin (GL_QUADS);
    glNormal3f (0.0, 0.0, 1.0);
    glVertex3f (-1.0, -1.0, 0.0);
    glVertex3f (0.0, -1.0, 0.0);
    glVertex3f (0.0, 0.0, 0.0);
    glVertex3f (-1.0, 0.0, 0.0);

    glNormal3f (0.0, 0.0, 1.0);
    glVertex3f (0.0, -1.0, 0.0);
    glVertex3f (1.0, -1.0, 0.0);
    glVertex3f (1.0, 0.0, 0.0);
    glVertex3f (0.0, 0.0, 0.0);

    glNormal3f (0.0, 0.0, 1.0);
    glVertex3f (0.0, 0.0, 0.0);
    glVertex3f (1.0, 0.0, 0.0);
    glVertex3f (1.0, 1.0, 0.0);
    glVertex3f (0.0, 1.0, 0.0);

    glNormal3f (0.0, 0.0, 1.0);
    glVertex3f (0.0, 0.0, 0.0);
    glVertex3f (0.0, 1.0, 0.0);
    glVertex3f (-1.0, 1.0, 0.0);
    glVertex3f (-1.0, 0.0, 0.0);
    glEnd();
}

void CALLBACK display (void)
{
    GLfloat infinite_light[] = { 1.0, 1.0, 1.0, 0.0 };
    GLfloat local_light[] = { 1.0, 1.0, 1.0, 1.0 };

    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix ();
    glTranslatef (-1.5, 0.0, 0.0);
    glLightfv (GL_LIGHT0, GL_POSITION, infinite_light);
    drawPlane ();
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (1.5, 0.0, 0.0);
    glLightfv (GL_LIGHT0, GL_POSITION, local_light);
    drawPlane ();
    glPopMatrix ();
    glFlush ();
}

void CALLBACK myReshape (GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport (0, 0, w, h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    if (w <= h)
    glOrtho (-1.5, 1.5, -1.5*(GLdouble)h/(GLdouble)w,
        1.5*(GLdouble)h/(GLdouble)w, -10.0, 10.0);
    else
    glOrtho (-1.5*(GLdouble)w/(GLdouble)h,
        1.5*(GLdouble)w/(GLdouble)h, -1.5, 1.5, -10.0, 10.0);
    glMatrixMode (GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 200);
    auxInitWindow ("Local vs Infinite Lighting");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


PLANET.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  planet.c
 *  This program shows how to composite modeling transformations
 *  to draw translated and rotated models.
 *  Interaction:  pressing the left, right, up, and down
 *  arrow keys alters the rotation of the planet around the sun.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void drawPlane(void);
void CALLBACK dayAdd (void);
void CALLBACK daySubtract (void);
void CALLBACK yearAdd (void);
void CALLBACK yearSubtract (void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

static int year = 0, day = 0;

void CALLBACK dayAdd (void)
{
    day = (day + 10) % 360;
}

void CALLBACK daySubtract (void)
{
    day = (day - 10) % 360;
}

void CALLBACK yearAdd (void)
{
    year = (year + 5) % 360;
}

void CALLBACK yearSubtract (void)
{
    year = (year - 5) % 360;
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    glColor3f (1.0, 1.0, 1.0);
    glPushMatrix();
/*  draw sun    */
    auxWireSphere(1.0);
/*  draw smaller planet */
    glRotatef ((GLfloat) year, 0.0, 1.0, 0.0);
    glTranslatef (2.0, 0.0, 0.0);
    glRotatef ((GLfloat) day, 0.0, 1.0, 0.0);
    auxWireSphere(0.2);
    glPopMatrix();
    glFlush();
}

void myinit (void) {
    glShadeModel (GL_FLAT);
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef (0.0, 0.0, -5.0);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Composite Modeling Transformations");
    myinit ();
    auxKeyFunc (AUX_LEFT, yearSubtract);
    auxKeyFunc (AUX_RIGHT, yearAdd);
    auxKeyFunc (AUX_UP, dayAdd);
    auxKeyFunc (AUX_DOWN, daySubtract);
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


PLANETUP.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  planetup.c
 *  The planets (from planet.c) have been rotated so their
 *  polar regions are north/south.
 *  Interaction:  pressing the left, right, up, and down
 *  arrow keys alters the rotation of the planet around the sun.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void drawPlane(void);
void CALLBACK dayAdd (void);
void CALLBACK daySubtract (void);
void CALLBACK yearAdd (void);
void CALLBACK yearSubtract (void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

static int year = 0, day = 0;

void CALLBACK dayAdd (void)
{
    day = (day + 10) % 360;
}

void CALLBACK daySubtract (void)
{
    day = (day - 10) % 360;
}

void CALLBACK yearAdd (void)
{
    year = (year + 5) % 360;
}

void CALLBACK yearSubtract (void)
{
    year = (year - 5) % 360;
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    glColor3f (1.0, 1.0, 1.0);
    glPushMatrix();
/*  draw sun    */
    glPushMatrix();
    glRotatef (90.0, 1.0, 0.0, 0.0);  /*  rotate it upright  */
    auxWireSphere(1.0);
    glPopMatrix();
/*  draw smaller planet */
    glRotatef ((GLfloat) year, 0.0, 1.0, 0.0);
    glTranslatef (2.0, 0.0, 0.0);
    glRotatef ((GLfloat) day, 0.0, 1.0, 0.0);
    glRotatef (90.0, 1.0, 0.0, 0.0);  /*  rotate it upright  */
    auxWireSphere(0.2);
    glPopMatrix();
    glFlush();
}

void myinit (void) {
    glShadeModel (GL_FLAT);
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef (0.0, 0.0, -5.0);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Composite Modeling Transformations");
    myinit ();
    auxKeyFunc (AUX_LEFT, yearSubtract);
    auxKeyFunc (AUX_RIGHT, yearAdd);
    auxKeyFunc (AUX_UP, dayAdd);
    auxKeyFunc (AUX_DOWN, daySubtract);
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


POLYS.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  polys.c
 *  This program demonstrates polygon stippling.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);

void CALLBACK display(void)
{
    GLubyte fly[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x80, 0x01, 0xC0, 0x06, 0xC0, 0x03, 0x60, 0x04, 0x60, 0x06, 0x20,
0x04, 0x30, 0x0C, 0x20, 0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20,
0x04, 0x06, 0x60, 0x20, 0x44, 0x03, 0xC0, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22, 0x66, 0x01, 0x80, 0x66,
0x33, 0x01, 0x80, 0xCC, 0x19, 0x81, 0x81, 0x98, 0x0C, 0xC1, 0x83, 0x30,
0x07, 0xe1, 0x87, 0xe0, 0x03, 0x3f, 0xfc, 0xc0, 0x03, 0x31, 0x8c, 0xc0,
0x03, 0x33, 0xcc, 0xc0, 0x06, 0x64, 0x26, 0x60, 0x0c, 0xcc, 0x33, 0x30,
0x18, 0xcc, 0x33, 0x18, 0x10, 0xc4, 0x23, 0x08, 0x10, 0x63, 0xC6, 0x08,
0x10, 0x30, 0x0c, 0x08, 0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08};

    GLubyte halftone[] = {
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55};

    glClear (GL_COLOR_BUFFER_BIT);

/*  draw all polygons in white  */
    glColor3f (1.0, 1.0, 1.0);

/*  draw one solid, unstippled rectangle,   */
/*  then two stippled rectangles        */
    glRectf (25.0, 25.0, 125.0, 125.0);
    glEnable (GL_POLYGON_STIPPLE);
    glPolygonStipple (fly);
    glRectf (125.0, 25.0, 225.0, 125.0);
    glPolygonStipple (halftone);
    glRectf (225.0, 25.0, 325.0, 125.0);
    glDisable (GL_POLYGON_STIPPLE);

    glFlush ();
}

void myinit (void)
{
/*  clear background to black   */
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glShadeModel (GL_FLAT);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 350, 150);
    auxInitWindow ("Polygon Stippling");
    myinit ();
    auxMainLoop(display);
    return(0);
}


ROBOT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  robot.c
 *  This program shows how to composite modeling transformations
 *  to draw translated and rotated hierarchical models.
 *  Interaction:  pressing the arrow keys alters the rotation
 *  of robot arm.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void drawPlane(void);
void CALLBACK elbowAdd (void);
void CALLBACK elbowSubtract (void);
void CALLBACK shoulderAdd (void);
void CALLBACK shoulderSubtract (void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

static int shoulder = 0, elbow = 0;

void CALLBACK elbowAdd (void)
{
    elbow = (elbow + 5) % 360;
}

void CALLBACK elbowSubtract (void)
{
    elbow = (elbow - 5) % 360;
}

void CALLBACK shoulderAdd (void)
{
    shoulder = (shoulder + 5) % 360;
}

void CALLBACK shoulderSubtract (void)
{
    shoulder = (shoulder - 5) % 360;
}

void CALLBACK display(void)
{

    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f (1.0, 1.0, 1.0);

    glPushMatrix();
    glTranslatef (-1.0, 0.0, 0.0);
    glRotatef ((GLfloat) shoulder, 0.0, 0.0, 1.0);
    glTranslatef (1.0, 0.0, 0.0);
    auxWireBox(2.0, 0.4, 1.0);

    glTranslatef (1.0, 0.0, 0.0);
    glRotatef ((GLfloat) elbow, 0.0, 0.0, 1.0);
    glTranslatef (1.0, 0.0, 0.0);
    auxWireBox(2.0, 0.4, 1.0);

    glPopMatrix();
    glFlush();
}

void myinit (void)
{
    glShadeModel (GL_FLAT);
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef (0.0, 0.0, -5.0);  /* viewing transform  */
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 400, 400);
    auxInitWindow ("Composite Modeling Transformations");

    myinit ();

    auxKeyFunc (AUX_LEFT, shoulderSubtract);
    auxKeyFunc (AUX_RIGHT, shoulderAdd);
    auxKeyFunc (AUX_UP, elbowAdd);
    auxKeyFunc (AUX_DOWN, elbowSubtract);
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


SCCLRLT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  sccolorlight.c
 *  This program demonstrates the use of a colored
 *  (magenta, in this example) light source.  Objects
 *  are drawn using a grey material characteristic.
 *  A single light source illuminates the objects.
 */
#include "glos.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

/*  Initialize material property and light source.
 */
void myinit(void)
{
    GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 0.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 0.0, 1.0, 1.0 };
/*  light_position is NOT default value */
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix ();
    glRotatef (20.0, 1.0, 0.0, 0.0);

    glPushMatrix ();
    glTranslatef (-0.75, 0.5, 0.0);
    glRotatef (90.0, 1.0, 0.0, 0.0);
    auxSolidTorus (0.275, 0.85);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (-0.75, -0.5, 0.0);
    glRotatef (270.0, 1.0, 0.0, 0.0);
    auxSolidCone (1.0, 2.0);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (0.75, 0.0, -1.0);
    auxSolidSphere (1.0);
    glPopMatrix ();

    glPopMatrix ();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-2.5, 2.5, -2.5*(GLfloat)h/(GLfloat)w,
        2.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
    glOrtho (-2.5*(GLfloat)w/(GLfloat)h,
        2.5*(GLfloat)w/(GLfloat)h, -2.5, 2.5, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Colored Light Source");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


SCENE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  scene.c
 *  This program demonstrates the use of the GL lighting model.
 *  Objects are drawn using a grey material characteristic.
 *  A single light source illuminates the objects.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

/*  Initialize material property and light source.
 */
void myinit (void)
{
    GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
/*  light_position is NOT default value */
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

    glLightfv (GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv (GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv (GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv (GL_LIGHT0, GL_POSITION, light_position);

    glEnable (GL_LIGHTING);
    glEnable (GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

void CALLBACK display (void)
{
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix ();
    glRotatef (20.0, 1.0, 0.0, 0.0);

    glPushMatrix ();
    glTranslatef (-0.75, 0.5, 0.0);
    glRotatef (90.0, 1.0, 0.0, 0.0);
    auxSolidTorus (0.275, 0.85);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (-0.75, -0.5, 0.0);
    glRotatef (270.0, 1.0, 0.0, 0.0);
    auxSolidCone (1.0, 2.0);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (0.75, 0.0, -1.0);
    auxSolidSphere (1.0);
    glPopMatrix ();

    glPopMatrix ();
    glFlush ();
}

void CALLBACK myReshape (GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport (0, 0, w, h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    if (w <= h)
    glOrtho (-2.5, 2.5, -2.5*(GLfloat)h/(GLfloat)w,
        2.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
    glOrtho (-2.5*(GLfloat)w/(GLfloat)h,
        2.5*(GLfloat)w/(GLfloat)h, -2.5, 2.5, -10.0, 10.0);
    glMatrixMode (GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Lighting");
    myinit ();
    auxReshapeFunc (myReshape);
    auxMainLoop (display);
    return(0);
}


SCENEBMB.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  scenebamb.c
 *  This program demonstrates use of a blue ambient light
 *  source.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

/*  Initialize light source and lighting.
 */
void myinit(void)
{
    GLfloat light_ambient[] = { 0.0, 0.0, 1.0, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
/*  light_position is NOT default value */
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix ();
    glRotatef (20.0, 1.0, 0.0, 0.0);

    glPushMatrix ();
    glTranslatef (-0.75, 0.5, 0.0);
    glRotatef (90.0, 1.0, 0.0, 0.0);
    auxSolidTorus (0.275, 0.85);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (-0.75, -0.5, 0.0);
    glRotatef (270.0, 1.0, 0.0, 0.0);
    auxSolidCone (1.0, 2.0);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (0.75, 0.0, -1.0);
    auxSolidSphere (1.0);
    glPopMatrix ();

    glPopMatrix ();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-2.5, 2.5, -2.5*(GLfloat)h/(GLfloat)w,
        2.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
    glOrtho (-2.5*(GLfloat)w/(GLfloat)h,
        2.5*(GLfloat)w/(GLfloat)h, -2.5, 2.5, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Blue Ambient Light Source");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


SCENEFLT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  sceneflat.c
 *  This program draws lighted objects with flat shading.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

/*  Initialize light source and shading model (GL_FLAT).
 */
void myinit(void)
{
    GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
/*  light_position is NOT default value */
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glShadeModel (GL_FLAT);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix ();
    glRotatef (20.0, 1.0, 0.0, 0.0);

    glPushMatrix ();
    glTranslatef (-0.75, 0.5, 0.0);
    glRotatef (90.0, 1.0, 0.0, 0.0);
    auxSolidTorus (0.275, 0.85);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (-0.75, -0.5, 0.0);
    glRotatef (270.0, 1.0, 0.0, 0.0);
    auxSolidCone (1.0, 2.0);
    glPopMatrix ();

    glPushMatrix ();
    glTranslatef (0.75, 0.0, -1.0);
    auxSolidSphere (1.0);
    glPopMatrix ();

    glPopMatrix ();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-2.5, 2.5, -2.5*(GLfloat)h/(GLfloat)w,
        2.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
    glOrtho (-2.5*(GLfloat)w/(GLfloat)h,
        2.5*(GLfloat)w/(GLfloat)h, -2.5, 2.5, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Flat Shading");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


SELECT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  select.c
 *  This is an illustration of the selection mode and
 *  name stack, which detects whether objects which collide
 *  with a viewing volume.  First, four triangles and a
 *  rectangular box representing a viewing volume are drawn
 *  (drawScene routine).  The green triangle and yellow
 *  triangles appear to lie within the viewing volume, but
 *  the red triangle appears to lie outside it.  Then the
 *  selection mode is entered (selectObjects routine).
 *  Drawing to the screen ceases.  To see if any collisions
 *  occur, the four triangles are called.  In this example,
 *  the green triangle causes one hit with the name 1, and
 *  the yellow triangles cause one hit with the name 3.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include <stdio.h>

void myinit(void);
void drawTriangle (GLfloat x1, GLfloat y1, GLfloat x2,
    GLfloat y2, GLfloat x3, GLfloat y3, GLfloat z);
void drawViewVolume (GLfloat x1, GLfloat x2, GLfloat y1,
    GLfloat y2, GLfloat z1, GLfloat z2);
void drawScene (void);
void processHits (GLint hits, GLuint buffer[]);
void selectObjects(void);
void CALLBACK display(void);

/*  draw a triangle with vertices at (x1, y1), (x2, y2)
 *  and (x3, y3) at z units away from the origin.
 */
void drawTriangle (GLfloat x1, GLfloat y1, GLfloat x2,
    GLfloat y2, GLfloat x3, GLfloat y3, GLfloat z)
{
    glBegin (GL_TRIANGLES);
    glVertex3f (x1, y1, z);
    glVertex3f (x2, y2, z);
    glVertex3f (x3, y3, z);
    glEnd ();
}

/*  draw a rectangular box with these outer x, y, and z values  */
void drawViewVolume (GLfloat x1, GLfloat x2, GLfloat y1,
    GLfloat y2, GLfloat z1, GLfloat z2)
{
    glColor3f (1.0, 1.0, 1.0);
    glBegin (GL_LINE_LOOP);
    glVertex3f (x1, y1, -z1);
    glVertex3f (x2, y1, -z1);
    glVertex3f (x2, y2, -z1);
    glVertex3f (x1, y2, -z1);
    glEnd ();

    glBegin (GL_LINE_LOOP);
    glVertex3f (x1, y1, -z2);
    glVertex3f (x2, y1, -z2);
    glVertex3f (x2, y2, -z2);
    glVertex3f (x1, y2, -z2);
    glEnd ();

    glBegin (GL_LINES); /*  4 lines */
    glVertex3f (x1, y1, -z1);
    glVertex3f (x1, y1, -z2);
    glVertex3f (x1, y2, -z1);
    glVertex3f (x1, y2, -z2);
    glVertex3f (x2, y1, -z1);
    glVertex3f (x2, y1, -z2);
    glVertex3f (x2, y2, -z1);
    glVertex3f (x2, y2, -z2);
    glEnd ();
}

/*  drawScene() draws 4 triangles and a wire frame
 *  which represents the viewing volume.
 */
void drawScene (void)
{
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective (40.0, 4.0/3.0, 0.01, 100.0);

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();
    gluLookAt (7.5, 7.5, 12.5, 2.5, 2.5, -5.0, 0.0, 1.0, 0.0);
    glColor3f (0.0, 1.0, 0.0);  /*  green triangle  */
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, -5.0);
    glColor3f (1.0, 0.0, 0.0);  /*  red triangle    */
    drawTriangle (2.0, 7.0, 3.0, 7.0, 2.5, 8.0, -5.0);
    glColor3f (1.0, 1.0, 0.0);  /*  yellow triangles    */
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, 0.0);
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, -10.0);
    drawViewVolume (0.0, 5.0, 0.0, 5.0, 0.0, 10.0);
}

/*  processHits() prints out the contents of the selection array.
 */
void processHits (GLint hits, GLuint buffer[])
{
    int i;
    GLint names, *ptr;

    //printf ("hits = %d\n", hits);
    ptr = (GLuint *) buffer;
    for (i = 0; i < hits; i++) {    /*  for each hit  */
        names = *ptr;
        //printf (" number of names for hit = %d\n", names); ptr++;
        //printf ("  z1 is %u;", *ptr); ptr++;
        //printf (" z2 is %u\n", *ptr); ptr++;
        //printf ("   the name is ");
/*
        for (j = 0; j < names; j++) {
            //printf ("%d ", *ptr); ptr++;
        }
*/
        //printf ("\n");
    }
}

/*  selectObjects() "draws" the triangles in selection mode,
 *  assigning names for the triangles.  Note that the third
 *  and fourth triangles share one name, so that if either
 *  or both triangles intersects the viewing/clipping volume,
 *  only one hit will be registered.
 */
#define BUFSIZE 512

void selectObjects(void)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits;

    glSelectBuffer (BUFSIZE, selectBuf);
    (void) glRenderMode (GL_SELECT);

    glInitNames();
    glPushName((unsigned)-1);

    glPushMatrix ();
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    glOrtho (0.0, 5.0, 0.0, 5.0, 0.0, 10.0);
    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();
    glLoadName(1);
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, -5.0);
    glLoadName(2);
    drawTriangle (2.0, 7.0, 3.0, 7.0, 2.5, 8.0, -5.0);
    glLoadName(3);
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, 0.0);
    drawTriangle (2.0, 2.0, 3.0, 2.0, 2.5, 3.0, -10.0);
    glPopMatrix ();
    glFlush ();

    hits = glRenderMode (GL_RENDER);
    processHits (hits, selectBuf);
}

void myinit (void)
{
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_FLAT);
}

void CALLBACK display(void)
{
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    drawScene ();
    selectObjects ();
    glFlush();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, depth buffer, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 200, 200);
    auxInitWindow ("Selection Mode");
    myinit ();
    auxMainLoop(display);
    return(0);
}


SIMPLE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  simple.c
 *  This program draws a white rectangle on a black background.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glaux.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Simple OpengGL Sample");

    glClearColor (0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
    glBegin(GL_POLYGON);
        glVertex2f(-0.5, -0.5);
        glVertex2f(-0.5, 0.5);
        glVertex2f(0.5, 0.5);
        glVertex2f(0.5, -0.5);
    glEnd();
    glFlush();
    _sleep (10000);
    return(0);
}


SMOOTH.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 * smooth.c
 * This program demonstrates smooth shading.
 * A smooth shaded polygon is drawn in a 2-D projection.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void triangle(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

/*  GL_SMOOTH is actually the default shading model.  */
void myinit (void)
{
    glShadeModel (GL_SMOOTH);
}

void triangle(void)
{
    glBegin (GL_TRIANGLES);
    glColor3f (1.0, 0.0, 0.0);
    glVertex2f (5.0, 5.0);
    glColor3f (0.0, 1.0, 0.0);
    glVertex2f (25.0, 5.0);
    glColor3f (0.0, 0.0, 1.0);
    glVertex2f (5.0, 25.0);
    glEnd ();
}

void CALLBACK display(void)
{
    glClear (GL_COLOR_BUFFER_BIT);
    triangle ();
    glFlush ();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    gluOrtho2D (0.0, 30.0, 0.0, 30.0 * (GLfloat) h/(GLfloat) w);
    else
    gluOrtho2D (0.0, 30.0 * (GLfloat) w/(GLfloat) h, 0.0, 30.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Smooth Shading");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


SPHERE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  sphere.c
 *  This program draws a wire frame sphere.  It uses
 *  glTranslatef() as a viewing transformation.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

void CALLBACK display (void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f (1.0, 1.0, 1.0);
    glPushMatrix();
/*  glTranslatef() as viewing transformation    */
    glTranslatef (0.0, 0.0, -5.0);
    auxWireSphere(1.0);
    glPopMatrix();
    glFlush();
}

void myinit (void)
{
    glShadeModel (GL_FLAT);
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 400, 400);
    auxInitWindow ("Sphere");
    myinit ();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


STENCIL.C

/* Copyright (c) Mark J. Kilgard, 1994. */

/*
 * (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.
 */
/*  stencil.c
 *  This program draws two rotated tori in a window.
 *  A diamond in the center of the window masks out part
 *  of the scene.  Within this mask, a different model
 *  (a sphere) is drawn in a different color.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

#define YELLOWMAT   1
#define BLUEMAT 2

void myinit (void)
{
    GLfloat yellow_diffuse[] = { 0.7, 0.7, 0.0, 1.0 };
    GLfloat yellow_specular[] = { 1.0, 1.0, 1.0, 1.0 };

    GLfloat blue_diffuse[] = { 0.1, 0.1, 0.7, 1.0 };
    GLfloat blue_specular[] = { 0.1, 1.0, 1.0, 1.0 };

    GLfloat position_one[] = { 1.0, 1.0, 1.0, 0.0 };

    glNewList(YELLOWMAT, GL_COMPILE);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, yellow_specular);
    glMaterialf(GL_FRONT, GL_SHININESS, 64.0);
    glEndList();

    glNewList(BLUEMAT, GL_COMPILE);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, blue_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, blue_specular);
    glMaterialf(GL_FRONT, GL_SHININESS, 45.0);
    glEndList();

    glLightfv(GL_LIGHT0, GL_POSITION, position_one);

    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);

    glClearStencil(0x0);
    glEnable(GL_STENCIL_TEST);

}

/*  Draw a sphere in a diamond-shaped section in the
 *  middle of a window with 2 tori.
 */
GLsizei width, height;

void CALLBACK display(void)
{
#if 0
    if (!h) return;
    glViewport(0, 0, w, h);
#endif

    glClear(GL_STENCIL_BUFFER_BIT);
/* create a diamond shaped stencil area */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-3.0, 3.0, -3.0, 3.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glStencilFunc (GL_ALWAYS, 0x1, 0x1);
    glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
    glBegin(GL_QUADS);
glVertex3f (-1.0, 0.0, 0.0);
glVertex3f (0.0, 1.0, 0.0);
glVertex3f (1.0, 0.0, 0.0);
glVertex3f (0.0, -1.0, 0.0);
    glEnd();

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (GLfloat) width/(GLfloat) height, 3.0, 7.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -5.0);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

/* draw blue sphere where the stencil is 1 */
    glStencilFunc (GL_EQUAL, 0x1, 0x1);
    glCallList (BLUEMAT);
    auxSolidSphere (0.5);

/* draw the tori where the stencil is not 1 */
    glStencilFunc (GL_NOTEQUAL, 0x1, 0x1);
    glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
    glPushMatrix();
glRotatef (45.0, 0.0, 0.0, 1.0);
glRotatef (45.0, 0.0, 1.0, 0.0);
glCallList (YELLOWMAT);
        auxSolidTorus (0.275, 0.85);
glPushMatrix();
    glRotatef (90.0, 1.0, 0.0, 0.0);
            auxSolidTorus (0.275, 0.85);
glPopMatrix();
    glPopMatrix();

    glFlush();
}

/*  Whenever the window is reshaped, redefine the
 *  coordinate system and redraw the stencil area.
 */
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);

    width = w;
    height = h;

    glClear(GL_STENCIL_BUFFER_BIT);
/* create a diamond shaped stencil area */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-3.0, 3.0, -3.0, 3.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glStencilFunc (GL_ALWAYS, 0x1, 0x1);
    glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
    glBegin(GL_QUADS);
glVertex3f (-1.0, 0.0, 0.0);
glVertex3f (0.0, 1.0, 0.0);
glVertex3f (1.0, 0.0, 0.0);
glVertex3f (0.0, -1.0, 0.0);
    glEnd();

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (GLfloat) w/(GLfloat) h, 3.0, 7.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -5.0);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB
    | AUX_DEPTH16 | AUX_STENCIL);
    auxInitPosition (0, 0, 400, 400);
    auxInitWindow ("Stencil Test");
    myinit ();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


STROKE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  stroke.c
 *  This program demonstrates some characters of a
 *  stroke (vector) font.  The characters are represented
 *  by display lists, which are given numbers which
 *  correspond to the ASCII values of the characters.
 *  Use of glCallLists() is demonstrated.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

typedef struct charpoint {
    GLfloat   x, y;
    int    type;
} CP;

void myinit(void);
void drawLetter(CP *l);
void printStrokedString(char *s);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

#define PT 1
#define STROKE 2
#define END 3

CP Adata[] = {
    { 0, 0, PT}, {0, 9, PT}, {1, 10, PT}, {4, 10, PT},
    {5, 9, PT}, {5, 0, STROKE}, {0, 5, PT}, {5, 5, END}
};

CP Edata[] = {
    {5, 0, PT}, {0, 0, PT}, {0, 10, PT}, {5, 10, STROKE},
    {0, 5, PT}, {4, 5, END}
};

CP Pdata[] = {
    {0, 0, PT}, {0, 10, PT},  {4, 10, PT}, {5, 9, PT}, {5, 6, PT},
    {4, 5, PT}, {0, 5, END}
};

CP Rdata[] = {
    {0, 0, PT}, {0, 10, PT},  {4, 10, PT}, {5, 9, PT}, {5, 6, PT},
    {4, 5, PT}, {0, 5, STROKE}, {3, 5, PT}, {5, 0, END}
};

CP Sdata[] = {
    {0, 1, PT}, {1, 0, PT}, {4, 0, PT}, {5, 1, PT}, {5, 4, PT},
    {4, 5, PT}, {1, 5, PT}, {0, 6, PT}, {0, 9, PT}, {1, 10, PT},
    {4, 10, PT}, {5, 9, END}
};

/*  drawLetter() interprets the instructions from the array
 *  for that letter and renders the letter with line segments.
 */
void drawLetter(CP *l)
{
    glBegin(GL_LINE_STRIP);
    while (1) {
    switch (l->type) {
        case PT:
        glVertex2fv(&l->x);
        break;
        case STROKE:
        glVertex2fv(&l->x);
        glEnd();
        glBegin(GL_LINE_STRIP);
        break;
        case END:
        glVertex2fv(&l->x);
        glEnd();
        glTranslatef(8.0, 0.0, 0.0);
        return;
    }
    l++;
    }
}

/*  Create a display list for each of 6 characters  */
void myinit (void)
{
    GLuint base;

    glShadeModel (GL_FLAT);

    base = glGenLists (128);
    glListBase(base);
    glNewList(base+'A', GL_COMPILE); drawLetter(Adata); glEndList();
    glNewList(base+'E', GL_COMPILE); drawLetter(Edata); glEndList();
    glNewList(base+'P', GL_COMPILE); drawLetter(Pdata); glEndList();
    glNewList(base+'R', GL_COMPILE); drawLetter(Rdata); glEndList();
    glNewList(base+'S', GL_COMPILE); drawLetter(Sdata); glEndList();
    glNewList(base+' ', GL_COMPILE); glTranslatef(8.0, 0.0, 0.0); glEndList();
}

char *test1 = "A SPARE SERAPE APPEARS AS";
char *test2 = "APES PREPARE RARE PEPPERS";

void printStrokedString(char *s)
{
    GLsizei len = strlen(s);
    glCallLists(len, GL_BYTE, (GLbyte *)s);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    glPushMatrix();
    glScalef(2.0, 2.0, 2.0);
    glTranslatef(10.0, 30.0, 0.0);
    printStrokedString(test1);
    glPopMatrix();
    glPushMatrix();
    glScalef(2.0, 2.0, 2.0);
    glTranslatef(10.0, 13.0, 0.0);
    printStrokedString(test2);
    glPopMatrix();
    glFlush();
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 440, 120);
    auxInitWindow ("Stroke Font");
    myinit ();
    auxMainLoop(display);
    return(0);
}


SURFACE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  surface.c
 *  This program draws a NURBS surface in the shape of a
 *  symmetrical hill.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void init_surface(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

GLfloat ctlpoints[4][4][3];

GLUnurbsObj *theNurb;

/*
 *  Initializes the control points of the surface to a small hill.
 *  The control points range from -3 to +3 in x, y, and z
 */
void init_surface(void)
{
    int u, v;
    for (u = 0; u < 4; u++) {
    for (v = 0; v < 4; v++) {
        ctlpoints[u][v][0] = 2.0*((GLfloat)u - 1.5);
        ctlpoints[u][v][1] = 2.0*((GLfloat)v - 1.5);

        if ( (u == 1 || u == 2) && (v == 1 || v == 2))
        ctlpoints[u][v][2] = 3.0;
        else
        ctlpoints[u][v][2] = -3.0;
    }
    }
}

/*  Initialize material property and depth buffer.
 */
void myinit(void)
{
    GLfloat mat_diffuse[] = { 0.7, 0.7, 0.7, 1.0 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 100.0 };

    glClearColor (0.0, 0.0, 0.0, 1.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);

    init_surface();

    theNurb = gluNewNurbsRenderer();
    gluNurbsProperty(theNurb, GLU_SAMPLING_TOLERANCE, 25.0);
    gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, GLU_FILL);
}

void CALLBACK display(void)
{
    GLfloat knots[8] = {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0};

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix();
    glRotatef(330.0, 1.,0.,0.);
    glScalef (0.5, 0.5, 0.5);

    gluBeginSurface(theNurb);
    gluNurbsSurface(theNurb,
        8, knots,
        8, knots,
        4 * 3,
        3,
        &ctlpoints[0][0][0],
        4, 4,
        GL_MAP2_VERTEX_3);
    gluEndSurface(theNurb);

    glPopMatrix();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (45.0, (GLdouble)w/(GLdouble)h, 3.0, 8.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef (0.0, 0.0, -5.0);
}

/*  Main Loop
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("NURBS Surface");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


TEA.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  tea.c
 *  This program demonstrates two-sided lighting and compares
 *  it with one-sided lighting.  Three teapots are drawn, with
 *  a clipping plane to expose the interior of the objects.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

/*  Initialize light source.
 */
void myinit(void)
{
    GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
/*  light_position is NOT default value */
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glFrontFace (GL_CW);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

void CALLBACK display(void)
{
    GLdouble eqn[4] = {1.0, 0.0, -1.0, 1.0};
    GLfloat two_side_on[] = { GL_TRUE };
    GLfloat two_side_off[] = { GL_FALSE };
    GLfloat mat_diffuse[] = { 0.8, 0.8, 0.8, 1.0 };
    GLfloat back_diffuse[] = { 0.8, 0.2, 0.8, 1.0 };

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix ();
    glClipPlane (GL_CLIP_PLANE0, eqn);  /*  slice objects   */
    glEnable (GL_CLIP_PLANE0);

    glPushMatrix ();
    glTranslatef (0.0, 2.0, 0.0);
    auxSolidTeapot(1.0);    /*  one-sided lighting  */
    glPopMatrix ();

    /*  two-sided lighting, but same material   */
    glLightModelf (GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
    glPushMatrix ();
    glTranslatef (0.0, 0.0, 0.0);
    auxSolidTeapot(1.0);
    glPopMatrix ();

    /*  two-sided lighting, two different materials */
    glMaterialfv (GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv (GL_BACK, GL_DIFFUSE, back_diffuse);
    glPushMatrix ();
    glTranslatef (0.0, -2.0, 0.0);
    auxSolidTeapot(1.0);
    glPopMatrix ();

    glLightModelf (GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
    glDisable (GL_CLIP_PLANE0);
    glPopMatrix ();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-4.0, 4.0, -4.0*(GLfloat)h/(GLfloat)w,
        4.0*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
    glOrtho (-4.0*(GLfloat)w/(GLfloat)h,
        4.0*(GLfloat)w/(GLfloat)h, -4.0, 4.0, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Two Sided Lighting");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


TEAAMB.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  teaambient.c
 *  This program renders three lighted, shaded teapots, with
 *  different ambient values.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

/*  Initialize light source and lighting model.
 */
void myinit(void)
{
    GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
/*  light_position is NOT default value */
    GLfloat light_position[] = { 1.0, 0.0, 0.0, 0.0 };
    GLfloat global_ambient[] = { 0.75, 0.75, 0.75, 1.0 };

    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);

    glFrontFace (GL_CW);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
}

void CALLBACK display(void)
{
    GLfloat low_ambient[] = { 0.1, 0.1, 0.1, 1.0 };
    GLfloat more_ambient[] = { 0.4, 0.4, 0.4, 1.0 };
    GLfloat most_ambient[] = { 1.0, 1.0, 1.0, 1.0 };

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    /*  material has small ambient reflection   */
    glMaterialfv(GL_FRONT, GL_AMBIENT, low_ambient);
    glMaterialf(GL_FRONT, GL_SHININESS, 40.0);
    glPushMatrix ();
    glTranslatef (0.0, 2.0, 0.0);
    auxSolidTeapot(1.0);
    glPopMatrix ();

    /*  material has moderate ambient reflection    */
    glMaterialfv(GL_FRONT, GL_AMBIENT, more_ambient);
    glPushMatrix ();
    glTranslatef (0.0, 0.0, 0.0);
    auxSolidTeapot(1.0);
    glPopMatrix ();

    /*  material has large ambient reflection   */
    glMaterialfv(GL_FRONT, GL_AMBIENT, most_ambient);
    glPushMatrix ();
    glTranslatef (0.0, -2.0, 0.0);
    auxSolidTeapot(1.0);
    glPopMatrix ();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-4.0, 4.0, -4.0*(GLfloat)h/(GLfloat)w,
        4.0*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
    glOrtho (-4.0*(GLfloat)w/(GLfloat)h,
        4.0*(GLfloat)w/(GLfloat)h, -4.0, 4.0, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Ambient Lighting");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


TEAPOTS.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  teapots.c
 *  This program demonstrates lots of material properties.
 *  A single light source illuminates the objects.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void renderTeapot (GLfloat x, GLfloat y,
    GLfloat ambr, GLfloat ambg, GLfloat ambb,
    GLfloat difr, GLfloat difg, GLfloat difb,
    GLfloat specr, GLfloat specg, GLfloat specb, GLfloat shine);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

/*  Initialize depth buffer, projection matrix, light source,
 *  and lighting model.  Do not specify a material property here.
 */
void myinit(void)
{
    GLfloat ambient[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat position[] = { 0.0, 3.0, 3.0, 0.0 };

    GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    GLfloat local_view[] = { 0.0 };

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);

    glFrontFace (GL_CW);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
}

/*  Move object into position.  Use 3rd through 12th parameters
 *  to specify the material property.  Draw a teapot.
 */
void renderTeapot (GLfloat x, GLfloat y,
    GLfloat ambr, GLfloat ambg, GLfloat ambb,
    GLfloat difr, GLfloat difg, GLfloat difb,
    GLfloat specr, GLfloat specg, GLfloat specb, GLfloat shine)
{
    float mat[4];

    glPushMatrix();
    glTranslatef (x, y, 0.0);
    mat[0] = ambr; mat[1] = ambg; mat[2] = ambb; mat[3] = 1.0;
    glMaterialfv (GL_FRONT, GL_AMBIENT, mat);
    mat[0] = difr; mat[1] = difg; mat[2] = difb;
    glMaterialfv (GL_FRONT, GL_DIFFUSE, mat);
    mat[0] = specr; mat[1] = specg; mat[2] = specb;
    glMaterialfv (GL_FRONT, GL_SPECULAR, mat);
    glMaterialf (GL_FRONT, GL_SHININESS, shine*128.0);
    auxSolidTeapot(1.0);
    glPopMatrix();
}

/*  First column:  emerald, jade, obsidian, pearl, ruby, turquoise
 *  2nd column:  brass, bronze, chrome, copper, gold, silver
 *  3rd column:  black, cyan, green, red, white, yellow plastic
 *  4th column:  black, cyan, green, red, white, yellow rubber
 */
void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    renderTeapot (2.0, 17.0, 0.0215, 0.1745, 0.0215,
    0.07568, 0.61424, 0.07568, 0.633, 0.727811, 0.633, 0.6);
    renderTeapot (2.0, 14.0, 0.135, 0.2225, 0.1575,
    0.54, 0.89, 0.63, 0.316228, 0.316228, 0.316228, 0.1);
    renderTeapot (2.0, 11.0, 0.05375, 0.05, 0.06625,
    0.18275, 0.17, 0.22525, 0.332741, 0.328634, 0.346435, 0.3);
    renderTeapot (2.0, 8.0, 0.25, 0.20725, 0.20725,
    1, 0.829, 0.829, 0.296648, 0.296648, 0.296648, 0.088);
    renderTeapot (2.0, 5.0, 0.1745, 0.01175, 0.01175,
    0.61424, 0.04136, 0.04136, 0.727811, 0.626959, 0.626959, 0.6);
    renderTeapot (2.0, 2.0, 0.1, 0.18725, 0.1745,
    0.396, 0.74151, 0.69102, 0.297254, 0.30829, 0.306678, 0.1);
    renderTeapot (6.0, 17.0, 0.329412, 0.223529, 0.027451,
    0.780392, 0.568627, 0.113725, 0.992157, 0.941176, 0.807843,
    0.21794872);
    renderTeapot (6.0, 14.0, 0.2125, 0.1275, 0.054,
    0.714, 0.4284, 0.18144, 0.393548, 0.271906, 0.166721, 0.2);
    renderTeapot (6.0, 11.0, 0.25, 0.25, 0.25,
    0.4, 0.4, 0.4, 0.774597, 0.774597, 0.774597, 0.6);
    renderTeapot (6.0, 8.0, 0.19125, 0.0735, 0.0225,
    0.7038, 0.27048, 0.0828, 0.256777, 0.137622, 0.086014, 0.1);
    renderTeapot (6.0, 5.0, 0.24725, 0.1995, 0.0745,
    0.75164, 0.60648, 0.22648, 0.628281, 0.555802, 0.366065, 0.4);
    renderTeapot (6.0, 2.0, 0.19225, 0.19225, 0.19225,
    0.50754, 0.50754, 0.50754, 0.508273, 0.508273, 0.508273, 0.4);
    renderTeapot (10.0, 17.0, 0.0, 0.0, 0.0, 0.01, 0.01, 0.01,
    0.50, 0.50, 0.50, .25);
    renderTeapot (10.0, 14.0, 0.0, 0.1, 0.06, 0.0, 0.50980392, 0.50980392,
    0.50196078, 0.50196078, 0.50196078, .25);
    renderTeapot (10.0, 11.0, 0.0, 0.0, 0.0,
    0.1, 0.35, 0.1, 0.45, 0.55, 0.45, .25);
    renderTeapot (10.0, 8.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0,
    0.7, 0.6, 0.6, .25);
    renderTeapot (10.0, 5.0, 0.0, 0.0, 0.0, 0.55, 0.55, 0.55,
    0.70, 0.70, 0.70, .25);
    renderTeapot (10.0, 2.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.0,
    0.60, 0.60, 0.50, .25);
    renderTeapot (14.0, 17.0, 0.02, 0.02, 0.02, 0.01, 0.01, 0.01,
    0.4, 0.4, 0.4, .078125);
    renderTeapot (14.0, 14.0, 0.0, 0.05, 0.05, 0.4, 0.5, 0.5,
    0.04, 0.7, 0.7, .078125);
    renderTeapot (14.0, 11.0, 0.0, 0.05, 0.0, 0.4, 0.5, 0.4,
    0.04, 0.7, 0.04, .078125);
    renderTeapot (14.0, 8.0, 0.05, 0.0, 0.0, 0.5, 0.4, 0.4,
    0.7, 0.04, 0.04, .078125);
    renderTeapot (14.0, 5.0, 0.05, 0.05, 0.05, 0.5, 0.5, 0.5,
    0.7, 0.7, 0.7, .078125);
    renderTeapot (14.0, 2.0, 0.05, 0.05, 0.0, 0.5, 0.5, 0.4,
    0.7, 0.7, 0.04, .078125);
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (0.0, 16.0, 0.0, 16.0*(GLfloat)h/(GLfloat)w,
        -10.0, 10.0);
    else
    glOrtho (0.0, 16.0*(GLfloat)w/(GLfloat)h, 0.0, 16.0,
        -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar,
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 600);
    auxInitWindow ("Material Properties");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


TEXGEN.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  texgen.c
 *  This program draws a texture mapped teapot with
 *  automatically generated texture coordinates.  The
 *  texture is rendered as stripes on the teapot.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void makeStripeImage(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

#define stripeImageWidth 32
GLubyte stripeImage[3*stripeImageWidth];

void makeStripeImage(void)
{
    int j;

    for (j = 0; j < stripeImageWidth; j++) {
        stripeImage[3*j] = (j<=4) ? 255 : 0;
        stripeImage[3*j+1] = (j>4) ? 255 : 0;
        stripeImage[3*j+2] = 0;
    }
}

/* glTexGen stuff: */

GLfloat sgenparams[] = {1.0, 1.0, 1.0, 0.0};

void myinit(void)
{
    glClearColor (0.0, 0.0, 0.0, 0.0);

    makeStripeImage();
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage1D(GL_TEXTURE_1D, 0, 3, stripeImageWidth, 0,
         GL_RGB, GL_UNSIGNED_BYTE, stripeImage);

    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
    glTexGenfv(GL_S, GL_OBJECT_PLANE, sgenparams);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_1D);
    glEnable(GL_CULL_FACE);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);
    glFrontFace(GL_CW);
    glCullFace(GL_BACK);
    glMaterialf (GL_FRONT, GL_SHININESS, 64.0);
}

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix ();
    glRotatef(45.0, 0.0, 0.0, 1.0);
    auxSolidTeapot(2.0);
    glPopMatrix ();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho (-3.5, 3.5, -3.5*(GLfloat)h/(GLfloat)w,
        3.5*(GLfloat)h/(GLfloat)w, -3.5, 3.5);
    else
    glOrtho (-3.5*(GLfloat)w/(GLfloat)h,
        3.5*(GLfloat)w/(GLfloat)h, -3.5, 3.5, -3.5, 3.5);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 200, 200);
    auxInitWindow ("Texture Map");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


TEXSURF.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*  texturesurf.c
 *  This program uses evaluators to generate a curved
 *  surface and automatically generated texture coordinates.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include <math.h>

void myinit(void);
void makeImage(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

GLfloat ctrlpoints[4][4][3] = {
    {{ -1.5, -1.5, 4.0}, { -0.5, -1.5, 2.0},
    {0.5, -1.5, -1.0}, {1.5, -1.5, 2.0}},
    {{ -1.5, -0.5, 1.0}, { -0.5, -0.5, 3.0},
    {0.5, -0.5, 0.0}, {1.5, -0.5, -1.0}},
    {{ -1.5, 0.5, 4.0}, { -0.5, 0.5, 0.0},
    {0.5, 0.5, 3.0}, {1.5, 0.5, 4.0}},
    {{ -1.5, 1.5, -2.0}, { -0.5, 1.5, -2.0},
    {0.5, 1.5, 0.0}, {1.5, 1.5, -1.0}}
};

GLfloat texpts[2][2][2] = {{{0.0, 0.0}, {0.0, 1.0}},
            {{1.0, 0.0}, {1.0, 1.0}}};

void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    glEvalMesh2(GL_FILL, 0, 20, 0, 20);
    glFlush();
}

#define imageWidth 64
#define imageHeight 64
GLubyte image[3*imageWidth*imageHeight];

void makeImage(void)
{
    int i, j;
    float ti, tj;

    for (i = 0; i < imageWidth; i++) {
    ti = 2.0*3.14159265*i/imageWidth;
    for (j = 0; j < imageHeight; j++) {
        tj = 2.0*3.14159265*j/imageHeight;

        image[3*(imageHeight*i+j)] = (GLubyte) 127*(1.0+sin(ti));
        image[3*(imageHeight*i+j)+1] = (GLubyte) 127*(1.0+cos(2*tj));
        image[3*(imageHeight*i+j)+2] = (GLubyte) 127*(1.0+cos(ti+tj));
    }
    }
}

void myinit(void)
{
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4,
        0, 1, 12, 4, &ctrlpoints[0][0][0]);
    glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2,
        0, 1, 4, 2, &texpts[0][0][0]);
    glEnable(GL_MAP2_TEXTURE_COORD_2);
    glEnable(GL_MAP2_VERTEX_3);
    glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
    makeImage();
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, imageWidth, imageHeight, 0,
         GL_RGB, GL_UNSIGNED_BYTE, image);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_NORMALIZE);
    glShadeModel (GL_FLAT);
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    glOrtho(-4.0, 4.0, -4.0*(GLfloat)h/(GLfloat)w,
        4.0*(GLfloat)h/(GLfloat)w, -4.0, 4.0);
    else
    glOrtho(-4.0*(GLfloat)w/(GLfloat)h,
        4.0*(GLfloat)w/(GLfloat)h, -4.0, 4.0, -4.0, 4.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glRotatef(85.0, 1.0, 1.0, 1.0);
}

int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 300, 300);
    auxInitWindow ("Evaluators");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


TRIM.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

/*
 *  trim.c
 *  This program draws a NURBS surface in the shape of a
 *  symmetrical hill, using both a NURBS curve and pwl
 *  (piecewise linear) curve to trim part of the surface.
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

void myinit(void);
void init_surface(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

GLfloat ctlpoints[4][4][3];

GLUnurbsObj *theNurb;

/*
 *  Initializes the control points of the surface to a small hill.
 *  The control points range from -3 to +3 in x, y, and z
 */
void init_surface(void)
{
    int u, v;
    for (u = 0; u < 4; u++) {
    for (v = 0; v < 4; v++) {
        ctlpoints[u][v][0] = 2.0*((GLfloat)u - 1.5);
        ctlpoints[u][v][1] = 2.0*((GLfloat)v - 1.5);

        if ( (u == 1 || u == 2) && (v == 1 || v == 2))
        ctlpoints[u][v][2] = 3.0;
        else
        ctlpoints[u][v][2] = -3.0;
    }
    }
}

/*  Initialize material property and depth buffer.
 */
void myinit(void)
{
    GLfloat mat_diffuse[] = { 0.6, 0.6, 0.6, 1.0 };
    GLfloat mat_specular[] = { 0.9, 0.9, 0.9, 1.0 };
    GLfloat mat_shininess[] = { 128.0 };

    glClearColor (0.0, 0.0, 0.0, 1.0);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_AUTO_NORMAL);
    glEnable(GL_NORMALIZE);

    init_surface();

    theNurb = gluNewNurbsRenderer();
    gluNurbsProperty(theNurb, GLU_SAMPLING_TOLERANCE, 50.0);
    gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, GLU_FILL);
}

void CALLBACK display(void)
{
    GLfloat knots[8] = {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0};
    GLfloat edgePt[5][2] = /* counter clockwise */
    {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}, {0.0, 0.0}};
    GLfloat curvePt[4][2] = /* clockwise */
    {{0.25, 0.5}, {0.25, 0.75}, {0.75, 0.75}, {0.75, 0.5}};
    GLfloat curveKnots[8] =
    {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0};
    GLfloat pwlPt[4][2] = /* clockwise */
    {{0.75, 0.5}, {0.5, 0.25}, {0.25, 0.5}};

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glRotatef(330.0, 1.,0.,0.);
    glScalef (0.5, 0.5, 0.5);

    gluBeginSurface(theNurb);
    gluNurbsSurface(theNurb,
        8, knots,
        8, knots,
        4 * 3,
        3,
        &ctlpoints[0][0][0],
        4, 4,
        GL_MAP2_VERTEX_3);
    gluBeginTrim (theNurb);
    gluPwlCurve (theNurb, 5, &edgePt[0][0], 2, GLU_MAP1_TRIM_2);
    gluEndTrim (theNurb);
    gluBeginTrim (theNurb);
    gluNurbsCurve (theNurb, 8, curveKnots, 2,
        &curvePt[0][0], 4, GLU_MAP1_TRIM_2);
    gluPwlCurve (theNurb, 3, &pwlPt[0][0], 2, GLU_MAP1_TRIM_2);
    gluEndTrim (theNurb);
    gluEndSurface(theNurb);

    glPopMatrix();
    glFlush();
}

void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    if (!h) return;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (45.0, (GLdouble)w/(GLdouble)h, 3.0, 8.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef (0.0, 0.0, -5.0);
}

/*  Main Loop
 */
int main(int argc, char** argv)
{
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("NURBS Surface");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    return(0);
}


AUXDEMO.C

/*
 * (c) Copyright 1993 - 1998 Microsoft Corporation
 *
 *    bgammon.c
 *
 *    start easy, just have pieces...
 *
 */
#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

GLvoidinitialize(GLvoid);
GLvoidCALLBACK drawScene(GLvoid);
GLvoid CALLBACK resize(GLsizei, GLsizei);
GLvoiddrawLight(GLvoid);
void polarView( GLdouble, GLdouble, GLdouble, GLdouble);

GLfloat latitude, longitude, radius;

void _CRTAPI1 main(void)
{
    initialize();

    auxMainLoop( drawScene );
}

GLvoid CALLBACK resize( GLsizei width, GLsizei height )
{
    GLfloat aspect;

    glViewport( 0, 0, width, height );

    aspect = (GLfloat) width / height;

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluPerspective( 45.0, aspect, 3.0, 7.0 );
    glMatrixMode( GL_MODELVIEW );
}

GLvoid initialize(GLvoid)
{
    GLfloatmaxObjectSize, aspect;
    GLdoublenear_plane, far_plane;
    GLsizeiwidth, height;

    GLfloatambientProperties[] = {0.7, 0.7, 0.7, 1.0};
    GLfloatdiffuseProperties[] = {0.8, 0.8, 0.8, 1.0};
    GLfloatspecularProperties[] = {1.0, 1.0, 1.0, 1.0};

    width = 1024.0;
    height = 768.0;

    auxInitPosition( width/4, height/4, width/2, height/2);

    auxInitDisplayMode( AUX_RGB | AUX_DEPTH | AUX_DOUBLE );

    auxInitWindow( "AUX Library Demo" );

    auxIdleFunc( drawScene );

    auxReshapeFunc( resize );

    glClearColor( 0.0, 0.0, 0.0, 1.0 );
    glClearDepth( 1.0 );

    glEnable(GL_DEPTH_TEST);

    glEnable(GL_LIGHTING);

    glLightfv( GL_LIGHT0, GL_AMBIENT, ambientProperties);
    glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuseProperties);
    glLightfv( GL_LIGHT0, GL_SPECULAR, specularProperties);
    glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1.0);

    glEnable( GL_LIGHT0 );

    glMatrixMode( GL_PROJECTION );
    aspect = (GLfloat) width / height;
    gluPerspective( 45.0, aspect, 3.0, 7.0 );
    glMatrixMode( GL_MODELVIEW );

    near_plane = 3.0;
    far_plane = 7.0;
    maxObjectSize = 3.0;
    radius = near_plane + maxObjectSize/2.0;

    latitude = 0.0;
    longitude = 0.0;

}

void polarView(GLdouble radius, GLdouble twist, GLdouble latitude,
       GLdouble longitude)
{
    glTranslated(0.0, 0.0, -radius);
    glRotated( -twist, 0.0, 0.0, 1.0 );
    glRotated( -latitude, 1.0, 0.0, 0.0);
    glRotated( longitude, 0.0, 0.0, 1.0);

}

GLvoid CALLBACK drawScene(GLvoid)
{
    static GLfloatwhiteAmbient[] = {0.3, 0.3, 0.3, 1.0};
    static GLfloatredAmbient[] = {0.3, 0.1, 0.1, 1.0};
    static GLfloatgreenAmbient[] = {0.1, 0.3, 0.1, 1.0};
    static GLfloatblueAmbient[] = {0.1, 0.1, 0.3, 1.0};
    static GLfloatwhiteDiffuse[] = {1.0, 1.0, 1.0, 1.0};
    static GLfloatredDiffuse[] = {1.0, 0.0, 0.0, 1.0};
    static GLfloatgreenDiffuse[] = {0.0, 1.0, 0.0, 1.0};
    static GLfloatblueDiffuse[] = {0.0, 0.0, 1.0, 1.0};
    static GLfloatwhiteSpecular[] = {1.0, 1.0, 1.0, 1.0};
    static GLfloatredSpecular[] = {1.0, 0.0, 0.0, 1.0};
    static GLfloatgreenSpecular[] = {0.0, 1.0, 0.0, 1.0};
    static GLfloatblueSpecular[] = {0.0, 0.0, 1.0, 1.0};

    static GLfloatlightPosition0[] = {1.0, 1.0, 1.0, 1.0};
    static GLfloatangle = 0.0;

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glPushMatrix();

    latitude += 4.0;
    longitude += 2.5;

    polarView( radius, 0, latitude, longitude );

    glPushMatrix();
        angle += 6.0;
        glRotatef(angle, 1.0, 0.0, 1.0);
        glTranslatef( 0.0, 1.5, 0.0);
        glLightfv(GL_LIGHT0, GL_POSITION, lightPosition0);
        drawLight();
    glPopMatrix();

    glPushAttrib(GL_LIGHTING_BIT);

        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, redAmbient);
        glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, redDiffuse);
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, whiteSpecular);
        glMaterialf(GL_FRONT, GL_SHININESS, 100.0);

        auxSolidCone( 0.3, 0.6 );

    glPopAttrib();
    auxWireSphere(1.5);

    glPushAttrib(GL_LIGHTING_BIT);

        glMaterialfv(GL_BACK, GL_AMBIENT, greenAmbient);
        glMaterialfv(GL_BACK, GL_DIFFUSE, greenDiffuse);
        glMaterialfv(GL_FRONT, GL_AMBIENT, blueAmbient);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, blueDiffuse);
        glMaterialfv(GL_FRONT, GL_SPECULAR, blueSpecular);
        glMaterialf(GL_FRONT, GL_SHININESS, 50.0);

        glPushMatrix();
            glTranslatef(0.8, -0.65, 0.0);
            glRotatef(30.0, 1.0, 0.5, 1.0);
            auxSolidCylinder( 0.3, 0.6 );
            glPopMatrix();

        glPopAttrib();

    glPopMatrix();

    auxSwapBuffers();
}

GLvoid drawLight(GLvoid)
{
    glPushAttrib(GL_LIGHTING_BIT);
    glDisable(GL_LIGHTING);
    glColor3f(1.0, 1.0, 1.0);
    auxSolidDodecahedron(0.1);
    glPopAttrib();
}


BACKTRAC.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/glu.h>
#include <GL/gl.h>
extern "C" {
#include <GL/glaux.h>
};

#ifdef GLX_MOTIF
#include <GL/glx.h>
#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include <Xm/CascadeB.h>
#include <Xm/Frame.h>
#include <Xm/MainW.h>
#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/RowColumn.h>
#include <Xm/ToggleB.h>
#include <GLwMDrawA.h>
#include <sys/time.h>
#endif

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

#include "scene.hxx"
#include "cbacks.hxx"
#include "menu.h"

LONG APIENTRY MyWndProc(HWND, UINT, UINT, LONG);
VOID SubclassWindow (HWND, WNDPROC);
void vCustomizeWnd(void);

extern light lights[];

extern GLboolean bAutoMotion(void);
extern void draw(void);

#ifdef GLX_MOTIF
static Display *display;
XtAppContext app_context;
Widget glw;
const int max_args = 20;
#endif

int quick_moves = 0;
int auto_motion = 0;

#ifdef GLX_MOTIF
XVisualInfo *find_visual()
{
  int attr[256], i, stencil, dbuffer;
  XVisualInfo *vi;

  i = 0;
  attr[i++] = GLX_RGBA;
  attr[i++] = GLX_RED_SIZE;
  attr[i++] = 1;
  attr[i++] = GLX_GREEN_SIZE;
  attr[i++] = 1;
  attr[i++] = GLX_BLUE_SIZE;
  attr[i++] = 1;
  attr[i++] = GLX_DEPTH_SIZE;
  attr[i++] = 1;
  dbuffer = i;
  attr[i++] = GLX_DOUBLEBUFFER;
  stencil = i;
  attr[i++] = GLX_STENCIL_SIZE;
  attr[i++] = 1;
  attr[i++] = (int)None;

  vi = glXChooseVisual(display, DefaultScreen(display), attr);
  if (vi == NULL) {
    //fprintf(stderr, "Unable to find visual with stencil buffer.\n");
    //fprintf(stderr, "(Things won't look quite as good).\n");
    attr[stencil] = (int)None;
    vi = glXChooseVisual(display, DefaultScreen(display), attr);
    if (vi == NULL) {
      //fprintf(stderr, "Unable to find double-buffered visual.\n");
      //fprintf(stderr, "(Things will look even worse).\n");
      attr[dbuffer] = (int)None;
      vi = glXChooseVisual(display, DefaultScreen(display), attr);
      //if (vi == NULL) fprintf(stderr, "Can't find visual at all.\n");
    }
  }
  return vi;
}
#endif

#ifdef GLX_MOTIF
Widget create_widgets(Widget parent)
{
  Widget main_window;
  Widget menu_bar;
  Widget menu_pane;
  Widget button;
  Widget cascade;
  Widget frame;
  XVisualInfo *vi;

  Arg args[max_args];
  int argcount;

  char buffer[128];

  int i;

  main_window = XmCreateMainWindow(parent, "main1", NULL, 0);
  XtManageChild(main_window);

  menu_bar = XmCreateMenuBar(main_window, "menu_bar", NULL, 0);
  XtManageChild(menu_bar);

  menu_pane = XmCreatePulldownMenu(menu_bar, "menu_pane", NULL, 0);
  button = XmCreatePushButton(menu_pane, "Exit", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNactivateCallback, (XtCallbackProc)exitCB, NULL);

  XtSetArg(args[0], XmNsubMenuId, menu_pane);
  cascade = XmCreateCascadeButton(menu_bar, "File", args, 1);
  XtManageChild(cascade);

  argcount = 0;
  XtSetArg(args[argcount], XmNradioBehavior, True); argcount++;
  menu_pane = XmCreatePulldownMenu(menu_bar, "menu_pane", args, argcount);
  XtSetArg(args[0], XmNset, TRUE);
  for (i = 0; i < nindices; i++) {
    if (i == def_refraction_index) argcount = 1;
    else argcount = 0;
    button = XmCreateToggleButton(menu_pane, (char *)indices[i].name,
args, argcount);
    XtManageChild(button);
    XtAddCallback(button, XmNvalueChangedCallback,
  (XtCallbackProc)refractionCB,
  (XtPointer)(&indices[i].index));
  }
  XtSetArg(args[0], XmNsubMenuId, menu_pane);
  cascade = XmCreateCascadeButton(menu_bar, "Material", args, 1);
  XtManageChild(cascade);

  menu_pane = XmCreatePulldownMenu(menu_bar, "menu_pane", NULL, 0);

  XtSetArg(args[0], XmNset, draw_square);
  button = XmCreateToggleButton(menu_pane, "Draw Square", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
                (XtCallbackProc)drawSomethingCB, &draw_square);

  XtSetArg(args[0], XmNset, draw_shadows);
  button = XmCreateToggleButton(menu_pane, "Draw Shadows", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
                (XtCallbackProc)drawSomethingCB, &draw_shadows);

  XtSetArg(args[0], XmNset, draw_refraction);
  button = XmCreateToggleButton(menu_pane, "Draw Refraction", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
                (XtCallbackProc)drawSomethingCB, &draw_refraction);

  XtSetArg(args[0], XmNset, draw_sphere);
  button = XmCreateToggleButton(menu_pane, "Draw Sphere", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
                (XtCallbackProc)drawSomethingCB, &draw_sphere);

  XtSetArg(args[0], XmNset, draw_lights);
  button = XmCreateToggleButton(menu_pane, "Draw Lights", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
                (XtCallbackProc)drawSomethingCB, &draw_lights);

#ifdef TEXTURE
  XtSetArg(args[0], XmNset, draw_texture);
  button = XmCreateToggleButton(menu_pane, "Texture Map", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
                (XtCallbackProc)drawSomethingCB, &draw_texture);
#endif

  XtSetArg(args[0], XmNsubMenuId, menu_pane);
  cascade = XmCreateCascadeButton(menu_bar, "Draw", args, 1);
  XtManageChild(cascade);

  argcount = 0;
  XtSetArg(args[argcount], XmNradioBehavior, True); argcount++;
  menu_pane = XmCreatePulldownMenu(menu_bar, "menu_pane", args, argcount);
  XtSetArg(args[0], XmNset, TRUE);
  for (i = 0; i < npossible_divisions; i++) {
    if (i == def_divisions_index) argcount = 1;
    else argcount = 0;
    sprintf(buffer, "%d", possible_divisions[i]);
    button = XmCreateToggleButton(menu_pane, buffer, args, argcount);
    XtManageChild(button);
    XtAddCallback(button, XmNvalueChangedCallback,
  (XtCallbackProc)subdivisionCB, &possible_divisions[i]);
  }
  XtSetArg(args[0], XmNsubMenuId, menu_pane);
  cascade = XmCreateCascadeButton(menu_bar, "Subdivision", args, 1);
  XtManageChild(cascade);

  menu_pane = XmCreatePulldownMenu(menu_bar, "menu_pane", args, argcount);
  button = XmCreatePushButton(menu_pane, "Reset Position", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNactivateCallback,
(XtCallbackProc)resetLightsCB, NULL);

  XtSetArg(args[0], XmNset, TRUE);
  for (i = 0; i < nlights; i++) {
    button = XmCreateToggleButton(menu_pane, lights[i].name, args,
  lights[i].on ? 1 : 0);
    XtManageChild(button);
    XtAddCallback(button, XmNvalueChangedCallback,
  (XtCallbackProc)light_onCB, &lights[i]);
  }
  XtSetArg(args[0], XmNsubMenuId, menu_pane);
  cascade = XmCreateCascadeButton(menu_bar, "Lights", args, 1);
  XtManageChild(cascade);

  menu_pane = XmCreatePulldownMenu(menu_bar, "menu_pane", args,
   argcount);
  XtSetArg(args[0], XmNset, quick_moves);
  button = XmCreateToggleButton(menu_pane, "Quick Motion",
args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
(XtCallbackProc)intToggleCB, &quick_moves);

  XtSetArg(args[0], XmNset, auto_motion);
  button = XmCreateToggleButton(menu_pane, "Rotate Automatically",
args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
(XtCallbackProc)autoMotionCB, NULL);

  XtSetArg(args[0], XmNsubMenuId, menu_pane);
  cascade = XmCreateCascadeButton(menu_bar, "Motion", args, 1);
  XtManageChild(cascade);

  argcount = 0;
  XtSetArg(args[argcount], XmNmarginWidth, 0); argcount++;
  XtSetArg(args[argcount], XmNmarginHeight, 0); argcount++;
  XtSetArg(args[argcount], XmNshadowThickness, 1); argcount++;
  XtSetArg(args[argcount], XmNshadowType, XmSHADOW_OUT); argcount++;
  frame = XmCreateFrame(main_window, "frame", args, argcount);
  XtManageChild(frame);

  argcount = 0;
  vi = find_visual();
  if (vi) {
    XtSetArg(args[argcount], GLwNvisualInfo, vi); argcount++;
  }
  else {
    XtSetArg(args[argcount], GLwNrgba, TRUE); argcount++;
    XtSetArg(args[argcount], GLwNdepthSize, 1); argcount++;
    XtSetArg(args[argcount], GLwNdoublebuffer, TRUE); argcount++;
  }
  XtSetArg(args[argcount], XmNbottomAttachment, XmATTACH_FORM); argcount++;
  XtSetArg(args[argcount], XmNtopAttachment, XmATTACH_FORM); argcount++;
  XtSetArg(args[argcount], XmNleftAttachment, XmATTACH_FORM); argcount++;
  XtSetArg(args[argcount], XmNrightAttachment, XmATTACH_FORM); argcount++;
  glw = GLwCreateMDrawingArea(frame, "glwidget", args, argcount);
  XtManageChild(glw);
  XtAddCallback(glw, GLwNginitCallback, (XtCallbackProc)initCB, 0);
  XtAddCallback(glw, GLwNexposeCallback, (XtCallbackProc)exposeCB, 0);
  XtAddCallback(glw, GLwNresizeCallback, (XtCallbackProc)resizeCB, 0);
  XtAddCallback(glw, GLwNinputCallback, (XtCallbackProc)inputCB, 0);

  return main_window;
}
#endif

void idle_func (void)
{
    bAutoMotion();
}

void null_func (void)
{
}

int CRTAPI1 main(int argc, char **argv)
{
    auxInitDisplayMode(AUX_RGB | AUX_STENCIL | AUX_DOUBLE );
    auxInitPosition(100, 100, 300, 300);
    auxInitWindow("BackTrace");
    vCustomizeWnd();
    scene_load_texture((char *)def_texfile);

    vInit();
    auxReshapeFunc(vResize);

    //
    // we've a choice of using accelerator or auxKeyFunc
    // the use of auxKeyFunc here is solely for demonstration purpose
    //
    auxKeyFunc(AUX_SPACE, vQuickMove);
    auxKeyFunc(AUX_RETURN, vResetLights);
    auxKeyFunc(AUX_p, vAutoMotion);

    auxKeyFunc(AUX_q, vDrawAll);
    auxKeyFunc(AUX_w, vDrawSquare);
    auxKeyFunc(AUX_e, vDrawShadow);
    auxKeyFunc(AUX_r, vDrawRefraction);
    auxKeyFunc(AUX_t, vDrawSphere);
    auxKeyFunc(AUX_y, vDrawLight);
    auxKeyFunc(AUX_u, vDrawTexture);

    auxKeyFunc(AUX_a, vRefractionAIR);
    auxKeyFunc(AUX_s, vRefractionICE);
    auxKeyFunc(AUX_d, vRefractionWATER);
    auxKeyFunc(AUX_f, vRefractionZincGLASS);
    auxKeyFunc(AUX_g, vRefractionLightGLASS);
    auxKeyFunc(AUX_h, vRefractionHeavyGLASS);

    auxKeyFunc(AUX_1, vSubdivision10);
    auxKeyFunc(AUX_2, vSubdivision20);
    auxKeyFunc(AUX_3, vSubdivision30);
    auxKeyFunc(AUX_4, vSubdivision40);

    auxKeyFunc(AUX_R, vRLight_on);
    auxKeyFunc(AUX_G, vGLight_on);
    auxKeyFunc(AUX_B, vBLight_on);

    auxMouseFunc(AUX_LEFTBUTTON, AUX_MOUSEDOWN, vMouseDown);
    auxMouseFunc(AUX_MIDDLEBUTTON, AUX_MOUSEDOWN, vMouseDown);
    auxMouseFunc(AUX_RIGHTBUTTON, AUX_MOUSEDOWN, vMouseDown);

    auxMouseFunc(AUX_LEFTBUTTON, AUX_MOUSEUP, vLeftMouseUp);
    auxMouseFunc(AUX_MIDDLEBUTTON, AUX_MOUSEUP, vMiddleMouseUp);
    auxMouseFunc(AUX_RIGHTBUTTON, AUX_MOUSEUP, vRightMouseUp);

    auxMouseFunc(AUX_LEFTBUTTON, AUX_MOUSELOC, vMouseMove);
    auxMouseFunc(AUX_MIDDLEBUTTON, AUX_MOUSELOC, vMouseMove);
    auxMouseFunc(AUX_RIGHTBUTTON, AUX_MOUSELOC, vMouseMove);

    auxMainLoop(draw);
    if (auto_motion) auxIdleFunc (idle_func);
    else auxIdleFunc (null_func);
    return 0;
}

#ifdef GLX_MOTIF
void main(int argc, char **argv)
{
  Widget app_shell;
  Arg args[max_args];
  int argcount;

  scene_load_texture((char *)def_texfile);

  XtToolkitInitialize();
  app_context = XtCreateApplicationContext();
  display = XtOpenDisplay(app_context, NULL, argv[0],
                          "XMdemos", NULL, 0, &argc, argv);
  if (!display) {
    XtWarning("Can't open display.");
    exit(0);
  }

  argcount = 0;
  XtSetArg(args[argcount], XmNmaxAspectX, 1); argcount++;
  XtSetArg(args[argcount], XmNmaxAspectY, 1); argcount++;
  XtSetArg(args[argcount], XmNminAspectX, 1); argcount++;
  XtSetArg(args[argcount], XmNminAspectY, 1); argcount++;
  app_shell =
    XtAppCreateShell(argv[0], "XMdemos", applicationShellWidgetClass,
                     display, args, argcount);

  create_widgets(app_shell);

  XtRealizeWidget(app_shell);

  XtAppMainLoop(app_context);

}
#endif

/**************************************************************************\
*
*  function:  MyWndProc
*
*  input parameters:  normal window procedure parameters.
*
\**************************************************************************/
LONG APIENTRY MyWndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
  WNDPROC     pfnOldProc;
  static UINT uiTmID = 0;
  int wmId, wmEvent;

  pfnOldProc = (WNDPROC) GetWindowLong (hwnd, GWL_USERDATA);

  switch (message) {

    case WM_COMMAND: {
        HMENU hmenu;

        hmenu = GetMenu(auxGetHWND());
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        switch (wmId) {
            case IDM_EXIT      : vExit(); break;
            case IDM_AIR       :
            case IDM_ICE       :
            case IDM_WATER     :
            case IDM_ZINC      :
            case IDM_LIGHT     :
            case IDM_HEAVY     : vRefraction(wmId - IDM_AIR); break;

            case IDM_SQUARE    :
            {
                if (draw_square)
                    CheckMenuItem(hmenu, IDM_SQUARE, MF_BYCOMMAND | MF_UNCHECKED);
                else
                    CheckMenuItem(hmenu, IDM_SQUARE, MF_BYCOMMAND | MF_CHECKED);
                vDrawStuff(&draw_square);
                break;
            }
            case IDM_SHADOW    :
            {
                if (draw_shadows)
                    CheckMenuItem(hmenu, IDM_SHADOW, MF_BYCOMMAND | MF_UNCHECKED);
                else
                    CheckMenuItem(hmenu, IDM_SHADOW, MF_BYCOMMAND | MF_CHECKED);
                vDrawStuff(&draw_shadows);
                break;
            }
            case IDM_REFRACTION:
            {
                if (draw_refraction)
                    CheckMenuItem(hmenu, IDM_REFRACTION, MF_BYCOMMAND | MF_UNCHECKED);
                else
                    CheckMenuItem(hmenu, IDM_REFRACTION, MF_BYCOMMAND | MF_CHECKED);
                vDrawStuff(&draw_refraction);
                break;
            }
            case IDM_SPHERE    :
            {
                if (draw_sphere)
                    CheckMenuItem(hmenu, IDM_SPHERE, MF_BYCOMMAND | MF_UNCHECKED);
                else
                    CheckMenuItem(hmenu, IDM_SPHERE, MF_BYCOMMAND | MF_CHECKED);
                vDrawStuff(&draw_sphere);
                break;
            }
            case IDM_LIGHTS    :
            {
                if (draw_lights)
                    CheckMenuItem(hmenu, IDM_LIGHTS, MF_BYCOMMAND | MF_UNCHECKED);
                else
                    CheckMenuItem(hmenu, IDM_LIGHTS, MF_BYCOMMAND | MF_CHECKED);
                vDrawStuff(&draw_lights);
                break;
            }
            case IDM_TEXTURE   :
            {
                if (draw_texture)
                    CheckMenuItem(hmenu, IDM_TEXTURE, MF_BYCOMMAND | MF_UNCHECKED);
                else
                    CheckMenuItem(hmenu, IDM_TEXTURE, MF_BYCOMMAND | MF_CHECKED);
                vDrawStuff(&draw_texture);
                break;
            }
            case IDM_ALL       : vDrawAll(); break;
            case IDM_10        :
            case IDM_20        :
            case IDM_30        :
            case IDM_40        : vSubdivision(wmId - IDM_10); break;

            case IDM_RESETLGT  : vResetLights(); break;

            case IDM_RED       :
            case IDM_GREEN     :
            case IDM_BLUE      : vLight_on(wmId - IDM_RED); break;

            case IDM_QUICK     : vQuickMove(); break;
            case IDM_AUTO      : vAutoMotion(); break;
            default:
                break;
        }
    }

    case WM_USER:
      if (auto_motion) auxIdleFunc (idle_func);
      else auxIdleFunc (null_func);

        return 0;

    case WM_DESTROY:
        if (uiTmID != 0)
            KillTimer(hwnd, uiTmID);
        return (pfnOldProc)(hwnd, message, wParam, lParam);

    default:
        return (pfnOldProc)(hwnd, message, wParam, lParam);

  } /* end switch */

  return 0;
}

/**************************************************************************\
*  function:  SubclassWindow
*
*  input parameters:
*   hwnd            - window handle to be subclassed,
*   SubclassWndProc - the new window procedure.
*
\**************************************************************************/
VOID SubclassWindow (HWND hwnd, WNDPROC SubclassWndProc)
{
  LONG pfnOldProc;

  pfnOldProc = GetWindowLong (hwnd, GWL_WNDPROC);

  SetWindowLong (hwnd, GWL_USERDATA, (LONG) pfnOldProc);
  SetWindowLong (hwnd, GWL_WNDPROC,  (LONG) SubclassWndProc);
}

/******************************Public*Routine******************************\
*
* vCustomizeWnd
*
* Effects: Subclass the window created by the toolkit
*          Add menu bar to the window
*          Setup timer
*
* History:
*  01-Dec-1993
*
\**************************************************************************/

void vCustomizeWnd(void)
{
    HWND    hWnd;

    if ((hWnd = auxGetHWND()) == NULL) {
OutputDebugString("auxGetHWND() failed\n");
return;
    }

    SubclassWindow (hWnd, (WNDPROC) MyWndProc);
    SendMessage(hWnd, WM_USER, 0L, 0L);
    SetMenu(hWnd, LoadMenu(GetModuleHandle(NULL), "Backtrac"));
    DrawMenuBar(hWnd);

    return;
}


CBACKS.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#ifdef GLX_MOTIF
#include <X11/keysym.h>

#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include <Xm/ToggleB.h>
#include <GL/glx.h>
#include <GLwMDrawA.h>
#include <sys/time.h>
#endif

#include <GL/glu.h>
#include <GL/gl.h>
extern "C" {
#include <GL/glaux.h>
}

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

#include "scene.hxx"
#include "cbacks.hxx"
#include "menu.h"

#ifdef GLX_MOTIF
extern Widget glw;
extern XtAppContext app_context;
GLXContext glx_con1005text;
#endif

extern light lights[];
extern int quick_moves;
extern int auto_motion;
static float dtheta[nlights];
static float last_motion_update;

//struct timeval starttime;
SYSTEMTIME starttime;

static int button_down;
static int winx, winy;
GLint mouse_x, mouse_y;

static int name_selected;

#ifdef GLX_MOTIF
static XtWorkProcId workproc = NULL;
#endif

const float time_fudge = .0001;

inline
float current_time()
{
  //struct timeval time;
  SYSTEMTIME time;

  //gettimeofday(&time, NULL);
  GetSystemTime(&time);

  //return ((double)(time.tv_sec - starttime.tv_sec) +
  //  (double)(time.tv_usec - starttime.tv_usec) / 1000000.0);

  return ((float)(time.wSecond - starttime.wSecond) +
        (float)(time.wMilliseconds - starttime.wMilliseconds) / (float)1000.0);

}

inline float rand(float min, float max)
{
  double r;
  r = (double)rand() / (double)RAND_MAX;
  return (float)(min + r * (max - min));
}

/******************************Public*Routine******************************\
*
* void draw(void)
*
* History:
*  29-Nov-1993                      Replaced with wgl and aux calls
*
\**************************************************************************/

void CALLBACK draw(void)
{

#ifdef GLX_MOTIF
  GLwDrawingAreaMakeCurrent(glw, glx_context);
//#else
  //wglMakeCurrent(auxGetHDC(), auxGetHGLRC());
#endif

  scene_draw();

#ifdef GLX_MOTIF
  GLwDrawingAreaSwapBuffers(glw);
#else
  // Win32's SwapBuffers takes HDC
  // SwapBuffers(auxGetHDC);
  auxSwapBuffers();
#endif

}

/******************************Public*Routine******************************\
*
* vQuickMove(void)
*
* //void intToggleCB(Widget w, XtPointer client_data, XtPointer call_data)
*
* Effects:  set or reset the global flag quick_moves
*
* History:
*  30-Nov-1993
*
\**************************************************************************/

void CALLBACK vQuickMove(void)
{
  HMENU   hmenu;

  hmenu = GetMenu(auxGetHWND());

#ifdef GLX_MOTIF
  int *data;
  XmToggleButtonCallbackStruct *ptr;

  ptr = (XmToggleButtonCallbackStruct *)call_data;
  data = (int *)client_data;
  *data = ptr->set;
#endif

  quick_moves = quick_moves ? 0 : 1;

  if (quick_moves)
    CheckMenuItem(hmenu, IDM_QUICK, MF_BYCOMMAND | MF_CHECKED);
  else
    CheckMenuItem(hmenu, IDM_QUICK, MF_BYCOMMAND | MF_UNCHECKED);

  // This redraw may or may not be needed - do it to be safe
  draw();
}

/******************************Public*Routine******************************\
*
* vResetLights(void)
*
* // void resetLightsCB(Widget w)
*
* Effects:
*
* History:
*  30-Nov-1993
*
\**************************************************************************/

void CALLBACK vResetLights(void)
{
  scene_reset_lights();
  draw();
}

/******************************Public*Routine******************************\
*
* vAutoMotion(void)
*
* // void autoMotionCB(Widget w, XtPointer client_data, XtPointer call_data)
*
* Effects:  !!! turns on/off the timer?
*           (Re)set the global auto_moves
*
* Warnings:
*
* History:
*  30-Nov-1993
*
\**************************************************************************/

void CALLBACK vAutoMotion(void)
{
  int i;
  HMENU   hmenu;

  hmenu = GetMenu(auxGetHWND());

#ifdef GLX_MOTIF
  XmToggleButtonCallbackStruct *ptr;

  ptr = (XmToggleButtonCallbackStruct *)call_data;

  auto_motion = ptr->set;
#endif

  auto_motion = auto_motion ? 0 : 1;
  SendMessage(auxGetHWND(), WM_USER, 0L, 0L);

  if (auto_motion) {
    CheckMenuItem(hmenu, IDM_AUTO, MF_BYCOMMAND | MF_CHECKED);

    for (i = 0; i < nlights; i++)
        dtheta[i] = rand(-1, 1);
    last_motion_update = current_time();
  } else {
    CheckMenuItem(hmenu, IDM_AUTO, MF_BYCOMMAND | MF_UNCHECKED);
  }

#ifdef GLX_MOTIF
  if (auto_motion) {
    workproc = XtAppAddWorkProc(app_context, drawWP, NULL);
    for (i = 0; i < nlights; i++) dtheta[i] = rand(-1, 1);
    last_motion_update = current_time();
  } else {
    XtRemoveWorkProc(workproc);
  }
#endif

}

/******************************Public*Routine******************************\
*
* vInit(void)
*
* // void initCB(Widget w)
*
* Effects:
*
* History:
*  30-Nov-1993
*
\**************************************************************************/

void vInit(void)
{
  RECT  rect;
  HMENU hmenu;
  HWND  hWnd;
  int   i;

#ifdef GLX_MOTIF
  Arg args[1];
  XVisualInfo *vi;

  XtSetArg(args[0], GLwNvisualInfo, &vi);
  XtGetValues(w, args, 1);

  // !!! creating indirect context, make current
  //
  glx_context = glXCreateContext(XtDisplay(w), vi, 0, GL_FALSE);
  GLwDrawingAreaMakeCurrent(w, glx_context);
#endif

#ifdef TESTTEST
  PIXELFORMATDESCRIPTOR pfd;
  HDC                   hDC;
  HGLRC                 hRC;
  INT                   iPixelFormat;

  hDC = auxGetHDC();

  iPixelFormat = GetPixelFormat(hDC);
  DescribePixelFormat(hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

  if ((pfd.dwFlags & PFD_SUPPORT_GDI) == 0) {

    OutputDebugString("PFD_SUPPORT_GDI not supported!\n");

    pfd.dwFlags |= PFD_SUPPORT_GDI;

  }

  if (!SetPixelFormat(hDC, iPixelFormat, &pfd))
    OutputDebugString("PFD_SUPPORT_GDI still not supported!\n");

  //
  // Maynot need this
  //
  hRC = wglCreateContext(hDC);
  if (!wglMakeCurrent(hDC, hRC))
    OutputDebugString("MakeCurrentFailed!\n");
#endif

  scene_init();

  //gettimeofday(&starttime, NULL);
  //srand(starttime.tv_usec);

  GetSystemTime(&starttime);
  srand(starttime.wMilliseconds);

  hWnd = auxGetHWND();
  GetClientRect(hWnd, &rect);

  glViewport(0, 0, rect.right, rect.bottom);
  glClear(GL_COLOR_BUFFER_BIT);

  //
  // this is for initizating the menu items
  //
  hmenu = GetMenu(hWnd);

  for (i=0; i < nlights; i++) {
    if (lights[i].on)
      CheckMenuItem(hmenu, IDM_RED+i, MF_BYCOMMAND | MF_CHECKED);
    else
      CheckMenuItem(hmenu, IDM_RED+i, MF_BYCOMMAND | MF_UNCHECKED);
  }

  if (draw_square)
    CheckMenuItem(hmenu, IDM_SQUARE, MF_BYCOMMAND | MF_CHECKED);
  if (draw_shadows)
    CheckMenuItem(hmenu, IDM_SHADOW, MF_BYCOMMAND | MF_CHECKED);
  if (draw_refraction)
    CheckMenuItem(hmenu, IDM_REFRACTION, MF_BYCOMMAND | MF_CHECKED);
  if (draw_lights)
    CheckMenuItem(hmenu, IDM_LIGHTS, MF_BYCOMMAND | MF_CHECKED);
  if (draw_sphere)
    CheckMenuItem(hmenu, IDM_SPHERE, MF_BYCOMMAND | MF_CHECKED);
  if (draw_texture)
    CheckMenuItem(hmenu, IDM_TEXTURE, MF_BYCOMMAND | MF_CHECKED);

  CheckMenuItem(hmenu, IDM_AIR+def_refraction_index, MF_BYCOMMAND | MF_CHECKED);
  CheckMenuItem(hmenu, IDM_10+def_divisions_index, MF_BYCOMMAND | MF_CHECKED);

}

/******************************Public*Routine******************************\
*
* vExpose
*
* // !!! void exposeCB(Widget w)
*
* History:
*  30-Nov-1993
*
\**************************************************************************/

void vExpose(int x, int y)
{
  draw();
}

/******************************Public*Routine******************************\
*
* vResize
*
* // void resizeCB(Widget w, XtPointer client_data, XtPointer call)
*
* Effects:  changes globals winx, winy and aspect
*
* History:
*  30-Nov-1993
*
\**************************************************************************/

void CALLBACK vResize(GLsizei width, GLsizei height)
{
#ifdef GLX_MOTIF
  GLwDrawingAreaCallbackStruct *call_data;
  call_data = (GLwDrawingAreaCallbackStruct *)call;

  GLwDrawingAreaMakeCurrent(w, glx_context);
  winx = call_data->width;
  winy = call_data->height;
#endif

  winx = width;
  winy = height;

  glViewport(0, 0, winx, winy);

  aspect = (GLfloat)winx / (GLfloat)winy;
}

#ifdef GLX_MOTIF
//
// !!! mouse down/up function??
//
void inputCB(Widget w, XtPointer client_data, XtPointer call_data)
{
  int picked;
  GLwDrawingAreaCallbackStruct *call;

  char buffer[5];
  int bufsize = 5;
  KeySym key;
  XComposeStatus compose;

  static int mousex, mousey;
  /* Just to confuse everybody, I've made these go from 0-1. */
  float dmousex, dmousey;
  float r1, r2;

  call = (GLwDrawingAreaCallbackStruct *)call_data;

  GLwDrawingAreaMakeCurrent(w, glx_context);
  switch(call->event->type) {
  case ButtonPress:
    button_down = call->event->xbutton.button;
    mousex = call->event->xbutton.x;
    mousey = call->event->xbutton.y;
    picked = scene_pick(mousex, mousey);
    if (picked >= name_lights) name_selected = picked;
    break;
  case ButtonRelease:
    if (quick_moves)
      scene_move_update(name_selected, button_down == Button2,
button_down == Button3, button_down = Button1);
    button_down = 0;
    break;
  case MotionNotify:
    if (button_down == Button1) {
      /* This is the "default" mouse button - moves things in theta
       * since this is easy and computationally cheap */
      dmousex = (double)(call->event->xmotion.x - mousex) / (double)winx;
      scene_move(name_selected, 0, 0, dmousex, quick_moves ? 0 : 1);
    } else if (button_down == Button2) {
      /* Change the radius - figue out the component of the mouse motion
       * that's going toward the center of the screen */
      mousex = (winx / 2) - mousex;
      mousey = (winy / 2) - mousey;
      r1 = sqrt((float)(mousex*mousex) / (float)(winx*winx) +
(float)(mousey*mousey) / (float)(winy*winy));
      mousex = call->event->xmotion.x;
      mousey = call->event->xmotion.y;
      mousex = (winx / 2) - mousex;
      mousey = (winy / 2) - mousey;
      r2 = sqrt((float)(mousex*mousex) / (float)(winx*winx) +
(float)(mousey*mousey) / (float)(winy*winy));
      scene_move(name_selected, r2 - r1, 0, 0, quick_moves ? 0 : 1);
    } else if (button_down == Button3) {
      /* Change phi - this is expensive */
      dmousex = (double)(call->event->xmotion.x - mousex) / (double)winx;
      scene_move(name_selected, 0, dmousex, 0, quick_moves ? 0 : 1);
    }
    mousex = call->event->xmotion.x;
    mousey = call->event->xmotion.y;
    break;
  case KeyPress:
    XLookupString(&(call->event->xkey), buffer, bufsize, &key, &compose);
    if (key == XK_Escape) exit(0);
    break;
  default:
    break;
  }

  draw();
}
#endif

/******************************Public*Routine******************************\
*
* vDrawAll(void)
*
* //void drawAllCB(Widget w)
*
* Effects:
*
* History:
*  30-Nov-1993
*
\**************************************************************************/

void CALLBACK vDrawAll(void)
{
  HMENU hmenu;
  int       i;
  static int fAll=0;

  hmenu = GetMenu(auxGetHWND());

  fAll = fAll ? 0 : 1;

  if (fAll) {
    for (i=0; i < 6; i++)
       CheckMenuItem(hmenu, IDM_SQUARE+i, MF_BYCOMMAND | MF_CHECKED);

    CheckMenuItem(hmenu, IDM_ALL, MF_BYCOMMAND | MF_CHECKED);
    draw_square = 1;
    draw_shadows = 1;
    draw_refraction = 1;
    draw_sphere = 1;
    draw_lights = 1;

  } else {
    CheckMenuItem(hmenu, IDM_ALL, MF_BYCOMMAND | MF_UNCHECKED);
  }

  draw();
}

/******************************Public*Routine******************************\
*
* //void drawSomethingCB(Widget w, XtPointer client_data, XtPointer call_data)
*
* Effects:  functions for auxKeyFunc can't have parameters so we have
*           these similar functions here.
*
* History:
*  30-Nov-1993
*
\**************************************************************************/

void CALLBACK vDrawSquare(void)
{
#ifdef GLX_MOTIF
  XmToggleButtonCallbackStruct *ptr;
  int *data;
  int i;

  ptr = (XmToggleButtonCallbackStruct *)call_data;
  data = (int *)client_data;
  *data = ptr->set;
#endif
  HMENU hmenu;

  hmenu = GetMenu(auxGetHWND());

  draw_square = draw_square ? 0 : 1;
  if (draw_square)
      CheckMenuItem(hmenu, IDM_SQUARE, MF_BYCOMMAND | MF_CHECKED);
  else
      CheckMenuItem(hmenu, IDM_SQUARE, MF_BYCOMMAND | MF_UNCHECKED);

  draw();
}

void CALLBACK vDrawShadow(void)
{
  HMENU hmenu;

  hmenu = GetMenu(auxGetHWND());

  draw_shadows = draw_shadows ? 0 : 1;
  if (draw_shadows)
      CheckMenuItem(hmenu, IDM_SHADOW, MF_BYCOMMAND | MF_CHECKED);
  else
      CheckMenuItem(hmenu, IDM_SHADOW, MF_BYCOMMAND | MF_UNCHECKED);
  draw();
}

void CALLBACK vDrawRefraction(void)
{
  HMENU hmenu;

  hmenu = GetMenu(auxGetHWND());

  draw_refraction = draw_refraction ? 0 : 1;
  if (draw_refraction)
      CheckMenuItem(hmenu, IDM_REFRACTION, MF_BYCOMMAND | MF_CHECKED);
  else
      CheckMenuItem(hmenu, IDM_REFRACTION, MF_BYCOMMAND | MF_UNCHECKED);
  draw();
}
void CALLBACK vDrawSphere(void)
{
  HMENU hmenu;

  hmenu = GetMenu(auxGetHWND());

  draw_sphere = draw_sphere ? 0 : 1;
  if (draw_sphere)
      CheckMenuItem(hmenu, IDM_SPHERE, MF_BYCOMMAND | MF_CHECKED);
  else
      CheckMenuItem(hmenu, IDM_SPHERE, MF_BYCOMMAND | MF_UNCHECKED);
  draw();
}
void CALLBACK vDrawLight(void)
{
  HMENU hmenu;

  hmenu = GetMenu(auxGetHWND());

  draw_lights = draw_lights ? 0 : 1;
  if (draw_lights)
      CheckMenuItem(hmenu, IDM_LIGHTS, MF_BYCOMMAND | MF_CHECKED);
  else
      CheckMenuItem(hmenu, IDM_LIGHTS, MF_BYCOMMAND | MF_UNCHECKED);
  draw();
}
void CALLBACK vDrawTexture(void)
{
  HMENU hmenu;

  hmenu = GetMenu(auxGetHWND());

  draw_texture = draw_texture ? 0 : 1;
  if (draw_texture)
      CheckMenuItem(hmenu, IDM_TEXTURE, MF_BYCOMMAND | MF_CHECKED);
  else
      CheckMenuItem(hmenu, IDM_TEXTURE, MF_BYCOMMAND | MF_UNCHECKED);
  draw();
}

void vDrawStuff(int *what)
{
  *what = *what ? 0 : 1;
  draw();
}

/******************************Public*Routine******************************\
*
* //void refractionCB(Widget w, XtPointer client_data, XtPointer call_data)
*
* Effects:  functions for auxKeyFunc can't have parameters so we have
*           these similar functions here.
* History:
*  30-Nov-1993
*
\**************************************************************************/

void CALLBACK vRefractionAIR(void)
{
#ifdef GLX_MOTIF
  XmToggleButtonCallbackStruct *ptr;
  GLfloat refraction;

  ptr = (XmToggleButtonCallbackStruct *)call_data;
  if (!ptr->set) return;
  refraction = *((GLfloat *)client_data);
#endif

  GLfloat refraction;
  HMENU   hmenu;
  int         i;

  hmenu = GetMenu(auxGetHWND());
  for (i = 0; i < nindices; i++)
    CheckMenuItem(hmenu, IDM_AIR+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_AIR+0, MF_BYCOMMAND | MF_CHECKED);
  refraction = indices[0].index;
  refraction_change(refraction);
  draw();
}

void CALLBACK vRefractionICE(void)
{
  GLfloat refraction;
  HMENU   hmenu;
  int         i;

  hmenu = GetMenu(auxGetHWND());
  for (i = 0; i < nindices; i++)
    CheckMenuItem(hmenu, IDM_AIR+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_AIR+1, MF_BYCOMMAND | MF_CHECKED);
  refraction = indices[1].index;
  refraction_change(refraction);
  draw();
}

void CALLBACK vRefractionWATER(void)
{
  GLfloat refraction;
  HMENU   hmenu;
  int         i;

  hmenu = GetMenu(auxGetHWND());
  for (i = 0; i < nindices; i++)
    CheckMenuItem(hmenu, IDM_AIR+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_AIR+2, MF_BYCOMMAND | MF_CHECKED);
  refraction = indices[2].index;
  refraction_change(refraction);
  draw();
}

void CALLBACK vRefractionZincGLASS(void)
{
  GLfloat refraction;
  HMENU   hmenu;
  int         i;

  hmenu = GetMenu(auxGetHWND());
  for (i = 0; i < nindices; i++)
    CheckMenuItem(hmenu, IDM_AIR+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_AIR+3, MF_BYCOMMAND | MF_CHECKED);
  refraction = indices[3].index;
  refraction_change(refraction);
  draw();
}
void CALLBACK vRefractionLightGLASS(void)
{
  GLfloat refraction;
  HMENU   hmenu;
  int         i;

  hmenu = GetMenu(auxGetHWND());
  for (i = 0; i < nindices; i++)
    CheckMenuItem(hmenu, IDM_AIR+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_AIR+4, MF_BYCOMMAND | MF_CHECKED);
  refraction = indices[4].index;
  refraction_change(refraction);
  draw();
}
void CALLBACK vRefractionHeavyGLASS(void)
{
  GLfloat refraction;
  HMENU   hmenu;
  int         i;

  hmenu = GetMenu(auxGetHWND());
  for (i = 0; i < nindices; i++)
    CheckMenuItem(hmenu, IDM_AIR+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_AIR+5, MF_BYCOMMAND | MF_CHECKED);
  refraction = indices[5].index;
  refraction_change(refraction);
  draw();
}

void vRefraction(int type)
{
  GLfloat refraction;
  HMENU   hmenu;
  int         i;

  hmenu = GetMenu(auxGetHWND());
  for (i = 0; i < nindices; i++)
    CheckMenuItem(hmenu, IDM_AIR+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_AIR+type, MF_BYCOMMAND | MF_CHECKED);
  refraction = indices[type].index;
  refraction_change(refraction);
  draw();
}

/******************************Public*Routine******************************\
*
* vSubdivision
*
* //void subdivisionCB(Widget w, XtPointer client_data, XtPointer call_data)
*
* Effects:  functions for auxKeyFunc can't have parameters so we have
*           these similar functions here.
* History:
*  30-Nov-1993
*
\**************************************************************************/

void CALLBACK vSubdivision10(void)
{
#ifdef GLX_MOTIF
  XmToggleButtonCallbackStruct *ptr;
  int subdivisions;

  ptr = (XmToggleButtonCallbackStruct *)call_data;
  if (!ptr->set) return;
  subdivisions = *((int *)client_data);
#endif

  HMENU hmenu;
  int       i;

  hmenu = GetMenu(auxGetHWND());

  for (i = 0; i < npossible_divisions; i++)
    CheckMenuItem(hmenu, IDM_10+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_10, MF_BYCOMMAND | MF_CHECKED);
  divisions_change(possible_divisions[0]);

  draw();
}

void CALLBACK vSubdivision20(void)
{
  HMENU hmenu;
  int       i;

  hmenu = GetMenu(auxGetHWND());

  for (i = 0; i < npossible_divisions; i++)
    CheckMenuItem(hmenu, IDM_10+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_20, MF_BYCOMMAND | MF_CHECKED);
  divisions_change(possible_divisions[1]);

  draw();
}

void CALLBACK vSubdivision30(void)
{
  HMENU hmenu;
  int       i;

  hmenu = GetMenu(auxGetHWND());

  for (i = 0; i < npossible_divisions; i++)
    CheckMenuItem(hmenu, IDM_10+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_30, MF_BYCOMMAND | MF_CHECKED);
  divisions_change(possible_divisions[2]);

  draw();
}

void CALLBACK vSubdivision40(void)
{
  HMENU hmenu;
  int       i;

  hmenu = GetMenu(auxGetHWND());

  for (i = 0; i < npossible_divisions; i++)
    CheckMenuItem(hmenu, IDM_10+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_40, MF_BYCOMMAND | MF_CHECKED);
  divisions_change(possible_divisions[3]);

  draw();
}

void vSubdivision(int which)
{
  HMENU hmenu;
  int       i;

  hmenu = GetMenu(auxGetHWND());

  for (i = 0; i < npossible_divisions; i++)
    CheckMenuItem(hmenu, IDM_10+i, MF_BYCOMMAND | MF_UNCHECKED);

  CheckMenuItem(hmenu, IDM_10+which, MF_BYCOMMAND | MF_CHECKED);

  divisions_change(possible_divisions[which]);
  draw();
}

/******************************Public*Routine******************************\
*
* v[RGB]Light_on
*
* //void light_onCB(Widget w, XtPointer client_data, XtPointer call_data)
*
* Effects:  functions for auxKeyFunc can't have parameters so we have
*           these similar functions here.
* History:
*  30-Nov-1993
*
\**************************************************************************/
#define RED_LIGHT   0
#define GREEN_LIGHT 1
#define BLUE_LIGHT  2

void CALLBACK vRLight_on(void)
{
#ifdef GLX_MOTIF
  XmToggleButtonCallbackStruct *ptr;

  ptr = (XmToggleButtonCallbackStruct *)call_data;
  lights_onoff((light *)client_data - lights, ptr->set);
#endif

  int fOn;
  HMENU   hmenu;

  hmenu = GetMenu(auxGetHWND());

  fOn = lights[RED_LIGHT].on ? 0 : 1;
  if (fOn)
    CheckMenuItem(hmenu, IDM_RED, MF_BYCOMMAND | MF_CHECKED);
  else
    CheckMenuItem(hmenu, IDM_RED, MF_BYCOMMAND | MF_UNCHECKED);
  lights_onoff(RED_LIGHT, fOn);
  draw();
}

void CALLBACK vGLight_on(void)
{
  int fOn;
  HMENU   hmenu;

  hmenu = GetMenu(auxGetHWND());

  fOn = lights[GREEN_LIGHT].on ? 0 : 1;
  if (fOn)
    CheckMenuItem(hmenu, IDM_GREEN, MF_BYCOMMAND | MF_CHECKED);
  else
    CheckMenuItem(hmenu, IDM_GREEN, MF_BYCOMMAND | MF_UNCHECKED);
  lights_onoff(GREEN_LIGHT, fOn);
  draw();
}

void CALLBACK vBLight_on(void)
{
  int fOn;
  HMENU   hmenu;

  hmenu = GetMenu(auxGetHWND());

  fOn = lights[BLUE_LIGHT].on ? 0 : 1;
  if (fOn)
    CheckMenuItem(hmenu, IDM_BLUE, MF_BYCOMMAND | MF_CHECKED);
  else
    CheckMenuItem(hmenu, IDM_BLUE, MF_BYCOMMAND | MF_UNCHECKED);
  lights_onoff(BLUE_LIGHT, fOn);
  draw();
}

void vLight_on(int which)
{
  int fOn;
  HMENU   hmenu;

  hmenu = GetMenu(auxGetHWND());

  fOn = lights[which].on ? 0 : 1;

  if (fOn)
    CheckMenuItem(hmenu, IDM_RED+which, MF_BYCOMMAND | MF_CHECKED);
  else
    CheckMenuItem(hmenu, IDM_RED+which, MF_BYCOMMAND | MF_UNCHECKED);

  lights_onoff(which, fOn);
  draw();
}

/******************************Public*Routine******************************\
*
* vExit
*
* //void exitCB(Widget w, XtPointer client_data, XtPointer call_data)
*
* Effects: !!! this is redundant unless we do stuff other than auxQuit
*
* History:
*  30-Nov-1993
*
\**************************************************************************/

void vExit(void)
{
#ifdef GLX_MOTIF
  exit(0);
#endif

    auxQuit();
}

/******************************Public*Routine******************************\
*
* bAutoMotion(void)
*
* // Boolean drawWP(XtPointer data)
*
* Effects:  !!! for WM_TIMER ?
*
* History:
*  30-Nov-1993
*
\**************************************************************************/

GLboolean bAutoMotion(void)
{
  float t, dt;
  int i;

  t = current_time();
  dt = t - last_motion_update;
  dt = (dt < 0) ? -dt : dt;

  if (dt < time_fudge) {

    //wsprintf(text, "dt = %lx\n", dt);
    //OutputDebugString(text);
    return FALSE;
  }

  for (i = 0; i < nlights; i++) {
    scene_move(name_lights + i, 0, 0, dtheta[i] * dt, 1);
  }

  last_motion_update = t;

  draw();
  return FALSE;
}

void CALLBACK vMouseDown(AUX_EVENTREC *event)
{
  int    picked;

  mouse_x = event->data[AUX_MOUSEX];
  mouse_y = event->data[AUX_MOUSEY];

  picked = scene_pick(mouse_x, mouse_y);
  if (picked >= name_lights)
     name_selected = picked;

  return;
}

void CALLBACK vLeftMouseUp(AUX_EVENTREC *event)
{
  if (quick_moves)
    scene_move_update(name_selected, 0,     // dr
                     0,     // dphi
                                     1);    // dtheta

  return;
}

void CALLBACK vMiddleMouseUp(AUX_EVENTREC *event)
{
  if (quick_moves)
    scene_move_update(name_selected, 1,     // dr
                     0,     // dphi
                                     0);    // dtheta

  return;
}

void CALLBACK vRightMouseUp(AUX_EVENTREC *event)
{
  if (quick_moves)
    scene_move_update(name_selected, 0,     // dr
                     1,     // dphi
                                     0);    // dtheta

  return;
}

void CALLBACK vMouseMove(AUX_EVENTREC *event)
{
  float dmousex;
  float r1, r2;
  GLint button = event->data[AUX_MOUSESTATUS];
  GLint x = event->data[AUX_MOUSEX];
  GLint y = event->data[AUX_MOUSEY];

  switch( button ) {
    case AUX_LEFTBUTTON:
      /* This is the "default" mouse button - moves things in theta
       * since this is easy and computationally cheap */
      dmousex = (float)((double)(x - mouse_x) / (double)winx);
      scene_move(name_selected, 0, 0, dmousex, quick_moves ? 0 : 1);
      break;

    case AUX_MIDDLEBUTTON:
      /* Change the radius - figue out the component of the mouse motion
       * that's going toward the center of the screen */
      mouse_x = (winx / 2) - mouse_x;
      mouse_y = (winy / 2) - mouse_y;
      r1 = (float)sqrt((double)(mouse_x*mouse_x) / (double)(winx*winx) +
    (double)(mouse_y*mouse_y) / (double)(winy*winy));
      mouse_x = x;
      mouse_y = y;
      mouse_x = (winx / 2) - mouse_x;
      mouse_y = (winy / 2) - mouse_y;
      r2 = (float)sqrt((double)(mouse_x*mouse_x) / (double)(winx*winx) +
  (double)(mouse_y*mouse_y) / (double)(winy*winy));
      scene_move(name_selected, r2 - r1, 0, 0, quick_moves ? 0 : 1);
      break;

    case AUX_RIGHTBUTTON :
      /* Change phi - this is expensive */
      dmousex = (float)((double)(x - mouse_x) / (double)winx);
      scene_move(name_selected, 0, dmousex, 0, quick_moves ? 0 : 1);
      break;
    }

    mouse_x = x;
    mouse_y = y;
}

//
// hack these since cfront generates these...
//
PVOID __nw(unsigned int ui)
{
    return LocalAlloc(LMEM_FIXED, ui);
}

VOID __dl(PVOID pv)
{

    LocalFree(pv);
    return;
}

PVOID __vec_new(void *p, int x, int y, void *q)
{
    return LocalAlloc(LMEM_FIXED, x*y);
}


MENU.H

#define IDM_EXIT        1000

#define IDM_AIR         1001
#define IDM_ICE         1002
#define IDM_WATER       1003
#define IDM_ZINC        1004
#define IDM_LIGHT       1005
#define IDM_HEAVY       1006

#define IDM_SQUARE      1007
#define IDM_SHADOW      1008
#define IDM_REFRACTION  1009
#define IDM_SPHERE      1010
#define IDM_LIGHTS      1011
#define IDM_TEXTURE     1012
#define IDM_ALL         1013

#define IDM_10          1014
#define IDM_20          1015
#define IDM_30          1016
#define IDM_40          1017

#define IDM_RESETLGT    1018

#define IDM_RED         1019
#define IDM_GREEN       1020
#define IDM_BLUE        1021

#define IDM_QUICK       1022
#define IDM_AUTO        1023


POINT.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/gl.h>

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

#define POINT_EXTERN
#include "point.hxx"

// Change this back to inline
Point Point::rotate_abouty(GLfloat c, GLfloat s)
{
  val.pt[0] = c*pt[0] + s*pt[2];
  val.pt[1] = pt[1];
  val.pt[2] = -s*pt[0] + c*pt[2];
  return val;
}

void Point::refract_self(Point light, Point N, GLfloat I)
{
  GLfloat t;
  Point dlight;

  dlight = refract_direction(light, N, I);
  t = -pt[2] / dlight.pt[2];
  pt[0] = pt[0] + dlight.pt[0]*t;
  pt[1] = pt[1] + dlight.pt[1]*t;
  pt[2] = 0;
}

Point Point::refract_direction(Point light, Point N, GLfloat I)
{
  GLfloat cos1, sin1, cos2, sin2, m;
  GLfloat dlight[3], dN[3], axis[3];

  /* dlight = (light - *this).unit() * -1.0; */
  dlight[0] = pt[0] - light.pt[0];
  dlight[1] = pt[1] - light.pt[1];
  dlight[2] = pt[2] - light.pt[2];
  m = (GLfloat)sqrt((double)(dlight[0]*dlight[0] + dlight[1]*dlight[1] + dlight[2]*dlight[2]));
  dlight[0] /= m;
  dlight[1] /= m;
  dlight[2] /= m;

  // dN = N * -1.0;
  dN[0] = -N.pt[0];
  dN[1] = -N.pt[1];
  dN[2] = -N.pt[2];

  // cos1 = dN.dot(dlight);
  cos1 = dN[0]*dlight[0] + dN[1]*dlight[1] + dN[2]*dlight[2];

  if (1.0 - cos1*cos1 < point_fudge) {
    val = dN;
    return val;
  }

  // axis = ((dN * dlight) * dN).unit();
  val.pt[0] = dN[1]*dlight[2] - dlight[1]*dN[2];
  val.pt[1] = dN[2]*dlight[0] - dlight[2]*dN[0];
  val.pt[2] = dN[0]*dlight[1] - dN[1]*dlight[0];
  axis[0] = val.pt[1]*dN[2] - dN[1]*val.pt[2];
  axis[1] = val.pt[2]*dN[0] - dN[2]*val.pt[0];
  axis[2] = val.pt[0]*dN[1] - val.pt[1]*dN[0];
  m = (GLfloat)sqrt((double)(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]));
  axis[0] /= m;
  axis[1] /= m;
  axis[2] /= m;

  if (axis[0]*axis[0] > point_fudge)
    sin1 = (dlight[0] - dN[0] * cos1) / axis[0];
  else if (axis[1]*axis[1] > point_fudge)
    sin1 = (dlight[1] - dN[1] * cos1) / axis[1];
  else sin1 = dlight[2] - dN[2] * cos1;

  sin2 = sin1 / I;
  cos2 = (sin1*sin1 < 1.0) ? (GLfloat)sqrt((double)(1.0 - sin2*sin2)) : (GLfloat)0;

  dlight[0] = dN[0]*cos2 + axis[0]*sin2;
  dlight[1] = dN[1]*cos2 + axis[1]*sin2;
  dlight[2] = dN[2]*cos2 + axis[2]*sin2;

  /* I'm not sure this is quite legal */
  if (dlight[2] > 0.0) dlight[2] = -dlight[2];

  val = dlight;

  return val;
}

void Point::print()
{
  print("%f %f %f\n");
}

void Point::print(const char *format)
{
  //printf(format, this->pt[0], this->pt[1], this->pt[2], 1.0);
}


SCENE.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

extern "C" {
#include <GL/glaux.h>
}
#include <GL/glu.h>
#include <GL/gl.h>

#ifdef GLX_MOTIF
#include <GL/glx.h>
#endif

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

#include "Unitdisk.hxx"
#include "scene.hxx"

const GLfloat I[16] = {
                        1, 0, 0, 0,
                        0, 1, 0, 0,
                        0, 0, 1, 0,
                        0, 0, 0, 1
                      };

Color white;
Color black;

const double M_2PI      = 2.0 * M_PI;
const float scene_fudge = .000001;

// Lights are native to the xz plane and are rotated into position -
// shadows and refraction will not have to be changed if lights are
// just rotating about the z axis

light lights[] = {
                  {{1, 0, 0, 1}, {0, 0, 0, 0}, {1, 0, 0, 0},
                     {1, 0, 0, 0,  0, 1, 0, 0,   0, 0, 1, 0,   0, 0, 0, 1},
                        "Red", 1},
                  {{0, 1, 0, 1}, {0, 0, 0, 0}, {0, 1, 0, 0},
                     {1, 0, 0, 0,  0, 1, 0, 0,   0, 0, 1, 0,   0, 0, 0, 1},
                        "Green", 1},
                  {{0, 0, 1, 1}, {0, 0, 0, 0}, {0, 0, 1, 0},
                     {1, 0, 0, 0,  0, 1, 0, 0,   0, 0, 1, 0,   0, 0, 0, 1},
                        "Blue", 1}
                 };

GLfloat light_init_position[nlights][4] = {
                                           {1.5, 0, 2.5, 1},
                                           {1, 0, 3, 1},
                                           {2, 0, 3, 1}
                                          };

GLfloat light_init_rotation[nlights] = {135, 0, 90};
GLfloat light_rotation[nlights];

Color world_ambient(.25, .25, .25);

GLfloat index = indices[def_refraction_index].index;

GLfloat square_ambient[4]  = {.25, .25, .25, 1};
GLfloat square_diffuse[4]  = {1, 1, 1, 1};
GLfloat square_specular[4] = {0, 0, 0, 1};

const GLfloat fov          = 45.0;
GLfloat aspect             = 1.0;
GLfloat eyep[3]            = {-6, 0, 6};
GLfloat lookp[3]           = {0, 0, 1};

#ifdef GLX_MOTIF
static GLXContext glx_context;
#endif

const int max_args = 20;

static int list_square;
static int lists_shadows;
static int lists_refraction;
static int lists_lights    = 5;
static int list_sphere     = 4;
static int list_spheredisk = 9;
static int list_lights_on  = 6;
static int list_lights_off = 7;
static int list_light_draw = 8;

int draw_square     = 1;
int draw_shadows    = 1;
int draw_refraction = 1;
int draw_sphere     = 1;
int draw_lights     = 1;
int draw_texture    = 0;

int possible_divisions[] = {10, 20, 30, 40};

// Sphere is stored as floats - more efficient
GLfloat *spherepts = NULL;
int nspherepts     = 0;
int spherediv      = 0;
Point sphere_position = {0, 0, 1, 1};
GLfloat sphere_size   = .5;
const GLfloat sphere_ambient[4]  = {0, 0, 0, 0};
const GLfloat sphere_specular[4] = {0, 0, 0, 0};
Unitdisk sphere_disk;
static void sphere_build();
static void sphere_list_init();
static void sphere_draw();

static void square_list_init();

static void lights_init_onoff();
static void lights_init_position();
static void lights_init_position(int i);
static void lights_list_init();
static void lights_list_init(int n);

static void light_draw_list_init();

Unitdisk disks[nlights];
int diskdiv = possible_divisions[def_divisions_index];
static void disk_build();
static void disk_build(int disk);

Unitdisk shadows[nlights];
static void shadow_list_init();
static void shadow_list_init(int n);
static void shadow_draw(int n);

Unitdisk refraction[nlights];
static void refraction_list_init();
static void refraction_list_init(int n);

static void shadow_refraction_full_build();
static void shadow_refraction_full_build(int n);

void scene_init();

#ifdef MYDEBUG
void lists_init();
void lights_init();
int  lights_move(int light, float dr, float dphi, float dtheta,
       int update);

void lights_move_update(int light, int dr, int dphi, int dtheta);
#else
static void lists_init();
static void lights_init();
static int lights_move(int light, float dr, float dphi, float dtheta,
       int update);
static void lights_move_update(int light, int dr, int dphi, int dtheta);
#endif

void scene_draw();

void texture_init();
AUX_RGBImageRec *teximage = NULL;

inline float sign(float a)
{
  // This is badly written so let's not call it too often, 'k?
  return (a > 0) ? (float)1 : (a < 0) ? (float) -1 : (float) 0;
}

inline double degrees(double a)
{
  return (a * 180.0 / M_PI);
}

inline double radians(double a)
{
  return (a * M_PI / 180.0);
}

inline double degrees_clamp(double a)
{
  while (a < 0.0) a += 360.0;
  while (a > 360.0) a -= 360.0;
  return a;
}

inline double radians_clamp(double a)
{
  while (a < 0.0) a += M_2PI;
  while (a > M_2PI) a -= M_2PI;
  return a;
}

void scene_init()
{
  int i;

  white.c[0] = white.c[1] = white.c[2] = white.c[3] = 1;
  black.c[0] = black.c[1] = black.c[2] = 0;
  black.c[3] = 1;

  lists_init();

  for (i = 0; i < nlights; i++) {
    lights[i].pos = light_init_position[i];
    light_rotation[i] = light_init_rotation[i];
    lights_init_position(i);
  }

  divisions_change(possible_divisions[def_divisions_index]);

  lights_init_onoff();
  lights_init();
  lights_init_position();

  texture_init();

  glClearStencil(0);

  // This is for profiling
  // exit(0);
}

static void scene_project()
{
  glMatrixMode(GL_PROJECTION);
  gluPerspective(fov, aspect, 0.01, 20.0);
  gluLookAt(eyep[0], eyep[1], eyep[2], lookp[0], lookp[1], lookp[2],
            1, 0, 0);
}

static void scene_rasterize()
{
  int i;

  glLoadName(name_square);
  if (draw_square) {
    if (draw_texture) glEnable(GL_TEXTURE_2D);
    glCallList(list_square);
    glDisable(GL_TEXTURE_2D);
  }
  if (draw_shadows)
    for (i = 0; i < nlights; i++)
      if (lights[i].on) {
glPushMatrix();
glRotatef(-light_rotation[i], 0, 0, 1);
glCallList(lists_shadows + i);
glPopMatrix();
      }
  if (draw_refraction)
    for (i = 0; i < nlights; i++)
      if (lights[i].on) {
glPushMatrix();
glRotatef(-light_rotation[i], 0, 0, 1);
glCallList(lists_refraction + i);
glPopMatrix();
      }

  glLoadName(name_sphere);
  /* Drawing the sphere here makes the sphere visible through itself when we
   * do the refraction redraw hack -- for now, just don't draw it */
  //  if (draw_sphere) glCallList(list_sphere);

  for (i = 0; i < nlights; i++)
    if (draw_lights) glCallList(lists_lights + i);
}

/* This draws an image of the scene seen through the sphere */
static void scene_draw_refracted()
{
  int i;

  if (!draw_sphere) return;

  /* Draw an image of the sphere into the stencil plane  -
   * must do this every time in case the lights have moved in front
   * of it */
  glEnable(GL_STENCIL_TEST);
  glClearStencil(0);
  glClear(GL_STENCIL_BUFFER_BIT);
  glStencilFunc(GL_ALWAYS, 0x1, 0x1);
  glStencilOp(GL_REPLACE, GL_KEEP, GL_REPLACE);

  glColorMask(0, 0, 0, 0);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  scene_project();

  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
  glCallList(list_sphere);
  glDisable(GL_CULL_FACE);
  glDisable(GL_DEPTH_TEST);

  glColorMask(1, 1, 1, 1);

  /* Set up a transform with a wider field of view  - this is inaccurate
   * but I don't have time to do it right */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(fov * index, aspect, 0.01, 20.0);
  gluLookAt(eyep[0], eyep[1], eyep[2], lookp[0], lookp[1], lookp[2],
            1, 0, 0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  /* Set up the stencil stuff which will be used to draw the image */
  glStencilFunc(GL_NOTEQUAL, 0x0, 0xffffffff);
  glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

  /* Draw the image, gambling that we'll never see anything but the
   * floor through the table */
  glLoadName(name_sphere);
  if (draw_texture) glEnable(GL_TEXTURE_2D);
  if (draw_square) glCallList(list_square);
  if (draw_shadows)
    for (i = 0; i < nlights; i++)
      if (lights[i].on) {
glPushMatrix();
glRotatef(-light_rotation[i], 0, 0, 1);
glCallList(lists_shadows + i);
glPopMatrix();
      }
  if (draw_refraction)
    for (i = 0; i < nlights; i++)
      if (lights[i].on) {
glPushMatrix();
glRotatef(-light_rotation[i], 0, 0, 1);
glCallList(lists_refraction + i);
glPopMatrix();
      }
  glDisable(GL_TEXTURE_2D);

  /* Draw the sphere to make it look like it
   * has some substance */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  scene_project();
  glCallList(list_spheredisk);

  glDisable(GL_STENCIL_TEST);
}

void scene_draw()
{
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  scene_project();

  /* Should draw an image of the square into the stencil buffer
   * to make sure that refractions which are not on the square do not get
   * drawn, but it can wait. */

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  scene_rasterize();

  scene_draw_refracted();

}

const int pick_maxz = 0xffffffff;

int scene_pick(GLdouble x, GLdouble y)
{
  GLuint buffer[128];
  GLint vp[4], nhits, nnames;
  GLuint minz, hit = name_background;
  GLint i, j;

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glGetIntegerv(GL_VIEWPORT, vp);

  glSelectBuffer(128, buffer);
  glRenderMode(GL_SELECT);

  // Where is this supposed to go, anyway?
  gluPickMatrix(x, vp[3] - y, 1, 1, vp);

  scene_project();

  glMatrixMode(GL_MODELVIEW);

  glInitNames();
  glPushName(name_background);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  scene_rasterize();
  glFlush();
  nhits = glRenderMode(GL_RENDER);

  minz = pick_maxz;
  for (i = j = 0; j < nhits; j++) {
    nnames = buffer[i];
    i++;
    if (buffer[i] < minz) {
      minz = buffer[i];
      hit = buffer[i + 1 + nnames];
    }
    i++;
    i += nnames + 1;
  }
  if (minz == pick_maxz) return name_background;
  else return hit;
}

void scene_reset_lights()
{
  int i;
  for (i = 0; i < nlights; i++) {
    lights[i].pos = light_init_position[i];
    light_rotation[i] = light_init_rotation[i];
  }
  lights_init_position();
  lights_list_init();
}

static void square_list_init()
{
  GLfloat x, y, inc;
  int i, j;
  glNewList(list_square, GL_COMPILE);
  glLoadName(name_square);
  glNormal3f(0, 0, 1);
  glEnable(GL_LIGHTING);
  glCallList(list_lights_on);
  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, square_ambient);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, square_diffuse);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, square_specular);
  inc = (GLfloat) (10.0 / diskdiv);
  glEnable(GL_CULL_FACE);

  for (i = 0, y = -5.0; i < diskdiv; i++, y += inc) {
    glBegin(GL_TRIANGLE_STRIP);
    for (j = 0, x = -5.0; j <= diskdiv; j++, x += inc) {
      glTexCoord2f(x, y + inc);
      glVertex2f(x, y + inc);
      glTexCoord2f(x, y);
      glVertex2f(x, y);
    }
    glEnd();
  }

  glDisable(GL_CULL_FACE);
  glCallList(list_lights_off);
  glDisable(GL_LIGHTING);
  glEndList();
}

static void spheredisk_list_init()
{
  glNewList(list_spheredisk, GL_COMPILE);
  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
  glEnable(GL_LIGHTING);
  glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
  glEnable(GL_COLOR_MATERIAL);
  glMaterialfv(GL_AMBIENT, GL_FRONT_AND_BACK, sphere_ambient);
  glMaterialfv(GL_SPECULAR, GL_FRONT_AND_BACK, sphere_specular);
  glCallList(list_lights_on);
  sphere_disk.draw();
  glCallList(list_lights_off);
  glDisable(GL_COLOR_MATERIAL);
  glDisable(GL_LIGHTING);
  glDisable(GL_BLEND);
  glEndList();
}

void lights_onoff(int light, int val)
{
  lights[light].on = val;
  lights_init_onoff();
  lights_list_init(light);
  square_list_init();
}

void refraction_change(GLfloat refraction)
{
  if (refraction == index) return;
  index = refraction;
  shadow_refraction_full_build();
  refraction_list_init();
}

void divisions_change(int divisions)
{
  Point eye, look;

  if (divisions != spherediv) {
    spherediv = divisions;

    light_draw_list_init();
    lights_list_init();

    sphere_disk.set_divisions(spherediv, spherediv);
    sphere_disk.fill_points();
    sphere_disk.set_colors(white);
    sphere_disk.scale_alpha_by_z();
    eye = eyep;
    look = lookp;
    sphere_disk.face_direction((eye - look).unit());
    sphere_disk.copy_normals_from_points();
    sphere_disk.scale_translate(sphere_size, sphere_position);
    sphere_build();
    sphere_list_init();

    diskdiv = divisions;
    disk_build();
    shadow_refraction_full_build();
    square_list_init();
    spheredisk_list_init();
    shadow_list_init();
    refraction_list_init();
  }
}

int scene_move(int name, float dr, float dphi, float dtheta, int update)
{
  switch(name) {
  case name_background:
    return 0;
  case name_square:
    return 0;
  case name_sphere:
    return 0;
  default:
    if (name < name_lights || name > name_lights + nlights) return 0;
    return lights_move(name - name_lights, dr, dphi, dtheta, update);
  }
}

void scene_move_update(int name, int dr, int dphi, int dtheta)
{
  switch(name) {
  case name_background:
    break;
  case name_square:
    break;
  case name_sphere:
    break;
  default:
    if (name < name_lights || name > name_lights + nlights) break;
    lights_move_update(name - name_lights, dr, dphi, dtheta);
    break;
  }
}

#ifdef MYDEBUG
void lights_init_onoff()
#else
static void lights_init_onoff()
#endif
{
  int i;

  glNewList(list_lights_on, GL_COMPILE);
  for (i = 0; i < nlights; i++)
    if (lights[i].on) glEnable(GL_LIGHT0 + i);
    else glDisable(GL_LIGHT0 + i);
  glEndList();

  glNewList(list_lights_off, GL_COMPILE);
  for (i = 0; i < nlights; i++) glDisable(GL_LIGHT0 + i);
  glEndList();
}

#ifdef MYDEBUG
void lights_init_position()
#else
static void lights_init_position()
#endif
{
  int i;

  for (i = 0; i < nlights; i++) lights_init_position(i);

}

static void lights_init_position(int i)
{
  Point l, d;

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  l = lights[i].pos;
  l.pt[0] = (GLfloat)(lights[i].pos.pt[0] * cos((double)radians(light_rotation[i])));
  l.pt[1] = (GLfloat)(lights[i].pos.pt[0] * -sin((double)radians(light_rotation[i])));
  d = (sphere_position - l).unit();
  glLightfv(GL_LIGHT0 + i, GL_POSITION, l.pt);
  glLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, d.pt);
}

static void lights_list_init()
{
  int i;
  for (i = 0; i < nlights; i++) lights_list_init(i);
}

static void lights_list_init(int n)
{
  Color c;

  glNewList(lists_lights + n, GL_COMPILE);
  if (lights[n].on) {
    glLoadName(name_lights + n);

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
    glCallList(list_lights_on);

    c = lights[n].diffuse;
    glMaterialfv(GL_BACK, GL_AMBIENT, c.c);
    glMaterialfv(GL_BACK, GL_DIFFUSE, black.c);
    glMaterialfv(GL_BACK, GL_SPECULAR, black.c);

    glMaterialfv(GL_FRONT, GL_AMBIENT, (c * .75).c);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, white.c);
    glMaterialfv(GL_FRONT, GL_SPECULAR, white.c);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glRotatef(-light_rotation[n], 0, 0, 1);
    glTranslatef(lights[n].pos.pt[0], lights[n].pos.pt[1],
 lights[n].pos.pt[2]);
    glRotatef((GLfloat)-degrees(atan2((double)(lights[n].pos.pt[2] - sphere_position.pt[2]),
     (double)(lights[n].pos.pt[0]))), 0, 1, 0);
    glCallList(list_light_draw);
    glPopMatrix();

    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
    glDisable(GL_LIGHTING);
    glCallList(list_lights_off);
    glDisable(GL_DEPTH_TEST);
  } else {
    /* 5.0.1 for Elans seems to object strongly to replacing
     * empty display lists, so will put some stupid command
     * in here */
    glColor3f(0, 0, 0);
  }
  glEndList();
}

static void light_draw_list_init()
{
  float c, s;
  int t;

  glNewList(list_light_draw, GL_COMPILE);
  glEnable(GL_NORMALIZE);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glScalef(.25, .15, .15);
  glBegin(GL_QUAD_STRIP);
  for (t = 0; t <= spherediv; t++) {
    c = (float) cos((double)(M_2PI * (float)t / (float)spherediv));
    s = (float) sin((double)(M_2PI * (float)t / (float)spherediv));
    glNormal3f((GLfloat).25, (GLfloat)(.968*s), (GLfloat)(.968*c));
    glVertex3f((GLfloat)0, (GLfloat)s, (GLfloat)c);
    glVertex3f((GLfloat)1, (GLfloat)(.75*s), (GLfloat)(.75*c));
  }
  glEnd();
  glNormal3f(1, 0, 0);
  glBegin(GL_TRIANGLE_STRIP);
  for (t = 0; t <= spherediv; t++) {
    c = (float)cos((double)(M_2PI * (float)t / (float)spherediv));
    s = (float)sin((double)(M_2PI * (float)t / (float)spherediv));
    glVertex3f((GLfloat)1, (GLfloat)(.75*s), (GLfloat)(.75*c));
    glVertex3f(1, 0, 0);
  }
  glEnd();
  glPopMatrix();
  glDisable(GL_NORMALIZE);
  glEndList();
}

#ifdef MYDEBUG
void lights_init()
#else
static void lights_init()
#endif
{
  int i;

  for (i = 0; i < nlights; i++) {
    glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, lights[i].diffuse);
    glLightfv(GL_LIGHT0 + i, GL_SPECULAR, black.c);
    glLightfv(GL_LIGHT0 + i, GL_AMBIENT, black.c);
    glLightf(GL_LIGHT0 + i, GL_SPOT_EXPONENT, 4);
    glLightf(GL_LIGHT0 + i, GL_SPOT_CUTOFF, 90);
  }

  glLightfv(GL_LIGHT0 + nlights, GL_DIFFUSE, black.c);
  glLightfv(GL_LIGHT0 + nlights, GL_SPECULAR, black.c);
  glLightfv(GL_LIGHT0 + nlights, GL_AMBIENT, world_ambient.c);
  glEnable(GL_LIGHT0 + nlights);

  /* GL_LIGHT0 + nlights + 1 willl eventually be used to draw the
   * refractions - stay tuned. */
  glLightfv(GL_LIGHT0 + nlights + 1, GL_DIFFUSE, black.c);
  glLightfv(GL_LIGHT0 + nlights + 1, GL_SPECULAR, black.c);
  glLightfv(GL_LIGHT0 + nlights + 1, GL_AMBIENT, white.c);
}

#ifdef MYDEBUG
int lights_move(int light, float dr, float dphi, float dtheta,
#else
static int lights_move(int light, float dr, float dphi, float dtheta,

#endif
                       int update)
{
  float cphi, sphi, x, y;
  Point l, dl;

  if (!(dr || dphi || dtheta)) return 0;

  l = lights[light].pos - sphere_position;

  if (dr) {
    dl = l + l*dr;
    if (dl.mag() > sphere_size) l = dl;
  }

  if (dphi) {
    cphi = (float)cos((double)dphi);
    sphi = (float)sin((double)dphi);
    y = -l.pt[0]*sphi + l.pt[2]*cphi;

    /* This hack keeps with light from getting below the sphere -
     * the projection sections would completely freak if this ever
     * happened  - sphere_size is multiplied by two as a fudge factor*/
    if (y < 2.0*sphere_size) {
      dphi = (float)atan2((double)(l.pt[2] - 2.0*sphere_size), (double)l.pt[0]);
      cphi = (float)cos((double)dphi);
      sphi = (float)sin((double)dphi);

    }
    x = l.pt[0];
    l.pt[0] = x*cphi + l.pt[2]*sphi;
    l.pt[2] = -x*sphi + l.pt[2]*cphi;
  }

  if (dtheta) {
    light_rotation[light] += (GLfloat)degrees((double)dtheta);
    light_rotation[light] =  (GLfloat)degrees_clamp((double)light_rotation[light]);
  }

  lights[light].pos = l + sphere_position;
  lights[light].pos.pt[3] = 1;

  lights_init_position(light);
  lights_list_init(light);

  if (update) lights_move_update(light, dr ? 1 : 0, dphi ? 1 : 0,
 dtheta ? 1 : 0);
  return 1;
}

#ifdef MYDEBUG
void lights_move_update(int light, int dr, int dphi,
#else
static void lights_move_update(int light, int dr, int dphi,
#endif
       int dtheta)
{
  if (dr) {
    disk_build(light);
    shadow_refraction_full_build(light);
    shadow_list_init(light);
    refraction_list_init(light);
  } else if (dphi) {
    shadow_refraction_full_build(light);
    shadow_list_init(light);
    refraction_list_init(light);
  } else if (dtheta) {
  }

}

#ifdef MYDEBUG
int get_lists(int size)
#else
static int get_lists(int size)
#endif
{
  int i;
  i = glGenLists(size);
  if (size && !i) {
    //fprintf(stderr, "Unable to allocate %d display lists.\n");
    exit(1);
  }
  return i;
}

#ifdef MYDEBUG
void lists_init()
#else
static void lists_init()
#endif
{
  list_square = get_lists(1);
  lists_shadows = get_lists(nlights);
  lists_refraction = get_lists(nlights);
  lists_lights = get_lists(nlights);
  list_sphere = get_lists(1);
  list_spheredisk = get_lists(1);
  list_lights_on = get_lists(1);
  list_lights_off = get_lists(1);
  list_light_draw = get_lists(1);
//  sphere_build();
}

static inline int sphere_npoints()
{
  return (spherediv+1)*spherediv*3;
}

void sphere_build()
{
  int nspherepts;
  int r, t, index;
  float c, s;

  delete spherepts;
  nspherepts = sphere_npoints();
  if (nspherepts == 0) return;
  spherepts = new GLfloat[nspherepts];

  index = 0;
  for (r = 0; r <= spherediv; r++) {
    spherepts[index++] = (GLfloat)sin((double)(M_PI * (float)r / (float)spherediv));
    spherepts[index++] = 0;
    spherepts[index++] = (GLfloat)-cos((double)(M_PI * (float)r / (float)spherediv));
  }
  for (t = 1; t < spherediv; t++) {
    c = (float)cos((double)(2.0 * M_PI * (float)t / (float)spherediv));
    s = (float)sin((double)(2.0 * M_PI * (float)t / (float)spherediv));
    for (r = 0; r <= spherediv; r++) {
      spherepts[index++] = c*spherepts[r*3];
      spherepts[index++] = s*spherepts[r*3];
      spherepts[index++] = spherepts[r*3 + 2];
    }
  }

}

void sphere_list_init()
{
  glNewList(list_sphere, GL_COMPILE);
  sphere_disk.draw_by_perimeter();
  glEndList();
}

void sphere_draw()
{
  int r, t, p1, p2;

  for (t = 1; t < spherediv; t++) {
    glBegin(GL_QUAD_STRIP);
    p1 = (t - 1) * (spherediv + 1);
    p2 = t * (spherediv + 1);
    for (r = 0; r <= spherediv; r++, p1++, p2++) {
      glNormal3fv(&spherepts[p1*3]);
      glVertex3fv(&spherepts[p1*3]);
      glNormal3fv(&spherepts[p2*3]);
      glVertex3fv(&spherepts[p2*3]);
    }
    glEnd();
  }

  glBegin(GL_QUAD_STRIP);
  p1 = (spherediv + 1) * (spherediv - 1);
  p2 = 0;
  for (r = 0; r <= spherediv; r++, p1++, p2++) {
    glNormal3fv(&spherepts[p1*3]);
    glVertex3fv(&spherepts[p1*3]);
    glNormal3fv(&spherepts[p2*3]);
    glVertex3fv(&spherepts[p2*3]);
  }
  glEnd();
}

static void disk_build()
{
  int i;
  for (i = 0; i < nlights; i++) disk_build(i);
}

static void disk_build(int disk)
{
  Point light;
  light = lights[disk].pos;

  disks[disk].free_points_normals();
  disks[disk].free_colors();

  disks[disk].set_divisions(diskdiv, diskdiv);
  disks[disk].set_angle((float)(2.0 *
acos((double)(sphere_size / light.dist(sphere_position)))));
  disks[disk].fill_points();
}

static void shadow_list_init()
{
  int i;
  for (i = 0; i < nlights; i++) shadow_list_init(i);
}

static void shadow_list_init(int n)
{
  Color c(square_ambient[0], square_ambient[1], square_ambient[2]);

  c *= world_ambient;

  glNewList(lists_shadows + n, GL_COMPILE);
  glColorMask(lights[n].shadow_mask[0], lights[n].shadow_mask[1],
      lights[n].shadow_mask[2], lights[n].shadow_mask[3]);
  glDisable(GL_DEPTH_TEST);
  glColor3fv(c.c);
  shadows[n].draw_by_perimeter(0, 0, 1);
  glColorMask(1, 1, 1, 1);
  glEndList();
}

static void refraction_list_init()
{
  int i;
  for (i = 0; i < nlights; i++) refraction_list_init(i);
}

static void refraction_list_init(int n) {
  /* This could be loads simpler if it weren't for the texture mapping -
   * that's where all this weirdness with GL_LIGHT0 + nlights + 1 comes
   * in */
  glNewList(lists_refraction + n, GL_COMPILE);

  glEnable(GL_LIGHTING);
  glCallList(list_lights_off);
  /* This is white ambient light */
  glEnable(GL_LIGHT0 + nlights + 1);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, black.c);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, black.c);
  glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT);
  glEnable(GL_COLOR_MATERIAL);

  glBlendFunc(GL_ONE, GL_ONE);
  glEnable(GL_BLEND);

  glDisable(GL_DEPTH_TEST);
  refraction[n].draw();

  glDisable(GL_BLEND);

  glDisable(GL_COLOR_MATERIAL);
  glDisable(GL_LIGHT0 + nlights + 1);
  glDisable(GL_LIGHTING);

  glEndList();
}

static void shadow_refraction_full_build()
{
  int i;
  for (i = 0; i < nlights; i++) shadow_refraction_full_build(i);
}

/* This entire function is written a bit oddly... */
static void shadow_refraction_full_build(int n)
{
  Color c;
  float dist_light;
  Point dlight, zaxis;

  /* Make sure that we're starting over from scratch */
  shadows[n].free_points_normals();
  shadows[n].free_colors();
  refraction[n].free_points_normals();
  refraction[n].free_colors();

  dlight = lights[n].pos - sphere_position;
  dist_light = dlight.mag();
  dlight.unitize();
  zaxis.pt[0] = 0;
  zaxis.pt[1] = 0;
  zaxis.pt[2] = 1;

  shadows[n].set_divisions(disks[n].get_rdivisions(),
   disks[n].get_tdivisions());
  refraction[n].set_divisions(disks[n].get_rdivisions(),
      disks[n].get_tdivisions());

  shadows[n].alloc_points();
  shadows[n].face_direction(dlight, disks[n]);
  shadows[n].scale_translate(sphere_size, sphere_position);

  c = square_diffuse;
  c *= lights[n].diffuse;

  refraction[n].copy_points(disks[n]);
  refraction[n].set_colors(c);
  refraction[n].scale_colors_by_z();

  refraction[n].scale(sphere_size);
  refraction[n].refract_normals(zaxis * dist_light, index);
  refraction[n].face_direction(dlight);

  refraction[n].project_borrow_points(shadows[n]);
  refraction[n].free_normals();
  shadows[n].project(lights[n].pos);
  if (index != 1.0) refraction[n].scale_colors_by_darea(shadows[n]);
}

int scene_load_texture(char *texfile)
{
#ifdef TEXTURE
  teximage = auxRGBImageLoad(texfile);
#else
  teximage = NULL;
#endif

  if (teximage == NULL) return 0;
  else return 1;
}

void texture_init()
{
  if (teximage == NULL) return;

  gluBuild2DMipmaps(GL_TEXTURE_2D, 3, teximage->sizeX, teximage->sizeY,
    GL_RGB, GL_UNSIGNED_BYTE, teximage->data);

  glMatrixMode(GL_TEXTURE);
  glLoadIdentity();
  glRotatef(90, 0, 0, 1);
  /* This scales the texture so that it fits on the square */
  glTranslatef(.5, .5, 0);
  glScalef(2, 2, 1);
  glMatrixMode(GL_MODELVIEW);

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
 GL_NEAREST_MIPMAP_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
 GL_LINEAR);
}


UNITDISK.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/glu.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "Unitdisk.hxx"

const float fudge = .000001;
const float M_2PI = 2.0 * M_PI;

Unitdisk::Unitdisk()
{
  rdivisions = 3;
  tdivisions = 10;
  points = normals = NULL;
  colors = NULL;
  points_size = normals_size = colors_size = 0;
  angle = M_PI;
  zaxis[0] = 0;
  zaxis[1] = 0;
  zaxis[2] = 1;

  still_in_xy = 1;

  sintable = costable = NULL;
}

Unitdisk::~Unitdisk()
{
}

void Unitdisk::draw()
{
  if (points == NULL) return;
  glNormal3f(0, 0, 1);
  if (colors == NULL) draw_nocolors();
  else if (normals == NULL) draw_colors_nonormals();
  else draw_colors_normals();
}

void Unitdisk::draw_nocolors()
{
  int r, t, p1, p2;
  int has_n;

  has_n = (normals != NULL);

  for (t = 1; t < tdivisions; t++) {
    glBegin(GL_QUAD_STRIP);
    p1 = t * (rdivisions + 1);
    p2 = (t - 1) * (rdivisions + 1);
    for (r = 0; r <= rdivisions; r++, p1++, p2++) {
      if (has_n) glNormal3fv(normals[p1].pt);
#ifdef TEXTURE
      glTexCoord2fv(points[p1].pt);
#endif
      glVertex3fv(points[p1].pt);
      if (has_n) glNormal3fv(normals[p2].pt);
#ifdef TEXTURE
      glTexCoord2fv(points[p2].pt);
#endif
      glVertex3fv(points[p2].pt);
    }
    glEnd();
  }

  glBegin(GL_QUAD_STRIP);
  p1 = 0;
  p2 = (rdivisions + 1) * (tdivisions - 1);
  for (r = 0; r <= rdivisions; r++, p1++, p2++) {
    if (has_n) glNormal3fv(normals[p1].pt);
#ifdef TEXTURE
    glTexCoord2fv(points[p1].pt);
#endif
    glVertex3fv(points[p1].pt);
    if (has_n) glNormal3fv(normals[p2].pt);
#ifdef TEXTURE
    glTexCoord2fv(points[p2].pt);
#endif
    glVertex3fv(points[p2].pt);
  }
  glEnd();
}

void Unitdisk::draw_colors_nonormals()
{
  int r, t, p1, p2;

  for (t = 1; t < tdivisions; t++) {
    glBegin(GL_QUAD_STRIP);
    p1 = t * (rdivisions + 1);
    p2 = (t - 1) * (rdivisions + 1);
    for (r = 0; r <= rdivisions; r++, p1++, p2++) {
      glColor4fv(colors[p1].c);
#ifdef TEXTURE
      glTexCoord2fv(points[p1].pt);
#endif
      glVertex3fv(points[p1].pt);
      glColor4fv(colors[p2].c);
#ifdef TEXTURE
      glTexCoord2fv(points[p2].pt);
#endif
      glVertex3fv(points[p2].pt);
    }
    glEnd();
  }

  glBegin(GL_QUAD_STRIP);
  p1 = 0;
  p2 = (rdivisions + 1) * (tdivisions - 1);
  for (r = 0; r <= rdivisions; r++, p1++, p2++) {
    glColor4fv(colors[p1].c);
#ifdef TEXTURE
    glTexCoord2fv(points[p1].pt);
#endif
    glVertex3fv(points[p1].pt);
    glColor4fv(colors[p2].c);
#ifdef TEXTURE
    glTexCoord2fv(points[p2].pt);
#endif
    glVertex3fv(points[p2].pt);
  }
  glEnd();
}

void Unitdisk::draw_colors_normals()
{
  int r, t, p1, p2;

  for (t = 1; t < tdivisions; t++) {
    glBegin(GL_QUAD_STRIP);
    p1 = t * (rdivisions + 1);
    p2 = (t - 1) * (rdivisions + 1);
    for (r = 0; r <= rdivisions; r++, p1++, p2++) {
      glColor4fv(colors[p1].c);
      glNormal3fv(normals[p1].pt);
#ifdef TEXTURE
      glTexCoord2fv(points[p1].pt);
#endif
      glVertex3fv(points[p1].pt);
      glColor4fv(colors[p2].c);
      glNormal3fv(normals[p2].pt);
#ifdef TEXTURE
      glTexCoord2fv(points[p2].pt);
#endif
      glVertex3fv(points[p2].pt);
    }
    glEnd();
  }

  glBegin(GL_QUAD_STRIP);
  p1 = 0;
  p2 = (rdivisions + 1) * (tdivisions - 1);
  for (r = 0; r <= rdivisions; r++, p1++, p2++) {
    glColor4fv(colors[p1].c);
    glNormal3fv(normals[p1].pt);
#ifdef TEXTURE
    glTexCoord2fv(points[p1].pt);
#endif
    glVertex3fv(points[p1].pt);
    glColor4fv(colors[p2].c);
    glNormal3fv(normals[p2].pt);
#ifdef TEXTURE
    glTexCoord2fv(points[p2].pt);
#endif
    glVertex3fv(points[p2].pt);
  }
  glEnd();
}

void Unitdisk::draw_by_perimeter(int pass_colors, int pass_norms,
 int pass_tex)
{
  int i, index, r = rdivisions + 1;

  if (points == NULL) return;
  if (pass_colors && colors == NULL) {
    //fprintf(stderr, "Warning:  No colors to draw in Unitdisk.c++");
    pass_colors = 0;
  }
  if (pass_norms && normals == NULL) {
    //fprintf(stderr, "Warning:  No normals to draw in Unitdisk.c++");
    pass_norms = 0;
  }
  glBegin(GL_POLYGON);
  for (i = 0, index = rdivisions; i < tdivisions; i++, index += r) {
    if (pass_colors) glColor4fv(colors[index].c);
    if (pass_norms) glNormal3fv(normals[index].pt);
#ifdef TEXTURE
    if (pass_tex) glTexCoord2fv(points[index].pt);
#endif
    glVertex3fv(points[index].pt);
  }
  glEnd();

}

void Unitdisk::set_angle(float new_angle)
{
  angle = new_angle;
}

GLfloat Unitdisk::get_angle()
{
  return angle;
}

GLfloat Unitdisk::get_radius()
{
  return (GLfloat)cos((double)((M_PI - angle) / 2.0));
}

void Unitdisk::set_divisions(int new_rdivisions, int new_tdivisions)
{
  if (tdivisions != new_tdivisions) {
    delete sintable;
    delete costable;
    sintable = costable = NULL;
  }
  if (tdivisions != new_tdivisions || rdivisions != new_rdivisions) {
    rdivisions = new_rdivisions;
    tdivisions = new_tdivisions;
    free_points();
    free_normals();
    free_colors();
  }
}

int Unitdisk::get_rdivisions()
{
  return rdivisions;
}

int Unitdisk::get_tdivisions()
{
  return tdivisions;
}

void Unitdisk::alloc_points()
{
  int npoints = get_npoints();
  if (npoints > points_size) {
    delete points;
    points = new Point[npoints];
    points_size = npoints;
  }
}

void Unitdisk::alloc_normals()
{
  int npoints = get_npoints();
  if (npoints > normals_size) {
    delete normals;
    normals = new Point[npoints];
    normals_size = npoints;
  }
}

void Unitdisk::alloc_points_normals()
{
  alloc_points();
  alloc_normals();
}

void Unitdisk::free_points()
{
  delete points;
  points = NULL;
  points_size = 0;
}

void Unitdisk::free_normals()
{
  delete normals;
  normals = NULL;
  normals_size = 0;
}

void Unitdisk::free_points_normals()
{
  free_points();
  free_normals();
}

void Unitdisk::fill_points()
{
  alloc_points();
  fill_either(points);
}

void Unitdisk::fill_normals()
{
  alloc_normals();
  fill_either(normals);
}

void Unitdisk::fill_points_normals()
{
  alloc_points();
  alloc_normals();
  fill_either(points);
  fill_either(normals);
}

void Unitdisk::copy_points(Unitdisk src)
{
  set_divisions(src.rdivisions, src.tdivisions);
  alloc_points();
  copy_either(points, src.points);
}

void Unitdisk::copy_normals(Unitdisk src)
{
  set_divisions(src.rdivisions, src.tdivisions);
  alloc_normals();
  copy_either(normals, src.normals);
}

void Unitdisk::copy_either(Point *dpt, Point *spt) {
  int i, npoints;
  npoints = get_npoints();
  for (i = 0; i < npoints; i++, dpt++, spt++) {
    dpt->pt[0] = spt->pt[0];
    dpt->pt[1] = spt->pt[1];
    dpt->pt[2] = spt->pt[2];
  }
}

void Unitdisk::copy_normals_from_points(Unitdisk src) {
  set_divisions(src.rdivisions, src.tdivisions);
  alloc_normals();
  copy_either(normals, src.points);
}

void Unitdisk::copy_normals_from_points() {
  copy_normals_from_points(*this);
}

void Unitdisk::fill_either(Point *what) {
  int t, r;
  int i;

  fill_either_strip1(what);
  if (sintable == NULL) fill_trig_tables();
  i = rdivisions + 1;
  for (t = 1; t < tdivisions; t++) {
    for (r = 0; r <= rdivisions; r++) {
      what[i].pt[0] = costable[t] * what[r].pt[0];
      what[i].pt[1] = sintable[t] * what[r].pt[0];
      what[i].pt[2] = what[r].pt[2];
      i++;
    }
  }
}

void Unitdisk::fill_points_strip1()
{
  alloc_points();
  fill_either_strip1(points);
}

void Unitdisk::fill_either_strip1(Point *what) {
  float radius, rinc;
  int r;

  rinc = get_radius() / (float)rdivisions;
  radius = 0.0;
  for (r = 0; r <= rdivisions; r++, radius += rinc) {
    what[r].pt[0] = radius;
    what[r].pt[1] = 0;
    // Round-off error avoidance hack
    what[r].pt[2] = (GLfloat)(1.0 - what[r].pt[0]*what[r].pt[0]);
    if (what[r].pt[2] > 0.0) what[r].pt[2] = (GLfloat)sqrt((double)what[r].pt[2]);
    else what[r].pt[2] = 0.0;
  }
}

void Unitdisk::translate(Point trans)
{
  int i, npoints;
  npoints = get_npoints();
  for (i = 0; i < npoints; i++) {
    points[i].pt[0] += trans.pt[0];
    points[i].pt[1] += trans.pt[1];
    points[i].pt[2] += trans.pt[2];
  }
}

void Unitdisk::scale(float s)
{
  int i, npoints;
  npoints = get_npoints();
  for (i = 0; i < npoints; i++) {
    points[i].pt[0] *= s;
    points[i].pt[1] *= s;
    points[i].pt[2] *= s;
  }
}

void Unitdisk::scale_translate(float s, Point trans)
{
  int i, npoints;
  npoints = get_npoints();
  for (i = 0; i < npoints; i++) {
    points[i].pt[0] = points[i].pt[0]*s + trans.pt[0];
    points[i].pt[1] = points[i].pt[1]*s + trans.pt[1];
    points[i].pt[2] = points[i].pt[2]*s + trans.pt[2];
  }
}

int Unitdisk::get_npoints()
{
  return (rdivisions + 1) * tdivisions;
}

void Unitdisk::project()
{
  int i, npoints;

  if (normals == NULL) {
    //fprintf(stderr, "Warning:  No normals defined when project() called.\n");
    fill_normals();
  }
  if (points == NULL) fill_points();
  npoints = get_npoints();
  for (i = 0; i < npoints; i++) {
    /* I'm not sure quite what the justification for this is, but it
     * seems to work */
    if (normals[i].pt[2] < 0.0) normals[i].pt[2] = -normals[i].pt[2];
    points[i] = points[i].project_direction(normals[i]);
  }
}

void Unitdisk::project(Point projpt)
{
  int i, npoints = get_npoints();
  float x, y, z;
  Point *pt;

  if (points == NULL) fill_points();
  x = projpt.pt[0];
  y = projpt.pt[1];
  z = projpt.pt[2];
  pt = points;
  for (i = 0; i < npoints; i++, pt++) pt->project_self(x, y, z);
}

void Unitdisk::project_borrow_points(Unitdisk src)
{
  int i, npoints = get_npoints();
  Point *pt, *spt, *sn;
  spt = src.points;
  sn = normals;
  alloc_points();
  pt = points;
  for (i = 0; i < npoints; i++, pt++, spt++, sn++) {
    /* I'm not sure quite what the justification for this is, but it
     * seems to work */
    if (normals[i].pt[2] < 0.0) normals[i].pt[2] = -normals[i].pt[2];
    pt->compute_projected(spt->pt[0], spt->pt[1], spt->pt[2],
  sn->pt[0], sn->pt[1], sn->pt[2]);
  }
}

void Unitdisk::refract_normals(Point light, GLfloat I)
{
  Point dlight;
  float cos1, sin1, cos2, sin2;
  int use_normals;
  int r, t, i;

  if (points == NULL) {
    //fprintf(stderr, "Attempting to refract without points.\n");
    fill_normals();
  }

  use_normals = (normals != NULL);
  alloc_normals();

  /* Do the theta = 0 diagonal */
  for (r = 0; r <= rdivisions; r++) {
    /* Find the original normal - use the unit of the points if there are
     * no normals */
    if (!use_normals) normals[r] = points[r].unit();

    /* Compute the direction to the light */
    dlight = (light - points[r]).unit();

    /* Compute the cosine and the sine of the original angle */
    cos1 = dlight.dot(normals[r]);
    sin1 = (float)(1.0 - cos1*cos1);
    if (sin1 <= 0.0) continue;
    sin1 = (float)sqrt((double)sin1);

    /* Compute the cosine and the sine of the new angle */
    sin2 = sin1 / I;
    cos2 = (float)sqrt((double)(1.0 - sin2*sin2));

    /* Rotate the normal by the new sine and cosine */
    normals[r] = normals[r].rotate_abouty(cos2, -sin2);
  }

  /* Copy the rest of the rows from the current row */
  i = rdivisions + 1;
  if (sintable == NULL) fill_trig_tables();
  for (t = 1; t < tdivisions; t++) {
    for (r = 0; r <= rdivisions; r++) {
      normals[i].pt[0] = costable[t] * normals[r].pt[0];
      normals[i].pt[1] = sintable[t] * normals[r].pt[0];
      normals[i].pt[2] = normals[r].pt[2];
      i++;
    }
  }

}

void Unitdisk::face_direction(Point d)
{
  face_direction(d, *this);
}

void Unitdisk::face_direction(Point d, Unitdisk src)
{
  Point *spt, *dpt, *sn, *dn;
  float sin1, cos1;
  float x;
  int npoints, i;

  if (d.pt[1]) {
    /*fprintf(stderr,
    "Internal error:  Can't face in direction not in xz plane.");*/
    return;
  }

  cos1 = d.pt[2];
  sin1 = d.pt[0];

  if (sin1 * sin1 > fudge) {
    spt = src.points;
    dpt = points;
    sn = src.normals;
    dn = normals;
    /* Change this to be seperate loops for points&&normals, points, normals
     * (faster than testing every iteration */
    npoints = get_npoints();
    for (i = 0; i < npoints; i++) {
      if (points != NULL) {
x = spt->pt[0];
dpt->pt[0] = x*cos1 + spt->pt[2]*sin1;
dpt->pt[1] = spt->pt[1];
dpt->pt[2] = -x*sin1 + spt->pt[2]*cos1;
spt++; dpt++;
      }
      if (normals != NULL) {
x = sn->pt[0];
dn->pt[0] = x*cos1 + sn->pt[2]*sin1;
dn->pt[1] = sn->pt[1];
dn->pt[2] = -x*sin1 + sn->pt[2]*cos1;
sn++; dn++;
      }
    }
  } else if (points != NULL && points != src.points) {
    copy_points(src);
    if (normals != NULL) copy_normals(src);
  }
}

void Unitdisk::alloc_colors()
{
  int ncolors = get_npoints();
  if (ncolors > colors_size) {
    delete colors;
    colors = new Color[ncolors];
    colors_size = ncolors;
  }
}

void Unitdisk::map_normals_to_colors()
{
  int t, r;
  int i;

  if (normals == NULL) fill_normals();
  alloc_colors();
  i = 0;
  for (t = 1; t <= tdivisions; t++) {
    for (r = 0; r <= rdivisions; r++) {
      colors[i] *= normals[r].pt[2];
      colors[i].c[3] = 1;
      i++;
    }
  }
}

void Unitdisk::map_z_to_colors()
{
  int i, npoints = get_npoints();

  if (points == NULL) {
    //fprintf(stderr, "Warning:  no points defined in map_z_to_colors()\n");
    fill_points();
  }

  alloc_colors();
  for (i = 0; i < npoints; i++) {
    colors[i] = points[i].pt[2];
    colors[i].c[3] = 1;
  }
}

void Unitdisk::scale_alpha_by_z()
{
  int i, npoints = get_npoints();

  if (colors == NULL) alloc_colors();
  if (points == NULL) {
    alloc_points();
    fill_points();
  }
  for (i = 0; i < npoints; i++) colors[i].c[3] *= points[i].pt[2];
}

void Unitdisk::scale_colors_by_z()
{
  int i, npoints = get_npoints();

  if (colors == NULL) alloc_colors();
  if (points == NULL) {
    alloc_points();
    fill_points();
  }
  for (i = 0; i < npoints; i++) colors[i] *= points[i].pt[2];
}

void Unitdisk::scale_colors_by_normals(Point light)
{
  scale_colors_by_normals(light, *this);
}

void Unitdisk::scale_colors_by_normals(Point light, Unitdisk src_normals)
{
  scale_colors_by_either(light, src_normals.normals);
}

void Unitdisk::scale_colors_by_points(Point light, Unitdisk src_points)
{
  scale_colors_by_either(light, src_points.points);
}

void Unitdisk::scale_colors_by_either(Point light, Point *what)
{
  int t, r;
  int i;
  if (what == NULL) {
    //fprintf(stderr, "Scaling colors to NULL pointer.\n");
    return;
  }
  /*if (light.pt[0] || light.pt[1] || light.pt[2] < 0.0) {
    fprintf(stderr, "Light not on z axis in scale_colors_by_normals.\n");
  }*/

  alloc_colors();
  for (r = 0; r <= rdivisions; r++)
    colors[r] *= what[r].dot(light);
  i = rdivisions + 1;
  for (t = 1; t < tdivisions; t++) {
    for (r = 0; r <= rdivisions; r++) {
      colors[i] = colors[r];
      i++;
    }
  }
}

void Unitdisk::set_colors(Color c)
{
  Color *dst;
  int i, npoints = get_npoints();
  alloc_colors();
  dst = colors;
  for (i = 0; i < npoints; i++, dst++) {
    dst->c[0] = c.c[0];
    dst->c[1] = c.c[1];
    dst->c[2] = c.c[2];
    dst->c[3] = c.c[3];
  }
}

void Unitdisk::add_colors(Color c)
{
  int i, npoints = get_npoints();
  alloc_colors();
  for (i = 0; i < npoints; i++) colors[i] += c;
}

void Unitdisk::free_colors()
{
  delete colors;
  colors = NULL;
  colors_size = 0;
}

inline float Unitdisk::area_triangle(Point a, Point b, Point c)
{
  return (float)(((a.pt[0]*b.pt[1] + b.pt[0]*c.pt[1] + c.pt[0]*a.pt[1]) -
   (a.pt[1]*b.pt[0] + b.pt[1]*c.pt[0] + c.pt[1]*a.pt[0])) * .5);
}

inline float Unitdisk::area_triangle(GLfloat *a, GLfloat *b,
     GLfloat *c)
{
  return (float)(((a[0]*b[1] + b[0]*c[1] + c[0]*a[1]) -
   (a[1]*b[0] + b[1]*c[0] + c[1]*a[0])) * .5);
}

inline float Unitdisk::area_2triangle(GLfloat *a, GLfloat *b,
     GLfloat *c)
{
  return ((a[0]*b[1] + b[0]*c[1] + c[0]*a[1]) -
  (a[1]*b[0] + b[1]*c[0] + c[1]*a[0]));
}

void Unitdisk::scale_colors_by_darea(Unitdisk disk)
{
  int pt1, pt2, pt3;
  int t, r, i;
  int npoints = get_npoints();
  float *rproducts1, *tproducts1, *rproducts2, *tproducts2;
  float area1, area2;

  rproducts1 = new float[npoints];
  tproducts1 = new float[npoints];
  rproducts2 = new float[npoints];
  tproducts2 = new float[npoints];

  /* Compute the products of the segments which make up the disk -
   * these will later be used in the area calculations */
  i = 0;
  for (t = 0; t < tdivisions; t++) {
    for (r = 0; r < rdivisions; r++) {
      pt1 = i;
      pt2 = i + 1;
      rproducts1[i] = (points[pt1].pt[0]*points[pt2].pt[1] -
       points[pt1].pt[1]*points[pt2].pt[0]);
      rproducts2[i] = (disk.points[pt1].pt[0]*disk.points[pt2].pt[1] -
       disk.points[pt1].pt[1]*disk.points[pt2].pt[0]);
      pt2 = ((t+1)%tdivisions)*(rdivisions + 1) + r;
      tproducts1[i] = (points[pt1].pt[0]*points[pt2].pt[1] -
       points[pt1].pt[1]*points[pt2].pt[0]);
      tproducts2[i] = (disk.points[pt1].pt[0]*disk.points[pt2].pt[1] -
       disk.points[pt1].pt[1]*disk.points[pt2].pt[0]);
      i++;
    }
    pt1 = i;
    pt2 = ((t+1)%tdivisions)*(rdivisions + 1) + r;
    tproducts1[i] = (points[pt1].pt[0]*points[pt2].pt[1] -
     points[pt1].pt[1]*points[pt2].pt[0]);
    tproducts2[i] = (disk.points[pt1].pt[0]*disk.points[pt2].pt[1] -
     disk.points[pt1].pt[1]*disk.points[pt2].pt[0]);
    i++;
  }

  /* Compute the area at the center of the disk */
  area1 = area2 = 0.0;
  r = 1;
  for (t = 0; t <= tdivisions; t++) {
    pt1 = (t%tdivisions)*(rdivisions+1) + r;
    area1 += tproducts1[pt1];
    area2 += tproducts2[pt1];
  }
  if (area1 != 0.0) area1 = (float)fabs((double)(area2 / area1));
  for (t = 0; t < tdivisions; t++) {
    colors[t*(rdivisions+1)] *= area1;
  }

  for (t = 0; t < tdivisions; t++) {
    for (r = 1; r < rdivisions; r++) {
      pt1 = (t ? t-1 : tdivisions-1)*(rdivisions+1) + r - 1;
      pt3 = t*(rdivisions + 1) + r - 1;
      pt2 = ((t+1) % tdivisions)*(rdivisions+1) + r - 1;
      area1 = rproducts1[pt1] + rproducts1[pt1 + 1];
      area1 += tproducts1[pt1 + 2] + tproducts1[pt3 + 2];
      area1 -= rproducts1[pt2 + 1] + rproducts1[pt2];
      area1 -= tproducts1[pt3] + tproducts1[pt1];
      area2 = rproducts2[pt1] + rproducts2[pt1 + 1];
      area2 += tproducts2[pt1 + 2] + tproducts2[pt3 + 2];
      area2 -= rproducts2[pt2 + 1] + rproducts2[pt2];
      area2 -= tproducts2[pt3] + tproducts2[pt1];
      if (area1 != 0.0) area1 = (float)fabs((double)(area2 / area1));
      colors[pt3 + 1] *= area1;
    }
  }

  /* Compute the area around the outside of the disk */
  r = rdivisions;
  for (t = 0; t < tdivisions; t++) {
    pt1 = (t ? t-1 : tdivisions-1)*(rdivisions+1) + r - 1;
    pt3 = t*(rdivisions + 1) + r - 1;
    pt2 = ((t+1) % tdivisions)*(rdivisions+1) + r - 1;
    area1 = rproducts1[pt1];
    area1 += tproducts1[pt1 + 1] + tproducts1[pt3 + 1];
    area1 -= rproducts1[pt2];
    area1 -= tproducts1[pt1] + tproducts1[pt3];
    area2 = rproducts2[pt1];
    area2 += tproducts2[pt1 + 1] + tproducts2[pt3 + 1];
    area2 -= rproducts2[pt2];
    area2 -= tproducts2[pt1] + tproducts2[pt3];
    if (area1 != 0.0) area1 = (float)fabs((double)(area2 / area1));
    colors[pt3 + 1] *= area1;
  }

  delete rproducts1;
  delete tproducts1;
  delete rproducts2;
  delete tproducts2;
}

void Unitdisk::fill_trig_tables()
{
  int t;

  delete sintable;
  delete costable;
  sintable = new float[tdivisions];
  costable = new float[tdivisions];
  for (t = 0; t < tdivisions; t++) {
    costable[t] = (float)cos((double)(M_2PI * (float)t / (float)tdivisions));
    sintable[t] = (float)sin((double)(M_2PI * (float)t / (float)tdivisions));
  }
}


GENGL.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include "gengl.h"

CHAR szAppName[]="OpenGLGeneric";

HGLRC hRC;

extern BOOL draw_cone;
extern BOOL draw_pyramid;
extern HPALETTE ghpalOld, ghPalette;

/* forward declarations of helper functions in this module */
HWND   WINAPI InitializeWindow (HINSTANCE, int);
LONG   WINAPI CommandHandler (HWND, WPARAM, LPARAM);
LONG   WINAPI MainWndProc (HWND, UINT, WPARAM, LPARAM);

/* entry point of this executable */
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG        msg;
    HWND       hWnd;

    /* previous instances do not exist in Win32 */
    if (hPrevInstance)
        return 0;

    if (!(hWnd = InitializeWindow (hInstance, nCmdShow)))
        return FALSE;

    /* main window message loop */
    while (GetMessage (&msg, NULL, 0, 0))
    {
      TranslateMessage (&msg);
      DispatchMessage (&msg);
    }

    /* return success of application */
    return TRUE;
}

HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow)
    {
    WNDCLASS   wc;
    HWND       hWnd;

    /* Register the frame class */
    wc.style         = 0;
    wc.lpfnWndProc   = (WNDPROC)MainWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon (hInstance, szAppName);
    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName  = szAppName;
    wc.lpszClassName = szAppName;

    if (!RegisterClass (&wc) )
        return FALSE;

    /* Create the frame */
    hWnd = CreateWindow (szAppName,
             "Generic OpenGL Sample",
     WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
             CW_USEDEFAULT,
             CW_USEDEFAULT,
             CW_USEDEFAULT,
             CW_USEDEFAULT,
             NULL,
             NULL,
             hInstance,
             NULL);

    /* make sure window was created */
    if (!hWnd)
        return FALSE;

    SendMessage(hWnd, WM_INIT, 0, 0L);

    /* show and update main window */
    ShowWindow (hWnd, nCmdShow);

    UpdateWindow (hWnd);

    return hWnd;
}

/* main window procedure */
LONG WINAPI MainWndProc (
    HWND    hWnd,
    UINT    uMsg,
    WPARAM  wParam,
    LPARAM  lParam)
{
    LONG    lRet = 1;

    switch (uMsg)
    {
    case WM_CREATE:
        {
    HDC  hDC;

            hDC = GetDC(hWnd);
    bSetupPixelFormat(hDC);

            hRC = wglCreateContext( hDC );
            wglMakeCurrent( hDC, hRC );
}
break;

    case WM_PAINT:
        {
    HDChDC;
    PAINTSTRUCTps;

    hDC = BeginPaint(hWnd, &ps);

    draw_scene(hWnd);

    EndPaint(hWnd, &ps);
        }
break;

    case WM_INIT:
        initialize(hWnd);
        break;

    case WM_SIZE:
        resize(hWnd);
            break;

        // The WM_QUERYNEWPALETTE message informs a window that it is about to
        // receive input focus. In response, the window receiving focus should
        // realize its palette as a foreground palette and update its client
        // area. If the window realizes its palette, it should return TRUE;
        // otherwise, it should return FALSE.

        case WM_QUERYNEWPALETTE:
        {
            HDC     hDC;

            if(ghPalette)
            {
                hDC = GetDC(hWnd);

                // Select and realize the palette

                ghpalOld = SelectPalette(hDC, ghPalette, FALSE);
                RealizePalette(hDC);

                // Redraw the client area

                InvalidateRect(hWnd, NULL, TRUE);
                UpdateWindow(hWnd);

                if(ghpalOld)
                    SelectPalette(hDC, ghpalOld, FALSE);

                ReleaseDC(hWnd, hDC);

                return TRUE;
            }

            return FALSE;
        }

        // The WM_PALETTECHANGED message informs all windows that the window
        // with input focus has realized its logical palette, thereby changing
        // the system palette. This message allows a window without input focus
        // that uses a color palette to realize its logical palettes and update
        // its client area.
        //
        // This message is sent to all windows, including the one that changed
        // the system palette and caused this message to be sent. The wParam of
        // this message contains the handle of the window that caused the system
        // palette to change. To avoid an infinite loop, care must be taken to
        // check that the wParam of this message does not match the window's
        // handle.

        case WM_PALETTECHANGED:
        {
            HDC         hDC;

            // Before processing this message, make sure we
            // are indeed using a palette

            if (ghPalette)
            {
                // If this application did not change the palette, select
                // and realize this application's palette

                if (wParam != (WPARAM)hWnd)
                {
                    // Need the window's DC for SelectPalette/RealizePalette

                    hDC = GetDC(hWnd);

                    // Select and realize our palette

                    ghpalOld = SelectPalette(hDC, ghPalette, FALSE);
                    RealizePalette(hDC);

                    // WHen updating the colors for an inactive window,
                    // UpdateColors can be called because it is faster than
                    // redrawing the client area (even though the results are
                    // not as good)

                    UpdateColors(hDC);

                    // Clean up

                    if (ghpalOld)
                       SelectPalette(hDC, ghpalOld, FALSE);

                    ReleaseDC(hWnd, hDC);
                }
            }
            break;
        }

    case WM_COMMAND:
            lRet = CommandHandler (hWnd, wParam, lParam);
            break;

       case WM_CLOSE:
        {
        HGLRC hRC;
        HDC  hDC;

                /* release and free the device context and rendering context */
        hRC = wglGetCurrentContext();
        hDC = wglGetCurrentDC();

        wglMakeCurrent(NULL, NULL);

        if (hRC)
        wglDeleteContext(hRC);
        if (hDC)
            ReleaseDC(hWnd, hDC);

                /* call destroy window to cleanup and go away */
                DestroyWindow (hWnd);
        }
        break;

       case WM_DESTROY:
        {
        HGLRC hRC;
        HDC  hDC;

                /* release and free the device context and rendering context */
        hRC = wglGetCurrentContext();
        hDC = wglGetCurrentDC();

        wglMakeCurrent(NULL, NULL);

        if (hRC)
        wglDeleteContext(hRC);
        if (hDC)
            ReleaseDC(hWnd, hDC);

                PostQuitMessage (0);
        }
        break;

    default:
            /* pass all unhandled messages to DefWindowProc */
            lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
        break;
    }

    /* return 1 if handled message, 0 if not */
    return lRet;
}

/* handle all WM_COMMAND messages here */
LONG WINAPI CommandHandler (
    HWND    hWnd,
    WPARAM  wParam,
    LPARAM  lParam)
{
    HMENU   hMenu;

    switch (LOWORD(wParam))
    {
    case IDM_EXIT:
            /* exit application */
            PostMessage (hWnd, WM_CLOSE, 0, 0L);
        break;

    case IDM_CONE:
    hMenu = GetMenu(hWnd);
    draw_cone = !draw_cone;
    CheckMenuItem(hMenu,IDM_CONE,(draw_cone?MF_CHECKED:MF_UNCHECKED));
    PostMessage (hWnd, WM_PAINT, 0, 0L);
break;

    case IDM_PYRAMID:
    hMenu = GetMenu(hWnd);
    draw_pyramid = !draw_pyramid;
    CheckMenuItem(hMenu,IDM_PYRAMID,(draw_pyramid?MF_CHECKED:MF_UNCHECKED));
    PostMessage (hWnd, WM_PAINT, 0, 0L);
break;

    default:
            return FALSE;
    }
    return TRUE;
}


GENGL.H

#include "glos.h"
#include <gl.h>
#include <glu.h>
#include "genglrc.h"

#define WM_INITWM_USER

GLvoid initialize(HWND);
GLvoid draw_scene(HWND);
GLvoid resize(HWND);
BOOL bSetupPixelFormat(HDC);


GENGLRC.H

#define IDM_EXIT100
#define IDM_CONE200
#define IDM_PYRAMID201


RENDER.C

#include "gengl.h"

HPALETTE ghpalOld, ghPalette = (HPALETTE) 0;

BOOL draw_cone = FALSE;
BOOL draw_pyramid = FALSE;

GLvoid drawCone(GLvoid);
GLvoid drawPyramid(GLvoid);

GLfloat radius;
RECToldrect;

unsigned char threeto8[8] = {
    0, 0111>>1, 0222>>1, 0333>>1, 0444>>1, 0555>>1, 0666>>1, 0377
};

unsigned char twoto8[4] = {
    0, 0x55, 0xaa, 0xff
};

unsigned char oneto8[2] = {
    0, 255
};

static int defaultOverride[13] = {
    0, 3, 24, 27, 64, 67, 88, 173, 181, 236, 247, 164, 91
};

static PALETTEENTRY defaultPalEntry[20] = {
    { 0,   0,   0,    0 },
    { 0x80,0,   0,    0 },
    { 0,   0x80,0,    0 },
    { 0x80,0x80,0,    0 },
    { 0,   0,   0x80, 0 },
    { 0x80,0,   0x80, 0 },
    { 0,   0x80,0x80, 0 },
    { 0xC0,0xC0,0xC0, 0 },

    { 192, 220, 192,  0 },
    { 166, 202, 240,  0 },
    { 255, 251, 240,  0 },
    { 160, 160, 164,  0 },

    { 0x80,0x80,0x80, 0 },
    { 0xFF,0,   0,    0 },
    { 0,   0xFF,0,    0 },
    { 0xFF,0xFF,0,    0 },
    { 0,   0,   0xFF, 0 },
    { 0xFF,0,   0xFF, 0 },
    { 0,   0xFF,0xFF, 0 },
    { 0xFF,0xFF,0xFF, 0 }
};

unsigned char
ComponentFromIndex(int i, UINT nbits, UINT shift)
{
    unsigned char val;

    val = (unsigned char) (i >> shift);
    switch (nbits) {

    case 1:
        val &= 0x1;
        return oneto8[val];

    case 2:
        val &= 0x3;
        return twoto8[val];

    case 3:
        val &= 0x7;
        return threeto8[val];

    default:
        return 0;
    }
}

void
CreateRGBPalette(HDC hDC)
{
    PIXELFORMATDESCRIPTOR pfd;
    LOGPALETTE *pPal;
    int n, i;

    n = GetPixelFormat(hDC);
    DescribePixelFormat(hDC, n, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

    if (pfd.dwFlags & PFD_NEED_PALETTE) {
        n = 1 << pfd.cColorBits;
        pPal = (PLOGPALETTE)LocalAlloc(LMEM_FIXED, sizeof(LOGPALETTE) +
                n * sizeof(PALETTEENTRY));
        pPal->palVersion = 0x300;
        pPal->palNumEntries = n;
        for (i=0; i<n; i++) {
            pPal->palPalEntry[i].peRed =
                    ComponentFromIndex(i, pfd.cRedBits, pfd.cRedShift);
            pPal->palPalEntry[i].peGreen =
                    ComponentFromIndex(i, pfd.cGreenBits, pfd.cGreenShift);
            pPal->palPalEntry[i].peBlue =
                    ComponentFromIndex(i, pfd.cBlueBits, pfd.cBlueShift);
            pPal->palPalEntry[i].peFlags = 0;
        }

        /* fix up the palette to include the default GDI palette */
        if ((pfd.cColorBits == 8)                           &&
            (pfd.cRedBits   == 3) && (pfd.cRedShift   == 0) &&
            (pfd.cGreenBits == 3) && (pfd.cGreenShift == 3) &&
            (pfd.cBlueBits  == 2) && (pfd.cBlueShift  == 6)
           ) {
            for (i = 1 ; i <= 12 ; i++)
                pPal->palPalEntry[defaultOverride[i]] = defaultPalEntry[i];
        }

        ghPalette = CreatePalette(pPal);
        LocalFree(pPal);

        ghpalOld = SelectPalette(hDC, ghPalette, FALSE);
        n = RealizePalette(hDC);
    }
}

BOOL bSetupPixelFormat(HDC hDC)
{
    static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),// size of this pfd
1,// version number
PFD_DRAW_TO_WINDOW |// support window
  PFD_SUPPORT_OPENGL |// support OpenGL
  PFD_DOUBLEBUFFER,// double buffered
PFD_TYPE_RGBA,// RGBA type
24,// 24-bit color depth
0, 0, 0, 0, 0, 0,// color bits ignored
0,// no alpha buffer
0,// shift bit ignored
0,// no accumulation buffer
0, 0, 0, 0, // accum bits ignored
32,// 32-bit z-buffer
0,// no stencil buffer
0,// no auxiliary buffer
PFD_MAIN_PLANE,// main layer
0,// reserved
0, 0, 0// layer masks ignored
    };
    int pixelformat;

    if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
    {
        MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK);
        return FALSE;
    }

    if (SetPixelFormat(hDC, pixelformat, &pfd) == FALSE)
    {
        MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK);
        return FALSE;
    }

    CreateRGBPalette(hDC);

    return TRUE;
}

GLvoid initialize(HWND hWnd)
{
    GLfloatmaxObjectSize, aspect;
    GLdoublenear_plane, far_plane;

    GetClientRect(hWnd, &oldrect);

    glClearColor( 0.0, 0.0, 0.0, 1.0 );
    glClearDepth( 1.0 );

    glEnable(GL_DEPTH_TEST);

    glMatrixMode( GL_PROJECTION );
    aspect = (GLfloat) oldrect.right / oldrect.bottom;;
    gluPerspective( 45.0, aspect, 3.0, 7.0 );
    glMatrixMode( GL_MODELVIEW );

    near_plane = 3.0;
    far_plane = 7.0;
    maxObjectSize = 3.0;
    radius = near_plane + maxObjectSize/2.0;
}

GLvoid draw_scene(HWND hWnd)
{
    HDC hDC;

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glPushMatrix();
    glTranslatef(0.0, 0.0, -radius);

if(draw_cone)
    drawCone();

if(draw_pyramid)
    drawPyramid();

    glPopMatrix();
    glFinish();

    hDC = wglGetCurrentDC();
    SwapBuffers(hDC);
}

GLvoid resize(HWND hWnd)
{
    RECTrect;

    GetClientRect(hWnd, &rect);

    glViewport(0, 0, rect.right, rect.bottom);

    if((oldrect.right > rect.right) || (oldrect.bottom > rect.bottom))
PostMessage (hWnd, WM_PAINT, 0, 0L);

    oldrect.right = rect.right;
    oldrect.bottom = rect.bottom;
}

GLvoid drawCone(GLvoid)
{
    GLUquadricObj *quadObj;

    glColor3f(0.0, 1.0, 0.0);

    glPushMatrix();
glTranslatef(-1.0, 0.0, 0.0);
        quadObj = gluNewQuadric();
        gluQuadricDrawStyle(quadObj, GLU_FILL);
        gluQuadricNormals(quadObj, GLU_SMOOTH);
        gluCylinder(quadObj, 1.0, 0.0, 1.0, 20, 10);
        gluDeleteQuadric(quadObj);
    glPopMatrix();

}

GLvoid drawPyramid(GLvoid)
{

    glPushMatrix();
glTranslatef(1.0, 0.0, 0.0);
glBegin(GL_TRIANGLE_FAN);
    glColor3f(1.0, 0.0, 0.0);
    glVertex3f(0.0, 1.0, 0.0);
    glColor3f(0.0, 1.0, 0.0);
    glVertex3f(-1.0, 0.0, 0.0);
    glColor3f(0.0, 0.0, 1.0);
    glVertex3f(0.0, 0.0, 1.0);
    glColor3f(0.0, 1.0, 0.0);
    glVertex3f(1.0, 0.0, 0.0);
glEnd();
    glPopMatrix();
}


README.TXT

Using Polygon Offsets for Efficient Object Translation

The PGONOFFS sample demonstrates the use of the polygon-offset call available
in OpenGL 1.1. Using this call, the depth values of the fragments generated by
the rasterization of the polygon may be shifted away or towards the origin,
as an affine function of the window coordinate depth slope of the polygon.

In this sample, a quadric (cylinder, sphere or disk) is drawn normally and
with a polygon-offset value. A trackball is used to view the object from
various angles, by dragging it with a mouse. Additionally, the following
key inputs are recognized:

 Key        Description
 ---        -----------
 left/right Decrease/increase the units in the glPolygonOffset() call.
 up/down    Increase/decrease the factors in the glPolygonOffset() call.
 a/A        Decrease/increase the number of stacks in the quadric.
 c          Toggle on/off culling.
 f          Cycle through the various quadrics.
 g/G        Decrease/increase the radius of the quadric.
 h/H        Decrease/increase the height of the quadric.
 i          Set initial values for units and factors.
 t          Toggle on/off texturing.
 z/Z        Translate the object in Z.
 1/2/3      Enable GL_POLYGON_OFFSET_FILL/GL_POLYGON_OFFSET_LINE/
            GL_POLYGON_OFFSET_POINT.
 0          Toggle on/off smooth shading.

Note
----

PGONOFFS will not run on an implementation of OpenGL version 1.0. The OpenGL
for Windows 95 redistributables included on the Win32 SDK are an
implementation of OpenGL version 1.0.


GLOS.H

// GLOS.H
//
// This is an OS specific header file

#include <windows.h>

// disable data conversion warnings

#pragma warning(disable : 4305)
#pragma warning(disable : 4244)     // MIPS
#pragma warning(disable : 4136)     // X86
#pragma warning(disable : 4309)     // X86
#pragma warning(disable : 4051)     // ALPHA


TOFFSET.C

/******************************Module*Header*******************************\
* Module Name: toffset.c
*
* Program demonstrating the Polygon Offset call.
*
* Draws a quadric, first normally and then with polygon offset.
*
* Copyright 1994 - 1998 Microsoft Corporation
*
\**************************************************************************/

/*
 * (c) Copyright 1993, Silicon Graphics, Inc.
 *               1993, 1994 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <gl\glaux.h>
#include "trackbal.h"

#define PI 3.141592654
#define BLACK 0
#define GRAY 128
#define WHITE 255
#define RD 0xA40000FF
#define WT 0xFFFFFFFF
#define brickImageWidth 16
#define brickImageHeight 16

//static void CALLBACK ErrorHandler(unsigned long which);
static void Init(void );

static void CALLBACK Key_Left(void );
static void CALLBACK Key_Right(void );
static void CALLBACK Key_Up(void );
static void CALLBACK Key_Down(void );
static void CALLBACK Key_a(void );
static void CALLBACK Key_A(void );
static void CALLBACK Key_b(void );
static void CALLBACK Key_c(void );
static void CALLBACK Key_f(void );
static void CALLBACK Key_g(void );
static void CALLBACK Key_G(void );
static void CALLBACK Key_h(void );
static void CALLBACK Key_H(void );
static void CALLBACK Key_i(void );
static void CALLBACK Key_j(void );
static void CALLBACK Key_J(void );
static void CALLBACK Key_l(void );
static void CALLBACK Key_L(void );
static void CALLBACK Key_k(void );
static void CALLBACK Key_K(void );
static void CALLBACK Key_o(void );
static void CALLBACK Key_p(void );
static void CALLBACK Key_s(void );
static void CALLBACK Key_S(void );
static void CALLBACK Key_t(void );
static void CALLBACK Key_z(void );
static void CALLBACK Key_Z(void );
static void CALLBACK Key_0(void );
static void CALLBACK Key_1(void );
static void CALLBACK Key_2(void );
static void CALLBACK Key_3(void );

static void CALLBACK Reshape(int width,int height);
static void CALLBACK Draw( void );
static unsigned long Args(int argc,char **argv );

GLenum rgb, doubleBuffer;

GLint wWidth = 300, wHeight = 300;

GLenum doDither = GL_TRUE;
GLenum shade = GL_TRUE;
GLenum texture = GL_TRUE;

BOOL bPolyOffset = TRUE;

float factor=-1.0f, units=0.0f;

float inc = 1.0f;
int polygonMode = GL_LINE;
GLenum polyFace = GL_FRONT;
float zTrans = -2.7;
BOOL bCullFace = FALSE;
BOOL bHiddenLine;

GLint radius1, radius2;
GLdouble angle1, angle2;
GLint slices, stacks;
GLint height;
GLint whichQuadric;
GLUquadricObj *quadObj;

GLubyte brickImage[brickImageWidth*brickImageHeight] = {
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT,
    RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD
};
char *texFileName = 0;

static void UpdateInfo()
{
    HWND hwnd = auxGetHWND();
    char buf[100];

    sprintf( buf, "Factor = %4.1f, Units = %4.1f", factor, units );
    SendMessage( hwnd, WM_SETTEXT, 0, (LPARAM)buf );
}

static void SetMaterial( bBlack )
{
    static float front_mat_shininess[] = {30.0};
    static float front_mat_specular[] = {0.2, 0.2, 0.2, 1.0};
    static float front_mat_diffuse[] = {0.5, 0.28, 0.38, 1.0};
    static float back_mat_shininess[] = {50.0};
    static float back_mat_specular[] = {0.5, 0.5, 0.2, 1.0};
    static float back_mat_diffuse[] = {1.0, 1.0, 0.2, 1.0};
    static float black_mat_shininess[] = {0.0};
    static float black_mat_specular[] = {0.0, 0.0, 0.0, 0.0};
    static float black_mat_diffuse[] = {0.0, 0.0, 0.0, 0.0};
    static float ambient[] = {0.1, 0.1, 0.1, 1.0};
    static float no_ambient[] = {0.0, 0.0, 0.0, 0.0};
    static float lmodel_ambient[] = {1.0, 1.0, 1.0, 1.0};
    static float lmodel_no_ambient[] = {0.0, 0.0, 0.0, 0.0};

    if( !bBlack ) {
        glMaterialfv(GL_FRONT, GL_SHININESS, front_mat_shininess);
        glMaterialfv(GL_FRONT, GL_SPECULAR, front_mat_specular);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, front_mat_diffuse);
        glMaterialfv(GL_BACK, GL_SHININESS, back_mat_shininess);
        glMaterialfv(GL_BACK, GL_SPECULAR, back_mat_specular);
        glMaterialfv(GL_BACK, GL_DIFFUSE, back_mat_diffuse);
        glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    } else {
        glMaterialfv(GL_FRONT, GL_SHININESS, black_mat_shininess);
        glMaterialfv(GL_FRONT, GL_SPECULAR, black_mat_specular);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, black_mat_diffuse);
        glMaterialfv(GL_BACK, GL_SHININESS, black_mat_shininess);
        glMaterialfv(GL_BACK, GL_SPECULAR, black_mat_specular);
        glMaterialfv(GL_BACK, GL_DIFFUSE, black_mat_diffuse);
        glLightfv(GL_LIGHT0, GL_AMBIENT, no_ambient);
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_no_ambient);
    }
}

static void Init(void)
{
    static GLint colorIndexes[3] = {0, 200, 255};
    static float ambient[] = {0.1, 0.1, 0.1, 1.0};
    static float diffuse[] = {0.5, 1.0, 1.0, 1.0};
    static float position[] = {90.0, 90.0, 150.0, 0.0};
    static float lmodel_ambient[] = {1.0, 1.0, 1.0, 1.0};
    static float lmodel_twoside[] = {GL_TRUE};
    static float decal[] = {GL_DECAL};
    static float modulate[] = {GL_MODULATE};
    static float repeat[] = {GL_REPEAT};
    static float nearest[] = {GL_NEAREST};
    AUX_RGBImageRec *image;

    if (!rgb) {
        auxSetGreyRamp();
    }
    glClearColor(0.0, 0.0, 0.0, 0.0);

    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    bHiddenLine = FALSE;
    SetMaterial( bHiddenLine );

    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, decal);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, nearest);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, nearest);
    if (texFileName) {
        image = auxRGBImageLoad(texFileName);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image->sizeX, image->sizeY,
              GL_RGB, GL_UNSIGNED_BYTE, image->data);
    } else {
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glTexImage2D(GL_TEXTURE_2D, 0, 4, brickImageWidth, brickImageHeight,
             0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)brickImage);
    }

    quadObj = gluNewQuadric();

    radius1 = 10;
    radius2 = 5;
    angle1 = 90;
    angle2 = 180;
    slices = 16;
    stacks = 10;
    height = 20;

    glCullFace( GL_BACK );

    UpdateInfo();
}

static void SetDistance( void )
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-1, 1, -1, 1, 1, 10);
    // This defines how far away we're looking from
    glTranslated( 0, 0, zTrans );
}

static void CALLBACK Reshape(int width, int height)
{
    trackball_Resize( width, height );

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-1, 1, -1, 1, 1, 10);
    // This defines how far away we're looking from
    glTranslated( 0, 0, zTrans );
}

static void CALLBACK Key_Left (void)
{
    units -= inc;
}

static void CALLBACK Key_Right (void)
{
    units += inc;
}

static void CALLBACK Key_Up (void)
{
    factor += inc;
}

static void CALLBACK Key_Down (void)
{
    factor -= inc;
}

static void CALLBACK Key_a (void)
{
    if (stacks > 1)
        stacks--;
}

static void CALLBACK Key_A (void)
{
    stacks++;
}

static void CALLBACK Key_b (void)
{
    bHiddenLine = !bHiddenLine;
    SetMaterial( bHiddenLine );
}

static void CALLBACK Key_c (void)
{
    bCullFace = !bCullFace;
    if( bCullFace )
        glEnable( GL_CULL_FACE );
    else
        glDisable( GL_CULL_FACE );
}

static void CALLBACK Key_f (void)
{
whichQuadric = whichQuadric >= 3 ? 0 : whichQuadric + 1;
}

static void CALLBACK Key_g (void)
{
    if (radius1 > 0)
        radius1 -= 1;
}

static void CALLBACK Key_G (void)
{
    radius1 += 1;
}

static void CALLBACK Key_h (void)
{
    if (height > 0)
        height -= 2;
}

static void CALLBACK Key_H (void)
{
    height += 2;
}

static void CALLBACK Key_i (void)
{
    factor = -1.0f;
    units = 0.0f;
}

static void CALLBACK Key_j (void)
{
    if (radius2 > 0)
        radius2 -= 1;
}

static void CALLBACK Key_J (void)
{
    radius2 += 1;
}

static void CALLBACK Key_k (void)
{
angle1 -= 5;
}

static void CALLBACK Key_K (void)
{
angle1 += 5;
}

static void CALLBACK Key_l (void)
{
angle2 -= 5;
}

static void CALLBACK Key_L (void)
{
angle2 += 5;
}

static void CALLBACK Key_o (void)
{
    bPolyOffset = !bPolyOffset;
}

static void CALLBACK Key_p (void)
{
switch (polyFace) {
  case GL_BACK:
    polyFace = GL_FRONT;
    break;
  case GL_FRONT:
    polyFace = GL_FRONT_AND_BACK;
    break;
  case GL_FRONT_AND_BACK:
    polyFace = GL_BACK;
    break;
}
}

static void CALLBACK Key_s (void)
{
    if (slices > 3)
        slices--;
}

static void CALLBACK Key_S (void)
{
    slices++;
}

static void CALLBACK Key_t (void)
{
    texture = !texture;
    if (texture) {
        gluQuadricTexture(quadObj, GL_TRUE);
        glEnable(GL_TEXTURE_2D);
    } else {
        gluQuadricTexture(quadObj, GL_FALSE);
        glDisable(GL_TEXTURE_2D);
    }
}

static void CALLBACK Key_z (void)
{
    zTrans += 0.1f;
    SetDistance();
}

static void CALLBACK Key_Z (void)
{
    zTrans -= 0.1f;
    SetDistance();
}

static void CALLBACK Key_0 (void)
{
    shade = !shade;
    if (shade) {
        glShadeModel(GL_SMOOTH);
        gluQuadricNormals(quadObj, GLU_SMOOTH);
    } else {
        glShadeModel(GL_FLAT);
        gluQuadricNormals(quadObj, GLU_FLAT);
    }
}

static void CALLBACK Key_1 (void)
{
    polygonMode = GL_FILL;
}

static void CALLBACK Key_2 (void)
{
    polygonMode = GL_LINE;
}

static void CALLBACK Key_3 (void)
{
    polygonMode = GL_POINT;
}

static void DrawObject( void )
{
    switch (whichQuadric) {
      case 0:
gluCylinder(quadObj, radius1/10.0, radius2/10.0, height/10.0,
    slices, stacks);
break;
      case 1:
gluSphere(quadObj, radius1/10.0, slices, stacks);
break;
      case 2:
gluPartialDisk(quadObj, radius2/10.0, radius1/10.0, slices,
       stacks, angle1, angle2);
break;
      case 3:
gluDisk(quadObj, radius2/10.0, radius1/10.0, slices, stacks);
break;
    }
}

static void CALLBACK Draw( void )
{
    float matRot[4][4];

    UpdateInfo();
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    trackball_CalcRotMatrix( matRot );
    glMultMatrixf( &(matRot[0][0]) );

    if( whichQuadric == 0 ) // cylinder
glTranslatef(0, 0, -height/20.0);

    // Draw object normally

    DrawObject();

    // Draw object again with polygon offset

    // Set polygon mode for offset faces
    glPolygonMode( GL_FRONT_AND_BACK, polygonMode );

    if( bPolyOffset ) {
        switch( polygonMode ) {
            case GL_FILL:
                glEnable( GL_POLYGON_OFFSET_FILL );
                break;
            case GL_LINE:
                glEnable( GL_POLYGON_OFFSET_LINE );
                break;
            case GL_POINT:
                glEnable( GL_POLYGON_OFFSET_POINT );
                break;
        }
    }

    glPolygonOffset( factor, units );
    glColor3f(1.0, 0.0, 0.0);
    glDisable( GL_LIGHTING );
    DrawObject();
    glEnable( GL_LIGHTING );
    // restore modes
    glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

    if( bPolyOffset ) {
        glDisable( GL_POLYGON_OFFSET_FILL );
        glDisable( GL_POLYGON_OFFSET_LINE );
        glDisable( GL_POLYGON_OFFSET_POINT );
    }

    glFlush();

    if (doubleBuffer) {
auxSwapBuffers();
    }
}

static unsigned long Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_TRUE;

    for (i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-ci") == 0) {
            rgb = GL_FALSE;
        } else if (strcmp(argv[i], "-rgb") == 0) {
            rgb = GL_TRUE;
        } else if (strcmp(argv[i], "-sb") == 0) {
            doubleBuffer = GL_FALSE;
        } else if (strcmp(argv[i], "-db") == 0) {
            doubleBuffer = GL_TRUE;
        } else if (strcmp(argv[i], "-f") == 0) {
            if (i+1 >= argc || argv[i+1][0] == '-') {
                //printf("-f (No file name).\n");
                return GL_FALSE;
            } else {
                texFileName = argv[++i];
            }
        } else {
            //printf("%s (Bad option).\n", argv[i]);
            return GL_FALSE;
        }
    }
    return GL_TRUE;
}

void  _CRTAPI1 main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
       auxQuit();
    }

    auxInitPosition(0, 0, wWidth, wHeight);

    type = AUX_DEPTH16;
    type |= (rgb) ? AUX_RGB : AUX_INDEX;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Polygon Offset Demo") == GL_FALSE) {
        auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);

    auxKeyFunc( AUX_LEFT, Key_Left );
    auxKeyFunc( AUX_RIGHT, Key_Right );
    auxKeyFunc( AUX_UP, Key_Up );
    auxKeyFunc( AUX_DOWN, Key_Down );
    auxKeyFunc( AUX_a, Key_a );
    auxKeyFunc( AUX_A, Key_A );
    auxKeyFunc( AUX_b, Key_b );
    auxKeyFunc( AUX_c, Key_c );
    auxKeyFunc( AUX_f, Key_f );
    auxKeyFunc( AUX_g, Key_g );
    auxKeyFunc( AUX_G, Key_G );
    auxKeyFunc( AUX_h, Key_h );
    auxKeyFunc( AUX_H, Key_H );
    auxKeyFunc( AUX_i, Key_i );
    auxKeyFunc( AUX_j, Key_j );
    auxKeyFunc( AUX_J, Key_J );
    auxKeyFunc( AUX_k, Key_k );
    auxKeyFunc( AUX_K, Key_K );
    auxKeyFunc( AUX_l, Key_l );
    auxKeyFunc( AUX_L, Key_L );
    auxKeyFunc( AUX_o, Key_o );
    auxKeyFunc( AUX_p, Key_p );
    auxKeyFunc( AUX_s, Key_s );
    auxKeyFunc( AUX_S, Key_S );
    auxKeyFunc( AUX_t, Key_t );
    auxKeyFunc( AUX_z, Key_z );
    auxKeyFunc( AUX_Z, Key_Z );
    auxKeyFunc( AUX_0, Key_0 );
    auxKeyFunc( AUX_1, Key_1 );
    auxKeyFunc( AUX_2, Key_2 );
    auxKeyFunc( AUX_3, Key_3 );

    auxMouseFunc( AUX_LEFTBUTTON, AUX_MOUSEDOWN, trackball_MouseDown );
    auxMouseFunc( AUX_LEFTBUTTON, AUX_MOUSEUP, trackball_MouseUp );

    trackball_Init( wWidth, wHeight );

    auxIdleFunc( Draw );
    auxMainLoop( Draw );
}


TRACKBAL.C

/*
 * Trackball code:
 *
 * Implementation of a virtual trackball.
 * Implemented by Gavin Bell, lots of ideas from Thant Tessman and
 *   the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129.
 *
 * Vector manip code:
 *
 * Original code from:
 * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli
 *
 * Much mucking with by:
 * Gavin Bell
 *
 * Shell hacking courtesy of:
 * Reptilian Inhaleware
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include "trackbal.h"

/*
 * globals
 */
static GLenum (*MouseDownFunc)(int, int, GLenum) = NULL;
static GLenum (*MouseUpFunc)(int, int, GLenum)   = NULL;
static HWND ghwnd;
GLint giWidth, giHeight;
LONG    glMouseDownX, glMouseDownY;
BOOL    gbLeftMouse = FALSE;
BOOL    gbSpinning = FALSE;
float   curquat[4], lastquat[4];

/*
 * This size should really be based on the distance from the center of
 * rotation to the point on the object underneath the mouse.  That
 * point would then track the mouse as closely as possible.  This is a
 * simple example, though, so that is left as an Exercise for the
 * Programmer.
 */
#define TRACKBALLSIZE  (0.8f)

/*
 * Local function prototypes (not defined in trackball.h)
 */
static float tb_project_to_sphere(float, float, float);
static void normalize_quat(float [4]);

void
trackball_Init( GLint width, GLint height )
{
    ghwnd = auxGetHWND();
    giWidth = width;
    giHeight = height;

    trackball_calc_quat( curquat, 0.0f, 0.0f, 0.0f, 0.0f );
}

void
trackball_Resize( GLint width, GLint height )
{
    giWidth = width;
    giHeight = height;
}

GLenum
#ifdef _USE_TK
trackball_MouseDown( int mouseX, int mouseY, GLenum button )
#else
trackball_MouseDown( AUX_EVENTREC *event)
#endif
{
    SetCapture(ghwnd);
    glMouseDownX = event->data[AUX_MOUSEX];
    glMouseDownY = event->data[AUX_MOUSEY];
    gbLeftMouse = TRUE;
    return GL_TRUE;
}

GLenum
#ifdef _USE_TK
trackball_MouseUp( int mouseX, int mouseY, GLenum button )
#else
trackball_MouseUp( AUX_EVENTREC *event )
#endif
{
    ReleaseCapture();
    gbLeftMouse = FALSE;
    return GL_TRUE;
}

/* these 4 not used yet */
void
trackball_MouseDownEvent( int mouseX, int mouseY, GLenum button )
{
}

void
trackball_MouseUpEvent( int mouseX, int mouseY, GLenum button )
{
}

void
trackball_MouseDownFunc(GLenum (*Func)(int, int, GLenum))
{
    MouseDownFunc = Func;
}

void
trackball_MouseUpFunc(GLenum (*Func)(int, int, GLenum))
{
    MouseUpFunc = Func;
}

void
trackball_CalcRotMatrix( GLfloat matRot[4][4] )
{
    POINT pt;

    if (gbLeftMouse)
    {
        auxGetMouseLoc( &pt.x, &pt.y );

        // If mouse has moved since button was pressed, change quaternion.

            if (pt.x != glMouseDownX || pt.y != glMouseDownY)
            {
#if 1
    /* negate all params for proper operation with glTranslate(-z)
     */
                trackball_calc_quat(lastquat,
                          -(2.0f * ( giWidth - glMouseDownX ) / giWidth - 1.0f),
                          -(2.0f * glMouseDownY / giHeight - 1.0f),
                          -(2.0f * ( giWidth - pt.x ) / giWidth - 1.0f),
                          -(2.0f * pt.y / giHeight - 1.0f)
                         );
#else
// now out-of-date
                trackball_calc_quat(lastquat,
                          2.0f * ( Width - glMouseDownX ) / Width - 1.0f,
                          2.0f * glMouseDownY / Height - 1.0f,
                          2.0f * ( Width - pt.x ) / Width - 1.0f,
                          2.0f * pt.y / Height - 1.0f );
#endif

                gbSpinning = TRUE;
            }
            else
                gbSpinning = FALSE;

            glMouseDownX = pt.x;
            glMouseDownY = pt.y;
    }

    if (gbSpinning)
        trackball_add_quats(lastquat, curquat, curquat);

    trackball_build_rotmatrix(matRot, curquat);
}

void
vzero(float *v)
{
    v[0] = 0.0f;
    v[1] = 0.0f;
    v[2] = 0.0f;
}

void
vset(float *v, float x, float y, float z)
{
    v[0] = x;
    v[1] = y;
    v[2] = z;
}

void
vsub(const float *src1, const float *src2, float *dst)
{
    dst[0] = src1[0] - src2[0];
    dst[1] = src1[1] - src2[1];
    dst[2] = src1[2] - src2[2];
}

void
vcopy(const float *v1, float *v2)
{
    register int i;
    for (i = 0 ; i < 3 ; i++)
        v2[i] = v1[i];
}

void
vcross(const float *v1, const float *v2, float *cross)
{
    float temp[3];

    temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
    temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
    temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
    vcopy(temp, cross);
}

float
vlength(const float *v)
{
    return (float) sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}

void
vscale(float *v, float div)
{
    v[0] *= div;
    v[1] *= div;
    v[2] *= div;
}

void
vnormal(float *v)
{
    vscale(v,1.0f/vlength(v));
}

float
vdot(const float *v1, const float *v2)
{
    return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}

void
vadd(const float *src1, const float *src2, float *dst)
{
    dst[0] = src1[0] + src2[0];
    dst[1] = src1[1] + src2[1];
    dst[2] = src1[2] + src2[2];
}

/*
 * Ok, simulate a track-ball.  Project the points onto the virtual
 * trackball, then figure out the axis of rotation, which is the cross
 * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0)
 * Note:  This is a deformed trackball-- is a trackball in the center,
 * but is deformed into a hyperbolic sheet of rotation away from the
 * center.  This particular function was chosen after trying out
 * several variations.
 *
 * It is assumed that the arguments to this routine are in the range
 * (-1.0 ... 1.0)
 */
void
trackball_calc_quat(float q[4], float p1x, float p1y, float p2x, float p2y)
{
    float a[3]; /* Axis of rotation */
    float phi;  /* how much to rotate about axis */
    float p1[3], p2[3], d[3];
    float t;

    if (p1x == p2x && p1y == p2y) {
/* Zero rotation */
        vzero(q);
q[3] = 1.0f;
        return;
    }

    /*
     * First, figure out z-coordinates for projection of P1 and P2 to
     * deformed sphere
     */
    vset(p1,p1x,p1y,tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y));
    vset(p2,p2x,p2y,tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y));

    /*
     *  Now, we want the cross product of P1 and P2
     */
    vcross(p2,p1,a);

    /*
     *  Figure out how much to rotate around that axis.
     */
    vsub(p1,p2,d);
    t = vlength(d) / (2.0f*TRACKBALLSIZE);

    /*
     * Avoid problems with out-of-control values...
     */
    if (t > 1.0f) t = 1.0f;
    if (t < -1.0f) t = -1.0f;
    phi = 2.0f * (float) asin(t);

    trackball_axis_to_quat(a,phi,q);
}

/*
 *  Given an axis and angle, compute quaternion.
 */
void
trackball_axis_to_quat(float a[3], float phi, float q[4])
{
    vnormal(a);
    vcopy(a,q);
    vscale(q,(float) sin(phi/2.0f));
    q[3] = (float) cos(phi/2.0f);
}

/*
 * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
 * if we are away from the center of the sphere.
 */
static float
tb_project_to_sphere(float r, float x, float y)
{
    float d, t, z;

    d = (float) sqrt(x*x + y*y);
    if (d < r * 0.70710678118654752440f) {    /* Inside sphere */
z = (float) sqrt(r*r - d*d);
    } else {           /* On hyperbola */
        t = r / 1.41421356237309504880f;
        z = t*t / d;
    }
    return z;
}

/*
 * Given two rotations, e1 and e2, expressed as quaternion rotations,
 * figure out the equivalent single rotation and stuff it into dest.
 *
 * This routine also normalizes the result every RENORMCOUNT times it is
 * called, to keep error from creeping in.
 *
 * NOTE: This routine is written so that q1 or q2 may be the same
 * as dest (or each other).
 */

#define RENORMCOUNT 97

void
trackball_add_quats(float q1[4], float q2[4], float dest[4])
{
    static int count=0;
    float t1[4], t2[4], t3[4];
    float tf[4];

    vcopy(q1,t1);
    vscale(t1,q2[3]);

    vcopy(q2,t2);
    vscale(t2,q1[3]);

    vcross(q2,q1,t3);
    vadd(t1,t2,tf);
    vadd(t3,tf,tf);
    tf[3] = q1[3] * q2[3] - vdot(q1,q2);

    dest[0] = tf[0];
    dest[1] = tf[1];
    dest[2] = tf[2];
    dest[3] = tf[3];

    if (++count > RENORMCOUNT) {
        count = 0;
        normalize_quat(dest);
    }
}

/*
 * Quaternions always obey:  a^2 + b^2 + c^2 + d^2 = 1.0
 * If they don't add up to 1.0, dividing by their magnitued will
 * renormalize them.
 *
 * Note: See the following for more information on quaternions:
 *
 * - Shoemake, K., Animating rotation with quaternion curves, Computer
 *   Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985.
 * - Pletinckx, D., Quaternion calculus as a basic tool in computer
 *   graphics, The Visual Computer 5, 2-13, 1989.
 */
static void
normalize_quat(float q[4])
{
    int i;
    float mag;

    mag = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
    for (i = 0; i < 4; i++) q[i] /= mag;
}

/*
 * Build a rotation matrix, given a quaternion rotation.
 *
 */
void
trackball_build_rotmatrix(float m[4][4], float q[4])
{
    m[0][0] = 1.0f - 2.0f * (q[1] * q[1] + q[2] * q[2]);
    m[0][1] = 2.0f * (q[0] * q[1] - q[2] * q[3]);
    m[0][2] = 2.0f * (q[2] * q[0] + q[1] * q[3]);
    m[0][3] = 0.0f;

    m[1][0] = 2.0f * (q[0] * q[1] + q[2] * q[3]);
    m[1][1]= 1.0f - 2.0f * (q[2] * q[2] + q[0] * q[0]);
    m[1][2] = 2.0f * (q[1] * q[2] - q[0] * q[3]);
    m[1][3] = 0.0f;

    m[2][0] = 2.0f * (q[2] * q[0] - q[1] * q[3]);
    m[2][1] = 2.0f * (q[1] * q[2] + q[0] * q[3]);
    m[2][2] = 1.0f - 2.0f * (q[1] * q[1] + q[0] * q[0]);
    m[2][3] = 0.0f;

    m[3][0] = 0.0f;
    m[3][1] = 0.0f;
    m[3][2] = 0.0f;
    m[3][3] = 1.0f;
}


TRACKBAL.H

/*
 * trackball.h
 * A virtual trackball implementation
 * Written by Gavin Bell for Silicon Graphics, November 1988.
 */

/*
 * Initialize trackball in win32 environment
 */
extern void
trackball_Init( GLint width, GLint height );

extern void
trackball_Resize( GLint width, GLint height );

extern GLenum
trackball_MouseDown( AUX_EVENTREC *event );

extern GLenum
trackball_MouseUp( AUX_EVENTREC *event );

/* These next Mouse fns are required if both the trackbal and user
 * need mouse events.  Otherwise, can just supply above two functions
 * to tk to call
 */

/*
 * Mouse functions called directly on events
 */
extern void
trackball_MouseDownEvent( int mouseX, int mouseY, GLenum button );

extern void
trackball_MouseUpEvent( int mouseX, int mouseY, GLenum button );

/*
 * Functions to register mouse event callbacks
 */
extern void
trackball_MouseDownFunc(GLenum (*)(int, int, GLenum));

extern void
trackball_MouseUpFunc(GLenum (*)(int, int, GLenum));

/*
 * Calculate rotation matrix based on mouse movement
 */
void
trackball_CalcRotMatrix( GLfloat matRot[4][4] );

/*
 * Pass the x and y coordinates of the last and current positions of
 * the mouse, scaled so they are from (-1.0 ... 1.0).
 *
 * if ox,oy is the window's center and sizex,sizey is its size, then
 * the proper transformation from screen coordinates (sc) to world
 * coordinates (wc) is:
 * wcx = (2.0 * (scx-ox)) / (float)sizex - 1.0
 * wcy = (2.0 * (scy-oy)) / (float)sizey - 1.0
 *
 * The resulting rotation is returned as a quaternion rotation in the
 * first paramater.
 */
void
trackball_calc_quat(float q[4], float p1x, float p1y, float p2x, float p2y);

/*
 * Given two quaternions, add them together to get a third quaternion.
 * Adding quaternions to get a compound rotation is analagous to adding
 * translations to get a compound translation.  When incrementally
 * adding rotations, the first argument here should be the new
 * rotation, the second and third the total rotation (which will be
 * over-written with the resulting new total rotation).
 */
void
trackball_add_quats(float *q1, float *q2, float *dest);

/*
 * A useful function, builds a rotation matrix in Matrix based on
 * given quaternion.
 */
void
trackball_build_rotmatrix(float m[4][4], float q[4]);

/*
 * This function computes a quaternion based on an axis (defined by
 * the given vector) and an angle about which to rotate.  The angle is
 * expressed in radians.  The result is put into the third argument.
 */
void
trackball_axis_to_quat(float a[3], float phi, float q[4]);


ATMOSPHE.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/glu.h>

#ifdef X11
#include <GL/glx.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include "stonehen.h"
#endif

#define ATMOSPHERE_EXTERN
#include "atmosphe.h"

const Weather clear("Clear", 0.);
const Weather foggy("Foggy", .04, .5, white, Color(.6, .6, 1), white);
const Weather very_foggy("Very Foggy", .25, .5, white, white, white);
const Weather rainy("Rainy", .01, 1., black, Color(.35, .35, .35), black);

Weather weathers[nweathers] = {clear, foggy, very_foggy, rainy};

const float root3_2 = sqrt(3.) / 2.;

inline float clamp(float a, float min = 0, float max = 1)
{
  if (a < min) return min;
  else if (a > max) return max;
  else return a;
}

Weather::Weather()
{
  strcpy(name, "No name");
  fog_density = 0;
  fog_color = white;
  fog_spread = 1;
  sun_brightness = 1;
  light_sun = GL_LIGHT0;
  light_ambient = GL_LIGHT1;
  sky_top = blue;
  sky_bottom = white;
}

Weather::Weather(const char *n, GLfloat fd, GLfloat fs, Color fc,
 Color s1, Color s2, GLfloat sb,
 GLenum ls, GLenum la)
{
  strcpy(name, n);
  fog_density = fd;
  fog_color = fc;
  fog_spread = fs;
  sun_brightness = sb;
  light_sun = ls;
  light_ambient = la;
  sky_top = s1;
  sky_bottom = s2;
}

Weather::~Weather()
{
}

Weather Weather::operator=(Weather a)
{
  strcpy(name, a.name);

  fog_density = a.fog_density;
  fog_color = a.fog_color;
  fog_spread = a.fog_spread;

  sun_brightness = a.sun_brightness;

  light_sun = a.light_sun;
  light_ambient = a.light_ambient;

  sky_top = a.sky_top;
  sky_bottom = a.sky_bottom;

  return *this;
}

void Weather::apply(Point sun)
{
  Color c;

  if (fog_density != 0) {
    glFogf(GL_FOG_DENSITY, fog_density);
    glFogfv(GL_FOG_COLOR, fog_color.c);
    glFogi(GL_FOG_MODE, GL_EXP2);
  }
  glLightfv(light_sun, GL_AMBIENT, black.c);
  c = white;
  c *= sun_brightness;
  glLightfv(light_sun, GL_DIFFUSE, c.c);
  glLightfv(light_sun, GL_SPECULAR, white.c);
  if (sun.pt[2] >= 0.0) glEnable(light_sun);
  else glDisable(light_sun);

  c = white;
  c *= .25;
  glLightfv(light_ambient, GL_AMBIENT, c.c);
  glLightfv(light_ambient, GL_DIFFUSE, black.c);
  glLightfv(light_ambient, GL_SPECULAR, black.c);
  glEnable(light_ambient);
}

void Weather::draw_sky(Point sun)
{
  Point p;
  float c;
  Color bottom, top;

  if (sun.pt[2] >= .5) c = 1.;
  else if (sun.pt[2] >= -.5)
    c = sqrt(1. - sun.pt[2]*sun.pt[2])*.5 + sun.pt[2]*root3_2;
  else c = 0;

  bottom = sky_bottom * c;
  top = sky_top * c;

  /* This is drawn as a far-away quad */
  glBegin(GL_QUADS);
  glColor3fv(bottom.c);
  glVertex3f(-1, -1, -1);
  glVertex3f(1, -1, -1);
  glColor3fv(top.c);
  glVertex3f(1, 1, -1);
  glVertex3f(-1, 1, -1);
  glEnd();
}

GLfloat Weather::shadow_blur()
{
  return clamp(fog_density * 10.);
}


ATMOSPHE.H

#ifndef ATMOSPHERE_H
#define ATMOSPHERE_H

#ifndef ATMOSPHERE_EXTERN
#define ATMOSPHERE_EXTERN extern
#endif

#include "Color.h"
#include "Point.h"

const int weather_max_name = 64;
class Weather {
 public:
  Weather();
  Weather(const char *n, GLfloat fd, GLfloat fs = 1, Color fc = white,
  Color s1 = blue, Color s2 = white,
  GLfloat sb = 1,
  GLenum ls = GL_LIGHT0, GLenum la = GL_LIGHT1);
  ~Weather();

  Weather operator=(Weather a);

  /* The sun position is used to determine how light / dark it is */
  void apply(Point sun);
  void draw_sky(Point sun);

  /* This is how much to blur shadows due to haze or fog */
  GLfloat shadow_blur();

  char name[weather_max_name];

  GLfloat fog_density;
  Color fog_color;
  /* Fog spread relates the height to the density of the fog -- 1.0
   * leads to uniform fog */
  GLfloat fog_spread;

  Color sky_top;
  Color sky_bottom;

  GLenum light_sun, light_ambient;
  GLfloat sun_brightness;

 private:
};
const int nweathers = 4;
extern Weather weathers[nweathers];
const int def_weather_index = 0;

#undef ATMOSPHERE_EXTERN

#endif


CALLBACK.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#ifdef X11
#include <X11/Intrinsic.h>
#include <X11/keysym.h>
#include <Xm/Xm.h>

#include <GL/glx.h>
#include <GLwMDrawA.h>
#endif

#include <GL/glu.h>

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

#ifdef X11
#include <sys/time.h>
#else
#include <time.h>
#endif

#ifdef WIN32
#include "stonehen.h"
#endif

#include "atmosphe.h"
#include "scene.h"

#include "callback.h"

int cb_demo_mode = 0;
float demo_time;

#ifdef X11
extern Widget glw;
extern XtAppContext app_context;
GLXContext glx_context;
#endif

static int needs_wp = 0;
#ifdef X11
static XtWorkProcId workproc = NULL;
#else
static int workproc = 0;
#endif

static int winx, winy;

static int button_down = 0;
int mousex, mousey;
/* What's moving */
GLint target;

/* Location of the telescope */
GLfloat tx, ty;

/* Movements of the camera position to be applied at the next redraw */
float rot_pendx = 0, rot_pendz = 0, trans_pend = 0;

/* How fast the camera is moving */
float trans_speed = 0;

/* This is how fast demo time moves relative to real time */
GLfloat time_scale = 0;

TimeDate last_update;

float last_time = 0;
const float time_fudge = 1000;
inline unsigned long current_time()
{
#ifdef X11
  struct timeval time;
  gettimeofday(&time, NULL);
  return (time.tv_sec * 1000000 + time.tv_usec);
#else
  return (GetTickCount() * 1000);
#endif
}

inline float clamp(float x, float min, float max)
{
  if (x < min) return min;
  else if (x > max) return max;
  else return x;
}

static void add_workproc(Widget w)
{
  needs_wp++;
#ifdef X11
  if (workproc == NULL)
    workproc = XtAppAddWorkProc(app_context, drawWP, NULL);
#endif
}

static void remove_workproc(Widget w)
{
  needs_wp--;
  if (needs_wp == 0) {
#ifdef X11
    XtRemoveWorkProc(workproc);
    workproc = NULL;
#else
    workproc = 0;
#endif
  } else if (needs_wp < 0) {
    //fprintf(stderr, "Internal Error:  No workproc to remove!\n");
    needs_wp = 0;
    workproc = NULL;
  }
}

static void reset_viewer()
{
  scene_viewer_center();
}

#ifdef X11
void intToggleCB(Widget w, XtPointer client_data, XtPointer call_data)
{
  int *data;
  XmToggleButtonCallbackStruct *ptr;
  ptr = (XmToggleButtonCallbackStruct *)call_data;
  data = (int *)client_data;
  *data = ptr->set;
  // This redraw may or may not be needed - do it to be safe
  drawWP(NULL);
}

void initCB(Widget w)
{
  Arg args[1];
  XVisualInfo *vi;

  glw = w;

  XtSetArg(args[0], GLwNvisualInfo, &vi);
  XtGetValues(w, args, 1);

  glx_context = glXCreateContext(XtDisplay(w), vi, 0, GL_FALSE);
  GLwDrawingAreaMakeCurrent(w, glx_context);

  scene_init();

  last_update.read_time();

  resetViewerCB(NULL, NULL, NULL);
}

void exposeCB(Widget w)
{
  drawWP(NULL);
}
#endif

void resizeCB(Widget w, XtPointer client_data, XtPointer call)
{
#ifdef X11
  GLwDrawingAreaCallbackStruct *call_data;

  GLwDrawingAreaMakeCurrent(w, glx_context);

  call_data = (GLwDrawingAreaCallbackStruct *)call;

  winx = call_data->width;
  winy = call_data->height;
#else
  RECT rect;
  GetClientRect(w, &rect);

  winx = WINDSIZEX(rect);
  winy = WINDSIZEY(rect);
#endif

  glViewport(0, 0, winx, winy);

  aspect = (GLfloat)winx / (GLfloat)winy;
}

void inputCB(Widget w, XtPointer client_data, XtPointer call_data)
{
  GLwDrawingAreaCallbackStruct *call;

  int bufsize = 5;
#ifdef X11
  char buffer[5];
  KeySym key;
  XComposeStatus compose;
#endif

  float dx, dy, r1, r2;

#ifdef X11
  GLwDrawingAreaMakeCurrent(w, glx_context);
#endif

  call = (GLwDrawingAreaCallbackStruct *)call_data;

  switch(call->event->type) {
  case ButtonPress:
    last_time = current_time();
    button_down = call->event->xbutton.button;
    mousex = call->event->xbutton.x;
    mousey = call->event->xbutton.y;
    /* Determine if the target should be the camera position
     * or the telescope */
    if (use_telescope) {
      scene_get_position_telescope(&tx, &ty);
      scene_get_radius_telescope(&r1);
      dx = (tx + .5) - ((GLfloat)(winx - mousex)/(GLfloat)winx);
      dy = (ty + .5) - ((GLfloat)(winy - mousey)/(GLfloat)winy);
      r2 = sqrt(dx*dx + dy*dy);
      if (r2 < r1) target = name_telescope;
      else target = name_background;
    } else target = name_background;
    add_workproc(w);
    break;
  case ButtonRelease:
#ifdef X11
    if (call->event->xbutton.button == Button3) {
      /* Use Button3 to stop */
      if (trans_speed) remove_workproc(w);
      trans_speed = 0;
    }
#endif
    remove_workproc(w);
    button_down = 0;
    break;
  case MotionNotify:
    switch(button_down) {
    case Button1:
      /* Use Button1 to control the way in which the viewer is looking
       * or to move the telescope around */
      if (target == name_background) {
dx = (float)(call->event->xmotion.x - mousex) / (float)winx;
dy = (float)(call->event->xmotion.y - mousey) / (float)winy;
rot_pendx -= dy * fov;
rot_pendz -= dx * fov;
      } else {
dx = (float)(mousex - call->event->xmotion.x) / (float)winx;
dy = (float)(mousey - call->event->xmotion.y) / (float)winy;
tx += dx;
ty += dy;
tx = clamp(tx, -.5, .5);
ty = clamp(ty, -.5, .5);
scene_position_telescope(tx, ty);
      }
      break;
    case Button2:
      /* Use Button2 to change speed */
      dx = (float)(mousex - call->event->xmotion.x) /
(float)winx;
      if (dx && !trans_speed) add_workproc(w);
#ifdef WIN32
      else if (!dx && trans_speed) remove_workproc(w);
#endif
      trans_speed += dx;
      break;
    }
    mousex = call->event->xmotion.x;
    mousey = call->event->xmotion.y;
    break;

#ifdef X11 // We can handle our own keys...
  case KeyPress:
    XLookupString(&(call->event->xkey), buffer, bufsize, &key, &compose);
    if (key == XK_Escape) exit(0);
    break;
#endif

  default:
    break;
  }
}

const float speed_t = .5;
const float speed_r = 15.;
const float speed_rx = 10.;
void demo_mode_update(float dt)
{
  float t;

  if (!cb_demo_mode) return;

  dt /= 1000000;
  demo_time += dt;

  t = demo_time;

  if (t < 10.5) {
    trans_speed = speed_t;
    return;
  } else t -= 10.5;

  if (t < 1) {
    trans_speed = 0;
    rot_pendz = 0;
    return;
  } else t -= 1;

  if (t < 3.) {
    trans_speed = 0.;
    rot_pendz = dt * speed_r;
    return;
  } else t -= 3.;

  if (t < 2.) {
    rot_pendx = -dt * speed_rx;
    return;
  } else t -= 2.;

  if (t < 2.) {
    return;
  } else t -= 2.;

  if (t < 2.) {
    rot_pendx = dt * speed_rx;
    return;
  } else t -= 2.;

  if (t < 3.) {
    trans_speed = 0.;
    rot_pendz = dt * speed_r;
    return;
  } else t -= 3.;

  if (t < 30.) {
    trans_speed  = speed_t;
    rot_pendz = 0;
    return;
  } else t -= 30.;

  if (t < 1.) {
    trans_speed = 0;
    return;
  } else t -= 1.;

  if (t < 1.3) {
    rot_pendz = -dt * speed_r;
    return;
  } else t -= 1.3;

  // Pan back to see entire thing
  if (t < 23) {
    trans_speed = speed_t;
    return;
  } else t -= 23;

  // Hold before starting over
  if (t < 20) {
    trans_speed = 0;
    return;
  }
  else t -= 20;

  demo_time = 0.;
  reset_viewer();
}

Boolean drawWP(Widget w)
{
  /* Right now, there's two completely independent time measurements:
   * one for the time of day in the demo and one for changing the camera
   * position */
  TimeDate t, dt;
  float elapsed_time, time;
#ifdef WIN32
  HDC hDC;
#endif

  if (time_scale != 0.0) {
    t.read_time();
    dt = (t - last_update) * time_scale;
    scene_inc_time(dt);
    last_update = t;
  }

  time = current_time();
  if (time - last_time > time_fudge) {
    elapsed_time = time - last_time;
    demo_mode_update(elapsed_time);
    trans_pend = trans_speed * (elapsed_time / 1000000);
    last_time = time;
  }

  scene_viewer_rotatex(rot_pendx);
  scene_viewer_rotatez(rot_pendz);
  scene_viewer_translate(trans_pend);
  rot_pendx = 0;
  rot_pendz = 0;
  trans_pend = 0;

#ifdef X11
  GLwDrawingAreaMakeCurrent(glw, glx_context);
  scene_render();
  /* This is a total hack */
//  if (!use_antialias)
GLwDrawingAreaSwapBuffers(glw);
#else
  hDC = GetDC(w);
  scene_render();
  SwapBuffers(hDC);
  ReleaseDC(w, hDC);
#endif

  return FALSE;
}

#ifdef X11
void weatherCB(Widget w, XtPointer client_data, XtPointer call_data)
{
  Weather *data;
  XmToggleButtonCallbackStruct *ptr;
  ptr = (XmToggleButtonCallbackStruct *)call_data;
  if (ptr->set) scene_set_weather(*((Weather *)client_data));
  drawWP(NULL);
}
#endif

void checkTimeMenuItem(HMENU hmenu, UINT idCheckItem)
{
    CheckMenuItem(hmenu, IDM_CURRENTTIME, MF_UNCHECKED);
    CheckMenuItem(hmenu, IDM_10AM, MF_UNCHECKED);
    CheckMenuItem(hmenu, IDM_NOON, MF_UNCHECKED);
    CheckMenuItem(hmenu, IDM_4PM, MF_UNCHECKED);
    CheckMenuItem(hmenu, idCheckItem, MF_CHECKED);
}

void currentTimeCB(Widget w)
{
  scene_set_time(TimeDate().read_time());
  drawWP(w);
}

void time10amCB(Widget w)
{
  scene_set_time(TimeDate(10, 0));
  drawWP(w);
}

void time12pmCB(Widget w)
{
  scene_set_time(TimeDate(12, 0));
  drawWP(w);
}

void time4pmCB(Widget w)
{
  scene_set_time(TimeDate(16, 0));
  drawWP(w);
}

void timeSpeedCB(Widget w, XtPointer client_data, XtPointer call_data)
{

#ifdef X11
  if (!((XmToggleButtonCallbackStruct *)call_data)->set) return;
  time_scale = (int)client_data;
#endif
  if (time_scale == 0.0) remove_workproc(w);
  else add_workproc(w);
  last_update.read_time();
}

void demo_modeCB(Widget w, XtPointer client_data, XtPointer call_data)
{
#ifdef X11
  int val  = ((XmToggleButtonCallbackStruct *)call_data)->set;
#else
  int val = cb_demo_mode;
#endif
  if (!val) {
    remove_workproc(w);
    resetViewerCB(w, NULL, NULL);
    last_time = current_time();
  } else {
    reset_viewer();
    add_workproc(w);
    trans_speed = 0;
    demo_time = 0;
    rot_pendx = -5;
  }
  drawWP(w);
}

void resetViewerCB(Widget w, XtPointer client_data, XtPointer call_data)
{
  trans_speed = 0;
  rot_pendx = rot_pendz = trans_pend = 0;
  rot_pendx = -5;
  reset_viewer();
  return;
}

void exitCB(Widget w, XtPointer client_data, XtPointer call_data)
{
  exit(0);
}


CALLBACK.H

#ifndef CALLBACKS_H
#define CALLBACKS_H

extern int cb_demo_mode;

void intToggleCB(Widget w, XtPointer client_data, XtPointer call_data);

void initCB(Widget w);
void exposeCB(Widget w);
void resizeCB(Widget w, XtPointer client_data, XtPointer call);
void inputCB(Widget w, XtPointer client_data, XtPointer call_data);

#ifdef X11
Boolean drawWP(XtPointer data);
#else
BOOL drawWP(HWND hWnd);
#endif

void weatherCB(Widget w, XtPointer client_data, XtPointer call_data);

void checkTimeMenuItem(HMENU hmenu, UINT idCheckItem);

void currentTimeCB(Widget w);
void time10amCB(Widget w);
void time12pmCB(Widget w);
void time4pmCB(Widget w);

/* This sets how fast time in the demo moves as opposed to
 * real time -- client_data is secretly an int */
void timeSpeedCB(Widget w, XtPointer client_data, XtPointer call_data);

void demo_modeCB(Widget w, XtPointer client_data, XtPointer call_data);
void resetViewerCB(Widget w, XtPointer client_data, XtPointer call_data);

void exitCB(Widget w, XtPointer client_data, XtPointer call_data);

#endif


COLOR.H

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#ifndef COLOR_H
#define COLOR_H

class Color {
 public:
  inline Color() {};
  inline Color(GLfloat r, GLfloat g, GLfloat b, GLfloat a = 1.0);

  inline Color operator=(GLfloat *a);
  inline Color operator=(GLfloat a);
  inline Color operator+(Color a);
  inline Color operator+=(Color a);
  inline Color operator*(Color a);
  inline Color operator*(GLfloat a);
  inline Color operator*=(Color a);
  inline Color operator*=(GLfloat *a);
  inline Color operator*=(GLfloat a);

  inline GLfloat& operator[](int index);

  inline Color clamp();

  inline void glcolor();

  inline void print();
  inline void print(const char *format);

  GLfloat c[4];
};

const Color white(1., 1., 1., 1.), black(0., 0., 0., 1.);
const Color red(1, 0, 0), green(0, 1, 0), blue(0, 0, 1);

inline Color::Color(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
  c[0] = r;
  c[1] = g;
  c[2] = b;
  c[3] = a;
}

inline Color Color::operator=(GLfloat a)
{
  c[0] = c[1] = c[2] = c[3] = a;
  return *this;
}

inline Color Color::operator=(GLfloat *a)
{
  c[0] = a[0];
  c[1] = a[1];
  c[2] = a[2];
  c[3] = a[3];
  return *this;
}

inline Color Color::operator+(Color a)
{
  Color val;
  val.c[0] = c[0] + a.c[0];
  val.c[1] = c[1] + a.c[1];
  val.c[2] = c[2] + a.c[2];
  val.c[3] = c[3] + a.c[3];
  return val;
}

inline Color Color::operator+=(Color a)
{
  c[0] += a.c[0];
  c[1] += a.c[1];
  c[2] += a.c[2];
  c[3] += a.c[3];
  return *this;
}

inline Color Color::operator*(Color a)
{
  Color val;
  val.c[0] = c[0] * a.c[0];
  val.c[1] = c[1] * a.c[1];
  val.c[2] = c[2] * a.c[2];
  val.c[3] = c[3] * a.c[3];
  return val;
}

inline Color Color::operator*(GLfloat a)
{
  Color val;
  val.c[0] = c[0] * a;
  val.c[1] = c[1] * a;
  val.c[2] = c[2] * a;
  val.c[3] = c[3] * a;
  return val;
}

inline Color Color::operator*=(Color a)
{
  c[0] *= a.c[0];
  c[1] *= a.c[1];
  c[2] *= a.c[2];
  return *this;
}

inline Color Color::operator*=(GLfloat *a)
{
  c[0] *= a[0];
  c[1] *= a[1];
  c[2] *= a[2];
  return *this;
}

inline Color Color::operator*=(GLfloat a)
{
  c[0] *= a;
  c[1] *= a;
  c[2] *= a;
  c[3] *= a;

  return *this;
}

inline GLfloat& Color::operator[](int index)
{
  return c[index];
}

inline Color Color::clamp()
{
  Color val;
  val.c[0] = c[0] < 0.0 ? 0.0 : (c[0] > 1.0 ? 1.0 : c[0]);
  val.c[1] = c[1] < 0.0 ? 0.0 : (c[1] > 1.0 ? 1.0 : c[1]);
  val.c[2] = c[2] < 0.0 ? 0.0 : (c[2] > 1.0 ? 1.0 : c[2]);
  val.c[3] = c[3] < 0.0 ? 0.0 : (c[3] > 1.0 ? 1.0 : c[3]);
  return val;
}

inline void Color::glcolor()
{
  glColor4fv(c);
}

inline void Color::print()
{
  print("%f %f %f %f\n");
}

inline void Color::print(const char *format)
{
  printf(format, c[0], c[1], c[2], c[3]);
}

#endif


ELLIPSE.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/glu.h>

#ifdef X11
#include <GL/glx.h>
#endif

#include <stdio.h>

#ifdef WIN32
#include "stonehen.h"
#endif

#include "Ellipse.h"

EllipseSt::EllipseSt()
{
  Point sar_dim, lin_dim;

  sar_dim.set(.3, .5, 1.2);
  lin_dim.set(.3, 1.2, .2);

  sarcens[0].set_dimensions(sar_dim);
  lintels[0].set_dimensions(lin_dim);
  sarcens[0].translate(5, 0, sar_dim.pt[2]);
  lintels[0].translate(4.9, 0, 2.*sar_dim.pt[2] + lin_dim.pt[2]);
  copy_zero();

  sarcens[0].rotate_self_aboutz(17);
  sarcens[1].rotate_self_aboutz(33);
  lintels[0].rotate_self_aboutz(25);

  sarcens[2].rotate_self_aboutz(-17);
  sarcens[3].rotate_self_aboutz(-33);
  lintels[1].rotate_self_aboutz(-25);

  sarcens[4].rotate_self_aboutz(197);
  sarcens[5].rotate_self_aboutz(213);
  lintels[2].rotate_self_aboutz(205);

  sarcens[6].rotate_self_aboutz(-197);
  sarcens[7].rotate_self_aboutz(-213);
  lintels[3].rotate_self_aboutz(-205);
}

void EllipseSt::erode(float p)
{
  int i;

  for (i = 0; i < nsarcens; i++) sarcens[i].erode(p);
  for (i = 0; i < nlintels; i++) lintels[i].erode(p);
}

void EllipseSt::draw()
{
  glPushMatrix();
  glScalef(1, 1.1, 1);
  draw_sarcens();
  draw_lintels();
  glPopMatrix();
}

void EllipseSt::draw_sarcens()
{
  int i;
  for (i = 0; i < nsarcens; i++) sarcens[i].draw();
}

void EllipseSt::draw_lintels()
{
  int i;
  for (i = 0; i < nlintels; i++) lintels[i].draw();
}

void EllipseSt::draw_shadow(Point dlight, GLfloat blur,
  Color color, Color diffuse)
{
  draw_sarcens_shadows(dlight, blur, color, diffuse);
  draw_lintels_shadows(dlight, blur, color, diffuse);
}

void EllipseSt::draw_sarcens_shadows(Point dlight, GLfloat blur,
   Color color, Color diffuse)
{
  int i;
  for (i = 0; i < nsarcens; i++)
sarcens[i].draw_shadow(dlight, blur, color, diffuse);
}

void EllipseSt::draw_lintels_shadows(Point dlight, GLfloat blur,
   Color color, Color diffuse)
{
  int i;
  for (i = 0; i < nlintels; i++)
lintels[i].draw_shadow(dlight, blur, color, diffuse);
}

void EllipseSt::copy_zero()
{
  int i;

  for (i = 1; i < nsarcens; i++) sarcens[i] = sarcens[0];
  for (i = 1; i < nlintels; i++) lintels[i] = lintels[0];
}


ELLIPSE.H

#ifndef ELLIPSE_H
#define ELLIPSE_H

#include "Stone.h"

const int nsarcens = 8;
const int nlintels = 4;

class EllipseSt {
 public:
  EllipseSt();
  ~EllipseSt() {};

  void erode(float p);

  void draw();
  void draw_shadow(Point dlight, GLfloat blur = 0.0,
                   Color color = black, Color diffuse = black);

 private:
  GLfloat r1, r2;

  Stone sarcens[nsarcens], lintels[nlintels];

  void copy_zero();

  void draw_sarcens();
  void draw_lintels();

  void draw_sarcens_shadows(Point dlight, GLfloat blur,
                            Color color, Color diffuse);
  void draw_lintels_shadows(Point dlight, GLfloat blur,
                            Color color, Color diffuse);
};

#endif


MAIN.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/glu.h>
#include <GL/glx.h>

#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include <Xm/CascadeB.h>
#include <Xm/Frame.h>
#include <Xm/MainW.h>
#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/RowColumn.h>
#include <Xm/ToggleB.h>
#include <GLwMDrawA.h>

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

#ifdef WIN32
#include "stonehen.h"
#endif

#include "atmosphe.h"
#include "scene.h"
#include "callback.h"

static Display *display;
XtAppContext app_context;
Widget glw;

const int max_args = 20;

XVisualInfo *find_visual()
{
  int attr[256], i, stencil, dbuffer, accum;
  XVisualInfo *vi;

  i = 0;
  attr[i++] = GLX_RGBA;
  attr[i++] = GLX_RED_SIZE;  attr[i++] = 1;
  attr[i++] = GLX_GREEN_SIZE;  attr[i++] = 1;
  attr[i++] = GLX_BLUE_SIZE;  attr[i++] = 1;
  attr[i++] = GLX_DEPTH_SIZE;  attr[i++] = 1;
  dbuffer = i;
  attr[i++] = GLX_DOUBLEBUFFER;
  stencil = i;
  attr[i++] = GLX_STENCIL_SIZE;
  attr[i++] = 1;
  accum = i;
  attr[i++] = GLX_ACCUM_RED_SIZE;  attr[i++] = 1;
  attr[i++] = GLX_ACCUM_BLUE_SIZE;  attr[i++] = 1;
  attr[i++] = GLX_ACCUM_GREEN_SIZE;  attr[i++] = 1;
  attr[i++] = GLX_ACCUM_ALPHA_SIZE;  attr[i++] = 1;
  attr[i++] = (int)None;

  vi = glXChooseVisual(display, DefaultScreen(display), attr);
  if (vi == NULL) {
    //fprintf(stderr, "Unable to find visual with accumulation buffer.\n");
    //fprintf(stderr, "(Fancy fog won't work).\n");
    attr[accum] = (int)None;
    vi = glXChooseVisual(display, DefaultScreen(display), attr);
    if (vi == NULL) {
      //fprintf(stderr, "Unable to find visual with stencil buffer.\n");
      //fprintf(stderr, "(Things won't look quite as good).\n");
      attr[stencil] = (int)None;
      vi = glXChooseVisual(display, DefaultScreen(display), attr);
      if (vi == NULL) {
//fprintf(stderr, "Unable to find double-buffered visual.\n");
//fprintf(stderr, "(Things will look even worse).\n");
attr[dbuffer] = (int)None;
vi = glXChooseVisual(display, DefaultScreen(display), attr);
//if (vi == NULL) fprintf(stderr, "Can't find visual at all.\n");
      }
    }
  }
  return vi;
}

Widget create_widgets(Widget parent)
{
  Widget main_window;
  Widget menu_bar;
  Widget menu_pane;
  Widget button;
  Widget cascade;
  Widget frame;
  XVisualInfo *vi;

  Arg args[max_args];
  int argcount;

#ifdef X11
  char buffer[128];
#endif

  int i;

  main_window = XmCreateMainWindow(parent, "main1", NULL, 0);
  XtManageChild(main_window);

  menu_bar = XmCreateMenuBar(main_window, "menu_bar", NULL, 0);
  XtManageChild(menu_bar);

  menu_pane = XmCreatePulldownMenu(menu_bar, "menu_pane", NULL, 0);
  button = XmCreatePushButton(menu_pane, "Exit", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNactivateCallback, (XtCallbackProc)exitCB, NULL);

  XtSetArg(args[0], XmNsubMenuId, menu_pane);
  cascade = XmCreateCascadeButton(menu_bar, "File", args, 1);
  XtManageChild(cascade);

  menu_pane = XmCreatePulldownMenu(menu_bar, "menu_pane", NULL, 0);
  XtSetArg(args[0], XmNset, use_lighting);
  button = XmCreateToggleButton(menu_pane, "Lighting", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
                (XtCallbackProc)intToggleCB, &use_lighting);
  XtSetArg(args[0], XmNset, draw_shadows);
  button = XmCreateToggleButton(menu_pane, "Shadows", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
                (XtCallbackProc)intToggleCB, &draw_shadows);

  XtSetArg(args[0], XmNset, use_normal_fog);
  button = XmCreateToggleButton(menu_pane, "Fog", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
                (XtCallbackProc)intToggleCB, &use_normal_fog);

#ifdef TEXTURE
  XtSetArg(args[0], XmNset, use_textures);
  button = XmCreateToggleButton(menu_pane, "Texture Map", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
                (XtCallbackProc)intToggleCB, &use_textures);
#endif

  XtSetArg(args[0], XmNset, use_telescope);
  button = XmCreateToggleButton(menu_pane, "Telescope", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
(XtCallbackProc)intToggleCB, &use_telescope);

  XtSetArg(args[0], XmNset, use_antialias);
  button = XmCreateToggleButton(menu_pane, "Antialias", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
(XtCallbackProc)intToggleCB, &use_antialias);

  XtSetArg(args[0], XmNsubMenuId, menu_pane);
  cascade = XmCreateCascadeButton(menu_bar, "SPFX", args, 1);
  XtManageChild(cascade);

  argcount = 0;
  XtSetArg(args[argcount], XmNradioBehavior, True); argcount++;
  menu_pane = XmCreatePulldownMenu(menu_bar, "menu_pane", args, argcount);
  XtSetArg(args[0], XmNset, TRUE);
  for (i = 0; i < nweathers; i++) {
    if (i == def_weather_index) argcount = 1;
    else argcount = 0;
    button = XmCreateToggleButton(menu_pane, (char *)weathers[i].name,
  args, argcount);
    XtManageChild(button);
    XtAddCallback(button, XmNvalueChangedCallback,
  (XtCallbackProc)weatherCB,
  (XtPointer)(&weathers[i]));
  }
  XtSetArg(args[0], XmNsubMenuId, menu_pane);
  cascade = XmCreateCascadeButton(menu_bar, "Weather", args, 1);
  XtManageChild(cascade);

  argcount = 0;
  XtSetArg(args[argcount], XmNradioBehavior, True); argcount++;
  menu_pane = XmCreatePulldownMenu(menu_bar, "menu_pane", args, argcount);
  button = XmCreatePushButton(menu_pane, "Current Time", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNactivateCallback,
(XtCallbackProc)currentTimeCB, NULL);
  button = XmCreatePushButton(menu_pane, "10 a.m.", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNactivateCallback,
(XtCallbackProc)time10amCB, NULL);
  button = XmCreatePushButton(menu_pane, "Noon", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNactivateCallback,
(XtCallbackProc)time12pmCB, NULL);
  button = XmCreatePushButton(menu_pane, "4 p.m.", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNactivateCallback,
(XtCallbackProc)time4pmCB, NULL);

  XtSetArg(args[0], XmNset, 1); argcount = 1;
  button = XmCreateToggleButton(menu_pane, "Hold Time", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
(XtCallbackProc)timeSpeedCB, (XtPointer)0);
  button = XmCreateToggleButton(menu_pane, "Scale = 1:1", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
(XtCallbackProc)timeSpeedCB, (XtPointer)1);
  button = XmCreateToggleButton(menu_pane, "Scane = 1:10", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
(XtCallbackProc)timeSpeedCB, (XtPointer)10);
  button = XmCreateToggleButton(menu_pane, "Scane = 1:100", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
(XtCallbackProc)timeSpeedCB, (XtPointer)100);
  button = XmCreateToggleButton(menu_pane, "Scane = 1:500", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
(XtCallbackProc)timeSpeedCB, (XtPointer)500);

  XtSetArg(args[0], XmNsubMenuId, menu_pane);
  cascade = XmCreateCascadeButton(menu_bar, "Time", args, 1);
  XtManageChild(cascade);

  menu_pane = XmCreatePulldownMenu(menu_bar, "menu_pane", NULL, 0);
  XtSetArg(args[0], XmNset, cb_demo_mode); argcount = 1;
  button = XmCreateToggleButton(menu_pane, "Demo Mode", args, 1);
  XtManageChild(button);
  XtAddCallback(button, XmNvalueChangedCallback,
(XtCallbackProc)demo_modeCB, (XtPointer)0);
  button = XmCreatePushButton(menu_pane, "Reset viewer", NULL, 0);
  XtManageChild(button);
  XtAddCallback(button, XmNactivateCallback,
(XtCallbackProc)resetViewerCB, NULL);
  XtSetArg(args[0], XmNsubMenuId, menu_pane);
  cascade = XmCreateCascadeButton(menu_bar, "Misc", args, 1);
  XtManageChild(cascade);

  argcount = 0;
  XtSetArg(args[argcount], XmNmarginWidth, 0); argcount++;
  XtSetArg(args[argcount], XmNmarginHeight, 0); argcount++;
  XtSetArg(args[argcount], XmNshadowThickness, 1); argcount++;
  XtSetArg(args[argcount], XmNshadowType, XmSHADOW_OUT); argcount++;
  frame = XmCreateFrame(main_window, "frame", args, argcount);
  XtManageChild(frame);

  argcount = 0;
  vi = find_visual();
  if (vi) {
    XtSetArg(args[argcount], GLwNvisualInfo, vi); argcount++;
  }
  else {
    XtSetArg(args[argcount], GLwNrgba, TRUE); argcount++;
    XtSetArg(args[argcount], GLwNdepthSize, 1); argcount++;
    XtSetArg(args[argcount], GLwNdoublebuffer, TRUE); argcount++;
  }
  XtSetArg(args[argcount], XmNbottomAttachment, XmATTACH_FORM); argcount++;
  XtSetArg(args[argcount], XmNtopAttachment, XmATTACH_FORM); argcount++;
  XtSetArg(args[argcount], XmNleftAttachment, XmATTACH_FORM); argcount++;
  XtSetArg(args[argcount], XmNrightAttachment, XmATTACH_FORM); argcount++;
  glw = GLwCreateMDrawingArea(frame, "glwidget", args, argcount);
  XtManageChild(glw);
  XtAddCallback(glw, GLwNginitCallback, (XtCallbackProc)initCB, 0);
  XtAddCallback(glw, GLwNexposeCallback, (XtCallbackProc)exposeCB, 0);
  XtAddCallback(glw, GLwNresizeCallback, (XtCallbackProc)resizeCB, 0);
  XtAddCallback(glw, GLwNinputCallback, (XtCallbackProc)inputCB, 0);

  return main_window;
}

void _cdecl main(int argc, char **argv)
{
  Widget app_shell;
  Arg args[max_args];
  int argcount;

  XtToolkitInitialize();
  app_context = XtCreateApplicationContext();
  display = XtOpenDisplay(app_context, NULL, argv[0],
                          "XMdemos", NULL, 0, &argc, argv);
  if (!display) {
    XtWarning("Can't open display.");
    exit(0);
  }

  argcount = 0;
  XtSetArg(args[argcount], XmNmaxAspectX, 1); argcount++;
  XtSetArg(args[argcount], XmNmaxAspectY, 1); argcount++;
  XtSetArg(args[argcount], XmNminAspectX, 1); argcount++;
  XtSetArg(args[argcount], XmNminAspectY, 1); argcount++;
  app_shell =
    XtAppCreateShell(argv[0], "XMdemos", applicationShellWidgetClass,
                     display, args, argcount);

  create_widgets(app_shell);

  XtRealizeWidget(app_shell);

  XtAppMainLoop(app_context);

}


POINT.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/gl.h>

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

#ifdef WIN32
#include "stonehen.h"
#endif

#define POINT_EXTERN
#include "Point.h"

Point Point::rotate_abouty(GLfloat c, GLfloat s)
{
  val.pt[0] = c*pt[0] + s*pt[2];
  val.pt[1] = pt[1];
  val.pt[2] = -s*pt[0] + c*pt[2];
  return val;
}

Point Point::rotate_aboutz(GLfloat c, GLfloat s)
{
  val.pt[0] = c*pt[0] - s*pt[1];
  val.pt[1] = s*pt[0] + c*pt[1];
  val.pt[2] = pt[2];
  return val;
}

void Point::refract_self(Point light, Point N, GLfloat I)
{
  GLfloat t;
  Point dlight;

  dlight = refract_direction(light, N, I);
  t = -pt[2] / dlight.pt[2];
  pt[0] = pt[0] + dlight.pt[0]*t;
  pt[1] = pt[1] + dlight.pt[1]*t;
  pt[2] = 0;
}

Point Point::refract_direction(Point light, Point N, GLfloat I)
{
  GLfloat cos1, sin1, cos2, sin2, m;
  GLfloat dlight[3], dN[3], axis[3];

  /* dlight = (light - *this).unit() * -1.0; */
  dlight[0] = pt[0] - light.pt[0];
  dlight[1] = pt[1] - light.pt[1];
  dlight[2] = pt[2] - light.pt[2];
  m = sqrt(dlight[0]*dlight[0] + dlight[1]*dlight[1] + dlight[2]*dlight[2]);
  dlight[0] /= m;
  dlight[1] /= m;
  dlight[2] /= m;

  // dN = N * -1.0;
  dN[0] = -N.pt[0];
  dN[1] = -N.pt[1];
  dN[2] = -N.pt[2];

  // cos1 = dN.dot(dlight);
  cos1 = dN[0]*dlight[0] + dN[1]*dlight[1] + dN[2]*dlight[2];

  if (1.0 - cos1*cos1 < point_fudge) {
    val = dN;
    return val;
  }

  // axis = ((dN * dlight) * dN).unit();
  val.pt[0] = dN[1]*dlight[2] - dlight[1]*dN[2];
  val.pt[1] = dN[2]*dlight[0] - dlight[2]*dN[0];
  val.pt[2] = dN[0]*dlight[1] - dN[1]*dlight[0];
  axis[0] = val.pt[1]*dN[2] - dN[1]*val.pt[2];
  axis[1] = val.pt[2]*dN[0] - dN[2]*val.pt[0];
  axis[2] = val.pt[0]*dN[1] - val.pt[1]*dN[0];
  m = sqrt(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]);
  axis[0] /= m;
  axis[1] /= m;
  axis[2] /= m;

  if (axis[0]*axis[0] > point_fudge)
    sin1 = (dlight[0] - dN[0] * cos1) / axis[0];
  else if (axis[1]*axis[1] > point_fudge)
    sin1 = (dlight[1] - dN[1] * cos1) / axis[1];
  else sin1 = dlight[2] - dN[2] * cos1;

  sin2 = sin1 / I;
  cos2 = (sin1*sin1 < 1.0) ? sqrt(1.0 - sin2*sin2) : 0;

  dlight[0] = dN[0]*cos2 + axis[0]*sin2;
  dlight[1] = dN[1]*cos2 + axis[1]*sin2;
  dlight[2] = dN[2]*cos2 + axis[2]*sin2;

  /* I'm not sure this is quite legal */
  if (dlight[2] > 0.0) dlight[2] = -dlight[2];

  val = dlight;

  return val;
}

void Point::print()
{
  print("%f %f %f\n");
}

void Point::print(const char *format)
{
  printf(format, this->pt[0], this->pt[1], this->pt[2], 1.0);
}


POINT.H

#ifndef POINT_H
#define POINT_H

#ifndef POINT_EXTERN
#define POINT_EXTERN extern
#endif

#include <math.h>

const float point_fudge = .000001;

class Point {
 public:
  inline Point operator=(Point a);
  inline Point operator=(GLfloat *a);
  inline Point operator+(Point a);
  inline Point operator+=(Point a);
  inline Point operator-(Point a);
  // This takes a cross-product
  inline Point operator*(Point b);
  inline Point operator*(GLfloat b);
  inline Point operator*=(GLfloat b);
  inline Point operator/(GLfloat b);
  inline Point operator/=(GLfloat b);
  inline GLfloat& operator[](int index);

  inline void set(GLfloat x, GLfloat y, GLfloat z, GLfloat w = 1.0);

  inline GLfloat dist(Point b);
  inline GLfloat dot(Point b);
  inline GLfloat dot(GLfloat x, GLfloat y, GLfloat z);
  inline GLfloat mag();
  inline GLfloat magsquared();
  inline Point unit();
  inline void unitize();

  inline Point scale(Point p);

  // Angle is in RADIANS
  inline Point rotate(Point axis, GLfloat angle);
  inline Point rotate(Point axis, GLfloat c, GLfloat s);
  inline void rotate_self(Point axis, GLfloat c, GLfloat s);
  Point rotate_abouty(GLfloat c, GLfloat s);
  Point rotate_aboutz(GLfloat c, GLfloat s);

  // Returns point projected through proj_pt into XY plane
  // Does nothing if proj_pt - *this is parallel to XY plane
  inline Point project(Point proj_pt);
  inline void project_self(Point proj_pt);
  inline void Point::project_self(GLfloat px, GLfloat py, GLfloat pz);
  inline Point project_direction(Point direction);
  inline Point Point::project_direction(GLfloat x, GLfloat y, GLfloat z);
  // This projects (px, py, pz) into this in direction (dx, dy, dz)
  inline void Point::compute_projected(GLfloat px, GLfloat py, GLfloat pz,
 GLfloat x, GLfloat y, GLfloat z);

  // Returns point projected through light and refracted into XY
  // plane.
  // N is normal at point (ie normal at *this)
  // I is the index of refraction
  inline Point refract(Point light, Point N, GLfloat I);
  void refract_self(Point light, Point N, GLfloat I);
  Point refract_direction(Point light, Point N, GLfloat I);

  inline void glvertex();
  inline void glnormal();

  void print();
  void print(const char *format);

  GLfloat pt[4];
 private:
};

POINT_EXTERN Point val;

#define DOT(a, b) (a.pt[0]*b.pt[0] + a.pt[1]*b.pt[1] + a.pt[2]*b.pt[2])
#define THIS_DOT(b) (pt[0]*b.pt[0] + pt[1]*b.pt[1] + pt[2]*b.pt[2])

inline Point Point::operator=(Point a)
{
  pt[0] = a.pt[0];
  pt[1] = a.pt[1];
  pt[2] = a.pt[2];
  pt[3] = a.pt[3];
  return *this;
}

inline Point Point::operator=(GLfloat *a)
{
  pt[0] = a[0];
  pt[1] = a[1];
  pt[2] = a[2];
  pt[3] = 1;
  return *this;
}

inline Point Point::operator+(Point a)
{
  val.pt[0] = pt[0] + a.pt[0];
  val.pt[1] = pt[1] + a.pt[1];
  val.pt[2] = pt[2] + a.pt[2];
  return val;
}

inline Point Point::operator+=(Point a)
{
  pt[0] += a.pt[0];
  pt[1] += a.pt[1];
  pt[2] += a.pt[2];
  return *this;
}

inline Point Point::operator-(Point a)
{
  val.pt[0] = pt[0] - a.pt[0];
  val.pt[1] = pt[1] - a.pt[1];
  val.pt[2] = pt[2] - a.pt[2];
  return val;
}

inline Point Point::operator*(Point b)
{
  val.pt[0] = pt[1]*b.pt[2] - b.pt[1]*pt[2];
  val.pt[1] = pt[2]*b.pt[0] - b.pt[2]*pt[0];
  val.pt[2] = pt[0]*b.pt[1] - pt[1]*b.pt[0];
  return val;
}

inline Point Point::operator*(GLfloat b)
{
  val.pt[0] = pt[0] * b;
  val.pt[1] = pt[1] * b;
  val.pt[2] = pt[2] * b;
  return val;
}

inline Point Point::operator*=(GLfloat b)
{
  pt[0] *= b;
  pt[1] *= b;
  pt[2] *= b;
  return *this;
}

inline Point Point::operator/(GLfloat b)
{
  val.pt[0] = pt[0] / b;
  val.pt[1] = pt[1] / b;
  val.pt[2] = pt[2] / b;
  return val;
}

inline Point Point::operator/=(GLfloat b)
{
  pt[0] /= b;
  pt[1] /= b;
  pt[2] /= b;
  return *this;
}

inline GLfloat& Point::operator[](int index)
{
  return pt[index];
}

inline void Point::set(GLfloat x, GLfloat y, GLfloat z, GLfloat w)
{
  pt[0] = x;
  pt[1] = y;
  pt[2] = z;
  pt[3] = w;
}

inline GLfloat Point::dist(Point b)
{
  return (*this - b).mag();
}

inline GLfloat Point::dot(Point b)
{
  return pt[0]*b.pt[0] + pt[1]*b.pt[1] + pt[2]*b.pt[2];
}

inline GLfloat Point::dot(GLfloat x, GLfloat y, GLfloat z)
{
  return pt[0]*x + pt[1]*y + pt[2]*z;
}

inline GLfloat Point::mag()
{
  return sqrt(pt[0]*pt[0] + pt[1]*pt[1] +
              pt[2]*pt[2]);
}

inline GLfloat Point::magsquared()
{
  return pt[0]*pt[0] + pt[1]*pt[1] + pt[2]*pt[2];
}

inline Point Point::unit()
{
  GLfloat m = sqrt(pt[0]*pt[0] + pt[1]*pt[1] + pt[2]*pt[2]);
  val.pt[0] = pt[0] / m;
  val.pt[1] = pt[1] / m;
  val.pt[2] = pt[2] / m;
  return val;
}

inline void Point::unitize()
{
  GLfloat m = sqrt(pt[0]*pt[0] + pt[1]*pt[1] + pt[2]*pt[2]);
  pt[0] /= m;
  pt[1] /= m;
  pt[2] /= m;
}

inline Point Point::scale(Point p)
{
  val.pt[0] = pt[0] * p.pt[0];
  val.pt[1] = pt[1] * p.pt[1];
  val.pt[2] = pt[2] * p.pt[2];
  return val;
}

inline Point Point::rotate(Point axis, GLfloat angle)
{
  return rotate(axis, cos(angle), sin(angle));
}

inline Point Point::rotate(Point axis, GLfloat c, GLfloat s)
{
  float x = axis.pt[0], y = axis.pt[1], z = axis.pt[2], t = 1.0 - c;
  float tx, ty;

  tx = t*x;
  /* Taking advantage of inside info that this is a common case */
  if (y == 0.0) {
    val.pt[0] = pt[0]*(tx*x + c) + pt[1]*(-s*z) + pt[2]*(tx*z);
    val.pt[1] = pt[0]*(s*z) + pt[1]*c + pt[2]*(-s*x);
    val.pt[2] = pt[0]*(tx*z) + pt[1]*s*x + pt[2]*(t*z*z + c);
  } else {
    ty = t*y;
    val.pt[0] = pt[0]*(tx*x + c) + pt[1]*(tx*y - s*z) +
      pt[2]*(tx*z + s*y);
    val.pt[1] = pt[0]*(tx*y + s*z) + pt[1]*(ty*y + c) +
      pt[2]*(ty*z - s*x);
    val.pt[2] = pt[0]*(tx*z - s*y) + pt[1]*(ty*z + s*x) +
      pt[2]*(t*z*z + c);
  }
  return val;
}

inline void Point::rotate_self(Point axis, GLfloat c, GLfloat s)
{
  float Px, Py, Pz;
  float x = axis.pt[0], y = axis.pt[1], z = axis.pt[2], t = 1.0 - c;
  float tx, ty;

  tx = t*x;
  Px = pt[0];
  Py = pt[1];
  Pz = pt[2];
  /* Taking advantage of inside info that this is a common case */
  if (!y) {
    pt[0] = Px*(tx*x + c) +Py*(-s*z) + Pz*(tx*z);
    pt[1] = Px*(s*z) + Py*c + Pz*(-s*x);
    pt[2] = Px*(tx*z) + Py*s*x + Pz*(t*z*z + c);
  } else {
    ty = t*y;
    pt[0] = Px*(tx*x + c) + Py*(tx*y - s*z) +
      Pz*(tx*z + s*y);
    pt[1] = Px*(tx*y + s*z) + Py*(ty*y + c) +
      Pz*(ty*z - s*x);
    pt[2] = Px*(tx*z - s*y) + Py*(ty*z + s*x) +
      Pz*(t*z*z + c);
  }
}

inline void Point::glvertex()
{
  glVertex3fv(pt);
}

inline void Point::glnormal()
{
  glNormal3fv(pt);
}

inline Point Point::project(Point proj_pt)
{
  GLfloat dirx = pt[0] - proj_pt.pt[0],
      diry = pt[1] - proj_pt.pt[1],
      dirz = pt[2] - proj_pt.pt[2];
  GLfloat t;

  if (fabs(dirz) < point_fudge) val = *this;
  else {
    t = -proj_pt.pt[2] / dirz;
    val.pt[0] = proj_pt.pt[0] + dirx*t;
    val.pt[1] = proj_pt.pt[1] + diry*t;
    val.pt[2] = 0.0;
  }
  return val;
}

// This naively assumes that proj_pt[z] != this->pt[z]
inline void Point::project_self(Point proj_pt)
{
  GLfloat dirx = pt[0] - proj_pt.pt[0],
      diry = pt[1] - proj_pt.pt[1],
      dirz = pt[2] - proj_pt.pt[2];
  GLfloat t;

  t = -proj_pt.pt[2] / dirz;
  pt[0] = proj_pt.pt[0] + dirx*t;
  pt[1] = proj_pt.pt[1] + diry*t;
  pt[2] = 0.0;
}

inline void Point::project_self(GLfloat px, GLfloat py, GLfloat pz)
{
  GLfloat dirx = pt[0] - px,
  diry = pt[1] - py,
  dirz = pt[2] - pz, t;

  t = -pz / dirz;
  pt[0] = px + dirx*t;
  pt[1] = py + diry*t;
  pt[2] = 0.0;
}

inline Point Point::project_direction(Point direction) {
  GLfloat t;

  t = -pt[2] / direction.pt[2];
  val.pt[0] = pt[0] + direction.pt[0]*t;
  val.pt[1] = pt[1] + direction.pt[1]*t;
  val.pt[2] = 0;
  return val;
}

inline Point Point::project_direction(GLfloat x, GLfloat y, GLfloat z)
{
  GLfloat t;

  t = -pt[2] / z;
  val.pt[0] = pt[0] + x*t;
  val.pt[1] = pt[1] + y*t;
  val.pt[2] = 0;
  return val;
}

inline void Point::compute_projected(GLfloat px, GLfloat py, GLfloat pz,
     GLfloat dx, GLfloat dy, GLfloat dz)
{
  GLfloat t = -pz / dz;
  pt[0] = px + dx*t;
  pt[1] = py + dy*t;
  pt[2] = 0;
}

#undef POINT_EXTERN

#endif


RGBIMAGE.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <GL/gl.h>
#include "stonehen.h"

#define IMAGIC      0x01da
#define IMAGIC_SWAP 0xda01

#define SWAP_SHORT_BYTES(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
#define SWAP_LONG_BYTES(x) (((((x) & 0xff) << 24) | (((x) & 0xff00) << 8)) | \
                            ((((x) & 0xff0000) >> 8) | (((x) & 0xff000000) >> 24)))

typedef struct _rawImageRec {
    unsigned short imagic;
    unsigned short type;
    unsigned short dim;
    unsigned short sizeX, sizeY, sizeZ;
    unsigned long min, max;
    unsigned long wasteBytes;
    char name[80];
    unsigned long colorMap;
    FILE *file;
    unsigned char *tmp, *tmpR, *tmpG, *tmpB;
    unsigned long rleEnd;
    unsigned long *rowStart;
    unsigned long *rowSize;
} rawImageRec;

static rawImageRec *RawImageOpen(char *fileName)
{
    rawImageRec *raw;
    unsigned long *rowStart, *rowSize, ulTmp;
    int x;

    raw = (rawImageRec *)malloc(sizeof(rawImageRec));
    if (raw == NULL) {
//fprintf(stderr, "Out of memory!\n");
exit(-1);
    }
    if ((raw->file = fopen(fileName, "rb")) == NULL) {
perror(fileName);
exit(-1);
    }

    fread(raw, 1, 12, raw->file);

    if (raw->imagic == IMAGIC_SWAP) {
        raw->type = SWAP_SHORT_BYTES(raw->type);
        raw->dim = SWAP_SHORT_BYTES(raw->dim);
        raw->sizeX = SWAP_SHORT_BYTES(raw->sizeX);
        raw->sizeY = SWAP_SHORT_BYTES(raw->sizeY);
        raw->sizeZ = SWAP_SHORT_BYTES(raw->sizeZ);
    }
    raw->tmp = (unsigned char *)malloc(raw->sizeX*256);
    raw->tmpR = (unsigned char *)malloc(raw->sizeX*256);
    raw->tmpG = (unsigned char *)malloc(raw->sizeX*256);
    raw->tmpB = (unsigned char *)malloc(raw->sizeX*256);
    if (raw->tmp == NULL || raw->tmpR == NULL || raw->tmpG == NULL ||
raw->tmpB == NULL) {
//fprintf(stderr, "Out of memory!\n");
exit(-1);
    }

    if ((raw->type & 0xFF00) == 0x0100) {
x = raw->sizeY * raw->sizeZ * sizeof(long);
raw->rowStart = (unsigned long *)malloc(x);
raw->rowSize = (unsigned long *)malloc(x);
if (raw->rowStart == NULL || raw->rowSize == NULL) {
    //fprintf(stderr, "Out of memory!\n");
    exit(-1);
}
raw->rleEnd = 512 + (2 * x);
fseek(raw->file, 512, SEEK_SET);
fread(raw->rowStart, 1, x, raw->file);
fread(raw->rowSize, 1, x, raw->file);
        if (raw->imagic == IMAGIC_SWAP) {
            x /= sizeof(long);
            rowStart = raw->rowStart;
            rowSize = raw->rowSize;
            while (x--) {
                ulTmp = *rowStart;
                *rowStart++ = SWAP_LONG_BYTES(ulTmp);
                ulTmp = *rowSize;
                *rowSize++ = SWAP_LONG_BYTES(ulTmp);
            }
        }
    }
    return raw;
}

static void RawImageClose(rawImageRec *raw)
{

    fclose(raw->file);
    free(raw->tmp);
    free(raw->tmpR);
    free(raw->tmpG);
    free(raw->tmpB);
    free(raw);
}

static void RawImageGetRow(rawImageRec *raw, unsigned char *buf, int y, int z)
{
    unsigned char *iPtr, *oPtr, pixel;
    int count;

    if ((raw->type & 0xFF00) == 0x0100) {
fseek(raw->file, raw->rowStart[y+z*raw->sizeY], SEEK_SET);
fread(raw->tmp, 1, (unsigned int)raw->rowSize[y+z*raw->sizeY],
      raw->file);

iPtr = raw->tmp;
oPtr = buf;
while (1) {
    pixel = *iPtr++;
    count = (int)(pixel & 0x7F);
    if (!count) {
return;
    }
    if (pixel & 0x80) {
while (count--) {
    *oPtr++ = *iPtr++;
}
    } else {
pixel = *iPtr++;
while (count--) {
    *oPtr++ = pixel;
}
    }
}
    } else {
fseek(raw->file, 512+(y*raw->sizeX)+(z*raw->sizeX*raw->sizeY),
      SEEK_SET);
fread(buf, 1, raw->sizeX, raw->file);
    }
}

static void RawImageGetData(rawImageRec *raw, RGBImageRec *final)
{
    unsigned char *ptr;
    int i, j;

    final->data = (unsigned char *)malloc((raw->sizeX+1)*(raw->sizeY+1)*4);
    if (final->data == NULL) {
//fprintf(stderr, "Out of memory!\n");
exit(-1);
    }

    ptr = final->data;
    for (i = 0; i < raw->sizeY; i++) {
RawImageGetRow(raw, raw->tmpR, i, 0);
RawImageGetRow(raw, raw->tmpG, i, 1);
RawImageGetRow(raw, raw->tmpB, i, 2);
for (j = 0; j < raw->sizeX; j++) {
    *ptr++ = *(raw->tmpR + j);
    *ptr++ = *(raw->tmpG + j);
    *ptr++ = *(raw->tmpB + j);
}
    }
}

RGBImageRec *RGBImageLoad(char *fileName)
{
    rawImageRec *raw;
    RGBImageRec *final;

    raw = RawImageOpen(fileName);
    final = (RGBImageRec *)malloc(sizeof(RGBImageRec));
    if (final == NULL) {
//fprintf(stderr, "Out of memory!\n");
exit(-1);
    }
    final->sizeX = raw->sizeX;
    final->sizeY = raw->sizeY;
    RawImageGetData(raw, final);
    RawImageClose(raw);
    return final;
}


RING.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/glu.h>

#ifdef X11
#include <GL/glx.h>
#endif

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

#ifdef WIN32
#include "stonehen.h"
#endif

#include "Ring.h"

inline float radians(float a) {return a * M_PI / 180.0;};

Ring::Ring()
{
  Point sar_dim, lin_dim;

  radius = 10;

  nstones = 30;

  sar_dim.set(.2, .5, 1);
  sarcen.set_dimensions(sar_dim);
  sarcen.translate(radius, 0, sar_dim.pt[2]);

  angle = 360.0 / (float)nstones;

  lin_dim.set(.2, .99 * tan(2.0 * M_PI / (float)nstones) *
      (radius - sar_dim.pt[0]) / 2.0, .2);
  lintel.set_dimensions(lin_dim);
  lintel.translate(radius, 0, 2.*sar_dim.pt[2] + lin_dim.pt[2]);
}

 Ring::~Ring()
{
}

void Ring::erode(float p)
{
  sarcen.erode(p);
  lintel.erode(p);
}

void Ring::draw()
{
  draw_sarcens();
  draw_lintels();
}

void Ring::draw_sarcens()
{
  int i;
  for (i = 0; i < nstones; i++) {
    glPushMatrix();
    glRotatef(i * angle, 0, 0, 1);
    sarcen.draw();
    glPopMatrix();
  }
}

void Ring::draw_lintels()
{
  int i;
  glPushMatrix();
  glRotatef(angle / 2.0, 0, 0, 1);
  for (i = 0; i < nstones; i++) {
    glPushMatrix();
    glRotatef(i * angle, 0, 0, 1);
    lintel.draw();
    glPopMatrix();
  }
  glPopMatrix();
}

void Ring::draw_shadow(Point dlight, GLfloat blur,
       Color color, Color diffuse)
{
  draw_sarcens_shadows(dlight, blur, color, diffuse);
  draw_lintels_shadows(dlight, blur, color, diffuse);
}

void Ring::draw_sarcens_shadows(Point dlight, GLfloat blur,
Color color, Color diffuse)
{
  int i;
  Stone proto;

  proto = sarcen;
  for (i = 0; i < nstones; i++) {
    proto.rotate_self_aboutz(angle);
    proto.draw_shadow(dlight, blur, color, diffuse);
  }
}

void Ring::draw_lintels_shadows(Point dlight, GLfloat blur,
Color color, Color diffuse)
{
  int i;
  Stone proto;

  proto = lintel;

  proto.rotate_self_aboutz(angle / 2.0);

  for (i = 0; i < nstones; i++) {
    proto.rotate_self_aboutz(angle);
    proto.draw_shadow(dlight, blur, color, diffuse);
  }
}


RING.H

#ifndef RING_H
#define RING_H

#include "Stone.h"

class Ring {
 public:
  Ring();
  ~Ring();

  void erode(float p);

  void draw();
  void draw_shadow(Point dlight, GLfloat blur = 0.0,
   Color color = black, Color diffuse = black);

 private:
  GLfloat radius, angle;
  int nstones;

  Stone sarcen;
  Stone lintel;

  void draw_sarcens();
  void draw_lintels();

  void draw_sarcens_shadows(Point dlight, GLfloat blur,
    Color color, Color diffuse);
  void draw_lintels_shadows(Point dlight, GLfloat blur,
    Color color, Color diffuse);
};

#endif


ROUNDWAL.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/glu.h>

#ifdef X11
#include <GL/glx.h>
#endif

#include <math.h>

#ifdef WIN32
#include "stonehen.h"
#endif

#include "Roundwal.h"

Roundwall::Roundwall()
{
  height = 2;
  radius = 20;
  divisions = 20;
  sint = cost = NULL;
}

void Roundwall::draw()
{
  float texx = 0;
  int i;
  if (sint == NULL) compute_tables();
  glBegin(GL_QUAD_STRIP);
  for (i = 0; i <= divisions; i++, texx += 1.) {
    glTexCoord2f(texx, 0);
    glNormal3f(-cost[i], -sint[i], 0);
    glVertex3f(radius * cost[i], radius * sint[i], 0);
    glTexCoord2f(texx, 1);
    glVertex3f(radius * cost[i], radius * sint[i], height);
  }
  glEnd();
}

void Roundwall::set_divisions(int d)
{
  if (divisions != d) delete_tables();
  divisions = d;
}

void Roundwall::delete_tables()
{
  delete sint;
  delete cost;
  sint = cost = NULL;
}

void Roundwall::compute_tables()
{
  int i;
  float t, dt;

  delete_tables();
  cost = new float[divisions + 1];
  sint = new float[divisions + 1];

  dt = 2. * M_PI / divisions;
  for (i = 0, t = 0.; i <= divisions; i++, t += dt) {
    cost[i] = cos(t);
    sint[i] = sin(t);
  }
}


ROUNDWAL.H

#ifndef ROUNDWALL_H
#define ROUNDWALL_H

class Roundwall {
 public:
  Roundwall();
  ~Roundwall() {};

  void draw();

  void set_divisions(int d);

  float height;
  float radius;

 private:
  int divisions;
  float *sint, *cost;

  void delete_tables();
  void compute_tables();
};

#endif


SCENE.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

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

#include <GL/glu.h>
#include <GL/glaux.h>

/*
#ifdef X11
#include <GL/glx.h>
extern "C" {
#include <tk.h>
};
#endif
*/

#ifdef WIN32
#include "stonehen.h"
#endif

#include "Point.h"
#include "Ring.h"
#include "Roundwal.h"
#include "Ellipse.h"
#include "Telescop.h"

#define SCENE_EXTERN
#include "scene.h"

GLfloat mat_view[16];
GLfloat view_rotx = 0;
GLfloat fov = 45.0, aspect = 1.0;
static Point eyep = {0, 0, .5};
static Point lookp = {0.05, 1, .25};

TimeDate current_time;

static int list_ground;
static int list_texture_ground;
static int list_trees;
static int list_texture_trees;
static int list_ring;
static int list_ellipse;
static int list_texture_stones;
static int list_shadows;
static int list_telescope;
static int list_texture_telescope;
int draw_ground = 1;
int draw_trees = 0;
int draw_ring = 1;
int draw_ellipse = 1;
int draw_shadows = 0;

int use_lighting = 1;
int use_textures = 0;
int texture_hack = 0;//HACK HACK HACK - only texture map the stone
int use_normal_fog = 0;
int use_fancy_fog = 0;
int use_telescope = 0;
int use_antialias = 0;

static void scene_identity();
static void scene_project(GLfloat f = fov, float dx = 0, float dy = 0);
static void scene_draw(int rend = 1);
static void scene_render_telescope();

static void draw_background();

Point sun_position = {0., .707, .707, 0.};
Color ambient(.25, .25, .25, 1.);
static void lights_init();

static void lists_init();

static void ground_list_init();
static void ground_draw();

Roundwall trees;
static void trees_list_init();
static void trees_draw();

Ring ring;
static void ring_list_init();
static void ring_draw();

EllipseSt ellipse;
static void ellipse_list_init();
static void ellipse_draw();

static void shadows_list_init();
static void shadows_draw();

Weather weather;

Telescope telescope;
GLfloat magnif = .5;
static void telescope_list_init();
static void telescope_draw();

/* Read the back buffer into the accum buffer, adding in the appropriate
 * alpha values */
static void fog_read_image();
/* Add the accum buffer to the back buffer */
static void fog_write_image();

inline float clamp(float x, float min, float max)
{
  if (x < min) return min;
  else if (x > max) return max;
  else return x;
}

void scene_init()
{

  scene_identity();
  scene_viewer_center();

  glEnable(GL_CULL_FACE);
  glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

  glEnable(GL_NORMALIZE);

  /* Initial time will be four in the afternoon */
  scene_set_time(TimeDate(16, 0));
  scene_set_weather(weathers[def_weather_index]);

  lights_init();
  lists_init();
}

inline double time_minutes(int hour, int minute, int second)
{
  return (double)hour*60.0 + (double)minute + (double)second / 60.0;
}

static void scene_time_changed()
{
  sun_position = current_time.sun_direction();

  weather.apply(sun_position);

  lights_init();
  shadows_list_init();
}

void scene_set_time(TimeDate t)
{
  current_time = t;
  scene_time_changed();
}

void scene_inc_time(TimeDate t)
{
  current_time += t;
  scene_time_changed();
}

/* This is a hack -- has to be called several times to get the antialiasing
 * to work */
static void scene_inner_render(GLfloat dx = 0, GLfloat dy = 0)
{
  /* This draws layered fog if the use_fancy_fog flag is on --
   * it's going to be slow on anything but high-end stuff */
  if (use_fancy_fog && weather.fog_density != 0.) {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glEnable(GL_FOG);
    draw_background();
    scene_project(fov, dx, dy);
    glClear(GL_DEPTH_BUFFER_BIT);
    if (use_lighting) glEnable(GL_LIGHTING);
    scene_draw();
    if (use_lighting) glDisable(GL_LIGHTING);
    fog_read_image();
    glDisable(GL_FOG);
  } else
    if (use_normal_fog && weather.fog_density != 0.) glEnable(GL_FOG);
    else glDisable(GL_FOG);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  /* This is the part where we actually draw the image */
  glClear(GL_DEPTH_BUFFER_BIT);
  draw_background();
  scene_project(fov, dx, dy);
  glClear(GL_DEPTH_BUFFER_BIT);
  if (use_lighting) glEnable(GL_LIGHTING);
  scene_draw();
  if (use_lighting) glDisable(GL_LIGHTING);

  if (use_fancy_fog && weather.fog_density != 0.) {
    fog_write_image();
  }

  if (use_telescope) scene_render_telescope();
}

void scene_render()
{
  GLint vp[4];

  scene_inner_render();

  if (!use_antialias) return;

  if (use_fancy_fog) {
    //fprintf(stderr, "Cannot antialias while using fancy fog.\n");
    return;
  }

  glGetIntegerv(GL_VIEWPORT, vp);
  glAccum(GL_LOAD, .5);

  scene_inner_render(2. / (float)vp[2], 2. / (float)vp[3]);
  glAccum(GL_ACCUM, .5);

/*
  scene_inner_render(-2. / (float)vp[2], -2. / (float)vp[3]);
  glAccum(GL_ACCUM, .25);
*/
  glAccum(GL_RETURN, 1);
/*
  glDrawBuffer(GL_BACK);
  glFlush();
*/
}

static void scene_render_telescope()
{
  telescope.draw_setup(fov, aspect);

  /* Don't fog the telescope - moisture makes it rust.
   * Seriously, it's in a strange coordinate system and fog will look
   * bad on it. */
  glPushAttrib(GL_ENABLE_BIT);
  glDisable(GL_FOG);
  if (use_textures) {
    glCallList(list_texture_telescope);
    glEnable(GL_TEXTURE_2D);
  }
  glCallList(list_telescope);
  glPopAttrib();

  if (use_lighting) glEnable(GL_LIGHTING);
  glEnable(GL_STENCIL_TEST);
  glClearStencil(0);
  glClear(GL_STENCIL_BUFFER_BIT);
  glStencilFunc(GL_ALWAYS, 0x1, 0x1);
  glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

  glDisable(GL_CULL_FACE);
  telescope.draw_lens();
  glEnable(GL_CULL_FACE);

  telescope.draw_takedown();

  if (use_lighting) glDisable(GL_LIGHTING);

  glStencilFunc(GL_NOTEQUAL, 0x0, 0xffffffff);
  glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

  scene_identity();

  draw_background();

  glMatrixMode(GL_PROJECTION);
  glTranslatef(telescope.xpos / magnif, -telescope.ypos / magnif, 0);
  scene_project(fov * magnif);

  glClear(GL_DEPTH_BUFFER_BIT);

  /* Pushing the lighting bit used to do really bad things, but
   * hopefully they've all gone away */
  glPushAttrib(GL_LIGHTING_BIT);
  lights_init();
  if (use_lighting) glEnable(GL_LIGHTING);
  scene_draw();
  if (use_lighting) glDisable(GL_LIGHTING);
  glPopAttrib();

  glDisable(GL_STENCIL_TEST);
}

static void scene_identity()
{
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

static void scene_project(GLfloat f, float dx, float dy)
{
  glMatrixMode(GL_PROJECTION);
  glOrtho(-1 - dx, 1, -1 - dy, 1, 0, -1);
  gluPerspective(f, aspect, 0.01, 40.0);
  glMatrixMode(GL_MODELVIEW);
  glRotatef(view_rotx, 1, 0, 0);
  gluLookAt(eyep.pt[0], eyep.pt[1], eyep.pt[2],
    lookp.pt[0], lookp.pt[1], lookp.pt[2],
            0, 0, 1);
  glMultMatrixf(mat_view);
lights_init();
}

/* scene_draw() just draws the geometry - it's used for rendering and
 * picking. */
static void scene_draw(int rend)
{
  if (draw_ground) {
    if (rend) {
      if (use_textures) {
glCallList(list_texture_ground);
glEnable(GL_TEXTURE_2D);
      } else glEnable(GL_COLOR_MATERIAL);
    }
    glCallList(list_ground);

    if (rend) {
      glDisable(GL_TEXTURE_2D);
      glDisable(GL_COLOR_MATERIAL);
    }
  }

  if (draw_shadows) {
    if (use_textures && rend && !draw_ground) {
      glCallList(list_texture_ground);
      glEnable(GL_TEXTURE_2D);
    }
    glCallList(list_shadows);
    if (use_textures && rend) glDisable(GL_TEXTURE_2D);
  }

  if (draw_trees) {
    if (use_textures && rend) {
      glCallList(list_texture_trees);
      glEnable(GL_TEXTURE_2D);
    }
    glCallList(list_trees);
    if (use_textures && rend) glDisable(GL_TEXTURE_2D);
  }

  glClear(GL_DEPTH_BUFFER_BIT);

  if (draw_ring) {
    if (rend) {
      if (use_textures || texture_hack) {
glCallList(list_texture_stones);
glEnable(GL_TEXTURE_2D);
      }
      glEnable(GL_COLOR_MATERIAL);
      glColor3f(.5, .5, .5);
    }
    glCallList(list_ring);
    if (rend) {
      if (use_textures || texture_hack) glDisable(GL_TEXTURE_2D);
      glDisable(GL_COLOR_MATERIAL);
    }
  }

  if (draw_ellipse) {
    if (use_textures && rend) {
      // Hack to avoid doing something expensive twice in a row
      if (!draw_ring) glCallList(list_texture_stones);
      glEnable(GL_TEXTURE_2D);
    }
    glCallList(list_ellipse);
    if (use_textures && rend) glDisable(GL_TEXTURE_2D);
  }
}

static void draw_background()
{
  weather.draw_sky(sun_position);
}

void scene_viewer_center()
{
  glPushMatrix();
  glLoadIdentity();
  glGetFloatv(GL_MODELVIEW_MATRIX, mat_view);
  glPopMatrix();
  view_rotx = 0;
}

void scene_viewer_rotate_worldz(GLfloat degrees)
{
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glRotatef(degrees, 0, 0, 1);
  glMultMatrixf(mat_view);
  glGetFloatv(GL_MODELVIEW_MATRIX, mat_view);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
}

void scene_viewer_rotatez(GLfloat degrees)
{
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glRotatef(degrees, 0, 0, 1);
  glMultMatrixf(mat_view);
  glGetFloatv(GL_PROJECTION_MATRIX, mat_view);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
}

void scene_viewer_rotatex(GLfloat degrees)
{
  view_rotx += degrees;
  view_rotx = clamp(view_rotx, -60, 60);
  scene_identity();
  scene_project();
  lights_init();
}

void scene_viewer_translate(GLfloat dist)
{
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glTranslatef(0, dist, 0);
  glMultMatrixf(mat_view);
  glGetFloatv(GL_PROJECTION_MATRIX, mat_view);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
  scene_identity();
  scene_project();
  lights_init();
}

void scene_position_telescope(GLfloat x, GLfloat y)
{
  telescope.xpos = x;
  telescope.ypos = y;
}

void scene_get_position_telescope(GLfloat *x, GLfloat *y)
{
  *x = telescope.xpos;
  *y = telescope.ypos;
}

void scene_get_radius_telescope(GLfloat *r)
{
  *r = telescope.get_radius();
}

void scene_set_weather(Weather w)
{
  weather = w;
  weather.apply(sun_position);
  shadows_list_init();
}

static int get_lists(int size)
{
  int i;
  i = glGenLists(size);
  if (size && !i) {
    //fprintf(stderr, "Unable to allocate %d display lists.\n");
    exit(1);
  }
  return i;
}

static void lights_init()
{
  glLightfv(GL_LIGHT0, GL_POSITION, sun_position.pt);

  /* This light gives a diffuse coefficient when the sun is off -
   * it's used for drawing shadows */
  glLightfv(GL_LIGHT1, GL_AMBIENT, black.c);
  glLightfv(GL_LIGHT1, GL_DIFFUSE, black.c);
  glLightfv(GL_LIGHT1, GL_SPECULAR, black.c);
}

#ifdef TEXTURE
static void textures_list_init()
{
  AUX_RGBImageRec *teximage = NULL;

  teximage = auxRGBImageLoad((char *)texfile_stones);
  glNewList(list_texture_stones, GL_COMPILE);
  gluBuild2DMipmaps(GL_TEXTURE_2D, 3, teximage->sizeX, teximage->sizeY,
    GL_RGB, GL_UNSIGNED_BYTE, teximage->data);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
 GL_NEAREST_MIPMAP_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
 GL_LINEAR);
  glMatrixMode(GL_TEXTURE);
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glDisable(GL_TEXTURE_GEN_S);
  glDisable(GL_TEXTURE_GEN_T);
  glEndList();

  /* tk is obnoxious and doesn't seem to provide any mechanism for this */
  free(teximage->data);
  free(teximage);

  teximage = auxRGBImageLoad((char *)texfile_ground);
  glNewList(list_texture_ground, GL_COMPILE);
  gluBuild2DMipmaps(GL_TEXTURE_2D, 3, teximage->sizeX, teximage->sizeY,
    GL_RGB, GL_UNSIGNED_BYTE, teximage->data);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
  GL_NEAREST_MIPMAP_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
  GL_LINEAR);
  glMatrixMode(GL_TEXTURE);
  glLoadIdentity();
  glScalef(100, 100, 1);
  glMatrixMode(GL_MODELVIEW);
  glDisable(GL_TEXTURE_GEN_S);
  glDisable(GL_TEXTURE_GEN_T);
  glEndList();

  free(teximage->data);
  free(teximage);

  /* Figure out some way to get an alpha component out of the tk --
   * otherwise we're really hosed */
  teximage = auxRGBImageLoad((char *)texfile_trees);
  glNewList(list_texture_trees, GL_COMPILE);
  /* In the final scenerio we probably won't want to mipmap this, but it's
   * not square and I don't feel like bothering to scale it */
  gluBuild2DMipmaps(GL_TEXTURE_2D, 3, teximage->sizeX, teximage->sizeY,
    GL_RGB, GL_UNSIGNED_BYTE, teximage->data);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
  GL_NEAREST_MIPMAP_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
  GL_LINEAR);
  glMatrixMode(GL_TEXTURE);
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glDisable(GL_TEXTURE_GEN_S);
  glDisable(GL_TEXTURE_GEN_T);
  glEndList();

  free(teximage->data);
  free(teximage);

  teximage = auxRGBImageLoad((char *)texfile_telescope);
  glNewList(list_texture_telescope, GL_COMPILE);
  glTexImage2D(GL_TEXTURE_2D, 0, 3, teximage->sizeX, teximage->sizeY,
       0, GL_RGB, GL_UNSIGNED_BYTE, teximage->data);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glMatrixMode(GL_TEXTURE);
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
  glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
  glEnable(GL_TEXTURE_GEN_S);
  glEnable(GL_TEXTURE_GEN_T);
  glEndList();

  free(teximage->data);
  free(teximage);
}
#endif

static void lists_init()
{
  list_ground = get_lists(1);
  list_texture_ground = get_lists(1);
  list_trees = get_lists(1);
  list_texture_trees = get_lists(1);
  list_ring = get_lists(1);
  list_ellipse = get_lists(1);
  list_texture_stones = get_lists(1);
  list_shadows = get_lists(1);
  list_telescope = get_lists(1);
  list_texture_telescope = get_lists(1);

  ground_list_init();
  trees_list_init();
  shadows_list_init();
  ring_list_init();
  ellipse_list_init();
#ifdef TEXTURE
  textures_list_init();
#endif
  telescope_list_init();
}

static void ground_list_init()
{
  glNewList(list_ground, GL_COMPILE);
  ground_draw();
  glEndList();
}

static void ground_draw()
{
  glColor3f(0, .75, 0);

  glLoadName(name_ground);

  glNormal3f(0, 0, 1);

  glPushMatrix();
  /* Making something this big would confuse the zbuffer, but we're
   * clearing that AFTER drawing this, so it's ok */
  glScalef(100, 100, 1);
  glBegin(GL_QUADS);
  glTexCoord2f(0, 0);
  glVertex2f(-1, -1);
  glTexCoord2f(1, 0);
  glVertex2f(1, -1);
  glTexCoord2f(1, 1);
  glVertex2f(1, 1);
  glTexCoord2f(0, 1);
  glVertex2f(-1, 1);
  glEnd();
  glPopMatrix();
}

static void trees_list_init()
{
  glNewList(list_trees, GL_COMPILE);
  trees_draw();
  glEndList();
}

static void trees_draw()
{
  glEnable(GL_COLOR_MATERIAL);
  glColor3f(0, .5, 0);

  glLoadName(name_trees);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  trees.draw();
  glDisable(GL_BLEND);

  glDisable(GL_COLOR_MATERIAL);
}

static void ring_list_init()
{
  glNewList(list_ring, GL_COMPILE);
  ring_draw();
  glEndList();
}

static void ring_draw()
{
  glLoadName(name_ring);

  glEnable(GL_DEPTH_TEST);
  ring.erode(.1);
  ring.draw();
  glDisable(GL_DEPTH_TEST);
}

static void ellipse_list_init()
{
  glNewList(list_ellipse, GL_COMPILE);
  ellipse_draw();
  glEndList();
}

static void ellipse_draw()
{
  glEnable(GL_COLOR_MATERIAL);
  glColor3f(.5, .5, .5);

  glEnable(GL_DEPTH_TEST);

  glLoadName(name_ellipse);

  ellipse.erode(.1);
  ellipse.draw();

  glDisable(GL_DEPTH_TEST);

  glDisable(GL_COLOR_MATERIAL);
}

static void shadows_list_init()
{
  glNewList(list_shadows, GL_COMPILE);
  shadows_draw();
  glEndList();
}

static void shadows_draw()
{
  Color grass(0, .75, 0);

  glPushAttrib(GL_ENABLE_BIT);

  /* Turn the sun off */
  glDisable(GL_LIGHT0);
  glEnable(GL_LIGHT1);
  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, grass.c);
  glColor3fv((grass * .5).c);

  glDisable(GL_CULL_FACE);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEnable(GL_BLEND);
  ring.draw_shadow(sun_position, weather.shadow_blur(),
   grass * .5, grass);
  ellipse.draw_shadow(sun_position, weather.shadow_blur(),
      grass * .5, grass);
  glPopAttrib();
}

static void telescope_list_init()
{
  glNewList(list_telescope, GL_COMPILE);
  telescope_draw();
  glEndList();
}

static void telescope_draw()
{
  glLoadName(name_telescope);
  glEnable(GL_COLOR_MATERIAL);
  glDisable(GL_CULL_FACE);
  glClear(GL_DEPTH_BUFFER_BIT);
  glEnable(GL_DEPTH_TEST);
  telescope.draw_body();
  glDisable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
  glDisable(GL_COLOR_MATERIAL);
}

static void fog_read_image()
{
  glPushMatrix();
  glLoadIdentity();

  /* This creates an alpha gradient across the image */
/*  glColorMask(0, 0, 0, 1);
  glBegin(GL_QUADS);
  glColor4f(1, 1, 1, 1);
  glVertex2f(-1, -1);
  glVertex2f(1, -1);
  glColor4f(1, 1, 1, 0);
  glVertex2f(1, 1);
  glVertex2f(-1, 1);
  glEnd();
  glColorMask(1, 1, 1, 1);
*/
  glDrawBuffer(GL_BACK);
  glReadBuffer(GL_BACK);
  glAccum(GL_LOAD, 1);

  glPopMatrix();
}

static void fog_write_image()
{
  glDrawBuffer(GL_BACK);

  /* Put this back in once we're done testing */
//  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glAccum(GL_RETURN, 1);
  glDisable(GL_BLEND);
}


SCENE.H

#ifndef SCENE_H
#define SCENE_H

#ifndef SCENE_EXTERN
#define SCENE_EXTERN extern
#endif

#include "TimeDate.h"
#include "atmosphe.h"

void scene_init();

void scene_set_time(TimeDate t);
void scene_inc_time(TimeDate t);

void scene_render();

void scene_viewer_center();
/* This rotates about the origin in the world coordinate system */
void scene_viewer_rotate_worldz(GLfloat degrees);
/* This rotates about the z axis */
void scene_viewer_rotatez(GLfloat degrees);
void scene_viewer_rotatex(GLfloat degrees);
/* This translates in y */
void scene_viewer_translate(GLfloat dist);

void scene_position_telescope(GLfloat x, GLfloat y);
void scene_get_position_telescope(GLfloat *x, GLfloat *y);
void scene_get_radius_telescope(GLfloat *r);

void scene_set_weather(Weather w);

const GLint name_background = 0;
const GLint name_ground = 1;
const GLint name_trees = 2;
const GLint name_ring = 3;
const GLint name_ellipse = 4;
const GLint name_telescope = 5;

extern int draw_ground;
extern int draw_trees;
extern int draw_ring;
extern int draw_ellipse;
extern int draw_shadows;

extern int use_lighting;
extern int use_textures;
extern int use_normal_fog;
extern int use_fancy_fog;
extern int use_telescope;
extern int use_antialias;

extern GLfloat fov, aspect;

const char texfile_ground[] = DATADIR "grass.rgb";
const char texfile_trees[] = DATADIR "treewall.rgb";
const char texfile_stones[] = DATADIR "marble.rgb";
const char texfile_telescope[] = DATADIR "cv.rgb";

extern Weather weather;

#undef SCENE_EXTERN
#endif


SETPIXEL.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/gl.h>
#include "stonehen.h"
#include "setpixel.h"

#define BLACK   PALETTERGB(0,0,0)
#define WHITE   PALETTERGB(255,255,255)
#define NUM_STATIC_COLORS   (COLOR_BTNHIGHLIGHT - COLOR_SCROLLBAR + 1)

static COLORREF gacrSave[NUM_STATIC_COLORS];

static COLORREF gacrBlackAndWhite[NUM_STATIC_COLORS] = {
    WHITE,  // COLOR_SCROLLBAR
    BLACK,  // COLOR_BACKGROUND
    BLACK,  // COLOR_ACTIVECAPTION
    WHITE,  // COLOR_INACTIVECAPTION
    WHITE,  // COLOR_MENU
    WHITE,  // COLOR_WINDOW
    BLACK,  // COLOR_WINDOWFRAME
    BLACK,  // COLOR_MENUTEXT
    BLACK,  // COLOR_WINDOWTEXT
    WHITE,  // COLOR_CAPTIONTEXT
    WHITE,  // COLOR_ACTIVEBORDER
    WHITE,  // COLOR_INACTIVEBORDER
    WHITE,  // COLOR_APPWORKSPACE
    BLACK,  // COLOR_HIGHLIGHT
    WHITE,  // COLOR_HIGHLIGHTTEXT
    WHITE,  // COLOR_BTNFACE
    BLACK,  // COLOR_BTNSHADOW
    BLACK,  // COLOR_GRAYTEXT
    BLACK,  // COLOR_BTNTEXT
    BLACK,  // COLOR_INACTIVECAPTIONTEXT
    BLACK   // COLOR_BTNHIGHLIGHT
    };
static INT gaiStaticIndex[NUM_STATIC_COLORS] = {
    COLOR_SCROLLBAR          ,
    COLOR_BACKGROUND         ,
    COLOR_ACTIVECAPTION      ,
    COLOR_INACTIVECAPTION    ,
    COLOR_MENU               ,
    COLOR_WINDOW             ,
    COLOR_WINDOWFRAME        ,
    COLOR_MENUTEXT           ,
    COLOR_WINDOWTEXT         ,
    COLOR_CAPTIONTEXT        ,
    COLOR_ACTIVEBORDER       ,
    COLOR_INACTIVEBORDER     ,
    COLOR_APPWORKSPACE       ,
    COLOR_HIGHLIGHT          ,
    COLOR_HIGHLIGHTTEXT      ,
    COLOR_BTNFACE            ,
    COLOR_BTNSHADOW          ,
    COLOR_GRAYTEXT           ,
    COLOR_BTNTEXT            ,
    COLOR_INACTIVECAPTIONTEXT,
    COLOR_BTNHIGHLIGHT
    };

HPALETTE    ghpalOld, ghPalette = (HPALETTE) 0;
BOOL        bUseStaticColors = FALSE;
BOOL        bSystemColorsInUse = FALSE;
BOOL        bStaticSaved = FALSE;

unsigned char threeto8[8] = {
    0, 0111>>1, 0222>>1, 0333>>1, 0444>>1, 0555>>1, 0666>>1, 0377
};

unsigned char twoto8[4] = {
    0, 0x55, 0xaa, 0xff
};

unsigned char oneto8[2] = {
    0, 255
};

unsigned char
ComponentFromIndex(UCHAR i, UINT nbits, UINT shift)
{
    unsigned char val;

    val = i >> shift;
    switch (nbits) {

    case 1:
        val &= 0x1;
        return oneto8[val];

    case 2:
        val &= 0x3;
        return twoto8[val];

    case 3:
        val &= 0x7;
        return threeto8[val];

    default:
        return 0;
    }
}

void
CreateRGBPalette(HDC hdc)
{
    PIXELFORMATDESCRIPTOR pfd, *ppfd;
    LOGPALETTE *pPal;
    int n, i;

    ppfd = &pfd;
    n = GetPixelFormat(hdc);
    DescribePixelFormat(hdc, n, sizeof(PIXELFORMATDESCRIPTOR), ppfd);

    if (ppfd->dwFlags & PFD_NEED_PALETTE) {
        n = 1 << ppfd->cColorBits;
        pPal = (PLOGPALETTE)LocalAlloc(LMEM_FIXED, sizeof(LOGPALETTE) +
                n * sizeof(PALETTEENTRY));
        pPal->palVersion = 0x300;
        pPal->palNumEntries = n;
        for (i=0; i<n; i++) {
            pPal->palPalEntry[i].peRed =
                    ComponentFromIndex(i, ppfd->cRedBits, ppfd->cRedShift);
            pPal->palPalEntry[i].peGreen =
                    ComponentFromIndex(i, ppfd->cGreenBits, ppfd->cGreenShift);
            pPal->palPalEntry[i].peBlue =
                    ComponentFromIndex(i, ppfd->cBlueBits, ppfd->cBlueShift);
            pPal->palPalEntry[i].peFlags = 0;
        }

        if (256 == n)
        {
            if (bUseStaticColors = ppfd->dwFlags & PFD_NEED_SYSTEM_PALETTE)
            {
                // Prepare to take over the system's static colors

                for (i = 0; i < n; i++)
                    pPal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
            }
        }

        ghPalette = CreatePalette(pPal);
        LocalFree(pPal);

        //ghpalOld = SelectPalette(hdc, ghPalette, FALSE);
        //n = RealizePalette(hdc);
        n = RealizePaletteNow(hdc, ghPalette, FALSE);
    }
}

BOOL bSetupPixelFormat(HDC hdc)
{
    PIXELFORMATDESCRIPTOR pfd, *ppfd;
    int pixelformat;

    ppfd = &pfd;

    ppfd->nSize = sizeof(PIXELFORMATDESCRIPTOR);
    ppfd->nVersion = 1;
    ppfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    ppfd->dwLayerMask = PFD_MAIN_PLANE;

    ppfd->iPixelType = PFD_TYPE_RGBA;
    ppfd->cColorBits = 24;

    ppfd->cDepthBits = 16;//GLX_DEPTH_SIZE

    ppfd->cRedBits = 1;//GLX_RED_SIZE
    ppfd->cRedShift = 0;
    ppfd->cGreenBits = 1;//GLX_GREEN_SIZE
    ppfd->cGreenShift = 0;
    ppfd->cBlueBits = 1;//GLX_BLUE_SIZE
    ppfd->cBlueShift = 0;
    ppfd->cAlphaBits = 0;
    ppfd->cAlphaShift = 0;
    ppfd->cAccumBits = 0;//ACCUM NOT SUPPORTED
    ppfd->cAccumRedBits = 0; //GLX_ACCUM_RED_SIZE
    ppfd->cAccumGreenBits = 0;//GLX_ACCUM_GREEN_SIZE
    ppfd->cAccumBlueBits = 0; //GLX_ACCUM_BLUE_SIZE
    ppfd->cAccumAlphaBits = 0;//GLX_ACCUM_ALPHA_SIZE

    ppfd->cStencilBits = 4;//GLX_STENCIL_SIZE
    ppfd->cAuxBuffers = 0;
    ppfd->bReserved = 0;
    ppfd->dwVisibleMask =
    ppfd->dwDamageMask = 0;

    pixelformat = ChoosePixelFormat(hdc, ppfd);

    if ( (pixelformat = ChoosePixelFormat(hdc, ppfd)) == 0 )
    {
        MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK);
        return FALSE;
    }

    if (SetPixelFormat(hdc, pixelformat, ppfd) == FALSE)
    {
        MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK);
        return FALSE;
    }

    CreateRGBPalette(hdc);

    return TRUE;
}

long RealizePaletteNow( HDC hdc, HPALETTE hPal, BOOL bForceBackground )
{
    long Result = -1;

    // If static system color usage is set, prepare to take over the
    // system palette.

    if ( bUseStaticColors )
    {
    // If foreground, take over the static colors.  If background, release
    // the static colors.
    //
    // Note: even though SaveStaticEntries is always called, it only saves
    // the static colors if we don't currently possess them.  Once saved,
    // they cannot be resaved until RestoreStaticEntries is called.  This
    // prevents a quirk in the palette messaging that may cause us to try
    // to save a copy of the B&W settings as the original settings.

        SaveStaticEntries( hdc );
        SetSystemPaletteUse( hdc, bForceBackground ? SYSPAL_STATIC
                                                  : SYSPAL_NOSTATIC );

    // Rerealize the palette.
    //
    // If set to TRUE, bForceBackground will force the palette to be realized
    // as a background palette, regardless of focus.

        if ( UnrealizeObject( hPal ) &&
                NULL != (ghpalOld = SelectPalette( hdc, hPal, bForceBackground )) )
        {
            Result = RealizePalette( hdc );
        }

    // If foreground, setup the B&W static colors.  If background, restore
    // the original static colors.

        if ( bForceBackground )
            RestoreStaticEntries( hdc );
        else
            UseStaticEntries( hdc );
    }
    else
    {
        if ( NULL != (ghpalOld = SelectPalette( hdc, hPal, FALSE )) )
            Result = RealizePalette( hdc );
    }

    return( Result );
}

static VOID SaveStaticEntries(HDC hdc)
{
    int i;

    if ( !bSystemColorsInUse )
    {
        for (i = COLOR_SCROLLBAR; i <= COLOR_BTNHIGHLIGHT; i++)
            gacrSave[i - COLOR_SCROLLBAR] = GetSysColor(i);

        bStaticSaved = TRUE;
    }
}

static VOID UseStaticEntries(HDC hdc)
{
    SetSysColors(NUM_STATIC_COLORS, gaiStaticIndex, gacrBlackAndWhite);
    bSystemColorsInUse = TRUE;

    PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);
}

static VOID RestoreStaticEntries(HDC hdc)
{
    if ( bStaticSaved )
    {
        SetSysColors(NUM_STATIC_COLORS, gaiStaticIndex, gacrSave);
        bSystemColorsInUse = FALSE;

        PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);
    }
}


SETPIXEL.H

BOOL bSetupPixelFormat(HDC);
long RealizePaletteNow( HDC, HPALETTE, BOOL );
static VOID SaveStaticEntries(HDC);
static VOID UseStaticEntries(HDC);
static VOID RestoreStaticEntries(HDC);


STONE.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/glu.h>

#ifdef X11
#include <GL/glx.h>
#endif

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

#ifdef WIN32
#include "stonehen.h"
#endif

#include "Stone.h"

const GLfloat root2 = 1.4142136;
const GLfloat root3 = sqrt(3.);
const GLfloat rt_half = .70710678;

inline float radians(float a) {return M_PI * a / 180.0; }

inline float sign(float a) {return (a > 0. ? 1 : (a < 0. ? -1 : 0)); }

Stone::Stone()
{
  translation.set(0, 0, 0);
  rotation = 0;
  dimensions.set(1, 1, 1);

  erosion = 0;

  points_valid = transforms_valid = 0;
}

Stone::~Stone()
{
}

Stone Stone::operator=(Stone a)
{
  int i;

  translation = a.translation;
  rotation = a.rotation;
  dimensions = a.dimensions;

  erosion = a.erosion;

  if (a.points_valid && a.transforms_valid) {
    for (i = 0; i < 24; i++) points[i] = a.points[i];
    points_valid = transforms_valid = 1;
  } else points_valid = transforms_valid = 0;

  return *this;
}

void Stone::set_dimensions(GLfloat x, GLfloat y, GLfloat z)
{
  dimensions.set(x, y, z);
  points_valid = 0;
}

void Stone::set_dimensions(Point p)
{
  dimensions = p;
  points_valid = 0;
}

void Stone::erode(float p)
{
  erosion += p;
  points_valid = 0;
}

void Stone::translate(GLfloat x, GLfloat y, GLfloat z)
{
  translation.pt[0] += x;
  translation.pt[1] += y;
  translation.pt[2] += z;
  points_valid = 0;
}

void Stone::translate(Point p)
{
  translation += p;
  points_valid = 0;
}

void Stone::rotate_self_aboutz(GLfloat angle)
{
  rotation += angle;
  points_valid = 0;
}

void Stone::draw()
{
  compute_points();

  glPushMatrix();
  glRotatef(rotation, 0, 0, 1);
  glTranslatef(translation.pt[0], translation.pt[1], translation.pt[2]);
  glScalef(dimensions.pt[0], dimensions.pt[1], dimensions.pt[2]);

  draw_faces();
  draw_edges();
  draw_corners();

  glPopMatrix();
}

void Stone::compute_points()
{
  GLfloat e = 1.0 - erosion;

  if (points_valid) return;

  points[0].set(1, e, e);
  points[1].set(e, 1, e);
  points[2].set(e, e, 1);

  points[3].set(-e, e, 1);
  points[4].set(-e, 1, e);
  points[5].set(-1, e, e);

  points[6].set(e, -e, 1);
  points[7].set(e, -1, e);
  points[8].set(1, -e, e);

  points[9 ].set(e, e, -1);
  points[10].set(e, 1, -e);
  points[11].set(1, e, -e);

  points[12].set(-1, -e, e);
  points[13].set(-e, -1, e);
  points[14].set(-e, -e, 1);

  points[15].set(1, -e, -e);
  points[16].set(e, -1, -e);
  points[17].set(e, -e, -1);

  points[18].set(-1, e, -e);
  points[19].set(-e, 1, -e);
  points[20].set(-e, e, -1);

  points[21].set(-e, -e, -1);
  points[22].set(-e, -1, -e);
  points[23].set(-1, -e, -e);

  points_valid = 1;
  transforms_valid = 0;
}

inline Point Stone::trans_rot_point(Point p, float c, float s)
{
  Point val;
  val = p + translation;
  val = val.rotate_aboutz(c, s);
  return val;
}

inline Point Stone::transform_point(Point p, float c, float s)
{
  Point val;
  val = p.scale(dimensions);
  val += translation;
  val = val.rotate_aboutz(c, s);
  return val;
}

void Stone::transform_points()
{
  float c, s;
  int i;

  if (points_valid && transforms_valid) return;
  points_valid = 0;
  compute_points();

  c = cos(radians(rotation));
  s = sin(radians(rotation));

  for (i = 0; i < 24; i++) points[i] = transform_point(points[i], c, s);

  points_valid = 0;
  transforms_valid = 1;
}

void Stone::draw_faces(int flat)
{
  draw_faces(points, flat);
}

void Stone::draw_faces(Point *p, int flat)
{
  glBegin(GL_QUADS);

  glNormal3f(1, 0, 0);
  if (flat) glTexCoord2fv(p[15].pt);
  else glTexCoord2f(0, 0);
  glVertex3fv(p[15].pt);
  if (flat) glTexCoord2fv(p[11].pt);
  else glTexCoord2f(1, 0);
  glVertex3fv(p[11].pt);
  if (flat) glTexCoord2fv(p[0].pt);
  else glTexCoord2f(1, 1);
  glVertex3fv(p[0].pt);
  if (flat) glTexCoord2fv(p[8].pt);
  else glTexCoord2f(0, 1);
  glVertex3fv(p[8].pt);

  glNormal3f(-1, 0, 0);
  if (flat) glTexCoord2fv(p[12].pt);
  else glTexCoord2f(0, 0);
  glVertex3fv(p[12].pt);
  if (flat) glTexCoord2fv(p[5].pt);
  else glTexCoord2f(1, 0);
  glVertex3fv(p[5].pt);
  if (flat) glTexCoord2fv(p[18].pt);
  else glTexCoord2f(1, 1);
  glVertex3fv(p[18].pt);
  if (flat) glTexCoord2fv(p[23].pt);
  else glTexCoord2f(0, 1);
  glVertex3fv(p[23].pt);

  glNormal3f(0, 1, 0);
  if (flat) glTexCoord2fv(p[4].pt);
  else glTexCoord2f(0, 0);
  glVertex3fv(p[4].pt);
  if (flat) glTexCoord2fv(p[1].pt);
  else glTexCoord2f(1, 0);
  glVertex3fv(p[1].pt);
  if (flat) glTexCoord2fv(p[10].pt);
  else glTexCoord2f(1, 1);
  glVertex3fv(p[10].pt);
  if (flat) glTexCoord2fv(p[19].pt);
  else glTexCoord2f(0, 1);
  glVertex3fv(p[19].pt);

  glNormal3f(0, -1, 0);
  if (flat) glTexCoord2fv(p[22].pt);
  else glTexCoord2f(0, 0);
  glVertex3fv(p[22].pt);
  if (flat) glTexCoord2fv(p[16].pt);
  else glTexCoord2f(1, 0);
  glVertex3fv(p[16].pt);
  if (flat) glTexCoord2fv(p[7].pt);
  else glTexCoord2f(1, 1);
  glVertex3fv(p[7].pt);
  if (flat) glTexCoord2fv(p[13].pt);
  else glTexCoord2f(0, 1);
  glVertex3fv(p[13].pt);

  glNormal3f(0, 0, 1);
  if (flat) glTexCoord2fv(p[14].pt);
  else glTexCoord2f(0, 0);
  glVertex3fv(p[14].pt);
  if (flat) glTexCoord2fv(p[6].pt);
  else glTexCoord2f(1, 0);
  glVertex3fv(p[6].pt);
  if (flat) glTexCoord2fv(p[2].pt);
  else glTexCoord2f(1, 1);
  glVertex3fv(p[2].pt);
  if (flat) glTexCoord2fv(p[3].pt);
  else glTexCoord2f(0, 1);
  glVertex3fv(p[3].pt);

  glNormal3f(0, 0, -1);
  if (flat) glTexCoord2fv(p[20].pt);
  else glTexCoord2f(0, 0);
  glVertex3fv(p[20].pt);
  if (flat) glTexCoord2fv(p[9].pt);
  else glTexCoord2f(1, 0);
  glVertex3fv(p[9].pt);
  if (flat) glTexCoord2fv(p[17].pt);
  else glTexCoord2f(1, 1);
  glVertex3fv(p[17].pt);
  if (flat) glTexCoord2fv(p[21].pt);
  else glTexCoord2f(0, 1);
  glVertex3fv(p[21].pt);

  glEnd();
}

void Stone::draw_edges(int flat)
{
  draw_edges(points, flat);
}

void Stone::draw_edges(Point *p, int flat)
{
  Point n1, n2;

  glBegin(GL_QUADS);

  n1.set(0, 1, 0);
  n2.set(1, 0, 0);
  draw_edge(n1, n2, p, 10, 1, 0, 11, flat);

  n1.set(1, 0, 0);
  n2.set(0, -1, 0);
  draw_edge(n1, n2, p, 15, 8, 7, 16, flat);

  n1.set(1, 0, 0);
  n2.set(0, 0, 1);
  draw_edge(n1, n2, p, 8, 0, 2, 6, flat);

  n1.set(1, 0, 0);
  n2.set(0, 0, -1);
  draw_edge(n1, n2, p, 11, 15, 17, 9, flat);

  n1.set(0, 1, 0);
  n2.set(-1, 0, 0);
  draw_edge(n1, n2, p, 4, 19, 18, 5, flat);

  n1.set(-1, 0, 0);
  n2.set(0, -1, 0);
  draw_edge(n1, n2, p, 12, 23, 22, 13, flat);

  n1.set(-1, 0, 0);
  n2.set(0, 0, 1);
  draw_edge(n1, n2, p, 5, 12, 14, 3, flat);

  n2.set(-1, 0, 0);
  n1.set(0, 0, -1);
  draw_edge(n1, n2, p, 20, 21, 23, 18, flat);

  n1.set(0, 1, 0);
  n2.set(0, 0, 1);
  draw_edge(n1, n2, p, 1, 4, 3, 2, flat);

  n1.set(0, 1, 0);
  n2.set(0, 0, -1);
  draw_edge(n1, n2, p, 19, 10, 9, 20, flat);

  n1.set(0, -1, 0);
  n2.set(0, 0, 1);
  draw_edge(n1, n2, p, 13, 7, 6, 14, flat);

  n1.set(0, -1, 0);
  n2.set(0, 0, -1);
  draw_edge(n1, n2, p, 16, 22, 21, 17, flat);

  glEnd();
}

void Stone::draw_edge(Point n1, Point n2, Point *p,
      int a, int b, int c, int d, int flat)
{
  glNormal3fv(n1.pt);
  if (flat) glTexCoord2fv(p[a].pt);
  else glTexCoord2f(0, 0);
  glVertex3fv(p[a].pt);
  if (flat) glTexCoord2fv(p[b].pt);
  else glTexCoord2f(0, 1);
  glVertex3fv(p[b].pt);
  glNormal3fv(n2.pt);
  if (flat) glTexCoord2fv(p[c].pt);
  else glTexCoord2f(1, 1);
  glVertex3fv(p[c].pt);
  if (flat) glTexCoord2fv(p[d].pt);
  else glTexCoord2f(1, 0);
  glVertex3fv(p[d].pt);
}

void Stone::draw_corners(Point *p, int flat)
{
  GLfloat e = 1.0 - erosion;

  glBegin(GL_TRIANGLES);

  if (flat) glTexCoord2fv(p[0].pt);
  else glTexCoord2f(0, 0);
  glNormal3f(1, 0, 0);
  glVertex3fv(p[0].pt);
  if (flat) glTexCoord2fv(p[1].pt);
  else glTexCoord2f(1, 0);
  glNormal3f(0, 1, 0);
  glVertex3fv(p[1].pt);
  if (flat) glTexCoord2fv(p[2].pt);
  else glTexCoord2f(.5, 1);
  glNormal3f(0, 0, 1);
  glVertex3fv(p[2].pt);

  if (flat) glTexCoord2fv(p[3].pt);
  else glTexCoord2f(0, 0);
  glNormal3f(0, 0, 1);
  glVertex3fv(p[3].pt);
  if (flat) glTexCoord2fv(p[4].pt);
  else glTexCoord2f(1, 0);
  glNormal3f(0, 1, 0);
  glVertex3fv(p[4].pt);
  if (flat) glTexCoord2fv(p[5].pt);
  else glTexCoord2f(.5, 1);
  glNormal3f(-1, 0, 0);
  glVertex3fv(p[5].pt);

  if (flat) glTexCoord2fv(p[6].pt);
  else glTexCoord2f(0, 0);
  glNormal3f(0, 0, 1);
  glVertex3fv(p[6].pt);
  if (flat) glTexCoord2fv(p[7].pt);
  else glTexCoord2f(1, 0);
  glNormal3f(0, -1, 0);
  glVertex3fv(p[7].pt);
  if (flat) glTexCoord2fv(p[8].pt);
  else glTexCoord2f(.5, 1);
  glNormal3f(1, 0, 0);
  glVertex3fv(p[8].pt);

  if (flat) glTexCoord2fv(p[9].pt);
  else glTexCoord2f(0, 0);
  glNormal3f(0, 0, -1);
  glVertex3fv(p[9].pt);
  if (flat) glTexCoord2fv(p[10].pt);
  else glTexCoord2f(1, 0);
  glNormal3f(0, 1, 0);
  glVertex3fv(p[10].pt);
  if (flat) glTexCoord2fv(p[11].pt);
  else glTexCoord2f(.5, 1);
  glNormal3f(1, 0, 0);
  glVertex3fv(p[11].pt);

  if (flat) glTexCoord2fv(p[12].pt);
  else glTexCoord2f(0, 0);
  glNormal3f(-1, 0, 0);
  glVertex3fv(p[12].pt);
  if (flat) glTexCoord2fv(p[13].pt);
  else glTexCoord2f(1, 0);
  glNormal3f(0, -1, 0);
  glVertex3fv(p[13].pt);
  if (flat) glTexCoord2fv(p[14].pt);
  else glTexCoord2f(.5, 1);
  glNormal3f(0, 0, 1);
  glVertex3fv(p[14].pt);

  if (flat) glTexCoord2fv(p[15].pt);
  else glTexCoord2f(0, 0);
  glNormal3f(1, 0, 0);
  glVertex3fv(p[15].pt);
  if (flat) glTexCoord2fv(p[16].pt);
  else glTexCoord2f(1, 0);
  glNormal3f(0, -1, 0);
  glVertex3fv(p[16].pt);
  if (flat) glTexCoord2fv(p[17].pt);
  else glTexCoord2f(.5, 1);
  glNormal3f(0, 0, -1);
  glVertex3fv(p[17].pt);

  if (flat) glTexCoord2fv(p[18].pt);
  else glTexCoord2f(0, 0);
  glNormal3f(-1, 0, 0);
  glVertex3fv(p[18].pt);
  if (flat) glTexCoord2fv(p[19].pt);
  else glTexCoord2f(1, 0);
  glNormal3f(0, 1, 0);
  glVertex3fv(p[19].pt);
  if (flat) glTexCoord2fv(p[20].pt);
  else glTexCoord2f(.5, 1);
  glNormal3f(0, 0, -1);
  glVertex3fv(p[20].pt);

  if (flat) glTexCoord2fv(p[21].pt);
  else glTexCoord2f(0, 0);
  glNormal3f(0, 0, -1);
  glVertex3fv(p[21].pt);
  if (flat) glTexCoord2fv(p[22].pt);
  else glTexCoord2f(1, 0);
  glNormal3f(0, -1, 0);
  glVertex3fv(p[22].pt);
  if (flat) glTexCoord2fv(p[23].pt);
  else glTexCoord2f(.5, 1);
  glNormal3f(-1, 0, 0);
  glVertex3fv(p[23].pt);

  glEnd();
}

void Stone::draw_shadow(Point dlight)
{
  Point p[24];
  int i;

  transform_points();
  for (i = 0; i < 24; i++) {
    p[i] = points[i];
    if (p[i].pt[2] < 0.0) p[i].pt[2] = 0.0;
    p[i] = p[i].project_direction(dlight);
  }
  draw_faces(p, 1);
  draw_edges(p, 1);
  draw_corners(p, 1);
}

void Stone::draw_shadow(Point dlight, GLfloat blur,
Color color, Color diffuse)
{
  float b = 1.0 + blur;
  float c, s;
  Point p[8], pp[8];
  Point d, n;
  Color colorp, diffusep;
  int draw[6];
  int i;

  if (blur == 0.0) {
    draw_shadow(dlight);
    return;
  }

  colorp = color;
  color.c[3] = 1;
  colorp.c[3] = 0;
  diffusep = diffuse;
  diffuse.c[3] = 1;
  diffusep.c[3] = 0;

  /* We're being slightly cowardly here and ignoring the erosion - it
   * shouldn't make a big difference */
  p[0].set(-1, -1, -1);
  pp[0] = p[0];
  p[1].set(1, -1, -1);
  pp[1] = p[1];
  p[2].set(1, 1, -1);
  pp[2] = p[2];
  p[3].set(-1, 1, -1);
  pp[3] = p[3];
  p[4].set(-1, -1, 1);
  pp[4] = p[4];
  p[5].set(1, -1, 1);
  pp[5] = p[5];
  p[6].set(1, 1, 1);
  pp[6] = p[6];
  p[7].set(-1, 1, 1);
  pp[7] = p[7];

  c = cos(radians(rotation));
  s = sin(radians(rotation));
  for (i = 0; i < 8; i++) {
    p[i] = transform_point(p[i], c, s);

    /* This is a complete and utter hack - the net effect is that
     * points which are higher are displaced further when the penumbra
     * is drawn */
    d = dimensions;
    d.pt[0] += blur * (pp[i].pt[2] + translation.pt[2]);
    d.pt[1] += blur * (pp[i].pt[2] + translation.pt[2]);
    d.pt[2] += blur * (pp[i].pt[2] + translation.pt[2]);
    pp[i] = pp[i].scale(d);
    pp[i] = trans_rot_point(pp[i], c, s);
    if (p[i].pt[2] < 0.0) p[i].pt[2] = 0.0;

    p[i] = p[i].project_direction(dlight);
    if (pp[i].pt[2] < 0.0) pp[i].pt[2] = 0.0;
    pp[i] = pp[i].project_direction(dlight);
  }

  /* Compute whether or not we should draw various parts of the shadow
   * based upon the normal direction */
  if (dlight.pt[2] > 0.0) dlight *= -1.0;
  n.set(0, 0, -1);
  n = n.rotate_aboutz(c, s);
  draw[0] = (dlight.dot(n) < 0.0);
  n.set(0, -1, 0);
  n = n.rotate_aboutz(c, s);
  draw[1] = (dlight.dot(n) < 0.0);
  n.set(1, 0, 0);
  n = n.rotate_aboutz(c, s);
  draw[2] = (dlight.dot(n) < 0.0);
  n.set(0, 1, 0);
  n = n.rotate_aboutz(c, s);
  draw[3] = (dlight.dot(n) < 0.0);
  n.set(-1, 0, 0);
  n = n.rotate_aboutz(c, s);
  draw[4] = (dlight.dot(n) < 0.0);
  n.set(0, 0, 1);
  n = n.rotate_aboutz(c, s);
  draw[5] = (dlight.dot(n) < 0.0);

  /* This part draws the "real" shadow */
  glColor4fv(color.c);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);

  glBegin(GL_QUADS);

  if (draw[0]) {
    glVertex2fv(p[0].pt);
    glVertex2fv(p[1].pt);
    glVertex2fv(p[2].pt);
    glVertex2fv(p[3].pt);
  }

  if (draw[1]) {
    glVertex2fv(p[0].pt);
    glVertex2fv(p[1].pt);
    glVertex2fv(p[5].pt);
    glVertex2fv(p[4].pt);
  }

  if (draw[2]) {
    glVertex2fv(p[1].pt);
    glVertex2fv(p[2].pt);
    glVertex2fv(p[6].pt);
    glVertex2fv(p[5].pt);
  }

  if (draw[3]) {
    glVertex2fv(p[2].pt);
    glVertex2fv(p[3].pt);
    glVertex2fv(p[7].pt);
    glVertex2fv(p[6].pt);
  }

  if (draw[4]) {
    glVertex2fv(p[3].pt);
    glVertex2fv(p[0].pt);
    glVertex2fv(p[4].pt);
    glVertex2fv(p[7].pt);
  }

  if (draw[5]) {
    glVertex2fv(p[4].pt);
    glVertex2fv(p[5].pt);
    glVertex2fv(p[6].pt);
    glVertex2fv(p[7].pt);
  }

  glEnd();

  /* This part draws the penumbra */

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glBegin(GL_QUADS);

  /* Top */
  if (draw[5]) {
    if (!draw[4]) {
      glColor4fv(color.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
      glVertex2fv(p[4].pt);
      glVertex2fv(p[5].pt);
      glColor4fv(colorp.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
      glVertex2fv(pp[5].pt);
      glVertex2fv(pp[4].pt);
    }

    if (!draw[2]) {
      glColor4fv(color.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
      glVertex2fv(p[5].pt);
      glVertex2fv(p[6].pt);
      glColor4fv(colorp.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
      glVertex2fv(pp[6].pt);
      glVertex2fv(pp[5].pt);
    }

    if (!draw[3]) {
      glColor4fv(color.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
      glVertex2fv(p[6].pt);
      glVertex2fv(p[7].pt);
      glColor4fv(colorp.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
      glVertex2fv(pp[7].pt);
      glVertex2fv(pp[6].pt);
    }

    if (!draw[4]) {
      glColor4fv(color.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
      glVertex2fv(p[7].pt);
      glVertex2fv(p[4].pt);
      glColor4fv(colorp.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
      glVertex2fv(pp[4].pt);
      glVertex2fv(pp[7].pt);
    }
  }
  /* End of Top */

  /* Sides */
  if ((draw[1] || draw[4]) && !(draw[1] && draw[4])) {
    glColor4fv(color.c);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
    glVertex2fv(p[0].pt);
    glVertex2fv(p[4].pt);
    glColor4fv(colorp.c);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
    glVertex2fv(pp[4].pt);
    glVertex2fv(pp[0].pt);
  }

  if ((draw[1] || draw[2]) && !(draw[1] && draw[2])) {
    glColor4fv(color.c);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
    glVertex2fv(p[1].pt);
    glVertex2fv(p[5].pt);
    glColor4fv(colorp.c);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
    glVertex2fv(pp[5].pt);
    glVertex2fv(pp[1].pt);
  }

  if ((draw[3] || draw[2]) && !(draw[3] && draw[2])) {
    glColor4fv(color.c);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
    glVertex2fv(p[2].pt);
    glVertex2fv(p[6].pt);
    glColor4fv(colorp.c);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
    glVertex2fv(pp[6].pt);
    glVertex2fv(pp[2].pt);
  }

  if ((draw[3] || draw[4]) && !(draw[3] && draw[4])) {
    glColor4fv(color.c);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
    glVertex2fv(p[3].pt);
    glVertex2fv(p[7].pt);
    glColor4fv(colorp.c);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
    glVertex2fv(pp[7].pt);
    glVertex2fv(pp[3].pt);
  }
  /* End of Sides */

  /* Bottom */
  if (draw[0]) {
    if (!draw[1]) {
      glColor4fv(color.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, diffuse.c);
      glVertex2fv(p[0].pt);
      glVertex2fv(p[1].pt);
      glColor4fv(colorp.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, diffusep.c);
      glVertex2fv(pp[1].pt);
      glVertex2fv(pp[0].pt);
    }

    if (!draw[2]) {
      glColor4fv(color.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
      glVertex2fv(p[1].pt);
      glVertex2fv(p[2].pt);
      glColor4fv(colorp.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
      glVertex2fv(pp[2].pt);
      glVertex2fv(pp[1].pt);
    }

    if (!draw[3]) {
      glColor4fv(color.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
      glVertex2fv(p[2].pt);
      glVertex2fv(p[3].pt);
      glColor4fv(colorp.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
      glVertex2fv(pp[3].pt);
      glVertex2fv(pp[2].pt);
    }

    if (!draw[4]) {
      glColor4fv(color.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.c);
      glVertex2fv(p[3].pt);
      glVertex2fv(p[0].pt);
      glColor4fv(colorp.c);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffusep.c);
      glVertex2fv(pp[0].pt);
      glVertex2fv(pp[3].pt);
    }
  }
  /* End of Bottom */

  glEnd();

  glDisable(GL_BLEND);

}


STONE.H

#ifndef STONE_H
#define STONE_H

#include "Color.h"
#include "Point.h"

class Stone {
 public:
  Stone();
  ~Stone();
  Stone operator=(Stone a);

  void set_dimensions(GLfloat x, GLfloat y, GLfloat z);
  void set_dimensions(Point p);
  Point get_dimensions() {return dimensions;};

  /* p = 0 --> sharp corners, p == 1 --> completely rounded corners */
  void erode(float p);
  float get_erosion() {return erosion;};

  void translate(GLfloat x, GLfloat y, GLfloat z);
  void translate(Point p);

  /* Angle in degrees */
  void rotate_self_aboutz(GLfloat angle);

  void draw();

  void draw_shadow(Point dlight);
  void draw_shadow(Point dlight, GLfloat blur, Color color, Color diffuse);

 private:
  Point translation;
  GLfloat rotation;
  /* dimensions contains the length, width, and height of the stone */
  Point dimensions;

  GLfloat erosion;

  Point points[24];
  int points_valid;
  void compute_points();

  int transforms_valid;
  inline Point trans_rot_point(Point p, float c, float s);
  inline Point transform_point(Point p, float c, float s);
  void transform_points();

  void draw_faces(int flat = 0);
  void draw_faces(Point *p, int flat = 0);

  void draw_edges(int flat = 0);
  void draw_edges(Point *p, int flat = 0);
  void draw_edge(Point n1, Point n2, Point *p, int a, int b, int c, int d,
 int flat = 0);

  void draw_corners(int flat = 0) {draw_corners(points, flat);};
  void draw_corners(Point *p, int flat = 0);
};

#endif


STONEHEN.CXX

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include "glos.h"

#include <GL/gl.h>
#include <GL/glu.h>

#include "stonehen.h"
#include <stdio.h>
#include <stdlib.h>

#include "atmosphe.h"
#include "scene.h"
#include "callback.h"
#include "setpixel.h"

extern int use_lighting;
extern int draw_shadows;
extern int use_normal_fog;
extern int use_textures;
extern int texture_hack;// HACK HACK - only texture map stones for now
extern int use_telescope;
extern int use_antialias;
extern int cb_demo_mode;

BOOL Con;

BOOL bTimeScale[5];
extern GLfloat time_scale;
extern HPALETTE ghPalette, ghpalOld;

GLfloat TimeScale[] = {0, 1, 10, 100, 500};

char szAppName[] = "Stonehenge";

extern TimeDate last_update;

int curr_weather;

HGLRC hRC;

int  iTimer;

/* forward declarations of helper functions in this module */
HWND   WINAPI InitializeWindow (HINSTANCE, int);
VOID   WINAPI InitializeMenu (HWND, HMENU);
LONG   WINAPI CommandHandler (HWND, UINT, LONG);
LONG   WINAPI MainWndProc (HWND, UINT, UINT, LONG);

/* entry point of this executable */
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG        msg;
    HWND       hWnd;

    /* previous instances do not exist in Win32 */
    if (hPrevInstance)
    return 0;

    if (!(hWnd = InitializeWindow (hInstance, nCmdShow)))
        return FALSE;

    /* main window message loop */
    while (TRUE) {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT) break;
            TranslateMessage (&msg);
            DispatchMessage (&msg);
        } else {
            PostMessage(hWnd, WM_PAINT, NULL, NULL);
        }
    }

    /* return success of application */
    return TRUE;
}

HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow)
    {
    WNDCLASS   wc;
    HWND       hWnd;
    RECT       rect;

    /* Register the frame class */
    wc.style         = CS_OWNDC;
    wc.lpfnWndProc   = (WNDPROC)MainWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon (hInstance, szAppName);
    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName  = szAppName;
    wc.lpszClassName = szAppName;

    if (!RegisterClass (&wc) )
    return FALSE;

    rect.left = 100;
    rect.top  = 100;
    rect.right = 652;
    rect.bottom = 500;

    AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
    /* Create the frame */
    hWnd = CreateWindow (szAppName,
             "OpenGL Stonehenge Demo",
     WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
             rect.left,
             rect.top,
             WINDSIZEX(rect),
             WINDSIZEY(rect),
             NULL,
             NULL,
             hInstance,
             NULL);

    /* make sure window was created */
    if (!hWnd)
    return FALSE;

    InitializeMenu(hWnd, (HMENU) GetMenu(hWnd));

    SendMessage(hWnd, WM_INIT, NULL, NULL);

    /* show and update main window */
    ShowWindow (hWnd, nCmdShow);

    UpdateWindow (hWnd);

    return hWnd;
}

/* main window procedure */
LONG WINAPI MainWndProc (
    HWND    hWnd,
    UINT    uMsg,
    UINT    uParam,
    LONG    lParam)
{
    LONG    lRet = 1;

    switch (uMsg)
    {
    case WM_CREATE:
        {
    HDC    hDC;

    /* This is equivalent to the find_visual routine */
            hDC = GetDC(hWnd);
    bSetupPixelFormat(hDC);

            hRC = wglCreateContext( hDC );
            Con = wglMakeCurrent( hDC, hRC );

    ReleaseDC(hWnd, hDC);

    // iTimer = SetTimer(hWnd, 1, 1000, NULL);
}
break;

    case WM_PAINT:
        {
HDChDC;
PAINTSTRUCTps;

hDC = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);

drawWP(hWnd);

        }
break;

    case WM_INIT:
{
    scene_init();

    last_update.read_time();

    resetViewerCB(NULL, NULL, NULL);

}
break;

        // The WM_QUERYNEWPALETTE message informs a window that it is about to
        // receive input focus. In response, the window receiving focus should
        // realize its palette as a foreground palette and update its client
        // area. If the window realizes its palette, it should return TRUE;
        // otherwise, it should return FALSE.

        case WM_QUERYNEWPALETTE:
        {
            HDC     hDC;

            if(ghPalette)
            {
                hDC = GetDC(hWnd);

                // Select and realize the palette

                //ghpalOld = SelectPalette(hDC, ghPalette, FALSE);
                RealizePaletteNow(hDC, ghPalette, FALSE);

                // Redraw the client area

                InvalidateRect(hWnd, NULL, TRUE);
                UpdateWindow(hWnd);

                if(ghpalOld)
                    SelectPalette(hDC, ghpalOld, FALSE);

                ReleaseDC(hWnd, hDC);

                return TRUE;
            }

            return FALSE;
        }

        // The WM_PALETTECHANGED message informs all windows that the window
        // with input focus has realized its logical palette, thereby changing
        // the system palette. This message allows a window without input focus
        // that uses a color palette to realize its logical palettes and update
        // its client area.
        //
        // This message is sent to all windows, including the one that changed
        // the system palette and caused this message to be sent. The wParam of
        // this message contains the handle of the window that caused the system
        // palette to change. To avoid an infinite loop, care must be taken to
        // check that the wParam of this message does not match the window's
        // handle.

        case WM_PALETTECHANGED:
        {
            HDC         hDC;

            // Before processing this message, make sure we
            // are indeed using a palette

            if (ghPalette)
            {
                // If this application did not change the palette, select
                // and realize this application's palette

                if (uParam != (WPARAM)hWnd)
                {
                    // Need the window's DC for SelectPalette/RealizePalette

                    hDC = GetDC(hWnd);

                    // Select and realize our palette

                    //ghpalOld = SelectPalette(hDC, ghPalette, FALSE);
                    RealizePaletteNow(hDC, ghPalette, FALSE);

                    // WHen updating the colors for an inactive window,
                    // UpdateColors can be called because it is faster than
                    // redrawing the client area (even though the results are
                    // not as good)

                    UpdateColors(hDC);

                    // Clean up

                    if (ghpalOld)
                       SelectPalette(hDC, ghpalOld, FALSE);

                    ReleaseDC(hWnd, hDC);
                }
            }
            break;
        }

    case WM_SIZE:
        {

 resizeCB(hWnd, NULL, NULL);

        }
        break;

    case WM_COMMAND:
        /* handle all command messages in a localized function */
         lRet = CommandHandler (hWnd, uParam, lParam);
        break;

    case WM_LBUTTONDOWN:
{
GLwDrawingAreaCallbackStruct *call_data;

call_data = (GLwDrawingAreaCallbackStruct *) LocalAlloc(0, sizeof(GLwDrawingAreaCallbackStruct));
call_data->event = (XEvent *) LocalAlloc(0, sizeof(XEvent));

call_data->event->type = ButtonPress;
call_data->event->xbutton.button = Button1;
call_data->event->xbutton.x = LOWORD(lParam);
call_data->event->xbutton.y = HIWORD(lParam);

inputCB(hWnd, NULL, (LPVOID)call_data);
LocalFree(call_data->event);
LocalFree(call_data);
}
break;

    case WM_RBUTTONDOWN:
{
GLwDrawingAreaCallbackStruct *call_data;

call_data = (GLwDrawingAreaCallbackStruct *) LocalAlloc(0, sizeof(GLwDrawingAreaCallbackStruct));
call_data->event = (XEvent *) LocalAlloc(0, sizeof(XEvent));

call_data->event->type = ButtonPress;
call_data->event->xbutton.button = Button2;
call_data->event->xbutton.x = LOWORD(lParam);
call_data->event->xbutton.y = HIWORD(lParam);

inputCB(hWnd, NULL, (LPVOID)call_data);
LocalFree(call_data->event);
LocalFree(call_data);
}
break;

    case WM_LBUTTONUP:
{
GLwDrawingAreaCallbackStruct *call_data;

call_data = (GLwDrawingAreaCallbackStruct *) LocalAlloc(0, sizeof(GLwDrawingAreaCallbackStruct));
call_data->event = (XEvent *) LocalAlloc(0, sizeof(XEvent));

call_data->event->type = ButtonRelease;
call_data->event->xbutton.button = Button1;

inputCB(hWnd, NULL, (LPVOID)call_data);
LocalFree(call_data->event);
LocalFree(call_data);
}
break;

    case WM_RBUTTONUP:
{
GLwDrawingAreaCallbackStruct *call_data;

call_data = (GLwDrawingAreaCallbackStruct *) LocalAlloc(0, sizeof(GLwDrawingAreaCallbackStruct));
call_data->event = (XEvent *) LocalAlloc(0, sizeof(XEvent));

call_data->event->type = ButtonRelease;
call_data->event->xbutton.button = Button2;

inputCB(hWnd, NULL, (LPVOID)call_data);
LocalFree(call_data->event);
LocalFree(call_data);
}
break;

    case WM_MOUSEMOVE:
{
GLwDrawingAreaCallbackStruct *call_data;

call_data = (GLwDrawingAreaCallbackStruct *) LocalAlloc(0, sizeof(GLwDrawingAreaCallbackStruct));
call_data->event = (XEvent *) LocalAlloc(0, sizeof(XEvent));

call_data->event->type = MotionNotify;
call_data->event->xmotion.x = LOWORD(lParam);
call_data->event->xmotion.y = HIWORD(lParam);

inputCB(hWnd, NULL, (LPVOID)call_data);
LocalFree(call_data->event);
LocalFree(call_data);
}
break;

    case WM_CLOSE:
        {

HGLRC hRC;

if(hRC = wglGetCurrentContext())
    wglDeleteContext(hRC);

//KillTimer(iTimer);

        /* call destroy window to cleanup and go away */
        DestroyWindow (hWnd);
        }
        break;

    case WM_DESTROY:
        {
wglDeleteContext(wglGetCurrentContext());
        PostQuitMessage (0);
        }
        break;

    default:
        /* pass all unhandled messages to DefWindowProc */
        lRet = DefWindowProc (hWnd, uMsg, uParam, lParam);
        break;
    }

    /* return 1 if handled message, 0 if not */
    return lRet;
}

/* handle all WM_COMMAND messages here */
LONG WINAPI CommandHandler (
    HWND    hWnd,
    UINT    uParam,
    LONG    lParam)
{
    int    i;
    HMENU   hMenu;

    switch (LOWORD(uParam))
    {
    case IDM_EXIT:
        /* exit application */
        PostMessage (hWnd, WM_CLOSE, 0, 0L);
        break;

    case IDM_LIGHTING:
hMenu = GetMenu(hWnd);
use_lighting = !use_lighting;
CheckMenuItem(hMenu,IDM_LIGHTING,(use_lighting?MF_CHECKED:MF_UNCHECKED));
break;

    case IDM_SHADOWS:
hMenu = GetMenu(hWnd);
draw_shadows = !draw_shadows;
CheckMenuItem(hMenu,IDM_SHADOWS,(draw_shadows?MF_CHECKED:MF_UNCHECKED));
break;

    case IDM_FOG:
hMenu = GetMenu(hWnd);
use_normal_fog = !use_normal_fog;
CheckMenuItem(hMenu,IDM_FOG,(use_normal_fog?MF_CHECKED:MF_UNCHECKED));
break;

#ifdef TEXTURE
    case IDM_TEXTUREMAP:
hMenu = GetMenu(hWnd);
/* HACK HACK HACK
 * This would be right, but for now I only want to map the stones, nothing
 * else, so I have a new variable - texture_hack

use_textures = !use_textures;
CheckMenuItem(hMenu,IDM_TEXTUREMAP,(use_textures?MF_CHECKED:MF_UNCHECKED));
 */
texture_hack = !texture_hack;
CheckMenuItem(hMenu,IDM_TEXTUREMAP,(texture_hack?MF_CHECKED:MF_UNCHECKED));
break;
#endif

    case IDM_TELESCOPE:
hMenu = GetMenu(hWnd);
use_telescope = !use_telescope;
CheckMenuItem(hMenu,IDM_TELESCOPE,(use_telescope?MF_CHECKED:MF_UNCHECKED));
break;

    case IDM_ANTIALIAS:
hMenu = GetMenu(hWnd);
use_antialias = !use_antialias;
CheckMenuItem(hMenu,IDM_ANTIALIAS,(use_antialias?MF_CHECKED:MF_UNCHECKED));
break;

    case IDM_CLEAR:
    case IDM_FOGGY:
    case IDM_VERYFOGGY:
    case IDM_RAINY:
hMenu = GetMenu(hWnd);
/* If this value is already checked, we do nothing */
if(curr_weather != (LOWORD(uParam) - IDM_CLEAR))
{
    CheckMenuItem(hMenu, curr_weather + IDM_CLEAR, MF_UNCHECKED);

    CheckMenuItem(hMenu, LOWORD(uParam), MF_CHECKED);
    curr_weather = LOWORD(uParam) - IDM_CLEAR;
    scene_set_weather(weathers[curr_weather]);
}

break;

    case IDM_CURRENTTIME:
currentTimeCB(hWnd);
        checkTimeMenuItem(GetMenu(hWnd), LOWORD(uParam));
break;

    case IDM_10AM:
time10amCB(hWnd);
        checkTimeMenuItem(GetMenu(hWnd), LOWORD(uParam));
break;

    case IDM_NOON:
time12pmCB(hWnd);
        checkTimeMenuItem(GetMenu(hWnd), LOWORD(uParam));
break;

    case IDM_4PM:
time4pmCB(hWnd);
        checkTimeMenuItem(GetMenu(hWnd), LOWORD(uParam));
break;

    case IDM_HOLDTIME:
    case IDM_SCALE1:
    case IDM_SCALE10:
    case IDM_SCALE100:
    case IDM_SCALE500:
hMenu = GetMenu(hWnd);
/* If this value is already checked, we do nothing */
if(!bTimeScale[LOWORD(uParam) - IDM_HOLDTIME])
{
    for(i=0;!bTimeScale[i];i++);

    time_scale = TimeScale[LOWORD(uParam) - IDM_HOLDTIME];

    CheckMenuItem(hMenu, IDM_HOLDTIME + i, MF_UNCHECKED);
    bTimeScale[i] = FALSE;

    CheckMenuItem(hMenu, LOWORD(uParam), MF_CHECKED);
    bTimeScale[LOWORD(uParam) - IDM_HOLDTIME] = TRUE;

    timeSpeedCB(hWnd, NULL, NULL);
}
break;

    case IDM_DEMOMODE:
hMenu = GetMenu(hWnd);

cb_demo_mode = !cb_demo_mode;
CheckMenuItem(hMenu,IDM_DEMOMODE,(cb_demo_mode?MF_CHECKED:MF_UNCHECKED));
demo_modeCB(hWnd, NULL, NULL);
break;

    case IDM_RESET:
resetViewerCB(hWnd, NULL, NULL);
break;

    default:
        return FALSE;
    }
    return TRUE;
}

VOID WINAPI InitializeMenu (HWND hWnd, HMENU hMenu)
{

    curr_weather = def_weather_index;

    CheckMenuItem(hMenu, IDM_CLEAR + curr_weather, MF_CHECKED);

    CheckMenuItem(hMenu, IDM_LIGHTING, MF_CHECKED);

    bTimeScale[0] = TRUE;
    bTimeScale[1] = FALSE;
    bTimeScale[2] = FALSE;
    bTimeScale[3] = FALSE;
    bTimeScale[4] = FALSE;

    CheckMenuItem(hMenu, IDM_4PM, MF_CHECKED);

    CheckMenuItem(hMenu, IDM_HOLDTIME, MF_CHECKED);

    cb_demo_mode = FALSE;
}


STONEHEN.H

#include "stonerc.h"

#pragma warning (disable : 4244)

#define M_PI 3.1415926

typedef HWND Widget;
typedef VOID *XtPointer;
typedef BOOL Boolean;
typedef UINT Dimension;

#define WINDSIZEX(Rect)   (Rect.right  - Rect.left)
#define WINDSIZEY(Rect)   (Rect.bottom - Rect.top)

#define WM_INITWM_USER

typedef struct
{
    intbutton;
    int x;
    int y;
}XButton;

typedef struct
{
    intx;
    inty;
}XMotion;

typedef struct
{
    int    type;
    XButton xbutton;
    XMotion xmotion;
} XEvent;

typedef struct
{
    int     reason;
    XEvent  *event;
    Dimension width, height;/* for resize callback */
} GLwDrawingAreaCallbackStruct;

/*
** RGB Image Structure
*/

typedef struct _RGBImageRec {
    GLint sizeX, sizeY;
    unsigned char *data;
} RGBImageRec;

#define Button11
#define Button22

#define ButtonPress1
#define ButtonRelease2
#defineMotionNotify3

RGBImageRec *RGBImageLoad(char *);

#define TK_RGBImageRec RGBImageRec
#define tkRGBImageLoad RGBImageLoad


STONERC.H

#define IDM_EXIT100
#define IDM_LIGHTING200
#define IDM_SHADOWS201
#define IDM_FOG202
#define IDM_TEXTUREMAP203
#define IDM_TELESCOPE204
#define IDM_ANTIALIAS205
#define IDM_CLEAR300
#define IDM_FOGGY301
#define IDM_VERYFOGGY302
#define IDM_RAINY303
#define IDM_CURRENTTIME400
#define IDM_10AM401
#define IDM_NOON402
#define IDM_4PM403
#define IDM_HOLDTIME410
#define IDM_SCALE1411
#define IDM_SCALE10412
#define IDM_SCALE100413
#define IDM_SCALE500414
#define IDM_DEMOMODE500
#define IDM_RESET501


TELESCOP.CXX

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/glu.h>

#ifdef X11
#include <GL/glx.h>
#endif

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

#ifdef WIN32
#include "stonehen.h"
#endif

#include "Color.h"
#include "Telescop.h"

inline float radians(float a) {return M_PI * a / 180.0;}

Telescope::Telescope(GLfloat x, GLfloat y)
{
  xpos = x;
  ypos = y;

  divisions = 20;
  radius = .1;

  disk = gluNewQuadric();
  cylinder = gluNewQuadric();
  gluQuadricNormals(disk, GLU_FLAT);
  gluQuadricNormals(cylinder, GLU_SMOOTH);
  gluQuadricTexture(disk, GL_TRUE);
  gluQuadricTexture(cylinder, GL_TRUE);

}

Telescope::~Telescope()
{
  gluDeleteQuadric(disk);
  gluDeleteQuadric(cylinder);
}

void Telescope::draw_setup(GLfloat fov, GLfloat aspect, int perspective)
{
  GLfloat near_plane;

  /* Worry about the aspect ratio later */
  near_plane = .5 / tan(radians(fov / 2.0));

  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  if (perspective) gluPerspective(fov, aspect, near_plane - .01, 10.0);
  /*else
    fprintf(stderr,
    "Warning: Drawing telescope using orthographic projection.\n");
  gluLookAt(0, 0, -near_plane,
    0, 0, 1,
    0, 1, 0);*/

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glTranslatef(xpos, ypos, 0);
}

void Telescope::draw_fake()
{
  glBegin(GL_QUADS);
  glColor3f(1, 1, 1);
  glVertex3f(-radius, -radius, .01);
  glVertex3f(radius, -radius, .01);
  glVertex3f(radius, radius, .01);
  glVertex3f(-radius, radius, .01);
  glEnd();
}

void Telescope::draw_body()
{
  Color c;

  glPushMatrix();

  glColor3f(1, 1, 0);
  gluDisk(disk, radius, 1.1 * radius, divisions, 1);
  gluCylinder(cylinder, 1.1 * radius, 1.1 * radius, .1 * radius, divisions, 1);
  glTranslatef(0, 0, .1*radius);
  gluDisk(disk, radius, 1.1 * radius, divisions, 1);

  glPushAttrib(GL_ENABLE_BIT);
  glDisable(GL_TEXTURE_2D);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, white.c);
  glColor3f(0, 0, 0);
  gluCylinder(cylinder, 1.05 * radius, .95 * radius, .25, divisions, 1);
  glPopAttrib();

  /* Would just do this with a push / pop, but that seems to be broken.
   * glGetMaterialfv also seems to be broken, so we can't use that either. */
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, black.c);

  glTranslatef(0, 0, .25);
  glColor3f(1, 1, 0);
  gluDisk(disk, .95 * radius, 1.05 * radius, divisions, 1);
  gluCylinder(cylinder, 1.05 * radius, 1.05 * radius, .1 * radius,
      divisions, 1);
  glTranslatef(0, 0, .1*radius);
  gluDisk(disk, .95 * radius, 1.05 * radius, divisions, 1);

  glPopMatrix();
}

void Telescope::draw_takedown()
{
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
  glPopMatrix();
}

void Telescope::draw_lens()
{
  gluDisk(disk, 0, radius, divisions, 1);
}

void Telescope::set_divisions(int d)
{
  // Someday we'll put all the quadric stuff in display lists...
  divisions = d;
}

int Telescope::get_divisions()
{
  return divisions;
}

void Telescope::set_radius(GLfloat r)
{
  // Someday this might have to update some display lists
  radius = r;
}

GLfloat Telescope::get_radius()
{
  return radius;
}


TELESCOP.H

#ifndef TELESCOPE_H
#define TELESCOPE_H

class Telescope {
 public:
  Telescope(GLfloat x = 0, GLfloat y = 0);
  ~Telescope();

  /* This draws the "outside" of the telescope - the fov and aspect are
   * needed since it is drawn in eye coordinates */
  void draw_setup(GLfloat fov, GLfloat aspect = 1.0, int perspective = 1);
  void draw_fake();
  void draw_body();
  void draw_takedown();

  /* This just draws the lens - usually it will be used to draw the lens
   * into the stencil buffer */
  void draw_lens();

  /* How finally to divide things as we're drawing */
  void set_divisions(int d);
  int get_divisions();

  /* This is the radius of the lens - the rest of the dimensions depend
   * upon it */
  void set_radius(GLfloat r);
  GLfloat get_radius();

  /* Positions are in eye coordinates and go from [0, 1] */
  GLfloat xpos, ypos;
 private:
  int divisions;

  GLfloat radius;

  GLUquadricObj *disk;
  GLUquadricObj *cylinder;
};

#endif


TIMEDATE.CXX

/*
 *               1993-1996 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <GL/glu.h>

#ifdef X11
#include <GL/glx.h>
#endif

#ifdef WIN32
#include "stonehen.h"
#endif

#include "TimeDate.h"

#include <stdio.h>
#include <stdlib.h>
#ifdef X11
#include <sys/time.h>
#else
#include <time.h>
#endif

inline float sign(float a) {return a > 0.0 ? 1. : (a < 0.0 ? -1. : 0.);}

inline float degrees(float a) {return a * 180.0 / M_PI;}
inline float radians(float a) {return a * M_PI / 180.0;}

inline float arcsin(float a) {return atan2(a, sqrt( (float) 1.0-a*a));}

/* Thirty days hath September... */
const int mday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

TimeDate::TimeDate(int hour, int minute, int second, long usecond)
{
  read_time();
  t.tm_hour = hour;
  t.tm_min = minute;
  t.tm_sec = second;
  usec = usecond;
}

TimeDate TimeDate::operator=(TimeDate a)
{
  usec = a.usec;
  t = a.t;
  return *this;
}

TimeDate TimeDate::operator+(TimeDate a)
{
  TimeDate td;
  td = *this + a.t;
  td.usec = usec + a.usec;
  return td;
}

TimeDate TimeDate::operator+(struct tm t2)
{
  TimeDate td;
  td.usec = 0;
  td.t.tm_sec = t.tm_sec + t2.tm_sec;
  td.t.tm_min = t.tm_min + t2.tm_min;
  td.t.tm_hour = t.tm_hour + t2.tm_hour;
  td.t.tm_mday = t.tm_mday + t2.tm_mday;
  td.t.tm_mon = t.tm_mon + t2.tm_mon;
  td.t.tm_year = t.tm_year + t2.tm_year;
  td.t.tm_wday = t.tm_wday + t2.tm_wday;
  td.t.tm_yday = t.tm_yday + t2.tm_yday;
  return td;
}

TimeDate TimeDate::operator+=(TimeDate a)
{
  /* This is slower than it needs to be */
  *this = *this + a.t;
  return *this;
}

TimeDate TimeDate::operator-(TimeDate a)
{
  TimeDate td;
  td.usec = usec - a.usec;
  td.t.tm_sec = t.tm_sec - a.t.tm_sec;
  td.t.tm_min = t.tm_min - a.t.tm_min;
  td.t.tm_hour = t.tm_hour - a.t.tm_hour;
  td.t.tm_mday = t.tm_mday - a.t.tm_mday;
  td.t.tm_mon = t.tm_mon - a.t.tm_mon;
  td.t.tm_year = t.tm_year - a.t.tm_year;
  td.t.tm_wday = t.tm_wday - a.t.tm_wday;
  td.t.tm_yday = t.tm_yday - a.t.tm_yday;
  return td;
}

TimeDate TimeDate::operator*(float f)
{
  TimeDate td;
  td.usec = (long)((float)usec * f);
  td.t.tm_sec = (int)((float)t.tm_sec * f);
  td.t.tm_min = (int)((float)t.tm_min * f);
  td.t.tm_hour = (int)((float)t.tm_hour * f);
  td.t.tm_mday = (int)((float)t.tm_mday * f);
  td.t.tm_mon = (int)((float)t.tm_mon * f);
  td.t.tm_year = (int)((float)t.tm_year * f);
  td.t.tm_wday = (int)((float)t.tm_wday * f);
  td.t.tm_yday = (int)((float)t.tm_yday * f);
  return td;
}

TimeDate TimeDate::read_time()
{

#ifdef X11
  struct timeval tv;
#else
  LPSYSTEMTIME lpst;
#endif

  time_t cal_time;
  struct tm *tmp;

  cal_time = time(NULL);
  tmp = localtime(&cal_time);

#ifdef X11
  gettimeofday(&tv);
  usec = tv.tv_usec;
#else
  lpst = (LPSYSTEMTIME) malloc(sizeof(SYSTEMTIME));
  GetSystemTime(lpst);
  usec = 1000 * lpst->wMilliseconds;
#endif

  t = *tmp;
  return *this;
}

void TimeDate::print()
{
  puts(asctime(&t));
}

/* The sun_direction routine comes from an awk program by Stuart Levy
 * (slevy@geom.umn.edu) */

/* Day number of March 21st, roughly the vernal equinox */
const float equinox = mday[0] + mday[1] + 21;

/*
 *        Date     E.T. [degrees]
 *        Jan 1    0.82
 *        Jan 31   3.35
 *        Mar 1    3.08
 *        Mar 31   1.02
 *        Apr 30  -0.71
 *        May 30  -0.62
 *        Jun 29   0.87
 *        Jul 29   1.60
 *        Aug 28   0.28
 *        Sep 27  -2.28
 *        Oct 27  -4.03
 *        Nov 26  -3.15
 *        Dec 26   0.19
 */
const float et[] = {
  .82, 3.35, 3.08, 1.02, -.71, -.62, .87,
  1.60, .28, -2.28, -4.03, -3.15, .19, 2.02};

const float DEG = 180. / M_PI;

Point TimeDate::sun_direction(float lon, float lat)
{
  float yearday, eti, etoffset, ET, HA, LON, decl, altitude, azimuth;
  int etindex;
  Point dir;
  long tmp_usec;
  int m;

  tmp_usec = usec;
  usec = 0;
  *this = correct_smaller();

  /*
   *   hour angle of the sun (time since local noon):
   *       HA = (time - noon) * 15 degrees/hour
   *          + (observer''s east longitude - long. of time zone''s
   *       central meridian
   *                           POSITIVE east, NEGATIVE west)
   *          - (15 degrees if time above was daylight savings time, else 0)
   *          - "equation of time" (depends on date, correcting the
   *   hour angle for
   *               the earth''s elliptical orbit and the slope of the ecliptic)
   */
  yearday = (float)t.tm_mday +
    ((float)t.tm_hour + (float)t.tm_min / 60.0) / 24.0;
  for (m = 0; m < t.tm_mon; m++) yearday += mday[m];

  /*
   * Index into equation-of-time table, tabulated at 30-day intervals
   * We''ve added an extra entry at the end of the et table, corresponding
   * to January 25th of the following year, to make interpolation work
   * throughout the year.
   * Use simple linear interpolation.
   */
  eti = (yearday - 1.) / 30.;
#ifdef X11
  etoffset = eti - trunc(eti);
  etindex = (int)trunc(eti) + 1;
#else
  etoffset = eti - (int)eti;
  etindex = (int)eti + 1;
#endif
  ET = et[etindex - 1] + etoffset*(et[etindex+1 - 1] - et[etindex - 1]);
  /* The 90. puts us in the Central time zone */
  HA = ((float)t.tm_hour + (float)t.tm_min/60. - 12.)*15. +
    lon + 90. - ET;

  /*
   *    Sun''s declination:
   *        sun''s longitude = (date - March 21) * 360 degrees / 365.25 days
   *                [This ignores the earth''s elliptical orbit...]
   */
  LON = (yearday - equinox) * 360. / 365.25;
  decl = DEG * arcsin( sin(LON/DEG) * sin(23.4/DEG) );

  /*
   *    Then a spherical triangle calculation to convert the Sun''s
   *    hour angle and declination, and the observer''s latitude,
   *    to give the Sun''s altitude and azimuth (angle east from north).
   */
  altitude = DEG * arcsin( sin(decl/DEG)*sin(lat/DEG)
  + cos(decl/DEG)*cos(lat/DEG)*cos(HA/DEG) );
  azimuth = DEG * atan2( -cos(decl/DEG)*sin(HA/DEG),
sin(decl/DEG)*cos(lat/DEG)
- cos(decl/DEG)*cos(HA/DEG)*sin(lat/DEG) );

  /*
  printf("On %d/%d at %d:%02d, lat %g, lon %g\n",
 t.tm_mon + 1, t.tm_mday + 1, t.tm_hour, t.tm_min, lat, lon);
  printf("HA %.1f ET %.1f Sun lon %.1f decl %.1f alt %.1f az %.1f\n",
 HA,ET,LON,decl,altitude,azimuth);
  */

  dir.pt[2] = sin(radians(altitude));
  dir.pt[0] = cos(radians(azimuth));
  dir.pt[1] = sin(radians(azimuth));
  dir.pt[3] = 0;
  dir.unitize();
  // dir.print();

  usec = tmp_usec;

  return dir;
}

TimeDate TimeDate::correct_bigger()
{
  TimeDate td;
  int days;

  td.t = t;
  td.usec = usec;

  td.t.tm_sec += (int)(td.usec / 1000000);
  td.usec %= 1000000;
  td.t.tm_min += td.t.tm_sec / 60;
  td.t.tm_sec %= 60;
  td.t.tm_hour += td.t.tm_min / 60;
  td.t.tm_min %= 60;

  if (td.t.tm_hour > 23) {
    days = td.t.tm_hour / 24;
    td.t.tm_hour %= 24;
    td.t.tm_mday += days;
    td.t.tm_wday = (td.t.tm_wday + days) % 7;
    td.t.tm_yday = (td.t.tm_yday + days) % 365;
    while (td.t.tm_mday > mday[td.t.tm_mon]) {
      td.t.tm_mday -= mday[td.t.tm_mon++];
      if (td.t.tm_mon >= 12) {
td.t.tm_year += td.t.tm_mon / 12;
td.t.tm_mon %= 12;
      }
    }
  }
  return td;
}

TimeDate TimeDate::correct_smaller()
{
  TimeDate td;

  td.t = t;
  td.usec = usec;

  while (td.usec < 0) {
    td.t.tm_sec--;
    td.usec += 1000000;
  }
  while (td.t.tm_sec < 0) {
    td.t.tm_min--;
    td.t.tm_sec += 60;
  }
  while (td.t.tm_min < 0) {
    td.t.tm_hour--;
    td.t.tm_min += 60;
  }
  while (td.t.tm_hour < 0) {
    td.t.tm_mday--;
    if (td.t.tm_wday) td.t.tm_wday--;
    else td.t.tm_wday = 6;
    td.t.tm_yday--;
    td.t.tm_hour += 24;
  }
  if (td.t.tm_mon < 0 || td.t.tm_year < 0 || td.t.tm_yday < 0)  {
    //fprintf(stderr, "Warning:  day < 0 in TimeDate.c++.\n");
    td.t.tm_mon = td.t.tm_year = td.t.tm_yday = 0;
  }
  return td.correct_bigger();
}


TIMEDATE.H

#ifndef TIMEDATE_H
#define TIMEDATE_H

#include "Point.h"

#include <time.h>

class TimeDate {
 public:
  TimeDate() {};
  TimeDate(int hour, int minute = 0, int second = 0, long usecond = 0);
  TimeDate(struct tm t2) {t = t2;};

  TimeDate operator=(TimeDate a);
  TimeDate operator+(TimeDate a);
  TimeDate operator+(struct tm t2);
  TimeDate operator+=(TimeDate a);
  TimeDate operator-(TimeDate a);
  TimeDate operator*(float f);

  /* This reads the current clock time and returns itself */
  TimeDate read_time();

  void print();

  Point sun_direction(float lon = -93, float lat = 45);
 private:
  /* Call this if the time can only have gotten bigger */
  TimeDate correct_bigger();
  /* Call this if the time could have gotten bigger or smaller */
  TimeDate correct_smaller();

  struct tm t;
  /* This won't be quite accurate down to microseconds, but should be
   * reasonably close */
  long usec;
};

#endif


README.TXT

Using Vertex Arrays for Improved Performance

The VASPHERE sample demonstrates the use of vertex-arrays, available
in OpenGL 1.1. A significant performance improvement can be gained
if objects in the scene are drawn using vertex-arrays instead of
other primitives. In this sample, a lit revolving sphere is drawn
using one of the following methods

 - Vertex-arrays
 - Triangles
 - Triangle-Strips

In addition, either immediate mode or display-list mode are used. Cycle
through the three primitives by pressing the space-bar, and toggle between
immediate-mode and display-list mode by pressing the 'd' key.
The window title shows the triangle/sec figure.

Note
----

VASPHERE will not run on an implementation of OpenGL version 1.0. The OpenGL
for Windows 95 redistributables included on the Win32 SDK are an
implementation of version 1.0.


VASPHERE.C

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>

#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <gl\glaux.h>

#define PI ((float)3.14159265358979323846)

#define WIDTH 512
#define HEIGHT 512

#define COMPUTE_SPHERE_VERTICES(rings, sections) \
    (((rings)+1)*(sections)+2)
#define COMPUTE_SPHERE_TRIANGLES(rings, sections) \
    (((rings)+1)*(sections)*2)
#define RINGS 50
#define SECTIONS 50
#define SVERTICES COMPUTE_SPHERE_VERTICES(RINGS, SECTIONS)
#define STRIANGLES COMPUTE_SPHERE_TRIANGLES(RINGS, SECTIONS)

#define RGB_COLOR(red, green, blue) \
    (((DWORD)(BYTE)(red) << 0) | \
     ((DWORD)(BYTE)(green) << 8) | \
     ((DWORD)(BYTE)(blue) << 16))

#define FRANDOM(x) (((float)rand() / RAND_MAX) * (x))

#define DROT 10.0f

#define NLISTS 3

typedef struct
{
    float fX, fY, fZ;
    float fNx, fNy, fNz;
    DWORD dwColor;
} VERTEX;

typedef struct
{
    int iV1;
    int iV2;
    int iV3;
} TRIANGLE;

typedef struct _SPHERE
{
    GLfloat fXr, fYr, fZr;
    GLfloat fDXr, fDYr, fDZr;
} SPHERE;

SPHERE sphere;

BOOL fSingle = FALSE;
BOOL fDisplayList = TRUE;

int iCurrentType = 0;
GLint dlLists[NLISTS];
char *pszListType[NLISTS] =
{
    "Vertex Array", "Direct Triangles", "Direct Strips"
};

void DrawVertexArray(void);
void DrawTriangles(void);
void DrawStrips(void);
void (*pfnDraw[NLISTS])(void) =
{
    DrawVertexArray, DrawTriangles, DrawStrips
};

VERTEX vtxSphere[SVERTICES];
TRIANGLE triSphere[STRIANGLES];

void *Malloc(size_t cb)
{
    void *pv;

    pv = malloc(cb);
    if (pv == NULL)
    {
        printf("Unable to alloc %d bytes\n", cb);
        exit(1);
    }
    return pv;
}

void GenerateSphere(float fRadius, int iRings, int iSections,
                    VERTEX *pvtx, TRIANGLE *ptri)
{
    float fTheta, fPhi;             /* Angles used to sweep around sphere */
    float fDTheta, fDPhi;           /* Angle between each section and ring */
    float fX, fY, fZ, fV, fRSinTheta;  /* Temporary variables */
    int   i, j, n, m;               /* counters */
    int   iVert, iTri;              /* Internal vertex and triangle count */

    iTri = COMPUTE_SPHERE_TRIANGLES(iRings, iSections);
    iVert = COMPUTE_SPHERE_VERTICES(iRings, iSections);

    /*
     * Generate vertices at the top and bottom points.
     */
    pvtx[0].fX = 0.0f;
    pvtx[0].fY = fRadius;
    pvtx[0].fZ = 0.0f;
    pvtx[0].fNx = 0.0f;
    pvtx[0].fNy = 1.0f;
    pvtx[0].fNz = 0.0f;
    pvtx[0].dwColor = RGB_COLOR(0, 0, 255);
    pvtx[iVert - 1].fX = 0.0f;
    pvtx[iVert - 1].fY = -fRadius;
    pvtx[iVert - 1].fZ = 0.0f;
    pvtx[iVert - 1].fNx = 0.0f;
    pvtx[iVert - 1].fNy = -1.0f;
    pvtx[iVert - 1].fNz = 0.0f;
    pvtx[iVert - 1].dwColor = RGB_COLOR(0, 255, 0);

    /*
     * Generate vertex points for rings
     */
    fDTheta = PI / (float) (iRings + 2);
    fDPhi = 2.0f * PI / (float) iSections;
    n = 1; /* vertex being generated, begins at 1 to skip top point */
    fTheta = fDTheta;

    for (i = 0; i <= iRings; i++)
    {
        fY = (float)(fRadius * cos(fTheta)); /* y is the same for each ring */
        fV = fTheta / PI; /* v is the same for each ring */
        fRSinTheta = (float)(fRadius * sin(fTheta));
        fPhi = 0.0f;

        for (j = 0; j < iSections; j++)
        {
            fX = (float)(fRSinTheta * sin(fPhi));
            fZ = (float)(fRSinTheta * cos(fPhi));
            pvtx[n].fX = fX;
            pvtx[n].fZ = fZ;
            pvtx[n].fY = fY;
            pvtx[n].fNx = fX / fRadius;
            pvtx[n].fNy = fY / fRadius;
            pvtx[n].fNz = fZ / fRadius;
            if (n & 1)
            {
                pvtx[n].dwColor = RGB_COLOR(0, 0, 255);
            }
            else
            {
                pvtx[n].dwColor = RGB_COLOR(0, 255, 0);
            }
            fPhi += fDPhi;
            n++;
        }

        fTheta += fDTheta;
    }

    /*
     * Generate triangles for top and bottom caps.
     */
    for (i = 0; i < iSections; i++)
    {
        ptri[i].iV1 = 0;
        ptri[i].iV2 = i + 1;
        ptri[i].iV3 = 1 + ((i + 1) % iSections);
        ptri[iTri - iSections + i].iV1 = iVert - 1;
        ptri[iTri - iSections + i].iV2 = iVert - 2 - i;
        ptri[iTri - iSections + i].iV3 = iVert - 2 - ((1 + i) % iSections);
    }

    /*
     * Generate triangles for the rings
     */
    m = 1; /* first vertex in current ring,begins at 1 to skip top point*/
    n = iSections; /* triangle being generated, skip the top cap */

    for (i = 0; i < iRings; i++)
    {
        for (j = 0; j < iSections; j++)
        {
            ptri[n].iV1 = m + j;
            ptri[n].iV2 = m + iSections + j;
            ptri[n].iV3 = m + iSections + ((j + 1) % iSections);
            ptri[n + 1].iV1 = ptri[n].iV1;
            ptri[n + 1].iV2 = ptri[n].iV3;
            ptri[n + 1].iV3 = m + ((j + 1) % iSections);
            n += 2;
        }

        m += iSections;
    }
}

void InitSphere(void)
{
    GenerateSphere(1.0f, RINGS, SECTIONS, vtxSphere, triSphere);

    glVertexPointer(3, GL_FLOAT, sizeof(VERTEX), &vtxSphere[0].fX);
    glNormalPointer(GL_FLOAT, sizeof(VERTEX), &vtxSphere[0].fNx);
    glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VERTEX), &vtxSphere[0].dwColor);

    srand(time(NULL));
    sphere.fXr = 0.0f;
    sphere.fYr = 0.0f;
    sphere.fZr = 0.0f;
    sphere.fDXr = DROT - FRANDOM(2 * DROT);
    sphere.fDYr = DROT - FRANDOM(2 * DROT);
    sphere.fDZr = DROT - FRANDOM(2 * DROT);
}

void UpdateSphere(void)
{
    sphere.fXr += sphere.fDXr;
    sphere.fYr += sphere.fDYr;
    sphere.fZr += sphere.fDZr;
}

void DrawVertexArray(void)
{
    glDrawElements(GL_TRIANGLES, STRIANGLES*3, GL_UNSIGNED_INT, triSphere);
}

void Vertex(int iVert)
{
    VERTEX *pvtx;

    pvtx = &vtxSphere[iVert];
    glColor3ubv((GLubyte *)&pvtx->dwColor);
    glNormal3fv(&pvtx->fNx);
    glVertex3fv(&pvtx->fX);
}

void DrawTriangles(void)
{
    int iVert, *pidx;

    glBegin(GL_TRIANGLES);
    pidx = (int *)&triSphere[0];
    for (iVert = 0; iVert < STRIANGLES*3; iVert++)
    {
        Vertex(*pidx++);
    }
    glEnd();
}

void DrawStrips(void)
{
    int iIdxBase;
    int iRing, iSection;

    // Triangle fans for top and bottom caps
    glBegin(GL_TRIANGLE_FAN);

    Vertex(0);
    iIdxBase = 1;
    for (iSection = 0; iSection <= SECTIONS; iSection++)
    {
        Vertex(iIdxBase+(iSection % SECTIONS));
    }

    glEnd();

    glBegin(GL_TRIANGLE_FAN);

    Vertex(SVERTICES-1);
    iIdxBase = SVERTICES-SECTIONS-1;
    for (iSection = SECTIONS; iSection >= 0 ; iSection--)
    {
        Vertex(iIdxBase+(iSection % SECTIONS));
    }

    glEnd();

    // Triangle strips for each ring
    iIdxBase = 1;
    for (iRing = 0; iRing < RINGS; iRing++)
    {
        glBegin(GL_TRIANGLE_STRIP);

        for (iSection = 0; iSection <= SECTIONS; iSection++)
        {
            Vertex(iIdxBase+(iSection % SECTIONS));
            Vertex(iIdxBase+(iSection % SECTIONS)+SECTIONS);
        }

        glEnd();

        iIdxBase += SECTIONS;
    }
}

void Init(void)
{
    float fv4[4];
    int iv1[1];
    int i;
    GLint dlBase;

    InitSphere();

    fv4[0] = 0.05f;
    fv4[1] = 0.05f;
    fv4[2] = 0.05f;
    fv4[3] = 1.0f;
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fv4);

    fv4[0] = 0.0f;
    fv4[1] = 1.0f;
    fv4[2] = 1.0f;
    fv4[3] = 0.0f;
    glLightfv(GL_LIGHT0, GL_POSITION, fv4);
    fv4[0] = 0.9f;
    fv4[1] = 0.9f;
    fv4[2] = 0.9f;
    fv4[3] = 1.0f;
    glLightfv(GL_LIGHT0, GL_DIFFUSE, fv4);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);

    fv4[0] = 0.6f;
    fv4[1] = 0.6f;
    fv4[2] = 0.6f;
    fv4[3] = 1.0f;
    glMaterialfv(GL_FRONT, GL_SPECULAR, fv4);
    iv1[0] = 40;
    glMaterialiv(GL_FRONT, GL_SHININESS, iv1);

    glEnable(GL_CULL_FACE);

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45, 1, .01, 15);
    gluLookAt(0, 0, 10, 0, 0, 0, 0, 1, 0);
    glMatrixMode(GL_MODELVIEW);

    dlBase = glGenLists(NLISTS);
    for (i = 0; i < NLISTS; i++)
    {
        dlLists[i] = dlBase+i;
        glNewList(dlLists[i], GL_COMPILE);
        pfnDraw[i]();
        glEndList();
    }
}

DWORD dwTotalMillis = 0;
int iTotalTriangles = 0;

void ResetTotals(void)
{
    dwTotalMillis = 0;
    iTotalTriangles = 0;
}

void CALLBACK Redraw(void)
{
    DWORD dwMillis;
    char szMsg[80];

    dwMillis = GetTickCount();

    glClear(GL_COLOR_BUFFER_BIT);

    glLoadIdentity();
    glRotatef(sphere.fXr, 1.0f, 0.0f, 0.0f);
    glRotatef(sphere.fYr, 0.0f, 1.0f, 0.0f);
    glRotatef(sphere.fZr, 0.0f, 0.0f, 1.0f);

    if (fDisplayList)
    {
        glCallList(dlLists[iCurrentType]);
    }
    else
    {
        pfnDraw[iCurrentType]();
    }

    UpdateSphere();

    if (fSingle)
    {
        glFlush();
    }
    else
    {
        auxSwapBuffers();
    }

    dwMillis = GetTickCount()-dwMillis;

    dwTotalMillis += dwMillis;
    iTotalTriangles += STRIANGLES;

    if (dwTotalMillis > 1000)
    {
        sprintf(szMsg, "%s%s: %.3lf tri/sec",
                fDisplayList ? "Display List " : "",
                pszListType[iCurrentType],
                (double)iTotalTriangles*1000.0/dwTotalMillis);
        SetWindowText(auxGetHWND(), szMsg);

        ResetTotals();
    }
}

void CALLBACK Reshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
}

void CALLBACK Keyd(void)
{
    fDisplayList = !fDisplayList;
    ResetTotals();
}

void CALLBACK KeySPACE(void)
{
    iCurrentType = (iCurrentType+1) % NLISTS;
    ResetTotals();
}

void _CRTAPI1 main(int argc, char **argv)
{
    GLenum eMode;

    while (--argc > 0)
    {
        argv++;

        if (!strcmp(*argv, "-sb"))
        {
            fSingle = TRUE;
        }
    }

    auxInitPosition(10, 10, WIDTH, HEIGHT);
    eMode = AUX_RGB;
    if (!fSingle)
    {
        eMode |= AUX_DOUBLE;
    }
    auxInitDisplayMode(eMode);
    auxInitWindow("Vertex Array/Direct Comparison");

    auxReshapeFunc(Reshape);
    auxIdleFunc(Redraw);

    auxKeyFunc(AUX_d, Keyd);
    auxKeyFunc(AUX_SPACE, KeySPACE);

    Init();
    auxMainLoop(Redraw);
}


3D.H

/*
 * 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.
 */
voiderror(char *);
voiddiff3(GLdouble [3], GLdouble [3], GLdouble [3]);
voidadd3(GLdouble [3], GLdouble [3], GLdouble [3]);
voidscalarmult(GLdouble, GLdouble [3], GLdouble [3]);
GLdoubledot3(GLdouble [3], GLdouble [3]);
GLdoublelength3(GLdouble [3]);
GLdoubledist3(GLdouble [3], GLdouble [3]);
voidcopy3(GLdouble [3], GLdouble [3]);
voidcrossprod(GLdouble [3], GLdouble [3], GLdouble [3]);
voidnormalize(GLdouble [3]);
voidprint3(GLdouble [3]);
voidprintmat3(GLdouble [3][3]);
voididentifymat3(GLdouble [3][3]);
voidcopymat3(GLdouble *, GLdouble *);
voidxformvec3(GLdouble [3], GLdouble [3][3], GLdouble [3]);

void m_resetmatrixstack();
void m_xformpt(GLdouble [3], GLdouble [3], GLdouble [3], GLdouble [3]);
void m_xformptonly(GLdouble [3], GLdouble [3]);
void m_pushmatrix();
void m_popmatrix();
void m_shear(GLdouble, GLdouble, GLdouble);
void m_translate(GLdouble, GLdouble, GLdouble);
void m_scale(GLdouble, GLdouble, GLdouble);
void m_rotate(GLdouble, char);


FONT.C

/*
 * 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 <gl/glaux.h>
#include "tk.h"

#define static

static GLuint bitmapBase;
void APIENTRY auxDrawStrAW(LPCSTR str, BOOL bUnicode);

void auxCreateFont(void)
{
    bitmapBase = glGenLists(256);
    if (tkCreateBitmapFont(bitmapBase) == GL_FALSE) {
        auxQuit();
    }
}

void APIENTRY auxDrawStrA(LPCSTR str)
{
    auxDrawStrAW(str, FALSE);
}

void APIENTRY auxDrawStrW(LPCWSTR str)
{
    auxDrawStrAW((LPCSTR)str, TRUE);
}

void APIENTRY auxDrawStrAW(LPCSTR str, BOOL bUnicode)
{

    glPushAttrib(GL_LIST_BIT);
    glListBase(bitmapBase);
    if (bUnicode)
        glCallLists(wcslen((LPWSTR) str), GL_UNSIGNED_SHORT, str);
    else
glCallLists(strlen(str), GL_UNSIGNED_BYTE, str);
    glPopAttrib();
}


GLAUX.C

/*
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <windows.h>
#include <GL/gl.h>
#include <gl/glaux.h>
#include "tk.h"

#define static

#if defined(__cplusplus) || defined(c_plusplus)
#define class c_class
#endif

static struct {
    int keyField;
    void (*KeyFunc)(void);
} keyTable[200];

static struct {
    int mouseField;
    void (*MouseFunc)(AUX_EVENTREC *);
} mouseDownTable[20], mouseUpTable[20], mouseLocTable[20];

static int keyTableCount = 0;
static int mouseDownTableCount = 0;
static int mouseUpTableCount = 0;
static int mouseLocTableCount = 0;
static GLenum displayModeType = 0;
GLenum APIENTRY auxInitWindowAW(LPCSTR title, BOOL bUnicode);

static void DefaultHandleReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho((GLdouble)0.0, (GLdouble)w, (GLdouble)0.0, (GLdouble)h, (GLdouble)-1.0, (GLdouble)1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

static void DefaultHandleExpose(int w, int h)
{
}

static GLenum MouseLoc(int x, int y, GLenum button)
{
    AUX_EVENTREC info;
    GLenum flag;
    int i;

    flag = GL_FALSE;
    for (i = 0; i < mouseLocTableCount; i++) {
        if ((int)(button & AUX_LEFTBUTTON) == mouseLocTable[i].mouseField) {
    info.event = AUX_MOUSELOC;
    info.data[AUX_MOUSEX] = x;
    info.data[AUX_MOUSEY] = y;
    info.data[AUX_MOUSESTATUS] = AUX_LEFTBUTTON;
    (*mouseLocTable[i].MouseFunc)(&info);
    flag |= GL_TRUE;
}
        if ((int)(button & AUX_RIGHTBUTTON) == mouseLocTable[i].mouseField) {
    info.event = AUX_MOUSELOC;
    info.data[AUX_MOUSEX] = x;
    info.data[AUX_MOUSEY] = y;
    info.data[AUX_MOUSESTATUS] = AUX_RIGHTBUTTON;
    (*mouseLocTable[i].MouseFunc)(&info);
    flag |= GL_TRUE;
}
        if ((int)(button & AUX_MIDDLEBUTTON) == mouseLocTable[i].mouseField) {
    info.event = AUX_MOUSELOC;
    info.data[AUX_MOUSEX] = x;
    info.data[AUX_MOUSEY] = y;
    info.data[AUX_MOUSESTATUS] = AUX_MIDDLEBUTTON;
    (*mouseLocTable[i].MouseFunc)(&info);
    flag |= GL_TRUE;
}
    }
    return flag;
}

static GLenum MouseUp(int x, int y, GLenum button)
{
    AUX_EVENTREC info;
    GLenum flag;
    int i;

    flag = GL_FALSE;
    for (i = 0; i < mouseUpTableCount; i++) {
        if ((int)(button & AUX_LEFTBUTTON) == mouseUpTable[i].mouseField) {
    info.event = AUX_MOUSEUP;
    info.data[AUX_MOUSEX] = x;
    info.data[AUX_MOUSEY] = y;
    info.data[AUX_MOUSESTATUS] = AUX_LEFTBUTTON;
    (*mouseUpTable[i].MouseFunc)(&info);
    flag |= GL_TRUE;
}
        if ((int)(button & AUX_RIGHTBUTTON) == mouseUpTable[i].mouseField) {
    info.event = AUX_MOUSEUP;
    info.data[AUX_MOUSEX] = x;
    info.data[AUX_MOUSEY] = y;
    info.data[AUX_MOUSESTATUS] = AUX_RIGHTBUTTON;
    (*mouseUpTable[i].MouseFunc)(&info);
    flag |= GL_TRUE;
}
        if ((int)(button & AUX_MIDDLEBUTTON) == mouseUpTable[i].mouseField) {
    info.event = AUX_MOUSEUP;
    info.data[AUX_MOUSEX] = x;
    info.data[AUX_MOUSEY] = y;
    info.data[AUX_MOUSESTATUS] = AUX_MIDDLEBUTTON;
    (*mouseUpTable[i].MouseFunc)(&info);
    flag |= GL_TRUE;
}
    }
    return flag;
}

static GLenum MouseDown(int x, int y, GLenum button)
{
    AUX_EVENTREC info;
    GLenum flag;
    int i;

    flag = GL_FALSE;
    for (i = 0; i < mouseDownTableCount; i++) {
        if ((int)(button & AUX_LEFTBUTTON) == mouseDownTable[i].mouseField) {
    info.event = AUX_MOUSEDOWN;
    info.data[AUX_MOUSEX] = x;
    info.data[AUX_MOUSEY] = y;
    info.data[AUX_MOUSESTATUS] = AUX_LEFTBUTTON;
    (*mouseDownTable[i].MouseFunc)(&info);
    flag |= GL_TRUE;
}
        if ((int)(button & AUX_RIGHTBUTTON) == mouseDownTable[i].mouseField) {
    info.event = AUX_MOUSEDOWN;
    info.data[AUX_MOUSEX] = x;
    info.data[AUX_MOUSEY] = y;
    info.data[AUX_MOUSESTATUS] = AUX_RIGHTBUTTON;
    (*mouseDownTable[i].MouseFunc)(&info);
    flag |= GL_TRUE;
}
        if ((int)(button & AUX_MIDDLEBUTTON) == mouseDownTable[i].mouseField) {
    info.event = AUX_MOUSEDOWN;
    info.data[AUX_MOUSEX] = x;
    info.data[AUX_MOUSEY] = y;
    info.data[AUX_MOUSESTATUS] = AUX_MIDDLEBUTTON;
    (*mouseDownTable[i].MouseFunc)(&info);
    flag |= GL_TRUE;
}
    }
    return flag;
}

static GLenum KeyDown(int key, GLenum status)
{
    GLenum flag;
    int i;

    flag = GL_FALSE;
    if (keyTableCount) {
for (i = 0; i < keyTableCount; i++) {
    if (key == keyTable[i].keyField) {
(*keyTable[i].KeyFunc)();
flag |= GL_TRUE;
    }
}
    }
    return flag;
}

void auxExposeFunc(AUXEXPOSEPROC Func)
{
    tkExposeFunc(Func);
}

void auxReshapeFunc(AUXRESHAPEPROC Func)
{
    tkExposeFunc((AUXEXPOSEPROC) Func);
    tkReshapeFunc(Func);
}

void auxIdleFunc(AUXIDLEPROC Func)
{
    tkIdleFunc(Func);
}

void auxKeyFunc(int key, AUXKEYPROC Func)
{
    keyTable[keyTableCount].keyField = key;
    keyTable[keyTableCount++].KeyFunc = Func;
}

void auxMouseFunc(int mouse, int mode, AUXMOUSEPROC Func)
{
    if (mode == AUX_MOUSEDOWN) {
mouseDownTable[mouseDownTableCount].mouseField = mouse;
mouseDownTable[mouseDownTableCount++].MouseFunc = Func;
    } else if (mode == AUX_MOUSEUP) {
mouseUpTable[mouseUpTableCount].mouseField = mouse;
mouseUpTable[mouseUpTableCount++].MouseFunc = Func;
    } else if (mode == AUX_MOUSELOC) {
mouseLocTable[mouseLocTableCount].mouseField = mouse;
mouseLocTable[mouseLocTableCount++].MouseFunc = Func;
    }
}

void auxMainLoop(AUXMAINPROC Func)
{
    tkDisplayFunc(Func);
    tkExec();
}

void auxInitPosition(int x, int y, int width, int height)
{
    tkInitPosition(x, y, width, height);
}

void auxInitDisplayMode(GLenum type)
{
    displayModeType = type;
    tkInitDisplayMode(type);
}

void auxInitDisplayModePolicy(GLenum type)
{
    tkInitDisplayModePolicy(type);
}

GLenum auxInitDisplayModeID(GLint id)
{
    return tkInitDisplayModeID(id);
}

GLenum APIENTRY auxInitWindowA(LPCSTR title)
{
    return auxInitWindowAW(title,FALSE);
}

GLenum APIENTRY auxInitWindowW(LPCWSTR title)
{
    return auxInitWindowAW((LPCSTR)title,TRUE);
}

GLenum APIENTRY auxInitWindowAW(LPCSTR title, BOOL bUnicode)
{
    int useDoubleAsSingle = 0;

    if (tkInitWindowAW((char *)title, bUnicode) == GL_FALSE) {
if (AUX_WIND_IS_SINGLE(displayModeType)) {
    tkInitDisplayMode(displayModeType | AUX_DOUBLE);
    if (tkInitWindowAW((char *)title, bUnicode) == GL_FALSE) {
return GL_FALSE;    /*  curses, foiled again*/
            }
            MESSAGEBOX(GetFocus(), "Can't initialize a single buffer visual. "
                                   "Will use a double buffer visual instead, "
                                   "only drawing into the front buffer.",
                                   "Warning", MB_OK);
    displayModeType = displayModeType | AUX_DOUBLE;
    useDoubleAsSingle = 1;
}
    }
    tkReshapeFunc(DefaultHandleReshape);
    tkExposeFunc(DefaultHandleExpose);
    tkMouseUpFunc(MouseUp);
    tkMouseDownFunc(MouseDown);
    tkMouseMoveFunc(MouseLoc);
    tkKeyDownFunc(KeyDown);
    auxKeyFunc(AUX_ESCAPE, auxQuit);
    glClearColor((GLclampf)0.0, (GLclampf)0.0, (GLclampf)0.0, (GLclampf)1.0);
    glClearIndex((GLfloat)0.0);
    glLoadIdentity();
    if (useDoubleAsSingle)
glDrawBuffer(GL_FRONT);
    return GL_TRUE;
}

void auxCloseWindow(void)
{
    tkCloseWindow();
    keyTableCount = 0;
    mouseDownTableCount = 0;
    mouseUpTableCount = 0;
    mouseLocTableCount = 0;
}

void auxQuit(void)
{
    tkQuit();
}

void auxSwapBuffers(void)
{
    tkSwapBuffers();
}

HWND auxGetHWND(void)
{
    return tkGetHWND();
}

HDC auxGetHDC(void)
{
    return tkGetHDC();
}

HGLRC auxGetHGLRC(void)
{
    return tkGetHRC();
}

GLenum auxGetDisplayModePolicy(void)
{
    return tkGetDisplayModePolicy();
}

GLint auxGetDisplayModeID(void)
{
    return tkGetDisplayModeID();
}

GLenum auxGetDisplayMode(void)
{
    return tkGetDisplayMode();
}

void auxSetOneColor(int index, float r, float g, float b)
{
    tkSetOneColor(index, r, g, b);
}

void auxSetFogRamp(int density, int startIndex)
{
    tkSetFogRamp(density, startIndex);
}

void auxSetGreyRamp(void)
{
    tkSetGreyRamp();
}

void auxSetRGBMap(int size, float *rgb)
{
    tkSetRGBMap(size, rgb);
}

int auxGetColorMapSize(void)
{
    return tkGetColorMapSize();
}

void auxGetMouseLoc(int *x, int *y)
{
    tkGetMouseLoc(x, y);
}


IMAGE.C

/*
 * 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 <gl/glaux.h>
#include "tk.h"

#define static

AUX_RGBImageRec * APIENTRY auxRGBImageLoadA(LPCSTR filename)
{
    return (AUX_RGBImageRec *)tkRGBImageLoadAW((char *)filename, FALSE);
}

AUX_RGBImageRec * APIENTRY auxRGBImageLoadW(LPCWSTR filename)
{
    return (AUX_RGBImageRec *)tkRGBImageLoadAW((char *)filename, TRUE);
}

AUX_RGBImageRec * APIENTRY auxDIBImageLoadA(LPCSTR filename)
{
    return (AUX_RGBImageRec *)tkDIBImageLoadAW((char *)filename, FALSE);
}

AUX_RGBImageRec * APIENTRY auxDIBImageLoadW(LPCWSTR filename)
{
    return (AUX_RGBImageRec *)tkDIBImageLoadAW((char *)filename, TRUE);
}


SHAPES.C

/*
 * 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 <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <gl/glaux.h>
#include "3d.h"

#define static

#define SPHEREWIRE      0
#define CUBEWIRE        1
#define BOXWIRE         2
#define TORUSWIRE       3
#define CYLINDERWIRE    4
#define ICOSAWIRE       5
#define OCTAWIRE        6
#define TETRAWIRE       7
#define DODECAWIRE      8
#define CONEWIRE        9
#define SPHERESOLID     10
#define CUBESOLID       11
#define BOXSOLID        12
#define TORUSSOLID      13
#define CYLINDERSOLID   14
#define ICOSASOLID      15
#define OCTASOLID       16
#define TETRASOLID      17
#define DODECASOLID     18
#define CONESOLID       19

#define PI ((GLdouble)3.1415926535897)

/*      structure for each geometric object     */
typedef struct model {
    GLuint list;        /*  display list to render object   */
    struct model *ptr;  /*  pointer to next object      */
    int numParam;       /*  # of parameters             */
    GLdouble *params;   /*  array with parameters       */
} MODEL, *MODELPTR;

/*      array of linked lists--used to keep track of display lists
 *      for each different type of geometric object.
 */
static MODELPTR lists[25] = {
    NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL
};

GLuint findList (int index, GLdouble *paramArray, int size);
int compareParams (GLdouble *oneArray, GLdouble *twoArray, int size);
GLuint makeModelPtr (int index, GLdouble *sizeArray, int count);

static void drawbox(GLdouble, GLdouble, GLdouble,
        GLdouble, GLdouble, GLdouble, GLenum);
static void doughnut(GLdouble, GLdouble, GLint, GLint, GLenum);
static void icosahedron(GLdouble *, GLdouble, GLenum);
static void octahedron(GLdouble *, GLdouble, GLenum);
static void tetrahedron(GLdouble *, GLdouble, GLenum);
static void subdivide(int, GLdouble *, GLdouble *, GLdouble *,
        GLdouble *, GLdouble, GLenum, int);
static void drawtriangle(int, int, int,
        GLdouble *, GLdouble, GLenum, int);
static void recorditem(GLdouble *, GLdouble *, GLdouble *,
        GLdouble *, GLdouble, GLenum, int);
static void initdodec(void);
static void dodecahedron(GLdouble *, GLdouble, GLenum);
static void pentagon(int, int, int, int, int, GLenum);

/*  Render wire frame or solid sphere.  If no display list with
 *  the current model size exists, create a new display list.
 */
void auxWireSphere (GLdouble radius)
{
    GLUquadricObj *quadObj;
    GLdouble *sizeArray;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = radius;
    displayList = findList (SPHEREWIRE, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (SPHEREWIRE, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            quadObj = gluNewQuadric ();
            gluQuadricDrawStyle (quadObj, GLU_LINE);
            gluSphere (quadObj, radius, 16, 16);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

void auxSolidSphere (GLdouble radius)
{
    GLUquadricObj *quadObj;
    GLdouble *sizeArray;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = radius;
    displayList = findList (SPHERESOLID, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (SPHERESOLID, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            quadObj = gluNewQuadric ();
            gluQuadricDrawStyle (quadObj, GLU_FILL);
            gluQuadricNormals (quadObj, GLU_SMOOTH);
            gluSphere (quadObj, radius, 16, 16);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

/*  Render wire frame or solid cube.  If no display list with
 *  the current model size exists, create a new display list.
 */
void auxWireCube (GLdouble size)
{
    GLdouble *sizeArray;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = size;
    displayList = findList (CUBEWIRE, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (CUBEWIRE, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            drawbox(-size/(GLdouble)2., size/(GLdouble)2., -size/(GLdouble)2., size/(GLdouble)2.,
                -size/(GLdouble)2., size/(GLdouble)2., GL_LINE_LOOP);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

void auxSolidCube (GLdouble size)
{
    GLdouble *sizeArray;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = size;
    displayList = findList (CUBESOLID, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (CUBESOLID, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            drawbox(-size/(GLdouble)2., size/(GLdouble)2., -size/(GLdouble)2., size/(GLdouble)2.,
                -size/(GLdouble)2., size/(GLdouble)2., GL_QUADS);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

/*  Render wire frame or solid cube.  If no display list with
 *  the current model size exists, create a new display list.
 */
void auxWireBox (GLdouble width, GLdouble height, GLdouble depth)
{
    GLdouble *sizeArray, *tmp;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 3);
    tmp = sizeArray;
    *tmp++ = width;
    *tmp++ = height;
    *tmp++ = depth;
    displayList = findList (BOXWIRE, sizeArray, 3);

    if (displayList == 0) {
        glNewList(makeModelPtr (BOXWIRE, sizeArray, 3),
            GL_COMPILE_AND_EXECUTE);
            drawbox(-width/(GLdouble)2., width/(GLdouble)2., -height/(GLdouble)2., height/(GLdouble)2.,
                -depth/(GLdouble)2., depth/(GLdouble)2., GL_LINE_LOOP);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

void auxSolidBox (GLdouble width, GLdouble height, GLdouble depth)
{
    GLdouble *sizeArray, *tmp;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 3);
    tmp = sizeArray;
    *tmp++ = width;
    *tmp++ = height;
    *tmp++ = depth;
    displayList = findList (BOXSOLID, sizeArray, 3);

    if (displayList == 0) {
        glNewList(makeModelPtr (BOXSOLID, sizeArray, 3),
            GL_COMPILE_AND_EXECUTE);
            drawbox(-width/(GLdouble)2., width/(GLdouble)2., -height/(GLdouble)2., height/(GLdouble)2.,
                -depth/(GLdouble)2., depth/(GLdouble)2., GL_QUADS);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

/*  Render wire frame or solid tori.  If no display list with
 *  the current model size exists, create a new display list.
 */
void auxWireTorus (GLdouble innerRadius, GLdouble outerRadius)
{
    GLdouble *sizeArray, *tmp;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 2);
    tmp = sizeArray;
    *tmp++ = innerRadius;
    *tmp++ = outerRadius;
    displayList = findList (TORUSWIRE, sizeArray, 2);

    if (displayList == 0) {
        glNewList(makeModelPtr (TORUSWIRE, sizeArray, 2),
            GL_COMPILE_AND_EXECUTE);
            doughnut(innerRadius, outerRadius, 5, 10, GL_LINE_LOOP);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

void auxSolidTorus (GLdouble innerRadius, GLdouble outerRadius)
{
    GLdouble *sizeArray, *tmp;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 2);
    tmp = sizeArray;
    *tmp++ = innerRadius;
    *tmp++ = outerRadius;
    displayList = findList (TORUSSOLID, sizeArray, 2);

    if (displayList == 0) {
        glNewList(makeModelPtr (TORUSSOLID, sizeArray, 2),
            GL_COMPILE_AND_EXECUTE);
            doughnut(innerRadius, outerRadius, 8, 15, GL_QUADS);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

/*  Render wire frame or solid cylinders.  If no display list with
 *  the current model size exists, create a new display list.
 */
void auxWireCylinder (GLdouble radius, GLdouble height)
{
    GLUquadricObj *quadObj;
    GLdouble *sizeArray, *tmp;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 2);
    tmp = sizeArray;
    *tmp++ = radius;
    *tmp++ = height;
    displayList = findList (CYLINDERWIRE, sizeArray, 2);

    if (displayList == 0) {
        glNewList(makeModelPtr (CYLINDERWIRE, sizeArray, 2),
            GL_COMPILE_AND_EXECUTE);
            glPushMatrix ();
            glRotatef ((GLfloat)90.0, (GLfloat)1.0, (GLfloat)0.0, (GLfloat)0.0);
            glTranslatef ((GLfloat)0.0, (GLfloat)0.0, (GLfloat)-1.0);
            quadObj = gluNewQuadric ();
            gluQuadricDrawStyle (quadObj, GLU_LINE);
            gluCylinder (quadObj, radius, radius, height, 12, 2);
            glPopMatrix ();
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

void auxSolidCylinder (GLdouble radius, GLdouble height)
{
    GLUquadricObj *quadObj;
    GLdouble *sizeArray, *tmp;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 2);
    tmp = sizeArray;
    *tmp++ = radius;
    *tmp++ = height;
    displayList = findList (CYLINDERWIRE, sizeArray, 2);

    if (displayList == 0) {
        glNewList(makeModelPtr (CYLINDERWIRE, sizeArray, 2),
            GL_COMPILE_AND_EXECUTE);
            glPushMatrix ();
            glRotatef ((GLfloat)90.0, (GLfloat)1.0, (GLfloat)0.0, (GLfloat)0.0);
            glTranslatef ((GLfloat)0.0, (GLfloat)0.0, (GLfloat)-1.0);
            quadObj = gluNewQuadric ();
            gluQuadricDrawStyle (quadObj, GLU_FILL);
            gluQuadricNormals (quadObj, GLU_SMOOTH);
            gluCylinder (quadObj, radius, radius, height, 12, 2);
            glPopMatrix ();
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

/*  Render wire frame or solid icosahedra.  If no display list with
 *  the current model size exists, create a new display list.
 */
void auxWireIcosahedron (GLdouble radius)
{
    GLdouble *sizeArray;
    GLuint displayList;
    GLdouble center[3] = {(GLdouble)0.0, (GLdouble)0.0, (GLdouble)0.0};

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = radius;
    displayList = findList (ICOSAWIRE, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (ICOSAWIRE, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            icosahedron (center, radius, GL_LINE_LOOP);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

void auxSolidIcosahedron (GLdouble radius)
{
    GLdouble *sizeArray;
    GLuint displayList;
    GLdouble center[3] = {(GLdouble)0.0, (GLdouble)0.0, (GLdouble)0.0};

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = radius;
    displayList = findList (ICOSASOLID, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (ICOSASOLID, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            icosahedron (center, radius, GL_TRIANGLES);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

/*  Render wire frame or solid octahedra.  If no display list with
 *  the current model size exists, create a new display list.
 */
void auxWireOctahedron (GLdouble radius)
{
    GLdouble *sizeArray;
    GLuint displayList;
    GLdouble center[3] = {(GLdouble)0.0, (GLdouble)0.0, (GLdouble)0.0};

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = radius;
    displayList = findList (OCTAWIRE, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (OCTAWIRE, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            octahedron (center, radius, GL_LINE_LOOP);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

void auxSolidOctahedron (GLdouble radius)
{
    GLdouble *sizeArray;
    GLuint displayList;
    GLdouble center[3] = {(GLdouble)0.0, (GLdouble)0.0, (GLdouble)0.0};

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = radius;
    displayList = findList (OCTASOLID, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (OCTASOLID, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            octahedron (center, radius, GL_TRIANGLES);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

/*  Render wire frame or solid tetrahedra.  If no display list with
 *  the current model size exists, create a new display list.
 */
void auxWireTetrahedron (GLdouble radius)
{
    GLdouble *sizeArray;
    GLuint displayList;
    GLdouble center[3] = {(GLdouble)0.0, (GLdouble)0.0, (GLdouble)0.0};

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = radius;
    displayList = findList (TETRAWIRE, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (TETRAWIRE, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            tetrahedron (center, radius, GL_LINE_LOOP);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

void auxSolidTetrahedron (GLdouble radius)
{
    GLdouble *sizeArray;
    GLuint displayList;
    GLdouble center[3] = {(GLdouble)0.0, (GLdouble)0.0, (GLdouble)0.0};

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = radius;
    displayList = findList (TETRASOLID, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (TETRASOLID, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            tetrahedron (center, radius, GL_TRIANGLES);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

/*  Render wire frame or solid dodecahedra.  If no display list with
 *  the current model size exists, create a new display list.
 */
void auxWireDodecahedron (GLdouble radius)
{
    GLdouble *sizeArray;
    GLuint displayList;
    GLdouble center[3] = {(GLdouble)0.0, (GLdouble)0.0, (GLdouble)0.0};

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = radius;
    displayList = findList (DODECAWIRE, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (DODECAWIRE, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            dodecahedron (center, radius/(GLdouble)1.73, GL_LINE_LOOP);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

void auxSolidDodecahedron (GLdouble radius)
{
    GLdouble *sizeArray;
    GLuint displayList;
    GLdouble center[3] = {(GLdouble)0.0, (GLdouble)0.0, (GLdouble)0.0};

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 1);
    *sizeArray = radius;
    displayList = findList (DODECASOLID, sizeArray, 1);

    if (displayList == 0) {
        glNewList(makeModelPtr (DODECASOLID, sizeArray, 1),
            GL_COMPILE_AND_EXECUTE);
            dodecahedron (center, radius/(GLdouble)1.73, GL_POLYGON);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

/*  Render wire frame or solid cones.  If no display list with
 *  the current model size exists, create a new display list.
 */
void auxWireCone (GLdouble base, GLdouble height)
{
    GLUquadricObj *quadObj;
    GLdouble *sizeArray, *tmp;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 2);
    tmp = sizeArray;
    *tmp++ = base;
    *tmp++ = height;
    displayList = findList (CONEWIRE, sizeArray, 2);

    if (displayList == 0) {
        glNewList(makeModelPtr (CONEWIRE, sizeArray, 2),
            GL_COMPILE_AND_EXECUTE);
            quadObj = gluNewQuadric ();
            gluQuadricDrawStyle (quadObj, GLU_LINE);
            gluCylinder (quadObj, base, (GLdouble)0.0, height, 15, 10);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

void auxSolidCone (GLdouble base, GLdouble height)
{
    GLUquadricObj *quadObj;
    GLdouble *sizeArray, *tmp;
    GLuint displayList;

    sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 2);
    tmp = sizeArray;
    *tmp++ = base;
    *tmp++ = height;
    displayList = findList (CONEWIRE, sizeArray, 2);

    if (displayList == 0) {
        glNewList(makeModelPtr (CONEWIRE, sizeArray, 2),
            GL_COMPILE_AND_EXECUTE);
            quadObj = gluNewQuadric ();
            gluQuadricDrawStyle (quadObj, GLU_FILL);
            gluQuadricNormals (quadObj, GLU_SMOOTH);
            gluCylinder (quadObj, base, (GLdouble)0.0, height, 15, 10);
        glEndList();
    }
    else {
        glCallList(displayList);
        free (sizeArray);
    }
}

/* Routines to build 3 dimensional solids, including:
 *
 * drawbox, doughnut, icosahedron,
 * octahedron, tetrahedron, dodecahedron.
 */

/* drawbox:
 *
 * draws a rectangular box with the given x, y, and z ranges.
 * The box is axis-aligned.
 */
void drawbox(GLdouble x0, GLdouble x1, GLdouble y0, GLdouble y1,
        GLdouble z0, GLdouble z1, GLenum type)
{
    static GLdouble 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 }
    };
    GLdouble v[8][3], tmp;
    GLint i;

    if (x0 > x1) {
        tmp = x0; x0 = x1; x1 = tmp;
    }
    if (y0 > y1) {
        tmp = y0; y0 = y1; y1 = tmp;
    }
    if (z0 > z1) {
        tmp = z0; z0 = z1; z1 = tmp;
    }
    v[0][0] = v[1][0] = v[2][0] = v[3][0] = x0;
    v[4][0] = v[5][0] = v[6][0] = v[7][0] = x1;
    v[0][1] = v[1][1] = v[4][1] = v[5][1] = y0;
    v[2][1] = v[3][1] = v[6][1] = v[7][1] = y1;
    v[0][2] = v[3][2] = v[4][2] = v[7][2] = z0;
    v[1][2] = v[2][2] = v[5][2] = v[6][2] = z1;

    for (i = 0; i < 6; i++) {
        glBegin(type);
        glNormal3dv(&n[i][0]);
        glVertex3dv(&v[faces[i][0]][0]);
        glNormal3dv(&n[i][0]);
        glVertex3dv(&v[faces[i][1]][0]);
        glNormal3dv(&n[i][0]);
        glVertex3dv(&v[faces[i][2]][0]);
        glNormal3dv(&n[i][0]);
        glVertex3dv(&v[faces[i][3]][0]);
        glEnd();
    }
}

/* doughnut:
 *
 * draws a doughnut, centered at (0, 0, 0) whose axis is aligned with
 * the z-axis.  The doughnut's major radius is R, and minor radius is r.
 */

void doughnut(GLdouble r, GLdouble R, GLint nsides, GLint rings, GLenum type)
{
    int i, j;
    GLdouble    theta, phi, theta1, phi1;
    GLdouble    p0[03], p1[3], p2[3], p3[3];
    GLdouble    n0[3], n1[3], n2[3], n3[3];

    for (i = 0; i < rings; i++) {
        theta = (GLdouble)i*(GLdouble)2.0*PI/rings;
        theta1 = (GLdouble)(i+1)*(GLdouble)2.0*PI/rings;
        for (j = 0; j < nsides; j++) {
            phi = (GLdouble)j*(GLdouble)2.0*PI/nsides;
            phi1 = (GLdouble)(j+1)*(GLdouble)2.0*PI/nsides;

            p0[0] = cos(theta)*(R + r*cos(phi));
            p0[1] = -sin(theta)*(R + r*cos(phi));
            p0[2] = r*sin(phi);

            p1[0] = cos(theta1)*(R + r*cos(phi));
            p1[1] = -sin(theta1)*(R + r*cos(phi));
            p1[2] = r*sin(phi);

            p2[0] = cos(theta1)*(R + r*cos(phi1));
            p2[1] = -sin(theta1)*(R + r*cos(phi1));
            p2[2] = r*sin(phi1);

            p3[0] = cos(theta)*(R + r*cos(phi1));
            p3[1] = -sin(theta)*(R + r*cos(phi1));
            p3[2] = r*sin(phi1);

            n0[0] = cos(theta)*(cos(phi));
            n0[1] = -sin(theta)*(cos(phi));
            n0[2] = sin(phi);

            n1[0] = cos(theta1)*(cos(phi));
            n1[1] = -sin(theta1)*(cos(phi));
            n1[2] = sin(phi);

            n2[0] = cos(theta1)*(cos(phi1));
            n2[1] = -sin(theta1)*(cos(phi1));
            n2[2] = sin(phi1);

            n3[0] = cos(theta)*(cos(phi1));
            n3[1] = -sin(theta)*(cos(phi1));
            n3[2] = sin(phi1);

            m_xformpt(p0, p0, n0, n0);
            m_xformpt(p1, p1, n1, n1);
            m_xformpt(p2, p2, n2, n2);
            m_xformpt(p3, p3, n3, n3);

            glBegin(type);
                glNormal3dv(n3);
                glVertex3dv(p3);
                glNormal3dv(n2);
                glVertex3dv(p2);
                glNormal3dv(n1);
                glVertex3dv(p1);
                glNormal3dv(n0);
                glVertex3dv(p0);
            glEnd();
        }
    }
}

/* octahedron data: The octahedron produced is centered
 * at the origin and has radius 1.0
 */
static GLdouble odata[6][3] = {
  {1.0, 0.0, 0.0},
  {-1.0, 0.0, 0.0},
  {0.0, 1.0, 0.0},
  {0.0, -1.0, 0.0},
  {0.0, 0.0, 1.0},
  {0.0, 0.0, -1.0}
};

static int ondex[8][3] = {
    {0, 4, 2}, {1, 2, 4}, {0, 3, 4}, {1, 4, 3},
    {0, 2, 5}, {1, 5, 2}, {0, 5, 3}, {1, 3, 5}
};

/* tetrahedron data: */

#define T       1.73205080756887729

static GLdouble tdata[4][3] = {
    {T, T, T}, {T, -T, -T}, {-T, T, -T}, {-T, -T, T}
};

static int tndex[4][3] = {
    {0, 1, 3}, {2, 1, 0}, {3, 2, 0}, {1, 2, 3}
};

/* icosahedron data: These numbers are rigged to
 * make an icosahedron of radius 1.0
 */

#define X .525731112119133606
#define Z .850650808352039932

static GLdouble idata[12][3] = {
  {-X, 0, Z},
  {X, 0, Z},
  {-X, 0, -Z},
  {X, 0, -Z},
  {0, Z, X},
  {0, Z, -X},
  {0, -Z, X},
  {0, -Z, -X},
  {Z, X, 0},
  {-Z, X, 0},
  {Z, -X, 0},
  {-Z, -X, 0}
};

static int index[20][3] = {
    {0, 4, 1},    {0, 9, 4},
    {9, 5, 4},    {4, 5, 8},
    {4, 8, 1},    {8, 10, 1},
    {8, 3, 10},    {5, 3, 8},
    {5, 2, 3},    {2, 7, 3},
    {7, 10, 3},    {7, 6, 10},
    {7, 11, 6},    {11, 0, 6},
    {0, 1, 6},    {6, 1, 10},
    {9, 0, 11},    {9, 11, 2},
    {9, 2, 5},    {7, 2, 11},
};

/* icosahedron:
 *
 * Draws an icosahedron with center at p0 having the
 * given radius.
 */

static void icosahedron(GLdouble p0[3], GLdouble radius, GLenum shadeType)
{
    int i;

    for (i = 0; i < 20; i++)
        drawtriangle(i, 0, 1, p0, radius, shadeType, 0);
}

/* octahedron:
 *
 * Draws an octahedron with center at p0 having the
 * given radius.
 */
static void octahedron(GLdouble p0[3], GLdouble radius, GLenum shadeType)
{
    int i;

    for (i = 0; i < 8; i++)
        drawtriangle(i, 1, 1, p0, radius, shadeType, 0);
}

/* tetrahedron:
 *
 * Draws an tetrahedron with center at p0 having the
 * given radius.
 */

static void tetrahedron(GLdouble p0[3], GLdouble radius, GLenum shadeType)
{
    int i;

    for (i = 0; i < 4; i++)
        drawtriangle(i, 2, 1, p0, radius, shadeType, 0);
}

static void subdivide(int depth, GLdouble *v0, GLdouble *v1, GLdouble *v2,
        GLdouble p0[3], GLdouble radius, GLenum shadeType, int avnormal)
{
    GLdouble w0[3], w1[3], w2[3];
    GLdouble l;
    int i, j, k, n;

    for (i = 0; i < depth; i++)
        for (j = 0; i + j < depth; j++) {
            k = depth - i - j;
            for (n = 0; n < 3; n++) {
                w0[n] = (i*v0[n] + j*v1[n] + k*v2[n])/depth;
                w1[n] = ((i+1)*v0[n] + j*v1[n] + (k-1)*v2[n])/depth;
                w2[n] = (i*v0[n] + (j+1)*v1[n] + (k-1)*v2[n])/depth;
            }
            l = sqrt(w0[0]*w0[0] + w0[1]*w0[1] + w0[2]*w0[2]);
            w0[0] /= l; w0[1] /= l; w0[2] /= l;
            l = sqrt(w1[0]*w1[0] + w1[1]*w1[1] + w1[2]*w1[2]);
            w1[0] /= l; w1[1] /= l; w1[2] /= l;
            l = sqrt(w2[0]*w2[0] + w2[1]*w2[1] + w2[2]*w2[2]);
            w2[0] /= l; w2[1] /= l; w2[2] /= l;
            recorditem(w1, w0, w2, p0, radius, shadeType, avnormal);
        }
    for (i = 0; i < depth-1; i++)
        for (j = 0; i + j < depth-1; j++) {
            k = depth - i - j;
            for (n = 0; n < 3; n++) {
                w0[n] = ((i+1)*v0[n] + (j+1)*v1[n] + (k-2)*v2[n])/depth;
                w1[n] = ((i+1)*v0[n] + j*v1[n] + (k-1)*v2[n])/depth;
                w2[n] = (i*v0[n] + (j+1)*v1[n] + (k-1)*v2[n])/depth;
            }
            l = sqrt(w0[0]*w0[0] + w0[1]*w0[1] + w0[2]*w0[2]);
            w0[0] /= l; w0[1] /= l; w0[2] /= l;
            l = sqrt(w1[0]*w1[0] + w1[1]*w1[1] + w1[2]*w1[2]);
            w1[0] /= l; w1[1] /= l; w1[2] /= l;
            l = sqrt(w2[0]*w2[0] + w2[1]*w2[1] + w2[2]*w2[2]);
            w2[0] /= l; w2[1] /= l; w2[2] /= l;
            recorditem(w0, w1, w2, p0, radius, shadeType, avnormal);
        }
}

static void drawtriangle(int i, int geomType, int depth,
        GLdouble p0[3], GLdouble radius, GLenum shadeType, int avnormal)
{
    GLdouble *x0, *x1, *x2;

    switch (geomType) {
        case 0: /* icosahedron */
            x0 = &idata[index[i][0]][0];
            x1 = &idata[index[i][1]][0];
            x2 = &idata[index[i][2]][0];
            break;
        case 1: /* octahedron */
            x0 = &odata[ondex[i][0]][0];
            x1 = &odata[ondex[i][1]][0];
            x2 = &odata[ondex[i][2]][0];
            break;
        case 2: /* tetrahedron */
            x0 = &tdata[tndex[i][0]][0];
            x1 = &tdata[tndex[i][1]][0];
            x2 = &tdata[tndex[i][2]][0];
            break;
    }
    subdivide(depth, x0, x1, x2, p0, radius, shadeType, avnormal);
}

static void recorditem(GLdouble *n1, GLdouble *n2, GLdouble *n3,
        GLdouble center[3], GLdouble radius, GLenum shadeType, int avnormal)
{
    GLdouble p1[3], p2[3], p3[3], q0[3], q1[3], n11[3], n22[3], n33[3];
    int i;

    for (i = 0; i < 3; i++) {
        p1[i] = n1[i]*radius + center[i];
        p2[i] = n2[i]*radius + center[i];
        p3[i] = n3[i]*radius + center[i];
    }
    if (avnormal == 0) {
        diff3(p1, p2, q0);
        diff3(p2, p3, q1);
        crossprod(q0, q1, q1);
        normalize(q1);
        m_xformpt(p1, p1, q1, n11);

m_xformptonly(p2, p2);
        m_xformptonly(p3, p3);

        glBegin (shadeType);
        glNormal3dv(n11);
        glVertex3dv(p1);
        glVertex3dv(p2);
        glVertex3dv(p3);
        glEnd();
        return;
    }
    m_xformpt(p1, p1, n1, n11);
    m_xformpt(p2, p2, n2, n22);
    m_xformpt(p3, p3, n3, n33);

    glBegin (shadeType);
    glNormal3dv(n11);
    glVertex3dv(p1);
    glNormal3dv(n22);
    glVertex3dv(p2);
    glNormal3dv(n33);
    glVertex3dv(p3);
    glEnd();
}

static GLdouble dodec[20][3];

static void initdodec()
{
    GLdouble alpha, beta;

    alpha = sqrt((double)2.0/((double)3.0 + sqrt((double)5.0)));
    beta = (double)1.0 + sqrt((double)6.0/((double)3.0 + sqrt((double)5.0)) - (double)2.0 + (double)2.0*sqrt((double)2.0/((double)3.0 +
                                                            sqrt((double)5.0))));
    dodec[0][0] = -alpha; dodec[0][1] = 0; dodec[0][2] = beta;
    dodec[1][0] = alpha; dodec[1][1] = 0; dodec[1][2] = beta;
    dodec[2][0] = -1; dodec[2][1] = -1; dodec[2][2] = -1;
    dodec[3][0] = -1; dodec[3][1] = -1; dodec[3][2] = 1;
    dodec[4][0] = -1; dodec[4][1] = 1; dodec[4][2] = -1;
    dodec[5][0] = -1; dodec[5][1] = 1; dodec[5][2] = 1;
    dodec[6][0] = 1; dodec[6][1] = -1; dodec[6][2] = -1;
    dodec[7][0] = 1; dodec[7][1] = -1; dodec[7][2] = 1;
    dodec[8][0] = 1; dodec[8][1] = 1; dodec[8][2] = -1;
    dodec[9][0] = 1; dodec[9][1] = 1; dodec[9][2] = 1;
    dodec[10][0] = beta; dodec[10][1] = alpha; dodec[10][2] = 0;
    dodec[11][0] = beta; dodec[11][1] = -alpha; dodec[11][2] = 0;
    dodec[12][0] = -beta; dodec[12][1] = alpha; dodec[12][2] = 0;
    dodec[13][0] = -beta; dodec[13][1] = -alpha; dodec[13][2] = 0;
    dodec[14][0] = -alpha; dodec[14][1] = 0; dodec[14][2] = -beta;
    dodec[15][0] = alpha; dodec[15][1] = 0; dodec[15][2] = -beta;
    dodec[16][0] = 0; dodec[16][1] = beta; dodec[16][2] = alpha;
    dodec[17][0] = 0; dodec[17][1] = beta; dodec[17][2] = -alpha;
    dodec[18][0] = 0; dodec[18][1] = -beta; dodec[18][2] = alpha;
    dodec[19][0] = 0; dodec[19][1] = -beta; dodec[19][2] = -alpha;
}

/* dodecahedron:
 *
 * Draws an dodecahedron with center at 0.0. The radius
 * is sqrt(3).
 */
static void dodecahedron(GLdouble center[3], GLdouble sc, GLenum type)
{
    static int inited = 0;

    if ( inited == 0) {
        inited = 1;
        initdodec();
    }
    m_pushmatrix();
    m_translate(center[0], center[1], center[2]);
    m_scale(sc, sc, sc);
    pentagon(0, 1, 9, 16, 5, type);
    pentagon(1, 0, 3, 18, 7, type);
    pentagon(1, 7, 11, 10, 9, type);
    pentagon(11, 7, 18, 19, 6, type);
    pentagon(8, 17, 16, 9, 10, type);
    pentagon(2, 14, 15, 6, 19, type);
    pentagon(2, 13, 12, 4, 14, type);
    pentagon(2, 19, 18, 3, 13, type);
    pentagon(3, 0, 5, 12, 13, type);
    pentagon(6, 15, 8, 10, 11, type);
    pentagon(4, 17, 8, 15, 14, type);
    pentagon(4, 12, 5, 16, 17, type);
    m_popmatrix();
}

static void pentagon(int a, int b, int c, int d, int e, GLenum shadeType)
{
    GLdouble n0[3], d1[3], d2[3], d3[3], d4[3], d5[3], nout[3];

    diff3(&dodec[a][0], &dodec[b][0], d1);
    diff3(&dodec[b][0], &dodec[c][0], d2);
    crossprod(d1, d2, n0);
    normalize(n0);
    m_xformpt(&dodec[a][0], d1, n0, nout);
    m_xformptonly(&dodec[b][0], d2);
    m_xformptonly(&dodec[c][0], d3);
    m_xformptonly(&dodec[d][0], d4);
    m_xformptonly(&dodec[e][0], d5);

    glBegin (shadeType);
    glNormal3dv(nout);
    glVertex3dv(d1);
    glVertex3dv(d2);
    glVertex3dv(d3);
    glVertex3dv(d4);
    glVertex3dv(d5);
    glEnd();
}

/*      linked lists--display lists for each different
 *      type of geometric objects.  The linked list is
 *      searched, until an object of the requested
 *      size is found.  If no geometric object of that size
 *      has been previously made, a new one is created.
 */
GLuint findList (int index, GLdouble *paramArray, int size)
{
    MODELPTR endList;
    int found = 0;

    endList = lists[index];
    while (endList != NULL) {
        if (compareParams (endList->params, paramArray, size))
            return (endList->list);
        endList = endList->ptr;
    }
/*  if not found, return 0 and calling routine should
 *  make a new list
 */
    return (0);
}

int compareParams (GLdouble *oneArray, GLdouble *twoArray, int size)
{
    int i;
    int matches = 1;

    for (i = 0; (i < size) && matches; i++) {
        if (*oneArray++ != *twoArray++)
            matches = 0;
    }
    return (matches);
}

GLuint makeModelPtr (int index, GLdouble *sizeArray, int count)
{
    MODELPTR newModel;

    newModel = (MODELPTR) malloc (sizeof (MODEL));
    newModel->list = glGenLists (1);
    newModel->numParam = count;
    newModel->params = sizeArray;
    newModel->ptr = lists[index];
    lists[index] = newModel;

    return (newModel->list);
}


TEAPOT.C

/*
 * 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 <windows.h>
#include <GL/gl.h>
#include <gl/glaux.h>
#include "teapot.h"

#define static

long GRD;

#define TEAPOTSOLID 0
#define TEAPOTWIRE 1

static GLuint teapots[2] = {0, 0};

static float tex[2][2][2] = {{{0, 0},{1, 0}},{{0, 1},{1, 1}}};

static void solidTeapot(long grid, GLdouble scale)
{
    float p[4][4][3], q[4][4][3], r[4][4][3], s[4][4][3];
    long i, j, k, l;

    if (grid < 2) grid = 7;
    GRD = grid;
    teapots[TEAPOTSOLID] = glGenLists (1);
    glNewList(teapots[TEAPOTSOLID], GL_COMPILE);
    glPushMatrix ();
    glRotatef ((GLfloat)270.0, (GLfloat)1.0, (GLfloat)0.0, (GLfloat)0.0);
    glScalef ((GLdouble)0.5*scale, (GLdouble)0.5*scale, (GLdouble)0.5*scale);
    glTranslatef ((GLfloat)0.0, (GLfloat)0.0, (GLfloat)-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] = cpdata[patchdata[i][j*4+k]][l];
    q[j][k][l] = cpdata[patchdata[i][j*4+(3-k)]][l];
    if (l == 1) q[j][k][l] *= (float)-1.0;
    if (i < 6) {
r[j][k][l] = cpdata[patchdata[i][j*4+(3-k)]][l];
if (l == 0) r[j][k][l] *= (float)-1.0;
s[j][k][l] = cpdata[patchdata[i][j*4+k]][l];
if (l == 0) s[j][k][l] *= (float)-1.0;
if (l == 1) s[j][k][l] *= (float)-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]);
glEnable(GL_MAP2_VERTEX_3); glEnable(GL_MAP2_TEXTURE_COORD_2);
glMapGrid2f(GRD, (GLfloat)0.0, (GLfloat)1.0, GRD, (GLfloat)0.0, (GLfloat)1.0);
glEvalMesh2(GL_FILL, 0, GRD, 0, GRD);
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &q[0][0][0]);
glEvalMesh2(GL_FILL, 0, GRD, 0, GRD);
if (i < 6) {
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &r[0][0][0]);
    glEvalMesh2(GL_FILL, 0, GRD, 0, GRD);
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &s[0][0][0]);
    glEvalMesh2(GL_FILL, 0, GRD, 0, GRD);
}
    }
    glDisable(GL_MAP2_VERTEX_3); glDisable(GL_MAP2_TEXTURE_COORD_2);
    glPopMatrix ();
    glEndList();
}

static void wireTeapot(long grid, GLdouble scale)
{
    float p[4][4][3], q[4][4][3], r[4][4][3], s[4][4][3];
    long i, j, k, l;

    if (grid < 2) grid = 7;
    GRD = grid;
    teapots[TEAPOTWIRE] = glGenLists (1);
    glNewList(teapots[TEAPOTWIRE], GL_COMPILE);
    glPushMatrix ();
    glRotatef ((GLfloat)270.0, (GLfloat)1.0, (GLfloat)0.0, (GLfloat)0.0);
    glScalef ((GLdouble)0.5*scale, (GLdouble)0.5*scale, (GLdouble)0.5*scale);
    glTranslatef ((GLfloat)0.0, (GLfloat)0.0, (GLfloat)-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] = cpdata[patchdata[i][j*4+k]][l];
    q[j][k][l] = cpdata[patchdata[i][j*4+(3-k)]][l];
    if (l == 1) q[j][k][l] *= (float)-1.0;
    if (i < 6) {
r[j][k][l] = cpdata[patchdata[i][j*4+(3-k)]][l];
if (l == 0) r[j][k][l] *= (float)-1.0;
s[j][k][l] = cpdata[patchdata[i][j*4+k]][l];
if (l == 0) s[j][k][l] *= (float)-1.0;
if (l == 1) s[j][k][l] *= (float)-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]);
glEnable(GL_MAP2_VERTEX_3); glEnable(GL_MAP2_TEXTURE_COORD_2);
glMapGrid2f(GRD, (GLfloat)0.0, (GLfloat)1.0, GRD, (GLfloat)0.0, (GLfloat)1.0);
glEvalMesh2(GL_LINE, 0, GRD, 0, GRD);
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &q[0][0][0]);
glEvalMesh2(GL_LINE, 0, GRD, 0, GRD);
if (i < 6) {
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &r[0][0][0]);
    glEvalMesh2(GL_LINE, 0, GRD, 0, GRD);
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &s[0][0][0]);
    glEvalMesh2(GL_LINE, 0, GRD, 0, GRD);
}
    }
    glDisable(GL_MAP2_VERTEX_3); glDisable(GL_MAP2_TEXTURE_COORD_2);
    glPopMatrix ();
    glEndList();
}

void auxSolidTeapot(GLdouble scale)
{
    if (glIsList(teapots[TEAPOTSOLID]) == 0)
solidTeapot (14, scale);
    glCallList(teapots[TEAPOTSOLID]);
}

void auxWireTeapot(GLdouble scale)
{
    if (glIsList(teapots[TEAPOTWIRE]) == 0)
wireTeapot (10, scale);
    glCallList(teapots[TEAPOTWIRE]);
}


TEAPOT.H

/*
 * 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.
 */
/* Rim, body, lid, and bottom data must be reflected in x and y;
 * handle and spout data across the y axis only.
 */
#include "glos.h"

long patchdata[][16] = {
    {102,103,104,105,4,5,6,7,8,9,10,11,12,13,14,15},   /* rim */
    {12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27},   /* body */
    {24,25,26,27,29,30,31,32,33,34,35,36,37,38,39,40},   /* body */
    {96,96,96,96,97,98,99,100,101,101,101,101,0,1,2,3,},   /* lid */
    {0,1,2,3,106,107,108,109,110,111,112,113,114,115,116,117},   /* lid */
    {118,118,118,118,124,122,119,121,123,126,125,120,40,39,38,37}, /* bottom */
    {41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56},   /* handle */
    {53,54,55,56,57,58,59,60,61,62,63,64,28,65,66,67},   /* handle */
    {68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83},   /* spout */
    {80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95}   /* spout */
};

float cpdata[][3] = {
{0.2,0,2.7},{0.2,-0.112,2.7},{0.112,-0.2,2.7},{0,-0.2,2.7},{1.3375,0,2.53125},
{1.3375,-0.749,2.53125},{0.749,-1.3375,2.53125},{0,-1.3375,2.53125},
{1.4375,0,2.53125},{1.4375,-0.805,2.53125},{0.805,-1.4375,2.53125},
{0,-1.4375,2.53125},{1.5,0,2.4},{1.5,-0.84,2.4},{0.84,-1.5,2.4},{0,-1.5,2.4},
{1.75,0,1.875},{1.75,-0.98,1.875},{0.98,-1.75,1.875},{0,-1.75,1.875},{2,0,1.35},
{2,-1.12,1.35},{1.12,-2,1.35},{0,-2,1.35},{2,0,0.9},{2,-1.12,0.9},{1.12,-2,0.9},
{0,-2,0.9},{-2,0,0.9},{2,0,0.45},{2,-1.12,0.45},{1.12,-2,0.45},{0,-2,0.45},
{1.5,0,0.225},{1.5,-0.84,0.225},{0.84,-1.5,0.225},{0,-1.5,0.225},{1.5,0,0.15},
{1.5,-0.84,0.15},{0.84,-1.5,0.15},{0,-1.5,0.15},{-1.6,0,2.025},{-1.6,-0.3,2.025},
{-1.5,-0.3,2.25},{-1.5,0,2.25},{-2.3,0,2.025},{-2.3,-0.3,2.025},{-2.5,-0.3,2.25},
{-2.5,0,2.25},{-2.7,0,2.025},{-2.7,-0.3,2.025},{-3,-0.3,2.25},{-3,0,2.25},
{-2.7,0,1.8},{-2.7,-0.3,1.8},{-3,-0.3,1.8},{-3,0,1.8},{-2.7,0,1.575},
{-2.7,-0.3,1.575},{-3,-0.3,1.35},{-3,0,1.35},{-2.5,0,1.125},{-2.5,-0.3,1.125},
{-2.65,-0.3,0.9375},{-2.65,0,0.9375},{-2,-0.3,0.9},{-1.9,-0.3,0.6},{-1.9,0,0.6},
{1.7,0,1.425},{1.7,-0.66,1.425},{1.7,-0.66,0.6},{1.7,0,0.6},{2.6,0,1.425},
{2.6,-0.66,1.425},{3.1,-0.66,0.825},{3.1,0,0.825},{2.3,0,2.1},{2.3,-0.25,2.1},
{2.4,-0.25,2.025},{2.4,0,2.025},{2.7,0,2.4},{2.7,-0.25,2.4},{3.3,-0.25,2.4},
{3.3,0,2.4},{2.8,0,2.475},{2.8,-0.25,2.475},{3.525,-0.25,2.49375},
{3.525,0,2.49375},{2.9,0,2.475},{2.9,-0.15,2.475},{3.45,-0.15,2.5125},
{3.45,0,2.5125},{2.8,0,2.4},{2.8,-0.15,2.4},{3.2,-0.15,2.4},{3.2,0,2.4},
{0,0,3.15},{0.8,0,3.15},{0.8,-0.45,3.15},{0.45,-0.8,3.15},{0,-0.8,3.15},
{0,0,2.85},{1.4,0,2.4},{1.4,-0.784,2.4},{0.784,-1.4,2.4},{0,-1.4,2.4},
{0.4,0,2.55},{0.4,-0.224,2.55},{0.224,-0.4,2.55},{0,-0.4,2.55},{1.3,0,2.55},
{1.3,-0.728,2.55},{0.728,-1.3,2.55},{0,-1.3,2.55},{1.3,0,2.4},{1.3,-0.728,2.4},
{0.728,-1.3,2.4},{0,-1.3,2.4},{0,0,0},{1.425,-0.798,0},{1.5,0,0.075},{1.425,0,0},
{0.798,-1.425,0},{0,-1.5,0.075},{0,-1.425,0},{1.5,-0.84,0.075},{0.84,-1.5,0.075}
};


TK.C

/*
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tk.h"
#include "windows.h"

#if(WINVER < 0x0400)
// Ordinarily not defined for versions before 4.00.
#define COLOR_3DDKSHADOW        21
#define COLOR_3DLIGHT           22
#define COLOR_INFOTEXT          23
#define COLOR_INFOBK            24
#endif

#define static

#if defined(__cplusplus) || defined(c_plusplus)
#define class c_class
#endif

#if DBG
#define TKASSERT(x)                                     \
if ( !(x) ) {                                           \
    PrintMessage("%s(%d) Assertion failed %s\n",        \
        __FILE__, __LINE__, #x);                        \
}
#else
#define TKASSERT(x)
#endif  /* DBG */

/******************************************************************************/

static struct _WINDOWINFO {
    int x, y;
    int width, height;
    GLenum type;
    GLenum dmPolicy;
    int ipfd;
    BOOL bDefPos;
} windInfo = {
    0, 0, 100, 100, TK_INDEX | TK_SINGLE, TK_MINIMUM_CRITERIA, 0, TRUE
};

static HWND     tkhwnd     = NULL;
static HDC      tkhdc      = NULL;
static HGLRC    tkhrc      = NULL;
static HPALETTE tkhpalette = NULL;
static OSVERSIONINFO tkOSVerInfo;
GLboolean tkPopupEnable = TRUE;

// Fixed palette support.

#define BLACK   PALETTERGB(0,0,0)
#define WHITE   PALETTERGB(255,255,255)
#define MAX_STATIC_COLORS   (COLOR_INFOBK - COLOR_SCROLLBAR + 1)
static int tkNumStaticColors = MAX_STATIC_COLORS;

// TRUE if app wants to take over palette
static BOOL tkUseStaticColors = FALSE;

// TRUE if static system color settings have been replaced with B&W settings.
static BOOL tkSystemColorsInUse = FALSE;

// TRUE if original static colors saved
static BOOL tkStaticColorsSaved = FALSE;

// saved system static colors (initialize with default colors)
static COLORREF gacrSave[MAX_STATIC_COLORS];

// new B&W system static colors
static COLORREF gacrBlackAndWhite[] = {
    WHITE,  // COLOR_SCROLLBAR
    BLACK,  // COLOR_BACKGROUND
    BLACK,  // COLOR_ACTIVECAPTION
    WHITE,  // COLOR_INACTIVECAPTION
    WHITE,  // COLOR_MENU
    WHITE,  // COLOR_WINDOW
    BLACK,  // COLOR_WINDOWFRAME
    BLACK,  // COLOR_MENUTEXT
    BLACK,  // COLOR_WINDOWTEXT
    WHITE,  // COLOR_CAPTIONTEXT
    WHITE,  // COLOR_ACTIVEBORDER
    WHITE,  // COLOR_INACTIVEBORDER
    WHITE,  // COLOR_APPWORKSPACE
    BLACK,  // COLOR_HIGHLIGHT
    WHITE,  // COLOR_HIGHLIGHTTEXT
    WHITE,  // COLOR_BTNFACE
    BLACK,  // COLOR_BTNSHADOW
    BLACK,  // COLOR_GRAYTEXT
    BLACK,  // COLOR_BTNTEXT
    BLACK,  // COLOR_INACTIVECAPTIONTEXT
    BLACK,  // COLOR_BTNHIGHLIGHT
    BLACK,  // COLOR_3DDKSHADOW
    WHITE,  // COLOR_3DLIGHT
    BLACK,  // COLOR_INFOTEXT
    WHITE   // COLOR_INFOBK
    };
static INT gaiStaticIndex[] = {
    COLOR_SCROLLBAR          ,
    COLOR_BACKGROUND         ,
    COLOR_ACTIVECAPTION      ,
    COLOR_INACTIVECAPTION    ,
    COLOR_MENU               ,
    COLOR_WINDOW             ,
    COLOR_WINDOWFRAME        ,
    COLOR_MENUTEXT           ,
    COLOR_WINDOWTEXT         ,
    COLOR_CAPTIONTEXT        ,
    COLOR_ACTIVEBORDER       ,
    COLOR_INACTIVEBORDER     ,
    COLOR_APPWORKSPACE       ,
    COLOR_HIGHLIGHT          ,
    COLOR_HIGHLIGHTTEXT      ,
    COLOR_BTNFACE            ,
    COLOR_BTNSHADOW          ,
    COLOR_GRAYTEXT           ,
    COLOR_BTNTEXT            ,
    COLOR_INACTIVECAPTIONTEXT,
    COLOR_BTNHIGHLIGHT       ,
    COLOR_3DDKSHADOW         ,
    COLOR_3DLIGHT            ,
    COLOR_INFOTEXT           ,
    COLOR_INFOBK
    };

static BOOL GrabStaticEntries(HDC);
static BOOL ReleaseStaticEntries(HDC);

#define RESTORE_FROM_REGISTRY   1
#if RESTORE_FROM_REGISTRY
// Registry names for the system colors.
CHAR *gaszSysClrNames[] = {
    "Scrollbar",      // COLOR_SCROLLBAR              0
    "Background",     // COLOR_BACKGROUND             1   (also COLOR_DESKTOP)
    "ActiveTitle",    // COLOR_ACTIVECAPTION          2
    "InactiveTitle",  // COLOR_INACTIVECAPTION        3
    "Menu",           // COLOR_MENU                   4
    "Window",         // COLOR_WINDOW                 5
    "WindowFrame",    // COLOR_WINDOWFRAME            6
    "MenuText",       // COLOR_MENUTEXT               7
    "WindowText",     // COLOR_WINDOWTEXT             8
    "TitleText",      // COLOR_CAPTIONTEXT            9
    "ActiveBorder",   // COLOR_ACTIVEBORDER          10
    "InactiveBorder", // COLOR_INACTIVEBORDER        11
    "AppWorkspace",   // COLOR_APPWORKSPACE          12
    "Hilight",        // COLOR_HIGHLIGHT             13
    "HilightText",    // COLOR_HIGHLIGHTTEXT         14
    "ButtonFace",     // COLOR_BTNFACE               15   (also COLOR_3DFACE)
    "ButtonShadow",   // COLOR_BTNSHADOW             16   (also COLOR_3DSHADOW)
    "GrayText",       // COLOR_GRAYTEXT              17
    "ButtonText",     // COLOR_BTNTEXT               18
    "InactiveTitleText", // COLOR_INACTIVECAPTIONTEXT   19
    "ButtonHilight",  // COLOR_BTNHIGHLIGHT          20   (also COLOR_3DHILIGHT)
    "ButtonDkShadow", // COLOR_3DDKSHADOW            21
    "ButtonLight",    // COLOR_3DLIGHT               22
    "InfoText",       // COLOR_INFOTEXT              23
    "InfoWindow"      // COLOR_INFOBK                24
};

static BOOL GetRegistrySysColors(COLORREF *, int);
#endif

static void (*ExposeFunc)(int, int)              = NULL;
static void (*ReshapeFunc)(GLsizei, GLsizei)     = NULL;
static void (*DisplayFunc)(void)                 = NULL;
static GLenum (*KeyDownFunc)(int, GLenum)        = NULL;
static GLenum (*MouseDownFunc)(int, int, GLenum) = NULL;
static GLenum (*MouseUpFunc)(int, int, GLenum)   = NULL;
static GLenum (*MouseMoveFunc)(int, int, GLenum) = NULL;
static void (*IdleFunc)(void)                    = NULL;

static char     *lpszClassName = "tkLibWClass";
static WCHAR    *lpszClassNameW = L"tkLibWClass";

static long tkWndProc(HWND hWnd, UINT message, DWORD wParam, LONG lParam);
static unsigned char ComponentFromIndex(int i, int nbits, int shift );
static void PrintMessage( const char *Format, ... );
static PALETTEENTRY *FillRgbPaletteEntries( PIXELFORMATDESCRIPTOR *Pfd, PALETTEENTRY *Entries, UINT Count );
static HPALETTE CreateCIPalette( HDC Dc );
static HPALETTE CreateRGBPalette( HDC hdc );
static void DestroyThisWindow( HWND Window );
static void CleanUp( void );
static void DelayPaletteRealization( void );
static long RealizePaletteNow( HDC Dc, HPALETTE Palette, BOOL bForceBackground );
static void ForceRedraw( HWND Window );
static BOOL FindPixelFormat(HDC hdc, GLenum type);
static int FindBestPixelFormat(HDC hdc, GLenum type, PIXELFORMATDESCRIPTOR *ppfd);
static int FindExactPixelFormat(HDC hdc, GLenum type, PIXELFORMATDESCRIPTOR *ppfd);
static BOOL IsPixelFormatValid(HDC hdc, int ipfd, PIXELFORMATDESCRIPTOR *ppfd);
static int PixelFormatDescriptorFromDc( HDC Dc, PIXELFORMATDESCRIPTOR *Pfd );
static void *AllocateMemory( size_t Size );
static void *AllocateZeroedMemory( size_t Size );
static void FreeMemory( void *Chunk );

/*
 *  Prototypes for the debugging functions go here
 */

#define DBGFUNC 0
#if DBGFUNC

static void DbgPrintf( const char *Format, ... );
static void pwi( void );
static void pwr(RECT *pr);
static void ShowPixelFormat(HDC hdc);

#endif

static float colorMaps[] = {
    0.000000F, 1.000000F, 0.000000F, 1.000000F, 0.000000F, 1.000000F,
    0.000000F, 1.000000F, 0.333333F, 0.776471F, 0.443137F, 0.556863F,
    0.443137F, 0.556863F, 0.219608F, 0.666667F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.039216F, 0.078431F, 0.117647F, 0.156863F,
    0.200000F, 0.239216F, 0.278431F, 0.317647F, 0.356863F, 0.400000F,
    0.439216F, 0.478431F, 0.517647F, 0.556863F, 0.600000F, 0.639216F,
    0.678431F, 0.717647F, 0.756863F, 0.800000F, 0.839216F, 0.878431F,
    0.917647F, 0.956863F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 0.000000F, 0.000000F,
    1.000000F, 1.000000F, 0.000000F, 0.000000F, 1.000000F, 1.000000F,
    0.333333F, 0.443137F, 0.776471F, 0.556863F, 0.443137F, 0.219608F,
    0.556863F, 0.666667F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.039216F, 0.078431F, 0.117647F, 0.156863F, 0.200000F, 0.239216F,
    0.278431F, 0.317647F, 0.356863F, 0.400000F, 0.439216F, 0.478431F,
    0.517647F, 0.556863F, 0.600000F, 0.639216F, 0.678431F, 0.717647F,
    0.756863F, 0.800000F, 0.839216F, 0.878431F, 0.917647F, 0.956863F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.141176F, 0.282353F, 0.427451F,
    0.568627F, 0.713726F, 0.854902F, 1.000000F, 0.000000F, 0.141176F,
    0.282353F, 0.427451F, 0.568627F, 0.713726F, 0.854902F, 1.000000F,
    0.000000F, 0.141176F, 0.282353F, 0.427451F, 0.568627F, 0.713726F,
    0.854902F, 1.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 0.333333F, 0.443137F,
    0.443137F, 0.219608F, 0.776471F, 0.556863F, 0.556863F, 0.666667F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.666667F, 0.333333F,
    0.666667F, 0.333333F, 0.666667F, 0.333333F, 0.039216F, 0.078431F,
    0.117647F, 0.156863F, 0.200000F, 0.239216F, 0.278431F, 0.317647F,
    0.356863F, 0.400000F, 0.439216F, 0.478431F, 0.517647F, 0.556863F,
    0.600000F, 0.639216F, 0.678431F, 0.717647F, 0.756863F, 0.800000F,
    0.839216F, 0.878431F, 0.917647F, 0.956863F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F, 0.000000F,
    0.000000F, 0.000000F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F, 0.247059F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.498039F,
    0.498039F, 0.498039F, 0.498039F, 0.498039F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F, 0.749020F,
    0.749020F, 0.749020F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
    1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F, 1.000000F,
};

/* Default Palette */
float auxRGBMap[20][3] = {
    { 0.0F, 0.0F, 0.0F },                               /* 0: black */
    { 0x80/255.0F, 0.0F, 0.0F },                        /* 1: Half red */
    { 0.0F, 0x80/255.0F, 0.0F },                        /* 2: Half green */
    { 0x80/255.0F, 0x80/255.0F, 0.0F },                 /* 3: Half yellow */
    { 0.0F, 0.0F, 0x80/255.0F },                        /* 4: Half blue */
    { 0x80/255.0F, 0.0F, 0x80/255.0F },                 /* 5: Half magenta */
    { 0.0F, 0x80/255.0F, 0x80/255.0F },                 /* 6: Half cyan */
    { 0xC0/255.0F, 0xC0/255.0F, 0xC0/255.0F },          /* 7: Light gray */
    { 0xC0/255.0F, 0xDC/255.0F, 0xC0/255.0F },          /* 8: Green gray */
    { 0xA6/255.0F, 0xCA/255.0F, 0xF0/255.0F },          /* 9: Half gray */
    { 1.0F, 0xFB/255.0F, 0xF0/255.0F },                 /* 10: Pale */
    { 0xA0/255.0F, 0xA0/255.0F, 0xA4/255.0F },          /* 11: Med gray */
    { 0x80/255.0F, 0x80/255.0F, 0x80/255.0F },          /* 12: Dark gray */
    { 1.0F, 0.0F, 0.0F },                               /* 13: red */
    { 0.0F, 1.0F, 0.0F },                               /* 14: green */
    { 1.0F, 1.0F, 0.0F },                               /* 15: yellow */
    { 0.0F, 0.0F, 1.0F },                               /* 16: blue */
    { 1.0F, 0.0F, 1.0F },                               /* 17: magenta */
    { 0.0F, 1.0F, 1.0F },                               /* 18: cyan */
    { 1.0F, 1.0F, 1.0F },                               /* 19: white */
};

/***************************************************************
 *                                                             *
 *  Exported Functions go here                                 *
 *                                                             *
 ***************************************************************/

void tkErrorPopups(GLboolean bEnable)
{
    tkPopupEnable = bEnable;
}

void tkCloseWindow(void)
{
    DestroyThisWindow(tkhwnd);
}

void tkExec(void)
{
    MSG Message;

    /*
     *  WM_SIZE gets delivered before we get here!
     */

    if (ReshapeFunc)
    {
        RECT ClientRect;

        GetClientRect(tkhwnd, &ClientRect);
        (*ReshapeFunc)(ClientRect.right, ClientRect.bottom);
    }

    while (GL_TRUE)
    {
        /*
         *  Process all pending messages
         */

        if (IdleFunc) {
            while (PeekMessage(&Message, NULL, 0, 0, PM_NOREMOVE) == TRUE) {
                if (GetMessage(&Message, NULL, 0, 0) ) {
                    TranslateMessage(&Message);
                    DispatchMessage(&Message);
                } else {
                    /*
                     *  Nothing else to do here, just return
                     */

                    return;
                }
            }

            /*
             *  If an idle function was defined, call it
             */

            if (IdleFunc) {
                (*IdleFunc)();
            }
        } else {
            if (GetMessage(&Message, NULL, 0, 0)) {
                TranslateMessage(&Message);
                DispatchMessage(&Message);
            } else {
                return;
            }
        }
    }
}

void tkExposeFunc(void (*Func)(int, int))
{
    ExposeFunc = Func;
}

void tkReshapeFunc(void (*Func)(GLsizei, GLsizei))
{
    ReshapeFunc = Func;
}

void tkDisplayFunc(void (*Func)(void))
{
    DisplayFunc = Func;
}

void tkKeyDownFunc(GLenum (*Func)(int, GLenum))
{
    KeyDownFunc = Func;
}

void tkMouseDownFunc(GLenum (*Func)(int, int, GLenum))
{
    MouseDownFunc = Func;
}

void tkMouseUpFunc(GLenum (*Func)(int, int, GLenum))
{
    MouseUpFunc = Func;
}

void tkMouseMoveFunc(GLenum (*Func)(int, int, GLenum))
{
    MouseMoveFunc = Func;
}

void tkIdleFunc(void (*Func)(void))
{
    IdleFunc = Func;
}

void tkInitPosition(int x, int y, int width, int height)
{
    if (x == CW_USEDEFAULT)
    {
        x = 0;
        y = 0;
        windInfo.bDefPos = TRUE;
    }
    else
        windInfo.bDefPos = FALSE;

    windInfo.x = x + GetSystemMetrics(SM_CXFRAME);
    windInfo.y = y + GetSystemMetrics(SM_CYCAPTION)
                 - GetSystemMetrics(SM_CYBORDER)
                 + GetSystemMetrics(SM_CYFRAME);
    windInfo.width = width;
    windInfo.height = height;
}

void tkInitDisplayMode(GLenum type)
{
    windInfo.type = type;
}

void tkInitDisplayModePolicy(GLenum type)
{
    windInfo.dmPolicy = type;
}

GLenum tkInitDisplayModeID(GLint ipfd)
{
    windInfo.ipfd = ipfd;
    return GL_TRUE;
}

// Initialize a window, create a rendering context for that window
GLenum tkInitWindow(char *title)
{
    TKASSERT( NULL==tkhwnd      );
    TKASSERT( NULL==tkhdc       );
    TKASSERT( NULL==tkhrc       );
    TKASSERT( NULL==tkhpalette  );

    return tkInitWindowAW(title, FALSE);
}

GLenum tkInitWindowAW(char *title, BOOL bUnicode)
{
    WNDCLASS wndclass;
    RECT     WinRect;
    HANDLE   hInstance;
    ATOM     aRegister;
    GLenum   Result = GL_FALSE;
    BOOL     bGetVersionExRet;

    hInstance = GetModuleHandle(NULL);

    tkOSVerInfo.dwOSVersionInfoSize = sizeof(tkOSVerInfo);
    bGetVersionExRet = GetVersionEx(&tkOSVerInfo);
    TKASSERT(bGetVersionExRet);
    if ( tkOSVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
         tkOSVerInfo.dwMajorVersion == 3 &&
         (tkOSVerInfo.dwMinorVersion == 5 || tkOSVerInfo.dwMinorVersion == 51) )
        tkNumStaticColors = COLOR_BTNHIGHLIGHT - COLOR_SCROLLBAR + 1;
    else
        tkNumStaticColors = COLOR_INFOBK - COLOR_SCROLLBAR + 1;

    // Must not define CS_PARENTDC style.
    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc   = (WNDPROC)tkWndProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance     = hInstance;
    wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
    wndclass.lpszMenuName  = NULL;

    if (bUnicode)
        wndclass.lpszClassName = (LPCSTR)lpszClassNameW;
    else
        wndclass.lpszClassName = (LPCSTR)lpszClassName;

    if (bUnicode)
    {
        aRegister = RegisterClassW((CONST WNDCLASSW *)&wndclass);
    }
    else
    {
        aRegister = RegisterClass(&wndclass);
    }

    /*
     *  If the window failed to register, then there's no
     *  need to continue further.
     */

    if(0 == aRegister)
    {
        PrintMessage("Failed to register window class\n");
        return(Result);
    }

    /*
     *  Make window large enough to hold a client area as large as windInfo
     */

    WinRect.left   = windInfo.x;
    WinRect.right  = windInfo.x + windInfo.width;
    WinRect.top    = windInfo.y;
    WinRect.bottom = windInfo.y + windInfo.height;

    AdjustWindowRect(&WinRect, WS_OVERLAPPEDWINDOW, FALSE);

    /*
     *  Must use WS_CLIPCHILDREN and WS_CLIPSIBLINGS styles.
     */

    if (bUnicode)
    {
        tkhwnd = CreateWindowW(
                    (LPCWSTR)lpszClassNameW,
                    (LPCWSTR)title,
                    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                    (windInfo.bDefPos) ? CW_USEDEFAULT : WinRect.left,
                    (windInfo.bDefPos) ? CW_USEDEFAULT : WinRect.top,
                    WinRect.right - WinRect.left,
                    WinRect.bottom - WinRect.top,
                    NULL,
                    NULL,
                    hInstance,
                    NULL);
    }
    else
    {
        tkhwnd = CreateWindow(
                    lpszClassName,
                    title,
                    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                    (windInfo.bDefPos) ? CW_USEDEFAULT : WinRect.left,
                    (windInfo.bDefPos) ? CW_USEDEFAULT : WinRect.top,
                    WinRect.right - WinRect.left,
                    WinRect.bottom - WinRect.top,
                    NULL,
                    NULL,
                    hInstance,
                    NULL);
    }

    if ( NULL != tkhwnd )
    {
        // If default window positioning used, find out window position and fix
        // up the windInfo position info.

        if (windInfo.bDefPos)
        {
            GetWindowRect(tkhwnd, &WinRect);
            windInfo.x = WinRect.left + GetSystemMetrics(SM_CXFRAME);
            windInfo.y = WinRect.top  + GetSystemMetrics(SM_CYCAPTION)
                         - GetSystemMetrics(SM_CYBORDER)
                         + GetSystemMetrics(SM_CYFRAME);
        }

        tkhdc = GetDC(tkhwnd);

        if ( NULL != tkhdc )
        {
            ShowWindow(tkhwnd, SW_SHOWDEFAULT);

            if ( FindPixelFormat(tkhdc, windInfo.type) )
            {
                /*
                 *  Create a Rendering Context
                 */

                tkhrc = wglCreateContext(tkhdc);

                if ( NULL != tkhrc )
                {
                    /*
                     *  Make it Current
                     */

                    if ( wglMakeCurrent(tkhdc, tkhrc) )
                    {
                        Result = GL_TRUE;
                    }
                    else
                    {
                        PrintMessage("wglMakeCurrent Failed\n");
                    }
                }
                else
                {
                    PrintMessage("wglCreateContext Failed\n");
                }
            }
        }
        else
        {
            PrintMessage("Could not get an HDC for window 0x%08lX\n", tkhwnd );
        }
    }
    else
    {
        PrintMessage("create window failed\n");
    }

    if ( GL_FALSE == Result )
    {
        DestroyThisWindow(tkhwnd);  // Something Failed, Destroy this window
    }
    return( Result );
}

/******************************************************************************/

/*
 * You cannot just call DestroyWindow() here.  The programs do not expect
 * tkQuit() to return;  DestroyWindow() just sends a WM_DESTROY message
 */

void tkQuit(void)
{
    DestroyThisWindow(tkhwnd);
    ExitProcess(0);
}

/******************************************************************************/

void tkSetOneColor(int index, float r, float g, float b)
{
    PALETTEENTRY PalEntry;
    HPALETTE Palette;

    if ( NULL != (Palette = CreateCIPalette( tkhdc )) )
    {
        if ( tkUseStaticColors && ( index == 0 || index == 255 ) )
            return;

        PalEntry.peRed   = (BYTE)(r*(float)255.0 + (float)0.5);
        PalEntry.peGreen = (BYTE)(g*(float)255.0 + (float)0.5);
        PalEntry.peBlue  = (BYTE)(b*(float)255.0 + (float)0.5);
        PalEntry.peFlags = ( tkUseStaticColors ) ? PC_NOCOLLAPSE : 0;

        // This is a workaround for a GDI palette "feature".  If any of
        // the static colors are repeated in the palette, those colors
        // will map to the first occurance.  So, for our case where there
        // are only two static colors (black and white), if a white
        // color appears anywhere in the palette other than in the last
        // entry, the static white will remap to the first white.  This
        // destroys the nice one-to-one mapping we are trying to achieve.
        //
        // There are two ways to workaround this.  The first is to
        // simply not allow a pure white anywhere but in the last entry.
        // Such requests are replaced with an attenuated white of
        // (0xFE, 0xFE, 0xFE).
        //
        // The other way is to mark these extra whites with PC_RESERVED
        // which will cause GDI to skip these entries when mapping colors.
        // This way the app gets the actual colors requested, but can
        // have side effects on other apps.
        //
        // Both solutions are included below.  The PC_RESERVED solution is
        // the one currently enabled.  It may have side effects, but taking
        // over the static colors as we are is a really big side effect that
        // should swamp out the effects of using PC_RESERVED.

        if ( tkUseStaticColors )
        {
            if ( PalEntry.peRed   == 0xFF &&
                 PalEntry.peGreen == 0xFF &&
                 PalEntry.peBlue  == 0xFF )
            {
            #define USE_PC_RESERVED_WORKAROUND  1
            #if USE_PC_RESERVED_WORKAROUND
                PalEntry.peFlags |= PC_RESERVED;
            #else
                PalEntry.peRed   =
                PalEntry.peGreen =
                PalEntry.peBlue  = 0xFE;
            #endif
            }
        }

        SetPaletteEntries( Palette, index, 1, &PalEntry);

        DelayPaletteRealization();
    }
}

void tkSetFogRamp(int density, int startIndex)
{
    HPALETTE CurrentPal;
    PALETTEENTRY *pPalEntry;
    UINT n, i, j, k, intensity, fogValues, colorValues;

    if ( NULL != (CurrentPal = CreateCIPalette(tkhdc)) )
    {
        n = GetPaletteEntries( CurrentPal, 0, 0, NULL );

        pPalEntry = AllocateMemory( n * sizeof(PALETTEENTRY) );

        if ( NULL != pPalEntry)
        {
            fogValues = 1 << density;
            colorValues = 1 << startIndex;
            for (i = 0; i < colorValues; i++) {
                for (j = 0; j < fogValues; j++) {
                    k = i * fogValues + j;

                    intensity = i * fogValues + j * colorValues;
                    //mf: not sure what they're trying to do here
                    //intensity = (intensity << 8) | intensity; ???

                // This is a workaround for a GDI palette "feature".  If any of
                // the static colors are repeated in the palette, those colors
                // will map to the first occurance.  So, for our case where there
                // are only two static colors (black and white), if a white
                // color appears anywhere in the palette other than in the last
                // entry, the static white will remap to the first white.  This
                // destroys the nice one-to-one mapping we are trying to achieve.
                //
                // There are two ways to workaround this.  The first is to
                // simply not allow a pure white anywhere but in the last entry.
                // Such requests are replaced with an attenuated white of
                // (0xFE, 0xFE, 0xFE).
                //
                // The other way is to mark these extra whites with PC_RESERVED
                // which will cause GDI to skip these entries when mapping colors.
                // This way the app gets the actual colors requested, but can
                // have side effects on other apps.
                //
                // Both solutions are included below.  The PC_RESERVED solution is
                // the one currently enabled.  It may have side effects, but taking
                // over the static colors as we are is a really big side effect that
                // should swamp out the effects of using PC_RESERVED.

                #if USE_PC_RESERVED_WORKAROUND
                    if (intensity > 0xFF)
                        intensity = 0xFF;
                #else
                    if (intensity >= 0xFF)
                        intensity = ( tkUseStaticColors && k != 255) ? 0xFE : 0xFF;
                #endif

                    pPalEntry[k].peRed =
                    pPalEntry[k].peGreen =
                    pPalEntry[k].peBlue = (BYTE) intensity;
                    pPalEntry[k].peFlags = ( tkUseStaticColors && k != 0 && k != 255 )
                                           ? PC_NOCOLLAPSE : 0;

                #if USE_PC_RESERVED_WORKAROUND
                    if (tkUseStaticColors && intensity == 0xFF
                        && k != 0 && k!= 255)
                        pPalEntry[k].peFlags |= PC_RESERVED;
                #endif
                }
            }

            SetPaletteEntries(CurrentPal, 0, n, pPalEntry);
            FreeMemory( pPalEntry );

            DelayPaletteRealization();
        }
    }
}

void tkSetGreyRamp(void)
{
    HPALETTE CurrentPal;
    PALETTEENTRY *Entries;
    UINT Count, i;
    float intensity;

    if ( NULL != (CurrentPal = CreateCIPalette( tkhdc )) )
    {
        Count   = GetPaletteEntries( CurrentPal, 0, 0, NULL );
        Entries = AllocateMemory( Count * sizeof(PALETTEENTRY) );

        if ( NULL != Entries )
        {
            for (i = 0; i < Count; i++)
            {
                intensity = (float)(((double)i / (double)(Count-1)) * (double)255.0 + (double)0.5);
                Entries[i].peRed =
                Entries[i].peGreen =
                Entries[i].peBlue = (BYTE) intensity;
                Entries[i].peFlags = ( tkUseStaticColors && i != 0 && i != 255 )
                                     ? PC_NOCOLLAPSE : 0;
            }
            SetPaletteEntries( CurrentPal, 0, Count, Entries );
            FreeMemory( Entries );

            DelayPaletteRealization();
        }
    }
}

void tkSetRGBMap( int Size, float *Values )
{
    HPALETTE CurrentPal;
    PIXELFORMATDESCRIPTOR Pfd, *pPfd;
    PALETTEENTRY *Entries;
    UINT Count;

    if ( NULL != (CurrentPal = CreateCIPalette( tkhdc )) )
    {
        pPfd = &Pfd;

        if ( PixelFormatDescriptorFromDc( tkhdc, pPfd ) )
        {
            Count    = 1 << pPfd->cColorBits;
            Entries  = AllocateMemory( Count * sizeof(PALETTEENTRY) );

            if ( NULL != Entries )
            {
                FillRgbPaletteEntries( pPfd, Entries, Count );
                SetPaletteEntries( CurrentPal, 0, Count, Entries );
                FreeMemory(Entries);

                RealizePaletteNow( tkhdc, tkhpalette, FALSE );
            }
        }
    }
}

/******************************************************************************/

void tkSwapBuffers(void)
{
    SwapBuffers(tkhdc);
}

/******************************************************************************/

GLint tkGetColorMapSize(void)
{
    CreateCIPalette( tkhdc );

    if ( NULL == tkhpalette )
        return( 0 );

    return( GetPaletteEntries( tkhpalette, 0, 0, NULL ) );
}

void tkGetMouseLoc(int *x, int *y)
{
    POINT Point;

    *x = 0;
    *y = 0;

    GetCursorPos(&Point);

    /*
     *  GetCursorPos returns screen coordinates,
     *  we want window coordinates
     */

    *x = Point.x - windInfo.x;
    *y = Point.y - windInfo.y;
}

HWND tkGetHWND(void)
{
    return tkhwnd;
}

HDC tkGetHDC(void)
{
    return tkhdc;
}

HGLRC tkGetHRC(void)
{
    return tkhrc;
}

GLenum tkGetDisplayModePolicy(void)
{
    return windInfo.dmPolicy;
}

GLint tkGetDisplayModeID(void)
{
    return windInfo.ipfd;
}

GLenum tkGetDisplayMode(void)
{
    return windInfo.type;
}

/***********************************************************************
 *                                                                     *
 *  The Following functions are for our own use only. (ie static)      *
 *                                                                     *
 ***********************************************************************/

static long
tkWndProc(HWND hWnd, UINT message, DWORD wParam, LONG lParam)
{
    int key;
    PAINTSTRUCT paint;
    HDC hdc;
    PIXELFORMATDESCRIPTOR pfd;

    switch (message) {

    case WM_USER:
        if ( RealizePaletteNow( tkhdc, tkhpalette, FALSE ) > 0 )
        {
            ForceRedraw( hWnd );
        }
        return(0);

    case WM_SIZE:
        windInfo.width  = LOWORD(lParam);
        windInfo.height = HIWORD(lParam);

        if (ReshapeFunc)
        {
            (*ReshapeFunc)(windInfo.width, windInfo.height);

            ForceRedraw( hWnd );
        }
        return (0);

    case WM_MOVE:
        windInfo.x = LOWORD(lParam);
        windInfo.y = HIWORD(lParam);
        return (0);

    case WM_PAINT:
        /*
         *  Validate the region even if there are no DisplayFunc.
         *  Otherwise, USER will not stop sending WM_PAINT messages.
         */

        hdc = BeginPaint(tkhwnd, &paint);

        if (DisplayFunc)
        {
            (*DisplayFunc)();
        }

        EndPaint(tkhwnd, &paint);
        return (0);

    case WM_QUERYNEWPALETTE:

    // We don't actually realize the palette here (we do it at WM_ACTIVATE
    // time), but we need the system to think that we have so that a
    // WM_PALETTECHANGED message is generated.

        return (1);

    case WM_PALETTECHANGED:

    // Respond to this message only if the window that changed the palette
    // is not this app's window.

    // We are not the foreground window, so realize palette in the
    // background.  We cannot call RealizePaletteNow to do this because
    // we should not do any of the tkUseStaticColors processing while
    // in background.

        if ( hWnd != (HWND) wParam )
        {
            if ( !tkSystemColorsInUse &&
                 NULL != tkhpalette &&
                 NULL != SelectPalette( tkhdc, tkhpalette, TRUE ) )
                RealizePalette( tkhdc );
        }

        return (0);

    case WM_SYSCOLORCHANGE:

    // If the system colors have changed and we have a palette
    // for an RGB surface then we need to recompute the static
    // color mapping because they might have been changed in
    // the process of changing the system colors.

        if (tkhdc != NULL && tkhpalette != NULL &&
            PixelFormatDescriptorFromDc(tkhdc, &pfd) &&
            (pfd.dwFlags & PFD_NEED_PALETTE) &&
            pfd.iPixelType == PFD_TYPE_RGBA)
        {
            HPALETTE hpalTmp;

            hpalTmp = tkhpalette;
            tkhpalette = NULL;
            if (CreateRGBPalette(tkhdc) != NULL)
            {
                DeleteObject(hpalTmp);
                ForceRedraw(hWnd);
            }
            else
            {
                tkhpalette = hpalTmp;
            }
        }
        break;

    case WM_ACTIVATE:

    // If the window is going inactive, the palette must be realized to
    // the background.  Cannot depend on WM_PALETTECHANGED to be sent since
    // the window that comes to the foreground may or may not be palette
    // managed.

        if ( LOWORD(wParam) == WA_INACTIVE )
        {
            if ( NULL != tkhpalette )
            {
            // Realize as a background palette.  Need to call
            // RealizePaletteNow rather than RealizePalette directly to
            // because it may be necessary to release usage of the static
            // system colors.

                if ( RealizePaletteNow( tkhdc, tkhpalette, TRUE ) > 0 )
                    ForceRedraw( hWnd );
            }
        }

    // Window is going active.  If we are not iconized, realize palette
    // to the foreground.  If management of the system static colors is
    // needed, RealizePaletteNow will take care of it.

        else if ( HIWORD(wParam) == 0 )
        {
            if ( NULL != tkhpalette )
            {
                if ( RealizePaletteNow( tkhdc, tkhpalette, FALSE ) > 0 )
                    ForceRedraw( hWnd );

                return (1);
            }
        }

    // Allow DefWindowProc() to finish the default processing (which includes
    // changing the keyboard focus).

        break;

    case WM_MOUSEMOVE:

        if (MouseMoveFunc)
        {
            GLenum mask;

            mask = 0;
            if (wParam & MK_LBUTTON) {
                mask |= TK_LEFTBUTTON;
            }
            if (wParam & MK_MBUTTON) {
                mask |= TK_MIDDLEBUTTON;
            }
            if (wParam & MK_RBUTTON) {
                mask |= TK_RIGHTBUTTON;
            }

            if ((*MouseMoveFunc)( LOWORD(lParam), HIWORD(lParam), mask ))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_LBUTTONDOWN:

        SetCapture(hWnd);

        if (MouseDownFunc)
        {
            if ( (*MouseDownFunc)(LOWORD(lParam), HIWORD(lParam),
                 TK_LEFTBUTTON) )
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_LBUTTONUP:

        ReleaseCapture();

        if (MouseUpFunc)
        {
            if ((*MouseUpFunc)(LOWORD(lParam), HIWORD(lParam), TK_LEFTBUTTON))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_MBUTTONDOWN:

        SetCapture(hWnd);

        if (MouseDownFunc)
        {
            if ((*MouseDownFunc)(LOWORD(lParam), HIWORD(lParam),
                    TK_MIDDLEBUTTON))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_MBUTTONUP:

        ReleaseCapture();

        if (MouseUpFunc)
        {
            if ((*MouseUpFunc)(LOWORD(lParam), HIWORD(lParam),
                TK_MIDDLEBUTTON))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_RBUTTONDOWN:

        SetCapture(hWnd);

        if (MouseDownFunc)
        {
            if ((*MouseDownFunc)(LOWORD(lParam), HIWORD(lParam),
                TK_RIGHTBUTTON))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_RBUTTONUP:

        ReleaseCapture();

        if (MouseUpFunc)
        {
            if ((*MouseUpFunc)(LOWORD(lParam), HIWORD(lParam),
                TK_RIGHTBUTTON))
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_KEYDOWN:
        switch (wParam) {
        case VK_SPACE:          key = TK_SPACE;         break;
        case VK_RETURN:         key = TK_RETURN;        break;
        case VK_ESCAPE:         key = TK_ESCAPE;        break;
        case VK_LEFT:           key = TK_LEFT;          break;
        case VK_UP:             key = TK_UP;            break;
        case VK_RIGHT:          key = TK_RIGHT;         break;
        case VK_DOWN:           key = TK_DOWN;          break;
        default:                key = GL_FALSE;         break;
        }

        if (key && KeyDownFunc)
        {
            GLenum mask;

            mask = 0;
            if (GetKeyState(VK_CONTROL)) {
                mask |= TK_CONTROL;
            }

            if (GetKeyState(VK_SHIFT)) {

                mask |= TK_SHIFT;
            }

            if ( (*KeyDownFunc)(key, mask) )
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_CHAR:
        if (('0' <= wParam && wParam <= '9') ||
            ('a' <= wParam && wParam <= 'z') ||
            ('A' <= wParam && wParam <= 'Z')) {

            key = wParam;
        } else {
            key = GL_FALSE;
        }

        if (key && KeyDownFunc) {
            GLenum mask;

            mask = 0;

            if (GetKeyState(VK_CONTROL)) {
                mask |= TK_CONTROL;
            }

            if (GetKeyState(VK_SHIFT)) {
                mask |= TK_SHIFT;
            }

            if ( (*KeyDownFunc)(key, mask) )
            {
                ForceRedraw( hWnd );
            }
        }
        return (0);

    case WM_CLOSE:
        DestroyWindow(tkhwnd);
        return(0);

    case WM_DESTROY:
        CleanUp();
        PostQuitMessage(TRUE);
        return 0;
    }
    return(DefWindowProc( hWnd, message, wParam, lParam));
}

#if RESTORE_FROM_REGISTRY
/******************************Public*Routine******************************\
* GetRegistrySysColors
*
* Reads the Control Panel's color settings from the registry and stores
* those values in pcr.  If we fail to get any value, then the corresponding
* entry in pcr is not modified.
*
* History:
*  12-Apr-1995 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

static BOOL GetRegistrySysColors(COLORREF *pcr, int nColors)
{
    BOOL bRet = FALSE;
    long lRet;
    HKEY hkSysColors = (HKEY) NULL;
    int i;
    DWORD dwDataType;
    char achColor[64];
    DWORD cjColor;

    TKASSERT(nColors <= tkNumStaticColors);

// Open the key for the system color settings.

    lRet = RegOpenKeyExA(HKEY_CURRENT_USER,
                         "Control Panel\\Colors",
                         0,
                         KEY_QUERY_VALUE,
                         &hkSysColors);

    if ( lRet != ERROR_SUCCESS )
    {
        goto GetRegistrySysColors_exit;
    }

// Read each system color value.  The names are stored in the global
// array of char *, gaszSysClrNames.

    for (i = 0; i < nColors; i++)
    {
        cjColor = sizeof(achColor);
        lRet = RegQueryValueExA(hkSysColors,
                                (LPSTR) gaszSysClrNames[i],
                                (LPDWORD) NULL,
                                &dwDataType,
                                (LPBYTE) achColor,
                                &cjColor);

        TKASSERT(lRet != ERROR_MORE_DATA);

        if ( lRet == ERROR_SUCCESS && dwDataType == REG_SZ )
        {
            DWORD r, g, b;

            sscanf(achColor, "%ld %ld %ld", &r, &g, &b);
            pcr[i] = RGB(r, g, b);
        }
    }

    bRet = TRUE;

GetRegistrySysColors_exit:
    if (hkSysColors)
        RegCloseKey(hkSysColors);

    return bRet;
}
#endif

/******************************Public*Routine******************************\
* GrabStaticEntries
*
* Support routine for RealizePaletteNow to manage the static system color
* usage.
*
* This function will save the current static system color usage state.
* It will fail if:
*
*   1.  TK is not in "sys color in use state but system palette is in
*       SYSPAL_NOSTATIC mode.  This means that another app still possesses
*       the static system colors.  This this happens, GrabStaticEntries
*       will post a message to cause TK to try again (by calling
*       DelayPaletteRealization).
*
* Side effect:
*   If system colors are changed, then WM_SYSCOLORCHANGE message is
*   broadcast to all top level windows.
*
*   DelayPaletteRealization may be called in case 2 above, resulting in
*   a WM_USER message being posted to our message queue.
*
* Returns:
*   TRUE if successful, FALSE otherwise (see above).
*
* History:
*  26-Apr-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

static BOOL GrabStaticEntries(HDC hdc)
{
    int i;
    BOOL bRet = FALSE;

// Do nothing if sys colors already in use.

    if ( !tkSystemColorsInUse )
    {
    // Take possession only if no other app has the static colors.
    // How can we tell?  If the return from SetSystemPaletteUse is
    // SYSPAL_STATIC, then no other app has the statics.  If it is
    // SYSPAL_NOSTATIC, someone else has them and we must fail.
    //
    // SetSystemPaletteUse is properly synchronized internally
    // so that it is atomic.
    //
    // Because we are relying on SetSystemPaletteUse to synchronize TK,
    // it is important to observe the following order for grabbing and
    // releasing:
    //
    //      Grab        call SetSystemPaletteUse and check for SYSPAL_STATIC
    //                  save sys color settings
    //                  set new sys color settings
    //
    //      Release     restore sys color settings
    //                  call SetSystemPaletteUse

        if ( SetSystemPaletteUse( hdc, SYSPAL_NOSTATIC ) == SYSPAL_STATIC )
        {
        // Save current sys color settings.

            for (i = COLOR_SCROLLBAR; i <= COLOR_BTNHIGHLIGHT; i++)
                gacrSave[i - COLOR_SCROLLBAR] = GetSysColor(i);

        // Set b&w sys color settings.  Put TK in "sys colors in use" state.

            SetSysColors(tkNumStaticColors, gaiStaticIndex, gacrBlackAndWhite);
            tkSystemColorsInUse = TRUE;

        // Inform all other top-level windows of the system color change.

            PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);

            bRet = TRUE;
        }

    // Sleep a little and then post message to try palette realization again
    // later.

        else
        {
            Sleep(0L);
            DelayPaletteRealization();
        }
    }
    else
        bRet = TRUE;

    return bRet;
}

/******************************Public*Routine******************************\
* ReleaseStaticEntries
*
* Support routine for RealizePaletteNow to manage the static system color
* usage.
*
* This function will reset the current static system color usage state.
* It will fail if:
*
*   1.  TK is not in a "sys colors in use" state.  If we are in this case,
*       then the static system colors do not need to be released.
*
* Side effect:
*   If system colors are changed, then WM_SYSCOLORCHANGE message is
*   broadcast to all top level windows.
*
* Returns:
*   TRUE if successful, FALSE otherwise (see above).
*
* History:
*  21-Jul-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

static BOOL ReleaseStaticEntries(HDC hdc)
{
    BOOL bRet = FALSE;

// Do nothing if sys colors not in use.

    if ( tkSystemColorsInUse )
    {
#if RESTORE_FROM_REGISTRY
    // Replace saved system colors with registry values.  We do it now
    // rather than earlier because someone may have changed registry while
    // TK app was running in the foreground (very unlikely, but it could
    // happen).
    //
    // Also, we still try to save current setting in GrabStaticEntries so
    // that if for some reason we fail to grab one or more of the colors
    // from the registry, we can still fall back on what we grabbed via
    // GetSysColors (even though there is a chance its the wrong color).

        GetRegistrySysColors(gacrSave, tkNumStaticColors);
#endif

    // Restore the saved system color settings.

        SetSysColors(tkNumStaticColors, gaiStaticIndex, gacrSave);

    // Return the system palette to SYSPAL_STATIC.

        SetSystemPaletteUse( hdc, SYSPAL_STATIC );

    // Inform all other top-level windows of the system color change.

        PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);

    // Reset the "sys colors in use" state and return success.

        tkSystemColorsInUse = FALSE;
        bRet = TRUE;
    }

    return bRet;
}

// Default palette entry flags
#define PALETTE_FLAGS PC_NOCOLLAPSE

// Gamma correction factor * 10
#define GAMMA_CORRECTION 14

// Maximum color distance with 8-bit components
#define MAX_COL_DIST (3*256*256L)

// Number of static colors
#define STATIC_COLORS 20

// Flags used when matching colors
#define EXACT_MATCH 1
#define COLOR_USED 1

// Conversion tables for n bits to eight bits

#if GAMMA_CORRECTION == 10
// These tables are corrected for a gamma of 1.0
static unsigned char abThreeToEight[8] =
{
    0, 0111 >> 1, 0222 >> 1, 0333 >> 1, 0444 >> 1, 0555 >> 1, 0666 >> 1, 0377
};
static unsigned char abTwoToEight[4] =
{
    0, 0x55, 0xaa, 0xff
};
static unsigned char abOneToEight[2] =
{
    0, 255
};
#else
// These tables are corrected for a gamma of 1.4
static unsigned char abThreeToEight[8] =
{
    0, 63, 104, 139, 171, 200, 229, 255
};
static unsigned char abTwoToEight[4] =
{
    0, 116, 191, 255
};
static unsigned char abOneToEight[2] =
{
    0, 255
};
#endif

// Table which indicates which colors in a 3-3-2 palette should be
// replaced with the system default colors
#if GAMMA_CORRECTION == 10
static int aiDefaultOverride[STATIC_COLORS] =
{
    0, 4, 32, 36, 128, 132, 160, 173, 181, 245,
    247, 164, 156, 7, 56, 63, 192, 199, 248, 255
};
#else
static int aiDefaultOverride[STATIC_COLORS] =
{
    0, 3, 24, 27, 64, 67, 88, 173, 181, 236,
    247, 164, 91, 7, 56, 63, 192, 199, 248, 255
};
#endif

static unsigned char
ComponentFromIndex(int i, int nbits, int shift)
{
    unsigned char val;

    TKASSERT(nbits >= 1 && nbits <= 3);

    val = i >> shift;
    switch (nbits)
    {
    case 1:
        return abOneToEight[val & 1];

    case 2:
        return abTwoToEight[val & 3];

    case 3:
        return abThreeToEight[val & 7];
    }
}

// System default colors
static PALETTEENTRY apeDefaultPalEntry[STATIC_COLORS] =
{
    { 0,   0,   0,    0 },
    { 0x80,0,   0,    0 },
    { 0,   0x80,0,    0 },
    { 0x80,0x80,0,    0 },
    { 0,   0,   0x80, 0 },
    { 0x80,0,   0x80, 0 },
    { 0,   0x80,0x80, 0 },
    { 0xC0,0xC0,0xC0, 0 },

    { 192, 220, 192,  0 },
    { 166, 202, 240,  0 },
    { 255, 251, 240,  0 },
    { 160, 160, 164,  0 },

    { 0x80,0x80,0x80, 0 },
    { 0xFF,0,   0,    0 },
    { 0,   0xFF,0,    0 },
    { 0xFF,0xFF,0,    0 },
    { 0,   0,   0xFF, 0 },
    { 0xFF,0,   0xFF, 0 },
    { 0,   0xFF,0xFF, 0 },
    { 0xFF,0xFF,0xFF, 0 }
};

/******************************Public*Routine******************************\
*
* UpdateStaticMapping
*
* Computes the best match between the current system static colors
* and a 3-3-2 palette
*
* History:
*  Tue Aug 01 18:18:12 1995-by-Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

static void
UpdateStaticMapping(PALETTEENTRY *pe332Palette)
{
    HPALETTE hpalStock;
    int iStatic, i332;
    int iMinDist, iDist;
    int iDelta;
    int iMinEntry;
    PALETTEENTRY *peStatic, *pe332;

    hpalStock = GetStockObject(DEFAULT_PALETTE);

    // The system should always have one of these
    TKASSERT(hpalStock != NULL);
    // Make sure there's the correct number of entries
    TKASSERT(GetPaletteEntries(hpalStock, 0, 0, NULL) == STATIC_COLORS);

    // Get the current static colors
    GetPaletteEntries(hpalStock, 0, STATIC_COLORS, apeDefaultPalEntry);

    // Zero the flags in the static colors because they are used later
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        peStatic->peFlags = 0;
        peStatic++;
    }

    // Zero the flags in the incoming palette because they are used later
    pe332 = pe332Palette;
    for (i332 = 0; i332 < 256; i332++)
    {
        pe332->peFlags = 0;
        pe332++;
    }

    // Try to match each static color exactly
    // This saves time by avoiding the least-squares match for each
    // exact match
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        pe332 = pe332Palette;
        for (i332 = 0; i332 < 256; i332++)
        {
            if (peStatic->peRed == pe332->peRed &&
                peStatic->peGreen == pe332->peGreen &&
                peStatic->peBlue == pe332->peBlue)
            {
                TKASSERT(pe332->peFlags != COLOR_USED);

                peStatic->peFlags = EXACT_MATCH;
                pe332->peFlags = COLOR_USED;

aiDefaultOverride[iStatic] = i332;

                break;
            }

            pe332++;
        }

        peStatic++;
    }

    // Match each static color as closely as possible to an entry
    // in the 332 palette by minimized the square of the distance
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        // Skip colors already matched exactly
        if (peStatic->peFlags == EXACT_MATCH)
        {
            peStatic++;
            continue;
        }

        iMinDist = MAX_COL_DIST+1;
#if DBG
        iMinEntry = -1;
#endif

        pe332 = pe332Palette;
        for (i332 = 0; i332 < 256; i332++)
        {
            // Skip colors already used
            if (pe332->peFlags == COLOR_USED)
            {
                pe332++;
                continue;
            }

            // Compute Euclidean distance squared
            iDelta = pe332->peRed-peStatic->peRed;
            iDist = iDelta*iDelta;
            iDelta = pe332->peGreen-peStatic->peGreen;
            iDist += iDelta*iDelta;
            iDelta = pe332->peBlue-peStatic->peBlue;
            iDist += iDelta*iDelta;

            if (iDist < iMinDist)
            {
                iMinDist = iDist;
                iMinEntry = i332;
            }

            pe332++;
        }

        TKASSERT(iMinEntry != -1);

        // Remember the best match
        aiDefaultOverride[iStatic] = iMinEntry;
        pe332Palette[iMinEntry].peFlags = COLOR_USED;

        peStatic++;
    }

    // Zero the flags in the static colors because they may have been
    // set.  We want them to be zero so the colors can be remapped
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        peStatic->peFlags = 0;
        peStatic++;
    }

    // Reset the 332 flags because we may have set them
    pe332 = pe332Palette;
    for (i332 = 0; i332 < 256; i332++)
    {
        pe332->peFlags = PALETTE_FLAGS;
        pe332++;
    }

#if 0
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        PrintMessage("Static color %2d maps to %d\n",
                     iStatic, aiDefaultOverride[iStatic]);
    }
#endif
}

/******************************Public*Routine******************************\
* FillRgbPaletteEntries
*
* Fills a PALETTEENTRY array with values required for a logical rgb palette.
* If tkSetStaticColorUsage has been called with TRUE, the static system
* colors will be overridden.  Otherwise, the PALETTEENTRY array will be
* fixed up to contain the default static system colors.
*
* History:
*  26-Apr-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

static PALETTEENTRY *
FillRgbPaletteEntries(  PIXELFORMATDESCRIPTOR *Pfd,
                        PALETTEENTRY *Entries,
                        UINT Count
                     )
{
    PALETTEENTRY *Entry;
    UINT i;

    if ( NULL != Entries )
    {
        for ( i = 0, Entry = Entries ; i < Count ; i++, Entry++ )
        {
            Entry->peRed   = ComponentFromIndex(i, Pfd->cRedBits,
                                    Pfd->cRedShift);
            Entry->peGreen = ComponentFromIndex(i, Pfd->cGreenBits,
                                    Pfd->cGreenShift);
            Entry->peBlue  = ComponentFromIndex(i, Pfd->cBlueBits,
                                    Pfd->cBlueShift);
            Entry->peFlags = PALETTE_FLAGS;
        }

        if ( 256 == Count)
        {
        // If app set static system color usage for fixed palette support,
        // setup to take over the static colors.  Otherwise, fixup the
        // static system colors.

            if ( tkUseStaticColors )
            {
            // Black and white already exist as the only remaining static
            // colors.  Let those remap.  All others should be put into
            // the palette (i.e., set PC_NOCOLLAPSE).

                Entries[0].peFlags = 0;
                Entries[255].peFlags = 0;
            }
            else
            {
            // The defaultOverride array is computed assuming a 332
            // palette where red has zero shift, etc.

                if ( (3 == Pfd->cRedBits)   && (0 == Pfd->cRedShift)   &&
                     (3 == Pfd->cGreenBits) && (3 == Pfd->cGreenShift) &&
                     (2 == Pfd->cBlueBits)  && (6 == Pfd->cBlueShift) )
                {
                    UpdateStaticMapping(Entries);

                    for ( i = 0 ; i < STATIC_COLORS ; i++)
                    {
                        Entries[aiDefaultOverride[i]] = apeDefaultPalEntry[i];
                    }
                }
            }
        }
    }
    return( Entries );
}

static HPALETTE
CreateRGBPalette( HDC Dc )
{
    PIXELFORMATDESCRIPTOR Pfd, *pPfd;
    LOGPALETTE *LogPalette;
    UINT Count;

    if ( NULL == tkhpalette )
    {
        pPfd = &Pfd;

        if ( PixelFormatDescriptorFromDc( Dc, pPfd ) )
        {
            /*
             *  Make sure we need a palette
             */

            if ( (pPfd->iPixelType == PFD_TYPE_RGBA) &&
                 (pPfd->dwFlags & PFD_NEED_PALETTE) )
            {
                /*
                 *  Note how palette is to be realized.  Take over the
                 *  system colors if either the pixel format requires it
                 *  or the app wants it.
                 */
                tkUseStaticColors = ( pPfd->dwFlags & PFD_NEED_SYSTEM_PALETTE )
                                    || TK_USE_FIXED_332_PAL(windInfo.type);

                Count       = 1 << pPfd->cColorBits;
                LogPalette  = AllocateMemory( sizeof(LOGPALETTE) +
                                Count * sizeof(PALETTEENTRY));

                if ( NULL != LogPalette )
                {
                    LogPalette->palVersion    = 0x300;
                    LogPalette->palNumEntries = Count;

                    FillRgbPaletteEntries( pPfd,
                                           &LogPalette->palPalEntry[0],
                                           Count );

                    tkhpalette = CreatePalette(LogPalette);
                    FreeMemory(LogPalette);

                    RealizePaletteNow( Dc, tkhpalette, FALSE );
                }
            }
        }
    }
    return( tkhpalette );
}

static HPALETTE
CreateCIPalette( HDC Dc )
{
    PIXELFORMATDESCRIPTOR Pfd;
    LOGPALETTE *LogicalPalette;
    HPALETTE StockPalette;
    UINT PaletteSize, StockPaletteSize, EntriesToCopy;

    if ( (Dc != NULL) && (NULL == tkhpalette) )
    {
        if ( PixelFormatDescriptorFromDc( Dc, &Pfd ) )
        {
            if ( Pfd.iPixelType == PFD_TYPE_COLORINDEX )
            {
                /*
                 *  Note how palette is to be realized (Is this the correct place to do this?)
                 */
                tkUseStaticColors = ( Pfd.dwFlags & PFD_NEED_SYSTEM_PALETTE )
                                    || TK_USE_FIXED_332_PAL(windInfo.type);

                /*
                 *  Limit the size of the palette to 256 colors.
                 *  Why? Because this is what was decided.
                 */

                PaletteSize = (Pfd.cColorBits >= 8) ? 256 : (1 << Pfd.cColorBits);

                LogicalPalette = AllocateZeroedMemory( sizeof(LOGPALETTE) +
                                        (PaletteSize * sizeof(PALETTEENTRY)) );

                if ( NULL != LogicalPalette )
                {
                    LogicalPalette->palVersion    = 0x300;
                    LogicalPalette->palNumEntries = PaletteSize;

                    StockPalette     = GetStockObject(DEFAULT_PALETTE);
                    StockPaletteSize = GetPaletteEntries( StockPalette, 0, 0, NULL );

                    /*
                     *  start by copying default palette into new one
                     */

                    EntriesToCopy = StockPaletteSize < PaletteSize ?
                                        StockPaletteSize : PaletteSize;

                    GetPaletteEntries( StockPalette, 0, EntriesToCopy,
                                        LogicalPalette->palPalEntry );

                    /*
                     *  If we are taking possession of the system colors,
                     *  must guarantee that 0 and 255 are black and white
                     *  (respectively).
                     */

                    if ( tkUseStaticColors && PaletteSize == 256 )
                    {
                        int i;

                        LogicalPalette->palPalEntry[0].peRed =
                        LogicalPalette->palPalEntry[0].peGreen =
                        LogicalPalette->palPalEntry[0].peBlue = 0x00;

                        LogicalPalette->palPalEntry[255].peRed =
                        LogicalPalette->palPalEntry[255].peGreen =
                        LogicalPalette->palPalEntry[255].peBlue = 0xFF;

                        LogicalPalette->palPalEntry[0].peFlags =
                        LogicalPalette->palPalEntry[255].peFlags = 0;

                        /*
                         *  All other entries should be remappable,
                         *  so mark them as PC_NOCOLLAPSE.
                         */
                        for ( i = 1; i < 255; i++ )
                            LogicalPalette->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
                    }

                    tkhpalette = CreatePalette(LogicalPalette);

                    FreeMemory(LogicalPalette);

                    RealizePaletteNow( Dc, tkhpalette, FALSE );
                }
            }
        }
    }
    return( tkhpalette );
}

static BOOL
FindPixelFormat(HDC hdc, GLenum type)
{
    PIXELFORMATDESCRIPTOR pfd;
    int PfdIndex;
    BOOL Result = FALSE;

    if ( TK_MINIMUM_CRITERIA == windInfo.dmPolicy )
        PfdIndex = FindBestPixelFormat(hdc, type, &pfd);
    else if ( TK_EXACT_MATCH == windInfo.dmPolicy )
        PfdIndex = FindExactPixelFormat(hdc, type, &pfd);
    else if ( IsPixelFormatValid(hdc, windInfo.ipfd, &pfd) )
        PfdIndex = windInfo.ipfd;
    else
        PfdIndex = 0;

    if ( PfdIndex )
    {
        if ( SetPixelFormat(hdc, PfdIndex, &pfd) )
        {
            /*
             *  If this pixel format requires a palette do it now.
             *  In colorindex mode, create a logical palette only
             *  if the application needs to modify it.
             */

            CreateRGBPalette( hdc );
            Result = TRUE;
        }
        else
        {
            PrintMessage("SetPixelFormat failed\n");
        }
    }
    else
    {
        PrintMessage("Selecting a pixel format failed\n");
    }
    return(Result);
}

static int
FindBestPixelFormat(HDC hdc, GLenum type, PIXELFORMATDESCRIPTOR *ppfd)
{
    PIXELFORMATDESCRIPTOR pfd;

    pfd.nSize       = sizeof(pfd);
    pfd.nVersion    = 1;
    pfd.dwFlags     = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;

    if (TK_IS_DOUBLE(type))
        pfd.dwFlags |= PFD_DOUBLEBUFFER;

    if (TK_IS_INDEX(type)) {
        pfd.iPixelType = PFD_TYPE_COLORINDEX;
        pfd.cColorBits = 8;
    } else {
        pfd.iPixelType = PFD_TYPE_RGBA;
        pfd.cColorBits = 24;
    }

    if (TK_HAS_ALPHA(type))
        pfd.cAlphaBits = 8;
    else
        pfd.cAlphaBits = 0;

    if (TK_HAS_ACCUM(type))
        pfd.cAccumBits = pfd.cColorBits + pfd.cAlphaBits;
    else
        pfd.cAccumBits = 0;

    if (TK_HAS_DEPTH(type)) {
        if (TK_IS_DEPTH16(type))
            pfd.cDepthBits = 16;
        else
            pfd.cDepthBits = 32;
    } else {
        pfd.cDepthBits = 0;
    }

    if (TK_HAS_STENCIL(type))
        pfd.cStencilBits = 4;
    else
        pfd.cStencilBits = 0;

    pfd.cAuxBuffers = 0;
    pfd.iLayerType  = PFD_MAIN_PLANE;
    *ppfd = pfd;

    return ( ChoosePixelFormat(hdc, &pfd) );
}

static int
FindExactPixelFormat(HDC hdc, GLenum type, PIXELFORMATDESCRIPTOR *ppfd)
{
    int i, MaxPFDs, Score, BestScore, BestPFD;
    PIXELFORMATDESCRIPTOR pfd;

    i = 1;
    BestPFD = BestScore = 0;
    do
    {
        MaxPFDs = DescribePixelFormat(hdc, i, sizeof(pfd), &pfd);
        if ( MaxPFDs <= 0 )
            return ( 0 );

        Score = 0;
        if ( !( ( pfd.dwFlags & PFD_DRAW_TO_WINDOW ) &&
                ( pfd.dwFlags & PFD_SUPPORT_OPENGL ) ) )
            continue;
        if ( pfd.iLayerType != PFD_MAIN_PLANE )
            continue;
        if ( ( pfd.iPixelType == PFD_TYPE_RGBA ) && ( TK_IS_INDEX(type) ) )
            continue;
        if ( ( pfd.iPixelType == PFD_TYPE_COLORINDEX ) && ( TK_IS_RGB(type) ) )
            continue;
        if ( ( pfd.dwFlags & PFD_DOUBLEBUFFER ) && ( TK_IS_SINGLE(type) ) )
            continue;
        if ( !( pfd.dwFlags & PFD_DOUBLEBUFFER ) && ( TK_IS_DOUBLE(type) ) )
            continue;

/* If accum requested then accum rgb size must be > 0 */
/* If alpha requested then alpha size must be > 0 */
/* if accum & alpha requested then accum alpha size must be > 0 */
        if ( TK_IS_RGB(type) )
        {
            if ( TK_HAS_ACCUM(type) )
            {
                if (  pfd.cAccumBits <= 0 )
                    continue;
            }
            else
            {
                if ( pfd.cAccumBits > 0 )
                    continue;
            }

            if ( TK_HAS_ALPHA(type) )
            {
                if ( pfd.cAlphaBits <= 0 )
                    continue;
                if ( TK_HAS_ACCUM(type) && pfd.cAccumAlphaBits <= 0 )
                    continue;
            }
            else
            {
                if ( pfd.cAlphaBits > 0 )
                    continue;
            }
        }

        if ( TK_HAS_DEPTH(type) )
        {
            if ( pfd.cDepthBits <= 0 )
                continue;
        }
        else
        {
            if ( pfd.cDepthBits > 0 )
                continue;
        }

        if ( TK_HAS_STENCIL(type) )
        {
            if ( pfd.cStencilBits <= 0 )
                continue;
        }
        else
        {
            if ( pfd.cStencilBits > 0 )
                continue;
        }

        Score = pfd.cColorBits;

        if (Score > BestScore)
        {
            BestScore = Score;
            BestPFD = i;
            *ppfd = pfd;
        }
    } while (++i <= MaxPFDs);

    return ( BestPFD );
}

static BOOL IsPixelFormatValid(HDC hdc, int ipfd, PIXELFORMATDESCRIPTOR *ppfd)
{
    if ( ipfd > 0 )
    {
        if ( ipfd <= DescribePixelFormat(hdc, ipfd, sizeof(*ppfd), ppfd) )
        {
            if ( ( ppfd->dwFlags & PFD_DRAW_TO_WINDOW ) &&
                 ( ppfd->dwFlags & PFD_SUPPORT_OPENGL ) )
            {
                return ( TRUE );
            }
        }
    }
    return ( FALSE );
}

static void
PrintMessage( const char *Format, ... )
{
    va_list ArgList;
    char Buffer[256];

    va_start(ArgList, Format);
    vsprintf(Buffer, Format, ArgList);
    va_end(ArgList);

    MESSAGEBOX(GetFocus(), Buffer, "Error", MB_OK);
}

static void
DelayPaletteRealization( void )
{
    MSG Message;

    TKASSERT(NULL!=tkhwnd);

    /*
     *  Add a WM_USER message to the queue, if there isn't one there already.
     */

    if (!PeekMessage(&Message, tkhwnd, WM_USER, WM_USER, PM_NOREMOVE) )
    {
        PostMessage( tkhwnd, WM_USER, 0, 0);
    }
}

/******************************Public*Routine******************************\
* RealizePaletteNow
*
* Select the given palette in background or foreground mode (as specified
* by the bForceBackground flag), and realize the palette.
*
* If static system color usage is set, the system colors are replaced.
*
* History:
*  26-Apr-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

static long
RealizePaletteNow( HDC Dc, HPALETTE Palette, BOOL bForceBackground )
{
    long Result = -1;
    BOOL bHaveSysPal = TRUE;

    TKASSERT( NULL!=Dc      );
    TKASSERT( NULL!=Palette );

// If static system color usage is set, prepare to take over the
// system palette.

    if ( tkUseStaticColors )
    {
    // If foreground, take over the static colors.  If background, release
    // the static colors.

        if ( !bForceBackground )
        {
        // If GrabStaticEntries succeeds, then it is OK to take over the
        // static colors.  If not, then GrabStaticEntries will have
        // posted a WM_USER message for us to try again later.

            bHaveSysPal = GrabStaticEntries( Dc );
        }
        else
        {
        // If we are currently using the system colors (tkSystemColorsInUse)
        // and RealizePaletteNow was called with bForceBackground set, we
        // are being deactivated and must release the static system colors.

            ReleaseStaticEntries( Dc );
        }

    // Rerealize the palette.
    //
    // If set to TRUE, bForceBackground will force the palette to be realized
    // as a background palette, regardless of focus.  This will happen anyway
    // if the TK window does not have the keyboard focus.

        if ( (bForceBackground || bHaveSysPal) &&
             UnrealizeObject( Palette ) &&
             NULL != SelectPalette( Dc, Palette, bForceBackground ) )
        {
            Result = RealizePalette( Dc );
        }
    }
    else
    {
        if ( NULL != SelectPalette( Dc, Palette, FALSE ) )
        {
            Result = RealizePalette( Dc );
        }
    }

    return( Result );
}

static void
ForceRedraw( HWND Window )
{
    MSG Message;

    if (!PeekMessage(&Message, Window, WM_PAINT, WM_PAINT, PM_NOREMOVE) )
    {
        InvalidateRect( Window, NULL, FALSE );
    }
}

static int
PixelFormatDescriptorFromDc( HDC Dc, PIXELFORMATDESCRIPTOR *Pfd )
{
    int PfdIndex;

    if ( 0 < (PfdIndex = GetPixelFormat( Dc )) )
    {
        if ( 0 < DescribePixelFormat( Dc, PfdIndex, sizeof(*Pfd), Pfd ) )
        {
            return(PfdIndex);
        }
        else
        {
            PrintMessage("Could not get a description of pixel format %d\n",
                PfdIndex );
        }
    }
    else
    {
        PrintMessage("Could not get pixel format for Dc 0x%08lX\n", Dc );
    }
    return( 0 );
}

static void
DestroyThisWindow( HWND Window )
{
    if ( NULL != Window )
    {
        DestroyWindow( Window );
    }
}

/*
 *  This Should be called in response to a WM_DESTROY message
 */

static void
CleanUp( void )
{
    HPALETTE hStock;

// Cleanup the palette.

    if ( NULL != tkhpalette )
    {
    // If static system color usage is set, restore the system colors.

        if ( tkUseStaticColors )
        {
            RealizePaletteNow( tkhdc, GetStockObject(DEFAULT_PALETTE), TRUE );
        }
        else
        {
            if ( hStock = GetStockObject( DEFAULT_PALETTE ) )
                SelectPalette( tkhdc, hStock, FALSE );
        }

        DeleteObject( tkhpalette );
    }

// Cleanup the RC.

    if ( NULL != tkhrc )
    {
        wglMakeCurrent( tkhdc, NULL );  // Release first...
        wglDeleteContext( tkhrc );      // then delete.
    }

// Cleanup the DC.

    if ( NULL != tkhdc )
    {
        ReleaseDC( tkhwnd, tkhdc );
    }

// Be really nice and reset global values.

    tkhwnd        = NULL;
    tkhdc         = NULL;
    tkhrc         = NULL;
    tkhpalette    = NULL;

    ExposeFunc    = NULL;
    ReshapeFunc   = NULL;
    IdleFunc      = NULL;
    DisplayFunc   = NULL;
    KeyDownFunc   = NULL;
    MouseDownFunc = NULL;
    MouseUpFunc   = NULL;
    MouseMoveFunc = NULL;
}

static void *
AllocateMemory( size_t Size )
{
    return( LocalAlloc( LMEM_FIXED, Size ) );
}

static void *
AllocateZeroedMemory( size_t Size )
{
    return( LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Size ) );
}

static void
FreeMemory( void *Chunk )
{
    TKASSERT( NULL!=Chunk );

    LocalFree( Chunk );
}

/*******************************************************************
 *                                                                 *
 *  Debugging functions go here                                    *
 *                                                                 *
 *******************************************************************/

#if DBGFUNC

static void
DbgPrintf( const char *Format, ... )
{
    va_list ArgList;
    char Buffer[256];

    va_start(ArgList, Format);
    vsprintf(Buffer, Format, ArgList);
    va_end(ArgList);

    printf("%s", Buffer );
    fflush(stdout);
}

static void
pwi( void )
{
    DbgPrintf("windInfo: x %d, y %d, w %d, h %d\n", windInfo.x, windInfo.y, windInfo.width, windInfo.height);
}

static void
pwr(RECT *pr)
{
    DbgPrintf("Rect: left %d, top %d, right %d, bottom %d\n", pr->left, pr->top, pr->right, pr->bottom);
}

static void
ShowPixelFormat(HDC hdc)
{
    PIXELFORMATDESCRIPTOR pfd, *ppfd;
    int format;

    ppfd   = &pfd;
    format = PixelFormatDescriptorFromDc( hdc, ppfd );

    DbgPrintf("Pixel format %d\n", format);
    DbgPrintf("  dwFlags - 0x%x", ppfd->dwFlags);
        if (ppfd->dwFlags & PFD_DOUBLEBUFFER) DbgPrintf("PFD_DOUBLEBUFFER ");
        if (ppfd->dwFlags & PFD_STEREO) DbgPrintf("PFD_STEREO ");
        if (ppfd->dwFlags & PFD_DRAW_TO_WINDOW) DbgPrintf("PFD_DRAW_TO_WINDOW ");
        if (ppfd->dwFlags & PFD_DRAW_TO_BITMAP) DbgPrintf("PFD_DRAW_TO_BITMAP ");
        if (ppfd->dwFlags & PFD_SUPPORT_GDI) DbgPrintf("PFD_SUPPORT_GDI ");
        if (ppfd->dwFlags & PFD_SUPPORT_OPENGL) DbgPrintf("PFD_SUPPORT_OPENGL ");
        if (ppfd->dwFlags & PFD_GENERIC_FORMAT) DbgPrintf("PFD_GENERIC_FORMAT ");
        if (ppfd->dwFlags & PFD_NEED_PALETTE) DbgPrintf("PFD_NEED_PALETTE ");
        if (ppfd->dwFlags & PFD_NEED_SYSTEM_PALETTE) DbgPrintf("PFD_NEED_SYSTEM_PALETTE ");
        DbgPrintf("\n");
    DbgPrintf("  iPixelType - %d", ppfd->iPixelType);
        if (ppfd->iPixelType == PFD_TYPE_RGBA) DbgPrintf("PGD_TYPE_RGBA\n");
        if (ppfd->iPixelType == PFD_TYPE_COLORINDEX) DbgPrintf("PGD_TYPE_COLORINDEX\n");
    DbgPrintf("  cColorBits - %d\n", ppfd->cColorBits);
    DbgPrintf("  cRedBits - %d\n", ppfd->cRedBits);
    DbgPrintf("  cRedShift - %d\n", ppfd->cRedShift);
    DbgPrintf("  cGreenBits - %d\n", ppfd->cGreenBits);
    DbgPrintf("  cGreenShift - %d\n", ppfd->cGreenShift);
    DbgPrintf("  cBlueBits - %d\n", ppfd->cBlueBits);
    DbgPrintf("  cBlueShift - %d\n", ppfd->cBlueShift);
    DbgPrintf("  cAlphaBits - %d\n", ppfd->cAlphaBits);
    DbgPrintf("  cAlphaShift - 0x%x\n", ppfd->cAlphaShift);
    DbgPrintf("  cAccumBits - %d\n", ppfd->cAccumBits);
    DbgPrintf("  cAccumRedBits - %d\n", ppfd->cAccumRedBits);
    DbgPrintf("  cAccumGreenBits - %d\n", ppfd->cAccumGreenBits);
    DbgPrintf("  cAccumBlueBits - %d\n", ppfd->cAccumBlueBits);
    DbgPrintf("  cAccumAlphaBits - %d\n", ppfd->cAccumAlphaBits);
    DbgPrintf("  cDepthBits - %d\n", ppfd->cDepthBits);
    DbgPrintf("  cStencilBits - %d\n", ppfd->cStencilBits);
    DbgPrintf("  cAuxBuffers - %d\n", ppfd->cAuxBuffers);
    DbgPrintf("  iLayerType - %d\n", ppfd->iLayerType);
    DbgPrintf("  bReserved - %d\n", ppfd->bReserved);
    DbgPrintf("  dwLayerMask - 0x%x\n", ppfd->dwLayerMask);
    DbgPrintf("  dwVisibleMask - 0x%x\n", ppfd->dwVisibleMask);
    DbgPrintf("  dwDamageMask - 0x%x\n", ppfd->dwDamageMask);

}

#endif  /* DBG */


TK.H

/*
 * 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 <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>

/*
** Window Types
*/

#define TK_RGB0
#define TK_INDEX1
#define TK_SINGLE0
#define TK_DOUBLE2
#define TK_DIRECT0
#define TK_INDIRECT4
#define TK_ACCUM8
#define TK_ALPHA16
#define TK_DEPTH        32      /* 32-bit depth buffer */
#define TK_STENCIL      64
#define TK_DEPTH16      256     /* 16-bit depth buffer */
#define TK_FIXED_332_PAL    512

/*
** Display Mode Selection Criteria
*/

enum {
    TK_USE_ID = 1,
    TK_EXACT_MATCH,
    TK_MINIMUM_CRITERIA
};

/*
** Window Masks
*/

#define TK_IS_RGB(x)(((x) & TK_INDEX) == 0)
#define TK_IS_INDEX(x)(((x) & TK_INDEX) != 0)
#define TK_IS_SINGLE(x)(((x) & TK_DOUBLE) == 0)
#define TK_IS_DOUBLE(x)(((x) & TK_DOUBLE) != 0)
#define TK_HAS_ACCUM(x)(((x) & TK_ACCUM) != 0)
#define TK_HAS_ALPHA(x)(((x) & TK_ALPHA) != 0)
#define TK_HAS_DEPTH(x)         (((x) & (TK_DEPTH | TK_DEPTH16)) != 0)
#define TK_HAS_STENCIL(x)(((x) & TK_STENCIL) != 0)
#define TK_IS_DIRECT(x)(((x) & TK_INDIRECT) == 0)
#define TK_IS_INDIRECT(x)       (((x) & TK_INDIRECT) != 0)
#define TK_USE_FIXED_332_PAL(x) (((x) & TK_FIXED_332_PAL) != 0)
#define TK_IS_DEPTH16(x)        (((x) & TK_DEPTH16) != 0)

/*
** Event Status
*/

#defineTK_LEFTBUTTON1
#defineTK_RIGHTBUTTON2
#defineTK_MIDDLEBUTTON4
#defineTK_SHIFT1
#defineTK_CONTROL2

/*
** Key Codes
*/

#define TK_RETURN0x0D
#define TK_ESCAPE0x1B
#define TK_SPACE0x20
#define TK_LEFT0x25
#define TK_UP0x26
#define TK_RIGHT0x27
#define TK_DOWN0x28
#define TK_A'A'
#define TK_B'B'
#define TK_C'C'
#define TK_D'D'
#define TK_E'E'
#define TK_F'F'
#define TK_G'G'
#define TK_H'H'
#define TK_I'I'
#define TK_J'J'
#define TK_K'K'
#define TK_L'L'
#define TK_M'M'
#define TK_N'N'
#define TK_O'O'
#define TK_P'P'
#define TK_Q'Q'
#define TK_R'R'
#define TK_S'S'
#define TK_T'T'
#define TK_U'U'
#define TK_V'V'
#define TK_W'W'
#define TK_X'X'
#define TK_Y'Y'
#define TK_Z'Z'
#define TK_a'a'
#define TK_b'b'
#define TK_c'c'
#define TK_d'd'
#define TK_e'e'
#define TK_f'f'
#define TK_g'g'
#define TK_h'h'
#define TK_i'i'
#define TK_j'j'
#define TK_k'k'
#define TK_l'l'
#define TK_m'm'
#define TK_n'n'
#define TK_o'o'
#define TK_p'p'
#define TK_q'q'
#define TK_r'r'
#define TK_s's'
#define TK_t't'
#define TK_u'u'
#define TK_v'v'
#define TK_w'w'
#define TK_x'x'
#define TK_y'y'
#define TK_z'z'
#define TK_0'0'
#define TK_1'1'
#define TK_2'2'
#define TK_3'3'
#define TK_4'4'
#define TK_5'5'
#define TK_6'6'
#define TK_7'7'
#define TK_8'8'
#define TK_9'9'

/*
** Color Macros - Default Windows Logical palette
*/

enum {
    TK_BLACK = 0,
    TK_RED = 13,
    TK_GREEN,
    TK_YELLOW,
    TK_BLUE,
    TK_MAGENTA,
    TK_CYAN,
    TK_WHITE
};

extern float auxRGBMap[20][3];
#define tkRGBMap auxRGBMap

#define TK_SETCOLOR(x, y) (TK_IS_RGB((x)) ? \
           glColor3fv(auxRGBMap[(y)]) : glIndexf((y)))

/*
** RGB Image Structure
*/

typedef struct _TK_RGBImageRec {
    GLint sizeX, sizeY;
    unsigned char *data;
} TK_RGBImageRec;

/*
** Prototypes
*/

extern void tkInitDisplayMode(GLenum);
extern void tkInitPosition(int, int, int, int);
extern void tkInitDisplayModePolicy(GLenum);
extern GLenum tkInitDisplayModeID(GLint);
extern GLenum tkInitWindow(char *);
extern GLenum tkInitWindowAW(char *, BOOL);
extern void tkCloseWindow(void);
extern void tkQuit(void);

extern void tkExec(void);
extern void tkExposeFunc(void (*)(int, int));
extern void tkReshapeFunc(void (*)(GLsizei, GLsizei));
extern void tkDisplayFunc(void (*)(void));
extern void tkKeyDownFunc(GLenum (*)(int, GLenum));
extern void tkMouseDownFunc(GLenum (*)(int, int, GLenum));
extern void tkMouseUpFunc(GLenum (*)(int, int, GLenum));
extern void tkMouseMoveFunc(GLenum (*)(int, int, GLenum));
extern void tkIdleFunc(void (*)(void));

extern void tkSwapBuffers(void);

extern GLint tkGetColorMapSize(void);
extern void tkGetMouseLoc(int *, int *);
extern GLint tkGetDisplayModeID(void);
extern GLenum tkGetDisplayModePolicy(void);
extern GLenum tkGetDisplayMode(void);

extern void tkSetOneColor(int, float, float, float);
extern void tkSetFogRamp(int, int);
extern void tkSetGreyRamp(void);
extern void tkSetRGBMap(int, float *);

extern TK_RGBImageRec *tkRGBImageLoad(char *);
extern TK_RGBImageRec *tkRGBImageLoadAW(char *, BOOL);

extern TK_RGBImageRec *tkDIBImageLoad(char *);
extern TK_RGBImageRec *tkDIBImageLoadAW(char *, BOOL);

extern GLenum tkCreateStrokeFont(GLuint);
extern GLenum tkCreateOutlineFont(GLuint);
extern GLenum tkCreateFilledFont(GLuint);
extern GLenum tkCreateBitmapFont(GLuint);
extern void tkDrawStr(GLuint, char *);

/* Windows specific routines */
extern HWND  tkGetHWND(void);
extern HDC   tkGetHDC(void);
extern HGLRC tkGetHRC(void);

/* Error popup control */
extern void tkErrorPopups(GLboolean);
extern GLboolean tkPopupEnable;
#define MESSAGEBOX(a, b, c, d)  if (tkPopupEnable) MessageBoxA((a), (b), (c), (d))


TKDIB.C

/******************************Module*Header*******************************\
* Module Name: dib.c
*
* (Brief description)
*
* Created: 21-Feb-1994 23:12:58
* Author: Gilman Wong [gilmanw]
*
* Copyright (c) 1994 Microsoft Corporation
*
* (General description of its use)
*
* Dependencies:
*
*   (#defines)
*   (#includes)
*
\**************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "tk.h"

#define static

#define BFT_BITMAP  0x4d42  // 'BM' -- indicates structure is BITMAPFILEHEADER

// struct BITMAPFILEHEADER {
//      WORD  bfType
//      DWORD bfSize
//      WORD  bfReserved1
//      WORD  bfReserved2
//      DWORD bfOffBits
// }
#define OFFSET_bfType       0
#define OFFSET_bfSize       2
#define OFFSET_bfReserved1  6
#define OFFSET_bfReserved2  8
#define OFFSET_bfOffBits    10
#define SIZEOF_BITMAPFILEHEADER 14

// Read a WORD-aligned DWORD.  Needed because BITMAPFILEHEADER has
// WORD-alignment.
#define READDWORD(pv)   ( (DWORD)((PWORD)(pv))[0]               \
                          | ((DWORD)((PWORD)(pv))[1] << 16) )   \

// Computes the number of BYTES needed to contain n number of bits.
#define BITS2BYTES(n)   ( ((n) + 7) >> 3 )

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : DibNumColors(VOID FAR * pv)                                *
 *                                                                          *
 *  PURPOSE    : Determines the number of colors in the DIB by looking at   *
 *               the BitCount filed in the info block.                      *
 *                                                                          *
 *  RETURNS    : The number of colors in the DIB.                           *
 *                                                                          *
 * Stolen from SDK ShowDIB example.                                         *
 ****************************************************************************/

WORD DibNumColors(VOID FAR * pv)
{
    WORD                bits;
    BITMAPINFOHEADER UNALIGNED *lpbi;
    BITMAPCOREHEADER UNALIGNED *lpbc;

    lpbi = ((LPBITMAPINFOHEADER)pv);
    lpbc = ((LPBITMAPCOREHEADER)pv);

    /*  With the BITMAPINFO format headers, the size of the palette
     *  is in biClrUsed, whereas in the BITMAPCORE - style headers, it
     *  is dependent on the bits per pixel ( = 2 raised to the power of
     *  bits/pixel).
     *
     *  Because of the way we use this call, BITMAPINFOHEADER may be out
     *  of alignment if it follows a BITMAPFILEHEADER.  So use the macro
     *  to safely access DWORD fields.
     */
    if (READDWORD(&lpbi->biSize) != sizeof(BITMAPCOREHEADER)){
        if (READDWORD(&lpbi->biClrUsed) != 0)
        {
            return (WORD) READDWORD(&lpbi->biClrUsed);
        }
        bits = lpbi->biBitCount;
    }
    else
        bits = lpbc->bcBitCount;

    switch (bits){
        case 1:
            return 2;
        case 4:
            return 16;
        case 8:
            return 256;
        default:
            /* A 24 bitcount DIB has no color table */
            return 0;
    }
}

/******************************Public*Routine******************************\
* tkDIBImageLoad
*
* ANSI version stub.  Only here for orthogonality with tkRGBImageLoad.
*
* History:
*  22-Feb-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

TK_RGBImageRec *tkDIBImageLoadAW(char *fileName, BOOL bUnicode);

TK_RGBImageRec *tkDIBImageLoad(char *fileName)
{
    return tkDIBImageLoadAW(fileName, FALSE);
}

/******************************Public*Routine******************************\
* tkDIBImageLoadAW
*
* Loads a DIB file (specified as either an ANSI or Unicode filename,
* depending on the bUnicode flag) and converts it into a TK image format.
*
* The technique used is based on CreateDIBSection and SetDIBits.
* CreateDIBSection is used to create a DIB with a format easily converted
* into the TK image format (packed 24BPP RGB).  The only conversion
* required is swapping R and B in each RGB triplet (see history below)
* The resulting bitmap is selected into a memory DC.
*
* The DIB file is mapped into memory and SetDIBits called to initialize
* the memory DC bitmap.  It is during this step that GDI converts the
* arbitrary DIB file format to RGB format.
*
* Finally, the RGB data in the DIB section is read out and repacked
* as 24BPP 'BGR'.
*
* Returns:
*   Pointer to TK_RGBImageRec.  If an error occurs, a diagnostic error
*   message is put into the error stream and tkQuit() is called,
*   terminating the app.
*
* History:
*  - 22-Feb-1994 -by- Gilman Wong [gilmanw]
*    Wrote it.
*
*  - 01-May-1995 : [marcfo]
*    Don't quit if can't open DIB file - return NULL.
*
*  - 27-Jul-1995 : [marcfo]
*    Changed CreateDIBSection to create an RGB mapping, so this would work
*    on win95.  Swapping of R and B required, to generate BGR (R=low byte)
*    for GL.  If the GL 'BGR' (R=high byte) extension becomes part of api
*    in OpenGL 2.0, then we can avoid swap and do a memcpy to grab the bits.
*
\**************************************************************************/

TK_RGBImageRec *tkDIBImageLoadAW(char *fileName, BOOL bUnicode)
{
    TK_RGBImageRec *final = (TK_RGBImageRec *) NULL; // Ptr to TK image struct
                                                     // to return.  Non-NULL
                                                     // only for success.

    WORD             wNumColors;    // Number of colors in color table
    BITMAPFILEHEADER *pbmf;         // Ptr to file header
    BITMAPINFOHEADER UNALIGNED *pbmihFile;
    BITMAPCOREHEADER UNALIGNED *pbmchFile; // Ptr to file's core header (if it exists)
    PVOID            pvBitsFile;    // Ptr to bitmap bits in file
    PBYTE            pjBitsRGB;     // Ptr to 24BPP RGB image in DIB section
    PBYTE            pjTKBits = (PBYTE) NULL;   // Ptr to final TK image bits
    PBYTE            pjSrc;         // Ptr to image file used for conversion
    PBYTE            pjDst;         // Ptr to TK image used for conversion

    // These need to be cleaned up when we exit:
    HANDLE     hFile = INVALID_HANDLE_VALUE;        // File handle
    HANDLE     hMap = (HANDLE) NULL;                // Mapping object handle
    PVOID      pvFile = (PVOID) NULL;               // Ptr to mapped file
    HDC        hdcMem = (HDC) NULL;                 // 24BPP mem DC
    HBITMAP    hbmRGB = (HBITMAP) NULL;             // 24BPP RGB bitmap
    BITMAPINFO *pbmiSource = (BITMAPINFO *) NULL;   // Ptr to source BITMAPINFO
    BITMAPINFO *pbmiRGB = (BITMAPINFO *) NULL;      // Ptr to file's BITMAPINFO

    int i, j;
    int padBytes;

// Map the DIB file into memory.

    hFile = bUnicode ?
            CreateFileW((LPWSTR) fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
                        OPEN_EXISTING, 0, 0) :
            CreateFileA((LPSTR) fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
                        OPEN_EXISTING, 0, 0);

    if (hFile == INVALID_HANDLE_VALUE)
        goto tkDIBLoadImage_cleanup;

    hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    if (!hMap)
        goto tkDIBLoadImage_cleanup;

    pvFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
    if (!pvFile)
        goto tkDIBLoadImage_cleanup;

// Check the file header.  If the BFT_BITMAP magic number is there,
// then the file format is a BITMAPFILEHEADER followed immediately
// by either a BITMAPINFOHEADER or a BITMAPCOREHEADER.  The bitmap
// bits, in this case, are located at the offset bfOffBits from the
// BITMAPFILEHEADER.
//
// Otherwise, this may be a raw BITMAPINFOHEADER or BITMAPCOREHEADER
// followed immediately with the color table and the bitmap bits.

    pbmf = (BITMAPFILEHEADER *) pvFile;

    if ( pbmf->bfType == BFT_BITMAP )
    {
        pbmihFile = (BITMAPINFOHEADER *) ((PBYTE) pbmf + SIZEOF_BITMAPFILEHEADER);

    // BITMAPFILEHEADER is WORD aligned, so use safe macro to read DWORD
    // bfOffBits field.

        pvBitsFile = (PVOID *) ((PBYTE) pbmf
                                + READDWORD((PBYTE) pbmf + OFFSET_bfOffBits));
    }
    else
    {
        pbmihFile = (BITMAPINFOHEADER *) pvFile;

    // Determination of where the bitmaps bits are needs to wait until we
    // know for sure whether we have a BITMAPINFOHEADER or a BITMAPCOREHEADER.
    }

// Determine the number of colors in the DIB palette.  This is non-zero
// only for 8BPP or less.

    wNumColors = DibNumColors(pbmihFile);

// Create a BITMAPINFO (with color table) for the DIB file.  Because the
// file may not have one (BITMAPCORE case) and potential alignment problems,
// we will create a new one in memory we allocate.
//
// We distinguish between BITMAPINFO and BITMAPCORE cases based upon
// BITMAPINFOHEADER.biSize.

    pbmiSource = (BITMAPINFO *)
        LocalAlloc(LMEM_FIXED, sizeof(BITMAPINFO)
                               + wNumColors * sizeof(RGBQUAD));
    if (!pbmiSource)
    {
        MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
        goto tkDIBLoadImage_cleanup;
    }

    // Note: need to use safe READDWORD macro because pbmihFile may
    // have only WORD alignment if it follows a BITMAPFILEHEADER.

    switch (READDWORD(&pbmihFile->biSize))
    {
    case sizeof(BITMAPINFOHEADER):

    // Convert WORD-aligned BITMAPINFOHEADER to aligned BITMAPINFO.

        pbmiSource->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
        pbmiSource->bmiHeader.biWidth         = READDWORD(&pbmihFile->biWidth);
        pbmiSource->bmiHeader.biHeight        = READDWORD(&pbmihFile->biHeight);
        pbmiSource->bmiHeader.biPlanes        = pbmihFile->biPlanes;
        pbmiSource->bmiHeader.biBitCount      = pbmihFile->biBitCount;
        pbmiSource->bmiHeader.biCompression   =
                                        READDWORD(&pbmihFile->biCompression);
        pbmiSource->bmiHeader.biSizeImage     =
                                        READDWORD(&pbmihFile->biSizeImage);
        pbmiSource->bmiHeader.biXPelsPerMeter =
                                        READDWORD(&pbmihFile->biXPelsPerMeter);
        pbmiSource->bmiHeader.biYPelsPerMeter =
                                        READDWORD(&pbmihFile->biYPelsPerMeter);
        pbmiSource->bmiHeader.biClrUsed       =
                                        READDWORD(&pbmihFile->biClrUsed);
        pbmiSource->bmiHeader.biClrImportant  =
                                        READDWORD(&pbmihFile->biClrImportant);

    // Copy color table.  It immediately follows the BITMAPINFOHEADER.

        memcpy((PVOID) &pbmiSource->bmiColors[0], (PVOID) (pbmihFile + 1),
               wNumColors * sizeof(RGBQUAD));

    // If we haven't already determined the position of the image bits,
    // we may now assume that they immediately follow the color table.

        if (!pvBitsFile)
            pvBitsFile = (PVOID) ((PBYTE) (pbmihFile + 1)
                         + wNumColors * sizeof(RGBQUAD));
        break;

    case sizeof(BITMAPCOREHEADER):
        pbmchFile = (BITMAPCOREHEADER *) pbmihFile;

    // Convert BITMAPCOREHEADER to BITMAPINFOHEADER.

        pbmiSource->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
        pbmiSource->bmiHeader.biWidth         = (DWORD) pbmchFile->bcWidth;
        pbmiSource->bmiHeader.biHeight        = (DWORD) pbmchFile->bcHeight;
        pbmiSource->bmiHeader.biPlanes        = pbmchFile->bcPlanes;
        pbmiSource->bmiHeader.biBitCount      = pbmchFile->bcBitCount;
        pbmiSource->bmiHeader.biCompression   = BI_RGB;
        pbmiSource->bmiHeader.biSizeImage     = 0;
        pbmiSource->bmiHeader.biXPelsPerMeter = 0;
        pbmiSource->bmiHeader.biYPelsPerMeter = 0;
        pbmiSource->bmiHeader.biClrUsed       = wNumColors;
        pbmiSource->bmiHeader.biClrImportant  = wNumColors;

    // Convert RGBTRIPLE color table into RGBQUAD color table.

        {
            RGBQUAD *rgb4 = pbmiSource->bmiColors;
            RGBTRIPLE *rgb3 = (RGBTRIPLE *) (pbmchFile + 1);

            for (i = 0; i < wNumColors; i++)
            {
                rgb4->rgbRed   = rgb3->rgbtRed  ;
                rgb4->rgbGreen = rgb3->rgbtGreen;
                rgb4->rgbBlue  = rgb3->rgbtBlue ;
                rgb4->rgbReserved = 0;

                rgb4++;
                rgb3++;
            }
        }

    // If we haven't already determined the position of the image bits,
    // we may now assume that they immediately follow the color table.

        if (!pvBitsFile)
            pvBitsFile = (PVOID) ((PBYTE) (pbmihFile + 1)
                         + wNumColors * sizeof(RGBTRIPLE));
        break;

    default:
        MESSAGEBOX(GetFocus(), "Unknown DIB file format.", "Error", MB_OK);
        goto tkDIBLoadImage_cleanup;
    }

// Fill in default values (for fields that can have defaults).

    if (pbmiSource->bmiHeader.biSizeImage == 0)
        pbmiSource->bmiHeader.biSizeImage =
            BITS2BYTES( (DWORD) pbmiSource->bmiHeader.biWidth *
                                pbmiSource->bmiHeader.biBitCount ) *
                                pbmiSource->bmiHeader.biHeight;
    if (pbmiSource->bmiHeader.biClrUsed == 0)
        pbmiSource->bmiHeader.biClrUsed = wNumColors;

// Create memory DC.

    hdcMem = CreateCompatibleDC(NULL);
    if (!hdcMem) {
        MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
        goto tkDIBLoadImage_cleanup;
    }

// Create a 24BPP RGB DIB section and select it into the memory DC.

    pbmiRGB = (BITMAPINFO *)
              LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(BITMAPINFO) );
    if (!pbmiRGB)
    {
        MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
        goto tkDIBLoadImage_cleanup;
    }

    pbmiRGB->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
    pbmiRGB->bmiHeader.biWidth         = pbmiSource->bmiHeader.biWidth;
    pbmiRGB->bmiHeader.biHeight        = pbmiSource->bmiHeader.biHeight;
    pbmiRGB->bmiHeader.biPlanes        = 1;
    pbmiRGB->bmiHeader.biBitCount      = 24;
    pbmiRGB->bmiHeader.biCompression   = BI_RGB;
    pbmiRGB->bmiHeader.biSizeImage     = pbmiRGB->bmiHeader.biWidth
                                         * abs(pbmiRGB->bmiHeader.biHeight) * 3;

    hbmRGB = CreateDIBSection(hdcMem, pbmiRGB, DIB_RGB_COLORS,
                              (PVOID *) &pjBitsRGB, NULL, 0);

    if (!hbmRGB)
    {
        MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
        goto tkDIBLoadImage_cleanup;
    }

    if (!SelectObject(hdcMem, hbmRGB))
    {
        MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
        goto tkDIBLoadImage_cleanup;
    }

// Slam the DIB file image into the memory DC.  GDI will do the work of
// translating whatever format the DIB file has into RGB format.

    if (!SetDIBits(hdcMem, hbmRGB, 0, pbmiSource->bmiHeader.biHeight,
                   pvBitsFile, pbmiSource, DIB_RGB_COLORS))
    {
        MESSAGEBOX(GetFocus(), "Image file conversion error.", "Error", MB_OK);
        goto tkDIBLoadImage_cleanup;
    }
    GdiFlush();     // make sure that SetDIBits executes

// Convert to TK image format (packed RGB format).
// Allocate with malloc to be consistent with tkRGBImageLoad (i.e., app
// can deallocate with free()).

    pjTKBits = (PBYTE) malloc(pbmiRGB->bmiHeader.biSizeImage);
    if (!pjTKBits)
    {
        MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
        goto tkDIBLoadImage_cleanup;
    }

    pjSrc = pjBitsRGB;
    pjDst = pjTKBits;
    // src lines end on LONG boundary - so need to skip over any padding bytes
    padBytes = pbmiSource->bmiHeader.biWidth % sizeof(LONG);
    for (i = 0; i < pbmiSource->bmiHeader.biHeight; i++)
    {
        for (j = 0; j < pbmiSource->bmiHeader.biWidth; j++)
        {
            // swap R and B
            *pjDst++ = pjSrc[2];
            *pjDst++ = pjSrc[1];
            *pjDst++ = pjSrc[0];
            pjSrc += 3;
        }
        pjSrc += padBytes;
    }

// Allocate and initialize the TK_RGBImageRec.
// Allocate with malloc to be consistent with tkRGBImageLoad (i.e., app
// can deallocate with free()).

    final = (TK_RGBImageRec *)malloc(sizeof(TK_RGBImageRec));
    if (final == NULL) {
        MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
        goto tkDIBLoadImage_cleanup;
    }
    // If we get to here, we have suceeded!
    final->sizeX = pbmiSource->bmiHeader.biWidth;
    final->sizeY = pbmiSource->bmiHeader.biHeight;
    final->data = pjTKBits;

// Cleanup objects.

tkDIBLoadImage_cleanup:
    {
        if (hdcMem)
            DeleteDC(hdcMem);

        if (hbmRGB)
            DeleteObject(hbmRGB);

        if (pbmiRGB)
            LocalFree(pbmiRGB);

        if (pbmiSource)
            LocalFree(pbmiSource);

        if (pvFile)
            UnmapViewOfFile(pvFile);

        if (hMap)
            CloseHandle(hMap);

        if (hFile != INVALID_HANDLE_VALUE)
            CloseHandle(hFile);
    }

// Check for error.

    if (!final)
    {
        if (pjTKBits)
            free(pjTKBits);

        if ( (hFile == INVALID_HANDLE_VALUE) || !hMap || !pvFile )
        {
            CHAR ach[256];

            bUnicode ? wsprintf(ach, "Failed to open DIB file %ws.\n", fileName) :
                       wsprintf(ach, "Failed to open DIB file %s.\n", fileName);

            MESSAGEBOX(GetFocus(), ach, "Error", MB_OK);
        }
    }

    return final;
}


TKFONT.C

/*
 * 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 "tk.h"

#define static

enum {
    FONT_BEGIN = 1,
    FONT_NEXT,
    FONT_END,
    FONT_ADVANCE
};
#define MAX_STROKES        256
#define END_OF_LIST        256
#define BITMAP_END_OF_LIST 0

#define STROKE_SCALE ((GLfloat) 1.71)
#define OUTLINE_SCALE ((GLfloat) 0.023)
#define FILLED_SCALE ((GLfloat) 0.023)

GLint strokeFont[][1+MAX_STROKES*3] = {
    {
1,
FONT_BEGIN, 0, 4,
FONT_NEXT, 2, 2,
FONT_END, 4, 4,
FONT_BEGIN, 2, 8,
FONT_END, 2, 2,
FONT_ADVANCE, 6, 0
    },
    {
2,
FONT_BEGIN, 0, 3,
FONT_NEXT, 0, 5,
FONT_NEXT, 1, 6,
FONT_NEXT, 2, 6,
FONT_NEXT, 3, 5,
FONT_END, 4, 6,
FONT_BEGIN, 3, 5,
FONT_NEXT, 3, 3,
FONT_END, 4, 2,
FONT_BEGIN, 3, 3,
FONT_NEXT, 2, 2,
FONT_NEXT, 1, 2,
FONT_END, 0, 3,
FONT_ADVANCE, 6, 0
    },
    {
3,
FONT_BEGIN, 0, 0,
FONT_NEXT, 1, 1,
FONT_NEXT, 1, 5,
FONT_NEXT, 2, 6,
FONT_NEXT, 4, 6,
FONT_NEXT, 5, 5,
FONT_NEXT, 4, 4,
FONT_END, 1, 4,
FONT_BEGIN, 4, 4,
FONT_NEXT, 5, 3,
FONT_NEXT, 4, 2,
FONT_END, 1, 2,
FONT_ADVANCE, 7, 0
    },
    {
4,
FONT_BEGIN, 0, 4,
FONT_NEXT, 2, 6,
FONT_END, 4, 4,
FONT_ADVANCE, 6, 0
    },
    {
5,
FONT_BEGIN, 0, 5,
FONT_NEXT, 4, 5,
FONT_END, 4, 4,
FONT_ADVANCE, 6, 0
    },
    {
6,
FONT_BEGIN, 1, 4,
FONT_END, 3, 4,
FONT_BEGIN, 3, 6,
FONT_NEXT, 2, 6,
FONT_NEXT, 1, 5,
FONT_NEXT, 1, 3,
FONT_NEXT, 2, 2,
FONT_END, 3, 2,
FONT_ADVANCE, 6, 0
    },
    {
7,
FONT_BEGIN, 1, 2,
FONT_END, 1, 6,
FONT_BEGIN, 0, 6,
FONT_END, 4, 6,
FONT_BEGIN, 3, 6,
FONT_END, 3, 2,
FONT_ADVANCE, 6, 0
    },
    {
8,
FONT_BEGIN, 0, 2,
FONT_END, 2, 4,
FONT_BEGIN, 0, 7,
FONT_NEXT, 0, 6,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
10,
FONT_ADVANCE, 0, -9
    },
    {
11,
FONT_BEGIN, 0, 2,
FONT_NEXT, 1, 1,
FONT_NEXT, 2, 2,
FONT_NEXT, 2, 7,
FONT_NEXT, 3, 8,
FONT_END, 4, 7,
FONT_ADVANCE, 6, 0
    },
    {
12,
FONT_BEGIN, 0, 3,
FONT_END, 4, 3,
FONT_BEGIN, 4, 6,
FONT_END, 0, 6,
FONT_BEGIN, 2, 8,
FONT_END, 2, 4,
FONT_ADVANCE, 6, 0
    },
    {
14,
FONT_BEGIN, 1, 3,
FONT_NEXT, 2, 4,
FONT_NEXT, 2, 5,
FONT_NEXT, 3, 6,
FONT_NEXT, 4, 5,
FONT_NEXT, 4, 4,
FONT_NEXT, 3, 3,
FONT_END, 2, 4,
FONT_BEGIN, 2, 5,
FONT_NEXT, 1, 6,
FONT_NEXT, 0, 5,
FONT_NEXT, 0, 4,
FONT_END, 1, 3,
FONT_ADVANCE, 6, 0
    },
    {
15,
FONT_BEGIN, 0, 3,
FONT_NEXT, 0, 4,
FONT_NEXT, 1, 5,
FONT_NEXT, 3, 5,
FONT_NEXT, 4, 4,
FONT_NEXT, 4, 6,
FONT_NEXT, 2, 8,
FONT_END, 1, 8,
FONT_BEGIN, 4, 4,
FONT_NEXT, 4, 3,
FONT_NEXT, 3, 2,
FONT_NEXT, 1, 2,
FONT_END, 0, 3,
FONT_ADVANCE, 6, 0
    },
    {
16,
FONT_BEGIN, 4, 7,
FONT_NEXT, 1, 7,
FONT_NEXT, 0, 6,
FONT_NEXT, 0, 4,
FONT_NEXT, 1, 3,
FONT_END, 4, 3,
FONT_ADVANCE, 6, 0
    },
    {
17,
FONT_BEGIN, 0, 3,
FONT_NEXT, 3, 3,
FONT_NEXT, 4, 4,
FONT_NEXT, 4, 6,
FONT_NEXT, 3, 7,
FONT_END, 0, 7,
FONT_ADVANCE, 6, 0
    },
    {
18,
FONT_BEGIN, 0, 4,
FONT_NEXT, 0, 6,
FONT_NEXT, 1, 7,
FONT_NEXT, 3, 7,
FONT_NEXT, 4, 6,
FONT_END, 4, 4,
FONT_ADVANCE, 6, 0
    },
    {
19,
FONT_BEGIN, 0, 7,
FONT_NEXT, 0, 5,
FONT_NEXT, 1, 4,
FONT_NEXT, 3, 4,
FONT_NEXT, 4, 5,
FONT_END, 4, 7,
FONT_ADVANCE, 6, 0
    },
    {
20,
FONT_BEGIN, 0, 8,
FONT_NEXT, 2, 2,
FONT_END, 4, 8,
FONT_BEGIN, 3, 6,
FONT_END, 1, 6,
FONT_ADVANCE, 6, 0
    },
    {
21,
FONT_BEGIN, 0, 2,
FONT_NEXT, 4, 2,
FONT_NEXT, 4, 5,
FONT_END, 1, 5,
FONT_BEGIN, 0, 8,
FONT_NEXT, 4, 8,
FONT_END, 4, 5,
FONT_ADVANCE, 6, 0
    },
    {
22,
FONT_BEGIN, 0, 4,
FONT_NEXT, 0, 6,
FONT_NEXT, 1, 7,
FONT_NEXT, 3, 7,
FONT_NEXT, 4, 6,
FONT_NEXT, 4, 4,
FONT_NEXT, 3, 3,
FONT_NEXT, 1, 3,
FONT_END, 0, 4,
FONT_BEGIN, 1, 4,
FONT_END, 3, 6,
FONT_BEGIN, 1, 6,
FONT_END, 3, 4,
FONT_ADVANCE, 6, 0
    },
    {
23,
FONT_BEGIN, 2, 0,
FONT_NEXT, 0, 2,
FONT_NEXT, 4, 6,
FONT_END, 2, 8,
FONT_BEGIN, 0, 6,
FONT_END, 4, 6,
FONT_BEGIN, 0, 2,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
24,
FONT_BEGIN, 0, 0,
FONT_END, 4, 0,
FONT_ADVANCE, 6, 0
    },
    {
25,
FONT_BEGIN, 2, 3,
FONT_NEXT, 4, 5,
FONT_END, 2, 7,
FONT_BEGIN, 0, 5,
FONT_END, 4, 5,
FONT_ADVANCE, 6, 0
    },
    {
26,
FONT_BEGIN, 0, 8,
FONT_NEXT, 1, 9,
FONT_NEXT, 2, 9,
FONT_NEXT, 2, 8,
FONT_NEXT, 3, 8,
FONT_END, 4, 9,
FONT_ADVANCE, 6, 0
    },
    {
27,
FONT_BEGIN, 0, 1,
FONT_END, 4, 7,
FONT_BEGIN, 4, 5,
FONT_END, 0, 5,
FONT_BEGIN, 0, 3,
FONT_END, 4, 3,
FONT_ADVANCE, 6, 1
    },
    {
28,
FONT_BEGIN, 1, 2,
FONT_END, 3, 2,
FONT_BEGIN, 3, 4,
FONT_NEXT, 1, 6,
FONT_END, 3, 8,
FONT_ADVANCE, 6, 0
    },
    {
29,
FONT_BEGIN, 1, 2,
FONT_END, 3, 2,
FONT_BEGIN, 1, 4,
FONT_NEXT, 3, 6,
FONT_END, 1, 8,
FONT_ADVANCE, 6, 0
    },
    {
30,
FONT_BEGIN, 0, 3,
FONT_END, 4, 3,
FONT_BEGIN, 4, 5,
FONT_END, 0, 5,
FONT_BEGIN, 0, 7,
FONT_END, 4, 7,
FONT_ADVANCE, 6, 0
    },
    {
31,
FONT_BEGIN, 0, 6,
FONT_NEXT, 2, 4,
FONT_END, 4, 6,
FONT_ADVANCE, 6, 0
    },
    {
32,
FONT_ADVANCE, 6, 0
    },
    {
33,
FONT_BEGIN, 2, 1,
FONT_END, 2, 2,
FONT_BEGIN, 2, 4,
FONT_END, 2, 8,
FONT_ADVANCE, 6, 0
    },
    {
34,
FONT_BEGIN, 1, 7,
FONT_END, 1, 9,
FONT_BEGIN, 3, 9,
FONT_END, 3, 7,
FONT_ADVANCE, 6, 0
    },
    {
35,
FONT_BEGIN, 1, 2,
FONT_END, 1, 7,
FONT_BEGIN, 3, 7,
FONT_END, 3, 2,
FONT_BEGIN, 4, 3,
FONT_END, 0, 3,
FONT_BEGIN, 0, 6,
FONT_END, 4, 6,
FONT_ADVANCE, 6, 0
    },
    {
36,
FONT_BEGIN, 2, 1,
FONT_END, 2, 9,
FONT_BEGIN, 4, 7,
FONT_NEXT, 3, 8,
FONT_NEXT, 1, 8,
FONT_NEXT, 0, 7,
FONT_NEXT, 0, 6,
FONT_NEXT, 1, 5,
FONT_NEXT, 3, 5,
FONT_NEXT, 4, 4,
FONT_NEXT, 4, 2,
FONT_NEXT, 3, 1,
FONT_NEXT, 1, 1,
FONT_END, 0, 2,
FONT_ADVANCE, 6, -1
    },
    {
37,
FONT_BEGIN, 0, 2,
FONT_NEXT, 0, 3,
FONT_NEXT, 4, 7,
FONT_END, 4, 8,
FONT_BEGIN, 1, 8,
FONT_NEXT, 0, 8,
FONT_NEXT, 0, 7,
FONT_NEXT, 1, 7,
FONT_END, 1, 8,
FONT_BEGIN, 4, 3,
FONT_NEXT, 5, 3,
FONT_NEXT, 5, 2,
FONT_NEXT, 4, 2,
FONT_END, 5, 2,
FONT_ADVANCE, 8, 1
    },
    {
38,
FONT_BEGIN, 4, 4,
FONT_NEXT, 2, 2,
FONT_NEXT, 1, 2,
FONT_NEXT, 0, 3,
FONT_NEXT, 0, 4,
FONT_NEXT, 2, 6,
FONT_NEXT, 2, 7,
FONT_NEXT, 1, 8,
FONT_NEXT, 0, 7,
FONT_NEXT, 0, 6,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
39,
FONT_BEGIN, 0, 7,
FONT_NEXT, 1, 8,
FONT_NEXT, 1, 9,
FONT_NEXT, 2, 9,
FONT_NEXT, 2, 8,
FONT_END, 1, 8,
FONT_ADVANCE, 6, 1
    },
    {
40,
FONT_BEGIN, 4, 2,
FONT_NEXT, 2, 4,
FONT_NEXT, 2, 6,
FONT_END, 4, 8,
FONT_ADVANCE, 6, 0
    },
    {
41,
FONT_BEGIN, 0, 2,
FONT_NEXT, 2, 4,
FONT_NEXT, 2, 6,
FONT_END, 0, 8,
FONT_ADVANCE, 6, 0
    },
    {
42,
FONT_BEGIN, 2, 2,
FONT_END, 2, 8,
FONT_BEGIN, 0, 7,
FONT_END, 4, 3,
FONT_BEGIN, 4, 5,
FONT_END, 0, 5,
FONT_BEGIN, 0, 3,
FONT_END, 4, 7,
FONT_ADVANCE, 6, 0
    },
    {
43,
FONT_BEGIN, 2, 3,
FONT_END, 2, 7,
FONT_BEGIN, 0, 5,
FONT_END, 4, 5,
FONT_ADVANCE, 6, 0
    },
    {
44,
FONT_BEGIN, 0, 1,
FONT_NEXT, 1, 2,
FONT_END, 1, 3,
FONT_ADVANCE, 6, 0
    },
    {
45,
FONT_BEGIN, 0, 5,
FONT_END, 4, 5,
FONT_ADVANCE, 6, 0
    },
    {
46,
FONT_BEGIN, 1, 2,
FONT_END, 2, 2,
FONT_ADVANCE, 6, 0
    },
    {
47,
FONT_BEGIN, 0, 3,
FONT_END, 4, 7,
FONT_ADVANCE, 6, 0
    },
    {
48,
FONT_BEGIN, 0, 3,
FONT_NEXT, 4, 7,
FONT_NEXT, 3, 8,
FONT_NEXT, 1, 8,
FONT_NEXT, 0, 7,
FONT_NEXT, 0, 3,
FONT_NEXT, 1, 2,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_END, 4, 7,
FONT_ADVANCE, 6, 0
    },
    {
49,
FONT_BEGIN, 1, 2,
FONT_END, 3, 2,
FONT_BEGIN, 2, 2,
FONT_NEXT, 2, 8,
FONT_END, 1, 7,
FONT_ADVANCE, 6, 0
    },
    {
50,
FONT_BEGIN, 0, 7,
FONT_NEXT, 1, 8,
FONT_NEXT, 3, 8,
FONT_NEXT, 4, 7,
FONT_NEXT, 4, 6,
FONT_NEXT, 0, 2,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
51,
FONT_BEGIN, 0, 3,
FONT_NEXT, 1, 2,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_NEXT, 4, 4,
FONT_NEXT, 3, 5,
FONT_END, 2, 5,
FONT_BEGIN, 3, 5,
FONT_NEXT, 4, 6,
FONT_NEXT, 4, 7,
FONT_NEXT, 3, 8,
FONT_NEXT, 1, 8,
FONT_END, 0, 7,
FONT_ADVANCE, 6, 0
    },
    {
52,
FONT_BEGIN, 3, 2,
FONT_NEXT, 3, 8,
FONT_NEXT, 0, 5,
FONT_NEXT, 0, 4,
FONT_END, 4, 4,
FONT_ADVANCE, 6, 0
    },
    {
53,
FONT_BEGIN, 0, 3,
FONT_NEXT, 1, 2,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_NEXT, 4, 5,
FONT_NEXT, 3, 6,
FONT_NEXT, 0, 6,
FONT_NEXT, 0, 8,
FONT_END, 4, 8,
FONT_ADVANCE, 6, 0
    },
    {
54,
FONT_BEGIN, 0, 5,
FONT_NEXT, 0, 3,
FONT_NEXT, 1, 2,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_NEXT, 4, 4,
FONT_NEXT, 3, 5,
FONT_NEXT, 0, 5,
FONT_NEXT, 0, 6,
FONT_NEXT, 2, 8,
FONT_END, 3, 8,
FONT_ADVANCE, 6, 0
    },
    {
55,
FONT_BEGIN, 0, 8,
FONT_NEXT, 4, 8,
FONT_NEXT, 2, 4,
FONT_END, 2, 2,
FONT_ADVANCE, 6, 0
    },
    {
56,
FONT_BEGIN, 1, 5,
FONT_NEXT, 0, 4,
FONT_NEXT, 0, 3,
FONT_NEXT, 1, 2,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_NEXT, 4, 4,
FONT_NEXT, 3, 5,
FONT_NEXT, 1, 5,
FONT_NEXT, 0, 6,
FONT_NEXT, 0, 7,
FONT_NEXT, 1, 8,
FONT_NEXT, 3, 8,
FONT_NEXT, 4, 7,
FONT_NEXT, 4, 6,
FONT_END, 3, 5,
FONT_ADVANCE, 6, 0
    },
    {
57,
FONT_BEGIN, 1, 2,
FONT_NEXT, 2, 2,
FONT_NEXT, 4, 4,
FONT_NEXT, 4, 7,
FONT_NEXT, 3, 8,
FONT_NEXT, 1, 8,
FONT_NEXT, 0, 7,
FONT_NEXT, 0, 6,
FONT_NEXT, 1, 5,
FONT_END, 4, 5,
FONT_ADVANCE, 6, 0
    },
    {
58,
FONT_BEGIN, 0, 3,
FONT_END, 1, 3,
FONT_BEGIN, 1, 6,
FONT_END, 0, 6,
FONT_ADVANCE, 6, 0
    },
    {
59,
FONT_BEGIN, 0, 1,
FONT_NEXT, 1, 2,
FONT_END, 1, 3,
FONT_BEGIN, 1, 6,
FONT_END, 0, 6,
FONT_ADVANCE, 6, 0
    },
    {
60,
FONT_BEGIN, 3, 3,
FONT_NEXT, 1, 5,
FONT_END, 3, 7,
FONT_ADVANCE, 6, 0
    },
    {
61,
FONT_BEGIN, 0, 4,
FONT_END, 4, 4,
FONT_BEGIN, 4, 6,
FONT_END, 0, 6,
FONT_ADVANCE, 6, 0
    },
    {
62,
FONT_BEGIN, 1, 7,
FONT_NEXT, 3, 5,
FONT_END, 1, 3,
FONT_ADVANCE, 6, 0
    },
    {
63,
FONT_BEGIN, 1, 2,
FONT_END, 2, 2,
FONT_BEGIN, 2, 4,
FONT_NEXT, 2, 5,
FONT_NEXT, 4, 7,
FONT_NEXT, 3, 8,
FONT_NEXT, 1, 8,
FONT_END, 0, 7,
FONT_ADVANCE, 6, 0
    },
    {
64,
FONT_BEGIN, 3, 2,
FONT_NEXT, 1, 2,
FONT_NEXT, 0, 3,
FONT_NEXT, 0, 7,
FONT_NEXT, 1, 8,
FONT_NEXT, 3, 8,
FONT_NEXT, 4, 7,
FONT_NEXT, 4, 4,
FONT_NEXT, 2, 4,
FONT_NEXT, 2, 6,
FONT_END, 4, 6,
FONT_ADVANCE, 6, 0
    },
    {
65,
FONT_BEGIN, 0, 2,
FONT_NEXT, 0, 7,
FONT_NEXT, 1, 8,
FONT_NEXT, 3, 8,
FONT_NEXT, 4, 7,
FONT_END, 4, 2,
FONT_BEGIN, 0, 5,
FONT_END, 4, 5,
FONT_ADVANCE, 6, 0
    },
    {
66,
FONT_BEGIN, 0, 2,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_NEXT, 4, 4,
FONT_NEXT, 3, 5,
FONT_END, 0, 5,
FONT_BEGIN, 3, 5,
FONT_NEXT, 4, 6,
FONT_NEXT, 4, 7,
FONT_NEXT, 3, 8,
FONT_NEXT, 0, 8,
FONT_END, 0, 2,
FONT_ADVANCE, 6, 0
    },
    {
67,
FONT_BEGIN, 4, 7,
FONT_NEXT, 3, 8,
FONT_NEXT, 1, 8,
FONT_NEXT, 0, 7,
FONT_NEXT, 0, 3,
FONT_NEXT, 1, 2,
FONT_NEXT, 3, 2,
FONT_END, 4, 3,
FONT_ADVANCE, 6, 0
    },
    {
68,
FONT_BEGIN, 0, 2,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_NEXT, 4, 7,
FONT_NEXT, 3, 8,
FONT_END, 0, 8,
FONT_BEGIN, 1, 8,
FONT_END, 1, 2,
FONT_ADVANCE, 6, 0
    },
    {
69,
FONT_BEGIN, 4, 2,
FONT_NEXT, 0, 2,
FONT_NEXT, 0, 8,
FONT_END, 4, 8,
FONT_BEGIN, 3, 5,
FONT_END, 0, 5,
FONT_ADVANCE, 6, 0
    },
    {
70,
FONT_BEGIN, 0, 2,
FONT_NEXT, 0, 8,
FONT_END, 4, 8,
FONT_BEGIN, 3, 5,
FONT_END, 0, 5,
FONT_ADVANCE, 6, 0
    },
    {
71,
FONT_BEGIN, 3, 4,
FONT_NEXT, 4, 4,
FONT_NEXT, 4, 3,
FONT_NEXT, 3, 2,
FONT_NEXT, 1, 2,
FONT_NEXT, 0, 3,
FONT_NEXT, 0, 7,
FONT_NEXT, 1, 8,
FONT_NEXT, 3, 8,
FONT_END, 4, 7,
FONT_ADVANCE, 6, 0
    },
    {
72,
FONT_BEGIN, 0, 2,
FONT_END, 0, 8,
FONT_BEGIN, 0, 5,
FONT_END, 4, 5,
FONT_BEGIN, 4, 8,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
73,
FONT_BEGIN, 1, 2,
FONT_END, 3, 2,
FONT_BEGIN, 2, 2,
FONT_END, 2, 8,
FONT_BEGIN, 1, 8,
FONT_END, 3, 8,
FONT_ADVANCE, 6, 0
    },
    {
74,
FONT_BEGIN, 0, 3,
FONT_NEXT, 1, 2,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_END, 4, 8,
FONT_ADVANCE, 6, 0
    },
    {
75,
FONT_BEGIN, 0, 2,
FONT_END, 0, 8,
FONT_BEGIN, 4, 8,
FONT_NEXT, 1, 5,
FONT_END, 4, 2,
FONT_ADVANCE, 8, 0
    },
    {
76,
FONT_BEGIN, 0, 8,
FONT_NEXT, 0, 2,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
77,
FONT_BEGIN, 0, 2,
FONT_NEXT, 0, 8,
FONT_NEXT, 2, 6,
FONT_NEXT, 4, 8,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
78,
FONT_BEGIN, 0, 2,
FONT_NEXT, 0, 8,
FONT_NEXT, 4, 2,
FONT_END, 4, 8,
FONT_ADVANCE, 6, 0
    },
    {
79,
FONT_BEGIN, 0, 3,
FONT_NEXT, 0, 7,
FONT_NEXT, 1, 8,
FONT_NEXT, 3, 8,
FONT_NEXT, 4, 7,
FONT_NEXT, 4, 3,
FONT_NEXT, 3, 2,
FONT_NEXT, 1, 2,
FONT_END, 0, 3,
FONT_ADVANCE, 6, 0
    },
    {
80,
FONT_BEGIN, 0, 2,
FONT_NEXT, 0, 8,
FONT_NEXT, 3, 8,
FONT_NEXT, 4, 7,
FONT_NEXT, 4, 6,
FONT_NEXT, 3, 5,
FONT_END, 0, 5,
FONT_ADVANCE, 6, 0
    },
    {
81,
FONT_BEGIN, 0, 3,
FONT_NEXT, 0, 7,
FONT_NEXT, 1, 8,
FONT_NEXT, 3, 8,
FONT_NEXT, 4, 7,
FONT_NEXT, 4, 4,
FONT_NEXT, 2, 2,
FONT_NEXT, 1, 2,
FONT_END, 0, 3,
FONT_BEGIN, 2, 5,
FONT_END, 4, 3,
FONT_ADVANCE, 6, 0
    },
    {
82,
FONT_BEGIN, 0, 2,
FONT_NEXT, 0, 8,
FONT_NEXT, 3, 8,
FONT_NEXT, 4, 7,
FONT_NEXT, 4, 6,
FONT_NEXT, 3, 5,
FONT_END, 0, 5,
FONT_BEGIN, 1, 5,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
83,
FONT_BEGIN, 4, 7,
FONT_NEXT, 3, 8,
FONT_NEXT, 1, 8,
FONT_NEXT, 0, 7,
FONT_NEXT, 0, 6,
FONT_NEXT, 1, 5,
FONT_NEXT, 3, 5,
FONT_NEXT, 4, 4,
FONT_NEXT, 4, 3,
FONT_NEXT, 3, 2,
FONT_NEXT, 1, 2,
FONT_END, 0, 3,
FONT_ADVANCE, 6, 0
    },
    {
84,
FONT_BEGIN, 0, 8,
FONT_END, 4, 8,
FONT_BEGIN, 2, 8,
FONT_END, 2, 2,
FONT_ADVANCE, 6, 0
    },
    {
85,
FONT_BEGIN, 0, 3,
FONT_END, 0, 8,
FONT_BEGIN, 4, 8,
FONT_NEXT, 4, 3,
FONT_NEXT, 3, 2,
FONT_NEXT, 1, 2,
FONT_END, 0, 3,
FONT_ADVANCE, 6, 0
    },
    {
86,
FONT_BEGIN, 0, 8,
FONT_NEXT, 0, 5,
FONT_NEXT, 2, 2,
FONT_NEXT, 4, 5,
FONT_END, 4, 8,
FONT_ADVANCE, 6, 0
    },
    {
87,
FONT_BEGIN, 0, 2,
FONT_END, 0, 8,
FONT_BEGIN, 4, 8,
FONT_NEXT, 4, 2,
FONT_NEXT, 2, 4,
FONT_END, 0, 2,
FONT_ADVANCE, 6, 0
    },
    {
88,
FONT_BEGIN, 0, 2,
FONT_END, 4, 8,
FONT_BEGIN, 0, 8,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
89,
FONT_BEGIN, 2, 2,
FONT_NEXT, 2, 5,
FONT_END, 0, 8,
FONT_BEGIN, 4, 8,
FONT_END, 2, 5,
FONT_ADVANCE, 6, 0
    },
    {
90,
FONT_BEGIN, 0, 8,
FONT_NEXT, 4, 8,
FONT_NEXT, 0, 2,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
91,
FONT_BEGIN, 3, 1,
FONT_NEXT, 1, 1,
FONT_NEXT, 1, 9,
FONT_END, 3, 9,
FONT_ADVANCE, 6, 0
    },
    {
92,
FONT_BEGIN, 0, 7,
FONT_END, 4, 3,
FONT_ADVANCE, 6, 0
    },
    {
93,
FONT_BEGIN, 1, 9,
FONT_NEXT, 3, 9,
FONT_NEXT, 3, 1,
FONT_END, 1, 1,
FONT_ADVANCE, 6, 0
    },
    {
94,
FONT_BEGIN, 2, 2,
FONT_END, 2, 8,
FONT_BEGIN, 0, 6,
FONT_NEXT, 2, 8,
FONT_END, 4, 6,
FONT_ADVANCE, 6, 0
    },
    {
95,
FONT_BEGIN, 2, 3,
FONT_NEXT, 0, 5,
FONT_END, 2, 7,
FONT_BEGIN, 0, 5,
FONT_END, 4, 5,
FONT_ADVANCE, 6, 0
    },
    {
96,
FONT_BEGIN, 3, 8,
FONT_NEXT, 2, 8,
FONT_NEXT, 2, 9,
FONT_NEXT, 3, 9,
FONT_NEXT, 3, 8,
FONT_END, 4, 7,
FONT_ADVANCE, 6, 0
    },
    {
97,
FONT_BEGIN, 1, 6,
FONT_NEXT, 3, 6,
FONT_NEXT, 4, 5,
FONT_NEXT, 4, 2,
FONT_NEXT, 1, 2,
FONT_NEXT, 0, 3,
FONT_NEXT, 1, 4,
FONT_END, 4, 4,
FONT_ADVANCE, 6, 0
    },
    {
98,
FONT_BEGIN, 0, 8,
FONT_NEXT, 0, 2,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_NEXT, 4, 5,
FONT_NEXT, 3, 6,
FONT_END, 0, 6,
FONT_ADVANCE, 6, 0
    },
    {
99,
FONT_BEGIN, 4, 5,
FONT_NEXT, 3, 6,
FONT_NEXT, 1, 6,
FONT_NEXT, 0, 5,
FONT_NEXT, 0, 3,
FONT_NEXT, 1, 2,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
100,
FONT_BEGIN, 4, 8,
FONT_NEXT, 4, 2,
FONT_NEXT, 1, 2,
FONT_NEXT, 0, 3,
FONT_NEXT, 0, 5,
FONT_NEXT, 1, 6,
FONT_END, 4, 6,
FONT_ADVANCE, 6, 0
    },
    {
101,
FONT_BEGIN, 3, 2,
FONT_NEXT, 1, 2,
FONT_NEXT, 0, 3,
FONT_NEXT, 0, 5,
FONT_NEXT, 1, 6,
FONT_NEXT, 3, 6,
FONT_NEXT, 4, 5,
FONT_NEXT, 3, 4,
FONT_END, 0, 4,
FONT_ADVANCE, 6, 0
    },
    {
102,
FONT_BEGIN, 1, 2,
FONT_NEXT, 1, 7,
FONT_NEXT, 2, 8,
FONT_NEXT, 3, 8,
FONT_END, 4, 7,
FONT_BEGIN, 2, 5,
FONT_END, 0, 5,
FONT_ADVANCE, 6, 0
    },
    {
103,
FONT_BEGIN, 1, 0,
FONT_NEXT, 3, 0,
FONT_NEXT, 4, 1,
FONT_NEXT, 4, 5,
FONT_NEXT, 3, 6,
FONT_NEXT, 1, 6,
FONT_NEXT, 0, 5,
FONT_NEXT, 0, 3,
FONT_NEXT, 1, 2,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
104,
FONT_BEGIN, 0, 2,
FONT_END, 0, 8,
FONT_BEGIN, 0, 6,
FONT_NEXT, 3, 6,
FONT_NEXT, 4, 5,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
105,
FONT_BEGIN, 2, 2,
FONT_END, 2, 5,
FONT_BEGIN, 2, 6,
FONT_END, 2, 7,
FONT_ADVANCE, 6, 0
    },
    {
106,
FONT_BEGIN, 0, 1,
FONT_NEXT, 1, 0,
FONT_NEXT, 3, 0,
FONT_NEXT, 4, 1,
FONT_END, 4, 5,
FONT_BEGIN, 4, 6,
FONT_END, 4, 7,
FONT_ADVANCE, 6, 0
    },
    {
107,
FONT_BEGIN, 0, 2,
FONT_END, 0, 8,
FONT_BEGIN, 4, 6,
FONT_NEXT, 2, 4,
FONT_END, 0, 4,
FONT_BEGIN, 2, 4,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
108,
FONT_BEGIN, 1, 2,
FONT_END, 1, 8,
FONT_ADVANCE, 6, 0
    },
    {
109,
FONT_BEGIN, 0, 2,
FONT_NEXT, 0, 6,
FONT_NEXT, 1, 6,
FONT_NEXT, 2, 5,
FONT_NEXT, 3, 6,
FONT_NEXT, 4, 5,
FONT_END, 4, 2,
FONT_BEGIN, 2, 2,
FONT_END, 2, 5,
FONT_ADVANCE, 6, 0
    },
    {
110,
FONT_BEGIN, 0, 2,
FONT_NEXT, 0, 6,
FONT_NEXT, 1, 5,
FONT_NEXT, 2, 6,
FONT_NEXT, 3, 6,
FONT_NEXT, 4, 5,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
111,
FONT_BEGIN, 0, 3,
FONT_NEXT, 0, 5,
FONT_NEXT, 1, 6,
FONT_NEXT, 3, 6,
FONT_NEXT, 4, 5,
FONT_NEXT, 4, 3,
FONT_NEXT, 3, 2,
FONT_NEXT, 1, 2,
FONT_END, 0, 3,
FONT_ADVANCE, 6, 0
    },
    {
112,
FONT_BEGIN, 0, 0,
FONT_NEXT, 0, 6,
FONT_NEXT, 3, 6,
FONT_NEXT, 4, 5,
FONT_NEXT, 4, 3,
FONT_NEXT, 3, 2,
FONT_END, 0, 2,
FONT_ADVANCE, 6, 0
    },
    {
113,
FONT_BEGIN, 4, 2,
FONT_NEXT, 1, 2,
FONT_NEXT, 0, 3,
FONT_NEXT, 0, 5,
FONT_NEXT, 1, 6,
FONT_NEXT, 3, 6,
FONT_NEXT, 4, 5,
FONT_END, 4, 0,
FONT_ADVANCE, 6, 0
    },
    {
114,
FONT_BEGIN, 0, 2,
FONT_END, 0, 6,
FONT_BEGIN, 0, 4,
FONT_NEXT, 2, 6,
FONT_NEXT, 3, 6,
FONT_END, 4, 5,
FONT_ADVANCE, 6, 0
    },
    {
115,
FONT_BEGIN, 0, 2,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_NEXT, 3, 4,
FONT_NEXT, 1, 4,
FONT_NEXT, 0, 5,
FONT_NEXT, 1, 6,
FONT_END, 4, 6,
FONT_ADVANCE, 6, 0
    },
    {
116,
FONT_BEGIN, 0, 6,
FONT_END, 4, 6,
FONT_BEGIN, 2, 8,
FONT_NEXT, 2, 3,
FONT_NEXT, 3, 2,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
117,
FONT_BEGIN, 0, 6,
FONT_NEXT, 0, 3,
FONT_NEXT, 1, 2,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_END, 4, 6,
FONT_ADVANCE, 6, 0
    },
    {
118,
FONT_BEGIN, 0, 6,
FONT_NEXT, 0, 4,
FONT_NEXT, 2, 2,
FONT_NEXT, 4, 4,
FONT_END, 4, 6,
FONT_ADVANCE, 6, 0
    },
    {
119,
FONT_BEGIN, 0, 6,
FONT_NEXT, 0, 3,
FONT_NEXT, 1, 2,
FONT_NEXT, 2, 3,
FONT_NEXT, 3, 2,
FONT_NEXT, 4, 3,
FONT_END, 4, 6,
FONT_ADVANCE, 6, 0
    },
    {
120,
FONT_BEGIN, 0, 2,
FONT_END, 4, 6,
FONT_BEGIN, 0, 6,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
121,
FONT_BEGIN, 0, 0,
FONT_NEXT, 4, 4,
FONT_END, 4, 6,
FONT_BEGIN, 0, 6,
FONT_NEXT, 0, 4,
FONT_END, 2, 2,
FONT_ADVANCE, 6, 0
    },
    {
122,
FONT_BEGIN, 0, 6,
FONT_NEXT, 4, 6,
FONT_NEXT, 0, 2,
FONT_END, 4, 2,
FONT_ADVANCE, 6, 0
    },
    {
123,
FONT_BEGIN, 4, 9,
FONT_NEXT, 3, 8,
FONT_NEXT, 3, 6,
FONT_NEXT, 2, 5,
FONT_NEXT, 3, 4,
FONT_NEXT, 3, 2,
FONT_END, 4, 1,
FONT_ADVANCE, 6, 0
    },
    {
124,
FONT_BEGIN, 2, 9,
FONT_END, 2, 0,
FONT_ADVANCE, 6, 0
    },
    {
125,
FONT_BEGIN, 2, 2,
FONT_NEXT, 2, 3,
FONT_NEXT, 0, 5,
FONT_NEXT, 2, 7,
FONT_END, 2, 8,
FONT_BEGIN, 2, 7,
FONT_NEXT, 4, 5,
FONT_END, 2, 3,
FONT_ADVANCE, 6, 0
    },
    {
126,
FONT_BEGIN, 0, 9,
FONT_NEXT, 1, 8,
FONT_NEXT, 1, 6,
FONT_NEXT, 2, 5,
FONT_NEXT, 1, 4,
FONT_NEXT, 1, 2,
FONT_END, 0, 1,
FONT_ADVANCE, 6, 0
    },
    {
END_OF_LIST
    }
};

GLint outlineFont[][1+MAX_STROKES*3] = {
    {
32,
FONT_ADVANCE, 250, 0
    },
    {
33,
FONT_BEGIN, 234, 559,
FONT_NEXT, 236, 605,
FONT_NEXT, 229, 642,
FONT_NEXT, 211, 667,
FONT_NEXT, 182, 676,
FONT_NEXT, 156, 668,
FONT_NEXT, 140, 648,
FONT_NEXT, 130, 592,
FONT_NEXT, 131, 568,
FONT_NEXT, 134, 534,
FONT_NEXT, 146, 447,
FONT_NEXT, 158, 355,
FONT_NEXT, 163, 315,
FONT_NEXT, 167, 284,
FONT_NEXT, 176, 176,
FONT_END, 189, 176,
FONT_BEGIN, 219, 3,
FONT_NEXT, 232, 18,
FONT_NEXT, 238, 42,
FONT_NEXT, 233, 62,
FONT_NEXT, 222, 79,
FONT_NEXT, 204, 91,
FONT_NEXT, 183, 96,
FONT_NEXT, 162, 91,
FONT_NEXT, 145, 80,
FONT_NEXT, 134, 63,
FONT_NEXT, 130, 42,
FONT_NEXT, 135, 18,
FONT_NEXT, 148, 2,
FONT_END, 183, -9,
FONT_ADVANCE, 333, 0
    },
    {
34,
FONT_BEGIN, 308, 482,
FONT_NEXT, 318, 543,
FONT_NEXT, 327, 599,
FONT_NEXT, 331, 635,
FONT_NEXT, 317, 665,
FONT_NEXT, 289, 676,
FONT_NEXT, 260, 665,
FONT_NEXT, 246, 635,
FONT_NEXT, 249, 599,
FONT_NEXT, 258, 543,
FONT_NEXT, 268, 482,
FONT_NEXT, 278, 431,
FONT_END, 299, 431,
FONT_BEGIN, 139, 482,
FONT_NEXT, 149, 543,
FONT_NEXT, 158, 599,
FONT_NEXT, 162, 635,
FONT_NEXT, 148, 665,
FONT_NEXT, 120, 676,
FONT_NEXT, 91, 665,
FONT_NEXT, 77, 635,
FONT_NEXT, 80, 599,
FONT_NEXT, 89, 543,
FONT_NEXT, 99, 482,
FONT_NEXT, 109, 431,
FONT_END, 130, 431,
FONT_ADVANCE, 408, 0
    },
    {
35,
FONT_BEGIN, 371, 271,
FONT_NEXT, 391, 405,
FONT_NEXT, 496, 405,
FONT_NEXT, 496, 460,
FONT_NEXT, 399, 460,
FONT_NEXT, 429, 662,
FONT_NEXT, 371, 662,
FONT_NEXT, 341, 460,
FONT_NEXT, 208, 460,
FONT_NEXT, 239, 662,
FONT_NEXT, 181, 662,
FONT_NEXT, 150, 460,
FONT_NEXT, 32, 460,
FONT_NEXT, 32, 405,
FONT_NEXT, 142, 405,
FONT_NEXT, 121, 271,
FONT_NEXT, 5, 271,
FONT_NEXT, 5, 216,
FONT_NEXT, 112, 216,
FONT_NEXT, 79, 0,
FONT_NEXT, 137, 0,
FONT_NEXT, 170, 216,
FONT_NEXT, 304, 216,
FONT_NEXT, 273, 0,
FONT_NEXT, 331, 0,
FONT_NEXT, 362, 216,
FONT_NEXT, 471, 216,
FONT_END, 471, 271,
FONT_BEGIN, 313, 271,
FONT_NEXT, 179, 271,
FONT_NEXT, 200, 405,
FONT_END, 333, 405,
FONT_ADVANCE, 500, 0
    },
    {
36,
FONT_BEGIN, 425, 611,
FONT_NEXT, 387, 634,
FONT_NEXT, 345, 649,
FONT_NEXT, 264, 664,
FONT_NEXT, 264, 727,
FONT_NEXT, 230, 727,
FONT_NEXT, 230, 664,
FONT_NEXT, 185, 658,
FONT_NEXT, 148, 646,
FONT_NEXT, 93, 611,
FONT_NEXT, 61, 563,
FONT_NEXT, 52, 511,
FONT_NEXT, 55, 476,
FONT_NEXT, 67, 445,
FONT_NEXT, 106, 392,
FONT_NEXT, 163, 349,
FONT_NEXT, 230, 310,
FONT_NEXT, 230, 28,
FONT_NEXT, 166, 37,
FONT_NEXT, 117, 66,
FONT_NEXT, 81, 114,
FONT_NEXT, 59, 181,
FONT_NEXT, 44, 181,
FONT_NEXT, 44, 51,
FONT_NEXT, 70, 35,
FONT_NEXT, 110, 19,
FONT_NEXT, 162, 5,
FONT_NEXT, 230, 0,
FONT_NEXT, 230, -87,
FONT_NEXT, 264, -87,
FONT_NEXT, 264, 0,
FONT_NEXT, 338, 16,
FONT_NEXT, 399, 49,
FONT_NEXT, 441, 100,
FONT_NEXT, 453, 134,
FONT_NEXT, 457, 174,
FONT_NEXT, 453, 211,
FONT_NEXT, 444, 243,
FONT_NEXT, 408, 295,
FONT_NEXT, 348, 341,
FONT_NEXT, 264, 391,
FONT_NEXT, 264, 637,
FONT_NEXT, 305, 628,
FONT_NEXT, 348, 607,
FONT_NEXT, 385, 566,
FONT_NEXT, 399, 537,
FONT_NEXT, 410, 500,
FONT_END, 425, 500,
FONT_BEGIN, 187, 435,
FONT_NEXT, 155, 464,
FONT_NEXT, 133, 495,
FONT_NEXT, 126, 532,
FONT_NEXT, 128, 555,
FONT_NEXT, 141, 586,
FONT_NEXT, 171, 616,
FONT_NEXT, 196, 628,
FONT_NEXT, 229, 637,
FONT_END, 229, 407,
FONT_BEGIN, 308, 264,
FONT_NEXT, 344, 234,
FONT_NEXT, 369, 197,
FONT_NEXT, 378, 151,
FONT_NEXT, 374, 116,
FONT_NEXT, 364, 89,
FONT_NEXT, 333, 53,
FONT_NEXT, 295, 35,
FONT_NEXT, 264, 28,
FONT_END, 264, 293,
FONT_ADVANCE, 500, 0
    },
    {
37,
FONT_BEGIN, 622, 365,
FONT_NEXT, 583, 349,
FONT_NEXT, 514, 293,
FONT_NEXT, 466, 218,
FONT_NEXT, 453, 177,
FONT_NEXT, 449, 137,
FONT_NEXT, 455, 86,
FONT_NEXT, 475, 42,
FONT_NEXT, 513, 11,
FONT_NEXT, 572, 0,
FONT_NEXT, 615, 6,
FONT_NEXT, 654, 25,
FONT_NEXT, 688, 53,
FONT_NEXT, 717, 88,
FONT_NEXT, 757, 173,
FONT_NEXT, 772, 261,
FONT_NEXT, 768, 295,

FONT_NEXT, 760, 321,
FONT_NEXT, 731, 354,
FONT_NEXT, 695, 368,
FONT_END, 663, 371,
FONT_BEGIN, 703, 332,
FONT_NEXT, 726, 314,
FONT_NEXT, 740, 287,
FONT_NEXT, 746, 254,
FONT_NEXT, 733, 183,
FONT_NEXT, 700, 110,
FONT_NEXT, 650, 53,
FONT_NEXT, 620, 36,
FONT_NEXT, 589, 30,
FONT_NEXT, 552, 39,
FONT_NEXT, 533, 60,
FONT_NEXT, 525, 106,
FONT_NEXT, 527, 128,
FONT_NEXT, 534, 160,
FONT_NEXT, 564, 238,
FONT_NEXT, 611, 308,
FONT_NEXT, 641, 330,
FONT_END, 676, 339,
FONT_BEGIN, 595, 676,
FONT_NEXT, 536, 630,
FONT_NEXT, 493, 614,
FONT_NEXT, 438, 608,
FONT_NEXT, 404, 610,
FONT_NEXT, 381, 616,
FONT_NEXT, 352, 635,
FONT_NEXT, 325, 653,
FONT_NEXT, 305, 659,
FONT_NEXT, 276, 662,
FONT_NEXT, 232, 655,
FONT_NEXT, 192, 639,
FONT_NEXT, 156, 613,
FONT_NEXT, 124, 581,
FONT_NEXT, 78, 504,
FONT_NEXT, 65, 463,
FONT_NEXT, 61, 425,
FONT_NEXT, 64, 390,
FONT_NEXT, 72, 361,
FONT_NEXT, 102, 319,
FONT_NEXT, 140, 296,
FONT_NEXT, 178, 289,
FONT_NEXT, 223, 295,
FONT_NEXT, 264, 314,
FONT_NEXT, 299, 343,
FONT_NEXT, 329, 379,
FONT_NEXT, 369, 463,
FONT_NEXT, 384, 548,
FONT_NEXT, 377, 593,
FONT_NEXT, 401, 585,
FONT_NEXT, 443, 583,
FONT_NEXT, 478, 585,
FONT_NEXT, 507, 592,
FONT_NEXT, 548, 611,
FONT_NEXT, 550, 609,
FONT_NEXT, 201, -13,
FONT_NEXT, 249, -13,
FONT_END, 634, 676,
FONT_BEGIN, 172, 326,
FONT_NEXT, 153, 339,
FONT_NEXT, 141, 362,
FONT_NEXT, 137, 397,
FONT_NEXT, 150, 468,
FONT_NEXT, 184, 545,
FONT_NEXT, 230, 606,
FONT_NEXT, 255, 625,
FONT_NEXT, 281, 632,
FONT_NEXT, 299, 622,
FONT_NEXT, 315, 611,
FONT_NEXT, 350, 600,
FONT_NEXT, 359, 553,
FONT_NEXT, 345, 469,
FONT_NEXT, 309, 396,
FONT_NEXT, 257, 343,
FONT_NEXT, 228, 328,
FONT_END, 199, 323,
FONT_ADVANCE, 833, 0
    },
    {
38,
FONT_BEGIN, 287, 596,
FONT_NEXT, 304, 623,
FONT_NEXT, 329, 639,
FONT_NEXT, 359, 644,
FONT_NEXT, 389, 637,
FONT_NEXT, 414, 620,
FONT_NEXT, 431, 594,
FONT_NEXT, 438, 560,
FONT_NEXT, 423, 504,
FONT_NEXT, 389, 462,
FONT_NEXT, 350, 432,
FONT_NEXT, 321, 416,
FONT_NEXT, 293, 483,
FONT_END, 281, 557,
FONT_BEGIN, 495, 426,
FONT_NEXT, 495, 405,
FONT_NEXT, 540, 395,
FONT_NEXT, 554, 381,
FONT_NEXT, 559, 359,
FONT_NEXT, 548, 310,
FONT_NEXT, 524, 259,
FONT_NEXT, 494, 213,
FONT_NEXT, 468, 178,
FONT_NEXT, 396, 277,
FONT_NEXT, 336, 384,
FONT_NEXT, 387, 409,
FONT_NEXT, 437, 444,
FONT_NEXT, 475, 492,
FONT_NEXT, 486, 521,
FONT_NEXT, 491, 556,
FONT_NEXT, 480, 606,
FONT_NEXT, 450, 644,
FONT_NEXT, 407, 667,
FONT_NEXT, 355, 676,
FONT_NEXT, 319, 672,
FONT_NEXT, 288, 661,
FONT_NEXT, 240, 624,
FONT_NEXT, 211, 573,
FONT_NEXT, 202, 519,
FONT_NEXT, 211, 452,
FONT_NEXT, 237, 377,
FONT_NEXT, 199, 355,
FONT_NEXT, 146, 320,
FONT_NEXT, 95, 274,
FONT_NEXT, 57, 215,
FONT_NEXT, 42, 144,
FONT_NEXT, 46, 99,
FONT_NEXT, 59, 64,
FONT_NEXT, 78, 36,
FONT_NEXT, 101, 16,
FONT_NEXT, 155, -7,
FONT_NEXT, 207, -13,
FONT_NEXT, 286, -4,
FONT_NEXT, 347, 19,
FONT_NEXT, 394, 49,
FONT_NEXT, 429, 78,
FONT_NEXT, 456, 51,
FONT_NEXT, 494, 21,
FONT_NEXT, 542, -3,
FONT_NEXT, 599, -13,
FONT_NEXT, 635, -9,
FONT_NEXT, 667, 4,
FONT_NEXT, 713, 42,
FONT_NEXT, 740, 80,
FONT_NEXT, 747, 93,
FONT_NEXT, 750, 100,
FONT_NEXT, 735, 111,
FONT_NEXT, 697, 73,
FONT_NEXT, 672, 62,
FONT_NEXT, 639, 58,
FONT_NEXT, 585, 70,
FONT_NEXT, 540, 98,
FONT_NEXT, 507, 129,
FONT_NEXT, 491, 150,
FONT_NEXT, 528, 200,
FONT_NEXT, 550, 232,
FONT_NEXT, 568, 261,
FONT_NEXT, 591, 300,
FONT_NEXT, 617, 346,
FONT_NEXT, 638, 375,
FONT_NEXT, 661, 392,
FONT_NEXT, 711, 405,
FONT_END, 711, 426,
FONT_BEGIN, 146, 238,
FONT_NEXT, 176, 283,
FONT_NEXT, 214, 317,
FONT_NEXT, 252, 343,
FONT_NEXT, 322, 219,
FONT_NEXT, 404, 104,
FONT_NEXT, 356, 69,
FONT_NEXT, 316, 49,
FONT_NEXT, 285, 41,
FONT_NEXT, 263, 39,
FONT_NEXT, 212, 50,
FONT_NEXT, 171, 80,
FONT_NEXT, 144, 125,
FONT_END, 134, 180,
FONT_ADVANCE, 778, 0
    },
    {
39,
FONT_BEGIN, 136, 450,
FONT_NEXT, 173, 483,
FONT_NEXT, 204, 528,
FONT_NEXT, 218, 580,
FONT_NEXT, 208, 630,
FONT_NEXT, 186, 659,
FONT_NEXT, 159, 672,
FONT_NEXT, 137, 676,
FONT_NEXT, 98, 663,
FONT_NEXT, 84, 645,
FONT_NEXT, 79, 618,
FONT_NEXT, 85, 593,
FONT_NEXT, 100, 578,
FONT_NEXT, 141, 568,
FONT_NEXT, 156, 570,
FONT_NEXT, 165, 572,
FONT_NEXT, 174, 568,
FONT_NEXT, 179, 558,
FONT_NEXT, 175, 539,
FONT_NEXT, 162, 513,
FONT_NEXT, 137, 483,
FONT_NEXT, 97, 452,
FONT_END, 106, 433,
FONT_ADVANCE, 333, 0
    },
    {
40,
FONT_BEGIN, 239, 638,
FONT_NEXT, 191, 596,
FONT_NEXT, 148, 549,
FONT_NEXT, 113, 499,
FONT_NEXT, 85, 444,
FONT_NEXT, 64, 386,
FONT_NEXT, 52, 325,
FONT_NEXT, 48, 262,
FONT_NEXT, 53, 169,
FONT_NEXT, 70, 96,
FONT_NEXT, 96, 35,
FONT_NEXT, 130, -20,
FONT_NEXT, 203, -108,
FONT_NEXT, 247, -148,
FONT_NEXT, 292, -177,
FONT_NEXT, 304, -161,
FONT_NEXT, 247, -116,
FONT_NEXT, 205, -63,
FONT_NEXT, 175, -4,
FONT_NEXT, 155, 56,
FONT_NEXT, 136, 174,
FONT_NEXT, 134, 225,
FONT_NEXT, 134, 269,
FONT_NEXT, 138, 356,
FONT_NEXT, 150, 428,
FONT_NEXT, 168, 488,
FONT_NEXT, 191, 537,
FONT_NEXT, 246, 609,
FONT_NEXT, 304, 660,
FONT_END, 295, 676,
FONT_ADVANCE, 333, 0
    },
    {
41,
FONT_BEGIN, 93, -140,
FONT_NEXT, 141, -98,
FONT_NEXT, 184, -51,
FONT_NEXT, 219, 0,
FONT_NEXT, 247, 54,
FONT_NEXT, 268, 112,
FONT_NEXT, 280, 173,
FONT_NEXT, 285, 237,
FONT_NEXT, 279, 329,
FONT_NEXT, 262, 402,
FONT_NEXT, 236, 463,
FONT_NEXT, 203, 519,
FONT_NEXT, 129, 606,
FONT_NEXT, 85, 646,
FONT_NEXT, 41, 676,
FONT_NEXT, 29, 660,
FONT_NEXT, 85, 614,
FONT_NEXT, 127, 561,
FONT_NEXT, 157, 502,
FONT_NEXT, 177, 442,
FONT_NEXT, 196, 324,
FONT_NEXT, 198, 273,
FONT_NEXT, 199, 230,
FONT_NEXT, 194, 142,
FONT_NEXT, 182, 70,
FONT_NEXT, 164, 10,
FONT_NEXT, 141, -39,
FONT_NEXT, 86, -111,
FONT_NEXT, 29, -161,
FONT_END, 38, -177,
FONT_ADVANCE, 333, 0
    },
    {
42,
FONT_BEGIN, 76, 362,
FONT_NEXT, 103, 351,
FONT_NEXT, 129, 358,
FONT_NEXT, 143, 371,
FONT_NEXT, 164, 394,
FONT_NEXT, 192, 422,
FONT_NEXT, 210, 435,
FONT_NEXT, 240, 456,
FONT_NEXT, 241, 449,
FONT_NEXT, 240, 405,
FONT_NEXT, 230, 365,
FONT_NEXT, 219, 331,
FONT_NEXT, 214, 304,
FONT_NEXT, 225, 275,
FONT_NEXT, 249, 265,
FONT_NEXT, 277, 278,
FONT_NEXT, 288, 307,
FONT_NEXT, 274, 352,
FONT_NEXT, 264, 389,
FONT_NEXT, 260, 449,
FONT_NEXT, 260, 456,
FONT_NEXT, 267, 453,
FONT_NEXT, 307, 425,
FONT_NEXT, 338, 391,
FONT_NEXT, 366, 362,
FONT_NEXT, 396, 350,
FONT_NEXT, 421, 359,
FONT_NEXT, 432, 386,
FONT_NEXT, 428, 404,
FONT_NEXT, 419, 416,
FONT_NEXT, 384, 428,
FONT_NEXT, 334, 439,
FONT_NEXT, 276, 465,
FONT_NEXT, 268, 470,
FONT_NEXT, 304, 489,
FONT_NEXT, 336, 502,
FONT_NEXT, 388, 513,
FONT_NEXT, 420, 525,
FONT_NEXT, 428, 537,
FONT_NEXT, 431, 557,
FONT_NEXT, 427, 574,
FONT_NEXT, 417, 585,
FONT_NEXT, 395, 593,
FONT_NEXT, 368, 581,
FONT_NEXT, 341, 554,
FONT_NEXT, 309, 520,
FONT_NEXT, 267, 488,
FONT_NEXT, 260, 484,
FONT_NEXT, 260, 510,
FONT_NEXT, 273, 582,
FONT_NEXT, 282, 613,
FONT_NEXT, 287, 637,
FONT_NEXT, 279, 661,
FONT_NEXT, 269, 671,
FONT_NEXT, 253, 676,
FONT_NEXT, 231, 671,
FONT_NEXT, 220, 659,
FONT_NEXT, 216, 637,
FONT_NEXT, 228, 581,
FONT_NEXT, 237, 548,
FONT_NEXT, 241, 499,
FONT_NEXT, 241, 486,
FONT_NEXT, 185, 523,
FONT_NEXT, 152, 557,
FONT_NEXT, 129, 581,
FONT_NEXT, 102, 591,
FONT_NEXT, 85, 586,
FONT_NEXT, 75, 576,
FONT_NEXT, 69, 557,
FONT_NEXT, 72, 538,
FONT_NEXT, 83, 526,
FONT_NEXT, 120, 511,
FONT_NEXT, 170, 500,
FONT_NEXT, 223, 478,
FONT_NEXT, 234, 471,
FONT_NEXT, 172, 441,
FONT_NEXT, 120, 428,
FONT_NEXT, 82, 415,
FONT_NEXT, 72, 402,
FONT_END, 69, 383,
FONT_ADVANCE, 500, 0
    },
    {
43,
FONT_BEGIN, 30, 286,
FONT_NEXT, 30, 220,
FONT_NEXT, 249, 220,
FONT_NEXT, 249, 0,
FONT_NEXT, 315, 0,
FONT_NEXT, 315, 220,
FONT_NEXT, 534, 220,
FONT_NEXT, 534, 286,
FONT_NEXT, 315, 286,
FONT_NEXT, 315, 506,
FONT_NEXT, 249, 506,
FONT_END, 249, 286,
FONT_ADVANCE, 564, 0
    },
    {
44,
FONT_BEGIN, 113, -124,
FONT_NEXT, 150, -91,
FONT_NEXT, 181, -46,
FONT_NEXT, 195, 6,
FONT_NEXT, 185, 56,
FONT_NEXT, 163, 85,
FONT_NEXT, 136, 98,
FONT_NEXT, 114, 102,
FONT_NEXT, 75, 89,
FONT_NEXT, 61, 71,
FONT_NEXT, 56, 44,
FONT_NEXT, 62, 19,
FONT_NEXT, 77, 4,
FONT_NEXT, 118, -6,
FONT_NEXT, 133, -4,
FONT_NEXT, 142, -2,
FONT_NEXT, 151, -6,
FONT_NEXT, 156, -16,
FONT_NEXT, 152, -35,
FONT_NEXT, 139, -61,
FONT_NEXT, 114, -91,
FONT_NEXT, 74, -122,
FONT_END, 83, -141,
FONT_ADVANCE, 250, 0
    },
    {
45,
FONT_BEGIN, 39, 194,
FONT_NEXT, 285, 194,
FONT_NEXT, 285, 257,
FONT_END, 39, 257,
FONT_ADVANCE, 333, 0
    },
    {
46,
FONT_BEGIN, 160, 2,
FONT_NEXT, 175, 18,
FONT_NEXT, 181, 43,
FONT_NEXT, 176, 64,
FONT_NEXT, 164, 83,
FONT_NEXT, 146, 95,
FONT_NEXT, 125, 100,
FONT_NEXT, 103, 95,
FONT_NEXT, 86, 83,
FONT_NEXT, 74, 65,
FONT_NEXT, 70, 43,
FONT_NEXT, 75, 18,
FONT_NEXT, 89, 1,
FONT_END, 125, -11,
FONT_ADVANCE, 250, 0
    },
    {
47,
FONT_BEGIN, 220, 676,
FONT_NEXT, -9, -14,
FONT_NEXT, 59, -14,
FONT_END, 287, 676,
FONT_ADVANCE, 278, 0
    },
    {
48,
FONT_BEGIN, 292, 639,
FONT_NEXT, 324, 609,
FONT_NEXT, 347, 566,
FONT_NEXT, 363, 516,
FONT_NEXT, 377, 409,
FONT_NEXT, 379, 364,
FONT_NEXT, 380, 331,
FONT_NEXT, 379, 294,
FONT_NEXT, 377, 247,
FONT_NEXT, 363, 141,
FONT_NEXT, 347, 92,
FONT_NEXT, 324, 50,
FONT_NEXT, 292, 22,
FONT_NEXT, 250, 12,
FONT_NEXT, 207, 22,
FONT_NEXT, 175, 50,
FONT_NEXT, 152, 91,
FONT_NEXT, 137, 141,
FONT_NEXT, 122, 246,
FONT_NEXT, 120, 294,
FONT_NEXT, 120, 331,
FONT_NEXT, 120, 364,
FONT_NEXT, 122, 409,
FONT_NEXT, 137, 516,
FONT_NEXT, 152, 566,
FONT_NEXT, 175, 609,
FONT_NEXT, 207, 639,
FONT_END, 250, 650,
FONT_BEGIN, 187, 666,
FONT_NEXT, 137, 638,
FONT_NEXT, 97, 597,
FONT_NEXT, 68, 547,
FONT_NEXT, 33, 434,
FONT_NEXT, 26, 379,
FONT_NEXT, 24, 331,
FONT_NEXT, 33, 226,
FONT_NEXT, 68, 113,
FONT_NEXT, 97, 63,
FONT_NEXT, 137, 22,
FONT_NEXT, 187, -5,
FONT_NEXT, 250, -14,
FONT_NEXT, 312, -5,
FONT_NEXT, 362, 22,
FONT_NEXT, 402, 63,
FONT_NEXT, 432, 113,
FONT_NEXT, 466, 226,
FONT_NEXT, 476, 331,
FONT_NEXT, 473, 379,
FONT_NEXT, 466, 434,
FONT_NEXT, 432, 547,
FONT_NEXT, 402, 597,
FONT_NEXT, 362, 638,
FONT_NEXT, 312, 666,
FONT_END, 250, 676,
FONT_ADVANCE, 500, 0
    },
    {
49,
FONT_BEGIN, 394, 0,
FONT_NEXT, 394, 15,
FONT_NEXT, 347, 17,
FONT_NEXT, 318, 26,
FONT_NEXT, 303, 44,
FONT_NEXT, 299, 74,
FONT_NEXT, 299, 674,
FONT_NEXT, 291, 676,
FONT_NEXT, 111, 585,
FONT_NEXT, 111, 571,
FONT_NEXT, 143, 583,
FONT_NEXT, 161, 590,
FONT_NEXT, 179, 593,
FONT_NEXT, 198, 588,
FONT_NEXT, 208, 577,
FONT_NEXT, 213, 546,
FONT_NEXT, 213, 93,
FONT_NEXT, 206, 49,
FONT_NEXT, 187, 27,
FONT_NEXT, 157, 17,
FONT_NEXT, 118, 15,
FONT_END, 118, 0,
FONT_ADVANCE, 500, 0
    },
    {
50,
FONT_BEGIN, 462, 142,
FONT_NEXT, 435, 105,
FONT_NEXT, 413, 85,
FONT_NEXT, 367, 76,
FONT_NEXT, 128, 76,
FONT_NEXT, 296, 252,
FONT_NEXT, 334, 295,
FONT_NEXT, 376, 351,
FONT_NEXT, 410, 419,
FONT_NEXT, 424, 496,
FONT_NEXT, 419, 535,
FONT_NEXT, 408, 571,
FONT_NEXT, 367, 628,
FONT_NEXT, 309, 663,
FONT_NEXT, 243, 676,
FONT_NEXT, 176, 666,
FONT_NEXT, 115, 633,
FONT_NEXT, 88, 606,
FONT_NEXT, 64, 571,
FONT_NEXT, 45, 528,
FONT_NEXT, 31, 477,
FONT_NEXT, 52, 472,
FONT_NEXT, 70, 515,
FONT_NEXT, 97, 557,
FONT_NEXT, 137, 589,
FONT_NEXT, 195, 602,
FONT_NEXT, 235, 597,
FONT_NEXT, 267, 586,
FONT_NEXT, 310, 549,
FONT_NEXT, 332, 502,
FONT_NEXT, 338, 459,
FONT_NEXT, 329, 400,
FONT_NEXT, 305, 336,
FONT_NEXT, 265, 270,
FONT_NEXT, 208, 201,
FONT_NEXT, 30, 12,
FONT_NEXT, 30, 0,
FONT_NEXT, 420, 0,
FONT_END, 475, 137,
FONT_ADVANCE, 500, 0
    },
    {
51,
FONT_BEGIN, 197, 330,
FONT_NEXT, 230, 326,
FONT_NEXT, 283, 308,
FONT_NEXT, 321, 276,
FONT_NEXT, 345, 239,
FONT_NEXT, 356, 202,
FONT_NEXT, 359, 176,
FONT_NEXT, 351, 120,
FONT_NEXT, 326, 70,
FONT_NEXT, 285, 35,
FONT_NEXT, 225, 22,
FONT_NEXT, 184, 30,
FONT_NEXT, 148, 50,
FONT_NEXT, 115, 69,
FONT_NEXT, 82, 78,
FONT_NEXT, 56, 71,
FONT_NEXT, 46, 60,
FONT_NEXT, 43, 43,
FONT_NEXT, 56, 12,
FONT_NEXT, 89, -5,
FONT_NEXT, 126, -13,
FONT_NEXT, 154, -14,
FONT_NEXT, 210, -10,
FONT_NEXT, 263, 1,
FONT_NEXT, 310, 21,
FONT_NEXT, 351, 47,
FONT_NEXT, 384, 80,
FONT_NEXT, 409, 120,
FONT_NEXT, 425, 165,
FONT_NEXT, 431, 216,
FONT_NEXT, 427, 261,
FONT_NEXT, 417, 298,
FONT_NEXT, 383, 351,
FONT_NEXT, 341, 383,
FONT_NEXT, 304, 401,
FONT_NEXT, 346, 433,
FONT_NEXT, 375, 466,
FONT_NEXT, 391, 502,
FONT_NEXT, 397, 541,
FONT_NEXT, 390, 579,
FONT_NEXT, 367, 623,
FONT_NEXT, 319, 660,
FONT_NEXT, 284, 671,
FONT_NEXT, 241, 676,
FONT_NEXT, 187, 669,
FONT_NEXT, 132, 644,
FONT_NEXT, 83, 594,
FONT_NEXT, 62, 558,
FONT_NEXT, 45, 514,
FONT_NEXT, 60, 510,
FONT_NEXT, 76, 538,
FONT_NEXT, 105, 573,
FONT_NEXT, 148, 603,
FONT_NEXT, 208, 616,
FONT_NEXT, 256, 606,
FONT_NEXT, 290, 583,
FONT_NEXT, 311, 549,
FONT_NEXT, 318, 512,
FONT_NEXT, 304, 450,
FONT_NEXT, 269, 403,
FONT_NEXT, 216, 368,
FONT_NEXT, 152, 343,
FONT_END, 153, 330,
FONT_ADVANCE, 500, 0
    },
    {
52,
FONT_BEGIN, 370, 231,
FONT_NEXT, 370, 676,
FONT_NEXT, 326, 676,
FONT_NEXT, 12, 231,
FONT_NEXT, 12, 167,
FONT_NEXT, 293, 167,
FONT_NEXT, 293, 0,
FONT_NEXT, 370, 0,
FONT_NEXT, 370, 167,
FONT_NEXT, 472, 167,
FONT_END, 472, 231,
FONT_BEGIN, 52, 231,
FONT_NEXT, 290, 571,
FONT_NEXT, 292, 571,
FONT_END, 292, 231,
FONT_ADVANCE, 500, 0
    },
    {
53,
FONT_BEGIN, 377, 583,
FONT_NEXT, 391, 584,
FONT_NEXT, 400, 592,
FONT_NEXT, 438, 681,
FONT_NEXT, 429, 688,
FONT_NEXT, 411, 669,
FONT_NEXT, 383, 662,
FONT_NEXT, 174, 662,
FONT_NEXT, 65, 425,
FONT_NEXT, 64, 415,
FONT_NEXT, 71, 412,
FONT_NEXT, 161, 399,
FONT_NEXT, 254, 365,
FONT_NEXT, 294, 337,
FONT_NEXT, 326, 300,
FONT_NEXT, 348, 255,
FONT_NEXT, 356, 201,
FONT_NEXT, 352, 158,
FONT_NEXT, 343, 121,
FONT_NEXT, 310, 66,
FONT_NEXT, 266, 33,
FONT_NEXT, 217, 23,
FONT_NEXT, 72, 85,
FONT_NEXT, 48, 80,
FONT_NEXT, 37, 69,
FONT_NEXT, 32, 46,
FONT_NEXT, 40, 21,
FONT_NEXT, 65, 2,
FONT_NEXT, 104, -10,
FONT_NEXT, 158, -14,
FONT_NEXT, 214, -10,
FONT_NEXT, 266, 4,
FONT_NEXT, 312, 26,
FONT_NEXT, 351, 55,
FONT_NEXT, 382, 90,
FONT_NEXT, 406, 131,
FONT_NEXT, 420, 176,
FONT_NEXT, 426, 225,
FONT_NEXT, 423, 281,
FONT_NEXT, 415, 323,
FONT_NEXT, 398, 359,
FONT_NEXT, 372, 395,
FONT_NEXT, 321, 439,
FONT_NEXT, 263, 468,
FONT_NEXT, 139, 498,
FONT_END, 181, 583,
FONT_ADVANCE, 500, 0
    },
    {
54,
FONT_BEGIN, 358, 673,
FONT_NEXT, 279, 647,
FONT_NEXT, 209, 609,
FONT_NEXT, 149, 560,
FONT_NEXT, 100, 502,
FONT_NEXT, 64, 436,
FONT_NEXT, 41, 365,
FONT_NEXT, 34, 291,
FONT_NEXT, 38, 217,
FONT_NEXT, 51, 154,
FONT_NEXT, 72, 102,
FONT_NEXT, 99, 59,
FONT_NEXT, 132, 27,
FONT_NEXT, 169, 4,
FONT_NEXT, 210, -10,
FONT_NEXT, 254, -14,
FONT_NEXT, 314, -8,
FONT_NEXT, 362, 11,
FONT_NEXT, 399, 40,
FONT_NEXT, 427, 74,
FONT_NEXT, 459, 150,
FONT_NEXT, 468, 218,
FONT_NEXT, 464, 265,
FONT_NEXT, 454, 307,
FONT_NEXT, 437, 343,
FONT_NEXT, 415, 373,
FONT_NEXT, 357, 414,
FONT_NEXT, 284, 428,
FONT_NEXT, 247, 425,
FONT_NEXT, 217, 417,
FONT_NEXT, 187, 403,
FONT_NEXT, 152, 383,
FONT_NEXT, 181, 475,
FONT_NEXT, 207, 519,
FONT_NEXT, 240, 560,
FONT_NEXT, 281, 597,
FONT_NEXT, 329, 628,
FONT_NEXT, 384, 652,
FONT_NEXT, 448, 668,
FONT_END, 446, 684,
FONT_BEGIN, 278, 377,
FONT_NEXT, 307, 364,
FONT_NEXT, 331, 344,
FONT_NEXT, 349, 319,
FONT_NEXT, 371, 254,
FONT_NEXT, 378, 179,
FONT_NEXT, 369, 105,
FONT_NEXT, 347, 54,
FONT_NEXT, 312, 23,
FONT_NEXT, 269, 14,
FONT_NEXT, 215, 28,
FONT_NEXT, 191, 46,
FONT_NEXT, 169, 72,
FONT_NEXT, 152, 105,
FONT_NEXT, 138, 147,
FONT_NEXT, 130, 197,
FONT_NEXT, 127, 256,
FONT_NEXT, 128, 294,
FONT_NEXT, 133, 321,
FONT_NEXT, 147, 350,
FONT_NEXT, 191, 375,
FONT_END, 242, 382,
FONT_ADVANCE, 500, 0
    },
    {
55,
FONT_BEGIN, 79, 662,
FONT_NEXT, 63, 618,
FONT_NEXT, 20, 515,
FONT_NEXT, 37, 507,
FONT_NEXT, 59, 541,
FONT_NEXT, 83, 567,
FONT_NEXT, 112, 582,
FONT_NEXT, 153, 588,
FONT_NEXT, 370, 588,
FONT_NEXT, 172, -8,
FONT_NEXT, 237, -8,
FONT_NEXT, 449, 646,
FONT_END, 449, 662,
FONT_ADVANCE, 500, 0
    },
    {
56,
FONT_BEGIN, 285, 258,
FONT_NEXT, 333, 216,
FONT_NEXT, 360, 174,
FONT_NEXT, 369, 126,
FONT_NEXT, 360, 78,
FONT_NEXT, 337, 43,
FONT_NEXT, 302, 21,
FONT_NEXT, 259, 14,
FONT_NEXT, 204, 25,
FONT_NEXT, 165, 56,
FONT_NEXT, 140, 101,
FONT_NEXT, 132, 156,
FONT_NEXT, 140, 215,
FONT_NEXT, 160, 258,
FONT_NEXT, 186, 289,
FONT_END, 212, 312,
FONT_BEGIN, 68, 88,
FONT_NEXT, 104, 36,
FONT_NEXT, 164, 0,
FONT_NEXT, 202, -11,
FONT_NEXT, 246, -14,
FONT_NEXT, 302, -10,
FONT_NEXT, 347, 4,
FONT_NEXT, 381, 24,
FONT_NEXT, 407, 48,
FONT_NEXT, 437, 102,
FONT_NEXT, 445, 150,
FONT_NEXT, 439, 199,
FONT_NEXT, 417, 249,
FONT_NEXT, 370, 305,
FONT_NEXT, 335, 336,
FONT_NEXT, 290, 371,
FONT_NEXT, 333, 395,
FONT_NEXT, 377, 428,
FONT_NEXT, 410, 473,
FONT_NEXT, 424, 534,
FONT_NEXT, 412, 588,
FONT_NEXT, 378, 633,
FONT_NEXT, 324, 664,
FONT_NEXT, 249, 676,
FONT_NEXT, 183, 665,
FONT_NEXT, 123, 636,
FONT_NEXT, 79, 587,
FONT_NEXT, 66, 556,
FONT_NEXT, 62, 521,
FONT_NEXT, 69, 467,
FONT_NEXT, 92, 423,
FONT_NEXT, 131, 381,
FONT_NEXT, 186, 332,
FONT_NEXT, 128, 286,
FONT_NEXT, 87, 246,
FONT_NEXT, 63, 202,
FONT_END, 56, 146,
FONT_BEGIN, 142, 583,
FONT_NEXT, 161, 615,
FONT_NEXT, 194, 638,
FONT_NEXT, 243, 648,
FONT_NEXT, 292, 638,
FONT_NEXT, 327, 614,
FONT_NEXT, 348, 579,
FONT_NEXT, 355, 539,
FONT_NEXT, 345, 481,
FONT_NEXT, 322, 440,
FONT_NEXT, 291, 410,
FONT_NEXT, 261, 389,
FONT_NEXT, 226, 412,
FONT_NEXT, 185, 447,
FONT_NEXT, 150, 492,
FONT_END, 136, 547,
FONT_ADVANCE, 500, 0
    },
    {
57,
FONT_BEGIN, 149, -9,
FONT_NEXT, 228, 20,
FONT_NEXT, 297, 61,
FONT_NEXT, 354, 113,
FONT_NEXT, 399, 174,
FONT_NEXT, 432, 243,
FONT_NEXT, 452, 317,
FONT_NEXT, 459, 396,
FONT_NEXT, 454, 455,
FONT_NEXT, 442, 510,
FONT_NEXT, 422, 557,
FONT_NEXT, 395, 598,
FONT_NEXT, 363, 631,
FONT_NEXT, 326, 655,
FONT_NEXT, 284, 670,
FONT_NEXT, 240, 676,
FONT_NEXT, 193, 671,
FONT_NEXT, 152, 657,
FONT_NEXT, 116, 634,
FONT_NEXT, 86, 605,
FONT_NEXT, 62, 569,
FONT_NEXT, 44, 528,
FONT_NEXT, 30, 435,
FONT_NEXT, 39, 369,
FONT_NEXT, 69, 305,
FONT_NEXT, 94, 278,
FONT_NEXT, 125, 256,
FONT_NEXT, 163, 242,
FONT_NEXT, 210, 237,
FONT_NEXT, 285, 250,
FONT_NEXT, 357, 292,
FONT_NEXT, 359, 290,
FONT_NEXT, 354, 271,
FONT_NEXT, 343, 239,
FONT_NEXT, 324, 197,
FONT_NEXT, 296, 149,
FONT_NEXT, 256, 101,
FONT_NEXT, 204, 56,
FONT_NEXT, 138, 21,
FONT_NEXT, 56, -2,
FONT_END, 59, -22,
FONT_BEGIN, 358, 332,
FONT_NEXT, 346, 315,
FONT_NEXT, 302, 291,
FONT_NEXT, 250, 280,
FONT_NEXT, 214, 285,
FONT_NEXT, 185, 299,
FONT_NEXT, 146, 348,
FONT_NEXT, 127, 410,
FONT_NEXT, 122, 473,
FONT_NEXT, 126, 534,
FONT_NEXT, 144, 591,
FONT_NEXT, 176, 632,
FONT_NEXT, 200, 643,
FONT_NEXT, 229, 648,
FONT_NEXT, 272, 639,
FONT_NEXT, 305, 616,
FONT_NEXT, 329, 583,
FONT_NEXT, 345, 543,
FONT_NEXT, 359, 458,
FONT_NEXT, 361, 421,
FONT_NEXT, 362, 394,
FONT_END, 362, 355,
FONT_ADVANCE, 500, 0
    },
    {
58,
FONT_BEGIN, 171, 361,
FONT_NEXT, 186, 377,
FONT_NEXT, 192, 402,
FONT_NEXT, 187, 423,
FONT_NEXT, 175, 442,
FONT_NEXT, 157, 454,
FONT_NEXT, 136, 459,
FONT_NEXT, 114, 454,
FONT_NEXT, 97, 442,
FONT_NEXT, 85, 424,
FONT_NEXT, 81, 402,
FONT_NEXT, 86, 377,
FONT_NEXT, 100, 360,
FONT_END, 136, 348,
FONT_BEGIN, 171, 2,
FONT_NEXT, 186, 18,
FONT_NEXT, 192, 43,
FONT_NEXT, 187, 64,
FONT_NEXT, 175, 83,
FONT_NEXT, 157, 95,
FONT_NEXT, 136, 100,
FONT_NEXT, 114, 95,
FONT_NEXT, 97, 83,
FONT_NEXT, 85, 65,
FONT_NEXT, 81, 43,
FONT_NEXT, 86, 18,
FONT_NEXT, 100, 1,
FONT_END, 136, -11,
FONT_ADVANCE, 278, 0
    },
    {
59,
FONT_BEGIN, 138, -123,
FONT_NEXT, 175, -90,
FONT_NEXT, 206, -45,
FONT_NEXT, 219, 6,
FONT_NEXT, 209, 56,
FONT_NEXT, 186, 85,
FONT_NEXT, 160, 98,
FONT_NEXT, 139, 102,
FONT_NEXT, 125, 100,
FONT_NEXT, 105, 93,
FONT_NEXT, 87, 76,
FONT_NEXT, 80, 44,
FONT_NEXT, 85, 20,
FONT_NEXT, 99, 5,
FONT_NEXT, 142, -6,
FONT_NEXT, 157, -4,
FONT_NEXT, 166, -2,
FONT_NEXT, 175, -6,
FONT_NEXT, 180, -16,
FONT_NEXT, 176, -35,
FONT_NEXT, 163, -61,
FONT_NEXT, 138, -91,
FONT_NEXT, 98, -122,
FONT_END, 107, -141,
FONT_BEGIN, 171, 361,
FONT_NEXT, 186, 377,
FONT_NEXT, 192, 402,
FONT_NEXT, 187, 423,
FONT_NEXT, 175, 442,
FONT_NEXT, 157, 454,
FONT_NEXT, 136, 459,
FONT_NEXT, 114, 454,
FONT_NEXT, 97, 442,
FONT_NEXT, 85, 424,
FONT_NEXT, 81, 402,
FONT_NEXT, 86, 377,
FONT_NEXT, 100, 360,
FONT_END, 136, 348,
FONT_ADVANCE, 278, 0
    },
    {
60,
FONT_BEGIN, 111, 253,
FONT_NEXT, 536, 446,
FONT_NEXT, 536, 514,
FONT_NEXT, 28, 284,
FONT_NEXT, 28, 222,
FONT_NEXT, 536, -8,
FONT_END, 536, 60,
FONT_ADVANCE, 564, 0
    },
    {
61,
FONT_BEGIN, 30, 386,
FONT_NEXT, 30, 320,
FONT_NEXT, 534, 320,
FONT_END, 534, 386,
FONT_BEGIN, 30, 186,
FONT_NEXT, 30, 120,
FONT_NEXT, 534, 120,
FONT_END, 534, 186,
FONT_ADVANCE, 564, 0
    },
    {
62,
FONT_BEGIN, 28, -8,
FONT_NEXT, 536, 222,
FONT_NEXT, 536, 284,
FONT_NEXT, 28, 514,
FONT_NEXT, 28, 446,
FONT_NEXT, 453, 253,
FONT_END, 28, 60,
FONT_ADVANCE, 564, 0
    },
    {
63,
FONT_BEGIN, 257, 220,
FONT_NEXT, 276, 267,
FONT_NEXT, 302, 309,
FONT_NEXT, 339, 354,
FONT_NEXT, 363, 385,
FONT_NEXT, 387, 426,
FONT_NEXT, 406, 469,
FONT_NEXT, 414, 510,
FONT_NEXT, 412, 544,
FONT_NEXT, 406, 571,
FONT_NEXT, 378, 618,
FONT_NEXT, 352, 639,
FONT_NEXT, 316, 657,
FONT_NEXT, 274, 671,
FONT_NEXT, 231, 676,
FONT_NEXT, 176, 667,
FONT_NEXT, 123, 641,
FONT_NEXT, 83, 597,
FONT_NEXT, 72, 567,
FONT_NEXT, 68, 532,
FONT_NEXT, 78, 487,
FONT_NEXT, 93, 474,
FONT_NEXT, 118, 469,
FONT_NEXT, 135, 473,
FONT_NEXT, 148, 483,
FONT_NEXT, 157, 510,
FONT_NEXT, 119, 585,
FONT_NEXT, 127, 608,
FONT_NEXT, 149, 627,
FONT_NEXT, 179, 641,
FONT_NEXT, 212, 646,
FONT_NEXT, 261, 634,
FONT_NEXT, 295, 605,
FONT_NEXT, 315, 564,
FONT_NEXT, 322, 519,
FONT_NEXT, 310, 444,
FONT_NEXT, 285, 370,
FONT_NEXT, 257, 306,
FONT_NEXT, 249, 286,
FONT_NEXT, 239, 251,
FONT_NEXT, 227, 164,
FONT_END, 244, 164,
FONT_BEGIN, 273, 4,
FONT_NEXT, 286, 19,
FONT_NEXT, 292, 43,
FONT_NEXT, 287, 63,
FONT_NEXT, 276, 80,
FONT_NEXT, 258, 92,
FONT_NEXT, 237, 97,
FONT_NEXT, 216, 92,
FONT_NEXT, 199, 81,
FONT_NEXT, 188, 64,
FONT_NEXT, 184, 43,
FONT_NEXT, 189, 19,
FONT_NEXT, 202, 3,
FONT_END, 237, -8,
FONT_ADVANCE, 444, 0
    },
    {
64,
FONT_BEGIN, 554, 306,
FONT_NEXT, 524, 240,
FONT_NEXT, 489, 200,
FONT_NEXT, 455, 187,
FONT_NEXT, 430, 191,
FONT_NEXT, 410, 206,
FONT_NEXT, 396, 232,
FONT_NEXT, 392, 271,
FONT_NEXT, 395, 314,
FONT_NEXT, 406, 352,
FONT_NEXT, 442, 412,
FONT_NEXT, 487, 450,
FONT_NEXT, 529, 464,
FONT_NEXT, 548, 459,
FONT_NEXT, 561, 447,
FONT_NEXT, 569, 426,
FONT_END, 572, 399,
FONT_BEGIN, 601, 39,
FONT_NEXT, 550, 28,
FONT_NEXT, 490, 25,
FONT_NEXT, 427, 31,
FONT_NEXT, 371, 48,
FONT_NEXT, 321, 75,
FONT_NEXT, 279, 111,
FONT_NEXT, 245, 153,
FONT_NEXT, 220, 200,
FONT_NEXT, 205, 251,
FONT_NEXT, 200, 305,
FONT_NEXT, 205, 383,
FONT_NEXT, 222, 451,
FONT_NEXT, 248, 510,
FONT_NEXT, 283, 558,
FONT_NEXT, 324, 595,
FONT_NEXT, 372, 623,
FONT_NEXT, 424, 639,
FONT_NEXT, 481, 645,
FONT_NEXT, 545, 638,
FONT_NEXT, 603, 619,
FONT_NEXT, 652, 591,
FONT_NEXT, 693, 555,
FONT_NEXT, 726, 513,
FONT_NEXT, 749, 469,
FONT_NEXT, 764, 424,
FONT_NEXT, 769, 380,
FONT_NEXT, 760, 311,
FONT_NEXT, 734, 245,
FONT_NEXT, 693, 195,
FONT_NEXT, 667, 181,
FONT_NEXT, 639, 176,
FONT_NEXT, 613, 183,
FONT_NEXT, 603, 202,
FONT_NEXT, 603, 240,
FONT_NEXT, 668, 494,
FONT_NEXT, 599, 494,
FONT_NEXT, 589, 456,
FONT_NEXT, 565, 491,
FONT_NEXT, 546, 503,
FONT_NEXT, 518, 508,
FONT_NEXT, 470, 500,
FONT_NEXT, 430, 480,
FONT_NEXT, 396, 450,
FONT_NEXT, 368, 414,
FONT_NEXT, 332, 335,
FONT_NEXT, 323, 298,
FONT_NEXT, 321, 268,
FONT_NEXT, 323, 230,
FONT_NEXT, 332, 200,
FONT_NEXT, 358, 163,
FONT_NEXT, 390, 147,
FONT_NEXT, 417, 144,
FONT_NEXT, 457, 154,
FONT_NEXT, 492, 174,
FONT_NEXT, 517, 196,
FONT_NEXT, 532, 214,
FONT_NEXT, 534, 214,
FONT_NEXT, 538, 197,
FONT_NEXT, 553, 173,
FONT_NEXT, 581, 152,
FONT_NEXT, 623, 143,
FONT_NEXT, 665, 149,
FONT_NEXT, 702, 165,
FONT_NEXT, 734, 191,
FONT_NEXT, 760, 223,
FONT_NEXT, 796, 300,
FONT_NEXT, 809, 381,
FONT_NEXT, 802, 442,
FONT_NEXT, 783, 498,
FONT_NEXT, 753, 548,
FONT_NEXT, 714, 591,
FONT_NEXT, 665, 627,
FONT_NEXT, 609, 653,
FONT_NEXT, 547, 670,
FONT_NEXT, 481, 676,
FONT_NEXT, 408, 669,
FONT_NEXT, 340, 649,
FONT_NEXT, 278, 618,
FONT_NEXT, 224, 576,
FONT_NEXT, 179, 524,
FONT_NEXT, 145, 462,
FONT_NEXT, 123, 392,
FONT_NEXT, 116, 315,
FONT_NEXT, 121, 262,
FONT_NEXT, 137, 207,
FONT_NEXT, 165, 152,
FONT_NEXT, 205, 100,
FONT_NEXT, 257, 54,
FONT_NEXT, 322, 18,
FONT_NEXT, 399, -6,
FONT_NEXT, 491, -14,
FONT_NEXT, 547, -10,
FONT_NEXT, 601, 3,
FONT_NEXT, 700, 43,
FONT_END, 688, 73,
FONT_ADVANCE, 921, 0
    },
    {
65,
FONT_BEGIN, 677, 23,
FONT_NEXT, 656, 36,
FONT_NEXT, 637, 62,
FONT_NEXT, 616, 106,
FONT_NEXT, 367, 674,
FONT_NEXT, 347, 674,
FONT_NEXT, 139, 183,
FONT_NEXT, 104, 104,
FONT_NEXT, 84, 67,
FONT_NEXT, 66, 41,
FONT_NEXT, 39, 23,
FONT_NEXT, 15, 19,
FONT_NEXT, 15, 0,
FONT_NEXT, 213, 0,
FONT_NEXT, 213, 19,
FONT_NEXT, 171, 24,
FONT_NEXT, 152, 36,
FONT_NEXT, 145, 61,
FONT_NEXT, 153, 99,
FONT_NEXT, 199, 216,
FONT_NEXT, 461, 216,
FONT_NEXT, 502, 120,
FONT_NEXT, 521, 57,
FONT_NEXT, 515, 35,
FONT_NEXT, 499, 24,
FONT_NEXT, 451, 19,
FONT_NEXT, 451, 0,
FONT_NEXT, 706, 0,
FONT_END, 706, 19,
FONT_BEGIN, 331, 532,
FONT_NEXT, 447, 257,
FONT_END, 216, 257,
FONT_ADVANCE, 722, 0
    },
    {
66,
FONT_BEGIN, 66, 637,
FONT_NEXT, 95, 624,
FONT_NEXT, 109, 597,
FONT_NEXT, 113, 553,
FONT_NEXT, 113, 109,
FONT_NEXT, 109, 65,
FONT_NEXT, 96, 38,
FONT_NEXT, 67, 23,
FONT_NEXT, 17, 19,
FONT_NEXT, 17, 0,
FONT_NEXT, 351, 0,
FONT_NEXT, 425, 6,
FONT_NEXT, 482, 22,
FONT_NEXT, 525, 45,
FONT_NEXT, 555, 73,
FONT_NEXT, 574, 103,
FONT_NEXT, 586, 132,
FONT_NEXT, 593, 176,
FONT_NEXT, 589, 212,
FONT_NEXT, 579, 244,
FONT_NEXT, 542, 294,
FONT_NEXT, 488, 327,
FONT_NEXT, 426, 347,
FONT_NEXT, 426, 349,
FONT_NEXT, 465, 360,
FONT_NEXT, 509, 383,
FONT_NEXT, 544, 425,
FONT_NEXT, 555, 455,
FONT_NEXT, 559, 493,
FONT_NEXT, 551, 544,
FONT_NEXT, 530, 584,
FONT_NEXT, 499, 614,
FONT_NEXT, 460, 635,
FONT_NEXT, 374, 657,
FONT_NEXT, 297, 662,
FONT_NEXT, 17, 662,
FONT_END, 17, 643,
FONT_BEGIN, 276, 325,
FONT_NEXT, 319, 322,
FONT_NEXT, 377, 311,
FONT_NEXT, 414, 295,
FONT_NEXT, 447, 270,
FONT_NEXT, 469, 232,
FONT_NEXT, 478, 180,
FONT_NEXT, 472, 135,
FONT_NEXT, 458, 101,
FONT_NEXT, 436, 75,
FONT_NEXT, 409, 58,
FONT_NEXT, 348, 40,
FONT_NEXT, 291, 37,
FONT_NEXT, 254, 38,
FONT_NEXT, 231, 43,
FONT_NEXT, 218, 55,
FONT_NEXT, 215, 78,

FONT_END, 215, 326,
FONT_BEGIN, 218, 615,
FONT_NEXT, 237, 624,
FONT_NEXT, 255, 624,
FONT_NEXT, 282, 625,
FONT_NEXT, 339, 620,
FONT_NEXT, 396, 600,
FONT_NEXT, 420, 582,
FONT_NEXT, 439, 558,
FONT_NEXT, 452, 527,
FONT_NEXT, 457, 487,
FONT_NEXT, 451, 447,
FONT_NEXT, 437, 417,
FONT_NEXT, 394, 381,
FONT_NEXT, 344, 368,
FONT_NEXT, 310, 366,
FONT_NEXT, 215, 365,
FONT_END, 215, 595,
FONT_ADVANCE, 667, 0
    },
    {
67,
FONT_BEGIN, 611, 676,
FONT_NEXT, 590, 676,
FONT_NEXT, 580, 656,
FONT_NEXT, 565, 647,
FONT_NEXT, 544, 643,
FONT_NEXT, 519, 648,
FONT_NEXT, 481, 659,
FONT_NEXT, 429, 670,
FONT_NEXT, 368, 676,
FONT_NEXT, 304, 670,
FONT_NEXT, 242, 653,
FONT_NEXT, 184, 625,
FONT_NEXT, 133, 587,
FONT_NEXT, 90, 538,
FONT_NEXT, 56, 479,
FONT_NEXT, 35, 410,
FONT_NEXT, 28, 331,
FONT_NEXT, 35, 244,
FONT_NEXT, 57, 172,
FONT_NEXT, 91, 112,
FONT_NEXT, 134, 65,
FONT_NEXT, 185, 29,
FONT_NEXT, 241, 4,
FONT_NEXT, 301, -10,
FONT_NEXT, 362, -14,
FONT_NEXT, 426, -9,
FONT_NEXT, 481, 5,
FONT_NEXT, 528, 25,
FONT_NEXT, 566, 48,
FONT_NEXT, 595, 71,
FONT_NEXT, 616, 92,
FONT_NEXT, 628, 106,
FONT_NEXT, 633, 113,
FONT_NEXT, 615, 131,
FONT_NEXT, 583, 102,
FONT_NEXT, 536, 69,
FONT_NEXT, 471, 41,
FONT_NEXT, 389, 30,
FONT_NEXT, 342, 34,
FONT_NEXT, 297, 46,
FONT_NEXT, 256, 68,
FONT_NEXT, 219, 100,
FONT_NEXT, 188, 141,
FONT_NEXT, 164, 192,
FONT_NEXT, 149, 255,
FONT_NEXT, 144, 329,
FONT_NEXT, 151, 422,
FONT_NEXT, 170, 491,
FONT_NEXT, 197, 540,
FONT_NEXT, 227, 575,
FONT_NEXT, 261, 601,
FONT_NEXT, 299, 620,
FONT_NEXT, 377, 636,
FONT_NEXT, 420, 632,
FONT_NEXT, 459, 621,
FONT_NEXT, 523, 582,
FONT_NEXT, 569, 524,
FONT_NEXT, 597, 451,
FONT_END, 620, 451,
FONT_ADVANCE, 667, 0
    },
    {
68,
FONT_BEGIN, 300, 0,
FONT_NEXT, 404, 8,
FONT_NEXT, 489, 32,
FONT_NEXT, 556, 69,
FONT_NEXT, 607, 114,
FONT_NEXT, 643, 166,
FONT_NEXT, 667, 221,
FONT_NEXT, 681, 276,
FONT_NEXT, 685, 329,
FONT_NEXT, 677, 407,
FONT_NEXT, 656, 475,
FONT_NEXT, 622, 532,
FONT_NEXT, 575, 579,
FONT_NEXT, 517, 615,
FONT_NEXT, 449, 641,
FONT_NEXT, 372, 656,
FONT_NEXT, 286, 662,
FONT_NEXT, 16, 662,
FONT_NEXT, 16, 643,
FONT_NEXT, 62, 636,
FONT_NEXT, 88, 623,
FONT_NEXT, 101, 597,
FONT_NEXT, 104, 553,
FONT_NEXT, 104, 109,
FONT_NEXT, 100, 65,
FONT_NEXT, 88, 39,
FONT_NEXT, 61, 24,
FONT_NEXT, 16, 19,
FONT_END, 16, 0,
FONT_BEGIN, 211, 613,
FONT_NEXT, 225, 621,
FONT_NEXT, 253, 625,
FONT_NEXT, 335, 620,
FONT_NEXT, 397, 606,
FONT_NEXT, 444, 584,
FONT_NEXT, 483, 555,
FONT_NEXT, 526, 505,
FONT_NEXT, 555, 449,
FONT_NEXT, 571, 390,
FONT_NEXT, 576, 328,
FONT_NEXT, 573, 276,
FONT_NEXT, 565, 231,
FONT_NEXT, 536, 159,
FONT_NEXT, 494, 108,
FONT_NEXT, 445, 73,
FONT_NEXT, 392, 52,
FONT_NEXT, 341, 41,
FONT_NEXT, 297, 37,
FONT_NEXT, 266, 37,
FONT_NEXT, 236, 38,
FONT_NEXT, 218, 44,
FONT_NEXT, 208, 57,
FONT_NEXT, 206, 78,
FONT_END, 206, 586,
FONT_ADVANCE, 722, 0
    },
    {
69,
FONT_BEGIN, 569, 169,
FONT_NEXT, 533, 105,
FONT_NEXT, 489, 65,
FONT_NEXT, 430, 44,
FONT_NEXT, 350, 38,
FONT_NEXT, 300, 38,
FONT_NEXT, 263, 38,
FONT_NEXT, 237, 40,
FONT_NEXT, 219, 43,
FONT_NEXT, 203, 56,
FONT_NEXT, 201, 80,
FONT_NEXT, 201, 328,
FONT_NEXT, 355, 326,
FONT_NEXT, 405, 321,
FONT_NEXT, 436, 308,
FONT_NEXT, 454, 279,
FONT_NEXT, 465, 231,
FONT_NEXT, 488, 231,
FONT_NEXT, 488, 463,
FONT_NEXT, 465, 463,
FONT_NEXT, 455, 418,
FONT_NEXT, 439, 388,
FONT_NEXT, 408, 372,
FONT_NEXT, 355, 368,
FONT_NEXT, 201, 368,
FONT_NEXT, 201, 590,
FONT_NEXT, 203, 609,
FONT_NEXT, 209, 619,
FONT_NEXT, 234, 624,
FONT_NEXT, 369, 624,
FONT_NEXT, 437, 620,
FONT_NEXT, 481, 604,
FONT_NEXT, 506, 572,
FONT_NEXT, 521, 519,
FONT_NEXT, 546, 519,
FONT_NEXT, 543, 662,
FONT_NEXT, 12, 662,
FONT_NEXT, 12, 643,
FONT_NEXT, 52, 637,
FONT_NEXT, 79, 625,
FONT_NEXT, 94, 599,
FONT_NEXT, 99, 553,
FONT_NEXT, 99, 109,
FONT_NEXT, 94, 63,
FONT_NEXT, 79, 36,
FONT_NEXT, 52, 23,
FONT_NEXT, 12, 19,
FONT_NEXT, 12, 0,
FONT_NEXT, 552, 0,
FONT_END, 597, 169,
FONT_ADVANCE, 611, 0
    },
    {
70,
FONT_BEGIN, 479, 463,
FONT_NEXT, 456, 463,
FONT_NEXT, 446, 416,
FONT_NEXT, 428, 387,
FONT_NEXT, 396, 372,
FONT_NEXT, 346, 368,
FONT_NEXT, 201, 368,
FONT_NEXT, 201, 590,
FONT_NEXT, 202, 607,
FONT_NEXT, 207, 618,
FONT_NEXT, 233, 624,
FONT_NEXT, 369, 624,
FONT_NEXT, 437, 620,
FONT_NEXT, 481, 604,
FONT_NEXT, 506, 572,
FONT_NEXT, 521, 519,
FONT_NEXT, 546, 519,
FONT_NEXT, 543, 662,
FONT_NEXT, 12, 662,
FONT_NEXT, 12, 643,
FONT_NEXT, 52, 637,
FONT_NEXT, 79, 625,
FONT_NEXT, 94, 599,
FONT_NEXT, 99, 553,
FONT_NEXT, 99, 120,
FONT_NEXT, 95, 70,
FONT_NEXT, 83, 40,
FONT_NEXT, 57, 24,
FONT_NEXT, 12, 19,
FONT_NEXT, 12, 0,
FONT_NEXT, 292, 0,
FONT_NEXT, 292, 19,
FONT_NEXT, 247, 23,
FONT_NEXT, 219, 37,
FONT_NEXT, 205, 63,
FONT_NEXT, 201, 109,
FONT_NEXT, 201, 328,
FONT_NEXT, 346, 326,
FONT_NEXT, 393, 322,
FONT_NEXT, 425, 309,
FONT_NEXT, 444, 281,
FONT_NEXT, 456, 231,
FONT_END, 479, 231,
FONT_ADVANCE, 556, 0
    },
    {
71,
FONT_BEGIN, 454, 354,
FONT_NEXT, 454, 336,
FONT_NEXT, 496, 331,
FONT_NEXT, 523, 320,
FONT_NEXT, 537, 295,
FONT_NEXT, 542, 247,
FONT_NEXT, 542, 85,
FONT_NEXT, 530, 60,
FONT_NEXT, 499, 42,
FONT_NEXT, 455, 30,
FONT_NEXT, 405, 26,
FONT_NEXT, 349, 31,
FONT_NEXT, 299, 46,
FONT_NEXT, 254, 71,
FONT_NEXT, 217, 105,
FONT_NEXT, 187, 147,
FONT_NEXT, 164, 197,
FONT_NEXT, 150, 255,
FONT_NEXT, 146, 320,
FONT_NEXT, 152, 406,
FONT_NEXT, 170, 476,
FONT_NEXT, 197, 531,
FONT_NEXT, 231, 572,
FONT_NEXT, 269, 602,
FONT_NEXT, 310, 622,
FONT_NEXT, 388, 636,
FONT_NEXT, 436, 631,
FONT_NEXT, 477, 619,
FONT_NEXT, 539, 577,
FONT_NEXT, 581, 522,
FONT_NEXT, 607, 465,
FONT_NEXT, 630, 465,
FONT_NEXT, 622, 676,
FONT_NEXT, 600, 676,
FONT_NEXT, 591, 659,
FONT_NEXT, 577, 649,
FONT_NEXT, 553, 643,
FONT_NEXT, 525, 648,
FONT_NEXT, 489, 659,
FONT_NEXT, 440, 670,
FONT_NEXT, 374, 676,
FONT_NEXT, 298, 668,
FONT_NEXT, 231, 647,
FONT_NEXT, 172, 614,
FONT_NEXT, 123, 571,
FONT_NEXT, 84, 520,
FONT_NEXT, 55, 464,
FONT_NEXT, 38, 403,
FONT_NEXT, 32, 341,
FONT_NEXT, 37, 255,
FONT_NEXT, 53, 189,
FONT_NEXT, 79, 137,
FONT_NEXT, 115, 93,
FONT_NEXT, 174, 43,
FONT_NEXT, 245, 10,
FONT_NEXT, 320, -9,
FONT_NEXT, 396, -14,
FONT_NEXT, 480, -5,
FONT_NEXT, 558, 16,
FONT_NEXT, 616, 40,
FONT_NEXT, 633, 50,
FONT_NEXT, 639, 58,
FONT_NEXT, 639, 259,
FONT_NEXT, 643, 300,
FONT_NEXT, 657, 322,
FONT_NEXT, 679, 332,
FONT_NEXT, 709, 336,
FONT_END, 709, 354,
FONT_ADVANCE, 722, 0
    },
    {
72,
FONT_BEGIN, 297, 0,
FONT_NEXT, 297, 19,
FONT_NEXT, 250, 25,
FONT_NEXT, 223, 41,
FONT_NEXT, 211, 67,
FONT_NEXT, 209, 109,
FONT_NEXT, 209, 315,
FONT_NEXT, 512, 315,
FONT_NEXT, 512, 120,
FONT_NEXT, 510, 73,
FONT_NEXT, 499, 42,
FONT_NEXT, 473, 25,
FONT_NEXT, 424, 19,
FONT_NEXT, 424, 0,
FONT_NEXT, 702, 0,
FONT_NEXT, 702, 19,
FONT_NEXT, 655, 25,
FONT_NEXT, 628, 41,
FONT_NEXT, 616, 67,
FONT_NEXT, 614, 109,
FONT_NEXT, 614, 553,
FONT_NEXT, 617, 596,
FONT_NEXT, 629, 622,
FONT_NEXT, 656, 636,
FONT_NEXT, 702, 643,
FONT_NEXT, 702, 662,
FONT_NEXT, 424, 662,
FONT_NEXT, 424, 643,
FONT_NEXT, 468, 636,
FONT_NEXT, 495, 623,
FONT_NEXT, 508, 597,
FONT_NEXT, 512, 553,
FONT_NEXT, 512, 359,
FONT_NEXT, 209, 359,
FONT_NEXT, 209, 553,
FONT_NEXT, 212, 596,
FONT_NEXT, 224, 622,
FONT_NEXT, 251, 636,
FONT_NEXT, 297, 643,
FONT_NEXT, 297, 662,
FONT_NEXT, 19, 662,
FONT_NEXT, 19, 643,
FONT_NEXT, 63, 636,
FONT_NEXT, 90, 623,
FONT_NEXT, 103, 597,
FONT_NEXT, 107, 553,
FONT_NEXT, 107, 120,
FONT_NEXT, 105, 73,
FONT_NEXT, 94, 42,
FONT_NEXT, 68, 25,
FONT_NEXT, 19, 19,
FONT_END, 19, 0,
FONT_ADVANCE, 722, 0
    },
    {
73,
FONT_BEGIN, 315, 0,
FONT_NEXT, 315, 19,
FONT_NEXT, 268, 22,
FONT_NEXT, 238, 35,
FONT_NEXT, 221, 62,
FONT_NEXT, 217, 109,
FONT_NEXT, 217, 553,
FONT_NEXT, 222, 600,
FONT_NEXT, 238, 626,
FONT_NEXT, 268, 638,
FONT_NEXT, 315, 643,
FONT_NEXT, 315, 662,
FONT_NEXT, 18, 662,
FONT_NEXT, 18, 643,
FONT_NEXT, 68, 637,
FONT_NEXT, 97, 624,
FONT_NEXT, 111, 597,
FONT_NEXT, 115, 553,
FONT_NEXT, 115, 109,
FONT_NEXT, 111, 65,
FONT_NEXT, 98, 38,
FONT_NEXT, 69, 23,
FONT_NEXT, 18, 19,
FONT_END, 18, 0,
FONT_ADVANCE, 333, 0
    },
    {
74,
FONT_BEGIN, 281, 597,
FONT_NEXT, 294, 623,
FONT_NEXT, 322, 637,
FONT_NEXT, 370, 643,
FONT_NEXT, 370, 662,
FONT_NEXT, 83, 662,
FONT_NEXT, 83, 643,
FONT_NEXT, 131, 636,
FONT_NEXT, 159, 622,
FONT_NEXT, 172, 596,
FONT_NEXT, 176, 553,
FONT_NEXT, 176, 90,
FONT_NEXT, 173, 57,
FONT_NEXT, 165, 37,
FONT_NEXT, 153, 27,
FONT_NEXT, 137, 24,
FONT_NEXT, 117, 37,
FONT_NEXT, 107, 66,
FONT_NEXT, 92, 94,
FONT_NEXT, 77, 104,
FONT_NEXT, 56, 108,
FONT_NEXT, 36, 103,
FONT_NEXT, 22, 90,
FONT_NEXT, 10, 59,
FONT_NEXT, 15, 36,
FONT_NEXT, 32, 11,
FONT_NEXT, 59, -6,
FONT_NEXT, 110, -14,
FONT_NEXT, 124, -14,
FONT_NEXT, 147, -11,
FONT_NEXT, 204, 11,
FONT_NEXT, 231, 35,
FONT_NEXT, 255, 70,
FONT_NEXT, 271, 118,
FONT_NEXT, 278, 183,
FONT_END, 278, 553,
FONT_ADVANCE, 389, 0
    },
    {
75,
FONT_BEGIN, 444, 641,
FONT_NEXT, 465, 636,
FONT_NEXT, 477, 627,
FONT_NEXT, 481, 612,
FONT_NEXT, 472, 584,
FONT_NEXT, 459, 567,
FONT_NEXT, 438, 543,
FONT_NEXT, 406, 511,
FONT_NEXT, 361, 470,
FONT_NEXT, 301, 416,
FONT_NEXT, 226, 348,
FONT_NEXT, 226, 553,
FONT_NEXT, 229, 597,
FONT_NEXT, 242, 623,
FONT_NEXT, 270, 637,
FONT_NEXT, 318, 643,
FONT_NEXT, 318, 662,
FONT_NEXT, 34, 662,
FONT_NEXT, 34, 643,
FONT_NEXT, 76, 637,
FONT_NEXT, 104, 625,
FONT_NEXT, 119, 599,
FONT_NEXT, 124, 553,
FONT_NEXT, 124, 120,
FONT_NEXT, 120, 70,
FONT_NEXT, 108, 40,
FONT_NEXT, 80, 24,
FONT_NEXT, 34, 19,
FONT_NEXT, 34, 0,
FONT_NEXT, 316, 0,
FONT_NEXT, 316, 19,
FONT_NEXT, 269, 24,
FONT_NEXT, 241, 39,
FONT_NEXT, 229, 65,
FONT_NEXT, 226, 109,
FONT_NEXT, 226, 296,
FONT_NEXT, 252, 317,
FONT_NEXT, 358, 212,
FONT_NEXT, 392, 175,
FONT_NEXT, 435, 126,
FONT_NEXT, 472, 78,
FONT_NEXT, 483, 58,
FONT_NEXT, 488, 44,
FONT_NEXT, 484, 31,
FONT_NEXT, 472, 24,
FONT_NEXT, 451, 20,
FONT_NEXT, 418, 19,
FONT_NEXT, 418, 0,
FONT_NEXT, 723, 0,
FONT_NEXT, 723, 19,
FONT_NEXT, 683, 24,
FONT_NEXT, 647, 45,
FONT_NEXT, 609, 79,
FONT_NEXT, 566, 127,
FONT_NEXT, 333, 377,
FONT_NEXT, 523, 565,
FONT_NEXT, 568, 606,
FONT_NEXT, 603, 628,
FONT_NEXT, 635, 638,
FONT_NEXT, 675, 643,
FONT_NEXT, 675, 662,
FONT_NEXT, 413, 662,
FONT_END, 413, 643,
FONT_ADVANCE, 722, 0
    },
    {
76,
FONT_BEGIN, 12, 662,
FONT_NEXT, 12, 643,
FONT_NEXT, 52, 637,
FONT_NEXT, 79, 625,
FONT_NEXT, 94, 599,
FONT_NEXT, 99, 553,
FONT_NEXT, 99, 109,
FONT_NEXT, 94, 63,
FONT_NEXT, 79, 36,
FONT_NEXT, 52, 23,
FONT_NEXT, 12, 19,
FONT_NEXT, 12, 0,
FONT_NEXT, 550, 0,
FONT_NEXT, 598, 174,
FONT_NEXT, 573, 174,
FONT_NEXT, 551, 130,
FONT_NEXT, 527, 97,
FONT_NEXT, 501, 73,
FONT_NEXT, 472, 57,
FONT_NEXT, 399, 41,
FONT_NEXT, 302, 39,
FONT_NEXT, 251, 39,
FONT_NEXT, 220, 44,
FONT_NEXT, 205, 56,
FONT_NEXT, 201, 80,
FONT_NEXT, 201, 553,
FONT_NEXT, 205, 599,
FONT_NEXT, 222, 626,
FONT_NEXT, 250, 638,
FONT_NEXT, 294, 643,
FONT_END, 294, 662,
FONT_ADVANCE, 611, 0
    },
    {
77,
FONT_BEGIN, 664, 662,
FONT_NEXT, 443, 157,
FONT_NEXT, 212, 662,
FONT_NEXT, 14, 662,
FONT_NEXT, 14, 643,
FONT_NEXT, 60, 638,
FONT_NEXT, 89, 625,
FONT_NEXT, 104, 599,
FONT_NEXT, 109, 553,
FONT_NEXT, 109, 147,
FONT_NEXT, 107, 110,
FONT_NEXT, 104, 82,
FONT_NEXT, 89, 44,
FONT_NEXT, 59, 25,
FONT_NEXT, 12, 19,
FONT_NEXT, 12, 0,
FONT_NEXT, 247, 0,
FONT_NEXT, 247, 19,
FONT_NEXT, 201, 25,
FONT_NEXT, 172, 45,
FONT_NEXT, 157, 83,
FONT_NEXT, 153, 147,
FONT_NEXT, 153, 546,
FONT_NEXT, 155, 546,
FONT_NEXT, 404, 0,
FONT_NEXT, 418, 0,
FONT_NEXT, 672, 569,
FONT_NEXT, 674, 569,
FONT_NEXT, 674, 120,
FONT_NEXT, 670, 70,
FONT_NEXT, 657, 40,
FONT_NEXT, 630, 24,
FONT_NEXT, 583, 19,
FONT_NEXT, 583, 0,
FONT_NEXT, 863, 0,
FONT_NEXT, 863, 19,
FONT_NEXT, 822, 23,
FONT_NEXT, 795, 36,
FONT_NEXT, 780, 63,
FONT_NEXT, 776, 109,
FONT_NEXT, 776, 553,
FONT_NEXT, 780, 599,
FONT_NEXT, 795, 625,
FONT_NEXT, 822, 637,
FONT_NEXT, 863, 643,
FONT_END, 863, 662,
FONT_ADVANCE, 889, 0
    },
    {
78,
FONT_BEGIN, 472, 662,
FONT_NEXT, 472, 643,
FONT_NEXT, 515, 637,
FONT_NEXT, 545, 620,
FONT_NEXT, 562, 582,
FONT_NEXT, 566, 553,
FONT_NEXT, 568, 515,
FONT_NEXT, 568, 181,
FONT_NEXT, 566, 181,
FONT_NEXT, 183, 662,
FONT_NEXT, 12, 662,
FONT_NEXT, 12, 643,
FONT_NEXT, 60, 634,
FONT_NEXT, 82, 617,
FONT_NEXT, 109, 588,
FONT_NEXT, 109, 147,
FONT_NEXT, 107, 110,
FONT_NEXT, 104, 82,
FONT_NEXT, 89, 44,
FONT_NEXT, 59, 25,
FONT_NEXT, 12, 19,
FONT_NEXT, 12, 0,
FONT_NEXT, 247, 0,
FONT_NEXT, 247, 19,
FONT_NEXT, 201, 25,
FONT_NEXT, 172, 45,
FONT_NEXT, 157, 83,
FONT_NEXT, 153, 147,
FONT_NEXT, 153, 537,
FONT_NEXT, 155, 537,
FONT_NEXT, 595, -11,
FONT_NEXT, 612, -11,
FONT_NEXT, 612, 515,
FONT_NEXT, 613, 553,
FONT_NEXT, 617, 583,
FONT_NEXT, 635, 620,
FONT_NEXT, 665, 636,
FONT_NEXT, 707, 643,
FONT_END, 707, 662,
FONT_ADVANCE, 722, 0
    },
    {
79,
FONT_BEGIN, 427, -8,
FONT_NEXT, 488, 9,
FONT_NEXT, 544, 38,
FONT_NEXT, 592, 78,
FONT_NEXT, 632, 127,
FONT_NEXT, 662, 186,
FONT_NEXT, 681, 254,
FONT_NEXT, 688, 330,
FONT_NEXT, 680, 410,
FONT_NEXT, 659, 481,
FONT_NEXT, 627, 540,
FONT_NEXT, 586, 589,
FONT_NEXT, 536, 627,
FONT_NEXT, 481, 654,
FONT_NEXT, 422, 670,
FONT_NEXT, 361, 676,
FONT_NEXT, 299, 670,
FONT_NEXT, 240, 654,
FONT_NEXT, 185, 627,
FONT_NEXT, 136, 589,
FONT_NEXT, 94, 540,
FONT_NEXT, 62, 481,
FONT_NEXT, 41, 410,
FONT_NEXT, 34, 330,
FONT_NEXT, 40, 254,
FONT_NEXT, 59, 186,
FONT_NEXT, 89, 127,
FONT_NEXT, 129, 78,
FONT_NEXT, 177, 38,
FONT_NEXT, 233, 9,
FONT_NEXT, 294, -8,
FONT_END, 361, -14,
FONT_BEGIN, 319, 26,
FONT_NEXT, 280, 39,
FONT_NEXT, 244, 61,
FONT_NEXT, 212, 94,
FONT_NEXT, 186, 136,
FONT_NEXT, 165, 189,
FONT_NEXT, 152, 254,
FONT_NEXT, 148, 331,
FONT_NEXT, 152, 409,
FONT_NEXT, 166, 475,
FONT_NEXT, 188, 528,
FONT_NEXT, 216, 571,
FONT_NEXT, 248, 602,
FONT_NEXT, 284, 623,
FONT_NEXT, 322, 636,
FONT_NEXT, 361, 640,
FONT_NEXT, 399, 636,
FONT_NEXT, 437, 623,
FONT_NEXT, 473, 602,
FONT_NEXT, 505, 571,
FONT_NEXT, 533, 528,
FONT_NEXT, 555, 475,
FONT_NEXT, 569, 409,
FONT_NEXT, 574, 331,
FONT_NEXT, 569, 254,
FONT_NEXT, 556, 189,
FONT_NEXT, 535, 136,
FONT_NEXT, 509, 94,
FONT_NEXT, 477, 61,
FONT_NEXT, 441, 39,
FONT_NEXT, 402, 26,
FONT_END, 361, 22,
FONT_ADVANCE, 722, 0
    },
    {
80,
FONT_BEGIN, 59, 635,
FONT_NEXT, 85, 622,
FONT_NEXT, 97, 596,
FONT_NEXT, 100, 553,
FONT_NEXT, 100, 120,
FONT_NEXT, 98, 74,
FONT_NEXT, 89, 44,
FONT_NEXT, 64, 27,
FONT_NEXT, 16, 19,
FONT_NEXT, 16, 0,
FONT_NEXT, 296, 0,
FONT_NEXT, 296, 19,
FONT_NEXT, 248, 23,
FONT_NEXT, 220, 37,
FONT_NEXT, 205, 64,
FONT_NEXT, 202, 109,
FONT_NEXT, 202, 291,
FONT_NEXT, 269, 288,
FONT_NEXT, 340, 291,
FONT_NEXT, 398, 302,
FONT_NEXT, 444, 320,
FONT_NEXT, 481, 347,
FONT_NEXT, 497, 363,
FONT_NEXT, 517, 391,
FONT_NEXT, 534, 430,
FONT_NEXT, 542, 482,
FONT_NEXT, 536, 526,
FONT_NEXT, 520, 563,
FONT_NEXT, 495, 594,
FONT_NEXT, 462, 619,
FONT_NEXT, 379, 651,
FONT_NEXT, 280, 662,
FONT_NEXT, 16, 662,
FONT_END, 16, 643,
FONT_BEGIN, 204, 610,
FONT_NEXT, 211, 620,
FONT_NEXT, 243, 625,
FONT_NEXT, 291, 622,
FONT_NEXT, 354, 606,
FONT_NEXT, 384, 588,
FONT_NEXT, 409, 562,
FONT_NEXT, 426, 527,
FONT_NEXT, 433, 480,
FONT_NEXT, 427, 429,
FONT_NEXT, 411, 392,
FONT_NEXT, 388, 365,
FONT_NEXT, 361, 347,
FONT_NEXT, 304, 330,
FONT_NEXT, 262, 328,
FONT_NEXT, 202, 331,
FONT_END, 202, 591,
FONT_ADVANCE, 556, 0
    },
    {
81,
FONT_BEGIN, 638, -153,
FONT_NEXT, 586, -138,
FONT_NEXT, 505, -90,
FONT_NEXT, 426, -7,
FONT_NEXT, 468, 2,
FONT_NEXT, 512, 20,
FONT_NEXT, 556, 47,
FONT_NEXT, 597, 83,
FONT_NEXT, 633, 130,
FONT_NEXT, 662, 186,
FONT_NEXT, 681, 252,
FONT_NEXT, 688, 330,
FONT_NEXT, 680, 410,
FONT_NEXT, 659, 481,
FONT_NEXT, 627, 540,
FONT_NEXT, 586, 589,
FONT_NEXT, 536, 627,
FONT_NEXT, 481, 654,
FONT_NEXT, 422, 670,
FONT_NEXT, 361, 676,
FONT_NEXT, 299, 670,
FONT_NEXT, 240, 654,
FONT_NEXT, 185, 626,
FONT_NEXT, 136, 588,
FONT_NEXT, 94, 540,
FONT_NEXT, 62, 480,
FONT_NEXT, 41, 410,
FONT_NEXT, 34, 330,
FONT_NEXT, 40, 255,
FONT_NEXT, 57, 191,
FONT_NEXT, 82, 138,
FONT_NEXT, 114, 94,
FONT_NEXT, 151, 58,
FONT_NEXT, 189, 31,
FONT_NEXT, 265, -1,
FONT_NEXT, 312, -56,
FONT_NEXT, 379, -113,
FONT_NEXT, 462, -151,
FONT_NEXT, 555, -172,
FONT_NEXT, 654, -178,
FONT_NEXT, 701, -177,
FONT_END, 701, -159,
FONT_BEGIN, 399, 636,
FONT_NEXT, 437, 623,
FONT_NEXT, 473, 602,
FONT_NEXT, 505, 570,
FONT_NEXT, 533, 528,
FONT_NEXT, 555, 475,
FONT_NEXT, 569, 409,
FONT_NEXT, 574, 330,
FONT_NEXT, 569, 253,
FONT_NEXT, 556, 189,
FONT_NEXT, 535, 136,
FONT_NEXT, 509, 93,
FONT_NEXT, 477, 61,
FONT_NEXT, 441, 39,
FONT_NEXT, 402, 26,
FONT_NEXT, 361, 22,
FONT_NEXT, 319, 26,
FONT_NEXT, 280, 39,
FONT_NEXT, 244, 61,
FONT_NEXT, 212, 93,
FONT_NEXT, 186, 136,
FONT_NEXT, 165, 189,
FONT_NEXT, 152, 253,
FONT_NEXT, 148, 330,
FONT_NEXT, 152, 409,
FONT_NEXT, 166, 475,
FONT_NEXT, 188, 528,
FONT_NEXT, 216, 570,
FONT_NEXT, 248, 602,
FONT_NEXT, 284, 623,
FONT_NEXT, 322, 636,
FONT_END, 361, 640,
FONT_ADVANCE, 722, 0
    },
    {
82,
FONT_BEGIN, 608, 33,
FONT_NEXT, 572, 66,
FONT_NEXT, 366, 319,
FONT_NEXT, 415, 330,
FONT_NEXT, 475, 356,
FONT_NEXT, 503, 377,
FONT_NEXT, 525, 405,
FONT_NEXT, 541, 441,
FONT_NEXT, 547, 487,
FONT_NEXT, 539, 538,
FONT_NEXT, 519, 579,
FONT_NEXT, 490, 610,
FONT_NEXT, 453, 632,
FONT_NEXT, 370, 656,
FONT_NEXT, 293, 662,
FONT_NEXT, 17, 662,
FONT_NEXT, 17, 643,
FONT_NEXT, 59, 636,
FONT_NEXT, 85, 623,
FONT_NEXT, 98, 597,
FONT_NEXT, 102, 553,
FONT_NEXT, 102, 120,
FONT_NEXT, 100, 73,
FONT_NEXT, 89, 43,
FONT_NEXT, 64, 26,
FONT_NEXT, 17, 19,
FONT_NEXT, 17, 0,
FONT_NEXT, 294, 0,
FONT_NEXT, 294, 19,
FONT_NEXT, 246, 25,
FONT_NEXT, 219, 40,
FONT_NEXT, 206, 67,
FONT_NEXT, 204, 109,
FONT_NEXT, 204, 306,
FONT_NEXT, 260, 308,
FONT_NEXT, 498, 0,
FONT_NEXT, 659, 0,
FONT_END, 659, 19,
FONT_BEGIN, 206, 606,
FONT_NEXT, 215, 617,
FONT_NEXT, 233, 623,
FONT_NEXT, 266, 625,
FONT_NEXT, 310, 622,
FONT_NEXT, 367, 606,
FONT_NEXT, 394, 590,
FONT_NEXT, 416, 566,
FONT_NEXT, 432, 533,
FONT_NEXT, 438, 489,
FONT_NEXT, 431, 441,
FONT_NEXT, 413, 406,
FONT_NEXT, 386, 380,
FONT_NEXT, 352, 362,
FONT_NEXT, 276, 346,
FONT_NEXT, 204, 343,
FONT_END, 204, 589,
FONT_ADVANCE, 667, 0
    },
    {
83,
FONT_BEGIN, 426, 676,
FONT_NEXT, 418, 654,
FONT_NEXT, 408, 645,
FONT_NEXT, 391, 642,
FONT_NEXT, 367, 647,
FONT_NEXT, 334, 659,
FONT_NEXT, 252, 676,
FONT_NEXT, 185, 665,
FONT_NEXT, 127, 633,
FONT_NEXT, 86, 580,
FONT_NEXT, 75, 546,
FONT_NEXT, 71, 506,
FONT_NEXT, 82, 441,
FONT_NEXT, 114, 390,
FONT_NEXT, 163, 349,
FONT_NEXT, 227, 310,
FONT_NEXT, 276, 280,
FONT_NEXT, 314, 254,
FONT_NEXT, 343, 231,
FONT_NEXT, 363, 210,
FONT_NEXT, 385, 172,
FONT_NEXT, 390, 133,
FONT_NEXT, 382, 92,
FONT_NEXT, 360, 57,
FONT_NEXT, 322, 31,
FONT_NEXT, 270, 22,
FONT_NEXT, 223, 27,
FONT_NEXT, 183, 43,
FONT_NEXT, 124, 92,
FONT_NEXT, 86, 151,
FONT_NEXT, 65, 199,
FONT_NEXT, 42, 199,
FONT_NEXT, 72, -13,
FONT_NEXT, 94, -13,
FONT_NEXT, 101, 9,
FONT_NEXT, 125, 20,
FONT_NEXT, 151, 14,
FONT_NEXT, 185, 3,
FONT_NEXT, 227, -9,
FONT_NEXT, 280, -14,
FONT_NEXT, 330, -10,
FONT_NEXT, 373, 2,
FONT_NEXT, 409, 20,
FONT_NEXT, 439, 44,
FONT_NEXT, 478, 102,
FONT_NEXT, 491, 167,
FONT_NEXT, 476, 235,
FONT_NEXT, 438, 290,
FONT_NEXT, 385, 336,
FONT_NEXT, 324, 375,
FONT_NEXT, 209, 448,
FONT_NEXT, 171, 491,
FONT_NEXT, 157, 542,
FONT_NEXT, 165, 582,
FONT_NEXT, 188, 611,
FONT_NEXT, 221, 629,
FONT_NEXT, 258, 635,
FONT_NEXT, 295, 630,
FONT_NEXT, 328, 619,
FONT_NEXT, 384, 579,
FONT_NEXT, 422, 524,
FONT_NEXT, 444, 463,
FONT_NEXT, 469, 463,
FONT_END, 447, 676,
FONT_ADVANCE, 556, 0
    },
    {
84,
FONT_BEGIN, 401, 24,
FONT_NEXT, 372, 39,
FONT_NEXT, 359, 66,
FONT_NEXT, 356, 109,
FONT_NEXT, 356, 620,
FONT_NEXT, 410, 620,
FONT_NEXT, 478, 615,
FONT_NEXT, 522, 597,
FONT_NEXT, 550, 558,
FONT_NEXT, 569, 492,
FONT_NEXT, 593, 492,
FONT_NEXT, 587, 662,
FONT_NEXT, 23, 662,
FONT_NEXT, 17, 492,
FONT_NEXT, 41, 492,
FONT_NEXT, 59, 557,
FONT_NEXT, 87, 596,
FONT_NEXT, 131, 615,
FONT_NEXT, 200, 620,
FONT_NEXT, 254, 620,
FONT_NEXT, 254, 120,
FONT_NEXT, 252, 73,
FONT_NEXT, 241, 43,
FONT_NEXT, 213, 26,
FONT_NEXT, 160, 19,
FONT_NEXT, 160, 0,
FONT_NEXT, 452, 0,
FONT_END, 452, 19,
FONT_ADVANCE, 611, 0
    },
    {
85,
FONT_BEGIN, 473, 662,
FONT_NEXT, 473, 643,
FONT_NEXT, 517, 636,
FONT_NEXT, 546, 617,
FONT_NEXT, 562, 579,
FONT_NEXT, 567, 515,
FONT_NEXT, 567, 245,
FONT_NEXT, 565, 192,
FONT_NEXT, 560, 154,
FONT_NEXT, 544, 109,
FONT_NEXT, 517, 76,
FONT_NEXT, 480, 51,
FONT_NEXT, 434, 35,
FONT_NEXT, 380, 30,
FONT_NEXT, 322, 36,
FONT_NEXT, 279, 52,
FONT_NEXT, 248, 77,
FONT_NEXT, 227, 107,
FONT_NEXT, 208, 174,
FONT_NEXT, 206, 233,
FONT_NEXT, 206, 553,
FONT_NEXT, 209, 596,
FONT_NEXT, 221, 622,
FONT_NEXT, 249, 636,
FONT_NEXT, 297, 643,
FONT_NEXT, 297, 662,
FONT_NEXT, 14, 662,
FONT_NEXT, 14, 643,
FONT_NEXT, 60, 637,
FONT_NEXT, 87, 623,
FONT_NEXT, 100, 597,
FONT_NEXT, 104, 553,
FONT_NEXT, 104, 241,
FONT_NEXT, 104, 212,
FONT_NEXT, 107, 175,
FONT_NEXT, 117, 133,
FONT_NEXT, 135, 91,
FONT_NEXT, 164, 50,
FONT_NEXT, 208, 17,
FONT_NEXT, 270, -6,
FONT_NEXT, 352, -14,
FONT_NEXT, 437, -6,
FONT_NEXT, 501, 16,
FONT_NEXT, 547, 49,
FONT_NEXT, 578, 90,
FONT_NEXT, 597, 134,
FONT_NEXT, 606, 178,
FONT_NEXT, 611, 254,
FONT_NEXT, 611, 515,
FONT_NEXT, 614, 578,
FONT_NEXT, 629, 615,
FONT_NEXT, 657, 634,
FONT_NEXT, 705, 643,
FONT_END, 705, 662,
FONT_ADVANCE, 722, 0
    },
    {
86,
FONT_BEGIN, 492, 662,
FONT_NEXT, 492, 643,
FONT_NEXT, 542, 634,
FONT_NEXT, 558, 621,
FONT_NEXT, 565, 597,
FONT_NEXT, 556, 556,
FONT_NEXT, 549, 538,
FONT_NEXT, 546, 528,
FONT_NEXT, 399, 161,
FONT_NEXT, 248, 499,
FONT_NEXT, 218, 566,
FONT_NEXT, 210, 590,
FONT_NEXT, 207, 606,
FONT_NEXT, 210, 624,
FONT_NEXT, 223, 635,
FONT_NEXT, 246, 640,
FONT_NEXT, 282, 643,
FONT_NEXT, 282, 662,
FONT_NEXT, 16, 662,
FONT_NEXT, 16, 643,
FONT_NEXT, 46, 638,
FONT_NEXT, 71, 623,
FONT_NEXT, 94, 592,
FONT_NEXT, 122, 538,
FONT_NEXT, 368, -11,
FONT_NEXT, 383, -11,
FONT_NEXT, 605, 550,
FONT_NEXT, 625, 596,
FONT_NEXT, 644, 623,
FONT_NEXT, 666, 637,
FONT_NEXT, 697, 643,
FONT_END, 697, 662,
FONT_ADVANCE, 722, 0
    },
    {
87,
FONT_BEGIN, 734, 662,
FONT_NEXT, 734, 643,
FONT_NEXT, 781, 633,
FONT_NEXT, 797, 620,
FONT_NEXT, 803, 597,
FONT_NEXT, 787, 525,
FONT_NEXT, 662, 186,
FONT_NEXT, 530, 527,
FONT_NEXT, 510, 574,
FONT_NEXT, 503, 605,
FONT_NEXT, 510, 626,
FONT_NEXT, 529, 637,
FONT_NEXT, 580, 643,
FONT_NEXT, 580, 662,
FONT_NEXT, 313, 662,
FONT_NEXT, 313, 643,
FONT_NEXT, 346, 640,
FONT_NEXT, 371, 628,
FONT_NEXT, 391, 601,
FONT_NEXT, 414, 553,
FONT_NEXT, 447, 471,
FONT_NEXT, 340, 189,
FONT_NEXT, 196, 565,
FONT_NEXT, 185, 609,
FONT_NEXT, 188, 625,
FONT_NEXT, 200, 635,
FONT_NEXT, 220, 640,
FONT_NEXT, 250, 643,
FONT_NEXT, 250, 662,
FONT_NEXT, 5, 662,
FONT_NEXT, 5, 643,
FONT_NEXT, 40, 634,
FONT_NEXT, 65, 613,
FONT_NEXT, 86, 578,
FONT_NEXT, 108, 526,
FONT_NEXT, 125, 480,
FONT_NEXT, 147, 421,
FONT_NEXT, 199, 277,
FONT_NEXT, 254, 123,
FONT_NEXT, 279, 51,
FONT_NEXT, 301, -11,
FONT_NEXT, 316, -11,
FONT_NEXT, 470, 412,
FONT_NEXT, 630, -11,
FONT_NEXT, 645, -11,
FONT_NEXT, 745, 288,
FONT_NEXT, 853, 582,
FONT_NEXT, 864, 606,
FONT_NEXT, 879, 624,
FONT_NEXT, 900, 635,
FONT_NEXT, 932, 643,
FONT_END, 932, 662,
FONT_ADVANCE, 944, 0
    },
    {
88,
FONT_BEGIN, 458, 662,
FONT_NEXT, 458, 643,
FONT_NEXT, 505, 637,
FONT_NEXT, 521, 628,
FONT_NEXT, 528, 610,
FONT_NEXT, 516, 579,
FONT_NEXT, 488, 542,
FONT_NEXT, 375, 404,
FONT_NEXT, 333, 463,
FONT_NEXT, 291, 523,
FONT_NEXT, 265, 565,
FONT_NEXT, 251, 592,
FONT_NEXT, 248, 611,
FONT_NEXT, 252, 628,
FONT_NEXT, 266, 637,
FONT_NEXT, 290, 641,
FONT_NEXT, 324, 643,
FONT_NEXT, 324, 662,
FONT_NEXT, 22, 662,
FONT_NEXT, 22, 643,
FONT_NEXT, 60, 637,
FONT_NEXT, 96, 618,
FONT_NEXT, 116, 600,
FONT_NEXT, 139, 573,
FONT_NEXT, 168, 535,
FONT_NEXT, 203, 486,
FONT_NEXT, 312, 326,
FONT_NEXT, 155, 133,
FONT_NEXT, 128, 100,
FONT_NEXT, 106, 74,
FONT_NEXT, 73, 41,
FONT_NEXT, 44, 24,
FONT_NEXT, 10, 19,
FONT_NEXT, 10, 0,
FONT_NEXT, 243, 0,
FONT_NEXT, 243, 19,
FONT_NEXT, 190, 26,
FONT_NEXT, 173, 37,
FONT_NEXT, 167, 56,
FONT_NEXT, 172, 73,
FONT_NEXT, 185, 95,
FONT_NEXT, 219, 140,
FONT_NEXT, 338, 288,
FONT_NEXT, 433, 148,
FONT_NEXT, 464, 98,
FONT_NEXT, 478, 72,
FONT_NEXT, 484, 53,
FONT_NEXT, 479, 35,
FONT_NEXT, 464, 25,
FONT_NEXT, 440, 21,
FONT_NEXT, 407, 19,
FONT_NEXT, 407, 0,
FONT_NEXT, 704, 0,
FONT_NEXT, 704, 19,
FONT_NEXT, 670, 24,
FONT_NEXT, 643, 37,
FONT_NEXT, 619, 58,
FONT_NEXT, 593, 93,
FONT_NEXT, 401, 367,
FONT_NEXT, 547, 549,
FONT_NEXT, 592, 599,
FONT_NEXT, 627, 627,
FONT_NEXT, 660, 638,
FONT_NEXT, 696, 643,
FONT_END, 696, 662,
FONT_ADVANCE, 722, 0
    },
    {
89,
FONT_BEGIN, 484, 662,
FONT_NEXT, 484, 643,
FONT_NEXT, 530, 638,
FONT_NEXT, 546, 628,
FONT_NEXT, 553, 610,
FONT_NEXT, 539, 573,
FONT_NEXT, 396, 347,
FONT_NEXT, 248, 569,
FONT_NEXT, 231, 612,
FONT_NEXT, 235, 628,
FONT_NEXT, 249, 637,
FONT_NEXT, 271, 641,
FONT_NEXT, 302, 643,
FONT_NEXT, 302, 662,
FONT_NEXT, 22, 662,
FONT_NEXT, 22, 643,
FONT_NEXT, 51, 637,
FONT_NEXT, 81, 619,
FONT_NEXT, 99, 600,
FONT_NEXT, 121, 573,
FONT_NEXT, 149, 535,
FONT_NEXT, 184, 486,
FONT_NEXT, 315, 294,
FONT_NEXT, 315, 120,
FONT_NEXT, 313, 72,
FONT_NEXT, 301, 41,
FONT_NEXT, 271, 24,
FONT_NEXT, 246, 20,
FONT_NEXT, 214, 19,
FONT_NEXT, 214, 0,
FONT_NEXT, 520, 0,
FONT_NEXT, 520, 19,
FONT_NEXT, 465, 23,
FONT_NEXT, 434, 37,
FONT_NEXT, 420, 64,
FONT_NEXT, 417, 109,
FONT_NEXT, 417, 303,
FONT_NEXT, 565, 529,
FONT_NEXT, 589, 564,
FONT_NEXT, 610, 590,
FONT_NEXT, 646, 624,
FONT_NEXT, 675, 638,
FONT_NEXT, 703, 643,
FONT_END, 703, 662,
FONT_ADVANCE, 722, 0
    },
    {
90,
FONT_BEGIN, 556, 123,
FONT_NEXT, 528, 79,
FONT_NEXT, 481, 49,
FONT_NEXT, 446, 40,
FONT_NEXT, 402, 38,
FONT_NEXT, 145, 38,
FONT_NEXT, 577, 647,
FONT_NEXT, 577, 662,
FONT_NEXT, 51, 662,
FONT_NEXT, 31, 491,
FONT_NEXT, 57, 491,
FONT_NEXT, 68, 540,
FONT_NEXT, 92, 582,
FONT_NEXT, 112, 599,
FONT_NEXT, 140, 612,
FONT_NEXT, 177, 621,
FONT_NEXT, 225, 624,
FONT_NEXT, 446, 624,
FONT_NEXT, 9, 15,
FONT_NEXT, 9, 0,
FONT_NEXT, 573, 0,
FONT_NEXT, 597, 176,
FONT_END, 574, 176,
FONT_ADVANCE, 611, 0
    },
    {
91,
FONT_BEGIN, 299, 662,
FONT_NEXT, 88, 662,
FONT_NEXT, 88, -156,
FONT_NEXT, 299, -156,
FONT_NEXT, 299, -131,
FONT_NEXT, 213, -131,
FONT_NEXT, 184, -125,
FONT_NEXT, 170, -110,

FONT_NEXT, 164, -79,
FONT_NEXT, 164, 593,
FONT_NEXT, 169, 620,
FONT_NEXT, 182, 632,
FONT_NEXT, 209, 637,
FONT_END, 299, 637,
FONT_ADVANCE, 333, 0
    },
    {
92,
FONT_BEGIN, 219, -14,
FONT_NEXT, 287, -14,
FONT_NEXT, 58, 676,
FONT_END, -9, 676,
FONT_ADVANCE, 278, 0
    },
    {
93,
FONT_BEGIN, 34, -156,
FONT_NEXT, 245, -156,
FONT_NEXT, 245, 662,
FONT_NEXT, 34, 662,
FONT_NEXT, 34, 637,
FONT_NEXT, 120, 637,
FONT_NEXT, 148, 630,
FONT_NEXT, 162, 615,
FONT_NEXT, 169, 585,
FONT_NEXT, 169, -87,
FONT_NEXT, 163, -115,
FONT_NEXT, 150, -127,
FONT_NEXT, 124, -131,
FONT_END, 34, -131,
FONT_ADVANCE, 333, 0
    },
    {
94,
FONT_BEGIN, 235, 586,
FONT_NEXT, 378, 297,
FONT_NEXT, 446, 297,
FONT_NEXT, 265, 662,
FONT_NEXT, 205, 662,
FONT_NEXT, 24, 297,
FONT_END, 92, 297,
FONT_ADVANCE, 469, 0
    },
    {
95,
FONT_BEGIN, 500, -75,
FONT_NEXT, 0, -75,
FONT_NEXT, 0, -125,
FONT_END, 500, -125,
FONT_ADVANCE, 500, 0
    },
    {
96,
FONT_BEGIN, 196, 658,
FONT_NEXT, 159, 625,
FONT_NEXT, 128, 580,
FONT_NEXT, 115, 529,
FONT_NEXT, 124, 478,
FONT_NEXT, 146, 449,
FONT_NEXT, 173, 436,
FONT_NEXT, 196, 433,
FONT_NEXT, 234, 445,
FONT_NEXT, 248, 463,
FONT_NEXT, 254, 491,
FONT_NEXT, 247, 515,
FONT_NEXT, 232, 531,
FONT_NEXT, 192, 541,
FONT_NEXT, 177, 539,
FONT_NEXT, 168, 537,
FONT_NEXT, 158, 540,
FONT_NEXT, 154, 551,
FONT_NEXT, 157, 569,
FONT_NEXT, 170, 595,
FONT_NEXT, 195, 625,
FONT_NEXT, 236, 657,
FONT_END, 227, 676,
FONT_ADVANCE, 333, 0
    },
    {
97,
FONT_BEGIN, 281, 88,
FONT_NEXT, 271, 77,
FONT_NEXT, 253, 66,
FONT_NEXT, 226, 55,
FONT_NEXT, 191, 48,
FONT_NEXT, 165, 53,
FONT_NEXT, 144, 69,
FONT_NEXT, 130, 93,
FONT_NEXT, 125, 126,
FONT_NEXT, 125, 128,
FONT_NEXT, 129, 157,
FONT_NEXT, 149, 192,
FONT_NEXT, 169, 210,
FONT_NEXT, 197, 229,
FONT_NEXT, 236, 249,
FONT_NEXT, 287, 268,
FONT_END, 287, 123,
FONT_BEGIN, 423, 53,
FONT_NEXT, 397, 47,
FONT_NEXT, 380, 51,
FONT_NEXT, 371, 65,
FONT_NEXT, 368, 105,
FONT_NEXT, 368, 300,
FONT_NEXT, 365, 350,
FONT_NEXT, 349, 402,
FONT_NEXT, 331, 425,
FONT_NEXT, 305, 443,
FONT_NEXT, 268, 455,
FONT_NEXT, 219, 460,
FONT_NEXT, 178, 456,
FONT_NEXT, 144, 448,
FONT_NEXT, 94, 419,
FONT_NEXT, 65, 383,
FONT_NEXT, 56, 349,
FONT_NEXT, 65, 321,
FONT_NEXT, 79, 309,
FONT_NEXT, 99, 305,
FONT_NEXT, 131, 318,
FONT_NEXT, 144, 348,
FONT_NEXT, 139, 387,
FONT_NEXT, 145, 407,
FONT_NEXT, 162, 423,
FONT_NEXT, 210, 436,
FONT_NEXT, 234, 433,
FONT_NEXT, 259, 421,
FONT_NEXT, 279, 396,
FONT_NEXT, 287, 353,
FONT_NEXT, 287, 292,
FONT_NEXT, 179, 248,
FONT_NEXT, 100, 206,
FONT_NEXT, 73, 183,
FONT_NEXT, 53, 158,
FONT_NEXT, 41, 128,
FONT_NEXT, 37, 94,
FONT_NEXT, 45, 49,
FONT_NEXT, 69, 16,
FONT_NEXT, 103, -4,
FONT_NEXT, 143, -10,
FONT_NEXT, 183, -4,
FONT_NEXT, 222, 14,
FONT_NEXT, 288, 63,
FONT_NEXT, 297, 21,
FONT_NEXT, 315, -1,
FONT_NEXT, 353, -10,
FONT_NEXT, 387, -4,
FONT_NEXT, 412, 11,
FONT_NEXT, 442, 40,
FONT_END, 442, 66,
FONT_ADVANCE, 444, 0
    },
    {
98,
FONT_BEGIN, 165, 357,
FONT_NEXT, 192, 380,
FONT_NEXT, 224, 393,
FONT_NEXT, 253, 397,
FONT_NEXT, 284, 392,
FONT_NEXT, 311, 379,
FONT_NEXT, 350, 333,
FONT_NEXT, 372, 270,
FONT_NEXT, 380, 201,
FONT_NEXT, 375, 145,
FONT_NEXT, 357, 86,
FONT_NEXT, 340, 61,
FONT_NEXT, 317, 40,
FONT_NEXT, 287, 26,
FONT_NEXT, 249, 22,
FONT_NEXT, 223, 24,
FONT_NEXT, 191, 32,
FONT_NEXT, 164, 47,
FONT_NEXT, 153, 70,
FONT_END, 153, 322,
FONT_BEGIN, 73, 40,
FONT_NEXT, 86, 28,
FONT_NEXT, 127, 8,
FONT_NEXT, 178, -6,
FONT_NEXT, 224, -10,
FONT_NEXT, 287, -4,
FONT_NEXT, 340, 16,
FONT_NEXT, 382, 44,
FONT_NEXT, 415, 80,
FONT_NEXT, 439, 119,
FONT_NEXT, 455, 161,
FONT_NEXT, 468, 238,
FONT_NEXT, 457, 317,
FONT_NEXT, 424, 389,
FONT_NEXT, 399, 417,
FONT_NEXT, 368, 440,
FONT_NEXT, 332, 454,
FONT_NEXT, 290, 460,
FONT_NEXT, 237, 450,
FONT_NEXT, 196, 428,
FONT_NEXT, 167, 401,
FONT_NEXT, 155, 379,
FONT_NEXT, 153, 379,
FONT_NEXT, 153, 681,
FONT_NEXT, 148, 683,
FONT_NEXT, 3, 639,
FONT_NEXT, 3, 623,
FONT_NEXT, 25, 624,
FONT_NEXT, 50, 620,
FONT_NEXT, 63, 609,
FONT_NEXT, 69, 573,
FONT_END, 69, 54,
FONT_ADVANCE, 500, 0
    },
    {
99,
FONT_BEGIN, 340, 89,
FONT_NEXT, 302, 69,
FONT_NEXT, 255, 62,
FONT_NEXT, 195, 75,
FONT_NEXT, 147, 114,
FONT_NEXT, 114, 174,
FONT_NEXT, 102, 253,
FONT_NEXT, 106, 302,
FONT_NEXT, 116, 342,
FONT_NEXT, 133, 373,
FONT_NEXT, 152, 397,
FONT_NEXT, 195, 423,
FONT_NEXT, 231, 431,
FONT_NEXT, 262, 427,
FONT_NEXT, 280, 418,
FONT_NEXT, 297, 383,
FONT_NEXT, 303, 361,
FONT_NEXT, 320, 329,
FONT_NEXT, 334, 318,
FONT_NEXT, 352, 315,
FONT_NEXT, 386, 329,
FONT_NEXT, 398, 359,
FONT_NEXT, 387, 392,
FONT_NEXT, 355, 429,
FONT_NEXT, 306, 450,
FONT_NEXT, 249, 460,
FONT_NEXT, 168, 444,
FONT_NEXT, 130, 425,
FONT_NEXT, 96, 398,
FONT_NEXT, 67, 363,
FONT_NEXT, 44, 320,
FONT_NEXT, 30, 270,
FONT_NEXT, 25, 213,
FONT_NEXT, 29, 158,
FONT_NEXT, 42, 111,
FONT_NEXT, 63, 73,
FONT_NEXT, 88, 42,
FONT_NEXT, 149, 2,
FONT_NEXT, 212, -10,
FONT_NEXT, 269, -2,
FONT_NEXT, 322, 25,
FONT_NEXT, 369, 74,
FONT_NEXT, 412, 147,
FONT_END, 398, 156,
FONT_ADVANCE, 444, 0
    },
    {
100,
FONT_BEGIN, 328, 77,
FONT_NEXT, 308, 58,
FONT_NEXT, 281, 46,
FONT_NEXT, 251, 42,
FONT_NEXT, 201, 53,
FONT_NEXT, 157, 88,
FONT_NEXT, 139, 116,
FONT_NEXT, 125, 152,
FONT_NEXT, 116, 195,
FONT_NEXT, 113, 247,
FONT_NEXT, 116, 293,
FONT_NEXT, 124, 333,
FONT_NEXT, 154, 390,
FONT_NEXT, 194, 422,
FONT_NEXT, 237, 432,
FONT_NEXT, 271, 425,
FONT_NEXT, 303, 407,
FONT_NEXT, 327, 376,
FONT_NEXT, 340, 332,
FONT_END, 340, 102,
FONT_BEGIN, 452, 58,
FONT_NEXT, 432, 68,
FONT_NEXT, 425, 87,
FONT_NEXT, 424, 114,
FONT_NEXT, 424, 681,
FONT_NEXT, 419, 683,
FONT_NEXT, 272, 639,
FONT_NEXT, 272, 623,
FONT_NEXT, 296, 624,
FONT_NEXT, 321, 620,
FONT_NEXT, 334, 609,
FONT_NEXT, 340, 573,
FONT_NEXT, 340, 417,
FONT_NEXT, 290, 449,
FONT_NEXT, 234, 460,
FONT_NEXT, 192, 454,
FONT_NEXT, 154, 439,
FONT_NEXT, 119, 416,
FONT_NEXT, 88, 386,
FONT_NEXT, 43, 307,
FONT_NEXT, 31, 262,
FONT_NEXT, 27, 214,
FONT_NEXT, 33, 145,
FONT_NEXT, 49, 92,
FONT_NEXT, 74, 53,
FONT_NEXT, 103, 25,
FONT_NEXT, 164, -4,
FONT_NEXT, 209, -10,
FONT_NEXT, 257, -4,
FONT_NEXT, 294, 11,
FONT_NEXT, 320, 32,
FONT_NEXT, 338, 54,
FONT_NEXT, 340, 54,
FONT_NEXT, 340, -7,
FONT_NEXT, 344, -10,
FONT_NEXT, 491, 42,
FONT_END, 491, 58,
FONT_ADVANCE, 500, 0
    },
    {
101,
FONT_BEGIN, 386, 131,
FONT_NEXT, 355, 97,
FONT_NEXT, 311, 70,
FONT_NEXT, 253, 59,
FONT_NEXT, 202, 68,
FONT_NEXT, 153, 101,
FONT_NEXT, 132, 130,
FONT_NEXT, 114, 168,
FONT_NEXT, 101, 216,
FONT_NEXT, 94, 277,
FONT_NEXT, 405, 277,
FONT_NEXT, 398, 322,
FONT_NEXT, 386, 360,
FONT_NEXT, 348, 417,
FONT_NEXT, 294, 449,
FONT_NEXT, 229, 460,
FONT_NEXT, 163, 447,
FONT_NEXT, 129, 430,
FONT_NEXT, 97, 406,
FONT_NEXT, 68, 373,
FONT_NEXT, 45, 330,
FONT_NEXT, 30, 277,
FONT_NEXT, 25, 214,
FONT_NEXT, 28, 163,
FONT_NEXT, 38, 119,
FONT_NEXT, 54, 80,
FONT_NEXT, 76, 48,
FONT_NEXT, 104, 23,
FONT_NEXT, 135, 5,
FONT_NEXT, 172, -7,
FONT_NEXT, 212, -10,
FONT_NEXT, 262, -5,
FONT_NEXT, 304, 11,
FONT_NEXT, 368, 61,
FONT_NEXT, 406, 117,
FONT_NEXT, 417, 140,
FONT_NEXT, 424, 157,
FONT_END, 408, 164,
FONT_BEGIN, 113, 364,
FONT_NEXT, 137, 399,
FONT_NEXT, 168, 418,
FONT_NEXT, 207, 424,
FONT_NEXT, 254, 412,
FONT_NEXT, 282, 384,
FONT_NEXT, 303, 309,
FONT_END, 97, 309,
FONT_ADVANCE, 444, 0
    },
    {
102,
FONT_BEGIN, 186, 450,
FONT_NEXT, 186, 566,
FONT_NEXT, 195, 624,
FONT_NEXT, 213, 646,
FONT_NEXT, 246, 655,
FONT_NEXT, 340, 580,
FONT_NEXT, 369, 590,
FONT_NEXT, 379, 603,
FONT_NEXT, 383, 622,
FONT_NEXT, 375, 645,
FONT_NEXT, 354, 665,
FONT_NEXT, 322, 678,
FONT_NEXT, 283, 683,
FONT_NEXT, 223, 675,
FONT_NEXT, 178, 654,
FONT_NEXT, 146, 624,
FONT_NEXT, 125, 587,
FONT_NEXT, 105, 510,
FONT_NEXT, 103, 476,
FONT_NEXT, 103, 450,
FONT_NEXT, 21, 450,
FONT_NEXT, 21, 418,
FONT_NEXT, 103, 418,
FONT_NEXT, 103, 104,
FONT_NEXT, 99, 58,
FONT_NEXT, 86, 31,
FONT_NEXT, 60, 19,
FONT_NEXT, 20, 15,
FONT_NEXT, 20, 0,
FONT_NEXT, 280, 0,
FONT_NEXT, 280, 15,
FONT_NEXT, 226, 21,
FONT_NEXT, 199, 37,
FONT_NEXT, 188, 64,
FONT_NEXT, 187, 104,
FONT_NEXT, 187, 418,
FONT_NEXT, 309, 418,
FONT_END, 309, 450,
FONT_ADVANCE, 333, 0
    },
    {
103,
FONT_BEGIN, 110, -49,
FONT_NEXT, 147, -2,
FONT_NEXT, 210, -12,
FONT_NEXT, 251, -14,
FONT_NEXT, 310, -15,
FONT_NEXT, 360, -18,
FONT_NEXT, 399, -24,
FONT_NEXT, 424, -38,
FONT_NEXT, 433, -64,
FONT_NEXT, 420, -102,
FONT_NEXT, 384, -133,
FONT_NEXT, 326, -154,
FONT_NEXT, 248, -161,
FONT_NEXT, 176, -155,
FONT_NEXT, 130, -138,
FONT_NEXT, 105, -115,
FONT_END, 98, -90,
FONT_BEGIN, 158, 384,
FONT_NEXT, 176, 412,
FONT_NEXT, 200, 427,
FONT_NEXT, 227, 432,
FONT_NEXT, 256, 426,
FONT_NEXT, 279, 410,
FONT_NEXT, 310, 361,
FONT_NEXT, 325, 307,
FONT_NEXT, 329, 270,
FONT_NEXT, 327, 245,
FONT_NEXT, 318, 213,
FONT_NEXT, 296, 185,
FONT_NEXT, 256, 174,
FONT_NEXT, 229, 177,
FONT_NEXT, 206, 189,
FONT_NEXT, 174, 228,
FONT_NEXT, 157, 282,
FONT_END, 152, 342,
FONT_BEGIN, 470, 427,
FONT_NEXT, 393, 427,
FONT_NEXT, 338, 437,
FONT_NEXT, 287, 454,
FONT_NEXT, 241, 460,
FONT_NEXT, 182, 450,
FONT_NEXT, 126, 421,
FONT_NEXT, 85, 369,
FONT_NEXT, 73, 334,
FONT_NEXT, 69, 293,
FONT_NEXT, 75, 251,
FONT_NEXT, 93, 216,
FONT_NEXT, 122, 186,
FONT_NEXT, 162, 163,
FONT_NEXT, 132, 136,
FONT_NEXT, 110, 115,
FONT_NEXT, 84, 85,
FONT_NEXT, 74, 67,
FONT_NEXT, 73, 54,
FONT_NEXT, 77, 35,
FONT_NEXT, 89, 21,
FONT_NEXT, 126, 1,
FONT_NEXT, 57, -57,
FONT_NEXT, 36, -89,
FONT_NEXT, 28, -121,
FONT_NEXT, 41, -159,
FONT_NEXT, 78, -190,
FONT_NEXT, 134, -211,
FONT_NEXT, 205, -218,
FONT_NEXT, 282, -208,
FONT_NEXT, 366, -176,
FONT_NEXT, 403, -153,
FONT_NEXT, 433, -124,
FONT_NEXT, 453, -89,
FONT_NEXT, 461, -49,
FONT_NEXT, 457, -22,
FONT_NEXT, 440, 13,
FONT_NEXT, 404, 43,
FONT_NEXT, 375, 53,
FONT_NEXT, 340, 58,
FONT_NEXT, 211, 64,
FONT_NEXT, 160, 69,
FONT_NEXT, 140, 77,
FONT_NEXT, 133, 91,
FONT_NEXT, 139, 109,
FONT_NEXT, 155, 129,
FONT_NEXT, 175, 146,
FONT_NEXT, 193, 154,
FONT_NEXT, 247, 149,
FONT_NEXT, 295, 156,
FONT_NEXT, 347, 182,
FONT_NEXT, 389, 227,
FONT_NEXT, 401, 258,
FONT_NEXT, 406, 296,
FONT_NEXT, 404, 328,
FONT_NEXT, 400, 352,
FONT_NEXT, 387, 388,
FONT_END, 470, 388,
FONT_ADVANCE, 500, 0
    },
    {
104,
FONT_BEGIN, 456, 22,
FONT_NEXT, 438, 34,
FONT_NEXT, 429, 58,
FONT_NEXT, 427, 102,
FONT_NEXT, 427, 301,
FONT_NEXT, 424, 342,
FONT_NEXT, 411, 395,
FONT_NEXT, 396, 420,
FONT_NEXT, 374, 440,
FONT_NEXT, 343, 454,
FONT_NEXT, 303, 460,
FONT_NEXT, 260, 454,
FONT_NEXT, 221, 437,
FONT_NEXT, 187, 411,
FONT_NEXT, 159, 378,
FONT_NEXT, 157, 378,
FONT_NEXT, 157, 680,
FONT_NEXT, 152, 683,
FONT_NEXT, 10, 639,
FONT_NEXT, 10, 623,
FONT_NEXT, 29, 624,
FONT_NEXT, 54, 620,
FONT_NEXT, 67, 609,
FONT_NEXT, 73, 573,
FONT_NEXT, 73, 102,
FONT_NEXT, 70, 58,
FONT_NEXT, 61, 33,
FONT_NEXT, 41, 21,
FONT_NEXT, 9, 15,
FONT_NEXT, 9, 0,
FONT_NEXT, 225, 0,
FONT_NEXT, 225, 15,
FONT_NEXT, 193, 19,
FONT_NEXT, 172, 31,
FONT_NEXT, 160, 56,
FONT_NEXT, 157, 102,
FONT_NEXT, 157, 343,
FONT_NEXT, 187, 373,
FONT_NEXT, 216, 392,
FONT_NEXT, 266, 406,
FONT_NEXT, 301, 398,
FONT_NEXT, 325, 378,
FONT_NEXT, 338, 344,
FONT_NEXT, 343, 300,
FONT_NEXT, 343, 102,
FONT_NEXT, 339, 56,
FONT_NEXT, 327, 31,
FONT_NEXT, 306, 19,
FONT_NEXT, 275, 15,
FONT_NEXT, 275, 0,
FONT_NEXT, 487, 0,
FONT_END, 487, 15,
FONT_ADVANCE, 500, 0
    },
    {
105,
FONT_BEGIN, 253, 0,
FONT_NEXT, 253, 15,
FONT_NEXT, 212, 21,
FONT_NEXT, 190, 35,
FONT_NEXT, 180, 61,
FONT_NEXT, 179, 102,
FONT_NEXT, 179, 457,
FONT_NEXT, 175, 460,
FONT_NEXT, 20, 405,
FONT_NEXT, 20, 390,
FONT_NEXT, 60, 394,
FONT_NEXT, 83, 386,
FONT_NEXT, 91, 368,
FONT_NEXT, 95, 334,
FONT_NEXT, 95, 102,
FONT_NEXT, 91, 55,
FONT_NEXT, 78, 30,
FONT_NEXT, 54, 18,
FONT_NEXT, 16, 15,
FONT_END, 16, 0,
FONT_BEGIN, 89, 599,
FONT_NEXT, 105, 586,
FONT_NEXT, 128, 581,
FONT_NEXT, 148, 584,
FONT_NEXT, 165, 595,
FONT_NEXT, 176, 611,
FONT_NEXT, 180, 632,
FONT_NEXT, 165, 668,
FONT_NEXT, 148, 678,
FONT_NEXT, 128, 683,
FONT_NEXT, 105, 677,
FONT_NEXT, 89, 664,
FONT_END, 78, 632,
FONT_ADVANCE, 278, 0
    },
    {
106,
FONT_BEGIN, 108, -102,
FONT_NEXT, 102, -146,
FONT_NEXT, 87, -174,
FONT_NEXT, 59, -184,
FONT_NEXT, -30, -124,
FONT_NEXT, -58, -135,
FONT_NEXT, -70, -162,
FONT_NEXT, -63, -187,
FONT_NEXT, -42, -204,
FONT_NEXT, -12, -215,
FONT_NEXT, 23, -218,
FONT_NEXT, 67, -214,
FONT_NEXT, 103, -201,
FONT_NEXT, 133, -180,
FONT_NEXT, 156, -153,
FONT_NEXT, 184, -84,
FONT_NEXT, 193, 0,
FONT_NEXT, 193, 457,
FONT_NEXT, 188, 460,
FONT_NEXT, 32, 406,
FONT_NEXT, 32, 390,
FONT_NEXT, 74, 394,
FONT_NEXT, 97, 386,
FONT_NEXT, 105, 368,
FONT_NEXT, 109, 334,
FONT_END, 109, -45,
FONT_BEGIN, 103, 599,
FONT_NEXT, 119, 586,
FONT_NEXT, 142, 581,
FONT_NEXT, 162, 584,
FONT_NEXT, 179, 595,
FONT_NEXT, 190, 611,
FONT_NEXT, 194, 632,
FONT_NEXT, 179, 668,
FONT_NEXT, 162, 678,
FONT_NEXT, 142, 683,
FONT_NEXT, 119, 677,
FONT_NEXT, 103, 664,
FONT_END, 92, 632,
FONT_ADVANCE, 278, 0
    },
    {
107,
FONT_BEGIN, 241, 0,
FONT_NEXT, 241, 15,
FONT_NEXT, 221, 16,
FONT_NEXT, 181, 26,
FONT_NEXT, 169, 42,
FONT_NEXT, 166, 67,
FONT_NEXT, 166, 248,
FONT_NEXT, 168, 248,
FONT_NEXT, 306, 64,
FONT_NEXT, 321, 43,
FONT_NEXT, 327, 31,
FONT_NEXT, 319, 18,
FONT_NEXT, 306, 15,
FONT_NEXT, 287, 15,
FONT_NEXT, 287, 0,
FONT_NEXT, 505, 0,
FONT_NEXT, 505, 15,
FONT_NEXT, 482, 18,
FONT_NEXT, 455, 28,
FONT_NEXT, 424, 49,
FONT_NEXT, 388, 88,
FONT_NEXT, 235, 282,
FONT_NEXT, 264, 309,
FONT_NEXT, 309, 350,
FONT_NEXT, 347, 381,
FONT_NEXT, 377, 404,
FONT_NEXT, 402, 419,
FONT_NEXT, 442, 433,
FONT_NEXT, 480, 435,
FONT_NEXT, 480, 450,
FONT_NEXT, 276, 450,
FONT_NEXT, 276, 436,
FONT_NEXT, 301, 434,
FONT_NEXT, 316, 430,
FONT_NEXT, 326, 417,
FONT_NEXT, 320, 401,
FONT_NEXT, 303, 383,
FONT_NEXT, 168, 263,
FONT_NEXT, 166, 265,
FONT_NEXT, 166, 681,
FONT_NEXT, 162, 683,
FONT_NEXT, 7, 639,
FONT_NEXT, 7, 623,
FONT_NEXT, 37, 625,
FONT_NEXT, 60, 621,
FONT_NEXT, 74, 611,
FONT_NEXT, 80, 592,
FONT_NEXT, 82, 564,
FONT_NEXT, 82, 82,
FONT_NEXT, 79, 50,
FONT_NEXT, 69, 32,
FONT_NEXT, 46, 22,
FONT_NEXT, 7, 15,
FONT_END, 7, 0,
FONT_ADVANCE, 500, 0
    },
    {
108,
FONT_BEGIN, 257, 0,
FONT_NEXT, 257, 15,
FONT_NEXT, 220, 18,
FONT_NEXT, 197, 28,
FONT_NEXT, 185, 49,
FONT_NEXT, 182, 84,
FONT_NEXT, 182, 681,
FONT_NEXT, 178, 683,
FONT_NEXT, 19, 639,
FONT_NEXT, 19, 623,
FONT_NEXT, 53, 625,
FONT_NEXT, 76, 621,
FONT_NEXT, 90, 611,
FONT_NEXT, 96, 592,
FONT_NEXT, 98, 564,
FONT_NEXT, 98, 87,
FONT_NEXT, 94, 53,
FONT_NEXT, 81, 31,
FONT_NEXT, 57, 20,
FONT_NEXT, 21, 15,
FONT_END, 21, 0,
FONT_ADVANCE, 278, 0
    },
    {
109,
FONT_BEGIN, 80, 45,
FONT_NEXT, 66, 25,
FONT_NEXT, 43, 16,
FONT_NEXT, 16, 15,
FONT_NEXT, 16, 0,
FONT_NEXT, 238, 0,
FONT_NEXT, 238, 15,
FONT_NEXT, 190, 23,
FONT_NEXT, 175, 38,
FONT_NEXT, 170, 67,
FONT_NEXT, 170, 349,
FONT_NEXT, 178, 360,
FONT_NEXT, 201, 380,
FONT_NEXT, 237, 399,
FONT_NEXT, 285, 408,
FONT_NEXT, 317, 400,
FONT_NEXT, 339, 380,
FONT_NEXT, 350, 347,
FONT_NEXT, 354, 303,
FONT_NEXT, 354, 86,
FONT_NEXT, 349, 47,
FONT_NEXT, 336, 27,
FONT_NEXT, 315, 18,
FONT_NEXT, 286, 15,
FONT_NEXT, 286, 0,
FONT_NEXT, 510, 0,
FONT_NEXT, 510, 15,
FONT_NEXT, 481, 17,
FONT_NEXT, 458, 26,
FONT_NEXT, 443, 50,
FONT_NEXT, 438, 95,
FONT_NEXT, 438, 347,
FONT_NEXT, 481, 390,
FONT_NEXT, 511, 403,
FONT_NEXT, 549, 408,
FONT_NEXT, 590, 397,
FONT_NEXT, 612, 372,
FONT_NEXT, 622, 298,
FONT_NEXT, 622, 87,
FONT_NEXT, 618, 49,
FONT_NEXT, 607, 29,
FONT_NEXT, 587, 19,
FONT_NEXT, 556, 15,
FONT_NEXT, 556, 0,
FONT_NEXT, 775, 0,
FONT_NEXT, 775, 15,
FONT_NEXT, 749, 17,
FONT_NEXT, 723, 26,
FONT_NEXT, 711, 40,
FONT_NEXT, 706, 76,
FONT_NEXT, 706, 282,
FONT_NEXT, 702, 341,
FONT_NEXT, 686, 398,
FONT_NEXT, 650, 442,
FONT_NEXT, 622, 455,
FONT_NEXT, 588, 460,
FONT_NEXT, 532, 450,
FONT_NEXT, 485, 427,
FONT_NEXT, 449, 399,
FONT_NEXT, 427, 376,
FONT_NEXT, 414, 406,
FONT_NEXT, 393, 433,
FONT_NEXT, 362, 452,
FONT_NEXT, 320, 460,
FONT_NEXT, 281, 454,
FONT_NEXT, 244, 438,
FONT_NEXT, 166, 383,
FONT_NEXT, 166, 458,
FONT_NEXT, 159, 460,
FONT_NEXT, 19, 415,
FONT_NEXT, 19, 398,
FONT_NEXT, 51, 402,
FONT_NEXT, 74, 393,
FONT_NEXT, 82, 374,
FONT_NEXT, 86, 338,
FONT_END, 86, 85,
FONT_ADVANCE, 778, 0
    },
    {
110,
FONT_BEGIN, 76, 53,
FONT_NEXT, 66, 31,
FONT_NEXT, 47, 19,
FONT_NEXT, 18, 15,
FONT_NEXT, 18, 0,
FONT_NEXT, 230, 0,
FONT_NEXT, 230, 15,
FONT_NEXT, 197, 19,
FONT_NEXT, 177, 29,
FONT_NEXT, 167, 45,
FONT_NEXT, 164, 67,
FONT_NEXT, 164, 348,
FONT_NEXT, 216, 390,
FONT_NEXT, 263, 405,
FONT_NEXT, 299, 398,
FONT_NEXT, 323, 379,
FONT_NEXT, 336, 348,
FONT_NEXT, 340, 308,
FONT_NEXT, 340, 99,
FONT_NEXT, 335, 54,
FONT_NEXT, 322, 29,
FONT_NEXT, 302, 18,
FONT_NEXT, 277, 15,
FONT_NEXT, 277, 0,
FONT_NEXT, 485, 0,
FONT_NEXT, 485, 15,
FONT_NEXT, 454, 20,
FONT_NEXT, 436, 31,
FONT_NEXT, 426, 51,
FONT_NEXT, 424, 81,
FONT_NEXT, 424, 310,
FONT_NEXT, 420, 351,
FONT_NEXT, 412, 385,
FONT_NEXT, 382, 431,
FONT_NEXT, 344, 453,
FONT_NEXT, 307, 460,
FONT_NEXT, 266, 453,
FONT_NEXT, 230, 436,
FONT_NEXT, 161, 379,
FONT_NEXT, 161, 458,
FONT_NEXT, 154, 460,
FONT_NEXT, 16, 415,
FONT_NEXT, 16, 398,
FONT_NEXT, 45, 402,
FONT_NEXT, 68, 393,
FONT_NEXT, 76, 374,
FONT_NEXT, 80, 338,
FONT_END, 80, 90,
FONT_ADVANCE, 500, 0
    },
    {
111,
FONT_BEGIN, 197, 455,
FONT_NEXT, 154, 441,
FONT_NEXT, 117, 419,
FONT_NEXT, 86, 390,
FONT_NEXT, 62, 355,
FONT_NEXT, 43, 316,
FONT_NEXT, 29, 228,
FONT_NEXT, 33, 179,
FONT_NEXT, 45, 133,
FONT_NEXT, 64, 93,
FONT_NEXT, 90, 58,
FONT_NEXT, 122, 29,
FONT_NEXT, 159, 8,
FONT_NEXT, 201, -6,
FONT_NEXT, 248, -10,
FONT_NEXT, 301, -4,
FONT_NEXT, 346, 13,
FONT_NEXT, 385, 39,
FONT_NEXT, 416, 71,
FONT_NEXT, 456, 149,
FONT_NEXT, 470, 228,
FONT_NEXT, 465, 279,
FONT_NEXT, 453, 325,
FONT_NEXT, 433, 365,
FONT_NEXT, 406, 398,
FONT_NEXT, 373, 424,
FONT_NEXT, 335, 444,
FONT_NEXT, 292, 455,
FONT_END, 245, 460,
FONT_BEGIN, 221, 26,
FONT_NEXT, 189, 47,
FONT_NEXT, 164, 80,
FONT_NEXT, 145, 119,
FONT_NEXT, 124, 204,
FONT_NEXT, 119, 276,
FONT_NEXT, 128, 344,
FONT_NEXT, 154, 393,
FONT_NEXT, 191, 422,
FONT_NEXT, 237, 432,
FONT_NEXT, 269, 427,
FONT_NEXT, 298, 413,
FONT_NEXT, 322, 390,
FONT_NEXT, 343, 362,
FONT_NEXT, 370, 287,
FONT_NEXT, 380, 199,
FONT_NEXT, 377, 150,
FONT_NEXT, 368, 110,
FONT_NEXT, 356, 78,
FONT_NEXT, 340, 54,
FONT_NEXT, 301, 26,
FONT_END, 262, 18,
FONT_ADVANCE, 500, 0
    },
    {
112,
FONT_BEGIN, 73, -160,
FONT_NEXT, 64, -180,
FONT_NEXT, 43, -194,
FONT_NEXT, 5, -200,
FONT_NEXT, 5, -217,
FONT_NEXT, 247, -217,
FONT_NEXT, 247, -199,
FONT_NEXT, 202, -197,
FONT_NEXT, 176, -186,
FONT_NEXT, 162, -163,
FONT_NEXT, 159, -124,
FONT_NEXT, 159, 33,
FONT_NEXT, 206, -1,
FONT_NEXT, 260, -10,
FONT_NEXT, 308, -4,
FONT_NEXT, 351, 13,
FONT_NEXT, 387, 40,
FONT_NEXT, 417, 74,
FONT_NEXT, 456, 156,
FONT_NEXT, 470, 245,
FONT_NEXT, 466, 292,
FONT_NEXT, 456, 334,
FONT_NEXT, 420, 402,
FONT_NEXT, 368, 445,
FONT_NEXT, 305, 460,
FONT_NEXT, 252, 451,
FONT_NEXT, 212, 432,
FONT_NEXT, 181, 406,
FONT_NEXT, 161, 383,
FONT_NEXT, 159, 385,
FONT_NEXT, 159, 458,
FONT_NEXT, 153, 460,
FONT_NEXT, 9, 409,
FONT_NEXT, 9, 393,
FONT_NEXT, 37, 394,
FONT_NEXT, 57, 390,
FONT_NEXT, 69, 380,
FONT_NEXT, 73, 362,
FONT_NEXT, 75, 337,
FONT_END, 75, -131,
FONT_BEGIN, 171, 360,
FONT_NEXT, 196, 381,
FONT_NEXT, 227, 395,
FONT_NEXT, 257, 400,
FONT_NEXT, 297, 392,
FONT_NEXT, 328, 374,
FONT_NEXT, 350, 347,
FONT_NEXT, 366, 316,
FONT_NEXT, 381, 253,
FONT_NEXT, 383, 229,
FONT_NEXT, 384, 215,
FONT_NEXT, 380, 156,
FONT_NEXT, 369, 111,
FONT_NEXT, 353, 77,
FONT_NEXT, 334, 53,
FONT_NEXT, 294, 27,
FONT_NEXT, 261, 22,
FONT_NEXT, 221, 29,
FONT_NEXT, 189, 46,
FONT_NEXT, 167, 67,
FONT_NEXT, 159, 88,
FONT_END, 159, 334,
FONT_ADVANCE, 500, 0
    },
    {
113,
FONT_BEGIN, 308, 450,
FONT_NEXT, 247, 460,
FONT_NEXT, 201, 455,
FONT_NEXT, 159, 440,
FONT_NEXT, 121, 417,
FONT_NEXT, 88, 387,
FONT_NEXT, 61, 351,
FONT_NEXT, 41, 308,
FONT_NEXT, 28, 262,
FONT_NEXT, 24, 212,
FONT_NEXT, 27, 159,
FONT_NEXT, 37, 113,
FONT_NEXT, 52, 75,
FONT_NEXT, 72, 44,
FONT_NEXT, 124, 3,
FONT_NEXT, 188, -10,
FONT_NEXT, 234, -3,
FONT_NEXT, 278, 14,
FONT_NEXT, 314, 38,
FONT_NEXT, 336, 62,
FONT_NEXT, 341, 62,
FONT_NEXT, 341, -124,
FONT_NEXT, 337, -157,
FONT_NEXT, 324, -180,
FONT_NEXT, 297, -194,
FONT_NEXT, 252, -200,
FONT_NEXT, 252, -217,
FONT_NEXT, 488, -217,
FONT_NEXT, 488, -203,
FONT_NEXT, 459, -196,
FONT_NEXT, 439, -187,
FONT_NEXT, 428, -170,
FONT_NEXT, 425, -141,
FONT_NEXT, 425, 456,
FONT_NEXT, 421, 460,
FONT_NEXT, 414, 457,
FONT_END, 360, 425,
FONT_BEGIN, 333, 88,
FONT_NEXT, 318, 73,
FONT_NEXT, 288, 60,
FONT_NEXT, 238, 51,
FONT_NEXT, 206, 55,
FONT_NEXT, 180, 67,
FONT_NEXT, 140, 110,
FONT_NEXT, 117, 169,
FONT_NEXT, 110, 238,
FONT_NEXT, 119, 319,
FONT_NEXT, 147, 380,
FONT_NEXT, 189, 418,
FONT_NEXT, 245, 432,
FONT_NEXT, 276, 428,
FONT_NEXT, 300, 420,
FONT_NEXT, 328, 393,
FONT_NEXT, 339, 360,
FONT_NEXT, 341, 333,
FONT_END, 341, 127,
FONT_ADVANCE, 500, 0
    },
    {
114,
FONT_BEGIN, 72, 51,
FONT_NEXT, 61, 32,
FONT_NEXT, 39, 22,
FONT_NEXT, 5, 15,
FONT_NEXT, 5, 0,
FONT_NEXT, 245, 0,
FONT_NEXT, 245, 15,
FONT_NEXT, 210, 17,
FONT_NEXT, 183, 27,
FONT_NEXT, 166, 49,
FONT_NEXT, 160, 90,
FONT_NEXT, 160, 315,
FONT_NEXT, 167, 341,
FONT_NEXT, 185, 368,
FONT_NEXT, 207, 388,
FONT_NEXT, 228, 397,
FONT_NEXT, 258, 379,
FONT_NEXT, 274, 367,
FONT_NEXT, 297, 362,
FONT_NEXT, 325, 374,
FONT_NEXT, 335, 406,
FONT_NEXT, 331, 428,
FONT_NEXT, 320, 445,
FONT_NEXT, 303, 456,
FONT_NEXT, 281, 460,
FONT_NEXT, 252, 455,
FONT_NEXT, 224, 441,
FONT_NEXT, 195, 413,
FONT_NEXT, 162, 369,
FONT_NEXT, 160, 369,
FONT_NEXT, 160, 458,
FONT_NEXT, 155, 460,
FONT_NEXT, 7, 406,
FONT_NEXT, 7, 390,
FONT_NEXT, 41, 394,
FONT_NEXT, 64, 386,
FONT_NEXT, 72, 368,
FONT_NEXT, 76, 334,
FONT_END, 76, 84,
FONT_ADVANCE, 333, 0
    },
    {
115,
FONT_BEGIN, 311, 451,
FONT_NEXT, 300, 451,
FONT_NEXT, 283, 440,
FONT_NEXT, 247, 450,
FONT_NEXT, 191, 460,
FONT_NEXT, 128, 448,
FONT_NEXT, 84, 420,
FONT_NEXT, 59, 380,
FONT_NEXT, 51, 338,
FONT_NEXT, 60, 288,
FONT_NEXT, 86, 250,
FONT_NEXT, 123, 219,
FONT_NEXT, 168, 192,
FONT_NEXT, 226, 159,
FONT_NEXT, 264, 127,
FONT_NEXT, 278, 85,
FONT_NEXT, 270, 49,
FONT_NEXT, 250, 27,
FONT_NEXT, 224, 15,
FONT_NEXT, 197, 12,
FONT_NEXT, 163, 15,
FONT_NEXT, 137, 26,
FONT_NEXT, 100, 62,
FONT_NEXT, 79, 107,
FONT_NEXT, 68, 152,
FONT_NEXT, 52, 152,
FONT_NEXT, 52, -4,
FONT_NEXT, 65, -4,
FONT_NEXT, 73, 5,
FONT_NEXT, 87, 8,
FONT_NEXT, 138, -1,
FONT_NEXT, 203, -10,
FONT_NEXT, 252, -3,
FONT_NEXT, 299, 21,
FONT_NEXT, 334, 62,
FONT_NEXT, 348, 119,
FONT_NEXT, 341, 158,
FONT_NEXT, 324, 189,
FONT_NEXT, 264, 237,
FONT_NEXT, 156, 301,
FONT_NEXT, 133, 318,
FONT_NEXT, 120, 337,
FONT_NEXT, 113, 372,
FONT_NEXT, 117, 393,
FONT_NEXT, 130, 414,
FONT_NEXT, 153, 430,
FONT_NEXT, 189, 437,
FONT_NEXT, 221, 432,
FONT_NEXT, 252, 413,
FONT_NEXT, 279, 376,
FONT_NEXT, 300, 314,
FONT_END, 315, 314,
FONT_ADVANCE, 389, 0
    },
    {
116,
FONT_BEGIN, 154, 450,
FONT_NEXT, 154, 566,
FONT_NEXT, 147, 579,
FONT_NEXT, 122, 544,
FONT_NEXT, 102, 516,
FONT_NEXT, 57, 465,
FONT_NEXT, 26, 441,
FONT_NEXT, 13, 425,
FONT_NEXT, 17, 418,
FONT_NEXT, 70, 418,
FONT_NEXT, 70, 117,
FONT_NEXT, 77, 54,
FONT_NEXT, 96, 16,
FONT_NEXT, 126, -5,
FONT_NEXT, 162, -10,
FONT_NEXT, 196, -5,
FONT_NEXT, 228, 11,
FONT_NEXT, 257, 35,
FONT_NEXT, 279, 66,
FONT_NEXT, 266, 77,
FONT_NEXT, 243, 54,
FONT_NEXT, 205, 42,
FONT_NEXT, 175, 51,
FONT_NEXT, 160, 75,
FONT_NEXT, 154, 132,
FONT_NEXT, 154, 418,
FONT_NEXT, 255, 418,
FONT_END, 255, 450,
FONT_ADVANCE, 278, 0
    },
    {
117,
FONT_BEGIN, 444, 53,
FONT_NEXT, 425, 64,
FONT_NEXT, 418, 82,
FONT_NEXT, 417, 107,
FONT_NEXT, 417, 450,
FONT_NEXT, 259, 450,
FONT_NEXT, 259, 433,
FONT_NEXT, 300, 427,
FONT_NEXT, 322, 415,
FONT_NEXT, 331, 396,
FONT_NEXT, 333, 370,
FONT_NEXT, 333, 135,
FONT_NEXT, 328, 100,
FONT_NEXT, 317, 84,
FONT_NEXT, 274, 57,
FONT_NEXT, 230, 48,
FONT_NEXT, 190, 57,
FONT_NEXT, 167, 80,
FONT_NEXT, 157, 106,
FONT_NEXT, 155, 124,
FONT_NEXT, 155, 450,
FONT_NEXT, 9, 450,
FONT_NEXT, 9, 436,
FONT_NEXT, 51, 425,
FONT_NEXT, 65, 407,
FONT_NEXT, 71, 372,
FONT_NEXT, 71, 120,
FONT_NEXT, 74, 83,
FONT_NEXT, 83, 54,
FONT_NEXT, 115, 15,
FONT_NEXT, 154, -5,
FONT_NEXT, 190, -10,
FONT_NEXT, 208, -9,
FONT_NEXT, 234, -4,
FONT_NEXT, 264, 9,
FONT_NEXT, 295, 33,
FONT_NEXT, 338, 76,
FONT_NEXT, 338, -7,
FONT_NEXT, 340, -9,
FONT_NEXT, 479, 36,
FONT_END, 479, 50,
FONT_ADVANCE, 500, 0
    },
    {
118,
FONT_BEGIN, 338, 450,
FONT_NEXT, 338, 435,
FONT_NEXT, 370, 426,
FONT_NEXT, 385, 402,
FONT_NEXT, 381, 383,
FONT_NEXT, 373, 355,
FONT_NEXT, 345, 280,
FONT_NEXT, 280, 114,
FONT_NEXT, 178, 370,
FONT_NEXT, 169, 405,
FONT_NEXT, 172, 419,
FONT_NEXT, 183, 428,
FONT_NEXT, 215, 435,
FONT_NEXT, 215, 450,
FONT_NEXT, 19, 450,
FONT_NEXT, 19, 435,
FONT_NEXT, 48, 427,
FONT_NEXT, 74, 398,
FONT_NEXT, 90, 364,
FONT_NEXT, 110, 319,
FONT_NEXT, 156, 212,
FONT_NEXT, 200, 106,
FONT_NEXT, 217, 63,
FONT_NEXT, 230, 33,
FONT_NEXT, 239, 7,
FONT_NEXT, 246, -7,
FONT_NEXT, 257, -14,
FONT_NEXT, 269, 0,
FONT_NEXT, 284, 36,
FONT_NEXT, 412, 357,
FONT_NEXT, 427, 393,
FONT_NEXT, 440, 417,
FONT_NEXT, 455, 429,
FONT_NEXT, 477, 435,
FONT_END, 477, 450,
FONT_ADVANCE, 500, 0
    },
    {
119,
FONT_BEGIN, 571, 435,
FONT_NEXT, 602, 425,
FONT_NEXT, 611, 416,
FONT_NEXT, 615, 402,
FONT_NEXT, 610, 374,
FONT_NEXT, 598, 338,
FONT_NEXT, 508, 116,
FONT_NEXT, 428, 330,
FONT_NEXT, 411, 371,
FONT_NEXT, 407, 398,
FONT_NEXT, 411, 415,
FONT_NEXT, 423, 425,
FONT_NEXT, 465, 435,
FONT_NEXT, 465, 450,
FONT_NEXT, 262, 450,
FONT_NEXT, 262, 435,
FONT_NEXT, 288, 429,
FONT_NEXT, 308, 413,
FONT_NEXT, 327, 376,
FONT_NEXT, 338, 347,
FONT_NEXT, 351, 310,
FONT_NEXT, 260, 111,
FONT_NEXT, 224, 205,
FONT_NEXT, 190, 290,
FONT_NEXT, 164, 358,
FONT_NEXT, 157, 383,
FONT_NEXT, 155, 401,
FONT_NEXT, 158, 417,
FONT_NEXT, 169, 427,
FONT_NEXT, 201, 435,
FONT_NEXT, 201, 450,
FONT_NEXT, 21, 450,
FONT_NEXT, 21, 435,
FONT_NEXT, 49, 420,
FONT_NEXT, 60, 401,
FONT_NEXT, 74, 372,
FONT_NEXT, 209, 30,
FONT_NEXT, 217, 8,
FONT_NEXT, 224, -5,
FONT_NEXT, 235, -14,
FONT_NEXT, 244, -5,
FONT_NEXT, 260, 25,
FONT_NEXT, 372, 265,
FONT_NEXT, 463, 29,
FONT_NEXT, 471, 7,
FONT_NEXT, 477, -6,
FONT_NEXT, 487, -14,
FONT_NEXT, 498, -5,

FONT_NEXT, 505, 10,
FONT_NEXT, 515, 35,
FONT_NEXT, 653, 381,
FONT_NEXT, 671, 419,
FONT_NEXT, 694, 435,
FONT_NEXT, 694, 450,
FONT_END, 571, 450,
FONT_ADVANCE, 722, 0
    },
    {
120,
FONT_BEGIN, 311, 60,
FONT_NEXT, 318, 41,
FONT_NEXT, 309, 20,
FONT_NEXT, 278, 15,
FONT_NEXT, 278, 0,
FONT_NEXT, 479, 0,
FONT_NEXT, 479, 15,
FONT_NEXT, 445, 23,
FONT_NEXT, 423, 41,
FONT_NEXT, 397, 75,
FONT_NEXT, 269, 271,
FONT_NEXT, 352, 391,
FONT_NEXT, 379, 417,
FONT_NEXT, 403, 429,
FONT_NEXT, 433, 435,
FONT_NEXT, 433, 450,
FONT_NEXT, 275, 450,
FONT_NEXT, 275, 435,
FONT_NEXT, 301, 431,
FONT_NEXT, 311, 424,
FONT_NEXT, 315, 412,
FONT_NEXT, 313, 402,
FONT_NEXT, 304, 386,
FONT_NEXT, 284, 356,
FONT_NEXT, 248, 304,
FONT_NEXT, 210, 362,
FONT_NEXT, 194, 391,
FONT_NEXT, 188, 412,
FONT_NEXT, 192, 424,
FONT_NEXT, 202, 431,
FONT_NEXT, 231, 435,
FONT_NEXT, 231, 450,
FONT_NEXT, 24, 450,
FONT_NEXT, 24, 435,
FONT_NEXT, 64, 428,
FONT_NEXT, 83, 410,
FONT_NEXT, 110, 375,
FONT_NEXT, 204, 231,
FONT_NEXT, 90, 66,
FONT_NEXT, 68, 39,
FONT_NEXT, 51, 24,
FONT_NEXT, 17, 15,
FONT_NEXT, 17, 0,
FONT_NEXT, 162, 0,
FONT_NEXT, 162, 15,
FONT_NEXT, 136, 17,
FONT_NEXT, 122, 33,
FONT_NEXT, 142, 74,
FONT_NEXT, 221, 197,
FONT_END, 302, 73,
FONT_ADVANCE, 500, 0
    },
    {
121,
FONT_BEGIN, 340, 450,
FONT_NEXT, 340, 435,
FONT_NEXT, 376, 428,
FONT_NEXT, 388, 407,
FONT_NEXT, 380, 379,
FONT_NEXT, 365, 339,
FONT_NEXT, 287, 117,
FONT_NEXT, 172, 370,
FONT_NEXT, 162, 406,
FONT_NEXT, 167, 421,
FONT_NEXT, 182, 430,
FONT_NEXT, 220, 435,
FONT_NEXT, 220, 450,
FONT_NEXT, 14, 450,
FONT_NEXT, 14, 436,
FONT_NEXT, 39, 428,
FONT_NEXT, 65, 404,
FONT_NEXT, 179, 158,
FONT_NEXT, 194, 125,
FONT_NEXT, 215, 83,
FONT_NEXT, 233, 43,
FONT_NEXT, 241, 20,
FONT_NEXT, 239, 8,
FONT_NEXT, 233, -11,
FONT_NEXT, 214, -63,
FONT_NEXT, 185, -113,
FONT_NEXT, 167, -129,
FONT_NEXT, 149, -134,
FONT_NEXT, 118, -125,
FONT_NEXT, 78, -116,
FONT_NEXT, 51, -123,
FONT_NEXT, 36, -136,
FONT_NEXT, 30, -160,
FONT_NEXT, 34, -181,
FONT_NEXT, 47, -199,
FONT_NEXT, 70, -213,
FONT_NEXT, 103, -218,
FONT_NEXT, 154, -206,
FONT_NEXT, 197, -169,
FONT_NEXT, 235, -106,
FONT_NEXT, 273, -18,
FONT_NEXT, 427, 390,
FONT_NEXT, 439, 414,
FONT_NEXT, 451, 427,
FONT_NEXT, 475, 435,
FONT_END, 475, 450,
FONT_ADVANCE, 500, 0
    },
    {
122,
FONT_BEGIN, 400, 139,
FONT_NEXT, 388, 89,
FONT_NEXT, 369, 55,
FONT_NEXT, 334, 36,
FONT_NEXT, 307, 31,
FONT_NEXT, 272, 30,
FONT_NEXT, 134, 30,
FONT_NEXT, 403, 435,
FONT_NEXT, 403, 450,
FONT_NEXT, 56, 450,
FONT_NEXT, 53, 332,
FONT_NEXT, 71, 332,
FONT_NEXT, 76, 368,
FONT_NEXT, 88, 396,
FONT_NEXT, 112, 413,
FONT_NEXT, 155, 420,
FONT_NEXT, 293, 420,
FONT_NEXT, 27, 15,
FONT_NEXT, 27, 0,
FONT_NEXT, 404, 0,
FONT_END, 418, 135,
FONT_ADVANCE, 444, 0
    },
    {
123,
FONT_BEGIN, 187, -82,
FONT_NEXT, 196, -114,
FONT_NEXT, 210, -139,
FONT_NEXT, 228, -156,
FONT_NEXT, 280, -176,
FONT_NEXT, 350, -181,
FONT_NEXT, 350, -170,
FONT_NEXT, 305, -154,
FONT_NEXT, 277, -129,
FONT_NEXT, 263, -94,
FONT_NEXT, 259, -47,
FONT_NEXT, 259, 121,
FONT_NEXT, 255, 168,
FONT_NEXT, 242, 204,
FONT_NEXT, 215, 230,
FONT_NEXT, 168, 248,
FONT_NEXT, 168, 250,
FONT_NEXT, 215, 267,
FONT_NEXT, 242, 294,
FONT_NEXT, 255, 330,
FONT_NEXT, 259, 378,
FONT_NEXT, 259, 546,
FONT_NEXT, 263, 592,
FONT_NEXT, 277, 627,
FONT_NEXT, 305, 652,
FONT_NEXT, 350, 669,
FONT_NEXT, 350, 680,
FONT_NEXT, 280, 675,
FONT_NEXT, 228, 655,
FONT_NEXT, 210, 637,
FONT_NEXT, 196, 612,
FONT_NEXT, 187, 581,
FONT_NEXT, 185, 541,
FONT_NEXT, 185, 363,
FONT_NEXT, 181, 320,
FONT_NEXT, 168, 289,
FONT_NEXT, 142, 266,
FONT_NEXT, 100, 250,
FONT_NEXT, 142, 233,
FONT_NEXT, 168, 209,
FONT_NEXT, 181, 178,
FONT_NEXT, 185, 136,
FONT_END, 185, -41,
FONT_ADVANCE, 480, 0
    },
    {
124,
FONT_BEGIN, 133, -14,
FONT_NEXT, 133, 676,
FONT_NEXT, 67, 676,
FONT_END, 67, -14,
FONT_ADVANCE, 200, 0
    },
    {
125,
FONT_BEGIN, 292, 580,
FONT_NEXT, 283, 612,
FONT_NEXT, 269, 637,
FONT_NEXT, 251, 655,
FONT_NEXT, 199, 675,
FONT_NEXT, 130, 680,
FONT_NEXT, 130, 669,
FONT_NEXT, 174, 652,
FONT_NEXT, 202, 627,
FONT_NEXT, 216, 592,
FONT_NEXT, 221, 546,
FONT_NEXT, 221, 378,
FONT_NEXT, 224, 330,
FONT_NEXT, 237, 294,
FONT_NEXT, 264, 268,
FONT_NEXT, 312, 251,
FONT_NEXT, 312, 249,
FONT_NEXT, 264, 231,
FONT_NEXT, 237, 204,
FONT_NEXT, 224, 168,
FONT_NEXT, 221, 121,
FONT_NEXT, 221, -47,
FONT_NEXT, 216, -94,
FONT_NEXT, 202, -129,
FONT_NEXT, 174, -154,
FONT_NEXT, 130, -170,
FONT_NEXT, 130, -181,
FONT_NEXT, 199, -177,
FONT_NEXT, 251, -157,
FONT_NEXT, 269, -139,
FONT_NEXT, 283, -114,
FONT_NEXT, 292, -83,
FONT_NEXT, 295, -42,
FONT_NEXT, 295, 136,
FONT_NEXT, 298, 178,
FONT_NEXT, 311, 209,
FONT_NEXT, 337, 233,
FONT_NEXT, 380, 250,
FONT_NEXT, 337, 266,
FONT_NEXT, 311, 289,
FONT_NEXT, 298, 320,
FONT_NEXT, 295, 363,
FONT_END, 295, 540,
FONT_ADVANCE, 480, 0
    },
    {
126,
FONT_BEGIN, 428, 273,
FONT_NEXT, 405, 257,
FONT_NEXT, 378, 251,
FONT_NEXT, 330, 263,
FONT_NEXT, 276, 287,
FONT_NEXT, 219, 309,
FONT_NEXT, 165, 319,
FONT_NEXT, 120, 311,
FONT_NEXT, 85, 291,
FONT_NEXT, 40, 233,
FONT_NEXT, 76, 183,
FONT_NEXT, 107, 232,
FONT_NEXT, 130, 248,
FONT_NEXT, 160, 255,
FONT_NEXT, 222, 244,
FONT_NEXT, 275, 220,
FONT_NEXT, 324, 196,
FONT_NEXT, 377, 187,
FONT_NEXT, 418, 194,
FONT_NEXT, 451, 214,
FONT_NEXT, 502, 273,
FONT_END, 466, 323,
FONT_ADVANCE, 541, 0
    },
    {
END_OF_LIST
    }
};

GLint filledFont[][1+MAX_STROKES*3] = {
    {
32,
FONT_ADVANCE, 250, 0
    },
    {
33,
FONT_BEGIN, 236, 605,
FONT_NEXT, 234, 559,
FONT_NEXT, 229, 642,
FONT_NEXT, 189, 176,
FONT_NEXT, 211, 667,
FONT_END, 182, 676,
FONT_BEGIN, 182, 676,
FONT_NEXT, 189, 176,
FONT_END, 176, 176,
FONT_BEGIN, 182, 676,
FONT_NEXT, 176, 176,
FONT_NEXT, 156, 668,
FONT_NEXT, 167, 284,
FONT_END, 163, 315,
FONT_BEGIN, 156, 668,
FONT_NEXT, 163, 315,
FONT_END, 158, 355,
FONT_BEGIN, 156, 668,
FONT_NEXT, 158, 355,
FONT_END, 146, 447,
FONT_BEGIN, 156, 668,
FONT_NEXT, 146, 447,
FONT_NEXT, 140, 648,
FONT_NEXT, 134, 534,
FONT_NEXT, 130, 592,
FONT_END, 131, 568,
FONT_BEGIN, 238, 42,
FONT_NEXT, 232, 18,
FONT_NEXT, 233, 62,
FONT_END, 222, 79,
FONT_BEGIN, 222, 79,
FONT_NEXT, 232, 18,
FONT_END, 219, 3,
FONT_BEGIN, 222, 79,
FONT_NEXT, 219, 3,
FONT_NEXT, 204, 91,
FONT_NEXT, 183, -9,
FONT_NEXT, 183, 96,
FONT_END, 162, 91,
FONT_BEGIN, 162, 91,
FONT_NEXT, 183, -9,
FONT_END, 148, 2,
FONT_BEGIN, 162, 91,
FONT_NEXT, 148, 2,
FONT_NEXT, 145, 80,
FONT_NEXT, 135, 18,
FONT_NEXT, 134, 63,
FONT_END, 130, 42,
FONT_ADVANCE, 333, 0
    },
    {
34,
FONT_BEGIN, 162, 635,
FONT_NEXT, 158, 599,
FONT_NEXT, 148, 665,
FONT_NEXT, 149, 543,
FONT_END, 139, 482,
FONT_BEGIN, 148, 665,
FONT_NEXT, 139, 482,
FONT_NEXT, 120, 676,
FONT_NEXT, 130, 431,
FONT_END, 109, 431,
FONT_BEGIN, 120, 676,
FONT_NEXT, 109, 431,
FONT_NEXT, 91, 665,
FONT_NEXT, 99, 482,
FONT_END, 89, 543,
FONT_BEGIN, 91, 665,
FONT_NEXT, 89, 543,
FONT_NEXT, 77, 635,
FONT_END, 80, 599,
FONT_BEGIN, 331, 635,
FONT_NEXT, 327, 599,
FONT_NEXT, 317, 665,
FONT_NEXT, 318, 543,
FONT_END, 308, 482,
FONT_BEGIN, 317, 665,
FONT_NEXT, 308, 482,
FONT_NEXT, 289, 676,
FONT_NEXT, 299, 431,
FONT_END, 278, 431,
FONT_BEGIN, 289, 676,
FONT_NEXT, 278, 431,
FONT_NEXT, 260, 665,
FONT_NEXT, 268, 482,
FONT_END, 258, 543,
FONT_BEGIN, 260, 665,
FONT_NEXT, 258, 543,
FONT_NEXT, 246, 635,
FONT_END, 249, 599,
FONT_ADVANCE, 408, 0
    },
    {
35,
FONT_BEGIN, 32, 405,
FONT_NEXT, 32, 460,
FONT_NEXT, 142, 405,
FONT_NEXT, 150, 460,
FONT_END, 137, 0,
FONT_BEGIN, 142, 405,
FONT_NEXT, 137, 0,
FONT_NEXT, 121, 271,
FONT_NEXT, 79, 0,
FONT_END, 112, 216,
FONT_BEGIN, 121, 271,
FONT_NEXT, 112, 216,
FONT_NEXT, 5, 271,
FONT_END, 5, 216,
FONT_BEGIN, 239, 662,
FONT_NEXT, 208, 460,
FONT_NEXT, 181, 662,
FONT_NEXT, 200, 405,
FONT_END, 179, 271,
FONT_BEGIN, 181, 662,
FONT_NEXT, 179, 271,
FONT_NEXT, 150, 460,
FONT_NEXT, 170, 216,
FONT_END, 137, 0,
FONT_BEGIN, 170, 216,
FONT_NEXT, 179, 271,
FONT_NEXT, 304, 216,
FONT_NEXT, 313, 271,
FONT_NEXT, 273, 0,
FONT_END, 331, 0,
FONT_BEGIN, 331, 0,
FONT_NEXT, 313, 271,
FONT_END, 333, 405,
FONT_BEGIN, 331, 0,
FONT_NEXT, 333, 405,
FONT_NEXT, 341, 460,
FONT_END, 208, 460,
FONT_BEGIN, 208, 460,
FONT_NEXT, 333, 405,
FONT_END, 200, 405,
FONT_BEGIN, 208, 460,
FONT_END, 200, 405,
FONT_BEGIN, 429, 662,
FONT_NEXT, 399, 460,
FONT_NEXT, 371, 662,
FONT_NEXT, 391, 405,
FONT_END, 371, 271,
FONT_BEGIN, 371, 662,
FONT_NEXT, 371, 271,
FONT_NEXT, 341, 460,
FONT_NEXT, 362, 216,
FONT_END, 331, 0,
FONT_BEGIN, 496, 460,
FONT_NEXT, 496, 405,
FONT_NEXT, 399, 460,
FONT_END, 391, 405,
FONT_BEGIN, 471, 271,
FONT_NEXT, 471, 216,
FONT_NEXT, 371, 271,
FONT_END, 362, 216,
FONT_ADVANCE, 500, 0
    },
    {
36,
FONT_BEGIN, 264, 727,
FONT_NEXT, 264, 664,
FONT_NEXT, 230, 727,
FONT_NEXT, 264, 637,
FONT_END, 264, 391,
FONT_BEGIN, 230, 727,
FONT_NEXT, 264, 391,
FONT_END, 264, 293,
FONT_BEGIN, 230, 727,
FONT_NEXT, 264, 293,
FONT_END, 264, 28,
FONT_BEGIN, 230, 727,
FONT_NEXT, 264, 28,
FONT_END, 264, 0,
FONT_BEGIN, 230, 727,
FONT_NEXT, 264, 0,
FONT_END, 264, -87,
FONT_BEGIN, 230, 727,
FONT_NEXT, 264, -87,
FONT_END, 230, -87,
FONT_BEGIN, 230, 727,
FONT_NEXT, 230, -87,
FONT_NEXT, 230, 664,
FONT_END, 230, 310,
FONT_BEGIN, 230, 310,
FONT_NEXT, 230, -87,
FONT_NEXT, 230, 28,
FONT_NEXT, 230, 0,
FONT_NEXT, 166, 37,
FONT_NEXT, 162, 5,
FONT_NEXT, 117, 66,
FONT_NEXT, 110, 19,
FONT_NEXT, 81, 114,
FONT_NEXT, 70, 35,
FONT_NEXT, 59, 181,
FONT_NEXT, 44, 51,
FONT_END, 44, 181,
FONT_BEGIN, 425, 611,
FONT_NEXT, 425, 500,
FONT_NEXT, 387, 634,
FONT_NEXT, 410, 500,
FONT_END, 399, 537,
FONT_BEGIN, 387, 634,
FONT_NEXT, 399, 537,
FONT_END, 385, 566,
FONT_BEGIN, 387, 634,
FONT_NEXT, 385, 566,
FONT_NEXT, 345, 649,
FONT_NEXT, 348, 607,
FONT_END, 305, 628,
FONT_BEGIN, 345, 649,
FONT_NEXT, 305, 628,
FONT_NEXT, 264, 664,
FONT_END, 264, 637,
FONT_BEGIN, 457, 174,
FONT_NEXT, 453, 134,
FONT_NEXT, 453, 211,
FONT_END, 444, 243,
FONT_BEGIN, 444, 243,
FONT_NEXT, 453, 134,
FONT_END, 441, 100,
FONT_BEGIN, 444, 243,
FONT_NEXT, 441, 100,
FONT_NEXT, 408, 295,
FONT_NEXT, 399, 49,
FONT_NEXT, 348, 341,
FONT_NEXT, 378, 151,
FONT_END, 369, 197,
FONT_BEGIN, 348, 341,
FONT_NEXT, 369, 197,
FONT_END, 344, 234,
FONT_BEGIN, 348, 341,
FONT_NEXT, 344, 234,
FONT_NEXT, 264, 391,
FONT_NEXT, 308, 264,
FONT_END, 264, 293,
FONT_BEGIN, 264, 28,
FONT_NEXT, 295, 35,
FONT_NEXT, 264, 0,
FONT_NEXT, 333, 53,
FONT_NEXT, 338, 16,
FONT_NEXT, 364, 89,
FONT_END, 374, 116,
FONT_BEGIN, 338, 16,
FONT_NEXT, 374, 116,
FONT_NEXT, 399, 49,
FONT_END, 378, 151,
FONT_BEGIN, 126, 532,
FONT_NEXT, 133, 495,
FONT_NEXT, 106, 392,
FONT_NEXT, 155, 464,
FONT_NEXT, 163, 349,
FONT_NEXT, 187, 435,
FONT_END, 229, 407,
FONT_BEGIN, 163, 349,
FONT_NEXT, 229, 407,
FONT_NEXT, 230, 310,
FONT_NEXT, 229, 637,
FONT_NEXT, 230, 664,
FONT_END, 185, 658,
FONT_BEGIN, 185, 658,
FONT_NEXT, 229, 637,
FONT_END, 196, 628,
FONT_BEGIN, 185, 658,
FONT_NEXT, 196, 628,
FONT_END, 171, 616,
FONT_BEGIN, 185, 658,
FONT_NEXT, 171, 616,
FONT_NEXT, 148, 646,
FONT_NEXT, 141, 586,
FONT_NEXT, 93, 611,
FONT_NEXT, 128, 555,
FONT_END, 126, 532,
FONT_BEGIN, 93, 611,
FONT_NEXT, 126, 532,
FONT_END, 106, 392,
FONT_BEGIN, 93, 611,
FONT_NEXT, 106, 392,
FONT_END, 67, 445,
FONT_BEGIN, 93, 611,
FONT_NEXT, 67, 445,
FONT_NEXT, 61, 563,
FONT_NEXT, 55, 476,
FONT_END, 52, 511,
FONT_ADVANCE, 500, 0
    },
    {
37,
FONT_BEGIN, 102, 319,
FONT_NEXT, 137, 397,
FONT_NEXT, 140, 296,
FONT_NEXT, 141, 362,
FONT_END, 153, 339,
FONT_BEGIN, 140, 296,
FONT_NEXT, 153, 339,
FONT_END, 172, 326,
FONT_BEGIN, 140, 296,
FONT_NEXT, 172, 326,
FONT_NEXT, 178, 289,
FONT_NEXT, 199, 323,
FONT_NEXT, 223, 295,
FONT_NEXT, 228, 328,
FONT_END, 257, 343,
FONT_BEGIN, 223, 295,
FONT_NEXT, 257, 343,
FONT_NEXT, 264, 314,
FONT_END, 299, 343,
FONT_BEGIN, 299, 343,
FONT_NEXT, 257, 343,
FONT_END, 309, 396,
FONT_BEGIN, 299, 343,
FONT_NEXT, 309, 396,
FONT_NEXT, 329, 379,
FONT_NEXT, 345, 469,
FONT_NEXT, 369, 463,
FONT_NEXT, 359, 553,
FONT_NEXT, 352, 635,
FONT_NEXT, 350, 600,
FONT_NEXT, 325, 653,
FONT_NEXT, 315, 611,
FONT_NEXT, 305, 659,
FONT_NEXT, 299, 622,
FONT_NEXT, 276, 662,
FONT_NEXT, 281, 632,
FONT_END, 255, 625,
FONT_BEGIN, 276, 662,
FONT_NEXT, 255, 625,
FONT_NEXT, 232, 655,
FONT_NEXT, 230, 606,
FONT_NEXT, 192, 639,
FONT_NEXT, 184, 545,
FONT_NEXT, 156, 613,
FONT_NEXT, 150, 468,
FONT_NEXT, 124, 581,
FONT_NEXT, 137, 397,
FONT_END, 102, 319,
FONT_BEGIN, 124, 581,
FONT_NEXT, 102, 319,
FONT_NEXT, 78, 504,
FONT_NEXT, 72, 361,
FONT_NEXT, 65, 463,
FONT_NEXT, 64, 390,
FONT_END, 61, 425,
FONT_BEGIN, 201, -13,
FONT_NEXT, 550, 609,
FONT_NEXT, 249, -13,
FONT_END, 595, 676,
FONT_BEGIN, 595, 676,
FONT_NEXT, 550, 609,
FONT_NEXT, 536, 630,
FONT_NEXT, 548, 611,
FONT_END, 507, 592,
FONT_BEGIN, 536, 630,
FONT_NEXT, 507, 592,
FONT_NEXT, 493, 614,
FONT_NEXT, 478, 585,
FONT_NEXT, 438, 608,
FONT_NEXT, 443, 583,
FONT_END, 401, 585,
FONT_BEGIN, 438, 608,
FONT_NEXT, 401, 585,
FONT_NEXT, 404, 610,
FONT_END, 381, 616,
FONT_BEGIN, 381, 616,
FONT_NEXT, 401, 585,
FONT_END, 377, 593,
FONT_BEGIN, 381, 616,
FONT_NEXT, 377, 593,
FONT_NEXT, 352, 635,
FONT_END, 369, 463,
FONT_BEGIN, 369, 463,
FONT_NEXT, 377, 593,
FONT_END, 384, 548,
FONT_BEGIN, 369, 463,
FONT_END, 384, 548,
FONT_BEGIN, 249, -13,
FONT_NEXT, 595, 676,
FONT_END, 634, 676,
FONT_BEGIN, 772, 261,
FONT_NEXT, 757, 173,
FONT_NEXT, 768, 295,
FONT_END, 760, 321,
FONT_BEGIN, 760, 321,
FONT_NEXT, 757, 173,
FONT_END, 746, 254,
FONT_BEGIN, 760, 321,
FONT_NEXT, 746, 254,
FONT_NEXT, 731, 354,
FONT_NEXT, 740, 287,
FONT_END, 726, 314,
FONT_BEGIN, 731, 354,
FONT_NEXT, 726, 314,
FONT_NEXT, 695, 368,
FONT_NEXT, 703, 332,
FONT_END, 676, 339,
FONT_BEGIN, 695, 368,
FONT_NEXT, 676, 339,
FONT_NEXT, 663, 371,
FONT_NEXT, 641, 330,
FONT_NEXT, 622, 365,
FONT_NEXT, 611, 308,
FONT_NEXT, 583, 349,
FONT_NEXT, 564, 238,
FONT_NEXT, 514, 293,
FONT_NEXT, 534, 160,
FONT_END, 527, 128,
FONT_BEGIN, 514, 293,
FONT_NEXT, 527, 128,
FONT_END, 525, 106,
FONT_BEGIN, 514, 293,
FONT_NEXT, 525, 106,
FONT_END, 513, 11,
FONT_BEGIN, 514, 293,
FONT_NEXT, 513, 11,
FONT_NEXT, 466, 218,
FONT_NEXT, 475, 42,
FONT_END, 455, 86,
FONT_BEGIN, 466, 218,
FONT_NEXT, 455, 86,
FONT_NEXT, 453, 177,
FONT_END, 449, 137,
FONT_BEGIN, 525, 106,
FONT_NEXT, 533, 60,
FONT_NEXT, 513, 11,
FONT_NEXT, 552, 39,
FONT_NEXT, 572, 0,
FONT_NEXT, 589, 30,
FONT_NEXT, 615, 6,
FONT_NEXT, 620, 36,
FONT_END, 650, 53,
FONT_BEGIN, 615, 6,
FONT_NEXT, 650, 53,
FONT_NEXT, 654, 25,
FONT_END, 688, 53,
FONT_BEGIN, 688, 53,
FONT_NEXT, 650, 53,
FONT_END, 700, 110,
FONT_BEGIN, 688, 53,
FONT_NEXT, 700, 110,
FONT_NEXT, 717, 88,
FONT_NEXT, 733, 183,
FONT_END, 746, 254,
FONT_BEGIN, 717, 88,
FONT_NEXT, 746, 254,
FONT_END, 757, 173,
FONT_ADVANCE, 833, 0
    },
    {
38,
FONT_BEGIN, 42, 144,
FONT_NEXT, 57, 215,
FONT_NEXT, 46, 99,
FONT_END, 59, 64,
FONT_BEGIN, 59, 64,
FONT_NEXT, 57, 215,
FONT_NEXT, 78, 36,
FONT_NEXT, 95, 274,
FONT_NEXT, 101, 16,
FONT_END, 134, 180,
FONT_BEGIN, 134, 180,
FONT_NEXT, 95, 274,
FONT_NEXT, 146, 238,
FONT_NEXT, 146, 320,
FONT_NEXT, 176, 283,
FONT_NEXT, 199, 355,
FONT_NEXT, 214, 317,
FONT_NEXT, 237, 377,
FONT_NEXT, 252, 343,
FONT_END, 240, 624,
FONT_BEGIN, 240, 624,
FONT_NEXT, 237, 377,
FONT_NEXT, 211, 573,
FONT_NEXT, 211, 452,
FONT_END, 202, 519,
FONT_BEGIN, 321, 416,
FONT_NEXT, 350, 432,
FONT_NEXT, 336, 384,
FONT_END, 387, 409,
FONT_BEGIN, 387, 409,
FONT_NEXT, 350, 432,
FONT_END, 389, 462,
FONT_BEGIN, 387, 409,
FONT_NEXT, 389, 462,
FONT_END, 423, 504,
FONT_BEGIN, 387, 409,
FONT_NEXT, 423, 504,
FONT_NEXT, 437, 444,
FONT_NEXT, 438, 560,
FONT_NEXT, 450, 644,
FONT_NEXT, 431, 594,
FONT_NEXT, 407, 667,
FONT_NEXT, 414, 620,
FONT_END, 389, 637,
FONT_BEGIN, 407, 667,
FONT_NEXT, 389, 637,
FONT_NEXT, 355, 676,
FONT_NEXT, 359, 644,
FONT_END, 329, 639,
FONT_BEGIN, 355, 676,
FONT_NEXT, 329, 639,
FONT_NEXT, 319, 672,
FONT_NEXT, 304, 623,
FONT_NEXT, 288, 661,
FONT_NEXT, 287, 596,
FONT_NEXT, 240, 624,
FONT_NEXT, 281, 557,
FONT_NEXT, 252, 343,
FONT_NEXT, 293, 483,
FONT_END, 321, 416,
FONT_BEGIN, 252, 343,
FONT_NEXT, 321, 416,
FONT_NEXT, 322, 219,
FONT_NEXT, 336, 384,
FONT_END, 396, 277,
FONT_BEGIN, 322, 219,
FONT_NEXT, 396, 277,
FONT_NEXT, 404, 104,
FONT_NEXT, 429, 78,
FONT_END, 394, 49,
FONT_BEGIN, 404, 104,
FONT_NEXT, 394, 49,
FONT_NEXT, 356, 69,
FONT_NEXT, 347, 19,
FONT_NEXT, 316, 49,
FONT_NEXT, 286, -4,
FONT_NEXT, 285, 41,
FONT_NEXT, 207, -13,
FONT_NEXT, 263, 39,
FONT_END, 212, 50,
FONT_BEGIN, 212, 50,
FONT_NEXT, 207, -13,
FONT_NEXT, 171, 80,
FONT_NEXT, 155, -7,
FONT_NEXT, 144, 125,
FONT_NEXT, 101, 16,
FONT_END, 134, 180,
FONT_BEGIN, 491, 556,
FONT_NEXT, 486, 521,
FONT_NEXT, 480, 606,
FONT_NEXT, 475, 492,
FONT_NEXT, 450, 644,
FONT_END, 437, 444,
FONT_BEGIN, 495, 405,
FONT_NEXT, 495, 426,
FONT_NEXT, 540, 395,
FONT_NEXT, 617, 346,
FONT_NEXT, 554, 381,
FONT_NEXT, 591, 300,
FONT_NEXT, 559, 359,
FONT_NEXT, 568, 261,
FONT_END, 550, 232,
FONT_BEGIN, 559, 359,
FONT_NEXT, 550, 232,
FONT_NEXT, 548, 310,
FONT_NEXT, 528, 200,
FONT_NEXT, 524, 259,
FONT_NEXT, 491, 150,
FONT_NEXT, 494, 213,
FONT_END, 468, 178,
FONT_BEGIN, 468, 178,
FONT_NEXT, 491, 150,
FONT_END, 456, 51,
FONT_BEGIN, 468, 178,
FONT_NEXT, 456, 51,
FONT_NEXT, 396, 277,
FONT_END, 429, 78,
FONT_BEGIN, 711, 426,
FONT_NEXT, 711, 405,
FONT_NEXT, 495, 426,
FONT_NEXT, 661, 392,
FONT_END, 638, 375,
FONT_BEGIN, 495, 426,
FONT_NEXT, 638, 375,
FONT_END, 617, 346,
FONT_BEGIN, 750, 100,
FONT_NEXT, 747, 93,
FONT_NEXT, 735, 111,
FONT_NEXT, 740, 80,
FONT_END, 713, 42,
FONT_BEGIN, 735, 111,
FONT_NEXT, 713, 42,
FONT_NEXT, 697, 73,
FONT_NEXT, 667, 4,
FONT_NEXT, 672, 62,
FONT_END, 639, 58,
FONT_BEGIN, 639, 58,
FONT_NEXT, 667, 4,
FONT_END, 635, -9,
FONT_BEGIN, 639, 58,
FONT_NEXT, 635, -9,
FONT_NEXT, 585, 70,
FONT_NEXT, 599, -13,
FONT_END, 542, -3,
FONT_BEGIN, 585, 70,
FONT_NEXT, 542, -3,
FONT_NEXT, 540, 98,
FONT_NEXT, 494, 21,
FONT_NEXT, 507, 129,
FONT_END, 491, 150,
FONT_BEGIN, 491, 150,
FONT_NEXT, 494, 21,
FONT_END, 456, 51,
FONT_BEGIN, 491, 150,
FONT_END, 456, 51,
FONT_ADVANCE, 778, 0
    },
    {
39,
FONT_BEGIN, 106, 433,
FONT_NEXT, 97, 452,
FONT_NEXT, 136, 450,
FONT_NEXT, 137, 483,
FONT_END, 162, 513,
FONT_BEGIN, 136, 450,
FONT_NEXT, 162, 513,
FONT_NEXT, 173, 483,
FONT_NEXT, 175, 539,
FONT_END, 179, 558,
FONT_BEGIN, 173, 483,
FONT_NEXT, 179, 558,
FONT_NEXT, 204, 528,
FONT_END, 186, 659,
FONT_BEGIN, 186, 659,
FONT_NEXT, 179, 558,
FONT_NEXT, 159, 672,
FONT_NEXT, 174, 568,
FONT_END, 165, 572,
FONT_BEGIN, 159, 672,
FONT_NEXT, 165, 572,
FONT_END, 156, 570,
FONT_BEGIN, 159, 672,
FONT_NEXT, 156, 570,
FONT_NEXT, 137, 676,
FONT_NEXT, 141, 568,
FONT_END, 100, 578,
FONT_BEGIN, 137, 676,
FONT_NEXT, 100, 578,
FONT_NEXT, 98, 663,
FONT_NEXT, 85, 593,
FONT_NEXT, 84, 645,
FONT_END, 79, 618,
FONT_BEGIN, 218, 580,
FONT_NEXT, 204, 528,
FONT_NEXT, 208, 630,
FONT_END, 186, 659,
FONT_ADVANCE, 333, 0
    },
    {
40,
FONT_BEGIN, 48, 262,
FONT_NEXT, 52, 325,
FONT_NEXT, 53, 169,
FONT_NEXT, 64, 386,
FONT_NEXT, 70, 96,
FONT_NEXT, 85, 444,
FONT_NEXT, 96, 35,
FONT_NEXT, 113, 499,
FONT_NEXT, 130, -20,
FONT_NEXT, 134, 225,
FONT_END, 136, 174,
FONT_BEGIN, 130, -20,
FONT_NEXT, 136, 174,
FONT_END, 155, 56,
FONT_BEGIN, 130, -20,
FONT_NEXT, 155, 56,
FONT_END, 175, -4,
FONT_BEGIN, 130, -20,
FONT_NEXT, 175, -4,
FONT_NEXT, 203, -108,
FONT_NEXT, 205, -63,
FONT_NEXT, 247, -148,
FONT_NEXT, 247, -116,
FONT_NEXT, 292, -177,
FONT_END, 304, -161,
FONT_BEGIN, 304, 660,
FONT_NEXT, 246, 609,
FONT_NEXT, 295, 676,
FONT_END, 239, 638,
FONT_BEGIN, 239, 638,
FONT_NEXT, 246, 609,
FONT_END, 191, 537,
FONT_BEGIN, 239, 638,
FONT_NEXT, 191, 537,
FONT_NEXT, 191, 596,
FONT_END, 148, 549,
FONT_BEGIN, 148, 549,
FONT_NEXT, 191, 537,
FONT_END, 168, 488,
FONT_BEGIN, 148, 549,
FONT_NEXT, 168, 488,
FONT_END, 150, 428,
FONT_BEGIN, 148, 549,
FONT_NEXT, 150, 428,
FONT_END, 138, 356,
FONT_BEGIN, 148, 549,
FONT_NEXT, 138, 356,
FONT_NEXT, 113, 499,
FONT_NEXT, 134, 269,
FONT_END, 134, 225,
FONT_ADVANCE, 333, 0
    },
    {
41,
FONT_BEGIN, 29, -161,
FONT_NEXT, 86, -111,
FONT_NEXT, 38, -177,
FONT_END, 93, -140,
FONT_BEGIN, 93, -140,
FONT_NEXT, 86, -111,
FONT_NEXT, 141, -98,
FONT_NEXT, 141, -39,
FONT_END, 164, 10,
FONT_BEGIN, 141, -98,
FONT_NEXT, 164, 10,
FONT_END, 182, 70,
FONT_BEGIN, 141, -98,
FONT_NEXT, 182, 70,
FONT_NEXT, 184, -51,
FONT_NEXT, 194, 142,
FONT_END, 199, 230,
FONT_BEGIN, 184, -51,
FONT_NEXT, 199, 230,
FONT_NEXT, 219, 0,
FONT_END, 203, 519,
FONT_BEGIN, 203, 519,
FONT_NEXT, 199, 230,
FONT_END, 198, 273,
FONT_BEGIN, 203, 519,
FONT_NEXT, 198, 273,
FONT_END, 196, 324,
FONT_BEGIN, 203, 519,
FONT_NEXT, 196, 324,
FONT_END, 177, 442,
FONT_BEGIN, 203, 519,
FONT_NEXT, 177, 442,
FONT_NEXT, 129, 606,
FONT_NEXT, 157, 502,
FONT_END, 127, 561,
FONT_BEGIN, 129, 606,
FONT_NEXT, 127, 561,
FONT_NEXT, 85, 646,
FONT_NEXT, 85, 614,
FONT_NEXT, 41, 676,
FONT_END, 29, 660,
FONT_BEGIN, 285, 237,
FONT_NEXT, 280, 173,
FONT_NEXT, 279, 329,
FONT_NEXT, 268, 112,
FONT_NEXT, 262, 402,
FONT_NEXT, 247, 54,
FONT_NEXT, 236, 463,
FONT_NEXT, 219, 0,
FONT_END, 203, 519,
FONT_ADVANCE, 333, 0
    },
    {
42,
FONT_BEGIN, 69, 557,
FONT_NEXT, 75, 576,
FONT_NEXT, 72, 538,
FONT_END, 83, 526,
FONT_BEGIN, 83, 526,
FONT_NEXT, 75, 576,
FONT_END, 85, 586,
FONT_BEGIN, 83, 526,
FONT_NEXT, 85, 586,
FONT_END, 102, 591,
FONT_BEGIN, 83, 526,
FONT_NEXT, 102, 591,
FONT_NEXT, 120, 511,
FONT_NEXT, 129, 581,
FONT_END, 152, 557,
FONT_BEGIN, 120, 511,
FONT_NEXT, 152, 557,
FONT_NEXT, 170, 500,
FONT_NEXT, 185, 523,
FONT_NEXT, 223, 478,
FONT_END, 234, 471,
FONT_BEGIN, 234, 471,
FONT_NEXT, 185, 523,
FONT_END, 241, 486,
FONT_BEGIN, 234, 471,
FONT_NEXT, 241, 486,
FONT_END, 240, 456,
FONT_BEGIN, 234, 471,
FONT_NEXT, 240, 456,
FONT_END, 210, 435,
FONT_BEGIN, 234, 471,
FONT_NEXT, 210, 435,
FONT_NEXT, 172, 441,
FONT_NEXT, 192, 422,
FONT_END, 164, 394,
FONT_BEGIN, 172, 441,
FONT_NEXT, 164, 394,
FONT_NEXT, 120, 428,
FONT_NEXT, 143, 371,
FONT_END, 129, 358,
FONT_BEGIN, 120, 428,
FONT_NEXT, 129, 358,
FONT_END, 103, 351,
FONT_BEGIN, 120, 428,
FONT_NEXT, 103, 351,
FONT_NEXT, 82, 415,
FONT_NEXT, 76, 362,
FONT_NEXT, 72, 402,
FONT_END, 69, 383,
FONT_BEGIN, 214, 304,
FONT_NEXT, 219, 331,
FONT_NEXT, 225, 275,
FONT_NEXT, 230, 365,
FONT_END, 240, 405,
FONT_BEGIN, 225, 275,
FONT_NEXT, 240, 405,
FONT_NEXT, 249, 265,
FONT_NEXT, 241, 449,
FONT_NEXT, 241, 499,
FONT_END, 241, 486,
FONT_BEGIN, 241, 486,
FONT_NEXT, 241, 449,
FONT_END, 240, 456,
FONT_BEGIN, 241, 486,
FONT_END, 240, 456,
FONT_BEGIN, 216, 637,
FONT_NEXT, 220, 659,
FONT_NEXT, 228, 581,
FONT_NEXT, 231, 671,
FONT_NEXT, 237, 548,
FONT_END, 241, 499,
FONT_BEGIN, 241, 499,
FONT_NEXT, 231, 671,
FONT_END, 253, 676,
FONT_BEGIN, 241, 499,
FONT_NEXT, 253, 676,
FONT_NEXT, 249, 265,
FONT_END, 260, 449,
FONT_BEGIN, 260, 449,
FONT_NEXT, 253, 676,
FONT_NEXT, 260, 456,
FONT_END, 260, 484,
FONT_BEGIN, 260, 484,
FONT_NEXT, 253, 676,
FONT_NEXT, 260, 510,
FONT_NEXT, 269, 671,
FONT_NEXT, 273, 582,
FONT_NEXT, 279, 661,
FONT_NEXT, 282, 613,
FONT_END, 287, 637,
FONT_BEGIN, 288, 307,
FONT_NEXT, 277, 278,
FONT_NEXT, 274, 352,
FONT_NEXT, 249, 265,
FONT_NEXT, 264, 389,
FONT_END, 260, 449,
FONT_BEGIN, 431, 557,
FONT_NEXT, 428, 537,
FONT_NEXT, 427, 574,
FONT_NEXT, 420, 525,
FONT_NEXT, 417, 585,
FONT_NEXT, 388, 513,
FONT_NEXT, 395, 593,
FONT_END, 368, 581,
FONT_BEGIN, 368, 581,
FONT_NEXT, 388, 513,
FONT_END, 336, 502,
FONT_BEGIN, 368, 581,
FONT_NEXT, 336, 502,
FONT_NEXT, 341, 554,
FONT_END, 309, 520,
FONT_BEGIN, 309, 520,
FONT_NEXT, 336, 502,
FONT_END, 304, 489,
FONT_BEGIN, 309, 520,
FONT_NEXT, 304, 489,
FONT_NEXT, 267, 488,
FONT_NEXT, 268, 470,
FONT_END, 267, 453,
FONT_BEGIN, 267, 488,
FONT_NEXT, 267, 453,
FONT_NEXT, 260, 484,
FONT_END, 260, 456,
FONT_BEGIN, 432, 386,
FONT_NEXT, 421, 359,
FONT_NEXT, 428, 404,
FONT_END, 419, 416,
FONT_BEGIN, 419, 416,
FONT_NEXT, 421, 359,
FONT_END, 396, 350,
FONT_BEGIN, 419, 416,
FONT_NEXT, 396, 350,
FONT_NEXT, 384, 428,
FONT_NEXT, 366, 362,
FONT_NEXT, 334, 439,
FONT_NEXT, 338, 391,
FONT_END, 307, 425,
FONT_BEGIN, 334, 439,
FONT_NEXT, 307, 425,
FONT_NEXT, 276, 465,
FONT_NEXT, 267, 453,
FONT_END, 268, 470,
FONT_ADVANCE, 500, 0
    },
    {
43,
FONT_BEGIN, 315, 506,
FONT_NEXT, 315, 286,
FONT_NEXT, 249, 506,
FONT_NEXT, 315, 220,
FONT_END, 315, 0,
FONT_BEGIN, 249, 506,
FONT_NEXT, 315, 0,
FONT_END, 249, 0,
FONT_BEGIN, 249, 506,
FONT_NEXT, 249, 0,
FONT_NEXT, 249, 286,
FONT_NEXT, 249, 220,
FONT_NEXT, 30, 286,
FONT_END, 30, 220,
FONT_BEGIN, 534, 286,
FONT_NEXT, 534, 220,
FONT_NEXT, 315, 286,
FONT_END, 315, 220,
FONT_ADVANCE, 564, 0
    },
    {
44,
FONT_BEGIN, 83, -141,
FONT_NEXT, 74, -122,
FONT_NEXT, 113, -124,
FONT_NEXT, 114, -91,
FONT_END, 139, -61,
FONT_BEGIN, 113, -124,
FONT_NEXT, 139, -61,
FONT_NEXT, 150, -91,
FONT_NEXT, 152, -35,
FONT_END, 156, -16,
FONT_BEGIN, 150, -91,
FONT_NEXT, 156, -16,
FONT_NEXT, 181, -46,
FONT_END, 163, 85,
FONT_BEGIN, 163, 85,
FONT_NEXT, 156, -16,
FONT_NEXT, 136, 98,
FONT_NEXT, 151, -6,
FONT_END, 142, -2,
FONT_BEGIN, 136, 98,
FONT_NEXT, 142, -2,
FONT_END, 133, -4,
FONT_BEGIN, 136, 98,
FONT_NEXT, 133, -4,
FONT_NEXT, 114, 102,
FONT_NEXT, 118, -6,
FONT_END, 77, 4,
FONT_BEGIN, 114, 102,
FONT_NEXT, 77, 4,
FONT_NEXT, 75, 89,
FONT_NEXT, 62, 19,
FONT_NEXT, 61, 71,
FONT_END, 56, 44,
FONT_BEGIN, 195, 6,
FONT_NEXT, 181, -46,
FONT_NEXT, 185, 56,
FONT_END, 163, 85,
FONT_ADVANCE, 250, 0
    },
    {
45,
FONT_BEGIN, 285, 257,
FONT_NEXT, 285, 194,
FONT_NEXT, 39, 257,
FONT_END, 39, 194,
FONT_ADVANCE, 333, 0
    },
    {
46,
FONT_BEGIN, 181, 43,
FONT_NEXT, 175, 18,
FONT_NEXT, 176, 64,
FONT_END, 164, 83,
FONT_BEGIN, 164, 83,
FONT_NEXT, 175, 18,
FONT_END, 160, 2,
FONT_BEGIN, 164, 83,
FONT_NEXT, 160, 2,
FONT_NEXT, 146, 95,
FONT_NEXT, 125, -11,
FONT_NEXT, 125, 100,
FONT_END, 103, 95,
FONT_BEGIN, 103, 95,
FONT_NEXT, 125, -11,
FONT_END, 89, 1,
FONT_BEGIN, 103, 95,
FONT_NEXT, 89, 1,
FONT_NEXT, 86, 83,
FONT_NEXT, 75, 18,
FONT_NEXT, 74, 65,
FONT_END, 70, 43,
FONT_ADVANCE, 250, 0
    },
    {
47,
FONT_BEGIN, 287, 676,
FONT_NEXT, 59, -14,
FONT_NEXT, 220, 676,
FONT_END, -9, -14,
FONT_ADVANCE, 278, 0
    },
    {
48,
FONT_BEGIN, 476, 331,
FONT_NEXT, 466, 226,
FONT_NEXT, 473, 379,
FONT_END, 466, 434,
FONT_BEGIN, 466, 434,

FONT_NEXT, 466, 226,
FONT_NEXT, 432, 547,
FONT_NEXT, 432, 113,
FONT_NEXT, 402, 597,
FONT_NEXT, 402, 63,
FONT_NEXT, 362, 638,
FONT_NEXT, 377, 409,
FONT_END, 363, 516,
FONT_BEGIN, 362, 638,
FONT_NEXT, 363, 516,
FONT_END, 347, 566,
FONT_BEGIN, 362, 638,
FONT_NEXT, 347, 566,
FONT_NEXT, 312, 666,
FONT_NEXT, 324, 609,
FONT_END, 292, 639,
FONT_BEGIN, 312, 666,
FONT_NEXT, 292, 639,
FONT_NEXT, 250, 676,
FONT_NEXT, 250, 650,
FONT_NEXT, 187, 666,
FONT_NEXT, 207, 639,
FONT_END, 175, 609,
FONT_BEGIN, 187, 666,
FONT_NEXT, 175, 609,
FONT_NEXT, 137, 638,
FONT_NEXT, 152, 566,
FONT_END, 137, 516,
FONT_BEGIN, 137, 638,
FONT_NEXT, 137, 516,
FONT_NEXT, 97, 597,
FONT_NEXT, 122, 409,
FONT_END, 120, 364,
FONT_BEGIN, 97, 597,
FONT_NEXT, 120, 364,
FONT_END, 120, 331,
FONT_BEGIN, 97, 597,
FONT_NEXT, 120, 331,
FONT_END, 120, 294,
FONT_BEGIN, 97, 597,
FONT_NEXT, 120, 294,
FONT_END, 97, 63,
FONT_BEGIN, 97, 597,
FONT_NEXT, 97, 63,
FONT_NEXT, 68, 547,
FONT_NEXT, 68, 113,
FONT_NEXT, 33, 434,
FONT_NEXT, 33, 226,
FONT_NEXT, 26, 379,
FONT_END, 24, 331,
FONT_BEGIN, 120, 294,
FONT_NEXT, 122, 246,
FONT_NEXT, 97, 63,
FONT_END, 137, 22,
FONT_BEGIN, 137, 22,
FONT_NEXT, 122, 246,
FONT_END, 137, 141,
FONT_BEGIN, 137, 22,
FONT_NEXT, 137, 141,
FONT_END, 152, 91,
FONT_BEGIN, 137, 22,
FONT_NEXT, 152, 91,
FONT_END, 175, 50,
FONT_BEGIN, 137, 22,
FONT_NEXT, 175, 50,
FONT_NEXT, 187, -5,
FONT_NEXT, 207, 22,
FONT_NEXT, 250, -14,
FONT_NEXT, 250, 12,
FONT_END, 292, 22,
FONT_BEGIN, 250, -14,
FONT_NEXT, 292, 22,
FONT_NEXT, 312, -5,
FONT_NEXT, 324, 50,
FONT_END, 347, 92,
FONT_BEGIN, 312, -5,
FONT_NEXT, 347, 92,
FONT_NEXT, 362, 22,
FONT_NEXT, 363, 141,
FONT_END, 377, 247,
FONT_BEGIN, 362, 22,
FONT_NEXT, 377, 247,
FONT_NEXT, 402, 63,
FONT_NEXT, 379, 294,
FONT_END, 380, 331,
FONT_BEGIN, 402, 63,
FONT_NEXT, 380, 331,
FONT_END, 379, 364,
FONT_BEGIN, 402, 63,
FONT_NEXT, 379, 364,
FONT_END, 377, 409,
FONT_ADVANCE, 500, 0
    },
    {
49,
FONT_BEGIN, 299, 674,
FONT_NEXT, 299, 74,
FONT_NEXT, 291, 676,
FONT_NEXT, 213, 93,
FONT_END, 213, 546,
FONT_BEGIN, 291, 676,
FONT_NEXT, 213, 546,
FONT_END, 208, 577,
FONT_BEGIN, 291, 676,
FONT_NEXT, 208, 577,
FONT_END, 198, 588,
FONT_BEGIN, 291, 676,
FONT_NEXT, 198, 588,
FONT_END, 179, 593,
FONT_BEGIN, 291, 676,
FONT_NEXT, 179, 593,
FONT_NEXT, 111, 585,
FONT_NEXT, 161, 590,
FONT_END, 143, 583,
FONT_BEGIN, 111, 585,
FONT_NEXT, 143, 583,
FONT_END, 111, 571,
FONT_BEGIN, 394, 15,
FONT_NEXT, 394, 0,
FONT_NEXT, 347, 17,
FONT_NEXT, 118, 0,
FONT_NEXT, 318, 26,
FONT_END, 303, 44,
FONT_BEGIN, 303, 44,
FONT_NEXT, 118, 0,
FONT_END, 187, 27,
FONT_BEGIN, 303, 44,
FONT_NEXT, 187, 27,
FONT_NEXT, 299, 74,
FONT_NEXT, 206, 49,
FONT_END, 213, 93,
FONT_BEGIN, 118, 15,
FONT_NEXT, 157, 17,
FONT_NEXT, 118, 0,
FONT_END, 187, 27,
FONT_ADVANCE, 500, 0
    },
    {
50,
FONT_BEGIN, 475, 137,
FONT_NEXT, 420, 0,
FONT_NEXT, 462, 142,
FONT_END, 435, 105,
FONT_BEGIN, 435, 105,
FONT_NEXT, 420, 0,
FONT_NEXT, 413, 85,
FONT_NEXT, 30, 0,
FONT_NEXT, 367, 76,
FONT_END, 128, 76,
FONT_BEGIN, 128, 76,
FONT_NEXT, 30, 0,
FONT_END, 30, 12,
FONT_BEGIN, 128, 76,
FONT_NEXT, 30, 12,
FONT_END, 208, 201,
FONT_BEGIN, 128, 76,
FONT_NEXT, 208, 201,
FONT_END, 265, 270,
FONT_BEGIN, 128, 76,
FONT_NEXT, 265, 270,
FONT_NEXT, 296, 252,
FONT_NEXT, 305, 336,
FONT_END, 329, 400,
FONT_BEGIN, 296, 252,
FONT_NEXT, 329, 400,
FONT_NEXT, 334, 295,
FONT_NEXT, 338, 459,
FONT_NEXT, 367, 628,
FONT_NEXT, 332, 502,
FONT_NEXT, 309, 663,
FONT_NEXT, 310, 549,
FONT_END, 267, 586,
FONT_BEGIN, 309, 663,
FONT_NEXT, 267, 586,
FONT_NEXT, 243, 676,
FONT_NEXT, 235, 597,
FONT_NEXT, 176, 666,
FONT_NEXT, 195, 602,
FONT_END, 137, 589,
FONT_BEGIN, 176, 666,
FONT_NEXT, 137, 589,
FONT_NEXT, 115, 633,
FONT_NEXT, 97, 557,
FONT_NEXT, 88, 606,
FONT_NEXT, 70, 515,
FONT_NEXT, 64, 571,
FONT_NEXT, 52, 472,
FONT_NEXT, 45, 528,
FONT_END, 31, 477,
FONT_BEGIN, 424, 496,
FONT_NEXT, 410, 419,
FONT_NEXT, 419, 535,
FONT_END, 408, 571,
FONT_BEGIN, 408, 571,
FONT_NEXT, 410, 419,
FONT_END, 376, 351,
FONT_BEGIN, 408, 571,
FONT_NEXT, 376, 351,
FONT_NEXT, 367, 628,
FONT_END, 334, 295,
FONT_ADVANCE, 500, 0
    },
    {
51,
FONT_BEGIN, 153, 330,
FONT_NEXT, 152, 343,
FONT_NEXT, 197, 330,
FONT_NEXT, 216, 368,
FONT_NEXT, 230, 326,
FONT_NEXT, 269, 403,
FONT_NEXT, 283, 308,
FONT_END, 304, 401,
FONT_BEGIN, 304, 401,
FONT_NEXT, 269, 403,
FONT_END, 304, 450,
FONT_BEGIN, 304, 401,
FONT_NEXT, 304, 450,
FONT_END, 318, 512,
FONT_BEGIN, 304, 401,
FONT_NEXT, 318, 512,
FONT_NEXT, 346, 433,
FONT_END, 319, 660,
FONT_BEGIN, 319, 660,
FONT_NEXT, 318, 512,
FONT_END, 311, 549,
FONT_BEGIN, 319, 660,
FONT_NEXT, 311, 549,
FONT_NEXT, 284, 671,
FONT_NEXT, 290, 583,
FONT_END, 256, 606,
FONT_BEGIN, 284, 671,
FONT_NEXT, 256, 606,
FONT_NEXT, 241, 676,
FONT_NEXT, 208, 616,
FONT_NEXT, 187, 669,
FONT_NEXT, 148, 603,
FONT_NEXT, 132, 644,
FONT_NEXT, 105, 573,
FONT_NEXT, 83, 594,
FONT_NEXT, 76, 538,
FONT_NEXT, 62, 558,
FONT_NEXT, 60, 510,
FONT_END, 45, 514,
FONT_BEGIN, 43, 43,
FONT_NEXT, 46, 60,
FONT_NEXT, 56, 12,
FONT_NEXT, 56, 71,
FONT_END, 82, 78,
FONT_BEGIN, 56, 12,
FONT_NEXT, 82, 78,
FONT_NEXT, 89, -5,
FONT_NEXT, 115, 69,
FONT_NEXT, 126, -13,
FONT_NEXT, 148, 50,
FONT_NEXT, 154, -14,
FONT_NEXT, 184, 30,
FONT_NEXT, 210, -10,
FONT_NEXT, 225, 22,
FONT_NEXT, 263, 1,
FONT_NEXT, 285, 35,
FONT_NEXT, 310, 21,
FONT_NEXT, 326, 70,
FONT_NEXT, 351, 47,
FONT_NEXT, 351, 120,
FONT_END, 359, 176,
FONT_BEGIN, 351, 47,
FONT_NEXT, 359, 176,
FONT_NEXT, 383, 351,
FONT_END, 341, 383,
FONT_BEGIN, 341, 383,
FONT_NEXT, 359, 176,
FONT_END, 356, 202,
FONT_BEGIN, 341, 383,
FONT_NEXT, 356, 202,
FONT_END, 345, 239,
FONT_BEGIN, 341, 383,
FONT_NEXT, 345, 239,
FONT_END, 321, 276,
FONT_BEGIN, 341, 383,
FONT_NEXT, 321, 276,
FONT_NEXT, 304, 401,
FONT_END, 283, 308,
FONT_BEGIN, 431, 216,
FONT_NEXT, 425, 165,
FONT_NEXT, 427, 261,
FONT_END, 417, 298,
FONT_BEGIN, 417, 298,
FONT_NEXT, 425, 165,
FONT_END, 409, 120,
FONT_BEGIN, 417, 298,
FONT_NEXT, 409, 120,
FONT_NEXT, 383, 351,
FONT_NEXT, 384, 80,
FONT_END, 351, 47,
FONT_BEGIN, 397, 541,
FONT_NEXT, 391, 502,
FONT_NEXT, 390, 579,
FONT_NEXT, 375, 466,
FONT_NEXT, 367, 623,
FONT_NEXT, 346, 433,
FONT_END, 319, 660,
FONT_ADVANCE, 500, 0
    },
    {
52,
FONT_BEGIN, 292, 571,
FONT_NEXT, 293, 167,
FONT_NEXT, 292, 231,
FONT_NEXT, 12, 167,
FONT_NEXT, 52, 231,
FONT_NEXT, 12, 231,
FONT_NEXT, 290, 571,
FONT_NEXT, 326, 676,
FONT_NEXT, 292, 571,
FONT_END, 293, 167,
FONT_BEGIN, 293, 167,
FONT_NEXT, 326, 676,
FONT_NEXT, 293, 0,
FONT_END, 370, 0,
FONT_BEGIN, 370, 0,
FONT_NEXT, 326, 676,
FONT_NEXT, 370, 167,
FONT_END, 370, 231,
FONT_BEGIN, 370, 231,
FONT_NEXT, 326, 676,
FONT_END, 370, 676,
FONT_BEGIN, 370, 231,
FONT_END, 370, 676,
FONT_BEGIN, 472, 231,
FONT_NEXT, 472, 167,
FONT_NEXT, 370, 231,
FONT_END, 370, 167,
FONT_ADVANCE, 500, 0
    },
    {
53,
FONT_BEGIN, 64, 415,
FONT_NEXT, 65, 425,
FONT_NEXT, 71, 412,
FONT_NEXT, 139, 498,
FONT_NEXT, 161, 399,
FONT_END, 254, 365,
FONT_BEGIN, 254, 365,
FONT_NEXT, 139, 498,
FONT_END, 263, 468,
FONT_BEGIN, 254, 365,
FONT_NEXT, 263, 468,
FONT_NEXT, 294, 337,
FONT_NEXT, 321, 439,
FONT_NEXT, 326, 300,
FONT_END, 348, 255,
FONT_BEGIN, 348, 255,
FONT_NEXT, 321, 439,
FONT_NEXT, 356, 201,
FONT_NEXT, 372, 395,
FONT_END, 351, 55,
FONT_BEGIN, 356, 201,
FONT_NEXT, 351, 55,
FONT_NEXT, 352, 158,
FONT_END, 343, 121,
FONT_BEGIN, 343, 121,
FONT_NEXT, 351, 55,
FONT_END, 312, 26,
FONT_BEGIN, 343, 121,
FONT_NEXT, 312, 26,
FONT_NEXT, 310, 66,
FONT_NEXT, 266, 4,
FONT_NEXT, 266, 33,
FONT_END, 217, 23,
FONT_BEGIN, 217, 23,
FONT_NEXT, 266, 4,
FONT_END, 214, -10,
FONT_BEGIN, 217, 23,
FONT_NEXT, 214, -10,
FONT_NEXT, 72, 85,
FONT_NEXT, 158, -14,
FONT_END, 104, -10,
FONT_BEGIN, 72, 85,
FONT_NEXT, 104, -10,
FONT_END, 65, 2,
FONT_BEGIN, 72, 85,
FONT_NEXT, 65, 2,
FONT_NEXT, 48, 80,
FONT_NEXT, 40, 21,
FONT_NEXT, 37, 69,
FONT_END, 32, 46,
FONT_BEGIN, 438, 681,
FONT_NEXT, 400, 592,
FONT_NEXT, 429, 688,
FONT_END, 411, 669,
FONT_BEGIN, 411, 669,
FONT_NEXT, 400, 592,
FONT_NEXT, 383, 662,
FONT_NEXT, 391, 584,
FONT_END, 377, 583,
FONT_BEGIN, 383, 662,
FONT_NEXT, 377, 583,
FONT_NEXT, 174, 662,
FONT_NEXT, 181, 583,
FONT_END, 139, 498,
FONT_BEGIN, 174, 662,
FONT_NEXT, 139, 498,
FONT_END, 65, 425,
FONT_BEGIN, 426, 225,
FONT_NEXT, 420, 176,
FONT_NEXT, 423, 281,
FONT_END, 415, 323,
FONT_BEGIN, 415, 323,
FONT_NEXT, 420, 176,
FONT_END, 406, 131,
FONT_BEGIN, 415, 323,
FONT_NEXT, 406, 131,
FONT_NEXT, 398, 359,
FONT_NEXT, 382, 90,
FONT_NEXT, 372, 395,
FONT_END, 351, 55,
FONT_ADVANCE, 500, 0
    },
    {
54,
FONT_BEGIN, 448, 668,
FONT_NEXT, 384, 652,
FONT_NEXT, 446, 684,
FONT_END, 358, 673,
FONT_BEGIN, 358, 673,
FONT_NEXT, 384, 652,
FONT_END, 329, 628,
FONT_BEGIN, 358, 673,
FONT_NEXT, 329, 628,
FONT_NEXT, 279, 647,
FONT_NEXT, 281, 597,
FONT_END, 240, 560,
FONT_BEGIN, 279, 647,
FONT_NEXT, 240, 560,
FONT_NEXT, 209, 609,
FONT_NEXT, 207, 519,
FONT_NEXT, 149, 560,
FONT_NEXT, 181, 475,
FONT_END, 152, 383,
FONT_BEGIN, 149, 560,
FONT_NEXT, 152, 383,
FONT_END, 147, 350,
FONT_BEGIN, 149, 560,
FONT_NEXT, 147, 350,
FONT_NEXT, 100, 502,
FONT_NEXT, 133, 321,
FONT_END, 128, 294,
FONT_BEGIN, 100, 502,
FONT_NEXT, 128, 294,
FONT_END, 127, 256,
FONT_BEGIN, 100, 502,
FONT_NEXT, 127, 256,
FONT_END, 99, 59,
FONT_BEGIN, 100, 502,
FONT_NEXT, 99, 59,
FONT_NEXT, 64, 436,
FONT_NEXT, 72, 102,
FONT_END, 51, 154,
FONT_BEGIN, 64, 436,
FONT_NEXT, 51, 154,
FONT_NEXT, 41, 365,
FONT_NEXT, 38, 217,
FONT_END, 34, 291,
FONT_BEGIN, 468, 218,
FONT_NEXT, 459, 150,
FONT_NEXT, 464, 265,
FONT_END, 454, 307,
FONT_BEGIN, 454, 307,
FONT_NEXT, 459, 150,
FONT_END, 427, 74,
FONT_BEGIN, 454, 307,
FONT_NEXT, 427, 74,
FONT_NEXT, 437, 343,
FONT_END, 415, 373,
FONT_BEGIN, 415, 373,
FONT_NEXT, 427, 74,
FONT_END, 399, 40,
FONT_BEGIN, 415, 373,
FONT_NEXT, 399, 40,
FONT_NEXT, 357, 414,
FONT_NEXT, 378, 179,
FONT_END, 371, 254,
FONT_BEGIN, 357, 414,
FONT_NEXT, 371, 254,
FONT_END, 349, 319,
FONT_BEGIN, 357, 414,
FONT_NEXT, 349, 319,
FONT_NEXT, 284, 428,
FONT_NEXT, 331, 344,
FONT_END, 307, 364,
FONT_BEGIN, 284, 428,
FONT_NEXT, 307, 364,
FONT_END, 278, 377,
FONT_BEGIN, 284, 428,
FONT_NEXT, 278, 377,
FONT_NEXT, 247, 425,
FONT_NEXT, 242, 382,
FONT_NEXT, 217, 417,
FONT_NEXT, 191, 375,
FONT_NEXT, 187, 403,
FONT_NEXT, 147, 350,
FONT_END, 152, 383,
FONT_BEGIN, 127, 256,
FONT_NEXT, 130, 197,
FONT_NEXT, 99, 59,
FONT_END, 132, 27,
FONT_BEGIN, 132, 27,
FONT_NEXT, 130, 197,
FONT_END, 138, 147,
FONT_BEGIN, 132, 27,
FONT_NEXT, 138, 147,
FONT_END, 152, 105,
FONT_BEGIN, 132, 27,
FONT_NEXT, 152, 105,
FONT_NEXT, 169, 4,
FONT_NEXT, 169, 72,
FONT_END, 191, 46,
FONT_BEGIN, 169, 4,
FONT_NEXT, 191, 46,
FONT_NEXT, 210, -10,
FONT_NEXT, 215, 28,
FONT_NEXT, 254, -14,
FONT_NEXT, 269, 14,
FONT_END, 312, 23,
FONT_BEGIN, 254, -14,
FONT_NEXT, 312, 23,
FONT_NEXT, 314, -8,
FONT_NEXT, 347, 54,
FONT_NEXT, 362, 11,
FONT_NEXT, 369, 105,
FONT_END, 378, 179,
FONT_BEGIN, 362, 11,
FONT_NEXT, 378, 179,
FONT_END, 399, 40,
FONT_ADVANCE, 500, 0
    },
    {
55,
FONT_BEGIN, 449, 662,
FONT_NEXT, 449, 646,
FONT_NEXT, 79, 662,
FONT_END, 370, 588,
FONT_BEGIN, 370, 588,
FONT_NEXT, 449, 646,
FONT_END, 237, -8,
FONT_BEGIN, 370, 588,
FONT_NEXT, 237, -8,
FONT_END, 172, -8,
FONT_BEGIN, 37, 507,
FONT_NEXT, 20, 515,
FONT_NEXT, 59, 541,
FONT_NEXT, 63, 618,
FONT_END, 79, 662,
FONT_BEGIN, 59, 541,
FONT_NEXT, 79, 662,
FONT_NEXT, 83, 567,
FONT_END, 112, 582,
FONT_BEGIN, 112, 582,
FONT_NEXT, 79, 662,
FONT_NEXT, 153, 588,
FONT_END, 370, 588,
FONT_ADVANCE, 500, 0
    },
    {
56,
FONT_BEGIN, 136, 547,
FONT_NEXT, 150, 492,
FONT_NEXT, 131, 381,
FONT_NEXT, 185, 447,
FONT_NEXT, 186, 332,
FONT_END, 212, 312,
FONT_BEGIN, 212, 312,
FONT_NEXT, 185, 447,
FONT_END, 226, 412,
FONT_BEGIN, 212, 312,
FONT_NEXT, 226, 412,
FONT_END, 261, 389,
FONT_BEGIN, 212, 312,
FONT_NEXT, 261, 389,
FONT_NEXT, 285, 258,
FONT_END, 290, 371,
FONT_BEGIN, 290, 371,
FONT_NEXT, 261, 389,
FONT_END, 291, 410,
FONT_BEGIN, 290, 371,
FONT_NEXT, 291, 410,
FONT_END, 322, 440,
FONT_BEGIN, 290, 371,
FONT_NEXT, 322, 440,
FONT_NEXT, 333, 395,
FONT_NEXT, 345, 481,
FONT_END, 355, 539,
FONT_BEGIN, 333, 395,
FONT_NEXT, 355, 539,
FONT_NEXT, 377, 428,
FONT_NEXT, 348, 579,
FONT_NEXT, 378, 633,
FONT_END, 324, 664,
FONT_BEGIN, 324, 664,
FONT_NEXT, 348, 579,
FONT_END, 327, 614,
FONT_BEGIN, 324, 664,
FONT_NEXT, 327, 614,
FONT_END, 292, 638,
FONT_BEGIN, 324, 664,
FONT_NEXT, 292, 638,
FONT_NEXT, 249, 676,
FONT_NEXT, 243, 648,
FONT_NEXT, 183, 665,
FONT_NEXT, 194, 638,
FONT_END, 161, 615,
FONT_BEGIN, 183, 665,
FONT_NEXT, 161, 615,
FONT_NEXT, 123, 636,
FONT_NEXT, 142, 583,
FONT_END, 136, 547,
FONT_BEGIN, 123, 636,
FONT_NEXT, 136, 547,
FONT_END, 131, 381,
FONT_BEGIN, 123, 636,
FONT_NEXT, 131, 381,
FONT_END, 92, 423,
FONT_BEGIN, 123, 636,
FONT_NEXT, 92, 423,
FONT_NEXT, 79, 587,
FONT_NEXT, 69, 467,
FONT_NEXT, 66, 556,
FONT_END, 62, 521,
FONT_BEGIN, 56, 146,
FONT_NEXT, 63, 202,
FONT_NEXT, 68, 88,
FONT_NEXT, 87, 246,
FONT_NEXT, 104, 36,
FONT_NEXT, 128, 286,
FONT_NEXT, 132, 156,
FONT_END, 140, 215,
FONT_BEGIN, 140, 215,
FONT_NEXT, 128, 286,
FONT_NEXT, 160, 258,
FONT_END, 186, 289,
FONT_BEGIN, 186, 289,
FONT_NEXT, 128, 286,
FONT_END, 186, 332,
FONT_BEGIN, 186, 289,
FONT_NEXT, 186, 332,
FONT_END, 212, 312,
FONT_BEGIN, 285, 258,
FONT_NEXT, 290, 371,
FONT_NEXT, 333, 216,
FONT_NEXT, 335, 336,
FONT_NEXT, 360, 174,
FONT_END, 369, 126,
FONT_BEGIN, 369, 126,
FONT_NEXT, 335, 336,
FONT_END, 370, 305,
FONT_BEGIN, 369, 126,
FONT_NEXT, 370, 305,
FONT_END, 381, 24,
FONT_BEGIN, 369, 126,
FONT_NEXT, 381, 24,
FONT_END, 347, 4,
FONT_BEGIN, 369, 126,
FONT_NEXT, 347, 4,
FONT_NEXT, 360, 78,
FONT_END, 337, 43,
FONT_BEGIN, 337, 43,
FONT_NEXT, 347, 4,
FONT_END, 302, -10,
FONT_BEGIN, 337, 43,
FONT_NEXT, 302, -10,
FONT_NEXT, 302, 21,
FONT_END, 259, 14,
FONT_BEGIN, 259, 14,
FONT_NEXT, 302, -10,
FONT_END, 246, -14,
FONT_BEGIN, 259, 14,
FONT_NEXT, 246, -14,
FONT_NEXT, 204, 25,
FONT_NEXT, 202, -11,
FONT_NEXT, 165, 56,
FONT_NEXT, 164, 0,
FONT_NEXT, 140, 101,
FONT_NEXT, 104, 36,
FONT_END, 132, 156,
FONT_BEGIN, 445, 150,
FONT_NEXT, 437, 102,
FONT_NEXT, 439, 199,
FONT_END, 417, 249,
FONT_BEGIN, 417, 249,
FONT_NEXT, 437, 102,
FONT_END, 407, 48,
FONT_BEGIN, 417, 249,
FONT_NEXT, 407, 48,
FONT_NEXT, 370, 305,
FONT_END, 381, 24,
FONT_BEGIN, 424, 534,
FONT_NEXT, 410, 473,
FONT_NEXT, 412, 588,
FONT_END, 378, 633,
FONT_BEGIN, 378, 633,
FONT_NEXT, 410, 473,
FONT_END, 377, 428,
FONT_BEGIN, 378, 633,
FONT_END, 377, 428,
FONT_ADVANCE, 500, 0
    },
    {
57,
FONT_BEGIN, 459, 396,
FONT_NEXT, 452, 317,
FONT_NEXT, 454, 455,
FONT_END, 442, 510,
FONT_BEGIN, 442, 510,
FONT_NEXT, 452, 317,
FONT_END, 432, 243,
FONT_BEGIN, 442, 510,
FONT_NEXT, 432, 243,
FONT_NEXT, 422, 557,
FONT_NEXT, 399, 174,
FONT_NEXT, 395, 598,
FONT_NEXT, 354, 113,
FONT_END, 362, 355,
FONT_BEGIN, 395, 598,
FONT_NEXT, 362, 355,
FONT_NEXT, 363, 631,
FONT_NEXT, 362, 394,
FONT_END, 361, 421,
FONT_BEGIN, 363, 631,
FONT_NEXT, 361, 421,
FONT_END, 359, 458,
FONT_BEGIN, 363, 631,
FONT_NEXT, 359, 458,
FONT_END, 345, 543,
FONT_BEGIN, 363, 631,
FONT_NEXT, 345, 543,
FONT_NEXT, 326, 655,
FONT_NEXT, 329, 583,
FONT_END, 305, 616,
FONT_BEGIN, 326, 655,
FONT_NEXT, 305, 616,
FONT_NEXT, 284, 670,
FONT_NEXT, 272, 639,
FONT_NEXT, 240, 676,
FONT_NEXT, 229, 648,
FONT_NEXT, 193, 671,
FONT_NEXT, 200, 643,
FONT_END, 176, 632,
FONT_BEGIN, 193, 671,
FONT_NEXT, 176, 632,
FONT_NEXT, 152, 657,
FONT_NEXT, 144, 591,
FONT_NEXT, 116, 634,
FONT_NEXT, 126, 534,
FONT_END, 122, 473,
FONT_BEGIN, 116, 634,
FONT_NEXT, 122, 473,
FONT_END, 94, 278,
FONT_BEGIN, 116, 634,
FONT_NEXT, 94, 278,
FONT_NEXT, 86, 605,
FONT_NEXT, 69, 305,
FONT_NEXT, 62, 569,
FONT_NEXT, 39, 369,
FONT_NEXT, 44, 528,
FONT_END, 30, 435,
FONT_BEGIN, 56, -2,
FONT_NEXT, 138, 21,
FONT_NEXT, 59, -22,
FONT_END, 149, -9,
FONT_BEGIN, 149, -9,
FONT_NEXT, 138, 21,
FONT_END, 204, 56,
FONT_BEGIN, 149, -9,
FONT_NEXT, 204, 56,
FONT_NEXT, 228, 20,
FONT_NEXT, 256, 101,
FONT_END, 296, 149,
FONT_BEGIN, 228, 20,
FONT_NEXT, 296, 149,
FONT_NEXT, 297, 61,
FONT_NEXT, 324, 197,
FONT_END, 343, 239,
FONT_BEGIN, 297, 61,
FONT_NEXT, 343, 239,
FONT_NEXT, 354, 113,
FONT_NEXT, 354, 271,
FONT_END, 359, 290,
FONT_BEGIN, 354, 113,
FONT_NEXT, 359, 290,
FONT_NEXT, 362, 355,
FONT_END, 358, 332,
FONT_BEGIN, 358, 332,
FONT_NEXT, 359, 290,
FONT_END, 357, 292,
FONT_BEGIN, 358, 332,
FONT_NEXT, 357, 292,
FONT_NEXT, 346, 315,
FONT_NEXT, 285, 250,
FONT_NEXT, 302, 291,
FONT_END, 250, 280,
FONT_BEGIN, 250, 280,
FONT_NEXT, 285, 250,
FONT_END, 210, 237,
FONT_BEGIN, 250, 280,
FONT_NEXT, 210, 237,
FONT_NEXT, 214, 285,
FONT_END, 185, 299,
FONT_BEGIN, 185, 299,
FONT_NEXT, 210, 237,
FONT_END, 163, 242,
FONT_BEGIN, 185, 299,
FONT_NEXT, 163, 242,
FONT_NEXT, 146, 348,
FONT_NEXT, 125, 256,
FONT_NEXT, 127, 410,
FONT_END, 122, 473,
FONT_BEGIN, 122, 473,
FONT_NEXT, 125, 256,
FONT_END, 94, 278,
FONT_BEGIN, 122, 473,
FONT_END, 94, 278,
FONT_ADVANCE, 500, 0
    },
    {
58,
FONT_BEGIN, 192, 43,
FONT_NEXT, 186, 18,
FONT_NEXT, 187, 64,
FONT_END, 175, 83,
FONT_BEGIN, 175, 83,
FONT_NEXT, 186, 18,
FONT_END, 171, 2,
FONT_BEGIN, 175, 83,
FONT_NEXT, 171, 2,
FONT_NEXT, 157, 95,
FONT_NEXT, 136, -11,
FONT_NEXT, 136, 100,
FONT_END, 114, 95,
FONT_BEGIN, 114, 95,
FONT_NEXT, 136, -11,
FONT_END, 100, 1,
FONT_BEGIN, 114, 95,
FONT_NEXT, 100, 1,
FONT_NEXT, 97, 83,
FONT_NEXT, 86, 18,
FONT_NEXT, 85, 65,
FONT_END, 81, 43,
FONT_BEGIN, 192, 402,
FONT_NEXT, 186, 377,
FONT_NEXT, 187, 423,
FONT_END, 175, 442,
FONT_BEGIN, 175, 442,
FONT_NEXT, 186, 377,
FONT_END, 171, 361,
FONT_BEGIN, 175, 442,
FONT_NEXT, 171, 361,
FONT_NEXT, 157, 454,
FONT_NEXT, 136, 348,
FONT_NEXT, 136, 459,
FONT_END, 114, 454,
FONT_BEGIN, 114, 454,
FONT_NEXT, 136, 348,
FONT_END, 100, 360,
FONT_BEGIN, 114, 454,
FONT_NEXT, 100, 360,
FONT_NEXT, 97, 442,
FONT_NEXT, 86, 377,
FONT_NEXT, 85, 424,
FONT_END, 81, 402,
FONT_ADVANCE, 278, 0
    },
    {
59,
FONT_BEGIN, 107, -141,
FONT_NEXT, 98, -122,
FONT_NEXT, 138, -123,
FONT_NEXT, 138, -91,
FONT_END, 163, -61,
FONT_BEGIN, 138, -123,
FONT_NEXT, 163, -61,
FONT_NEXT, 175, -90,
FONT_NEXT, 176, -35,
FONT_END, 180, -16,
FONT_BEGIN, 175, -90,
FONT_NEXT, 180, -16,
FONT_NEXT, 206, -45,
FONT_END, 186, 85,
FONT_BEGIN, 186, 85,
FONT_NEXT, 180, -16,
FONT_NEXT, 160, 98,
FONT_NEXT, 175, -6,
FONT_END, 166, -2,
FONT_BEGIN, 160, 98,
FONT_NEXT, 166, -2,
FONT_END, 157, -4,
FONT_BEGIN, 160, 98,
FONT_NEXT, 157, -4,
FONT_NEXT, 139, 102,
FONT_NEXT, 142, -6,
FONT_END, 99, 5,
FONT_BEGIN, 139, 102,
FONT_NEXT, 99, 5,
FONT_NEXT, 125, 100,
FONT_END, 105, 93,
FONT_BEGIN, 105, 93,
FONT_NEXT, 99, 5,
FONT_NEXT, 87, 76,
FONT_NEXT, 85, 20,
FONT_END, 80, 44,
FONT_BEGIN, 219, 6,
FONT_NEXT, 206, -45,
FONT_NEXT, 209, 56,
FONT_END, 186, 85,
FONT_BEGIN, 192, 402,
FONT_NEXT, 186, 377,
FONT_NEXT, 187, 423,
FONT_END, 175, 442,
FONT_BEGIN, 175, 442,
FONT_NEXT, 186, 377,
FONT_END, 171, 361,
FONT_BEGIN, 175, 442,
FONT_NEXT, 171, 361,
FONT_NEXT, 157, 454,
FONT_NEXT, 136, 348,
FONT_NEXT, 136, 459,
FONT_END, 114, 454,
FONT_BEGIN, 114, 454,
FONT_NEXT, 136, 348,
FONT_END, 100, 360,
FONT_BEGIN, 114, 454,
FONT_NEXT, 100, 360,
FONT_NEXT, 97, 442,
FONT_NEXT, 86, 377,
FONT_NEXT, 85, 424,
FONT_END, 81, 402,
FONT_ADVANCE, 278, 0
    },
    {
60,
FONT_BEGIN, 536, 514,
FONT_NEXT, 536, 446,
FONT_NEXT, 28, 284,
FONT_NEXT, 111, 253,
FONT_NEXT, 28, 222,
FONT_END, 536, -8,
FONT_BEGIN, 536, -8,
FONT_NEXT, 111, 253,
FONT_END, 536, 60,
FONT_BEGIN, 536, -8,
FONT_END, 536, 60,
FONT_ADVANCE, 564, 0
    },
    {
61,
FONT_BEGIN, 534, 186,
FONT_NEXT, 534, 120,
FONT_NEXT, 30, 186,
FONT_END, 30, 120,
FONT_BEGIN, 534, 386,
FONT_NEXT, 534, 320,
FONT_NEXT, 30, 386,
FONT_END, 30, 320,
FONT_ADVANCE, 564, 0
    },
    {
62,
FONT_BEGIN, 28, 446,
FONT_NEXT, 28, 514,
FONT_NEXT, 453, 253,
FONT_NEXT, 536, 222,
FONT_END, 28, -8,
FONT_BEGIN, 453, 253,
FONT_NEXT, 28, -8,
FONT_END, 28, 60,
FONT_BEGIN, 536, 222,
FONT_NEXT, 28, 514,
FONT_END, 536, 284,
FONT_ADVANCE, 564, 0
    },
    {
63,
FONT_BEGIN, 68, 532,
FONT_NEXT, 72, 567,
FONT_NEXT, 78, 487,
FONT_NEXT, 83, 597,
FONT_NEXT, 93, 474,
FONT_END, 118, 469,
FONT_BEGIN, 118, 469,
FONT_NEXT, 83, 597,
FONT_END, 119, 585,
FONT_BEGIN, 118, 469,
FONT_NEXT, 119, 585,
FONT_NEXT, 135, 473,
FONT_END, 148, 483,
FONT_BEGIN, 148, 483,
FONT_NEXT, 119, 585,
FONT_END, 157, 510,
FONT_BEGIN, 148, 483,
FONT_END, 157, 510,
FONT_BEGIN, 414, 510,
FONT_NEXT, 406, 469,
FONT_NEXT, 412, 544,
FONT_END, 406, 571,
FONT_BEGIN, 406, 571,
FONT_NEXT, 406, 469,
FONT_NEXT, 378, 618,
FONT_NEXT, 387, 426,
FONT_END, 363, 385,
FONT_BEGIN, 378, 618,
FONT_NEXT, 363, 385,
FONT_NEXT, 352, 639,
FONT_NEXT, 339, 354,
FONT_NEXT, 316, 657,
FONT_NEXT, 322, 519,
FONT_END, 315, 564,
FONT_BEGIN, 316, 657,
FONT_NEXT, 315, 564,
FONT_NEXT, 274, 671,
FONT_NEXT, 295, 605,
FONT_END, 261, 634,
FONT_BEGIN, 274, 671,
FONT_NEXT, 261, 634,
FONT_NEXT, 231, 676,
FONT_NEXT, 212, 646,
FONT_NEXT, 176, 667,
FONT_NEXT, 179, 641,
FONT_END, 149, 627,
FONT_BEGIN, 176, 667,
FONT_NEXT, 149, 627,
FONT_NEXT, 123, 641,
FONT_NEXT, 127, 608,
FONT_END, 119, 585,
FONT_BEGIN, 123, 641,
FONT_NEXT, 119, 585,
FONT_END, 83, 597,
FONT_BEGIN, 227, 164,
FONT_NEXT, 239, 251,
FONT_NEXT, 244, 164,
FONT_NEXT, 249, 286,
FONT_NEXT, 257, 220,
FONT_NEXT, 257, 306,
FONT_NEXT, 276, 267,
FONT_NEXT, 285, 370,
FONT_NEXT, 302, 309,
FONT_NEXT, 310, 444,
FONT_END, 322, 519,
FONT_BEGIN, 302, 309,
FONT_NEXT, 322, 519,
FONT_END, 339, 354,
FONT_BEGIN, 292, 43,
FONT_NEXT, 286, 19,
FONT_NEXT, 287, 63,
FONT_END, 276, 80,
FONT_BEGIN, 276, 80,
FONT_NEXT, 286, 19,
FONT_END, 273, 4,
FONT_BEGIN, 276, 80,
FONT_NEXT, 273, 4,
FONT_NEXT, 258, 92,
FONT_NEXT, 237, -8,
FONT_NEXT, 237, 97,
FONT_END, 216, 92,
FONT_BEGIN, 216, 92,
FONT_NEXT, 237, -8,
FONT_END, 202, 3,
FONT_BEGIN, 216, 92,
FONT_NEXT, 202, 3,
FONT_NEXT, 199, 81,
FONT_NEXT, 189, 19,
FONT_NEXT, 188, 64,
FONT_END, 184, 43,
FONT_ADVANCE, 444, 0
    },
    {
64,
FONT_BEGIN, 321, 268,
FONT_NEXT, 323, 298,
FONT_NEXT, 323, 230,
FONT_END, 332, 200,
FONT_BEGIN, 332, 200,
FONT_NEXT, 323, 298,
FONT_END, 332, 335,
FONT_BEGIN, 332, 200,
FONT_NEXT, 332, 335,
FONT_NEXT, 358, 163,
FONT_NEXT, 368, 414,
FONT_NEXT, 390, 147,
FONT_NEXT, 392, 271,
FONT_END, 396, 232,
FONT_BEGIN, 390, 147,
FONT_NEXT, 396, 232,
FONT_END, 410, 206,
FONT_BEGIN, 390, 147,
FONT_NEXT, 410, 206,
FONT_NEXT, 417, 144,
FONT_NEXT, 430, 191,
FONT_END, 455, 187,
FONT_BEGIN, 417, 144,
FONT_NEXT, 455, 187,
FONT_NEXT, 457, 154,
FONT_NEXT, 489, 200,
FONT_NEXT, 492, 174,
FONT_END, 517, 196,
FONT_BEGIN, 517, 196,
FONT_NEXT, 489, 200,
FONT_END, 524, 240,
FONT_BEGIN, 517, 196,
FONT_NEXT, 524, 240,
FONT_NEXT, 532, 214,
FONT_END, 534, 214,
FONT_BEGIN, 534, 214,
FONT_NEXT, 524, 240,
FONT_END, 553, 173,
FONT_BEGIN, 534, 214,
FONT_NEXT, 553, 173,
FONT_END, 538, 197,
FONT_BEGIN, 668, 494,
FONT_NEXT, 603, 240,
FONT_NEXT, 599, 494,
FONT_NEXT, 603, 202,
FONT_END, 581, 152,
FONT_BEGIN, 599, 494,
FONT_NEXT, 581, 152,
FONT_NEXT, 589, 456,
FONT_NEXT, 572, 399,
FONT_NEXT, 565, 491,
FONT_NEXT, 569, 426,
FONT_END, 561, 447,
FONT_BEGIN, 565, 491,
FONT_NEXT, 561, 447,
FONT_NEXT, 546, 503,
FONT_NEXT, 548, 459,
FONT_END, 529, 464,
FONT_BEGIN, 546, 503,
FONT_NEXT, 529, 464,
FONT_NEXT, 518, 508,
FONT_NEXT, 487, 450,
FONT_NEXT, 470, 500,
FONT_NEXT, 442, 412,
FONT_NEXT, 430, 480,
FONT_NEXT, 406, 352,
FONT_NEXT, 396, 450,
FONT_NEXT, 395, 314,
FONT_NEXT, 368, 414,
FONT_END, 392, 271,
FONT_BEGIN, 116, 315,
FONT_NEXT, 123, 392,
FONT_NEXT, 121, 262,
FONT_END, 137, 207,
FONT_BEGIN, 137, 207,
FONT_NEXT, 123, 392,
FONT_END, 145, 462,
FONT_BEGIN, 137, 207,
FONT_NEXT, 145, 462,
FONT_NEXT, 165, 152,
FONT_NEXT, 179, 524,
FONT_NEXT, 200, 305,
FONT_END, 205, 383,
FONT_BEGIN, 205, 383,
FONT_NEXT, 179, 524,
FONT_NEXT, 222, 451,
FONT_NEXT, 224, 576,
FONT_NEXT, 248, 510,
FONT_NEXT, 278, 618,
FONT_NEXT, 283, 558,
FONT_END, 324, 595,
FONT_BEGIN, 324, 595,
FONT_NEXT, 278, 618,
FONT_END, 340, 649,
FONT_BEGIN, 324, 595,
FONT_NEXT, 340, 649,
FONT_NEXT, 372, 623,
FONT_NEXT, 408, 669,
FONT_NEXT, 424, 639,
FONT_END, 481, 645,
FONT_BEGIN, 481, 645,
FONT_NEXT, 408, 669,
FONT_END, 481, 676,
FONT_BEGIN, 481, 645,
FONT_NEXT, 481, 676,
FONT_NEXT, 545, 638,
FONT_NEXT, 547, 670,
FONT_NEXT, 603, 619,
FONT_NEXT, 609, 653,
FONT_NEXT, 652, 591,
FONT_NEXT, 665, 627,
FONT_NEXT, 693, 555,
FONT_NEXT, 714, 591,
FONT_NEXT, 726, 513,
FONT_END, 749, 469,
FONT_BEGIN, 749, 469,
FONT_NEXT, 714, 591,
FONT_END, 753, 548,
FONT_BEGIN, 749, 469,
FONT_NEXT, 753, 548,
FONT_NEXT, 764, 424,
FONT_END, 769, 380,
FONT_BEGIN, 769, 380,
FONT_NEXT, 753, 548,
FONT_END, 783, 498,
FONT_BEGIN, 769, 380,
FONT_NEXT, 783, 498,
FONT_END, 760, 223,
FONT_BEGIN, 769, 380,
FONT_NEXT, 760, 223,
FONT_NEXT, 760, 311,
FONT_END, 734, 245,
FONT_BEGIN, 734, 245,
FONT_NEXT, 760, 223,
FONT_END, 734, 191,
FONT_BEGIN, 734, 245,
FONT_NEXT, 734, 191,
FONT_NEXT, 693, 195,
FONT_NEXT, 702, 165,
FONT_END, 665, 149,
FONT_BEGIN, 693, 195,
FONT_NEXT, 665, 149,
FONT_NEXT, 667, 181,
FONT_END, 639, 176,
FONT_BEGIN, 639, 176,
FONT_NEXT, 665, 149,
FONT_END, 623, 143,
FONT_BEGIN, 639, 176,
FONT_NEXT, 623, 143,
FONT_NEXT, 613, 183,
FONT_NEXT, 581, 152,
FONT_END, 603, 202,
FONT_BEGIN, 700, 43,
FONT_NEXT, 601, 3,
FONT_NEXT, 688, 73,
FONT_END, 601, 39,
FONT_BEGIN, 601, 39,
FONT_NEXT, 601, 3,
FONT_NEXT, 550, 28,
FONT_NEXT, 547, -10,
FONT_NEXT, 490, 25,
FONT_NEXT, 491, -14,
FONT_END, 399, -6,
FONT_BEGIN, 490, 25,
FONT_NEXT, 399, -6,
FONT_NEXT, 427, 31,
FONT_END, 371, 48,
FONT_BEGIN, 371, 48,
FONT_NEXT, 399, -6,
FONT_END, 322, 18,
FONT_BEGIN, 371, 48,
FONT_NEXT, 322, 18,
FONT_NEXT, 321, 75,
FONT_NEXT, 257, 54,
FONT_NEXT, 279, 111,
FONT_END, 245, 153,
FONT_BEGIN, 245, 153,
FONT_NEXT, 257, 54,
FONT_END, 205, 100,
FONT_BEGIN, 245, 153,
FONT_NEXT, 205, 100,
FONT_NEXT, 220, 200,
FONT_END, 205, 251,
FONT_BEGIN, 205, 251,
FONT_NEXT, 205, 100,
FONT_NEXT, 200, 305,
FONT_END, 165, 152,
FONT_BEGIN, 809, 381,
FONT_NEXT, 796, 300,
FONT_NEXT, 802, 442,
FONT_END, 783, 498,
FONT_BEGIN, 783, 498,
FONT_NEXT, 796, 300,
FONT_END, 760, 223,
FONT_BEGIN, 783, 498,
FONT_END, 760, 223,
FONT_BEGIN, 524, 240,
FONT_NEXT, 554, 306,
FONT_NEXT, 553, 173,
FONT_NEXT, 572, 399,
FONT_END, 581, 152,

FONT_ADVANCE, 921, 0
    },
    {
65,
FONT_BEGIN, 451, 19,
FONT_NEXT, 499, 24,
FONT_NEXT, 451, 0,
FONT_END, 637, 62,
FONT_BEGIN, 637, 62,
FONT_NEXT, 499, 24,
FONT_END, 515, 35,
FONT_BEGIN, 637, 62,
FONT_NEXT, 515, 35,
FONT_NEXT, 616, 106,
FONT_NEXT, 521, 57,
FONT_NEXT, 367, 674,
FONT_NEXT, 502, 120,
FONT_END, 461, 216,
FONT_BEGIN, 367, 674,
FONT_NEXT, 461, 216,
FONT_END, 447, 257,
FONT_BEGIN, 367, 674,
FONT_NEXT, 447, 257,
FONT_END, 331, 532,
FONT_BEGIN, 367, 674,
FONT_NEXT, 331, 532,
FONT_NEXT, 347, 674,
FONT_END, 139, 183,
FONT_BEGIN, 139, 183,
FONT_NEXT, 331, 532,
FONT_END, 216, 257,
FONT_BEGIN, 139, 183,
FONT_NEXT, 216, 257,
FONT_END, 199, 216,
FONT_BEGIN, 139, 183,
FONT_NEXT, 199, 216,
FONT_END, 153, 99,
FONT_BEGIN, 139, 183,
FONT_NEXT, 153, 99,
FONT_END, 145, 61,
FONT_BEGIN, 139, 183,
FONT_NEXT, 145, 61,
FONT_NEXT, 104, 104,
FONT_END, 84, 67,
FONT_BEGIN, 84, 67,
FONT_NEXT, 145, 61,
FONT_NEXT, 66, 41,
FONT_NEXT, 15, 0,
FONT_NEXT, 39, 23,
FONT_END, 15, 19,
FONT_BEGIN, 706, 19,
FONT_NEXT, 706, 0,
FONT_NEXT, 677, 23,
FONT_NEXT, 451, 0,
FONT_NEXT, 656, 36,
FONT_END, 637, 62,
FONT_BEGIN, 216, 257,
FONT_NEXT, 447, 257,
FONT_NEXT, 199, 216,
FONT_END, 461, 216,
FONT_BEGIN, 213, 19,
FONT_NEXT, 213, 0,
FONT_NEXT, 171, 24,
FONT_NEXT, 15, 0,
FONT_NEXT, 152, 36,
FONT_END, 145, 61,
FONT_ADVANCE, 722, 0
    },
    {
66,
FONT_BEGIN, 17, 19,
FONT_NEXT, 67, 23,
FONT_NEXT, 17, 0,
FONT_END, 215, 78,
FONT_BEGIN, 215, 78,
FONT_NEXT, 67, 23,
FONT_END, 96, 38,
FONT_BEGIN, 215, 78,
FONT_NEXT, 96, 38,
FONT_END, 109, 65,
FONT_BEGIN, 215, 78,
FONT_NEXT, 109, 65,
FONT_END, 113, 109,
FONT_BEGIN, 215, 78,
FONT_NEXT, 113, 109,
FONT_END, 113, 553,
FONT_BEGIN, 215, 78,
FONT_NEXT, 113, 553,
FONT_END, 109, 597,
FONT_BEGIN, 215, 78,
FONT_NEXT, 109, 597,
FONT_NEXT, 215, 326,
FONT_END, 215, 365,
FONT_BEGIN, 215, 365,
FONT_NEXT, 109, 597,
FONT_NEXT, 215, 595,
FONT_NEXT, 95, 624,
FONT_NEXT, 17, 662,
FONT_NEXT, 66, 637,
FONT_END, 17, 643,
FONT_BEGIN, 215, 326,
FONT_NEXT, 215, 365,
FONT_NEXT, 276, 325,
FONT_NEXT, 310, 366,
FONT_NEXT, 319, 322,
FONT_NEXT, 344, 368,
FONT_NEXT, 377, 311,
FONT_NEXT, 394, 381,
FONT_NEXT, 414, 295,
FONT_NEXT, 426, 347,
FONT_NEXT, 447, 270,
FONT_END, 469, 232,
FONT_BEGIN, 469, 232,
FONT_NEXT, 426, 347,
FONT_END, 488, 327,
FONT_BEGIN, 469, 232,
FONT_NEXT, 488, 327,
FONT_NEXT, 478, 180,
FONT_NEXT, 482, 22,
FONT_NEXT, 472, 135,
FONT_NEXT, 425, 6,
FONT_NEXT, 458, 101,
FONT_END, 436, 75,
FONT_BEGIN, 436, 75,
FONT_NEXT, 425, 6,
FONT_NEXT, 409, 58,
FONT_NEXT, 351, 0,
FONT_NEXT, 348, 40,
FONT_NEXT, 17, 0,
FONT_NEXT, 291, 37,
FONT_END, 254, 38,
FONT_BEGIN, 254, 38,
FONT_NEXT, 17, 0,
FONT_NEXT, 231, 43,
FONT_END, 218, 55,
FONT_BEGIN, 218, 55,
FONT_NEXT, 17, 0,
FONT_END, 215, 78,
FONT_BEGIN, 218, 55,
FONT_END, 215, 78,
FONT_BEGIN, 215, 595,
FONT_NEXT, 17, 662,
FONT_NEXT, 218, 615,
FONT_END, 237, 624,
FONT_BEGIN, 237, 624,
FONT_NEXT, 17, 662,
FONT_NEXT, 255, 624,
FONT_END, 282, 625,
FONT_BEGIN, 282, 625,
FONT_NEXT, 17, 662,
FONT_END, 297, 662,
FONT_BEGIN, 282, 625,
FONT_NEXT, 297, 662,
FONT_NEXT, 339, 620,
FONT_NEXT, 374, 657,
FONT_NEXT, 396, 600,
FONT_END, 420, 582,
FONT_BEGIN, 420, 582,
FONT_NEXT, 374, 657,
FONT_NEXT, 439, 558,
FONT_NEXT, 460, 635,
FONT_NEXT, 452, 527,
FONT_END, 457, 487,
FONT_BEGIN, 457, 487,
FONT_NEXT, 460, 635,
FONT_END, 465, 360,
FONT_BEGIN, 457, 487,
FONT_NEXT, 465, 360,
FONT_NEXT, 451, 447,
FONT_NEXT, 426, 349,
FONT_NEXT, 437, 417,
FONT_END, 394, 381,
FONT_BEGIN, 394, 381,
FONT_NEXT, 426, 349,
FONT_END, 426, 347,
FONT_BEGIN, 394, 381,
FONT_END, 426, 347,
FONT_BEGIN, 559, 493,
FONT_NEXT, 555, 455,
FONT_NEXT, 551, 544,
FONT_NEXT, 544, 425,
FONT_NEXT, 530, 584,
FONT_NEXT, 509, 383,
FONT_NEXT, 499, 614,
FONT_NEXT, 465, 360,
FONT_END, 460, 635,
FONT_BEGIN, 593, 176,
FONT_NEXT, 586, 132,
FONT_NEXT, 589, 212,
FONT_END, 579, 244,
FONT_BEGIN, 579, 244,
FONT_NEXT, 586, 132,
FONT_END, 574, 103,
FONT_BEGIN, 579, 244,
FONT_NEXT, 574, 103,
FONT_NEXT, 542, 294,
FONT_NEXT, 555, 73,
FONT_END, 525, 45,
FONT_BEGIN, 542, 294,
FONT_NEXT, 525, 45,
FONT_NEXT, 488, 327,
FONT_END, 482, 22,
FONT_ADVANCE, 667, 0
    },
    {
67,
FONT_BEGIN, 620, 451,
FONT_NEXT, 597, 451,
FONT_NEXT, 611, 676,
FONT_END, 590, 676,
FONT_BEGIN, 590, 676,
FONT_NEXT, 597, 451,
FONT_END, 569, 524,
FONT_BEGIN, 590, 676,
FONT_NEXT, 569, 524,
FONT_NEXT, 580, 656,
FONT_END, 565, 647,
FONT_BEGIN, 565, 647,
FONT_NEXT, 569, 524,
FONT_END, 523, 582,
FONT_BEGIN, 565, 647,
FONT_NEXT, 523, 582,
FONT_NEXT, 544, 643,
FONT_END, 519, 648,
FONT_BEGIN, 519, 648,
FONT_NEXT, 523, 582,
FONT_END, 459, 621,
FONT_BEGIN, 519, 648,
FONT_NEXT, 459, 621,
FONT_NEXT, 481, 659,
FONT_END, 429, 670,
FONT_BEGIN, 429, 670,
FONT_NEXT, 459, 621,
FONT_END, 420, 632,
FONT_BEGIN, 429, 670,
FONT_NEXT, 420, 632,
FONT_NEXT, 368, 676,
FONT_NEXT, 377, 636,
FONT_END, 299, 620,
FONT_BEGIN, 368, 676,
FONT_NEXT, 299, 620,
FONT_NEXT, 304, 670,
FONT_END, 242, 653,
FONT_BEGIN, 242, 653,
FONT_NEXT, 299, 620,
FONT_END, 261, 601,
FONT_BEGIN, 242, 653,
FONT_NEXT, 261, 601,
FONT_END, 227, 575,
FONT_BEGIN, 242, 653,
FONT_NEXT, 227, 575,
FONT_NEXT, 184, 625,
FONT_NEXT, 197, 540,
FONT_END, 170, 491,
FONT_BEGIN, 184, 625,
FONT_NEXT, 170, 491,
FONT_NEXT, 133, 587,
FONT_NEXT, 151, 422,
FONT_END, 144, 329,
FONT_BEGIN, 133, 587,
FONT_NEXT, 144, 329,
FONT_END, 134, 65,
FONT_BEGIN, 133, 587,
FONT_NEXT, 134, 65,
FONT_END, 91, 112,
FONT_BEGIN, 133, 587,
FONT_NEXT, 91, 112,
FONT_NEXT, 90, 538,
FONT_NEXT, 57, 172,
FONT_NEXT, 56, 479,
FONT_NEXT, 35, 244,
FONT_NEXT, 35, 410,
FONT_END, 28, 331,
FONT_BEGIN, 633, 113,
FONT_NEXT, 628, 106,
FONT_NEXT, 615, 131,
FONT_NEXT, 616, 92,
FONT_END, 595, 71,
FONT_BEGIN, 615, 131,
FONT_NEXT, 595, 71,
FONT_NEXT, 583, 102,
FONT_NEXT, 566, 48,
FONT_NEXT, 536, 69,
FONT_NEXT, 528, 25,
FONT_NEXT, 471, 41,
FONT_NEXT, 481, 5,
FONT_END, 426, -9,
FONT_BEGIN, 471, 41,
FONT_NEXT, 426, -9,
FONT_NEXT, 389, 30,
FONT_NEXT, 362, -14,
FONT_NEXT, 342, 34,
FONT_NEXT, 301, -10,
FONT_NEXT, 297, 46,
FONT_NEXT, 241, 4,
FONT_NEXT, 256, 68,
FONT_END, 219, 100,
FONT_BEGIN, 219, 100,
FONT_NEXT, 241, 4,
FONT_END, 185, 29,
FONT_BEGIN, 219, 100,
FONT_NEXT, 185, 29,
FONT_NEXT, 188, 141,
FONT_END, 164, 192,
FONT_BEGIN, 164, 192,
FONT_NEXT, 185, 29,
FONT_END, 134, 65,
FONT_BEGIN, 164, 192,
FONT_NEXT, 134, 65,
FONT_NEXT, 149, 255,
FONT_END, 144, 329,
FONT_ADVANCE, 667, 0
    },
    {
68,
FONT_BEGIN, 16, 643,
FONT_NEXT, 16, 662,
FONT_NEXT, 62, 636,
FONT_END, 88, 623,
FONT_BEGIN, 88, 623,
FONT_NEXT, 16, 662,
FONT_END, 206, 586,
FONT_BEGIN, 88, 623,
FONT_NEXT, 206, 586,
FONT_NEXT, 101, 597,
FONT_NEXT, 206, 78,
FONT_NEXT, 104, 553,
FONT_END, 104, 109,
FONT_BEGIN, 104, 109,
FONT_NEXT, 206, 78,
FONT_NEXT, 100, 65,
FONT_END, 88, 39,
FONT_BEGIN, 88, 39,
FONT_NEXT, 206, 78,
FONT_NEXT, 61, 24,
FONT_NEXT, 16, 0,
FONT_END, 16, 19,
FONT_BEGIN, 685, 329,
FONT_NEXT, 681, 276,
FONT_NEXT, 677, 407,
FONT_NEXT, 667, 221,
FONT_NEXT, 656, 475,
FONT_NEXT, 643, 166,
FONT_NEXT, 622, 532,
FONT_NEXT, 607, 114,
FONT_NEXT, 575, 579,
FONT_NEXT, 576, 328,
FONT_END, 571, 390,
FONT_BEGIN, 575, 579,
FONT_NEXT, 571, 390,
FONT_NEXT, 517, 615,
FONT_NEXT, 555, 449,
FONT_END, 526, 505,
FONT_BEGIN, 517, 615,
FONT_NEXT, 526, 505,
FONT_END, 483, 555,
FONT_BEGIN, 517, 615,
FONT_NEXT, 483, 555,
FONT_NEXT, 449, 641,
FONT_NEXT, 444, 584,
FONT_NEXT, 372, 656,
FONT_NEXT, 397, 606,
FONT_END, 335, 620,
FONT_BEGIN, 372, 656,
FONT_NEXT, 335, 620,
FONT_NEXT, 286, 662,
FONT_NEXT, 253, 625,
FONT_NEXT, 16, 662,
FONT_NEXT, 225, 621,
FONT_END, 211, 613,
FONT_BEGIN, 16, 662,
FONT_NEXT, 211, 613,
FONT_END, 206, 586,
FONT_BEGIN, 206, 78,
FONT_NEXT, 208, 57,
FONT_NEXT, 16, 0,
FONT_NEXT, 218, 44,
FONT_END, 236, 38,
FONT_BEGIN, 16, 0,
FONT_NEXT, 236, 38,
FONT_END, 266, 37,
FONT_BEGIN, 16, 0,
FONT_NEXT, 266, 37,
FONT_END, 297, 37,
FONT_BEGIN, 16, 0,
FONT_NEXT, 297, 37,
FONT_NEXT, 300, 0,
FONT_NEXT, 341, 41,
FONT_END, 392, 52,
FONT_BEGIN, 300, 0,
FONT_NEXT, 392, 52,
FONT_NEXT, 404, 8,
FONT_NEXT, 445, 73,
FONT_NEXT, 489, 32,
FONT_NEXT, 494, 108,
FONT_END, 536, 159,
FONT_BEGIN, 489, 32,
FONT_NEXT, 536, 159,
FONT_NEXT, 556, 69,
FONT_NEXT, 565, 231,
FONT_END, 573, 276,
FONT_BEGIN, 556, 69,
FONT_NEXT, 573, 276,
FONT_NEXT, 607, 114,
FONT_END, 576, 328,
FONT_ADVANCE, 722, 0
    },
    {
69,
FONT_BEGIN, 12, 19,
FONT_NEXT, 52, 23,
FONT_NEXT, 12, 0,
FONT_NEXT, 79, 36,
FONT_NEXT, 201, 80,
FONT_NEXT, 94, 63,
FONT_END, 99, 109,
FONT_BEGIN, 201, 80,
FONT_NEXT, 99, 109,
FONT_END, 99, 553,
FONT_BEGIN, 201, 80,
FONT_NEXT, 99, 553,
FONT_END, 94, 599,
FONT_BEGIN, 201, 80,
FONT_NEXT, 94, 599,
FONT_NEXT, 201, 328,
FONT_NEXT, 201, 368,
FONT_NEXT, 355, 326,
FONT_NEXT, 355, 368,
FONT_NEXT, 405, 321,
FONT_NEXT, 408, 372,
FONT_NEXT, 436, 308,
FONT_NEXT, 439, 388,
FONT_NEXT, 454, 279,
FONT_NEXT, 455, 418,
FONT_NEXT, 465, 231,
FONT_NEXT, 465, 463,
FONT_NEXT, 488, 231,
FONT_END, 488, 463,
FONT_BEGIN, 597, 169,
FONT_NEXT, 552, 0,
FONT_NEXT, 569, 169,
FONT_END, 533, 105,
FONT_BEGIN, 533, 105,
FONT_NEXT, 552, 0,
FONT_NEXT, 489, 65,
FONT_END, 430, 44,
FONT_BEGIN, 430, 44,
FONT_NEXT, 552, 0,
FONT_END, 12, 0,
FONT_BEGIN, 430, 44,
FONT_NEXT, 12, 0,
FONT_NEXT, 350, 38,
FONT_END, 300, 38,
FONT_BEGIN, 300, 38,
FONT_NEXT, 12, 0,
FONT_NEXT, 263, 38,
FONT_END, 237, 40,
FONT_BEGIN, 237, 40,
FONT_NEXT, 12, 0,
FONT_NEXT, 219, 43,
FONT_END, 203, 56,
FONT_BEGIN, 203, 56,
FONT_NEXT, 12, 0,
FONT_END, 201, 80,
FONT_BEGIN, 203, 56,
FONT_END, 201, 80,
FONT_BEGIN, 12, 643,
FONT_NEXT, 12, 662,
FONT_NEXT, 52, 637,
FONT_END, 79, 625,
FONT_BEGIN, 79, 625,
FONT_NEXT, 12, 662,
FONT_END, 201, 590,
FONT_BEGIN, 79, 625,
FONT_NEXT, 201, 590,
FONT_NEXT, 94, 599,
FONT_END, 201, 368,
FONT_BEGIN, 546, 519,
FONT_NEXT, 521, 519,
FONT_NEXT, 543, 662,
FONT_NEXT, 506, 572,
FONT_END, 481, 604,
FONT_BEGIN, 543, 662,
FONT_NEXT, 481, 604,
FONT_END, 437, 620,
FONT_BEGIN, 543, 662,
FONT_NEXT, 437, 620,
FONT_NEXT, 12, 662,
FONT_NEXT, 369, 624,
FONT_END, 234, 624,
FONT_BEGIN, 12, 662,
FONT_NEXT, 234, 624,
FONT_END, 209, 619,
FONT_BEGIN, 12, 662,
FONT_NEXT, 209, 619,
FONT_END, 203, 609,
FONT_BEGIN, 12, 662,
FONT_NEXT, 203, 609,
FONT_END, 201, 590,
FONT_ADVANCE, 611, 0
    },
    {
70,
FONT_BEGIN, 12, 643,
FONT_NEXT, 12, 662,
FONT_NEXT, 52, 637,
FONT_END, 79, 625,
FONT_BEGIN, 79, 625,
FONT_NEXT, 12, 662,
FONT_END, 201, 590,
FONT_BEGIN, 79, 625,
FONT_NEXT, 201, 590,
FONT_NEXT, 94, 599,
FONT_NEXT, 201, 368,
FONT_END, 201, 328,
FONT_BEGIN, 94, 599,
FONT_NEXT, 201, 328,
FONT_END, 201, 109,
FONT_BEGIN, 94, 599,
FONT_NEXT, 201, 109,
FONT_NEXT, 99, 553,
FONT_END, 99, 120,
FONT_BEGIN, 99, 120,
FONT_NEXT, 201, 109,
FONT_NEXT, 95, 70,
FONT_END, 83, 40,
FONT_BEGIN, 83, 40,
FONT_NEXT, 201, 109,
FONT_NEXT, 57, 24,
FONT_NEXT, 205, 63,
FONT_END, 12, 0,
FONT_BEGIN, 57, 24,
FONT_NEXT, 12, 0,
FONT_END, 12, 19,
FONT_BEGIN, 546, 519,
FONT_NEXT, 521, 519,
FONT_NEXT, 543, 662,
FONT_NEXT, 506, 572,
FONT_END, 481, 604,
FONT_BEGIN, 543, 662,
FONT_NEXT, 481, 604,
FONT_END, 437, 620,
FONT_BEGIN, 543, 662,
FONT_NEXT, 437, 620,
FONT_NEXT, 12, 662,
FONT_NEXT, 369, 624,
FONT_END, 233, 624,
FONT_BEGIN, 12, 662,
FONT_NEXT, 233, 624,
FONT_END, 207, 618,
FONT_BEGIN, 12, 662,
FONT_NEXT, 207, 618,
FONT_END, 202, 607,
FONT_BEGIN, 12, 662,
FONT_NEXT, 202, 607,
FONT_END, 201, 590,
FONT_BEGIN, 479, 463,
FONT_NEXT, 479, 231,
FONT_NEXT, 456, 463,
FONT_NEXT, 456, 231,
FONT_NEXT, 446, 416,
FONT_NEXT, 444, 281,
FONT_NEXT, 428, 387,
FONT_NEXT, 425, 309,
FONT_NEXT, 396, 372,
FONT_NEXT, 393, 322,
FONT_NEXT, 346, 368,
FONT_NEXT, 346, 326,
FONT_NEXT, 201, 368,
FONT_END, 201, 328,
FONT_BEGIN, 292, 19,
FONT_NEXT, 292, 0,
FONT_NEXT, 247, 23,
FONT_NEXT, 12, 0,
FONT_NEXT, 219, 37,
FONT_END, 205, 63,
FONT_ADVANCE, 556, 0
    },
    {
71,
FONT_BEGIN, 630, 465,
FONT_NEXT, 607, 465,
FONT_NEXT, 622, 676,
FONT_END, 600, 676,
FONT_BEGIN, 600, 676,
FONT_NEXT, 607, 465,
FONT_END, 581, 522,
FONT_BEGIN, 600, 676,
FONT_NEXT, 581, 522,
FONT_NEXT, 591, 659,
FONT_END, 577, 649,
FONT_BEGIN, 577, 649,
FONT_NEXT, 581, 522,
FONT_END, 539, 577,
FONT_BEGIN, 577, 649,
FONT_NEXT, 539, 577,
FONT_NEXT, 553, 643,
FONT_END, 525, 648,
FONT_BEGIN, 525, 648,
FONT_NEXT, 539, 577,
FONT_END, 477, 619,
FONT_BEGIN, 525, 648,
FONT_NEXT, 477, 619,
FONT_NEXT, 489, 659,
FONT_END, 440, 670,
FONT_BEGIN, 440, 670,
FONT_NEXT, 477, 619,
FONT_END, 436, 631,
FONT_BEGIN, 440, 670,
FONT_NEXT, 436, 631,
FONT_NEXT, 374, 676,
FONT_NEXT, 388, 636,
FONT_END, 310, 622,
FONT_BEGIN, 374, 676,
FONT_NEXT, 310, 622,
FONT_NEXT, 298, 668,
FONT_NEXT, 269, 602,
FONT_NEXT, 231, 647,
FONT_NEXT, 231, 572,
FONT_NEXT, 172, 614,
FONT_NEXT, 197, 531,
FONT_END, 170, 476,
FONT_BEGIN, 172, 614,
FONT_NEXT, 170, 476,
FONT_NEXT, 123, 571,
FONT_NEXT, 152, 406,
FONT_END, 146, 320,
FONT_BEGIN, 123, 571,
FONT_NEXT, 146, 320,
FONT_END, 115, 93,
FONT_BEGIN, 123, 571,
FONT_NEXT, 115, 93,
FONT_NEXT, 84, 520,
FONT_NEXT, 79, 137,
FONT_NEXT, 55, 464,
FONT_NEXT, 53, 189,
FONT_NEXT, 38, 403,
FONT_NEXT, 37, 255,
FONT_END, 32, 341,
FONT_BEGIN, 454, 336,
FONT_NEXT, 454, 354,
FONT_NEXT, 496, 331,
FONT_END, 523, 320,
FONT_BEGIN, 523, 320,
FONT_NEXT, 454, 354,
FONT_END, 643, 300,
FONT_BEGIN, 523, 320,
FONT_NEXT, 643, 300,
FONT_END, 639, 259,
FONT_BEGIN, 523, 320,
FONT_NEXT, 639, 259,
FONT_NEXT, 537, 295,
FONT_NEXT, 639, 58,
FONT_END, 633, 50,
FONT_BEGIN, 537, 295,
FONT_NEXT, 633, 50,
FONT_END, 616, 40,
FONT_BEGIN, 537, 295,
FONT_NEXT, 616, 40,
FONT_NEXT, 542, 247,
FONT_NEXT, 558, 16,
FONT_NEXT, 542, 85,
FONT_END, 530, 60,
FONT_BEGIN, 530, 60,
FONT_NEXT, 558, 16,
FONT_END, 480, -5,
FONT_BEGIN, 530, 60,
FONT_NEXT, 480, -5,
FONT_NEXT, 499, 42,
FONT_END, 455, 30,
FONT_BEGIN, 455, 30,
FONT_NEXT, 480, -5,
FONT_END, 396, -14,
FONT_BEGIN, 455, 30,
FONT_NEXT, 396, -14,
FONT_NEXT, 405, 26,
FONT_END, 349, 31,
FONT_BEGIN, 349, 31,
FONT_NEXT, 396, -14,
FONT_END, 320, -9,
FONT_BEGIN, 349, 31,
FONT_NEXT, 320, -9,
FONT_NEXT, 299, 46,
FONT_NEXT, 245, 10,
FONT_NEXT, 254, 71,
FONT_END, 217, 105,
FONT_BEGIN, 217, 105,
FONT_NEXT, 245, 10,
FONT_END, 174, 43,
FONT_BEGIN, 217, 105,
FONT_NEXT, 174, 43,
FONT_NEXT, 187, 147,
FONT_END, 164, 197,
FONT_BEGIN, 164, 197,
FONT_NEXT, 174, 43,
FONT_END, 115, 93,
FONT_BEGIN, 164, 197,
FONT_NEXT, 115, 93,
FONT_NEXT, 150, 255,
FONT_END, 146, 320,
FONT_BEGIN, 709, 354,
FONT_NEXT, 709, 336,
FONT_NEXT, 454, 354,
FONT_NEXT, 679, 332,
FONT_END, 657, 322,
FONT_BEGIN, 454, 354,
FONT_NEXT, 657, 322,
FONT_END, 643, 300,
FONT_ADVANCE, 722, 0
    },
    {
72,
FONT_BEGIN, 19, 643,
FONT_NEXT, 19, 662,
FONT_NEXT, 63, 636,
FONT_END, 90, 623,
FONT_BEGIN, 90, 623,
FONT_NEXT, 19, 662,
FONT_END, 212, 596,
FONT_BEGIN, 90, 623,
FONT_NEXT, 212, 596,
FONT_END, 209, 553,
FONT_BEGIN, 90, 623,
FONT_NEXT, 209, 553,
FONT_NEXT, 103, 597,
FONT_NEXT, 209, 359,
FONT_END, 209, 315,
FONT_BEGIN, 103, 597,
FONT_NEXT, 209, 315,
FONT_END, 209, 109,
FONT_BEGIN, 103, 597,
FONT_NEXT, 209, 109,
FONT_NEXT, 107, 553,
FONT_END, 107, 120,
FONT_BEGIN, 107, 120,
FONT_NEXT, 209, 109,
FONT_NEXT, 105, 73,
FONT_END, 94, 42,
FONT_BEGIN, 94, 42,
FONT_NEXT, 209, 109,
FONT_NEXT, 68, 25,
FONT_NEXT, 211, 67,
FONT_END, 19, 0,
FONT_BEGIN, 68, 25,
FONT_NEXT, 19, 0,
FONT_END, 19, 19,
FONT_BEGIN, 297, 662,
FONT_NEXT, 297, 643,
FONT_NEXT, 19, 662,
FONT_NEXT, 251, 636,
FONT_END, 224, 622,
FONT_BEGIN, 19, 662,
FONT_NEXT, 224, 622,
FONT_END, 212, 596,
FONT_BEGIN, 424, 643,
FONT_NEXT, 424, 662,
FONT_NEXT, 468, 636,
FONT_END, 495, 623,
FONT_BEGIN, 495, 623,
FONT_NEXT, 424, 662,
FONT_END, 617, 596,
FONT_BEGIN, 495, 623,
FONT_NEXT, 617, 596,
FONT_END, 614, 553,
FONT_BEGIN, 495, 623,
FONT_NEXT, 614, 553,
FONT_NEXT, 508, 597,
FONT_NEXT, 614, 109,
FONT_NEXT, 512, 553,
FONT_NEXT, 512, 120,
FONT_NEXT, 512, 359,
FONT_NEXT, 512, 315,
FONT_NEXT, 209, 359,
FONT_END, 209, 315,
FONT_BEGIN, 702, 662,
FONT_NEXT, 702, 643,
FONT_NEXT, 424, 662,
FONT_NEXT, 656, 636,
FONT_END, 629, 622,
FONT_BEGIN, 424, 662,
FONT_NEXT, 629, 622,
FONT_END, 617, 596,
FONT_BEGIN, 424, 19,
FONT_NEXT, 473, 25,
FONT_NEXT, 424, 0,
FONT_END, 616, 67,
FONT_BEGIN, 616, 67,
FONT_NEXT, 473, 25,
FONT_NEXT, 614, 109,
FONT_NEXT, 499, 42,
FONT_END, 510, 73,
FONT_BEGIN, 614, 109,
FONT_NEXT, 510, 73,
FONT_END, 512, 120,
FONT_BEGIN, 702, 19,
FONT_NEXT, 702, 0,
FONT_NEXT, 655, 25,
FONT_NEXT, 424, 0,
FONT_NEXT, 628, 41,
FONT_END, 616, 67,
FONT_BEGIN, 297, 19,
FONT_NEXT, 297, 0,
FONT_NEXT, 250, 25,
FONT_NEXT, 19, 0,
FONT_NEXT, 223, 41,
FONT_END, 211, 67,
FONT_ADVANCE, 722, 0
    },
    {
73,
FONT_BEGIN, 18, 643,
FONT_NEXT, 18, 662,
FONT_NEXT, 68, 637,
FONT_END, 97, 624,
FONT_BEGIN, 97, 624,
FONT_NEXT, 18, 662,
FONT_END, 222, 600,
FONT_BEGIN, 97, 624,
FONT_NEXT, 222, 600,
FONT_END, 217, 553,
FONT_BEGIN, 97, 624,
FONT_NEXT, 217, 553,
FONT_NEXT, 111, 597,
FONT_NEXT, 217, 109,
FONT_NEXT, 115, 553,
FONT_END, 115, 109,
FONT_BEGIN, 115, 109,
FONT_NEXT, 217, 109,
FONT_NEXT, 111, 65,
FONT_END, 98, 38,
FONT_BEGIN, 98, 38,
FONT_NEXT, 217, 109,
FONT_END, 221, 62,
FONT_BEGIN, 98, 38,
FONT_NEXT, 221, 62,
FONT_NEXT, 69, 23,
FONT_NEXT, 18, 0,
FONT_END, 18, 19,
FONT_BEGIN, 315, 662,
FONT_NEXT, 315, 643,
FONT_NEXT, 18, 662,
FONT_NEXT, 268, 638,
FONT_END, 238, 626,
FONT_BEGIN, 18, 662,
FONT_NEXT, 238, 626,
FONT_END, 222, 600,
FONT_BEGIN, 315, 19,
FONT_NEXT, 315, 0,
FONT_NEXT, 268, 22,
FONT_NEXT, 18, 0,
FONT_NEXT, 238, 35,
FONT_END, 221, 62,
FONT_ADVANCE, 333, 0
    },
    {
74,
FONT_BEGIN, 83, 643,
FONT_NEXT, 83, 662,
FONT_NEXT, 131, 636,
FONT_END, 159, 622,
FONT_BEGIN, 159, 622,
FONT_NEXT, 83, 662,
FONT_END, 281, 597,
FONT_BEGIN, 159, 622,
FONT_NEXT, 281, 597,
FONT_END, 278, 553,
FONT_BEGIN, 159, 622,
FONT_NEXT, 278, 553,
FONT_NEXT, 172, 596,
FONT_NEXT, 278, 183,
FONT_END, 271, 118,
FONT_BEGIN, 172, 596,
FONT_NEXT, 271, 118,
FONT_END, 255, 70,
FONT_BEGIN, 172, 596,
FONT_NEXT, 255, 70,
FONT_END, 231, 35,
FONT_BEGIN, 172, 596,
FONT_NEXT, 231, 35,
FONT_NEXT, 176, 553,
FONT_NEXT, 204, 11,
FONT_NEXT, 176, 90,
FONT_END, 173, 57,
FONT_BEGIN, 173, 57,
FONT_NEXT, 204, 11,
FONT_END, 147, -11,
FONT_BEGIN, 173, 57,
FONT_NEXT, 147, -11,
FONT_NEXT, 165, 37,
FONT_END, 153, 27,
FONT_BEGIN, 153, 27,
FONT_NEXT, 147, -11,
FONT_NEXT, 137, 24,
FONT_NEXT, 124, -14,
FONT_NEXT, 117, 37,
FONT_NEXT, 110, -14,
FONT_NEXT, 107, 66,
FONT_NEXT, 59, -6,
FONT_NEXT, 92, 94,
FONT_END, 77, 104,
FONT_BEGIN, 77, 104,
FONT_NEXT, 59, -6,
FONT_NEXT, 56, 108,
FONT_NEXT, 32, 11,
FONT_NEXT, 36, 103,
FONT_END, 22, 90,
FONT_BEGIN, 22, 90,
FONT_NEXT, 32, 11,
FONT_END, 15, 36,
FONT_BEGIN, 22, 90,
FONT_NEXT, 15, 36,
FONT_END, 10, 59,
FONT_BEGIN, 370, 662,
FONT_NEXT, 370, 643,
FONT_NEXT, 83, 662,
FONT_NEXT, 322, 637,
FONT_END, 294, 623,
FONT_BEGIN, 83, 662,
FONT_NEXT, 294, 623,
FONT_END, 281, 597,
FONT_ADVANCE, 389, 0
    },
    {
75,
FONT_BEGIN, 34, 643,
FONT_NEXT, 34, 662,
FONT_NEXT, 76, 637,
FONT_END, 104, 625,
FONT_BEGIN, 104, 625,
FONT_NEXT, 34, 662,
FONT_END, 229, 597,
FONT_BEGIN, 104, 625,
FONT_NEXT, 229, 597,
FONT_END, 226, 553,
FONT_BEGIN, 104, 625,
FONT_NEXT, 226, 553,
FONT_NEXT, 119, 599,
FONT_NEXT, 226, 348,
FONT_END, 226, 296,
FONT_BEGIN, 119, 599,
FONT_NEXT, 226, 296,
FONT_END, 226, 109,
FONT_BEGIN, 119, 599,
FONT_NEXT, 226, 109,
FONT_NEXT, 124, 553,
FONT_END, 124, 120,
FONT_BEGIN, 124, 120,
FONT_NEXT, 226, 109,
FONT_NEXT, 120, 70,
FONT_END, 108, 40,
FONT_BEGIN, 108, 40,
FONT_NEXT, 226, 109,
FONT_END, 229, 65,
FONT_BEGIN, 108, 40,
FONT_NEXT, 229, 65,
FONT_NEXT, 80, 24,
FONT_NEXT, 34, 0,
FONT_END, 34, 19,
FONT_BEGIN, 318, 662,
FONT_NEXT, 318, 643,
FONT_NEXT, 34, 662,
FONT_NEXT, 270, 637,
FONT_END, 242, 623,
FONT_BEGIN, 34, 662,
FONT_NEXT, 242, 623,
FONT_END, 229, 597,
FONT_BEGIN, 413, 643,
FONT_NEXT, 413, 662,
FONT_NEXT, 444, 641,
FONT_END, 465, 636,
FONT_BEGIN, 465, 636,
FONT_NEXT, 413, 662,
FONT_END, 568, 606,
FONT_BEGIN, 465, 636,
FONT_NEXT, 568, 606,
FONT_NEXT, 477, 627,
FONT_NEXT, 523, 565,
FONT_NEXT, 481, 612,
FONT_END, 472, 584,
FONT_BEGIN, 472, 584,
FONT_NEXT, 523, 565,
FONT_END, 333, 377,
FONT_BEGIN, 472, 584,
FONT_NEXT, 333, 377,
FONT_NEXT, 459, 567,
FONT_END, 438, 543,
FONT_BEGIN, 438, 543,
FONT_NEXT, 333, 377,
FONT_NEXT, 406, 511,
FONT_END, 361, 470,
FONT_BEGIN, 361, 470,
FONT_NEXT, 333, 377,
FONT_NEXT, 301, 416,
FONT_NEXT, 252, 317,
FONT_NEXT, 226, 348,
FONT_END, 226, 296,
FONT_BEGIN, 675, 662,
FONT_NEXT, 675, 643,
FONT_NEXT, 413, 662,
FONT_NEXT, 635, 638,
FONT_END, 603, 628,
FONT_BEGIN, 413, 662,
FONT_NEXT, 603, 628,
FONT_END, 568, 606,
FONT_BEGIN, 723, 19,
FONT_NEXT, 723, 0,
FONT_NEXT, 683, 24,
FONT_NEXT, 418, 0,
FONT_NEXT, 647, 45,
FONT_END, 609, 79,
FONT_BEGIN, 609, 79,
FONT_NEXT, 418, 0,
FONT_END, 472, 24,
FONT_BEGIN, 609, 79,
FONT_NEXT, 472, 24,
FONT_END, 484, 31,
FONT_BEGIN, 609, 79,
FONT_NEXT, 484, 31,
FONT_NEXT, 566, 127,
FONT_NEXT, 488, 44,
FONT_END, 483, 58,
FONT_BEGIN, 566, 127,
FONT_NEXT, 483, 58,
FONT_NEXT, 333, 377,
FONT_NEXT, 472, 78,
FONT_END, 435, 126,
FONT_BEGIN, 333, 377,
FONT_NEXT, 435, 126,
FONT_END, 392, 175,
FONT_BEGIN, 333, 377,
FONT_NEXT, 392, 175,
FONT_END, 358, 212,
FONT_BEGIN, 333, 377,
FONT_NEXT, 358, 212,
FONT_END, 252, 317,
FONT_BEGIN, 418, 19,
FONT_NEXT, 451, 20,
FONT_NEXT, 418, 0,
FONT_END, 472, 24,
FONT_BEGIN, 316, 19,
FONT_NEXT, 316, 0,
FONT_NEXT, 269, 24,
FONT_NEXT, 34, 0,
FONT_NEXT, 241, 39,
FONT_END, 229, 65,
FONT_ADVANCE, 722, 0
    },
    {
76,
FONT_BEGIN, 12, 19,
FONT_NEXT, 52, 23,
FONT_NEXT, 12, 0,
FONT_NEXT, 79, 36,
FONT_NEXT, 201, 80,
FONT_NEXT, 94, 63,
FONT_END, 99, 109,
FONT_BEGIN, 201, 80,
FONT_NEXT, 99, 109,
FONT_END, 99, 553,
FONT_BEGIN, 201, 80,
FONT_NEXT, 99, 553,
FONT_END, 94, 599,
FONT_BEGIN, 201, 80,
FONT_NEXT, 94, 599,
FONT_NEXT, 201, 553,
FONT_NEXT, 79, 625,
FONT_NEXT, 205, 599,
FONT_END, 12, 662,
FONT_BEGIN, 12, 662,
FONT_NEXT, 79, 625,
FONT_END, 52, 637,
FONT_BEGIN, 12, 662,
FONT_NEXT, 52, 637,
FONT_END, 12, 643,
FONT_BEGIN, 598, 174,
FONT_NEXT, 550, 0,
FONT_NEXT, 573, 174,
FONT_END, 551, 130,
FONT_BEGIN, 551, 130,
FONT_NEXT, 550, 0,
FONT_NEXT, 527, 97,
FONT_END, 501, 73,
FONT_BEGIN, 501, 73,
FONT_NEXT, 550, 0,
FONT_NEXT, 472, 57,
FONT_END, 399, 41,
FONT_BEGIN, 399, 41,
FONT_NEXT, 550, 0,
FONT_END, 12, 0,
FONT_BEGIN, 399, 41,
FONT_NEXT, 12, 0,
FONT_NEXT, 302, 39,
FONT_END, 251, 39,
FONT_BEGIN, 251, 39,
FONT_NEXT, 12, 0,
FONT_NEXT, 220, 44,
FONT_END, 205, 56,
FONT_BEGIN, 205, 56,
FONT_NEXT, 12, 0,
FONT_END, 201, 80,
FONT_BEGIN, 205, 56,
FONT_END, 201, 80,
FONT_BEGIN, 294, 662,
FONT_NEXT, 294, 643,
FONT_NEXT, 12, 662,
FONT_NEXT, 250, 638,
FONT_END, 222, 626,
FONT_BEGIN, 12, 662,
FONT_NEXT, 222, 626,
FONT_END, 205, 599,
FONT_ADVANCE, 611, 0
    },
    {
77,
FONT_BEGIN, 14, 643,
FONT_NEXT, 14, 662,
FONT_NEXT, 60, 638,
FONT_END, 89, 625,
FONT_BEGIN, 89, 625,
FONT_NEXT, 14, 662,
FONT_END, 212, 662,
FONT_BEGIN, 89, 625,
FONT_NEXT, 212, 662,
FONT_END, 155, 546,
FONT_BEGIN, 89, 625,
FONT_NEXT, 155, 546,
FONT_END, 153, 546,
FONT_BEGIN, 89, 625,
FONT_NEXT, 153, 546,
FONT_NEXT, 104, 599,
FONT_END, 109, 553,
FONT_BEGIN, 109, 553,
FONT_NEXT, 153, 546,
FONT_END, 153, 147,
FONT_BEGIN, 109, 553,
FONT_NEXT, 153, 147,
FONT_NEXT, 109, 147,
FONT_END, 107, 110,
FONT_BEGIN, 107, 110,
FONT_NEXT, 153, 147,
FONT_NEXT, 104, 82,
FONT_END, 89, 44,
FONT_BEGIN, 89, 44,
FONT_NEXT, 153, 147,
FONT_END, 157, 83,
FONT_BEGIN, 89, 44,
FONT_NEXT, 157, 83,
FONT_NEXT, 59, 25,
FONT_NEXT, 172, 45,
FONT_END, 12, 0,
FONT_BEGIN, 59, 25,
FONT_NEXT, 12, 0,
FONT_END, 12, 19,
FONT_BEGIN, 863, 662,
FONT_NEXT, 863, 643,
FONT_NEXT, 664, 662,
FONT_NEXT, 822, 637,
FONT_END, 795, 625,
FONT_BEGIN, 664, 662,
FONT_NEXT, 795, 625,
FONT_END, 780, 599,
FONT_BEGIN, 664, 662,
FONT_NEXT, 780, 599,
FONT_END, 776, 553,
FONT_BEGIN, 664, 662,
FONT_NEXT, 776, 553,
FONT_END, 776, 109,
FONT_BEGIN, 664, 662,
FONT_NEXT, 776, 109,
FONT_END, 674, 569,
FONT_BEGIN, 664, 662,
FONT_NEXT, 674, 569,
FONT_END, 672, 569,
FONT_BEGIN, 664, 662,
FONT_NEXT, 672, 569,
FONT_END, 418, 0,
FONT_BEGIN, 664, 662,
FONT_NEXT, 418, 0,
FONT_NEXT, 443, 157,
FONT_END, 212, 662,
FONT_BEGIN, 212, 662,
FONT_NEXT, 418, 0,
FONT_END, 404, 0,
FONT_BEGIN, 212, 662,
FONT_NEXT, 404, 0,
FONT_END, 155, 546,
FONT_BEGIN, 583, 19,
FONT_NEXT, 630, 24,
FONT_NEXT, 583, 0,
FONT_END, 780, 63,
FONT_BEGIN, 780, 63,
FONT_NEXT, 630, 24,
FONT_NEXT, 776, 109,
FONT_NEXT, 657, 40,
FONT_END, 670, 70,
FONT_BEGIN, 776, 109,
FONT_NEXT, 670, 70,
FONT_END, 674, 120,
FONT_BEGIN, 776, 109,
FONT_NEXT, 674, 120,
FONT_END, 674, 569,
FONT_BEGIN, 863, 19,
FONT_NEXT, 863, 0,
FONT_NEXT, 822, 23,
FONT_NEXT, 583, 0,
FONT_NEXT, 795, 36,
FONT_END, 780, 63,
FONT_BEGIN, 247, 19,
FONT_NEXT, 247, 0,
FONT_NEXT, 201, 25,
FONT_NEXT, 12, 0,
FONT_END, 172, 45,
FONT_ADVANCE, 889, 0
    },
    {
78,
FONT_BEGIN, 12, 643,
FONT_NEXT, 12, 662,
FONT_NEXT, 60, 634,
FONT_NEXT, 183, 662,
FONT_NEXT, 82, 617,
FONT_END, 109, 588,
FONT_BEGIN, 109, 588,
FONT_NEXT, 183, 662,
FONT_END, 155, 537,
FONT_BEGIN, 109, 588,
FONT_NEXT, 155, 537,
FONT_END, 153, 537,
FONT_BEGIN, 109, 588,
FONT_NEXT, 153, 537,
FONT_END, 153, 147,
FONT_BEGIN, 109, 588,
FONT_NEXT, 153, 147,
FONT_NEXT, 109, 147,
FONT_END, 107, 110,
FONT_BEGIN, 107, 110,
FONT_NEXT, 153, 147,
FONT_NEXT, 104, 82,
FONT_END, 89, 44,
FONT_BEGIN, 89, 44,
FONT_NEXT, 153, 147,
FONT_END, 157, 83,
FONT_BEGIN, 89, 44,
FONT_NEXT, 157, 83,
FONT_NEXT, 59, 25,
FONT_NEXT, 172, 45,
FONT_END, 12, 0,
FONT_BEGIN, 59, 25,
FONT_NEXT, 12, 0,
FONT_END, 12, 19,
FONT_BEGIN, 472, 643,
FONT_NEXT, 472, 662,
FONT_NEXT, 515, 637,
FONT_END, 545, 620,
FONT_BEGIN, 545, 620,
FONT_NEXT, 472, 662,
FONT_END, 617, 583,
FONT_BEGIN, 545, 620,
FONT_NEXT, 617, 583,
FONT_END, 613, 553,
FONT_BEGIN, 545, 620,
FONT_NEXT, 613, 553,
FONT_END, 612, 515,
FONT_BEGIN, 545, 620,
FONT_NEXT, 612, 515,
FONT_NEXT, 562, 582,
FONT_END, 566, 553,
FONT_BEGIN, 566, 553,
FONT_NEXT, 612, 515,
FONT_END, 612, -11,
FONT_BEGIN, 566, 553,
FONT_NEXT, 612, -11,
FONT_NEXT, 568, 515,
FONT_NEXT, 595, -11,
FONT_NEXT, 568, 181,
FONT_END, 566, 181,
FONT_BEGIN, 566, 181,
FONT_NEXT, 595, -11,
FONT_END, 155, 537,
FONT_BEGIN, 566, 181,
FONT_NEXT, 155, 537,
FONT_END, 183, 662,
FONT_BEGIN, 707, 662,

FONT_NEXT, 707, 643,
FONT_NEXT, 472, 662,
FONT_NEXT, 665, 636,
FONT_END, 635, 620,
FONT_BEGIN, 472, 662,
FONT_NEXT, 635, 620,
FONT_END, 617, 583,
FONT_BEGIN, 247, 19,
FONT_NEXT, 247, 0,
FONT_NEXT, 201, 25,
FONT_NEXT, 12, 0,
FONT_END, 172, 45,
FONT_ADVANCE, 722, 0
    },
    {
79,
FONT_BEGIN, 148, 331,
FONT_NEXT, 152, 254,
FONT_NEXT, 129, 78,
FONT_NEXT, 165, 189,
FONT_NEXT, 177, 38,
FONT_NEXT, 186, 136,
FONT_END, 212, 94,
FONT_BEGIN, 177, 38,
FONT_NEXT, 212, 94,
FONT_NEXT, 233, 9,
FONT_NEXT, 244, 61,
FONT_END, 280, 39,
FONT_BEGIN, 233, 9,
FONT_NEXT, 280, 39,
FONT_NEXT, 294, -8,
FONT_NEXT, 319, 26,
FONT_NEXT, 361, -14,
FONT_NEXT, 361, 22,
FONT_END, 402, 26,
FONT_BEGIN, 361, -14,
FONT_NEXT, 402, 26,
FONT_NEXT, 427, -8,
FONT_NEXT, 441, 39,
FONT_END, 477, 61,
FONT_BEGIN, 427, -8,
FONT_NEXT, 477, 61,
FONT_NEXT, 488, 9,
FONT_NEXT, 509, 94,
FONT_END, 535, 136,
FONT_BEGIN, 488, 9,
FONT_NEXT, 535, 136,
FONT_NEXT, 544, 38,
FONT_NEXT, 556, 189,
FONT_END, 569, 254,
FONT_BEGIN, 544, 38,
FONT_NEXT, 569, 254,
FONT_NEXT, 592, 78,
FONT_NEXT, 574, 331,
FONT_NEXT, 586, 589,
FONT_NEXT, 569, 409,
FONT_NEXT, 536, 627,
FONT_NEXT, 555, 475,
FONT_END, 533, 528,
FONT_BEGIN, 536, 627,
FONT_NEXT, 533, 528,
FONT_NEXT, 481, 654,
FONT_NEXT, 505, 571,
FONT_END, 473, 602,
FONT_BEGIN, 481, 654,
FONT_NEXT, 473, 602,
FONT_NEXT, 422, 670,
FONT_NEXT, 437, 623,
FONT_END, 399, 636,
FONT_BEGIN, 422, 670,
FONT_NEXT, 399, 636,
FONT_NEXT, 361, 676,
FONT_NEXT, 361, 640,
FONT_NEXT, 299, 670,
FONT_NEXT, 322, 636,
FONT_END, 284, 623,
FONT_BEGIN, 299, 670,
FONT_NEXT, 284, 623,
FONT_NEXT, 240, 654,
FONT_NEXT, 248, 602,
FONT_END, 216, 571,
FONT_BEGIN, 240, 654,
FONT_NEXT, 216, 571,
FONT_NEXT, 185, 627,
FONT_NEXT, 188, 528,
FONT_END, 166, 475,
FONT_BEGIN, 185, 627,
FONT_NEXT, 166, 475,
FONT_NEXT, 136, 589,
FONT_NEXT, 152, 409,
FONT_END, 148, 331,
FONT_BEGIN, 136, 589,
FONT_NEXT, 148, 331,
FONT_END, 129, 78,
FONT_BEGIN, 136, 589,
FONT_NEXT, 129, 78,
FONT_NEXT, 94, 540,
FONT_NEXT, 89, 127,
FONT_NEXT, 62, 481,
FONT_NEXT, 59, 186,
FONT_NEXT, 41, 410,
FONT_NEXT, 40, 254,
FONT_END, 34, 330,
FONT_BEGIN, 688, 330,
FONT_NEXT, 681, 254,
FONT_NEXT, 680, 410,
FONT_NEXT, 662, 186,
FONT_NEXT, 659, 481,
FONT_NEXT, 632, 127,
FONT_NEXT, 627, 540,
FONT_NEXT, 592, 78,
FONT_END, 586, 589,
FONT_ADVANCE, 722, 0
    },
    {
80,
FONT_BEGIN, 16, 643,
FONT_NEXT, 16, 662,
FONT_NEXT, 59, 635,
FONT_END, 85, 622,
FONT_BEGIN, 85, 622,
FONT_NEXT, 16, 662,
FONT_END, 202, 591,
FONT_BEGIN, 85, 622,
FONT_NEXT, 202, 591,
FONT_NEXT, 97, 596,
FONT_NEXT, 202, 331,
FONT_END, 202, 291,
FONT_BEGIN, 97, 596,
FONT_NEXT, 202, 291,
FONT_END, 202, 109,
FONT_BEGIN, 97, 596,
FONT_NEXT, 202, 109,
FONT_NEXT, 100, 553,
FONT_END, 100, 120,
FONT_BEGIN, 100, 120,
FONT_NEXT, 202, 109,
FONT_NEXT, 98, 74,
FONT_END, 89, 44,
FONT_BEGIN, 89, 44,
FONT_NEXT, 202, 109,
FONT_NEXT, 64, 27,
FONT_NEXT, 205, 64,
FONT_END, 16, 0,
FONT_BEGIN, 64, 27,
FONT_NEXT, 16, 0,
FONT_END, 16, 19,
FONT_BEGIN, 542, 482,
FONT_NEXT, 534, 430,
FONT_NEXT, 536, 526,
FONT_END, 520, 563,
FONT_BEGIN, 520, 563,
FONT_NEXT, 534, 430,
FONT_END, 517, 391,
FONT_BEGIN, 520, 563,
FONT_NEXT, 517, 391,
FONT_NEXT, 495, 594,
FONT_NEXT, 497, 363,
FONT_END, 481, 347,
FONT_BEGIN, 495, 594,
FONT_NEXT, 481, 347,
FONT_NEXT, 462, 619,
FONT_NEXT, 444, 320,
FONT_END, 433, 480,
FONT_BEGIN, 462, 619,
FONT_NEXT, 433, 480,
FONT_END, 426, 527,
FONT_BEGIN, 462, 619,
FONT_NEXT, 426, 527,
FONT_NEXT, 379, 651,
FONT_NEXT, 409, 562,
FONT_END, 384, 588,
FONT_BEGIN, 379, 651,
FONT_NEXT, 384, 588,
FONT_END, 354, 606,
FONT_BEGIN, 379, 651,
FONT_NEXT, 354, 606,
FONT_NEXT, 280, 662,
FONT_NEXT, 291, 622,
FONT_END, 243, 625,
FONT_BEGIN, 280, 662,
FONT_NEXT, 243, 625,
FONT_NEXT, 16, 662,
FONT_NEXT, 211, 620,
FONT_END, 204, 610,
FONT_BEGIN, 16, 662,
FONT_NEXT, 204, 610,
FONT_END, 202, 591,
FONT_BEGIN, 202, 331,
FONT_NEXT, 262, 328,
FONT_NEXT, 202, 291,
FONT_END, 269, 288,
FONT_BEGIN, 269, 288,
FONT_NEXT, 262, 328,
FONT_END, 304, 330,
FONT_BEGIN, 269, 288,
FONT_NEXT, 304, 330,
FONT_NEXT, 340, 291,
FONT_NEXT, 361, 347,
FONT_END, 388, 365,
FONT_BEGIN, 340, 291,
FONT_NEXT, 388, 365,
FONT_NEXT, 398, 302,
FONT_NEXT, 411, 392,
FONT_END, 427, 429,
FONT_BEGIN, 398, 302,
FONT_NEXT, 427, 429,
FONT_NEXT, 444, 320,
FONT_END, 433, 480,
FONT_BEGIN, 296, 19,
FONT_NEXT, 296, 0,
FONT_NEXT, 248, 23,
FONT_NEXT, 16, 0,
FONT_NEXT, 220, 37,
FONT_END, 205, 64,
FONT_ADVANCE, 556, 0
    },
    {
81,
FONT_BEGIN, 34, 330,
FONT_NEXT, 41, 410,
FONT_NEXT, 40, 255,
FONT_END, 57, 191,
FONT_BEGIN, 57, 191,
FONT_NEXT, 41, 410,
FONT_END, 62, 480,
FONT_BEGIN, 57, 191,
FONT_NEXT, 62, 480,
FONT_NEXT, 82, 138,
FONT_NEXT, 94, 540,
FONT_NEXT, 114, 94,
FONT_NEXT, 136, 588,
FONT_END, 148, 330,
FONT_BEGIN, 114, 94,
FONT_NEXT, 148, 330,
FONT_NEXT, 151, 58,
FONT_NEXT, 152, 253,
FONT_END, 165, 189,
FONT_BEGIN, 151, 58,
FONT_NEXT, 165, 189,
FONT_END, 186, 136,
FONT_BEGIN, 151, 58,
FONT_NEXT, 186, 136,
FONT_NEXT, 189, 31,
FONT_NEXT, 212, 93,
FONT_END, 244, 61,
FONT_BEGIN, 189, 31,
FONT_NEXT, 244, 61,
FONT_NEXT, 265, -1,
FONT_NEXT, 280, 39,
FONT_NEXT, 312, -56,
FONT_NEXT, 319, 26,
FONT_END, 361, 22,
FONT_BEGIN, 312, -56,
FONT_NEXT, 361, 22,
FONT_NEXT, 379, -113,
FONT_NEXT, 402, 26,
FONT_NEXT, 426, -7,
FONT_NEXT, 441, 39,
FONT_NEXT, 468, 2,
FONT_NEXT, 477, 61,
FONT_END, 509, 93,
FONT_BEGIN, 468, 2,
FONT_NEXT, 509, 93,
FONT_NEXT, 512, 20,
FONT_NEXT, 535, 136,
FONT_NEXT, 556, 47,
FONT_NEXT, 556, 189,
FONT_END, 569, 253,
FONT_BEGIN, 556, 47,
FONT_NEXT, 569, 253,
FONT_END, 574, 330,
FONT_BEGIN, 556, 47,
FONT_NEXT, 574, 330,
FONT_NEXT, 597, 83,
FONT_NEXT, 586, 589,
FONT_END, 627, 540,
FONT_BEGIN, 597, 83,
FONT_NEXT, 627, 540,
FONT_NEXT, 633, 130,
FONT_NEXT, 659, 481,
FONT_NEXT, 662, 186,
FONT_NEXT, 680, 410,
FONT_NEXT, 681, 252,
FONT_END, 688, 330,
FONT_BEGIN, 574, 330,
FONT_NEXT, 569, 409,
FONT_NEXT, 586, 589,
FONT_END, 536, 627,
FONT_BEGIN, 536, 627,
FONT_NEXT, 569, 409,
FONT_END, 555, 475,
FONT_BEGIN, 536, 627,
FONT_NEXT, 555, 475,
FONT_END, 533, 528,
FONT_BEGIN, 536, 627,
FONT_NEXT, 533, 528,
FONT_NEXT, 481, 654,
FONT_NEXT, 505, 570,
FONT_END, 473, 602,
FONT_BEGIN, 481, 654,
FONT_NEXT, 473, 602,
FONT_NEXT, 422, 670,
FONT_NEXT, 437, 623,
FONT_END, 399, 636,
FONT_BEGIN, 422, 670,
FONT_NEXT, 399, 636,
FONT_NEXT, 361, 676,
FONT_NEXT, 361, 640,
FONT_NEXT, 299, 670,
FONT_NEXT, 322, 636,
FONT_END, 284, 623,
FONT_BEGIN, 299, 670,
FONT_NEXT, 284, 623,
FONT_NEXT, 240, 654,
FONT_NEXT, 248, 602,
FONT_END, 216, 570,
FONT_BEGIN, 240, 654,
FONT_NEXT, 216, 570,
FONT_NEXT, 185, 626,
FONT_NEXT, 188, 528,
FONT_END, 166, 475,
FONT_BEGIN, 185, 626,
FONT_NEXT, 166, 475,
FONT_NEXT, 136, 588,
FONT_NEXT, 152, 409,
FONT_END, 148, 330,
FONT_BEGIN, 701, -159,
FONT_NEXT, 701, -177,
FONT_NEXT, 638, -153,
FONT_NEXT, 654, -178,
FONT_END, 555, -172,
FONT_BEGIN, 638, -153,
FONT_NEXT, 555, -172,
FONT_NEXT, 586, -138,
FONT_END, 505, -90,
FONT_BEGIN, 505, -90,
FONT_NEXT, 555, -172,
FONT_END, 462, -151,
FONT_BEGIN, 505, -90,
FONT_NEXT, 462, -151,
FONT_NEXT, 426, -7,
FONT_END, 379, -113,
FONT_ADVANCE, 722, 0
    },
    {
82,
FONT_BEGIN, 17, 643,
FONT_NEXT, 17, 662,
FONT_NEXT, 59, 636,
FONT_END, 85, 623,
FONT_BEGIN, 85, 623,
FONT_NEXT, 17, 662,
FONT_END, 204, 589,
FONT_BEGIN, 85, 623,
FONT_NEXT, 204, 589,
FONT_NEXT, 98, 597,
FONT_NEXT, 204, 343,
FONT_END, 204, 306,
FONT_BEGIN, 98, 597,
FONT_NEXT, 204, 306,
FONT_END, 204, 109,
FONT_BEGIN, 98, 597,
FONT_NEXT, 204, 109,
FONT_NEXT, 102, 553,
FONT_END, 102, 120,
FONT_BEGIN, 102, 120,
FONT_NEXT, 204, 109,
FONT_NEXT, 100, 73,
FONT_END, 89, 43,
FONT_BEGIN, 89, 43,
FONT_NEXT, 204, 109,
FONT_NEXT, 64, 26,
FONT_NEXT, 206, 67,
FONT_END, 17, 0,
FONT_BEGIN, 64, 26,
FONT_NEXT, 17, 0,
FONT_END, 17, 19,
FONT_BEGIN, 204, 306,
FONT_NEXT, 204, 343,
FONT_NEXT, 260, 308,
FONT_NEXT, 276, 346,
FONT_END, 352, 362,
FONT_BEGIN, 260, 308,
FONT_NEXT, 352, 362,
FONT_NEXT, 366, 319,
FONT_NEXT, 386, 380,
FONT_END, 413, 406,
FONT_BEGIN, 366, 319,
FONT_NEXT, 413, 406,
FONT_NEXT, 415, 330,
FONT_NEXT, 431, 441,
FONT_END, 438, 489,
FONT_BEGIN, 415, 330,
FONT_NEXT, 438, 489,
FONT_NEXT, 475, 356,
FONT_END, 453, 632,
FONT_BEGIN, 453, 632,
FONT_NEXT, 438, 489,
FONT_END, 432, 533,
FONT_BEGIN, 453, 632,
FONT_NEXT, 432, 533,
FONT_END, 416, 566,
FONT_BEGIN, 453, 632,
FONT_NEXT, 416, 566,
FONT_NEXT, 370, 656,
FONT_NEXT, 394, 590,
FONT_END, 367, 606,
FONT_BEGIN, 370, 656,
FONT_NEXT, 367, 606,
FONT_NEXT, 293, 662,
FONT_NEXT, 310, 622,
FONT_END, 266, 625,
FONT_BEGIN, 293, 662,
FONT_NEXT, 266, 625,
FONT_NEXT, 17, 662,
FONT_NEXT, 233, 623,
FONT_END, 215, 617,
FONT_BEGIN, 17, 662,
FONT_NEXT, 215, 617,
FONT_END, 206, 606,
FONT_BEGIN, 17, 662,
FONT_NEXT, 206, 606,
FONT_END, 204, 589,
FONT_BEGIN, 659, 19,
FONT_NEXT, 659, 0,
FONT_NEXT, 608, 33,
FONT_NEXT, 498, 0,
FONT_NEXT, 572, 66,
FONT_END, 366, 319,
FONT_BEGIN, 366, 319,
FONT_NEXT, 498, 0,
FONT_END, 260, 308,
FONT_BEGIN, 366, 319,
FONT_END, 260, 308,
FONT_BEGIN, 547, 487,
FONT_NEXT, 541, 441,
FONT_NEXT, 539, 538,
FONT_NEXT, 525, 405,
FONT_NEXT, 519, 579,
FONT_NEXT, 503, 377,
FONT_NEXT, 490, 610,
FONT_NEXT, 475, 356,
FONT_END, 453, 632,
FONT_BEGIN, 294, 19,
FONT_NEXT, 294, 0,
FONT_NEXT, 246, 25,
FONT_NEXT, 17, 0,
FONT_NEXT, 219, 40,
FONT_END, 206, 67,
FONT_ADVANCE, 667, 0
    },
    {
83,
FONT_BEGIN, 71, 506,
FONT_NEXT, 75, 546,
FONT_NEXT, 82, 441,
FONT_NEXT, 86, 580,
FONT_NEXT, 114, 390,
FONT_NEXT, 127, 633,
FONT_END, 157, 542,
FONT_BEGIN, 114, 390,
FONT_NEXT, 157, 542,
FONT_NEXT, 163, 349,
FONT_NEXT, 171, 491,
FONT_END, 209, 448,
FONT_BEGIN, 163, 349,
FONT_NEXT, 209, 448,
FONT_NEXT, 227, 310,
FONT_END, 276, 280,
FONT_BEGIN, 276, 280,
FONT_NEXT, 209, 448,
FONT_NEXT, 314, 254,
FONT_NEXT, 324, 375,
FONT_NEXT, 343, 231,
FONT_END, 363, 210,
FONT_BEGIN, 363, 210,
FONT_NEXT, 324, 375,
FONT_NEXT, 385, 172,
FONT_NEXT, 385, 336,
FONT_NEXT, 390, 133,
FONT_NEXT, 409, 20,
FONT_END, 373, 2,
FONT_BEGIN, 390, 133,
FONT_NEXT, 373, 2,
FONT_NEXT, 382, 92,
FONT_END, 360, 57,
FONT_BEGIN, 360, 57,
FONT_NEXT, 373, 2,
FONT_END, 330, -10,
FONT_BEGIN, 360, 57,
FONT_NEXT, 330, -10,
FONT_NEXT, 322, 31,
FONT_NEXT, 280, -14,
FONT_NEXT, 270, 22,
FONT_NEXT, 227, -9,
FONT_NEXT, 223, 27,
FONT_NEXT, 185, 3,
FONT_NEXT, 183, 43,
FONT_NEXT, 151, 14,
FONT_NEXT, 124, 92,
FONT_NEXT, 125, 20,
FONT_END, 101, 9,
FONT_BEGIN, 124, 92,
FONT_NEXT, 101, 9,
FONT_NEXT, 86, 151,
FONT_NEXT, 94, -13,
FONT_END, 72, -13,
FONT_BEGIN, 86, 151,
FONT_NEXT, 72, -13,
FONT_NEXT, 65, 199,
FONT_END, 42, 199,
FONT_BEGIN, 469, 463,
FONT_NEXT, 444, 463,
FONT_NEXT, 447, 676,
FONT_END, 426, 676,
FONT_BEGIN, 426, 676,
FONT_NEXT, 444, 463,
FONT_END, 422, 524,
FONT_BEGIN, 426, 676,
FONT_NEXT, 422, 524,
FONT_NEXT, 418, 654,
FONT_NEXT, 384, 579,
FONT_NEXT, 408, 645,
FONT_END, 391, 642,
FONT_BEGIN, 391, 642,
FONT_NEXT, 384, 579,
FONT_NEXT, 367, 647,
FONT_NEXT, 328, 619,
FONT_NEXT, 334, 659,
FONT_END, 252, 676,
FONT_BEGIN, 252, 676,
FONT_NEXT, 328, 619,
FONT_END, 295, 630,
FONT_BEGIN, 252, 676,
FONT_NEXT, 295, 630,
FONT_END, 258, 635,
FONT_BEGIN, 252, 676,
FONT_NEXT, 258, 635,
FONT_END, 221, 629,
FONT_BEGIN, 252, 676,
FONT_NEXT, 221, 629,
FONT_NEXT, 185, 665,
FONT_NEXT, 188, 611,
FONT_END, 165, 582,
FONT_BEGIN, 185, 665,
FONT_NEXT, 165, 582,
FONT_NEXT, 127, 633,
FONT_END, 157, 542,
FONT_BEGIN, 491, 167,
FONT_NEXT, 478, 102,
FONT_NEXT, 476, 235,
FONT_NEXT, 439, 44,
FONT_NEXT, 438, 290,
FONT_NEXT, 409, 20,
FONT_END, 385, 336,
FONT_ADVANCE, 556, 0
    },
    {
84,
FONT_BEGIN, 17, 492,
FONT_NEXT, 23, 662,
FONT_NEXT, 41, 492,
FONT_END, 59, 557,
FONT_BEGIN, 59, 557,
FONT_NEXT, 23, 662,
FONT_NEXT, 87, 596,
FONT_END, 131, 615,
FONT_BEGIN, 131, 615,
FONT_NEXT, 23, 662,
FONT_NEXT, 200, 620,
FONT_END, 254, 620,
FONT_BEGIN, 254, 620,
FONT_NEXT, 23, 662,
FONT_END, 356, 620,
FONT_BEGIN, 254, 620,
FONT_NEXT, 356, 620,
FONT_END, 356, 109,
FONT_BEGIN, 254, 620,
FONT_NEXT, 356, 109,
FONT_NEXT, 254, 120,
FONT_END, 252, 73,
FONT_BEGIN, 252, 73,
FONT_NEXT, 356, 109,
FONT_NEXT, 241, 43,
FONT_END, 213, 26,
FONT_BEGIN, 213, 26,
FONT_NEXT, 356, 109,
FONT_END, 359, 66,
FONT_BEGIN, 213, 26,
FONT_NEXT, 359, 66,
FONT_END, 160, 0,
FONT_BEGIN, 213, 26,
FONT_NEXT, 160, 0,
FONT_END, 160, 19,
FONT_BEGIN, 593, 492,
FONT_NEXT, 569, 492,
FONT_NEXT, 587, 662,
FONT_NEXT, 550, 558,
FONT_END, 522, 597,
FONT_BEGIN, 587, 662,
FONT_NEXT, 522, 597,
FONT_END, 478, 615,
FONT_BEGIN, 587, 662,
FONT_NEXT, 478, 615,
FONT_NEXT, 23, 662,
FONT_NEXT, 410, 620,
FONT_END, 356, 620,
FONT_BEGIN, 452, 19,
FONT_NEXT, 452, 0,
FONT_NEXT, 401, 24,
FONT_NEXT, 160, 0,
FONT_NEXT, 372, 39,
FONT_END, 359, 66,
FONT_ADVANCE, 611, 0
    },
    {
85,
FONT_BEGIN, 14, 643,
FONT_NEXT, 14, 662,
FONT_NEXT, 60, 637,
FONT_END, 87, 623,
FONT_BEGIN, 87, 623,
FONT_NEXT, 14, 662,
FONT_END, 209, 596,
FONT_BEGIN, 87, 623,
FONT_NEXT, 209, 596,
FONT_END, 206, 553,
FONT_BEGIN, 87, 623,
FONT_NEXT, 206, 553,
FONT_NEXT, 100, 597,
FONT_NEXT, 206, 233,
FONT_END, 164, 50,
FONT_BEGIN, 100, 597,
FONT_NEXT, 164, 50,
FONT_NEXT, 104, 553,
FONT_NEXT, 135, 91,
FONT_END, 117, 133,
FONT_BEGIN, 104, 553,
FONT_NEXT, 117, 133,
FONT_END, 107, 175,
FONT_BEGIN, 104, 553,
FONT_NEXT, 107, 175,
FONT_END, 104, 212,
FONT_BEGIN, 104, 553,
FONT_NEXT, 104, 212,
FONT_END, 104, 241,
FONT_BEGIN, 297, 662,
FONT_NEXT, 297, 643,
FONT_NEXT, 14, 662,
FONT_NEXT, 249, 636,
FONT_END, 221, 622,
FONT_BEGIN, 14, 662,
FONT_NEXT, 221, 622,
FONT_END, 209, 596,
FONT_BEGIN, 473, 643,
FONT_NEXT, 473, 662,
FONT_NEXT, 517, 636,
FONT_NEXT, 629, 615,
FONT_END, 614, 578,
FONT_BEGIN, 517, 636,
FONT_NEXT, 614, 578,
FONT_NEXT, 546, 617,
FONT_NEXT, 611, 515,
FONT_NEXT, 562, 579,
FONT_NEXT, 611, 254,
FONT_END, 606, 178,
FONT_BEGIN, 562, 579,
FONT_NEXT, 606, 178,
FONT_END, 597, 134,
FONT_BEGIN, 562, 579,
FONT_NEXT, 597, 134,
FONT_NEXT, 567, 515,
FONT_NEXT, 578, 90,
FONT_NEXT, 567, 245,
FONT_END, 565, 192,
FONT_BEGIN, 565, 192,
FONT_NEXT, 578, 90,
FONT_END, 547, 49,
FONT_BEGIN, 565, 192,
FONT_NEXT, 547, 49,
FONT_NEXT, 560, 154,
FONT_END, 544, 109,
FONT_BEGIN, 544, 109,
FONT_NEXT, 547, 49,
FONT_END, 501, 16,
FONT_BEGIN, 544, 109,
FONT_NEXT, 501, 16,
FONT_NEXT, 517, 76,
FONT_END, 480, 51,
FONT_BEGIN, 480, 51,
FONT_NEXT, 501, 16,
FONT_END, 437, -6,
FONT_BEGIN, 480, 51,
FONT_NEXT, 437, -6,
FONT_NEXT, 434, 35,
FONT_NEXT, 352, -14,
FONT_NEXT, 380, 30,
FONT_END, 322, 36,
FONT_BEGIN, 322, 36,
FONT_NEXT, 352, -14,
FONT_END, 270, -6,
FONT_BEGIN, 322, 36,
FONT_NEXT, 270, -6,
FONT_NEXT, 279, 52,
FONT_END, 248, 77,
FONT_BEGIN, 248, 77,
FONT_NEXT, 270, -6,
FONT_END, 208, 17,
FONT_BEGIN, 248, 77,
FONT_NEXT, 208, 17,
FONT_NEXT, 227, 107,
FONT_END, 208, 174,
FONT_BEGIN, 208, 174,
FONT_NEXT, 208, 17,
FONT_NEXT, 206, 233,
FONT_END, 164, 50,
FONT_BEGIN, 705, 662,
FONT_NEXT, 705, 643,
FONT_NEXT, 473, 662,
FONT_NEXT, 657, 634,
FONT_END, 629, 615,
FONT_ADVANCE, 722, 0
    },
    {
86,
FONT_BEGIN, 282, 662,
FONT_NEXT, 282, 643,
FONT_NEXT, 16, 662,
FONT_NEXT, 246, 640,
FONT_END, 223, 635,
FONT_BEGIN, 16, 662,
FONT_NEXT, 223, 635,
FONT_END, 210, 624,
FONT_BEGIN, 16, 662,
FONT_NEXT, 210, 624,
FONT_END, 207, 606,
FONT_BEGIN, 16, 662,
FONT_NEXT, 207, 606,
FONT_END, 71, 623,
FONT_BEGIN, 16, 662,
FONT_NEXT, 71, 623,
FONT_END, 46, 638,
FONT_BEGIN, 16, 662,
FONT_NEXT, 46, 638,
FONT_END, 16, 643,
FONT_BEGIN, 549, 538,
FONT_NEXT, 556, 556,
FONT_NEXT, 546, 528,
FONT_NEXT, 383, -11,
FONT_NEXT, 399, 161,
FONT_END, 248, 499,
FONT_BEGIN, 248, 499,
FONT_NEXT, 383, -11,
FONT_END, 368, -11,
FONT_BEGIN, 248, 499,
FONT_NEXT, 368, -11,
FONT_END, 122, 538,
FONT_BEGIN, 248, 499,
FONT_NEXT, 122, 538,
FONT_NEXT, 218, 566,
FONT_END, 210, 590,
FONT_BEGIN, 210, 590,
FONT_NEXT, 122, 538,
FONT_NEXT, 207, 606,
FONT_NEXT, 94, 592,
FONT_END, 71, 623,
FONT_BEGIN, 492, 643,
FONT_NEXT, 492, 662,
FONT_NEXT, 542, 634,
FONT_NEXT, 625, 596,
FONT_NEXT, 558, 621,
FONT_NEXT, 605, 550,
FONT_NEXT, 565, 597,
FONT_END, 556, 556,
FONT_BEGIN, 556, 556,
FONT_NEXT, 605, 550,
FONT_END, 383, -11,
FONT_BEGIN, 556, 556,
FONT_END, 383, -11,
FONT_BEGIN, 697, 662,
FONT_NEXT, 697, 643,
FONT_NEXT, 492, 662,
FONT_NEXT, 666, 637,
FONT_END, 644, 623,
FONT_BEGIN, 492, 662,
FONT_NEXT, 644, 623,
FONT_END, 625, 596,
FONT_ADVANCE, 722, 0
    },
    {
87,
FONT_BEGIN, 125, 480,
FONT_NEXT, 108, 526,
FONT_NEXT, 147, 421,
FONT_END, 185, 609,
FONT_BEGIN, 185, 609,
FONT_NEXT, 108, 526,
FONT_END, 86, 578,
FONT_BEGIN, 185, 609,
FONT_NEXT, 86, 578,
FONT_END, 65, 613,
FONT_BEGIN, 185, 609,
FONT_NEXT, 65, 613,
FONT_END, 40, 634,
FONT_BEGIN, 185, 609,
FONT_NEXT, 40, 634,
FONT_NEXT, 5, 662,
FONT_END, 5, 643,
FONT_BEGIN, 313, 643,
FONT_NEXT, 313, 662,
FONT_NEXT, 346, 640,
FONT_END, 371, 628,
FONT_BEGIN, 371, 628,
FONT_NEXT, 313, 662,
FONT_END, 503, 605,
FONT_BEGIN, 371, 628,
FONT_NEXT, 503, 605,
FONT_NEXT, 391, 601,
FONT_END, 414, 553,
FONT_BEGIN, 414, 553,
FONT_NEXT, 503, 605,
FONT_NEXT, 447, 471,
FONT_NEXT, 470, 412,
FONT_END, 316, -11,
FONT_BEGIN, 447, 471,
FONT_NEXT, 316, -11,
FONT_NEXT, 340, 189,
FONT_END, 196, 565,
FONT_BEGIN, 196, 565,
FONT_NEXT, 316, -11,
FONT_END, 301, -11,
FONT_BEGIN, 196, 565,
FONT_NEXT, 301, -11,
FONT_END, 279, 51,
FONT_BEGIN, 196, 565,
FONT_NEXT, 279, 51,
FONT_END, 254, 123,
FONT_BEGIN, 196, 565,
FONT_NEXT, 254, 123,
FONT_END, 199, 277,
FONT_BEGIN, 196, 565,
FONT_NEXT, 199, 277,
FONT_END, 147, 421,
FONT_BEGIN, 196, 565,
FONT_NEXT, 147, 421,
FONT_END, 185, 609,
FONT_BEGIN, 580, 662,
FONT_NEXT, 580, 643,
FONT_NEXT, 313, 662,
FONT_NEXT, 529, 637,
FONT_END, 510, 626,
FONT_BEGIN, 313, 662,
FONT_NEXT, 510, 626,
FONT_END, 503, 605,
FONT_BEGIN, 734, 643,
FONT_NEXT, 734, 662,
FONT_NEXT, 781, 633,
FONT_NEXT, 864, 606,
FONT_END, 853, 582,
FONT_BEGIN, 781, 633,
FONT_NEXT, 853, 582,
FONT_NEXT, 797, 620,
FONT_END, 803, 597,
FONT_BEGIN, 803, 597,
FONT_NEXT, 853, 582,
FONT_END, 745, 288,
FONT_BEGIN, 803, 597,
FONT_NEXT, 745, 288,
FONT_NEXT, 787, 525,
FONT_END, 662, 186,
FONT_BEGIN, 662, 186,
FONT_NEXT, 745, 288,
FONT_END, 645, -11,
FONT_BEGIN, 662, 186,
FONT_NEXT, 645, -11,
FONT_NEXT, 530, 527,
FONT_NEXT, 630, -11,
FONT_END, 470, 412,
FONT_BEGIN, 530, 527,
FONT_NEXT, 470, 412,
FONT_NEXT, 510, 574,
FONT_END, 503, 605,
FONT_BEGIN, 932, 662,
FONT_NEXT, 932, 643,
FONT_NEXT, 734, 662,
FONT_NEXT, 900, 635,
FONT_END, 879, 624,
FONT_BEGIN, 734, 662,
FONT_NEXT, 879, 624,
FONT_END, 864, 606,
FONT_BEGIN, 250, 662,
FONT_NEXT, 250, 643,
FONT_NEXT, 5, 662,
FONT_NEXT, 220, 640,
FONT_END, 200, 635,
FONT_BEGIN, 5, 662,
FONT_NEXT, 200, 635,
FONT_END, 188, 625,
FONT_BEGIN, 5, 662,
FONT_NEXT, 188, 625,
FONT_END, 185, 609,
FONT_ADVANCE, 944, 0
    },
    {
88,
FONT_BEGIN, 22, 643,
FONT_NEXT, 22, 662,
FONT_NEXT, 60, 637,
FONT_END, 96, 618,
FONT_BEGIN, 96, 618,
FONT_NEXT, 22, 662,
FONT_END, 248, 611,
FONT_BEGIN, 96, 618,
FONT_NEXT, 248, 611,
FONT_NEXT, 116, 600,
FONT_END, 139, 573,
FONT_BEGIN, 139, 573,
FONT_NEXT, 248, 611,
FONT_NEXT, 168, 535,
FONT_END, 203, 486,
FONT_BEGIN, 203, 486,
FONT_NEXT, 248, 611,
FONT_END, 251, 592,
FONT_BEGIN, 203, 486,
FONT_NEXT, 251, 592,
FONT_END, 265, 565,
FONT_BEGIN, 203, 486,
FONT_NEXT, 265, 565,
FONT_END, 291, 523,
FONT_BEGIN, 203, 486,
FONT_NEXT, 291, 523,
FONT_NEXT, 312, 326,
FONT_NEXT, 333, 463,
FONT_END, 338, 288,
FONT_BEGIN, 312, 326,
FONT_NEXT, 338, 288,
FONT_END, 219, 140,
FONT_BEGIN, 312, 326,
FONT_NEXT, 219, 140,
FONT_NEXT, 155, 133,
FONT_NEXT, 185, 95,
FONT_END, 172, 73,
FONT_BEGIN, 155, 133,
FONT_NEXT, 172, 73,
FONT_END, 167, 56,
FONT_BEGIN, 155, 133,
FONT_NEXT, 167, 56,
FONT_NEXT, 128, 100,
FONT_END, 106, 74,
FONT_BEGIN, 106, 74,
FONT_NEXT, 167, 56,
FONT_NEXT, 73, 41,
FONT_NEXT, 10, 0,
FONT_NEXT, 44, 24,
FONT_END, 10, 19,
FONT_BEGIN, 324, 662,
FONT_NEXT, 324, 643,
FONT_NEXT, 22, 662,
FONT_NEXT, 290, 641,
FONT_END, 266, 637,
FONT_BEGIN, 22, 662,
FONT_NEXT, 266, 637,
FONT_END, 252, 628,
FONT_BEGIN, 22, 662,
FONT_NEXT, 252, 628,
FONT_END, 248, 611,
FONT_BEGIN, 458, 643,
FONT_NEXT, 458, 662,
FONT_NEXT, 505, 637,
FONT_NEXT, 592, 599,
FONT_NEXT, 521, 628,
FONT_END, 528, 610,
FONT_BEGIN, 528, 610,
FONT_NEXT, 592, 599,
FONT_END, 547, 549,
FONT_BEGIN, 528, 610,
FONT_NEXT, 547, 549,
FONT_NEXT, 516, 579,
FONT_NEXT, 401, 367,
FONT_NEXT, 488, 542,
FONT_END, 375, 404,
FONT_BEGIN, 375, 404,
FONT_NEXT, 401, 367,
FONT_END, 338, 288,
FONT_BEGIN, 375, 404,
FONT_NEXT, 338, 288,
FONT_END, 333, 463,
FONT_BEGIN, 696, 662,
FONT_NEXT, 696, 643,
FONT_NEXT, 458, 662,
FONT_NEXT, 660, 638,
FONT_END, 627, 627,
FONT_BEGIN, 458, 662,
FONT_NEXT, 627, 627,
FONT_END, 592, 599,
FONT_BEGIN, 704, 19,
FONT_NEXT, 704, 0,
FONT_NEXT, 670, 24,
FONT_NEXT, 407, 0,
FONT_NEXT, 643, 37,
FONT_END, 619, 58,
FONT_BEGIN, 619, 58,
FONT_NEXT, 407, 0,
FONT_END, 464, 25,
FONT_BEGIN, 619, 58,
FONT_NEXT, 464, 25,
FONT_NEXT, 593, 93,
FONT_NEXT, 479, 35,
FONT_END, 484, 53,
FONT_BEGIN, 593, 93,
FONT_NEXT, 484, 53,
FONT_NEXT, 401, 367,
FONT_NEXT, 478, 72,
FONT_END, 464, 98,
FONT_BEGIN, 401, 367,
FONT_NEXT, 464, 98,
FONT_END, 433, 148,
FONT_BEGIN, 401, 367,
FONT_NEXT, 433, 148,
FONT_END, 338, 288,
FONT_BEGIN, 407, 19,
FONT_NEXT, 440, 21,
FONT_NEXT, 407, 0,
FONT_END, 464, 25,
FONT_BEGIN, 243, 19,
FONT_NEXT, 243, 0,
FONT_NEXT, 190, 26,
FONT_NEXT, 10, 0,
FONT_NEXT, 173, 37,
FONT_END, 167, 56,
FONT_ADVANCE, 722, 0
    },
    {
89,
FONT_BEGIN, 302, 662,
FONT_NEXT, 302, 643,
FONT_NEXT, 22, 662,
FONT_NEXT, 271, 641,
FONT_END, 249, 637,
FONT_BEGIN, 22, 662,
FONT_NEXT, 249, 637,
FONT_END, 235, 628,
FONT_BEGIN, 22, 662,
FONT_NEXT, 235, 628,
FONT_END, 231, 612,
FONT_BEGIN, 22, 662,
FONT_NEXT, 231, 612,
FONT_END, 81, 619,
FONT_BEGIN, 22, 662,
FONT_NEXT, 81, 619,
FONT_END, 51, 637,
FONT_BEGIN, 22, 662,
FONT_NEXT, 51, 637,
FONT_END, 22, 643,
FONT_BEGIN, 484, 643,
FONT_NEXT, 484, 662,
FONT_NEXT, 530, 638,
FONT_NEXT, 646, 624,
FONT_END, 610, 590,
FONT_BEGIN, 530, 638,
FONT_NEXT, 610, 590,
FONT_NEXT, 546, 628,
FONT_NEXT, 589, 564,
FONT_NEXT, 553, 610,
FONT_NEXT, 565, 529,
FONT_NEXT, 539, 573,
FONT_NEXT, 417, 303,
FONT_NEXT, 396, 347,
FONT_NEXT, 417, 109,
FONT_END, 313, 72,
FONT_BEGIN, 396, 347,
FONT_NEXT, 313, 72,
FONT_END, 315, 120,
FONT_BEGIN, 396, 347,
FONT_NEXT, 315, 120,
FONT_END, 315, 294,
FONT_BEGIN, 396, 347,
FONT_NEXT, 315, 294,
FONT_NEXT, 248, 569,
FONT_NEXT, 184, 486,
FONT_NEXT, 231, 612,
FONT_NEXT, 149, 535,
FONT_END, 121, 573,
FONT_BEGIN, 231, 612,
FONT_NEXT, 121, 573,
FONT_END, 99, 600,
FONT_BEGIN, 231, 612,
FONT_NEXT, 99, 600,
FONT_END, 81, 619,
FONT_BEGIN, 703, 662,
FONT_NEXT, 703, 643,
FONT_NEXT, 484, 662,
FONT_NEXT, 675, 638,
FONT_END, 646, 624,
FONT_BEGIN, 520, 19,
FONT_NEXT, 520, 0,
FONT_NEXT, 465, 23,
FONT_NEXT, 214, 0,
FONT_NEXT, 434, 37,
FONT_END, 420, 64,
FONT_BEGIN, 420, 64,
FONT_NEXT, 214, 0,
FONT_END, 271, 24,
FONT_BEGIN, 420, 64,
FONT_NEXT, 271, 24,
FONT_END, 301, 41,
FONT_BEGIN, 420, 64,
FONT_NEXT, 301, 41,
FONT_NEXT, 417, 109,
FONT_END, 313, 72,
FONT_BEGIN, 214, 19,
FONT_NEXT, 246, 20,
FONT_NEXT, 214, 0,
FONT_END, 271, 24,
FONT_ADVANCE, 722, 0
    },
    {
90,
FONT_BEGIN, 577, 662,
FONT_NEXT, 577, 647,
FONT_NEXT, 51, 662,
FONT_NEXT, 446, 624,
FONT_END, 225, 624,
FONT_BEGIN, 51, 662,
FONT_NEXT, 225, 624,
FONT_END, 177, 621,
FONT_BEGIN, 51, 662,
FONT_NEXT, 177, 621,
FONT_END, 140, 612,
FONT_BEGIN, 51, 662,
FONT_NEXT, 140, 612,
FONT_END, 112, 599,
FONT_BEGIN, 51, 662,
FONT_NEXT, 112, 599,
FONT_END, 92, 582,
FONT_BEGIN, 51, 662,
FONT_NEXT, 92, 582,
FONT_END, 68, 540,
FONT_BEGIN, 51, 662,
FONT_NEXT, 68, 540,
FONT_END, 57, 491,
FONT_BEGIN, 51, 662,
FONT_NEXT, 57, 491,
FONT_END, 31, 491,
FONT_BEGIN, 597, 176,
FONT_NEXT, 573, 0,
FONT_NEXT, 574, 176,
FONT_END, 556, 123,
FONT_BEGIN, 556, 123,
FONT_NEXT, 573, 0,
FONT_NEXT, 528, 79,
FONT_END, 481, 49,
FONT_BEGIN, 481, 49,
FONT_NEXT, 573, 0,
FONT_NEXT, 446, 40,
FONT_NEXT, 9, 0,
FONT_NEXT, 402, 38,
FONT_END, 145, 38,
FONT_BEGIN, 145, 38,
FONT_NEXT, 9, 0,
FONT_END, 9, 15,
FONT_BEGIN, 145, 38,
FONT_NEXT, 9, 15,
FONT_END, 446, 624,
FONT_BEGIN, 145, 38,
FONT_NEXT, 446, 624,
FONT_END, 577, 647,
FONT_ADVANCE, 611, 0
    },
    {
91,
FONT_BEGIN, 299, 662,
FONT_NEXT, 299, 637,
FONT_NEXT, 88, 662,
FONT_NEXT, 209, 637,
FONT_END, 182, 632,
FONT_BEGIN, 88, 662,
FONT_NEXT, 182, 632,
FONT_END, 169, 620,
FONT_BEGIN, 88, 662,
FONT_NEXT, 169, 620,
FONT_END, 164, 593,
FONT_BEGIN, 88, 662,
FONT_NEXT, 164, 593,
FONT_END, 164, -79,
FONT_BEGIN, 88, 662,
FONT_NEXT, 164, -79,
FONT_NEXT, 88, -156,
FONT_NEXT, 170, -110,
FONT_END, 184, -125,
FONT_BEGIN, 88, -156,
FONT_NEXT, 184, -125,
FONT_END, 213, -131,
FONT_BEGIN, 88, -156,
FONT_NEXT, 213, -131,
FONT_NEXT, 299, -156,
FONT_END, 299, -131,
FONT_ADVANCE, 333, 0
    },
    {
92,
FONT_BEGIN, 287, -14,
FONT_NEXT, 219, -14,
FONT_NEXT, 58, 676,
FONT_END, -9, 676,
FONT_ADVANCE, 278, 0
    },
    {
93,
FONT_BEGIN, 34, 637,
FONT_NEXT, 34, 662,
FONT_NEXT, 120, 637,
FONT_END, 148, 630,
FONT_BEGIN, 148, 630,
FONT_NEXT, 34, 662,
FONT_END, 245, 662,
FONT_BEGIN, 148, 630,
FONT_NEXT, 245, 662,
FONT_NEXT, 162, 615,
FONT_END, 169, 585,
FONT_BEGIN, 169, 585,
FONT_NEXT, 245, 662,
FONT_END, 245, -156,
FONT_BEGIN, 169, 585,
FONT_NEXT, 245, -156,
FONT_NEXT, 169, -87,
FONT_END, 163, -115,
FONT_BEGIN, 163, -115,
FONT_NEXT, 245, -156,
FONT_NEXT, 150, -127,
FONT_NEXT, 34, -156,
FONT_NEXT, 124, -131,
FONT_END, 34, -131,
FONT_ADVANCE, 333, 0
    },
    {
94,
FONT_BEGIN, 446, 297,
FONT_NEXT, 378, 297,
FONT_NEXT, 265, 662,
FONT_NEXT, 235, 586,
FONT_NEXT, 205, 662,
FONT_NEXT, 92, 297,
FONT_END, 24, 297,
FONT_ADVANCE, 469, 0
    },
    {
95,
FONT_BEGIN, 500, -75,
FONT_NEXT, 500, -125,
FONT_NEXT, 0, -75,
FONT_END, 0, -125,
FONT_ADVANCE, 500, 0
    },
    {
96,
FONT_BEGIN, 236, 657,
FONT_NEXT, 195, 625,
FONT_NEXT, 227, 676,
FONT_END, 196, 658,
FONT_BEGIN, 196, 658,
FONT_NEXT, 195, 625,
FONT_NEXT, 159, 625,
FONT_NEXT, 170, 595,
FONT_END, 157, 569,
FONT_BEGIN, 159, 625,
FONT_NEXT, 157, 569,
FONT_NEXT, 128, 580,
FONT_NEXT, 154, 551,
FONT_END, 146, 449,
FONT_BEGIN, 128, 580,
FONT_NEXT, 146, 449,
FONT_END, 124, 478,
FONT_BEGIN, 128, 580,
FONT_NEXT, 124, 478,
FONT_END, 115, 529,
FONT_BEGIN, 254, 491,
FONT_NEXT, 248, 463,
FONT_NEXT, 247, 515,
FONT_NEXT, 234, 445,

FONT_NEXT, 232, 531,
FONT_NEXT, 196, 433,
FONT_NEXT, 192, 541,
FONT_NEXT, 173, 436,
FONT_NEXT, 177, 539,
FONT_END, 168, 537,
FONT_BEGIN, 168, 537,
FONT_NEXT, 173, 436,
FONT_END, 146, 449,
FONT_BEGIN, 168, 537,
FONT_NEXT, 146, 449,
FONT_NEXT, 158, 540,
FONT_END, 154, 551,
FONT_ADVANCE, 333, 0
    },
    {
97,
FONT_BEGIN, 56, 349,
FONT_NEXT, 65, 383,
FONT_NEXT, 65, 321,
FONT_END, 79, 309,
FONT_BEGIN, 79, 309,
FONT_NEXT, 65, 383,
FONT_END, 94, 419,
FONT_BEGIN, 79, 309,
FONT_NEXT, 94, 419,
FONT_NEXT, 99, 305,
FONT_END, 131, 318,
FONT_BEGIN, 131, 318,
FONT_NEXT, 94, 419,
FONT_END, 139, 387,
FONT_BEGIN, 131, 318,
FONT_NEXT, 139, 387,
FONT_END, 144, 348,
FONT_BEGIN, 37, 94,
FONT_NEXT, 41, 128,
FONT_NEXT, 45, 49,
FONT_NEXT, 53, 158,
FONT_NEXT, 69, 16,
FONT_NEXT, 73, 183,
FONT_END, 100, 206,
FONT_BEGIN, 69, 16,
FONT_NEXT, 100, 206,
FONT_NEXT, 103, -4,
FONT_END, 125, 126,
FONT_BEGIN, 125, 126,
FONT_NEXT, 100, 206,
FONT_NEXT, 125, 128,
FONT_END, 129, 157,
FONT_BEGIN, 129, 157,
FONT_NEXT, 100, 206,
FONT_NEXT, 149, 192,
FONT_END, 169, 210,
FONT_BEGIN, 169, 210,
FONT_NEXT, 100, 206,
FONT_END, 179, 248,
FONT_BEGIN, 169, 210,
FONT_NEXT, 179, 248,
FONT_NEXT, 197, 229,
FONT_END, 236, 249,
FONT_BEGIN, 236, 249,
FONT_NEXT, 179, 248,
FONT_NEXT, 287, 268,
FONT_NEXT, 287, 292,
FONT_NEXT, 287, 123,
FONT_NEXT, 287, 353,
FONT_NEXT, 288, 63,
FONT_END, 297, 21,
FONT_BEGIN, 297, 21,
FONT_NEXT, 287, 353,
FONT_NEXT, 305, 443,
FONT_NEXT, 279, 396,
FONT_NEXT, 268, 455,
FONT_NEXT, 259, 421,
FONT_NEXT, 219, 460,
FONT_NEXT, 234, 433,
FONT_END, 210, 436,
FONT_BEGIN, 219, 460,
FONT_NEXT, 210, 436,
FONT_NEXT, 178, 456,
FONT_NEXT, 162, 423,
FONT_NEXT, 144, 448,
FONT_NEXT, 145, 407,
FONT_END, 139, 387,
FONT_BEGIN, 144, 448,
FONT_NEXT, 139, 387,
FONT_END, 94, 419,
FONT_BEGIN, 287, 123,
FONT_NEXT, 288, 63,
FONT_NEXT, 281, 88,
FONT_NEXT, 222, 14,
FONT_NEXT, 271, 77,
FONT_END, 253, 66,
FONT_BEGIN, 253, 66,
FONT_NEXT, 222, 14,
FONT_NEXT, 226, 55,
FONT_END, 191, 48,
FONT_BEGIN, 191, 48,
FONT_NEXT, 222, 14,
FONT_END, 183, -4,
FONT_BEGIN, 191, 48,
FONT_NEXT, 183, -4,
FONT_NEXT, 165, 53,
FONT_NEXT, 143, -10,
FONT_NEXT, 144, 69,
FONT_END, 130, 93,
FONT_BEGIN, 130, 93,
FONT_NEXT, 143, -10,
FONT_END, 103, -4,
FONT_BEGIN, 130, 93,
FONT_NEXT, 103, -4,
FONT_END, 125, 126,
FONT_BEGIN, 368, 300,
FONT_NEXT, 368, 105,
FONT_NEXT, 365, 350,
FONT_NEXT, 353, -10,
FONT_NEXT, 349, 402,
FONT_NEXT, 315, -1,
FONT_NEXT, 331, 425,
FONT_END, 305, 443,
FONT_BEGIN, 305, 443,
FONT_NEXT, 315, -1,
FONT_END, 297, 21,
FONT_BEGIN, 305, 443,
FONT_END, 297, 21,
FONT_BEGIN, 442, 66,
FONT_NEXT, 442, 40,
FONT_NEXT, 423, 53,
FONT_NEXT, 412, 11,
FONT_NEXT, 397, 47,
FONT_NEXT, 387, -4,
FONT_NEXT, 380, 51,
FONT_NEXT, 353, -10,
FONT_NEXT, 371, 65,
FONT_END, 368, 105,
FONT_ADVANCE, 444, 0
    },
    {
98,
FONT_BEGIN, 3, 623,
FONT_NEXT, 3, 639,
FONT_NEXT, 25, 624,
FONT_END, 50, 620,
FONT_BEGIN, 50, 620,
FONT_NEXT, 3, 639,
FONT_END, 148, 683,
FONT_BEGIN, 50, 620,
FONT_NEXT, 148, 683,
FONT_NEXT, 63, 609,
FONT_END, 69, 573,
FONT_BEGIN, 69, 573,
FONT_NEXT, 148, 683,
FONT_END, 127, 8,
FONT_BEGIN, 69, 573,
FONT_NEXT, 127, 8,
FONT_END, 86, 28,
FONT_BEGIN, 69, 573,
FONT_NEXT, 86, 28,
FONT_END, 73, 40,
FONT_BEGIN, 69, 573,
FONT_NEXT, 73, 40,
FONT_END, 69, 54,
FONT_BEGIN, 153, 379,
FONT_NEXT, 155, 379,
FONT_NEXT, 153, 322,
FONT_END, 165, 357,
FONT_BEGIN, 165, 357,
FONT_NEXT, 155, 379,
FONT_END, 167, 401,
FONT_BEGIN, 165, 357,
FONT_NEXT, 167, 401,
FONT_NEXT, 192, 380,
FONT_NEXT, 196, 428,
FONT_NEXT, 224, 393,
FONT_NEXT, 237, 450,
FONT_NEXT, 253, 397,
FONT_END, 284, 392,
FONT_BEGIN, 284, 392,
FONT_NEXT, 237, 450,
FONT_END, 290, 460,
FONT_BEGIN, 284, 392,
FONT_NEXT, 290, 460,
FONT_NEXT, 311, 379,
FONT_NEXT, 332, 454,
FONT_NEXT, 350, 333,
FONT_NEXT, 368, 440,
FONT_NEXT, 372, 270,
FONT_END, 380, 201,
FONT_BEGIN, 380, 201,
FONT_NEXT, 368, 440,
FONT_END, 399, 417,
FONT_BEGIN, 380, 201,
FONT_NEXT, 399, 417,
FONT_END, 382, 44,
FONT_BEGIN, 380, 201,
FONT_NEXT, 382, 44,
FONT_NEXT, 375, 145,
FONT_NEXT, 340, 16,
FONT_NEXT, 357, 86,
FONT_END, 340, 61,
FONT_BEGIN, 340, 61,
FONT_NEXT, 340, 16,
FONT_NEXT, 317, 40,
FONT_NEXT, 287, -4,
FONT_NEXT, 287, 26,
FONT_END, 249, 22,
FONT_BEGIN, 249, 22,
FONT_NEXT, 287, -4,
FONT_END, 224, -10,
FONT_BEGIN, 249, 22,
FONT_NEXT, 224, -10,
FONT_NEXT, 223, 24,
FONT_NEXT, 178, -6,
FONT_NEXT, 191, 32,
FONT_END, 164, 47,
FONT_BEGIN, 164, 47,
FONT_NEXT, 178, -6,
FONT_END, 127, 8,
FONT_BEGIN, 164, 47,
FONT_NEXT, 127, 8,
FONT_NEXT, 153, 70,
FONT_NEXT, 148, 683,
FONT_NEXT, 153, 322,
FONT_END, 153, 379,
FONT_BEGIN, 153, 379,
FONT_NEXT, 148, 683,
FONT_END, 153, 681,
FONT_BEGIN, 153, 379,
FONT_END, 153, 681,
FONT_BEGIN, 468, 238,
FONT_NEXT, 455, 161,
FONT_NEXT, 457, 317,
FONT_END, 424, 389,
FONT_BEGIN, 424, 389,
FONT_NEXT, 455, 161,
FONT_END, 439, 119,
FONT_BEGIN, 424, 389,
FONT_NEXT, 439, 119,
FONT_END, 415, 80,
FONT_BEGIN, 424, 389,
FONT_NEXT, 415, 80,
FONT_NEXT, 399, 417,
FONT_END, 382, 44,
FONT_ADVANCE, 500, 0
    },
    {
99,
FONT_BEGIN, 25, 213,
FONT_NEXT, 30, 270,
FONT_NEXT, 29, 158,
FONT_END, 42, 111,
FONT_BEGIN, 42, 111,
FONT_NEXT, 30, 270,
FONT_END, 44, 320,
FONT_BEGIN, 42, 111,
FONT_NEXT, 44, 320,
FONT_NEXT, 63, 73,
FONT_NEXT, 67, 363,
FONT_NEXT, 88, 42,
FONT_NEXT, 96, 398,
FONT_NEXT, 102, 253,
FONT_END, 106, 302,
FONT_BEGIN, 106, 302,
FONT_NEXT, 96, 398,
FONT_NEXT, 116, 342,
FONT_NEXT, 130, 425,
FONT_NEXT, 133, 373,
FONT_END, 152, 397,
FONT_BEGIN, 152, 397,
FONT_NEXT, 130, 425,
FONT_END, 168, 444,
FONT_BEGIN, 152, 397,
FONT_NEXT, 168, 444,
FONT_NEXT, 195, 423,
FONT_END, 231, 431,
FONT_BEGIN, 231, 431,
FONT_NEXT, 168, 444,
FONT_END, 249, 460,
FONT_BEGIN, 231, 431,
FONT_NEXT, 249, 460,
FONT_NEXT, 262, 427,
FONT_END, 280, 418,
FONT_BEGIN, 280, 418,
FONT_NEXT, 249, 460,
FONT_END, 306, 450,
FONT_BEGIN, 280, 418,
FONT_NEXT, 306, 450,
FONT_NEXT, 297, 383,
FONT_END, 303, 361,
FONT_BEGIN, 303, 361,
FONT_NEXT, 306, 450,
FONT_NEXT, 320, 329,
FONT_END, 334, 318,
FONT_BEGIN, 334, 318,
FONT_NEXT, 306, 450,
FONT_NEXT, 352, 315,
FONT_NEXT, 355, 429,
FONT_NEXT, 386, 329,
FONT_NEXT, 387, 392,
FONT_END, 398, 359,
FONT_BEGIN, 412, 147,
FONT_NEXT, 369, 74,
FONT_NEXT, 398, 156,
FONT_END, 340, 89,
FONT_BEGIN, 340, 89,
FONT_NEXT, 369, 74,
FONT_END, 322, 25,
FONT_BEGIN, 340, 89,
FONT_NEXT, 322, 25,
FONT_NEXT, 302, 69,
FONT_NEXT, 269, -2,
FONT_NEXT, 255, 62,
FONT_NEXT, 212, -10,
FONT_NEXT, 195, 75,
FONT_NEXT, 149, 2,
FONT_NEXT, 147, 114,
FONT_NEXT, 88, 42,
FONT_NEXT, 114, 174,
FONT_END, 102, 253,
FONT_ADVANCE, 444, 0
    },
    {
100,
FONT_BEGIN, 113, 247,
FONT_NEXT, 116, 195,
FONT_NEXT, 103, 25,
FONT_NEXT, 125, 152,
FONT_END, 139, 116,
FONT_BEGIN, 103, 25,
FONT_NEXT, 139, 116,
FONT_END, 157, 88,
FONT_BEGIN, 103, 25,
FONT_NEXT, 157, 88,
FONT_NEXT, 164, -4,
FONT_NEXT, 201, 53,
FONT_NEXT, 209, -10,
FONT_NEXT, 251, 42,
FONT_NEXT, 257, -4,
FONT_NEXT, 281, 46,
FONT_NEXT, 294, 11,
FONT_NEXT, 308, 58,
FONT_NEXT, 320, 32,
FONT_NEXT, 328, 77,
FONT_NEXT, 338, 54,
FONT_END, 340, 54,
FONT_BEGIN, 340, 54,
FONT_NEXT, 328, 77,
FONT_END, 340, 102,
FONT_BEGIN, 340, 54,
FONT_NEXT, 340, 102,
FONT_NEXT, 340, -7,
FONT_NEXT, 340, 332,
FONT_NEXT, 340, 417,
FONT_NEXT, 327, 376,
FONT_NEXT, 290, 449,
FONT_NEXT, 303, 407,
FONT_END, 271, 425,
FONT_BEGIN, 290, 449,
FONT_NEXT, 271, 425,
FONT_NEXT, 234, 460,
FONT_NEXT, 237, 432,
FONT_END, 194, 422,
FONT_BEGIN, 234, 460,
FONT_NEXT, 194, 422,
FONT_NEXT, 192, 454,
FONT_NEXT, 154, 390,
FONT_NEXT, 154, 439,
FONT_END, 119, 416,
FONT_BEGIN, 119, 416,
FONT_NEXT, 154, 390,
FONT_END, 124, 333,
FONT_BEGIN, 119, 416,
FONT_NEXT, 124, 333,
FONT_END, 116, 293,
FONT_BEGIN, 119, 416,
FONT_NEXT, 116, 293,
FONT_NEXT, 88, 386,
FONT_NEXT, 113, 247,
FONT_END, 103, 25,
FONT_BEGIN, 88, 386,
FONT_NEXT, 103, 25,
FONT_END, 74, 53,
FONT_BEGIN, 88, 386,
FONT_NEXT, 74, 53,
FONT_NEXT, 43, 307,
FONT_NEXT, 49, 92,
FONT_END, 33, 145,
FONT_BEGIN, 43, 307,
FONT_NEXT, 33, 145,
FONT_NEXT, 31, 262,
FONT_END, 27, 214,
FONT_BEGIN, 272, 623,
FONT_NEXT, 272, 639,
FONT_NEXT, 296, 624,
FONT_END, 321, 620,
FONT_BEGIN, 321, 620,
FONT_NEXT, 272, 639,
FONT_END, 419, 683,
FONT_BEGIN, 321, 620,
FONT_NEXT, 419, 683,
FONT_NEXT, 334, 609,
FONT_END, 340, 573,
FONT_BEGIN, 340, 573,
FONT_NEXT, 419, 683,
FONT_END, 344, -10,
FONT_BEGIN, 340, 573,
FONT_NEXT, 344, -10,
FONT_END, 340, -7,
FONT_BEGIN, 340, 573,
FONT_NEXT, 340, -7,
FONT_END, 340, 417,
FONT_BEGIN, 491, 58,
FONT_NEXT, 491, 42,
FONT_NEXT, 452, 58,
FONT_NEXT, 344, -10,
FONT_NEXT, 432, 68,
FONT_END, 425, 87,
FONT_BEGIN, 425, 87,
FONT_NEXT, 344, -10,
FONT_NEXT, 424, 114,
FONT_NEXT, 419, 683,
FONT_END, 424, 681,
FONT_ADVANCE, 500, 0
    },
    {
101,
FONT_BEGIN, 424, 157,
FONT_NEXT, 417, 140,
FONT_NEXT, 408, 164,
FONT_NEXT, 406, 117,
FONT_NEXT, 386, 131,
FONT_NEXT, 368, 61,
FONT_NEXT, 355, 97,
FONT_NEXT, 304, 11,
FONT_NEXT, 311, 70,
FONT_END, 253, 59,
FONT_BEGIN, 253, 59,
FONT_NEXT, 304, 11,
FONT_END, 262, -5,
FONT_BEGIN, 253, 59,
FONT_NEXT, 262, -5,
FONT_END, 212, -10,
FONT_BEGIN, 253, 59,
FONT_NEXT, 212, -10,
FONT_NEXT, 202, 68,
FONT_NEXT, 172, -7,
FONT_NEXT, 153, 101,
FONT_NEXT, 135, 5,
FONT_NEXT, 132, 130,
FONT_NEXT, 104, 23,
FONT_NEXT, 114, 168,
FONT_END, 101, 216,
FONT_BEGIN, 101, 216,
FONT_NEXT, 104, 23,
FONT_END, 76, 48,
FONT_BEGIN, 101, 216,
FONT_NEXT, 76, 48,
FONT_NEXT, 94, 277,
FONT_END, 68, 373,
FONT_BEGIN, 68, 373,
FONT_NEXT, 76, 48,
FONT_END, 54, 80,
FONT_BEGIN, 68, 373,
FONT_NEXT, 54, 80,
FONT_NEXT, 45, 330,
FONT_NEXT, 38, 119,
FONT_NEXT, 30, 277,
FONT_NEXT, 28, 163,
FONT_END, 25, 214,
FONT_BEGIN, 303, 309,
FONT_NEXT, 94, 277,
FONT_NEXT, 97, 309,
FONT_NEXT, 68, 373,
FONT_END, 97, 406,
FONT_BEGIN, 97, 309,
FONT_NEXT, 97, 406,
FONT_NEXT, 113, 364,
FONT_NEXT, 129, 430,
FONT_NEXT, 137, 399,
FONT_NEXT, 163, 447,
FONT_NEXT, 168, 418,
FONT_END, 207, 424,
FONT_BEGIN, 207, 424,
FONT_NEXT, 163, 447,
FONT_END, 229, 460,
FONT_BEGIN, 207, 424,
FONT_NEXT, 229, 460,
FONT_NEXT, 254, 412,
FONT_END, 282, 384,
FONT_BEGIN, 282, 384,
FONT_NEXT, 229, 460,
FONT_END, 294, 449,
FONT_BEGIN, 282, 384,
FONT_NEXT, 294, 449,
FONT_NEXT, 303, 309,
FONT_NEXT, 348, 417,
FONT_END, 386, 360,
FONT_BEGIN, 303, 309,
FONT_NEXT, 386, 360,
FONT_END, 398, 322,
FONT_BEGIN, 303, 309,
FONT_NEXT, 398, 322,
FONT_NEXT, 94, 277,
FONT_END, 405, 277,
FONT_ADVANCE, 444, 0
    },
    {
102,
FONT_BEGIN, 383, 622,
FONT_NEXT, 379, 603,
FONT_NEXT, 375, 645,
FONT_NEXT, 369, 590,
FONT_NEXT, 354, 665,
FONT_NEXT, 340, 580,
FONT_NEXT, 322, 678,
FONT_NEXT, 246, 655,
FONT_NEXT, 283, 683,
FONT_END, 223, 675,
FONT_BEGIN, 223, 675,
FONT_NEXT, 246, 655,
FONT_END, 213, 646,
FONT_BEGIN, 223, 675,
FONT_NEXT, 213, 646,
FONT_NEXT, 178, 654,
FONT_NEXT, 195, 624,
FONT_END, 186, 566,
FONT_BEGIN, 178, 654,
FONT_NEXT, 186, 566,
FONT_END, 186, 450,
FONT_BEGIN, 178, 654,
FONT_NEXT, 186, 450,
FONT_END, 99, 58,
FONT_BEGIN, 178, 654,
FONT_NEXT, 99, 58,
FONT_END, 103, 104,
FONT_BEGIN, 178, 654,
FONT_NEXT, 103, 104,
FONT_NEXT, 146, 624,
FONT_END, 125, 587,
FONT_BEGIN, 125, 587,
FONT_NEXT, 103, 104,
FONT_NEXT, 105, 510,
FONT_END, 103, 476,
FONT_BEGIN, 103, 476,
FONT_NEXT, 103, 104,
FONT_NEXT, 103, 450,
FONT_NEXT, 103, 418,
FONT_NEXT, 21, 450,
FONT_END, 21, 418,
FONT_BEGIN, 309, 450,
FONT_NEXT, 309, 418,
FONT_NEXT, 186, 450,
FONT_NEXT, 187, 418,
FONT_END, 187, 104,
FONT_BEGIN, 186, 450,
FONT_NEXT, 187, 104,
FONT_NEXT, 99, 58,
FONT_END, 86, 31,
FONT_BEGIN, 86, 31,
FONT_NEXT, 187, 104,
FONT_END, 188, 64,
FONT_BEGIN, 86, 31,
FONT_NEXT, 188, 64,
FONT_END, 20, 0,
FONT_BEGIN, 86, 31,
FONT_NEXT, 20, 0,
FONT_NEXT, 60, 19,
FONT_END, 20, 15,
FONT_BEGIN, 280, 15,
FONT_NEXT, 280, 0,
FONT_NEXT, 226, 21,
FONT_NEXT, 20, 0,
FONT_NEXT, 199, 37,
FONT_END, 188, 64,
FONT_ADVANCE, 333, 0
    },
    {
103,
FONT_BEGIN, 69, 293,
FONT_NEXT, 73, 334,
FONT_NEXT, 75, 251,
FONT_NEXT, 85, 369,
FONT_NEXT, 93, 216,
FONT_END, 122, 186,
FONT_BEGIN, 122, 186,
FONT_NEXT, 85, 369,
FONT_END, 126, 421,
FONT_BEGIN, 122, 186,
FONT_NEXT, 126, 421,
FONT_END, 152, 342,
FONT_BEGIN, 122, 186,
FONT_NEXT, 152, 342,
FONT_END, 157, 282,
FONT_BEGIN, 122, 186,
FONT_NEXT, 157, 282,
FONT_NEXT, 162, 163,
FONT_NEXT, 174, 228,
FONT_END, 175, 146,
FONT_BEGIN, 162, 163,
FONT_NEXT, 175, 146,
FONT_END, 155, 129,
FONT_BEGIN, 162, 163,
FONT_NEXT, 155, 129,
FONT_NEXT, 132, 136,
FONT_NEXT, 139, 109,
FONT_END, 133, 91,
FONT_BEGIN, 132, 136,
FONT_NEXT, 133, 91,
FONT_END, 126, 1,
FONT_BEGIN, 132, 136,
FONT_NEXT, 126, 1,
FONT_NEXT, 110, 115,
FONT_NEXT, 89, 21,
FONT_NEXT, 84, 85,
FONT_NEXT, 77, 35,
FONT_NEXT, 74, 67,
FONT_END, 73, 54,
FONT_BEGIN, 175, 146,
FONT_NEXT, 174, 228,
FONT_NEXT, 193, 154,
FONT_NEXT, 206, 189,
FONT_END, 229, 177,
FONT_BEGIN, 193, 154,
FONT_NEXT, 229, 177,
FONT_NEXT, 247, 149,
FONT_NEXT, 256, 174,
FONT_NEXT, 295, 156,
FONT_NEXT, 296, 185,
FONT_END, 318, 213,
FONT_BEGIN, 295, 156,
FONT_NEXT, 318, 213,
FONT_NEXT, 347, 182,
FONT_NEXT, 327, 245,
FONT_END, 329, 270,
FONT_BEGIN, 347, 182,
FONT_NEXT, 329, 270,
FONT_NEXT, 338, 437,
FONT_NEXT, 325, 307,
FONT_NEXT, 287, 454,
FONT_NEXT, 310, 361,
FONT_END, 279, 410,
FONT_BEGIN, 287, 454,
FONT_NEXT, 279, 410,
FONT_NEXT, 241, 460,
FONT_NEXT, 256, 426,
FONT_END, 227, 432,
FONT_BEGIN, 241, 460,
FONT_NEXT, 227, 432,
FONT_NEXT, 182, 450,
FONT_NEXT, 200, 427,
FONT_END, 176, 412,
FONT_BEGIN, 182, 450,
FONT_NEXT, 176, 412,
FONT_NEXT, 126, 421,
FONT_NEXT, 158, 384,
FONT_END, 152, 342,
FONT_BEGIN, 28, -121,
FONT_NEXT, 36, -89,
FONT_NEXT, 41, -159,
FONT_NEXT, 57, -57,
FONT_NEXT, 78, -190,
FONT_NEXT, 98, -90,
FONT_END, 105, -115,
FONT_BEGIN, 78, -190,
FONT_NEXT, 105, -115,
FONT_END, 130, -138,
FONT_BEGIN, 78, -190,
FONT_NEXT, 130, -138,
FONT_NEXT, 134, -211,
FONT_NEXT, 176, -155,
FONT_NEXT, 205, -218,
FONT_NEXT, 248, -161,
FONT_NEXT, 282, -208,
FONT_NEXT, 326, -154,
FONT_NEXT, 366, -176,
FONT_NEXT, 384, -133,
FONT_NEXT, 403, -153,
FONT_NEXT, 420, -102,
FONT_NEXT, 433, -124,
FONT_NEXT, 433, -64,
FONT_NEXT, 440, 13,
FONT_END, 404, 43,
FONT_BEGIN, 404, 43,
FONT_NEXT, 433, -64,
FONT_END, 424, -38,
FONT_BEGIN, 404, 43,
FONT_NEXT, 424, -38,
FONT_END, 399, -24,
FONT_BEGIN, 404, 43,
FONT_NEXT, 399, -24,
FONT_NEXT, 375, 53,
FONT_NEXT, 360, -18,
FONT_NEXT, 340, 58,
FONT_NEXT, 310, -15,
FONT_NEXT, 211, 64,
FONT_NEXT, 251, -14,
FONT_END, 210, -12,
FONT_BEGIN, 211, 64,
FONT_NEXT, 210, -12,
FONT_NEXT, 160, 69,
FONT_NEXT, 147, -2,
FONT_NEXT, 140, 77,
FONT_NEXT, 126, 1,
FONT_END, 133, 91,
FONT_BEGIN, 147, -2,
FONT_NEXT, 110, -49,
FONT_NEXT, 126, 1,
FONT_END, 57, -57,
FONT_BEGIN, 57, -57,
FONT_NEXT, 110, -49,
FONT_END, 98, -90,
FONT_BEGIN, 57, -57,
FONT_END, 98, -90,
FONT_BEGIN, 461, -49,
FONT_NEXT, 453, -89,
FONT_NEXT, 457, -22,
FONT_END, 440, 13,
FONT_BEGIN, 440, 13,
FONT_NEXT, 453, -89,
FONT_END, 433, -124,
FONT_BEGIN, 440, 13,
FONT_END, 433, -124,
FONT_BEGIN, 470, 427,
FONT_NEXT, 470, 388,
FONT_NEXT, 393, 427,
FONT_NEXT, 387, 388,
FONT_NEXT, 338, 437,
FONT_END, 347, 182,
FONT_BEGIN, 347, 182,
FONT_NEXT, 387, 388,
FONT_NEXT, 389, 227,
FONT_NEXT, 400, 352,
FONT_NEXT, 401, 258,
FONT_NEXT, 404, 328,
FONT_END, 406, 296,
FONT_ADVANCE, 500, 0
    },
    {
104,
FONT_BEGIN, 157, 680,
FONT_NEXT, 157, 378,
FONT_NEXT, 152, 683,
FONT_NEXT, 157, 343,
FONT_END, 157, 102,
FONT_BEGIN, 152, 683,
FONT_NEXT, 157, 102,
FONT_END, 70, 58,
FONT_BEGIN, 152, 683,
FONT_NEXT, 70, 58,
FONT_END, 73, 102,
FONT_BEGIN, 152, 683,
FONT_NEXT, 73, 102,
FONT_END, 73, 573,
FONT_BEGIN, 152, 683,
FONT_NEXT, 73, 573,
FONT_END, 67, 609,
FONT_BEGIN, 152, 683,
FONT_NEXT, 67, 609,
FONT_END, 54, 620,
FONT_BEGIN, 152, 683,
FONT_NEXT, 54, 620,
FONT_NEXT, 10, 639,
FONT_NEXT, 29, 624,
FONT_END, 10, 623,
FONT_BEGIN, 427, 301,
FONT_NEXT, 427, 102,
FONT_NEXT, 424, 342,
FONT_NEXT, 339, 56,
FONT_NEXT, 411, 395,
FONT_END, 396, 420,
FONT_BEGIN, 396, 420,
FONT_NEXT, 339, 56,
FONT_NEXT, 374, 440,
FONT_NEXT, 343, 102,
FONT_NEXT, 343, 454,
FONT_NEXT, 343, 300,
FONT_END, 338, 344,
FONT_BEGIN, 343, 454,
FONT_NEXT, 338, 344,
FONT_NEXT, 303, 460,
FONT_NEXT, 325, 378,
FONT_END, 301, 398,
FONT_BEGIN, 303, 460,
FONT_NEXT, 301, 398,
FONT_NEXT, 260, 454,
FONT_NEXT, 266, 406,
FONT_END, 216, 392,
FONT_BEGIN, 260, 454,
FONT_NEXT, 216, 392,
FONT_NEXT, 221, 437,
FONT_END, 187, 411,
FONT_BEGIN, 187, 411,
FONT_NEXT, 216, 392,
FONT_END, 187, 373,
FONT_BEGIN, 187, 411,
FONT_NEXT, 187, 373,
FONT_NEXT, 159, 378,
FONT_NEXT, 157, 343,
FONT_END, 157, 378,
FONT_BEGIN, 487, 15,
FONT_NEXT, 487, 0,
FONT_NEXT, 456, 22,
FONT_NEXT, 275, 0,
FONT_NEXT, 438, 34,
FONT_END, 429, 58,
FONT_BEGIN, 429, 58,
FONT_NEXT, 275, 0,
FONT_END, 327, 31,
FONT_BEGIN, 429, 58,
FONT_NEXT, 327, 31,
FONT_NEXT, 427, 102,
FONT_END, 339, 56,
FONT_BEGIN, 275, 15,
FONT_NEXT, 306, 19,
FONT_NEXT, 275, 0,
FONT_END, 327, 31,
FONT_BEGIN, 225, 15,
FONT_NEXT, 225, 0,
FONT_NEXT, 193, 19,
FONT_NEXT, 9, 0,
FONT_NEXT, 172, 31,
FONT_END, 160, 56,
FONT_BEGIN, 160, 56,
FONT_NEXT, 9, 0,
FONT_END, 61, 33,
FONT_BEGIN, 160, 56,
FONT_NEXT, 61, 33,
FONT_NEXT, 157, 102,
FONT_END, 70, 58,
FONT_BEGIN, 9, 15,
FONT_NEXT, 41, 21,
FONT_NEXT, 9, 0,
FONT_END, 61, 33,
FONT_ADVANCE, 500, 0
    },
    {
105,
FONT_BEGIN, 179, 457,
FONT_NEXT, 179, 102,
FONT_NEXT, 175, 460,
FONT_NEXT, 91, 55,
FONT_END, 95, 102,
FONT_BEGIN, 175, 460,
FONT_NEXT, 95, 102,
FONT_END, 95, 334,
FONT_BEGIN, 175, 460,
FONT_NEXT, 95, 334,
FONT_END, 91, 368,
FONT_BEGIN, 175, 460,
FONT_NEXT, 91, 368,
FONT_END, 83, 386,
FONT_BEGIN, 175, 460,
FONT_NEXT, 83, 386,
FONT_END, 60, 394,
FONT_BEGIN, 175, 460,
FONT_NEXT, 60, 394,
FONT_NEXT, 20, 405,
FONT_END, 20, 390,
FONT_BEGIN, 16, 15,
FONT_NEXT, 54, 18,
FONT_NEXT, 16, 0,
FONT_END, 180, 61,
FONT_BEGIN, 180, 61,
FONT_NEXT, 54, 18,
FONT_END, 78, 30,
FONT_BEGIN, 180, 61,
FONT_NEXT, 78, 30,
FONT_NEXT, 179, 102,
FONT_END, 91, 55,
FONT_BEGIN, 253, 15,
FONT_NEXT, 253, 0,
FONT_NEXT, 212, 21,
FONT_NEXT, 16, 0,
FONT_NEXT, 190, 35,
FONT_END, 180, 61,
FONT_BEGIN, 180, 632,
FONT_NEXT, 176, 611,
FONT_NEXT, 165, 668,
FONT_NEXT, 165, 595,
FONT_NEXT, 148, 678,
FONT_NEXT, 148, 584,
FONT_NEXT, 128, 683,
FONT_NEXT, 128, 581,
FONT_NEXT, 105, 677,
FONT_NEXT, 105, 586,
FONT_NEXT, 89, 664,
FONT_NEXT, 89, 599,
FONT_END, 78, 632,
FONT_ADVANCE, 278, 0
    },
    {
106,
FONT_BEGIN, 32, 390,
FONT_NEXT, 32, 406,
FONT_NEXT, 74, 394,
FONT_NEXT, 188, 460,
FONT_NEXT, 97, 386,
FONT_END, 105, 368,
FONT_BEGIN, 105, 368,
FONT_NEXT, 188, 460,
FONT_END, 184, -84,
FONT_BEGIN, 105, 368,
FONT_NEXT, 184, -84,
FONT_NEXT, 109, 334,
FONT_NEXT, 156, -153,
FONT_END, 133, -180,
FONT_BEGIN, 109, 334,
FONT_NEXT, 133, -180,
FONT_NEXT, 109, -45,
FONT_END, 108, -102,
FONT_BEGIN, 108, -102,
FONT_NEXT, 133, -180,
FONT_END, 103, -201,
FONT_BEGIN, 108, -102,
FONT_NEXT, 103, -201,
FONT_NEXT, 102, -146,
FONT_NEXT, 67, -214,
FONT_NEXT, 87, -174,
FONT_END, 59, -184,
FONT_BEGIN, 59, -184,
FONT_NEXT, 67, -214,
FONT_END, 23, -218,
FONT_BEGIN, 59, -184,
FONT_NEXT, 23, -218,
FONT_NEXT, -30, -124,
FONT_NEXT, -12, -215,
FONT_END, -42, -204,
FONT_BEGIN, -30, -124,
FONT_NEXT, -42, -204,
FONT_NEXT, -58, -135,
FONT_NEXT, -63, -187,
FONT_END, -70, -162,
FONT_BEGIN, 193, 457,
FONT_NEXT, 193, 0,
FONT_NEXT, 188, 460,
FONT_END, 184, -84,
FONT_BEGIN, 194, 632,
FONT_NEXT, 190, 611,
FONT_NEXT, 179, 668,
FONT_NEXT, 179, 595,
FONT_NEXT, 162, 678,
FONT_NEXT, 162, 584,
FONT_NEXT, 142, 683,
FONT_NEXT, 142, 581,
FONT_NEXT, 119, 677,
FONT_NEXT, 119, 586,
FONT_NEXT, 103, 664,
FONT_NEXT, 103, 599,
FONT_END, 92, 632,
FONT_ADVANCE, 278, 0
    },
    {
107,
FONT_BEGIN, 166, 681,
FONT_NEXT, 166, 265,
FONT_NEXT, 162, 683,
FONT_NEXT, 166, 248,
FONT_END, 166, 67,
FONT_BEGIN, 162, 683,
FONT_NEXT, 166, 67,
FONT_END, 79, 50,
FONT_BEGIN, 162, 683,
FONT_NEXT, 79, 50,
FONT_END, 82, 82,
FONT_BEGIN, 162, 683,
FONT_NEXT, 82, 82,
FONT_END, 82, 564,
FONT_BEGIN, 162, 683,
FONT_NEXT, 82, 564,
FONT_END, 80, 592,
FONT_BEGIN, 162, 683,
FONT_NEXT, 80, 592,
FONT_END, 74, 611,
FONT_BEGIN, 162, 683,
FONT_NEXT, 74, 611,
FONT_END, 60, 621,
FONT_BEGIN, 162, 683,
FONT_NEXT, 60, 621,
FONT_NEXT, 7, 639,
FONT_NEXT, 37, 625,
FONT_END, 7, 623,
FONT_BEGIN, 276, 436,
FONT_NEXT, 276, 450,
FONT_NEXT, 301, 434,
FONT_END, 316, 430,
FONT_BEGIN, 316, 430,
FONT_NEXT, 276, 450,
FONT_END, 377, 404,
FONT_BEGIN, 316, 430,
FONT_NEXT, 377, 404,
FONT_NEXT, 326, 417,
FONT_NEXT, 347, 381,
FONT_END, 309, 350,
FONT_BEGIN, 326, 417,
FONT_NEXT, 309, 350,
FONT_NEXT, 320, 401,
FONT_END, 303, 383,
FONT_BEGIN, 303, 383,
FONT_NEXT, 309, 350,
FONT_END, 264, 309,
FONT_BEGIN, 303, 383,
FONT_NEXT, 264, 309,
FONT_NEXT, 168, 263,
FONT_NEXT, 235, 282,
FONT_END, 168, 248,
FONT_BEGIN, 168, 263,
FONT_NEXT, 168, 248,
FONT_NEXT, 166, 265,
FONT_END, 166, 248,
FONT_BEGIN, 480, 450,
FONT_NEXT, 480, 435,
FONT_NEXT, 276, 450,
FONT_NEXT, 442, 433,
FONT_END, 402, 419,
FONT_BEGIN, 276, 450,
FONT_NEXT, 402, 419,
FONT_END, 377, 404,
FONT_BEGIN, 505, 15,
FONT_NEXT, 505, 0,
FONT_NEXT, 482, 18,
FONT_NEXT, 287, 0,
FONT_NEXT, 455, 28,
FONT_END, 424, 49,
FONT_BEGIN, 424, 49,
FONT_NEXT, 287, 0,
FONT_END, 319, 18,
FONT_BEGIN, 424, 49,
FONT_NEXT, 319, 18,
FONT_NEXT, 388, 88,
FONT_NEXT, 327, 31,
FONT_NEXT, 235, 282,
FONT_NEXT, 321, 43,
FONT_END, 306, 64,
FONT_BEGIN, 235, 282,
FONT_NEXT, 306, 64,
FONT_END, 168, 248,
FONT_BEGIN, 287, 15,
FONT_NEXT, 306, 15,
FONT_NEXT, 287, 0,
FONT_END, 319, 18,
FONT_BEGIN, 241, 15,
FONT_NEXT, 241, 0,
FONT_NEXT, 221, 16,
FONT_NEXT, 7, 0,
FONT_NEXT, 181, 26,
FONT_END, 169, 42,
FONT_BEGIN, 169, 42,
FONT_NEXT, 7, 0,
FONT_NEXT, 166, 67,
FONT_NEXT, 69, 32,
FONT_END, 79, 50,
FONT_BEGIN, 7, 15,
FONT_NEXT, 46, 22,
FONT_NEXT, 7, 0,
FONT_END, 69, 32,
FONT_ADVANCE, 500, 0
    },
    {
108,
FONT_BEGIN, 182, 681,
FONT_NEXT, 182, 84,
FONT_NEXT, 178, 683,
FONT_NEXT, 94, 53,
FONT_END, 98, 87,
FONT_BEGIN, 178, 683,
FONT_NEXT, 98, 87,
FONT_END, 98, 564,
FONT_BEGIN, 178, 683,
FONT_NEXT, 98, 564,
FONT_END, 96, 592,
FONT_BEGIN, 178, 683,
FONT_NEXT, 96, 592,
FONT_END, 90, 611,
FONT_BEGIN, 178, 683,
FONT_NEXT, 90, 611,
FONT_END, 76, 621,
FONT_BEGIN, 178, 683,
FONT_NEXT, 76, 621,
FONT_NEXT, 19, 639,
FONT_NEXT, 53, 625,
FONT_END, 19, 623,
FONT_BEGIN, 257, 15,
FONT_NEXT, 257, 0,
FONT_NEXT, 220, 18,
FONT_NEXT, 21, 0,
FONT_NEXT, 197, 28,
FONT_END, 185, 49,
FONT_BEGIN, 185, 49,
FONT_NEXT, 21, 0,
FONT_END, 81, 31,
FONT_BEGIN, 185, 49,
FONT_NEXT, 81, 31,
FONT_NEXT, 182, 84,
FONT_END, 94, 53,
FONT_BEGIN, 21, 15,
FONT_NEXT, 57, 20,
FONT_NEXT, 21, 0,
FONT_END, 81, 31,
FONT_ADVANCE, 278, 0
    },
    {
109,
FONT_BEGIN, 166, 458,
FONT_NEXT, 166, 383,
FONT_NEXT, 159, 460,
FONT_NEXT, 80, 45,
FONT_END, 86, 85,
FONT_BEGIN, 159, 460,
FONT_NEXT, 86, 85,
FONT_END, 86, 338,
FONT_BEGIN, 159, 460,
FONT_NEXT, 86, 338,
FONT_END, 82, 374,
FONT_BEGIN, 159, 460,
FONT_NEXT, 82, 374,
FONT_END, 74, 393,
FONT_BEGIN, 159, 460,
FONT_NEXT, 74, 393,
FONT_NEXT, 19, 415,
FONT_NEXT, 51, 402,
FONT_END, 19, 398,
FONT_BEGIN, 706, 282,
FONT_NEXT, 706, 76,
FONT_NEXT, 702, 341,
FONT_NEXT, 618, 49,
FONT_NEXT, 686, 398,
FONT_NEXT, 622, 87,
FONT_NEXT, 650, 442,
FONT_END, 622, 455,
FONT_BEGIN, 622, 455,
FONT_NEXT, 622, 87,
FONT_END, 622, 298,
FONT_BEGIN, 622, 455,
FONT_NEXT, 622, 298,
FONT_END, 612, 372,
FONT_BEGIN, 622, 455,
FONT_NEXT, 612, 372,
FONT_NEXT, 588, 460,
FONT_NEXT, 590, 397,
FONT_END, 549, 408,
FONT_BEGIN, 588, 460,
FONT_NEXT, 549, 408,
FONT_NEXT, 532, 450,
FONT_NEXT, 511, 403,
FONT_NEXT, 485, 427,
FONT_NEXT, 481, 390,
FONT_NEXT, 449, 399,
FONT_NEXT, 438, 347,
FONT_NEXT, 427, 376,
FONT_NEXT, 438, 95,
FONT_END, 349, 47,
FONT_BEGIN, 427, 376,
FONT_NEXT, 349, 47,
FONT_NEXT, 414, 406,
FONT_NEXT, 354, 86,
FONT_NEXT, 393, 433,
FONT_END, 362, 452,
FONT_BEGIN, 362, 452,
FONT_NEXT, 354, 86,
FONT_END, 354, 303,
FONT_BEGIN, 362, 452,
FONT_NEXT, 354, 303,
FONT_END, 350, 347,
FONT_BEGIN, 362, 452,
FONT_NEXT, 350, 347,
FONT_NEXT, 320, 460,
FONT_NEXT, 339, 380,
FONT_END, 317, 400,
FONT_BEGIN, 320, 460,
FONT_NEXT, 317, 400,
FONT_NEXT, 281, 454,
FONT_NEXT, 285, 408,
FONT_END, 237, 399,
FONT_BEGIN, 281, 454,
FONT_NEXT, 237, 399,
FONT_NEXT, 244, 438,
FONT_END, 166, 383,
FONT_BEGIN, 166, 383,
FONT_NEXT, 237, 399,
FONT_END, 201, 380,
FONT_BEGIN, 166, 383,
FONT_NEXT, 201, 380,
FONT_END, 178, 360,
FONT_BEGIN, 166, 383,
FONT_NEXT, 178, 360,
FONT_END, 170, 349,
FONT_BEGIN, 166, 383,
FONT_NEXT, 170, 349,
FONT_END, 170, 67,
FONT_BEGIN, 166, 383,
FONT_NEXT, 170, 67,
FONT_NEXT, 80, 45,
FONT_END, 66, 25,
FONT_BEGIN, 66, 25,
FONT_NEXT, 170, 67,
FONT_END, 16, 0,
FONT_BEGIN, 66, 25,
FONT_NEXT, 16, 0,
FONT_NEXT, 43, 16,
FONT_END, 16, 15,
FONT_BEGIN, 775, 15,
FONT_NEXT, 775, 0,
FONT_NEXT, 749, 17,
FONT_NEXT, 556, 0,
FONT_NEXT, 723, 26,
FONT_END, 711, 40,
FONT_BEGIN, 711, 40,
FONT_NEXT, 556, 0,
FONT_NEXT, 706, 76,
FONT_NEXT, 607, 29,
FONT_END, 618, 49,
FONT_BEGIN, 556, 15,
FONT_NEXT, 587, 19,
FONT_NEXT, 556, 0,
FONT_END, 607, 29,
FONT_BEGIN, 510, 15,
FONT_NEXT, 510, 0,
FONT_NEXT, 481, 17,
FONT_NEXT, 286, 0,
FONT_NEXT, 458, 26,
FONT_END, 443, 50,
FONT_BEGIN, 443, 50,
FONT_NEXT, 286, 0,
FONT_END, 336, 27,
FONT_BEGIN, 443, 50,
FONT_NEXT, 336, 27,
FONT_NEXT, 438, 95,
FONT_END, 349, 47,
FONT_BEGIN, 286, 15,
FONT_NEXT, 315, 18,
FONT_NEXT, 286, 0,
FONT_END, 336, 27,
FONT_BEGIN, 238, 15,
FONT_NEXT, 238, 0,
FONT_NEXT, 190, 23,
FONT_NEXT, 16, 0,
FONT_NEXT, 175, 38,
FONT_END, 170, 67,
FONT_ADVANCE, 778, 0
    },
    {
110,
FONT_BEGIN, 161, 458,
FONT_NEXT, 161, 379,
FONT_NEXT, 154, 460,
FONT_NEXT, 76, 53,
FONT_END, 80, 90,
FONT_BEGIN, 154, 460,
FONT_NEXT, 80, 90,
FONT_END, 80, 338,
FONT_BEGIN, 154, 460,
FONT_NEXT, 80, 338,
FONT_END, 76, 374,
FONT_BEGIN, 154, 460,
FONT_NEXT, 76, 374,
FONT_END, 68, 393,
FONT_BEGIN, 154, 460,
FONT_NEXT, 68, 393,
FONT_NEXT, 16, 415,
FONT_NEXT, 45, 402,
FONT_END, 16, 398,
FONT_BEGIN, 424, 310,
FONT_NEXT, 424, 81,
FONT_NEXT, 420, 351,
FONT_NEXT, 335, 54,
FONT_NEXT, 412, 385,
FONT_END, 382, 431,
FONT_BEGIN, 382, 431,
FONT_NEXT, 335, 54,
FONT_END, 340, 99,
FONT_BEGIN, 382, 431,
FONT_NEXT, 340, 99,
FONT_NEXT, 344, 453,
FONT_NEXT, 340, 308,
FONT_END, 336, 348,
FONT_BEGIN, 344, 453,
FONT_NEXT, 336, 348,
FONT_NEXT, 307, 460,
FONT_NEXT, 323, 379,
FONT_END, 299, 398,
FONT_BEGIN, 307, 460,

FONT_NEXT, 299, 398,
FONT_NEXT, 266, 453,
FONT_NEXT, 263, 405,
FONT_NEXT, 230, 436,
FONT_NEXT, 216, 390,
FONT_NEXT, 161, 379,
FONT_NEXT, 164, 348,
FONT_END, 164, 67,
FONT_BEGIN, 161, 379,
FONT_NEXT, 164, 67,
FONT_NEXT, 76, 53,
FONT_END, 66, 31,
FONT_BEGIN, 66, 31,
FONT_NEXT, 164, 67,
FONT_END, 18, 0,
FONT_BEGIN, 66, 31,
FONT_NEXT, 18, 0,
FONT_NEXT, 47, 19,
FONT_END, 18, 15,
FONT_BEGIN, 485, 15,
FONT_NEXT, 485, 0,
FONT_NEXT, 454, 20,
FONT_NEXT, 277, 0,
FONT_NEXT, 436, 31,
FONT_END, 426, 51,
FONT_BEGIN, 426, 51,
FONT_NEXT, 277, 0,
FONT_NEXT, 424, 81,
FONT_NEXT, 322, 29,
FONT_END, 335, 54,
FONT_BEGIN, 277, 15,
FONT_NEXT, 302, 18,
FONT_NEXT, 277, 0,
FONT_END, 322, 29,
FONT_BEGIN, 230, 15,
FONT_NEXT, 230, 0,
FONT_NEXT, 197, 19,
FONT_NEXT, 18, 0,
FONT_NEXT, 177, 29,
FONT_END, 167, 45,
FONT_BEGIN, 167, 45,
FONT_NEXT, 18, 0,
FONT_END, 164, 67,
FONT_BEGIN, 167, 45,
FONT_END, 164, 67,
FONT_ADVANCE, 500, 0
    },
    {
111,
FONT_BEGIN, 470, 228,
FONT_NEXT, 456, 149,
FONT_NEXT, 465, 279,
FONT_END, 453, 325,
FONT_BEGIN, 453, 325,
FONT_NEXT, 456, 149,
FONT_END, 416, 71,
FONT_BEGIN, 453, 325,
FONT_NEXT, 416, 71,
FONT_NEXT, 433, 365,
FONT_END, 406, 398,
FONT_BEGIN, 406, 398,
FONT_NEXT, 416, 71,
FONT_END, 385, 39,
FONT_BEGIN, 406, 398,
FONT_NEXT, 385, 39,
FONT_NEXT, 373, 424,
FONT_NEXT, 380, 199,
FONT_END, 370, 287,
FONT_BEGIN, 373, 424,
FONT_NEXT, 370, 287,
FONT_NEXT, 335, 444,
FONT_NEXT, 343, 362,
FONT_END, 322, 390,
FONT_BEGIN, 335, 444,
FONT_NEXT, 322, 390,
FONT_NEXT, 292, 455,
FONT_NEXT, 298, 413,
FONT_END, 269, 427,
FONT_BEGIN, 292, 455,
FONT_NEXT, 269, 427,
FONT_NEXT, 245, 460,
FONT_NEXT, 237, 432,
FONT_NEXT, 197, 455,
FONT_NEXT, 191, 422,
FONT_NEXT, 154, 441,
FONT_NEXT, 154, 393,
FONT_NEXT, 117, 419,
FONT_NEXT, 128, 344,
FONT_END, 119, 276,
FONT_BEGIN, 117, 419,
FONT_NEXT, 119, 276,
FONT_END, 90, 58,
FONT_BEGIN, 117, 419,
FONT_NEXT, 90, 58,
FONT_NEXT, 86, 390,
FONT_NEXT, 64, 93,
FONT_NEXT, 62, 355,
FONT_NEXT, 45, 133,
FONT_NEXT, 43, 316,
FONT_NEXT, 33, 179,
FONT_END, 29, 228,
FONT_BEGIN, 90, 58,
FONT_NEXT, 119, 276,
FONT_NEXT, 122, 29,
FONT_NEXT, 124, 204,
FONT_END, 145, 119,
FONT_BEGIN, 122, 29,
FONT_NEXT, 145, 119,
FONT_NEXT, 159, 8,
FONT_NEXT, 164, 80,
FONT_END, 189, 47,
FONT_BEGIN, 159, 8,
FONT_NEXT, 189, 47,
FONT_NEXT, 201, -6,
FONT_NEXT, 221, 26,
FONT_NEXT, 248, -10,
FONT_NEXT, 262, 18,
FONT_NEXT, 301, -4,
FONT_NEXT, 301, 26,
FONT_END, 340, 54,
FONT_BEGIN, 301, -4,
FONT_NEXT, 340, 54,
FONT_NEXT, 346, 13,
FONT_NEXT, 356, 78,
FONT_END, 368, 110,
FONT_BEGIN, 346, 13,
FONT_NEXT, 368, 110,
FONT_NEXT, 385, 39,
FONT_NEXT, 377, 150,
FONT_END, 380, 199,
FONT_ADVANCE, 500, 0
    },
    {
112,
FONT_BEGIN, 159, 458,
FONT_NEXT, 159, 385,
FONT_NEXT, 153, 460,
FONT_NEXT, 159, 334,
FONT_END, 159, 88,
FONT_BEGIN, 153, 460,
FONT_NEXT, 159, 88,
FONT_END, 159, 33,
FONT_BEGIN, 153, 460,
FONT_NEXT, 159, 33,
FONT_END, 159, -124,
FONT_BEGIN, 153, 460,
FONT_NEXT, 159, -124,
FONT_END, 73, -160,
FONT_BEGIN, 153, 460,
FONT_NEXT, 73, -160,
FONT_END, 75, -131,
FONT_BEGIN, 153, 460,
FONT_NEXT, 75, -131,
FONT_END, 75, 337,
FONT_BEGIN, 153, 460,
FONT_NEXT, 75, 337,
FONT_END, 73, 362,
FONT_BEGIN, 153, 460,
FONT_NEXT, 73, 362,
FONT_END, 69, 380,
FONT_BEGIN, 153, 460,
FONT_NEXT, 69, 380,
FONT_END, 57, 390,
FONT_BEGIN, 153, 460,
FONT_NEXT, 57, 390,
FONT_NEXT, 9, 409,
FONT_NEXT, 37, 394,
FONT_END, 9, 393,
FONT_BEGIN, 470, 245,
FONT_NEXT, 456, 156,
FONT_NEXT, 466, 292,
FONT_END, 456, 334,
FONT_BEGIN, 456, 334,
FONT_NEXT, 456, 156,
FONT_NEXT, 420, 402,
FONT_NEXT, 417, 74,
FONT_NEXT, 368, 445,
FONT_NEXT, 384, 215,
FONT_END, 383, 229,
FONT_BEGIN, 368, 445,
FONT_NEXT, 383, 229,
FONT_END, 381, 253,
FONT_BEGIN, 368, 445,
FONT_NEXT, 381, 253,
FONT_END, 366, 316,
FONT_BEGIN, 368, 445,
FONT_NEXT, 366, 316,
FONT_NEXT, 305, 460,
FONT_NEXT, 350, 347,
FONT_END, 328, 374,
FONT_BEGIN, 305, 460,
FONT_NEXT, 328, 374,
FONT_END, 297, 392,
FONT_BEGIN, 305, 460,
FONT_NEXT, 297, 392,
FONT_NEXT, 252, 451,
FONT_NEXT, 257, 400,
FONT_END, 227, 395,
FONT_BEGIN, 252, 451,
FONT_NEXT, 227, 395,
FONT_NEXT, 212, 432,
FONT_NEXT, 196, 381,
FONT_NEXT, 181, 406,
FONT_NEXT, 171, 360,
FONT_NEXT, 161, 383,
FONT_NEXT, 159, 334,
FONT_END, 159, 385,
FONT_BEGIN, 159, 88,
FONT_NEXT, 167, 67,
FONT_NEXT, 159, 33,
FONT_NEXT, 189, 46,
FONT_NEXT, 206, -1,
FONT_NEXT, 221, 29,
FONT_NEXT, 260, -10,
FONT_NEXT, 261, 22,
FONT_END, 294, 27,
FONT_BEGIN, 260, -10,
FONT_NEXT, 294, 27,
FONT_NEXT, 308, -4,
FONT_NEXT, 334, 53,
FONT_NEXT, 351, 13,
FONT_NEXT, 353, 77,
FONT_END, 369, 111,
FONT_BEGIN, 351, 13,
FONT_NEXT, 369, 111,
FONT_END, 380, 156,
FONT_BEGIN, 351, 13,
FONT_NEXT, 380, 156,
FONT_NEXT, 387, 40,
FONT_NEXT, 384, 215,
FONT_END, 417, 74,
FONT_BEGIN, 5, -200,
FONT_NEXT, 43, -194,
FONT_NEXT, 5, -217,
FONT_END, 159, -124,
FONT_BEGIN, 159, -124,
FONT_NEXT, 43, -194,
FONT_END, 64, -180,
FONT_BEGIN, 159, -124,
FONT_NEXT, 64, -180,
FONT_END, 73, -160,
FONT_BEGIN, 247, -199,
FONT_NEXT, 247, -217,
FONT_NEXT, 202, -197,
FONT_NEXT, 5, -217,
FONT_NEXT, 176, -186,
FONT_END, 162, -163,
FONT_BEGIN, 162, -163,
FONT_NEXT, 5, -217,
FONT_END, 159, -124,
FONT_BEGIN, 162, -163,
FONT_END, 159, -124,
FONT_ADVANCE, 500, 0
    },
    {
113,
FONT_BEGIN, 110, 238,
FONT_NEXT, 117, 169,
FONT_NEXT, 72, 44,
FONT_END, 124, 3,
FONT_BEGIN, 124, 3,
FONT_NEXT, 117, 169,
FONT_END, 140, 110,
FONT_BEGIN, 124, 3,
FONT_NEXT, 140, 110,
FONT_END, 180, 67,
FONT_BEGIN, 124, 3,
FONT_NEXT, 180, 67,
FONT_NEXT, 188, -10,
FONT_NEXT, 206, 55,
FONT_NEXT, 234, -3,
FONT_NEXT, 238, 51,
FONT_NEXT, 278, 14,
FONT_NEXT, 288, 60,
FONT_NEXT, 314, 38,
FONT_NEXT, 318, 73,
FONT_END, 333, 88,
FONT_BEGIN, 314, 38,
FONT_NEXT, 333, 88,
FONT_NEXT, 336, 62,
FONT_END, 341, 62,
FONT_BEGIN, 341, 62,
FONT_NEXT, 333, 88,
FONT_END, 341, 127,
FONT_BEGIN, 341, 62,
FONT_NEXT, 341, 127,
FONT_NEXT, 341, -124,
FONT_NEXT, 341, 333,
FONT_NEXT, 360, 425,
FONT_NEXT, 339, 360,
FONT_END, 328, 393,
FONT_BEGIN, 360, 425,
FONT_NEXT, 328, 393,
FONT_NEXT, 308, 450,
FONT_NEXT, 300, 420,
FONT_NEXT, 247, 460,
FONT_NEXT, 276, 428,
FONT_END, 245, 432,
FONT_BEGIN, 247, 460,
FONT_NEXT, 245, 432,
FONT_NEXT, 201, 455,
FONT_NEXT, 189, 418,
FONT_NEXT, 159, 440,
FONT_NEXT, 147, 380,
FONT_NEXT, 121, 417,
FONT_NEXT, 119, 319,
FONT_NEXT, 88, 387,
FONT_NEXT, 110, 238,
FONT_END, 72, 44,
FONT_BEGIN, 88, 387,
FONT_NEXT, 72, 44,
FONT_NEXT, 61, 351,
FONT_NEXT, 52, 75,
FONT_NEXT, 41, 308,
FONT_NEXT, 37, 113,
FONT_NEXT, 28, 262,
FONT_NEXT, 27, 159,
FONT_END, 24, 212,
FONT_BEGIN, 425, 456,
FONT_NEXT, 425, -141,
FONT_NEXT, 421, 460,
FONT_NEXT, 337, -157,
FONT_NEXT, 414, 457,
FONT_NEXT, 341, -124,
FONT_END, 360, 425,
FONT_BEGIN, 252, -200,
FONT_NEXT, 297, -194,
FONT_NEXT, 252, -217,
FONT_END, 425, -141,
FONT_BEGIN, 425, -141,
FONT_NEXT, 297, -194,
FONT_END, 324, -180,
FONT_BEGIN, 425, -141,
FONT_NEXT, 324, -180,
FONT_END, 337, -157,
FONT_BEGIN, 488, -203,
FONT_NEXT, 488, -217,
FONT_NEXT, 459, -196,
FONT_NEXT, 252, -217,
FONT_NEXT, 439, -187,
FONT_END, 428, -170,
FONT_BEGIN, 428, -170,
FONT_NEXT, 252, -217,
FONT_END, 425, -141,
FONT_BEGIN, 428, -170,
FONT_END, 425, -141,
FONT_ADVANCE, 500, 0
    },
    {
114,
FONT_BEGIN, 160, 458,
FONT_NEXT, 160, 369,
FONT_NEXT, 155, 460,
FONT_NEXT, 160, 315,
FONT_END, 160, 90,
FONT_BEGIN, 155, 460,
FONT_NEXT, 160, 90,
FONT_END, 72, 51,
FONT_BEGIN, 155, 460,
FONT_NEXT, 72, 51,
FONT_END, 76, 84,
FONT_BEGIN, 155, 460,
FONT_NEXT, 76, 84,
FONT_END, 76, 334,
FONT_BEGIN, 155, 460,
FONT_NEXT, 76, 334,
FONT_END, 72, 368,
FONT_BEGIN, 155, 460,
FONT_NEXT, 72, 368,
FONT_END, 64, 386,
FONT_BEGIN, 155, 460,
FONT_NEXT, 64, 386,
FONT_NEXT, 7, 406,
FONT_NEXT, 41, 394,
FONT_END, 7, 390,
FONT_BEGIN, 335, 406,
FONT_NEXT, 325, 374,
FONT_NEXT, 331, 428,
FONT_END, 320, 445,
FONT_BEGIN, 320, 445,
FONT_NEXT, 325, 374,
FONT_END, 297, 362,
FONT_BEGIN, 320, 445,
FONT_NEXT, 297, 362,
FONT_NEXT, 303, 456,
FONT_END, 281, 460,
FONT_BEGIN, 281, 460,
FONT_NEXT, 297, 362,
FONT_END, 274, 367,
FONT_BEGIN, 281, 460,
FONT_NEXT, 274, 367,
FONT_NEXT, 252, 455,
FONT_NEXT, 258, 379,
FONT_END, 228, 397,
FONT_BEGIN, 252, 455,
FONT_NEXT, 228, 397,
FONT_NEXT, 224, 441,
FONT_NEXT, 207, 388,
FONT_NEXT, 195, 413,
FONT_NEXT, 185, 368,
FONT_NEXT, 162, 369,
FONT_NEXT, 167, 341,
FONT_END, 160, 315,
FONT_BEGIN, 162, 369,
FONT_NEXT, 160, 315,
FONT_END, 160, 369,
FONT_BEGIN, 245, 15,
FONT_NEXT, 245, 0,
FONT_NEXT, 210, 17,
FONT_NEXT, 5, 0,
FONT_NEXT, 183, 27,
FONT_END, 166, 49,
FONT_BEGIN, 166, 49,
FONT_NEXT, 5, 0,
FONT_END, 61, 32,
FONT_BEGIN, 166, 49,
FONT_NEXT, 61, 32,
FONT_NEXT, 160, 90,
FONT_END, 72, 51,
FONT_BEGIN, 5, 15,
FONT_NEXT, 39, 22,
FONT_NEXT, 5, 0,
FONT_END, 61, 32,
FONT_ADVANCE, 333, 0
    },
    {
115,
FONT_BEGIN, 51, 338,
FONT_NEXT, 59, 380,
FONT_NEXT, 60, 288,
FONT_NEXT, 84, 420,
FONT_NEXT, 86, 250,
FONT_NEXT, 113, 372,
FONT_END, 120, 337,
FONT_BEGIN, 86, 250,
FONT_NEXT, 120, 337,
FONT_NEXT, 123, 219,
FONT_NEXT, 133, 318,
FONT_END, 156, 301,
FONT_BEGIN, 123, 219,
FONT_NEXT, 156, 301,
FONT_NEXT, 168, 192,
FONT_END, 226, 159,
FONT_BEGIN, 226, 159,
FONT_NEXT, 156, 301,
FONT_NEXT, 264, 127,
FONT_NEXT, 264, 237,
FONT_NEXT, 278, 85,
FONT_NEXT, 299, 21,
FONT_NEXT, 270, 49,
FONT_NEXT, 252, -3,
FONT_NEXT, 250, 27,
FONT_NEXT, 203, -10,
FONT_NEXT, 224, 15,
FONT_END, 197, 12,
FONT_BEGIN, 197, 12,
FONT_NEXT, 203, -10,
FONT_END, 138, -1,
FONT_BEGIN, 197, 12,
FONT_NEXT, 138, -1,
FONT_NEXT, 163, 15,
FONT_END, 137, 26,
FONT_BEGIN, 137, 26,
FONT_NEXT, 138, -1,
FONT_END, 87, 8,
FONT_BEGIN, 137, 26,
FONT_NEXT, 87, 8,
FONT_NEXT, 100, 62,
FONT_END, 79, 107,
FONT_BEGIN, 79, 107,
FONT_NEXT, 87, 8,
FONT_END, 73, 5,
FONT_BEGIN, 79, 107,
FONT_NEXT, 73, 5,
FONT_NEXT, 68, 152,
FONT_NEXT, 65, -4,
FONT_NEXT, 52, 152,
FONT_END, 52, -4,
FONT_BEGIN, 315, 314,
FONT_NEXT, 300, 314,
FONT_NEXT, 311, 451,
FONT_END, 300, 451,
FONT_BEGIN, 300, 451,
FONT_NEXT, 300, 314,
FONT_NEXT, 283, 440,
FONT_NEXT, 279, 376,
FONT_NEXT, 247, 450,
FONT_NEXT, 252, 413,
FONT_END, 221, 432,
FONT_BEGIN, 247, 450,
FONT_NEXT, 221, 432,
FONT_NEXT, 191, 460,
FONT_NEXT, 189, 437,
FONT_NEXT, 128, 448,
FONT_NEXT, 153, 430,
FONT_END, 130, 414,
FONT_BEGIN, 128, 448,
FONT_NEXT, 130, 414,
FONT_END, 117, 393,
FONT_BEGIN, 128, 448,
FONT_NEXT, 117, 393,
FONT_NEXT, 84, 420,
FONT_END, 113, 372,
FONT_BEGIN, 348, 119,
FONT_NEXT, 334, 62,
FONT_NEXT, 341, 158,
FONT_END, 324, 189,
FONT_BEGIN, 324, 189,
FONT_NEXT, 334, 62,
FONT_END, 299, 21,
FONT_BEGIN, 324, 189,
FONT_NEXT, 299, 21,
FONT_END, 264, 237,
FONT_ADVANCE, 389, 0
    },
    {
116,
FONT_BEGIN, 13, 425,
FONT_NEXT, 26, 441,
FONT_NEXT, 17, 418,
FONT_NEXT, 57, 465,
FONT_NEXT, 70, 418,
FONT_NEXT, 102, 516,
FONT_END, 96, 16,
FONT_BEGIN, 70, 418,
FONT_NEXT, 96, 16,
FONT_END, 77, 54,
FONT_BEGIN, 70, 418,
FONT_NEXT, 77, 54,
FONT_END, 70, 117,
FONT_BEGIN, 154, 566,
FONT_NEXT, 154, 450,
FONT_NEXT, 147, 579,
FONT_NEXT, 154, 418,
FONT_END, 154, 132,
FONT_BEGIN, 147, 579,
FONT_NEXT, 154, 132,
FONT_END, 126, -5,
FONT_BEGIN, 147, 579,
FONT_NEXT, 126, -5,
FONT_NEXT, 122, 544,
FONT_NEXT, 96, 16,
FONT_END, 102, 516,
FONT_BEGIN, 255, 450,
FONT_NEXT, 255, 418,
FONT_NEXT, 154, 450,
FONT_END, 154, 418,
FONT_BEGIN, 279, 66,
FONT_NEXT, 257, 35,
FONT_NEXT, 266, 77,
FONT_END, 243, 54,
FONT_BEGIN, 243, 54,
FONT_NEXT, 257, 35,
FONT_END, 228, 11,
FONT_BEGIN, 243, 54,
FONT_NEXT, 228, 11,
FONT_NEXT, 205, 42,
FONT_NEXT, 196, -5,
FONT_NEXT, 175, 51,
FONT_NEXT, 162, -10,
FONT_NEXT, 160, 75,
FONT_NEXT, 126, -5,
FONT_END, 154, 132,
FONT_ADVANCE, 278, 0
    },
    {
117,
FONT_BEGIN, 9, 436,
FONT_NEXT, 9, 450,
FONT_NEXT, 51, 425,
FONT_NEXT, 155, 450,
FONT_NEXT, 65, 407,
FONT_NEXT, 155, 124,
FONT_END, 154, -5,
FONT_BEGIN, 65, 407,
FONT_NEXT, 154, -5,
FONT_NEXT, 71, 372,
FONT_NEXT, 115, 15,
FONT_END, 83, 54,
FONT_BEGIN, 71, 372,
FONT_NEXT, 83, 54,
FONT_END, 74, 83,
FONT_BEGIN, 71, 372,
FONT_NEXT, 74, 83,
FONT_END, 71, 120,
FONT_BEGIN, 338, -7,
FONT_NEXT, 338, 76,
FONT_NEXT, 340, -9,
FONT_END, 333, 370,
FONT_BEGIN, 333, 370,
FONT_NEXT, 338, 76,
FONT_NEXT, 333, 135,
FONT_END, 328, 100,
FONT_BEGIN, 328, 100,
FONT_NEXT, 338, 76,
FONT_END, 295, 33,
FONT_BEGIN, 328, 100,
FONT_NEXT, 295, 33,
FONT_NEXT, 317, 84,
FONT_END, 274, 57,
FONT_BEGIN, 274, 57,
FONT_NEXT, 295, 33,
FONT_END, 264, 9,
FONT_BEGIN, 274, 57,
FONT_NEXT, 264, 9,
FONT_NEXT, 230, 48,
FONT_NEXT, 234, -4,
FONT_END, 208, -9,
FONT_BEGIN, 230, 48,
FONT_NEXT, 208, -9,
FONT_NEXT, 190, 57,
FONT_NEXT, 190, -10,
FONT_NEXT, 167, 80,
FONT_NEXT, 154, -5,
FONT_NEXT, 157, 106,
FONT_END, 155, 124,
FONT_BEGIN, 479, 50,
FONT_NEXT, 479, 36,
FONT_NEXT, 444, 53,
FONT_NEXT, 340, -9,
FONT_NEXT, 425, 64,
FONT_END, 418, 82,
FONT_BEGIN, 418, 82,
FONT_NEXT, 340, -9,
FONT_NEXT, 417, 107,
FONT_NEXT, 333, 370,
FONT_END, 331, 396,
FONT_BEGIN, 417, 107,
FONT_NEXT, 331, 396,
FONT_NEXT, 417, 450,
FONT_NEXT, 322, 415,
FONT_NEXT, 259, 450,
FONT_NEXT, 300, 427,
FONT_END, 259, 433,
FONT_ADVANCE, 500, 0
    },
    {
118,
FONT_BEGIN, 215, 450,
FONT_NEXT, 215, 435,
FONT_NEXT, 19, 450,
FONT_NEXT, 183, 428,
FONT_END, 172, 419,
FONT_BEGIN, 19, 450,
FONT_NEXT, 172, 419,
FONT_END, 169, 405,
FONT_BEGIN, 19, 450,
FONT_NEXT, 169, 405,
FONT_END, 48, 427,
FONT_BEGIN, 19, 450,
FONT_NEXT, 48, 427,
FONT_END, 19, 435,
FONT_BEGIN, 338, 435,
FONT_NEXT, 338, 450,
FONT_NEXT, 370, 426,
FONT_NEXT, 427, 393,
FONT_NEXT, 385, 402,
FONT_NEXT, 412, 357,
FONT_NEXT, 381, 383,
FONT_NEXT, 284, 36,
FONT_NEXT, 373, 355,
FONT_END, 345, 280,
FONT_BEGIN, 345, 280,
FONT_NEXT, 284, 36,
FONT_NEXT, 280, 114,
FONT_NEXT, 269, 0,
FONT_NEXT, 178, 370,
FONT_NEXT, 257, -14,
FONT_END, 246, -7,
FONT_BEGIN, 178, 370,
FONT_NEXT, 246, -7,
FONT_END, 239, 7,
FONT_BEGIN, 178, 370,
FONT_NEXT, 239, 7,
FONT_END, 230, 33,
FONT_BEGIN, 178, 370,
FONT_NEXT, 230, 33,
FONT_END, 217, 63,
FONT_BEGIN, 178, 370,
FONT_NEXT, 217, 63,
FONT_END, 200, 106,
FONT_BEGIN, 178, 370,
FONT_NEXT, 200, 106,
FONT_END, 156, 212,
FONT_BEGIN, 178, 370,
FONT_NEXT, 156, 212,
FONT_NEXT, 169, 405,
FONT_NEXT, 110, 319,
FONT_END, 90, 364,
FONT_BEGIN, 169, 405,
FONT_NEXT, 90, 364,
FONT_END, 74, 398,
FONT_BEGIN, 169, 405,
FONT_NEXT, 74, 398,
FONT_END, 48, 427,
FONT_BEGIN, 477, 450,
FONT_NEXT, 477, 435,
FONT_NEXT, 338, 450,
FONT_NEXT, 455, 429,
FONT_END, 440, 417,
FONT_BEGIN, 338, 450,
FONT_NEXT, 440, 417,
FONT_END, 427, 393,
FONT_ADVANCE, 500, 0
    },
    {
119,
FONT_BEGIN, 201, 450,
FONT_NEXT, 201, 435,
FONT_NEXT, 21, 450,
FONT_NEXT, 169, 427,
FONT_END, 158, 417,
FONT_BEGIN, 21, 450,
FONT_NEXT, 158, 417,
FONT_END, 155, 401,
FONT_BEGIN, 21, 450,
FONT_NEXT, 155, 401,
FONT_END, 49, 420,
FONT_BEGIN, 21, 450,
FONT_NEXT, 49, 420,
FONT_END, 21, 435,
FONT_BEGIN, 262, 435,
FONT_NEXT, 262, 450,
FONT_NEXT, 288, 429,
FONT_END, 308, 413,
FONT_BEGIN, 308, 413,
FONT_NEXT, 262, 450,
FONT_END, 407, 398,
FONT_BEGIN, 308, 413,
FONT_NEXT, 407, 398,
FONT_NEXT, 327, 376,
FONT_NEXT, 372, 265,
FONT_NEXT, 338, 347,
FONT_END, 351, 310,
FONT_BEGIN, 351, 310,
FONT_NEXT, 372, 265,
FONT_END, 260, 25,
FONT_BEGIN, 351, 310,
FONT_NEXT, 260, 25,
FONT_NEXT, 260, 111,
FONT_END, 224, 205,
FONT_BEGIN, 224, 205,
FONT_NEXT, 260, 25,
FONT_END, 244, -5,
FONT_BEGIN, 224, 205,
FONT_NEXT, 244, -5,
FONT_END, 235, -14,
FONT_BEGIN, 224, 205,
FONT_NEXT, 235, -14,
FONT_END, 224, -5,
FONT_BEGIN, 224, 205,
FONT_NEXT, 224, -5,
FONT_NEXT, 190, 290,
FONT_NEXT, 217, 8,
FONT_END, 209, 30,
FONT_BEGIN, 190, 290,
FONT_NEXT, 209, 30,
FONT_END, 74, 372,
FONT_BEGIN, 190, 290,
FONT_NEXT, 74, 372,
FONT_NEXT, 164, 358,
FONT_END, 157, 383,
FONT_BEGIN, 157, 383,
FONT_NEXT, 74, 372,
FONT_NEXT, 155, 401,
FONT_NEXT, 60, 401,
FONT_END, 49, 420,
FONT_BEGIN, 465, 450,
FONT_NEXT, 465, 435,
FONT_NEXT, 262, 450,
FONT_NEXT, 423, 425,
FONT_END, 411, 415,
FONT_BEGIN, 262, 450,
FONT_NEXT, 411, 415,
FONT_END, 407, 398,
FONT_BEGIN, 571, 435,
FONT_NEXT, 571, 450,
FONT_NEXT, 602, 425,
FONT_NEXT, 671, 419,
FONT_END, 653, 381,
FONT_BEGIN, 602, 425,
FONT_NEXT, 653, 381,
FONT_NEXT, 611, 416,
FONT_END, 615, 402,
FONT_BEGIN, 615, 402,
FONT_NEXT, 653, 381,
FONT_NEXT, 610, 374,
FONT_NEXT, 515, 35,
FONT_NEXT, 598, 338,
FONT_END, 508, 116,
FONT_BEGIN, 508, 116,
FONT_NEXT, 515, 35,
FONT_END, 505, 10,
FONT_BEGIN, 508, 116,
FONT_NEXT, 505, 10,
FONT_NEXT, 428, 330,
FONT_NEXT, 498, -5,
FONT_END, 487, -14,
FONT_BEGIN, 428, 330,
FONT_NEXT, 487, -14,
FONT_END, 477, -6,
FONT_BEGIN, 428, 330,
FONT_NEXT, 477, -6,
FONT_END, 471, 7,
FONT_BEGIN, 428, 330,
FONT_NEXT, 471, 7,
FONT_END, 463, 29,
FONT_BEGIN, 428, 330,
FONT_NEXT, 463, 29,
FONT_END, 372, 265,
FONT_BEGIN, 428, 330,
FONT_NEXT, 372, 265,
FONT_NEXT, 411, 371,
FONT_END, 407, 398,
FONT_BEGIN, 694, 450,
FONT_NEXT, 694, 435,
FONT_NEXT, 571, 450,
FONT_END, 671, 419,
FONT_ADVANCE, 722, 0
    },
    {
120,
FONT_BEGIN, 24, 435,
FONT_NEXT, 24, 450,
FONT_NEXT, 64, 428,
FONT_NEXT, 188, 412,
FONT_NEXT, 83, 410,
FONT_END, 110, 375,
FONT_BEGIN, 110, 375,
FONT_NEXT, 188, 412,
FONT_END, 194, 391,
FONT_BEGIN, 110, 375,
FONT_NEXT, 194, 391,
FONT_NEXT, 204, 231,
FONT_NEXT, 210, 362,
FONT_END, 221, 197,
FONT_BEGIN, 204, 231,
FONT_NEXT, 221, 197,
FONT_END, 142, 74,
FONT_BEGIN, 204, 231,
FONT_NEXT, 142, 74,
FONT_NEXT, 90, 66,
FONT_NEXT, 122, 33,
FONT_NEXT, 68, 39,
FONT_END, 51, 24,
FONT_BEGIN, 51, 24,
FONT_NEXT, 122, 33,
FONT_END, 17, 0,
FONT_BEGIN, 51, 24,
FONT_NEXT, 17, 0,
FONT_END, 17, 15,
FONT_BEGIN, 231, 450,
FONT_NEXT, 231, 435,
FONT_NEXT, 24, 450,
FONT_NEXT, 202, 431,
FONT_END, 192, 424,
FONT_BEGIN, 24, 450,
FONT_NEXT, 192, 424,
FONT_END, 188, 412,
FONT_BEGIN, 275, 435,
FONT_NEXT, 275, 450,
FONT_NEXT, 301, 431,
FONT_END, 311, 424,
FONT_BEGIN, 311, 424,
FONT_NEXT, 275, 450,
FONT_END, 379, 417,
FONT_BEGIN, 311, 424,
FONT_NEXT, 379, 417,
FONT_END, 352, 391,
FONT_BEGIN, 311, 424,
FONT_NEXT, 352, 391,
FONT_NEXT, 315, 412,
FONT_END, 313, 402,
FONT_BEGIN, 313, 402,
FONT_NEXT, 352, 391,
FONT_END, 269, 271,
FONT_BEGIN, 313, 402,
FONT_NEXT, 269, 271,
FONT_NEXT, 304, 386,
FONT_END, 284, 356,
FONT_BEGIN, 284, 356,
FONT_NEXT, 269, 271,
FONT_NEXT, 248, 304,
FONT_NEXT, 221, 197,
FONT_END, 210, 362,
FONT_BEGIN, 433, 450,
FONT_NEXT, 433, 435,
FONT_NEXT, 275, 450,
FONT_NEXT, 403, 429,
FONT_END, 379, 417,
FONT_BEGIN, 278, 15,
FONT_NEXT, 309, 20,
FONT_NEXT, 278, 0,
FONT_END, 397, 75,
FONT_BEGIN, 397, 75,
FONT_NEXT, 309, 20,
FONT_END, 318, 41,
FONT_BEGIN, 397, 75,
FONT_NEXT, 318, 41,
FONT_NEXT, 269, 271,
FONT_NEXT, 311, 60,
FONT_END, 302, 73,
FONT_BEGIN, 269, 271,
FONT_NEXT, 302, 73,
FONT_END, 221, 197,
FONT_BEGIN, 479, 15,
FONT_NEXT, 479, 0,
FONT_NEXT, 445, 23,
FONT_NEXT, 278, 0,
FONT_NEXT, 423, 41,
FONT_END, 397, 75,
FONT_BEGIN, 162, 15,
FONT_NEXT, 162, 0,
FONT_NEXT, 136, 17,
FONT_NEXT, 17, 0,
FONT_END, 122, 33,
FONT_ADVANCE, 500, 0
    },
    {
121,
FONT_BEGIN, 220, 450,
FONT_NEXT, 220, 435,
FONT_NEXT, 14, 450,
FONT_NEXT, 182, 430,
FONT_END, 167, 421,
FONT_BEGIN, 14, 450,
FONT_NEXT, 167, 421,
FONT_END, 162, 406,
FONT_BEGIN, 14, 450,
FONT_NEXT, 162, 406,
FONT_END, 39, 428,
FONT_BEGIN, 14, 450,
FONT_NEXT, 39, 428,
FONT_END, 14, 436,
FONT_BEGIN, 340, 435,
FONT_NEXT, 340, 450,
FONT_NEXT, 376, 428,
FONT_NEXT, 439, 414,
FONT_END, 427, 390,
FONT_BEGIN, 376, 428,
FONT_NEXT, 427, 390,
FONT_NEXT, 388, 407,
FONT_NEXT, 273, -18,
FONT_NEXT, 380, 379,
FONT_END, 365, 339,
FONT_BEGIN, 365, 339,
FONT_NEXT, 273, -18,
FONT_NEXT, 287, 117,
FONT_END, 172, 370,
FONT_BEGIN, 172, 370,
FONT_NEXT, 273, -18,
FONT_END, 241, 20,
FONT_BEGIN, 172, 370,
FONT_NEXT, 241, 20,
FONT_END, 233, 43,
FONT_BEGIN, 172, 370,
FONT_NEXT, 233, 43,
FONT_END, 215, 83,
FONT_BEGIN, 172, 370,
FONT_NEXT, 215, 83,
FONT_END, 194, 125,
FONT_BEGIN, 172, 370,
FONT_NEXT, 194, 125,
FONT_END, 179, 158,
FONT_BEGIN, 172, 370,
FONT_NEXT, 179, 158,
FONT_END, 65, 404,
FONT_BEGIN, 172, 370,
FONT_NEXT, 65, 404,
FONT_NEXT, 162, 406,
FONT_END, 39, 428,
FONT_BEGIN, 475, 450,
FONT_NEXT, 475, 435,
FONT_NEXT, 340, 450,
FONT_NEXT, 451, 427,
FONT_END, 439, 414,
FONT_BEGIN, 30, -160,
FONT_NEXT, 36, -136,
FONT_NEXT, 34, -181,
FONT_END, 47, -199,
FONT_BEGIN, 47, -199,
FONT_NEXT, 36, -136,
FONT_END, 51, -123,
FONT_BEGIN, 47, -199,
FONT_NEXT, 51, -123,
FONT_NEXT, 70, -213,
FONT_NEXT, 78, -116,
FONT_NEXT, 103, -218,
FONT_NEXT, 118, -125,
FONT_END, 149, -134,
FONT_BEGIN, 103, -218,
FONT_NEXT, 149, -134,
FONT_NEXT, 154, -206,
FONT_NEXT, 167, -129,
FONT_END, 185, -113,
FONT_BEGIN, 154, -206,
FONT_NEXT, 185, -113,
FONT_NEXT, 197, -169,
FONT_NEXT, 214, -63,
FONT_END, 233, -11,
FONT_BEGIN, 197, -169,
FONT_NEXT, 233, -11,
FONT_NEXT, 235, -106,
FONT_NEXT, 239, 8,
FONT_END, 241, 20,
FONT_BEGIN, 235, -106,
FONT_NEXT, 241, 20,
FONT_END, 273, -18,
FONT_ADVANCE, 500, 0
    },
    {
122,
FONT_BEGIN, 403, 450,
FONT_NEXT, 403, 435,
FONT_NEXT, 56, 450,
FONT_NEXT, 293, 420,
FONT_END, 155, 420,
FONT_BEGIN, 56, 450,
FONT_NEXT, 155, 420,
FONT_END, 112, 413,
FONT_BEGIN, 56, 450,
FONT_NEXT, 112, 413,
FONT_END, 88, 396,
FONT_BEGIN, 56, 450,
FONT_NEXT, 88, 396,
FONT_END, 76, 368,
FONT_BEGIN, 56, 450,
FONT_NEXT, 76, 368,
FONT_END, 71, 332,
FONT_BEGIN, 56, 450,
FONT_NEXT, 71, 332,
FONT_END, 53, 332,
FONT_BEGIN, 418, 135,
FONT_NEXT, 404, 0,
FONT_NEXT, 400, 139,
FONT_END, 388, 89,
FONT_BEGIN, 388, 89,
FONT_NEXT, 404, 0,
FONT_NEXT, 369, 55,
FONT_END, 334, 36,
FONT_BEGIN, 334, 36,
FONT_NEXT, 404, 0,
FONT_NEXT, 307, 31,
FONT_NEXT, 27, 0,
FONT_NEXT, 272, 30,
FONT_END, 134, 30,
FONT_BEGIN, 134, 30,
FONT_NEXT, 27, 0,
FONT_END, 27, 15,
FONT_BEGIN, 134, 30,
FONT_NEXT, 27, 15,
FONT_END, 293, 420,
FONT_BEGIN, 134, 30,
FONT_NEXT, 293, 420,
FONT_END, 403, 435,
FONT_ADVANCE, 444, 0
    },
    {
123,
FONT_BEGIN, 100, 250,
FONT_NEXT, 142, 266,
FONT_NEXT, 142, 233,
FONT_END, 168, 209,
FONT_BEGIN, 168, 209,
FONT_NEXT, 142, 266,
FONT_END, 168, 248,
FONT_BEGIN, 168, 209,
FONT_NEXT, 168, 248,
FONT_NEXT, 181, 178,
FONT_NEXT, 215, 230,
FONT_NEXT, 185, 136,
FONT_NEXT, 210, -139,
FONT_END, 196, -114,
FONT_BEGIN, 185, 136,
FONT_NEXT, 196, -114,
FONT_END, 187, -82,
FONT_BEGIN, 185, 136,
FONT_NEXT, 187, -82,
FONT_END, 185, -41,
FONT_BEGIN, 185, 541,
FONT_NEXT, 187, 581,
FONT_NEXT, 185, 363,
FONT_NEXT, 196, 612,
FONT_END, 210, 637,
FONT_BEGIN, 185, 363,
FONT_NEXT, 210, 637,
FONT_END, 215, 267,
FONT_BEGIN, 185, 363,
FONT_NEXT, 215, 267,
FONT_NEXT, 181, 320,
FONT_NEXT, 168, 250,
FONT_NEXT, 168, 289,
FONT_END, 142, 266,
FONT_BEGIN, 142, 266,
FONT_NEXT, 168, 250,
FONT_END, 168, 248,
FONT_BEGIN, 142, 266,
FONT_END, 168, 248,
FONT_BEGIN, 350, 680,
FONT_NEXT, 350, 669,
FONT_NEXT, 280, 675,
FONT_NEXT, 305, 652,
FONT_END, 277, 627,
FONT_BEGIN, 280, 675,
FONT_NEXT, 277, 627,
FONT_NEXT, 228, 655,
FONT_NEXT, 263, 592,
FONT_END, 259, 546,
FONT_BEGIN, 228, 655,
FONT_NEXT, 259, 546,
FONT_END, 259, 378,
FONT_BEGIN, 228, 655,
FONT_NEXT, 259, 378,
FONT_END, 255, 330,
FONT_BEGIN, 228, 655,
FONT_NEXT, 255, 330,
FONT_END, 242, 294,
FONT_BEGIN, 228, 655,
FONT_NEXT, 242, 294,
FONT_END, 215, 267,
FONT_BEGIN, 228, 655,
FONT_NEXT, 215, 267,
FONT_END, 210, 637,
FONT_BEGIN, 259, 121,
FONT_NEXT, 259, -47,
FONT_NEXT, 255, 168,
FONT_NEXT, 228, -156,
FONT_NEXT, 242, 204,
FONT_END, 215, 230,
FONT_BEGIN, 215, 230,
FONT_NEXT, 228, -156,
FONT_END, 210, -139,
FONT_BEGIN, 215, 230,
FONT_END, 210, -139,
FONT_BEGIN, 350, -170,
FONT_NEXT, 350, -181,
FONT_NEXT, 305, -154,
FONT_NEXT, 280, -176,
FONT_NEXT, 277, -129,
FONT_NEXT, 228, -156,
FONT_NEXT, 263, -94,
FONT_END, 259, -47,
FONT_ADVANCE, 480, 0
    },
    {
124,
FONT_BEGIN, 133, 676,
FONT_NEXT, 133, -14,
FONT_NEXT, 67, 676,
FONT_END, 67, -14,
FONT_ADVANCE, 200, 0
    },
    {
125,
FONT_BEGIN, 130, 669,
FONT_NEXT, 130, 680,
FONT_NEXT, 174, 652,
FONT_NEXT, 199, 675,
FONT_NEXT, 202, 627,
FONT_END, 216, 592,
FONT_BEGIN, 216, 592,
FONT_NEXT, 199, 675,
FONT_END, 251, 655,
FONT_BEGIN, 216, 592,
FONT_NEXT, 251, 655,
FONT_NEXT, 221, 546,
FONT_NEXT, 237, 294,
FONT_END, 224, 330,
FONT_BEGIN, 221, 546,
FONT_NEXT, 224, 330,
FONT_END, 221, 378,
FONT_BEGIN, 295, 540,
FONT_NEXT, 295, 363,
FONT_NEXT, 292, 580,
FONT_NEXT, 264, 268,
FONT_NEXT, 283, 612,
FONT_END, 269, 637,
FONT_BEGIN, 269, 637,
FONT_NEXT, 264, 268,
FONT_NEXT, 251, 655,
FONT_END, 237, 294,
FONT_BEGIN, 380, 250,
FONT_NEXT, 337, 233,
FONT_NEXT, 337, 266,
FONT_END, 311, 289,
FONT_BEGIN, 311, 289,
FONT_NEXT, 337, 233,
FONT_END, 312, 251,
FONT_BEGIN, 311, 289,
FONT_NEXT, 312, 251,
FONT_END, 264, 268,
FONT_BEGIN, 311, 289,
FONT_NEXT, 264, 268,
FONT_NEXT, 298, 320,
FONT_END, 295, 363,
FONT_BEGIN, 221, 121,
FONT_NEXT, 224, 168,
FONT_NEXT, 221, -47,
FONT_NEXT, 237, 204,
FONT_NEXT, 251, -157,
FONT_NEXT, 264, 231,
FONT_NEXT, 269, -139,
FONT_END, 283, -114,
FONT_BEGIN, 283, -114,
FONT_NEXT, 264, 231,
FONT_NEXT, 292, -83,
FONT_END, 295, -42,
FONT_BEGIN, 295, -42,
FONT_NEXT, 264, 231,
FONT_NEXT, 295, 136,
FONT_END, 298, 178,
FONT_BEGIN, 298, 178,
FONT_NEXT, 264, 231,
FONT_NEXT, 311, 209,
FONT_NEXT, 312, 249,
FONT_NEXT, 337, 233,
FONT_END, 312, 251,
FONT_BEGIN, 130, -170,
FONT_NEXT, 174, -154,
FONT_NEXT, 130, -181,
FONT_END, 199, -177,
FONT_BEGIN, 199, -177,
FONT_NEXT, 174, -154,
FONT_END, 202, -129,
FONT_BEGIN, 199, -177,
FONT_NEXT, 202, -129,
FONT_END, 216, -94,
FONT_BEGIN, 199, -177,
FONT_NEXT, 216, -94,
FONT_NEXT, 251, -157,
FONT_END, 221, -47,
FONT_ADVANCE, 480, 0
    },
    {
126,
FONT_BEGIN, 502, 273,
FONT_NEXT, 451, 214,
FONT_NEXT, 466, 323,
FONT_END, 428, 273,
FONT_BEGIN, 428, 273,
FONT_NEXT, 451, 214,
FONT_END, 418, 194,
FONT_BEGIN, 428, 273,
FONT_NEXT, 418, 194,
FONT_NEXT, 405, 257,
FONT_NEXT, 377, 187,
FONT_NEXT, 378, 251,
FONT_END, 330, 263,
FONT_BEGIN, 330, 263,
FONT_NEXT, 377, 187,
FONT_END, 324, 196,
FONT_BEGIN, 330, 263,
FONT_NEXT, 324, 196,
FONT_NEXT, 276, 287,
FONT_NEXT, 275, 220,
FONT_NEXT, 219, 309,
FONT_NEXT, 222, 244,
FONT_END, 160, 255,
FONT_BEGIN, 219, 309,
FONT_NEXT, 160, 255,
FONT_NEXT, 165, 319,
FONT_END, 120, 311,
FONT_BEGIN, 120, 311,
FONT_NEXT, 160, 255,
FONT_END, 130, 248,
FONT_BEGIN, 120, 311,
FONT_NEXT, 130, 248,
FONT_END, 107, 232,
FONT_BEGIN, 120, 311,
FONT_NEXT, 107, 232,
FONT_NEXT, 85, 291,
FONT_NEXT, 76, 183,
FONT_END, 40, 233,
FONT_ADVANCE, 541, 0
    },
    {
END_OF_LIST
    }
};

// Character 0 may not be defined for the bitmap font because it is used as

// BITMAP_END_OF_LIST.  We could use END_OF_LIST (which is 256), but that
// gets truncated to zero anyway, so lets be clean and use BITMAP_END_OF_LIST.

GLubyte bitmapFont[][1+13] = {
    {
32,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    },
    {
33,
0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18
    },
    {
34,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x36, 0x36, 0x36, 0x36
    },
    {
35,
0x00, 0x00, 0x00, 0x66, 0x66, 0xff, 0x66,
0x66, 0xff, 0x66, 0x66, 0x00, 0x00
    },
    {
36,
0x00, 0x00, 0x18, 0x7e, 0xff, 0x1b, 0x1f,
0x7e, 0xf8, 0xd8, 0xff, 0x7e, 0x18
    },
    {
37,
0x00, 0x00, 0x0e, 0x1b, 0xdb, 0x6e, 0x30,
0x18, 0x0c, 0x76, 0xdb, 0xd8, 0x70
    },
    {
38,
0x00, 0x00, 0x7f, 0xc6, 0xcf, 0xd8, 0x70,
0x70, 0xd8, 0xcc, 0xcc, 0x6c, 0x38
    },
    {
39,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x1c, 0x0c, 0x0e
    },
    {
40,
0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x18, 0x0c
    },
    {
41,
0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30
    },
    {
42,
0x00, 0x00, 0x00, 0x00, 0x99, 0x5a, 0x3c,
0xff, 0x3c, 0x5a, 0x99, 0x00, 0x00
    },
    {
43,
0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff,
0xff, 0x18, 0x18, 0x18, 0x00, 0x00
    },
    {
44,
0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    },
    {
45,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xff, 0x00, 0x00, 0x00, 0x00, 0x00
    },
    {
46,
0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    },
    {
47,
0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18,
0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03
    },
    {
48,
0x00, 0x00, 0x3c, 0x66, 0xc3, 0xe3, 0xf3,
0xdb, 0xcf, 0xc7, 0xc3, 0x66, 0x3c
    },
    {
49,
0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x78, 0x38, 0x18
    },
    {
50,
0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30,
0x18, 0x0c, 0x06, 0x03, 0xe7, 0x7e
    },
    {
51,
0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07,
0x7e, 0x07, 0x03, 0x03, 0xe7, 0x7e
    },
    {
52,
0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0xff, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c
    },
    {
53,
0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07,
0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xff
    },
    {
54,
0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc7,
0xfe, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e
    },
    {
55,
0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x18,
0x0c, 0x06, 0x03, 0x03, 0x03, 0xff
    },
    {
56,
0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7,
0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e
    },
    {
57,
0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x03,
0x7f, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e
    },
    {
58,
0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00,
0x38, 0x38, 0x00, 0x00, 0x00, 0x00
    },
    {
59,
0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00,
0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00
    },
    {
60,
0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60,
0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06
    },
    {
61,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00
    },
    {
62,
0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06,
0x03, 0x06, 0x0c, 0x18, 0x30, 0x60
    },
    {
63,
0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18,
0x0c, 0x06, 0x03, 0xc3, 0xc3, 0x7e
    },
    {
64,
0x00, 0x00, 0x3f, 0x60, 0xcf, 0xdb, 0xd3,
0xdd, 0xc3, 0x7e, 0x00, 0x00, 0x00
    },
    {
65,
0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xff,
0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18
    },
    {
66,
0x00, 0x00, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7,
0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe
    },
    {
67,
0x00, 0x00, 0x7e, 0xe7, 0xc0, 0xc0, 0xc0,
0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e
    },
    {
68,
0x00, 0x00, 0xfc, 0xce, 0xc7, 0xc3, 0xc3,
0xc3, 0xc3, 0xc3, 0xc7, 0xce, 0xfc
    },
    {
69,
0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0,
0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xff
    },
    {
70,
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xff
    },
    {
71,
0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xcf,
0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e
    },
    {
72,
0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3
    },
    {
73,
0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x7e
    },
    {
74,
0x00, 0x00, 0x7c, 0xee, 0xc6, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06
    },
    {
75,
0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0,
0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc3
    },
    {
76,
0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0,
0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0
    },
    {
77,
0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
0xc3, 0xdb, 0xff, 0xff, 0xe7, 0xc3
    },
    {
78,
0x00, 0x00, 0xc7, 0xc7, 0xcf, 0xcf, 0xdf,
0xdb, 0xfb, 0xf3, 0xf3, 0xe3, 0xe3
    },
    {
79,
0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3,
0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0x7e
    },
    {
80,
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe
    },
    {
81,
0x00, 0x00, 0x3f, 0x6e, 0xdf, 0xdb, 0xc3,
0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c
    },
    {
82,
0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0,
0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe
    },
    {
83,
0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07,
0x7e, 0xe0, 0xc0, 0xc0, 0xe7, 0x7e
    },
    {
84,
0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0xff
    },
    {
85,
0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3,
0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3
    },
    {
86,
0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66,
0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3
    },
    {
87,
0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb,
0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3
    },
    {
88,
0x00, 0x00, 0xc3, 0x66, 0x66, 0x3c, 0x3c,
0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3
    },
    {
89,
0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3
    },
    {
90,
0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30,
0x7e, 0x0c, 0x06, 0x03, 0x03, 0xff
    },
    {
91,
0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x3c
    },
    {
92,
0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c,
0x18, 0x18, 0x30, 0x30, 0x60, 0x60
    },
    {
93,
0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c
    },
    {
94,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18
    },
    {
95,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    },
    {
96,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x38, 0x30, 0x70
    },
    {
97,
0x00, 0x00, 0x7f, 0xc3, 0xc3, 0x7f, 0x03,
0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00
    },
    {
98,
0x00, 0x00, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3,
0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0
    },
    {
99,
0x00, 0x00, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0,
0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00
    },
    {
100,
0x00, 0x00, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3,
0x7f, 0x03, 0x03, 0x03, 0x03, 0x03
    },
    {
101,
0x00, 0x00, 0x7f, 0xc0, 0xc0, 0xfe, 0xc3,
0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00
    },
    {
102,
0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30,
0xfc, 0x30, 0x30, 0x30, 0x33, 0x1e
    },
    {
103,
0x7e, 0xc3, 0x03, 0x03, 0x7f, 0xc3, 0xc3,
0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00
    },
    {
104,
0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0
    },
    {
105,
0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x00, 0x00, 0x18, 0x00
    },
    {
106,
0x38, 0x6c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x00
    },
    {
107,
0x00, 0x00, 0xc6, 0xcc, 0xf8, 0xf0, 0xd8,
0xcc, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0
    },
    {
108,
0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x78
    },
    {
109,
0x00, 0x00, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
0xdb, 0xfe, 0x00, 0x00, 0x00, 0x00
    },
    {
110,
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00
    },
    {
111,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6,
0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00
    },
    {
112,
0xc0, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0xc3,
0xc3, 0xfe, 0x00, 0x00, 0x00, 0x00
    },
    {
113,
0x03, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3,
0xc3, 0x7f, 0x00, 0x00, 0x00, 0x00
    },
    {
114,
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
0xe0, 0xfe, 0x00, 0x00, 0x00, 0x00
    },
    {
115,
0x00, 0x00, 0xfe, 0x03, 0x03, 0x7e, 0xc0,
0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00
    },
    {
116,
0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x30,
0x30, 0xfc, 0x30, 0x30, 0x30, 0x00
    },
    {
117,
0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6,
0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00
    },
    {
118,
0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66,
0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00
    },
    {
119,
0x00, 0x00, 0xc3, 0xe7, 0xff, 0xdb, 0xc3,
0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00
    },
    {
120,
0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c,
0x66, 0xc3, 0x00, 0x00, 0x00, 0x00
    },
    {
121,
0xc0, 0x60, 0x60, 0x30, 0x18, 0x3c, 0x66,
0x66, 0xc3, 0x00, 0x00, 0x00, 0x00
    },
    {
122,
0x00, 0x00, 0xff, 0x60, 0x30, 0x18, 0x0c,
0x06, 0xff, 0x00, 0x00, 0x00, 0x00
    },
    {
123,
0x00, 0x00, 0x0f, 0x18, 0x18, 0x18, 0x38,
0xf0, 0x38, 0x18, 0x18, 0x18, 0x0f
    },
    {
124,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18
    },
    {
125,
0x00, 0x00, 0xf0, 0x18, 0x18, 0x18, 0x1c,
0x0f, 0x1c, 0x18, 0x18, 0x18, 0xf0
    },
    {
126,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0x8f, 0xf1, 0x60, 0x00, 0x00, 0x00
    },
    {
        BITMAP_END_OF_LIST
    }
};

GLenum tkCreateStrokeFont(GLuint fontBase)
{
    GLint mode, i, j;

    for (i = 0; strokeFont[i][0] != END_OF_LIST; i++) {
glNewList(fontBase+(unsigned int)strokeFont[i][0], GL_COMPILE);
for (j = 1; mode = strokeFont[i][j]; j += 3) {
    if (mode == FONT_BEGIN) {
glBegin(GL_LINE_STRIP);
glVertex2f((float)strokeFont[i][j+1]*STROKE_SCALE,
   (float)strokeFont[i][j+2]*STROKE_SCALE);
    } else if (mode == FONT_NEXT) {
glVertex2f((float)strokeFont[i][j+1]*STROKE_SCALE,
   (float)strokeFont[i][j+2]*STROKE_SCALE);
    } else if (mode == FONT_END) {
glVertex2f((float)strokeFont[i][j+1]*STROKE_SCALE,
   (float)strokeFont[i][j+2]*STROKE_SCALE);
glEnd();
    } else if (mode == FONT_ADVANCE) {
glTranslatef((float)strokeFont[i][j+1]*STROKE_SCALE,
     (float)strokeFont[i][j+2]*STROKE_SCALE, (GLfloat) 0.0);
break;
    }
}
glEndList();
    }
    return GL_TRUE;
}

GLenum tkCreateOutlineFont(GLuint fontBase)
{
    GLint mode, i, j;

    for (i = 0; outlineFont[i][0] != END_OF_LIST; i++) {
glNewList(fontBase+(unsigned int)outlineFont[i][0], GL_COMPILE);
for (j = 1; mode = outlineFont[i][j]; j += 3) {
    if (mode == FONT_BEGIN) {
glBegin(GL_LINE_STRIP);
glVertex2f((float)outlineFont[i][j+1]*OUTLINE_SCALE,
   (float)outlineFont[i][j+2]*OUTLINE_SCALE);
    } else if (mode == FONT_NEXT) {
glVertex2f((float)outlineFont[i][j+1]*OUTLINE_SCALE,
   (float)outlineFont[i][j+2]*OUTLINE_SCALE);
    } else if (mode == FONT_END) {
glVertex2f((float)outlineFont[i][j+1]*OUTLINE_SCALE,
   (float)outlineFont[i][j+2]*OUTLINE_SCALE);
glEnd();
    } else if (mode == FONT_ADVANCE) {
glTranslatef((float)outlineFont[i][j+1]*OUTLINE_SCALE,
     (float)outlineFont[i][j+2]*OUTLINE_SCALE, (GLfloat) 0.0);
break;
    }
}
glEndList();
    }
    return GL_TRUE;
}

GLenum tkCreateFilledFont(GLuint fontBase)
{
    GLint mode, i, j;

    for (i = 0; filledFont[i][0] != END_OF_LIST; i++) {
glNewList(fontBase+(unsigned int)filledFont[i][0], GL_COMPILE);
for (j = 1; mode = filledFont[i][j]; j += 3) {
    if (mode == FONT_BEGIN) {
glBegin(GL_TRIANGLE_STRIP);
glVertex2f((float)filledFont[i][j+1]*FILLED_SCALE,
   (float)filledFont[i][j+2]*FILLED_SCALE);
    } else if (mode == FONT_NEXT) {
glVertex2f((float)filledFont[i][j+1]*FILLED_SCALE,
   (float)filledFont[i][j+2]*FILLED_SCALE);
    } else if (mode == FONT_END) {
glVertex2f((float)filledFont[i][j+1]*FILLED_SCALE,
   (float)filledFont[i][j+2]*FILLED_SCALE);
glEnd();
    } else if (mode == FONT_ADVANCE) {
glTranslatef((float)filledFont[i][j+1]*FILLED_SCALE,
     (float)filledFont[i][j+2]*FILLED_SCALE, (GLfloat) 0.0);
break;
    }
}
glEndList();
    }
    return GL_TRUE;
}

GLenum tkCreateBitmapFont(GLuint fontBase)
{
    GLint i;

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    for (i = 0; bitmapFont[i][0] != BITMAP_END_OF_LIST; i++) {
glNewList(fontBase+(unsigned int)bitmapFont[i][0], GL_COMPILE);
glBitmap(8, 13, (GLfloat) 0.0, (GLfloat) 2.0, (GLfloat) 10.0, (GLfloat) 0.0, &bitmapFont[i][1]);
glEndList();
    }
    return GL_TRUE;
}

void tkDrawStr(GLuint base, char *str)
{

    glPushAttrib(GL_LIST_BIT);
    glListBase(base);
    glCallLists(strlen(str), GL_UNSIGNED_BYTE, (unsigned char *)str);
    glPopAttrib();
}


TKIMAGE.C

/*
 * 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 <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "tk.h"

#define static

#define IMAGIC      0x01da
#define IMAGIC_SWAP 0xda01

#define SWAP_SHORT_BYTES(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
#define SWAP_LONG_BYTES(x) (((((x) & 0xff) << 24) | (((x) & 0xff00) << 8)) | \
                            ((((x) & 0xff0000) >> 8) | (((x) & 0xff000000) >> 24)))

typedef struct _rawImageRec {
    unsigned short imagic;
    unsigned short type;
    unsigned short dim;
    unsigned short sizeX, sizeY, sizeZ;
    unsigned long min, max;
    unsigned long wasteBytes;
    char name[80];
    unsigned long colorMap;
    HANDLE file;
    unsigned char *tmp, *tmpR, *tmpG, *tmpB;
    unsigned long rleEnd;
    unsigned long *rowStart;
    long *rowSize;
} rawImageRec;

static void RawImageClose(rawImageRec *raw);

static rawImageRec *RawImageOpenAW(char *fileName, BOOL bUnicode)
{
    rawImageRec *raw;
    unsigned long *rowStart, *rowSize, ulTmp;
    int x;
    DWORD dwBytesRead;

    raw = (rawImageRec *)malloc(sizeof(rawImageRec));
    if (raw == NULL) {
        MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
        return NULL;
    }

    raw->file = bUnicode ? CreateFileW((LPWSTR) fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0) :
                           CreateFileA((LPSTR) fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
    if (raw->file == INVALID_HANDLE_VALUE) {
        char ach[256];

        bUnicode ? wsprintf(ach, "Failed to open image file %ws.\n", fileName) :
                   wsprintf(ach, "Failed to open image file %s.\n", fileName);

        MESSAGEBOX(GetFocus(), ach, "Error", MB_OK);

        free( raw );
        return NULL;
    }

    ReadFile(raw->file, (LPVOID) raw, 12, &dwBytesRead, (LPOVERLAPPED) NULL);

    if (raw->imagic == IMAGIC_SWAP) {
        raw->type = SWAP_SHORT_BYTES(raw->type);
        raw->dim = SWAP_SHORT_BYTES(raw->dim);
        raw->sizeX = SWAP_SHORT_BYTES(raw->sizeX);
        raw->sizeY = SWAP_SHORT_BYTES(raw->sizeY);
        raw->sizeZ = SWAP_SHORT_BYTES(raw->sizeZ);
    } else if (raw->imagic != IMAGIC) {
        // magic number is absent - conclude file is invalid (?)
        MESSAGEBOX(GetFocus(), "Invalid rgb file.", "Error", MB_OK);
        RawImageClose( raw );
        return NULL;
    }

    raw->tmp = (unsigned char *)malloc(raw->sizeX*256);
    raw->tmpR = (unsigned char *)malloc(raw->sizeX*256);
    raw->tmpG = (unsigned char *)malloc(raw->sizeX*256);
    raw->tmpB = (unsigned char *)malloc(raw->sizeX*256);
    if (raw->tmp == NULL || raw->tmpR == NULL || raw->tmpG == NULL ||
        raw->tmpB == NULL) {
        MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
        RawImageClose( raw );
        return NULL;
    }

    if ((raw->type & 0xFF00) == 0x0100) {
        x = raw->sizeY * raw->sizeZ * sizeof(long);
        raw->rowStart = (unsigned long *)malloc(x);
        raw->rowSize = (long *)malloc(x);
        if (raw->rowStart == NULL || raw->rowSize == NULL) {
            MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
            RawImageClose( raw );
            return NULL;
        }
        raw->rleEnd = 512 + (2 * x);
        SetFilePointer(raw->file, 512, NULL, FILE_BEGIN);
        ReadFile(raw->file, (LPVOID) raw->rowStart, x, &dwBytesRead,
                 (LPOVERLAPPED) NULL);
        ReadFile(raw->file, (LPVOID) raw->rowSize, x, &dwBytesRead,
                 (LPOVERLAPPED) NULL);
        if (raw->imagic == IMAGIC_SWAP) {
            x /= sizeof(long);
            rowStart = raw->rowStart;
            rowSize = raw->rowSize;
            while (x--) {
                ulTmp = *rowStart;
                *rowStart++ = SWAP_LONG_BYTES(ulTmp);
                ulTmp = *rowSize;
                *rowSize++ = SWAP_LONG_BYTES(ulTmp);
            }
        }
    }
    return raw;
}

static void RawImageClose(rawImageRec *raw)
{
    if( !raw )
        return;
    CloseHandle(raw->file);
    if( raw->tmp ) free(raw->tmp);
    if( raw->tmpR ) free(raw->tmpR);
    if( raw->tmpG ) free(raw->tmpG);
    if( raw->tmpB ) free(raw->tmpB);
    free(raw);
}

static void RawImageGetRow(rawImageRec *raw, unsigned char *buf, int y, int z)
{
    unsigned char *iPtr, *oPtr, pixel;
    int count;
    DWORD dwBytesRead;

    if ((raw->type & 0xFF00) == 0x0100) {
        SetFilePointer(raw->file, raw->rowStart[y+z*raw->sizeY], NULL, FILE_BEGIN);
        ReadFile(raw->file, (LPVOID) raw->tmp,
                 (unsigned int)raw->rowSize[y+z*raw->sizeY], &dwBytesRead,
                 (LPOVERLAPPED) NULL);

        iPtr = raw->tmp;
        oPtr = buf;
        while (1) {
            pixel = *iPtr++;
            count = (int)(pixel & 0x7F);
            if (!count) {
                return;
            }
            if (pixel & 0x80) {
                while (count--) {
                    *oPtr++ = *iPtr++;
                }
            } else {
                pixel = *iPtr++;
                while (count--) {
                    *oPtr++ = pixel;
                }
            }
        }
    } else {
        SetFilePointer(raw->file, 512+(y*raw->sizeX)+(z*raw->sizeX*raw->sizeY),
                       NULL, FILE_BEGIN);
        ReadFile(raw->file, (LPVOID) buf, raw->sizeX, &dwBytesRead,
                 (LPOVERLAPPED) NULL);
    }
}

static void RawImageGetData(rawImageRec *raw, TK_RGBImageRec *final)
{
    unsigned char *ptr;
    int i, j;

    final->data = (unsigned char *)malloc((raw->sizeX+1)*(raw->sizeY+1)*4);
    if (final->data == NULL) {
        MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
        return;
    }

    ptr = final->data;
    for (i = 0; i < raw->sizeY; i++) {
        RawImageGetRow(raw, raw->tmpR, i, 0);
        RawImageGetRow(raw, raw->tmpG, i, 1);
        RawImageGetRow(raw, raw->tmpB, i, 2);
        for (j = 0; j < raw->sizeX; j++) {
            *ptr++ = *(raw->tmpR + j);
            *ptr++ = *(raw->tmpG + j);
            *ptr++ = *(raw->tmpB + j);
        }
    }
}

TK_RGBImageRec *tkRGBImageLoad(char *fileName)
{
    return tkRGBImageLoadAW(fileName, FALSE);
}

TK_RGBImageRec *tkRGBImageLoadAW(char *fileName, BOOL bUnicode)
{
    rawImageRec *raw;
    TK_RGBImageRec *final;

    if( !(raw = RawImageOpenAW(fileName, bUnicode)) )
        return NULL;

    final = (TK_RGBImageRec *)malloc(sizeof(TK_RGBImageRec));
    if (final == NULL) {
        MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
        RawImageClose(raw);
        return NULL;
    }
    final->sizeX = raw->sizeX;
    final->sizeY = raw->sizeY;
    RawImageGetData(raw, final);
    RawImageClose(raw);
    return final;
}


VECT3D.C

/*
 * 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.
 */
/* Routines to manipulate 3 dimensional vectors.  All these routines
 * should work even if the input and output vectors are the same.
 */

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/gl.h>
#include "3d.h"
#include "tk.h"

#define static

void (*errfunc)(char *) = 0;

void seterrorfunc(void (*func)(char *))
{
    errfunc = func;
}

void error(char *s)
{
    if (errfunc)
        (*errfunc)(s);
    else {
        MESSAGEBOX(GetFocus(), s, "Error", MB_OK);
        exit(1);
    }
}

void diff3(GLdouble p[3], GLdouble q[3], GLdouble diff[3])
{
    diff[0] = p[0] - q[0];
    diff[1] = p[1] - q[1];
    diff[2] = p[2] - q[2];
}

void add3(GLdouble p[3], GLdouble q[3], GLdouble sum[3])
{
    sum[0] = p[0] + q[0];
    sum[1] = p[1] + q[1];
    sum[2] = p[2] + q[2];
}

void scalarmult(GLdouble s, GLdouble v[3], GLdouble vout[3])
{
    vout[0] = v[0]*s;
    vout[1] = v[1]*s;
    vout[2] = v[2]*s;
}

GLdouble dot3(GLdouble p[3], GLdouble q[3])
{
    return p[0]*q[0] + p[1]*q[1] + p[2]*q[2];
}

GLdouble length3(GLdouble v[3])
{
    return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
}

GLdouble dist3(GLdouble p[3], GLdouble q[3])
{
    GLdouble d[3];

    diff3(p, q, d);
    return length3(d);
}

void copy3(GLdouble old[3], GLdouble new[3])
{
    new[0] = old[0], new[1] = old[1], new[2] = old[2];
}

void crossprod(GLdouble v1[3], GLdouble v2[3], GLdouble prod[3])
{
    GLdouble p[3];      /* in case prod == v1 or v2 */

    p[0] = v1[1]*v2[2] - v2[1]*v1[2];
    p[1] = v1[2]*v2[0] - v2[2]*v1[0];
    p[2] = v1[0]*v2[1] - v2[0]*v1[1];
    prod[0] = p[0]; prod[1] = p[1]; prod[2] = p[2];
}

void normalize(GLdouble v[3])
{
    GLdouble d;

    d = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
    if (d == (GLdouble)0.0) {
        error("normalize: zero length vector");
        v[0] = d = (GLdouble)1.0;
    }
    d = (GLdouble)1/d;
    v[0] *= d; v[1] *= d; v[2] *= d;
}

void identifymat3(GLdouble m[3][3])
{
    int i, j;

    for (i=0; i<3; i++)
        for (j=0; j<3; j++)
            m[i][j] = (i == j) ? (GLdouble)1.0 : (GLdouble)0.0;
}

void copymat3(GLdouble *to, GLdouble *from)
{
    int i;

    for (i=0; i<9; i++) {
        *to++ = *from++;
    }
}

void xformvec3(GLdouble v[3], GLdouble m[3][3], GLdouble vm[3])
{
    GLdouble result[3]; /* in case v == vm */
    int i;

    for (i=0; i<3; i++) {
        result[i] = v[0]*m[0][i] + v[1]*m[1][i] + v[2]*m[2][i];
    }
    for (i=0; i<3; i++) {
        vm[i] = result[i];
    }
}

long samepoint(GLdouble p1[3], GLdouble p2[3])
{
    if (p1[0] == p2[0] && p1[1] == p2[1] && p1[2] == p2[2])
        return 1;
    return 0;
}

void perpnorm(GLdouble p1[3], GLdouble p2[3], GLdouble p3[3], GLdouble n[3])
{
    GLdouble d1[3], d2[3];

    diff3(p2, p1, d1);
    diff3(p2, p3, d2);
    crossprod(d1, d2, n);
    normalize(n);
}


XFORM.C

/*
 * 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 <windows.h>
#include <math.h>
#include <stdio.h>
#include <GL/gl.h>
#include "3d.h"

#define static

#define STACKDEPTH 10

typedef struct {
    GLdoublemat[4][4];
    GLdoublenorm[3][3];
} mat_t;

static mat_t matstack[STACKDEPTH] = {
    {{{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}},
    {{1.0, 0.0, 0.0},
    {0.0, 1.0, 0.0},
    {0.0, 0.0, 1.0}}}
};
static int identitymat = 1;

static int mattop = 0;

void m_xformpt(GLdouble pin[3], GLdouble pout[3],
    GLdouble nin[3], GLdouble nout[3])
{
    inti;
    GLdoubleptemp[3], ntemp[3];
    mat_t*m = &matstack[mattop];

    if (identitymat) {
for (i = 0; i < 3; i++) {
    pout[i] = pin[i];
    nout[i] = nin[i];
}
return;
    }
    for (i = 0; i < 3; i++) {
ptemp[i] = pin[0]*m->mat[0][i] +
   pin[1]*m->mat[1][i] +
   pin[2]*m->mat[2][i] +
   m->mat[3][i];
ntemp[i] = nin[0]*m->norm[0][i] +
   nin[1]*m->norm[1][i] +
   nin[2]*m->norm[2][i];
    }
    for (i = 0; i < 3; i++) {
pout[i] = ptemp[i];
nout[i] = ntemp[i];
    }
    normalize(nout);
}

void m_xformptonly(GLdouble pin[3], GLdouble pout[3])
{
    inti;
    GLdoubleptemp[3];
    mat_t*m = &matstack[mattop];

    if (identitymat) {
for (i = 0; i < 3; i++) {
    pout[i] = pin[i];
}
return;
    }
     for (i = 0; i < 3; i++) {
ptemp[i] = pin[0]*m->mat[0][i] +
   pin[1]*m->mat[1][i] +
   pin[2]*m->mat[2][i] +
   m->mat[3][i];
    }
    for (i = 0; i < 3; i++) {
pout[i] = ptemp[i];
    }
}

void m_pushmatrix(void)
{
    if (mattop < STACKDEPTH-1) {
matstack[mattop+1] = matstack[mattop];
mattop++;
    } else
error("m_pushmatrix: stack overflow\n");
}

void m_popmatrix(void)
{
    if (mattop > 0)
mattop--;
    else
error("m_popmatrix: stack underflow\n");
}

void m_translate(GLdouble x, GLdouble y, GLdouble z)
{
    inti;
    mat_t*m = &matstack[mattop];

    identitymat = 0;
    for (i = 0; i < 4; i++)
m->mat[3][i] = x*m->mat[0][i] +
 y*m->mat[1][i] +
 z*m->mat[2][i] +
 m->mat[3][i];
}

void m_scale(GLdouble x, GLdouble y, GLdouble z)
{
    inti;
    mat_t*m = &matstack[mattop];

    identitymat = 0;
    for (i = 0; i < 3; i++) {
m->mat[0][i] *= x;
m->mat[1][i] *= y;
m->mat[2][i] *= z;
    }
    for (i = 0; i < 3; i++) {
m->norm[0][i] /= x;
m->norm[1][i] /= y;
m->norm[2][i] /= z;
    }
}


ABOUT.TXT

OpenGL Samples

The OPENGL\SAMPLES directory contains a number of short samples. The following
are summaries for the operation of each of these samples.

TACCUM - Accumulation test.
 - RGBA, SB/DB (SB default).
 - Command line options:
   -sb Single buffer mode.
   -db Double buffer mode.
 - Keys:
   ESC Quit
   1   Use filled polygon mode.
   2   Use outlined polygon mode.

TBITMAP1 - Bitmap test.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgbRGBA mode.
   -ci  Color index mode.
   -sb  Single buffer mode.
   -db  Double buffer mode.
 - Keys:
   ESC  Quit.

TBITMAP2 - Bitmap test.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgb RGBA mode.
   -ci  Color index mode.
   -sb  single buffer mode.
   -db  Double buffer mode.
 - Keys:
   ESC  Quit.
   1    Toggle display list mode.
   2    Toggle color animation mode.

TCOPY - Pixel copy test.
 - RGBA, SB/DB (SB default).
 - Command line options:
   -sb  Single buffer mode.
   -db  Double buffer mode.
   -f   <file name> RGB image file.
 - Keys:
   ESC  Quit.
   Z    Increase zoom factor.
   z    Decrease zoom factor.
 - Mouse input:
   Left Copy location.

TDEPTH - Z buffer test.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgb RGBA mode.
   -ci  Color index mode.
   -sb  Single buffer mode.
   -db  Double buffer mode.
 - Keys:
   ESC  Quit.
   1    Toggle anti-aliased mode.
   2    Toggle stipple mode.

TEVAL - Evaluator test.
 - RGBA, SB/DB (SB default).
 - Command line options:
   -sb   Single buffer mode.
   -db   Double buffer mode.
 - Keys:
   ESC Quit.
   LEFT  Rotate.
   RIGHT Rotate.
   UP    Rotate.
   DOWN  Rotate.
   1     Toggle dimensions.
   2     Toggle dimensions.
   e     Use eval mode.
   m     Use mesh mode.
   f     Toggle polygon mode.
   p     Toggle point mode.
   c     Toggle color mode.
   t     Toggle texture mode.
   l     Toggle lighting mode.

TFOG - Fog test.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgb  RGBA mode.
   -ci   Color index mode.
   -sb   Single buffer mode.
   -db   Double buffer mode.
 - Keys:
   ESC Quit.
   LEFT Rotate.
   RIGHT Rotate.
   UP    Rotate.
   DOWN  Rotate.
   D     Increase fog density.
   d     Decrease fog density.

TLINE - Line test.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgbRGBA mode.
   -ciColor index mode.
   -sbSingle buffer mode.
   -dbDouble buffer mode.
 - Keys:
ESCQuit.
   W    Increase line width.
   w    Decrease line width.
   1    Toggle stipple mode.
   2    Toggle anti-aliased mode.

TLOGO - Demo.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgb  RGBA mode.
   -ci   Color index mode.
   -sb   Single buffer mode.
   -db   Double buffer mode.
 - Keys:
   ESC Quit.
   LEFT Rotate.
   RIGHT Rotate
   UP    Move clipping plane.
   DOWN  Move clipping plane.
   Z     Translate.
   z     Translate.
   1     Use GL_POINT polygon mode.
   2     Use GL_LINE polygon mode.
   3     Use GL_FILL polygon mode.
   p     Toggle polygon fill modes.
   4     Use GL_NICEST for GL_POLYGON_SMOOTH_HINT.
   5     Use anti-aliased polygon mode.
   6     Use aliased polygon mode.
   8     Toggle dither mode.
   9     Toggle stipple polygon mode.
   0     Toggle flat/smooth shading mode.
   q     Disable cull mode.
   w     Use front face cull mode.
   e     Use back face cull mode.
   r     Use clockwise front face mode.
   t     Use counter-clockwise front face mode.
   y     Use MSB first stipple pattern.
   u     Use LSB first stipple pattern.
   a     Use brick texture map.
   s     Use checker texture map.
   d     Disable texture map.
   f     Use decal texture environment mode.
   g     Use modulate texture environment mode.

TNURB - Nurb test.
 - RGBA, SB/DB (SB default).
 - Command line options:
   -sb   Single buffer mode.
   -db   Double buffer mode.
 - Keys:
   ESC   Quit.
   LEFT  Rotate.
   RIGHT Rotate.
   UP    Rotate.
   DOWN  Rotate.

TOLYMPIC - Olymipic rings demo.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgb  RGBA mode.
   -ci   Color index mode.
   -sb   Single buffer mode.
   -db   Double buffer mode.
 - Keys:
   ESC   Quit.
   SPACE Restart demo.

TPOINT - Point test.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgb  RGBA mode.
   -ci   Color index mode.
   -sb   Single buffer mode.
   -db   Double buffer mode.
 - Keys:
   ESC   Quit.
   LEFT  Translate.
   RIGHT Translate.
   UP    Translate.
   DOWN  Translate.
   W     Increase point width.
   w     Decrease point width.
   1     Toggle anti-aliased mode.

TPRIM - Primitive test.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgb RGBA mode.
   -ci  Color index mode.
   -sb  Single buffer mode.
   -db  Double buffer mode.
 - Keys:
   ESC  Quit.
   1    Toggle flat/smooth shade mode.
   2    Toggle outlined/filled polygon mode.
   3    Toggle color mask mode.

TQUAD - Quadric test.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgb  RGBA mode.
   -ci   Color index mode.
   -sb   Single buffer mode.
   -db   Double buffer mode.
   -f    <file name> Texture file.
 - Keys:
   ESC   Quit.
   LEFT  Rotate.
   RIGHT Rotate.
   UP    Rotate.
   DOWN  Rotate.
   X     Rotate.
   x     Rotate.
   1     Use GLU_FILL draw style.
   2     Use GLU_POINT draw style.
   3     Use GLU_LINE draw style.
   4     Use GLU_SILHOUETTE draw style.
   0     Toggle flat/smooth shade mode.
   f     Cycle through quadrics.
   d     Toggle orientation.
   A     Increase number of stacks.
   a     Decrease number of stacks.
   S     Increase number of slices.
   s     Decrease number of slices.
   G     Increase radius1.
   g     Decrease radius1.
   J     Increase radius2.
   j     Decrease radius2.
   H     Increase height.
   h     Decrease height.
   K     Increase angle1.
   k     Decrease angle1.
   L     Increase angle2.
   l     Decrease angle2.
   z     Toggle texture mode.
   q     Disable cull mode.
   w     Use front face cull mode.
   e     Use back face cull mode.
   r     Use clockwise front face mode.
   t     Use counter-clockwise front face mode.
   y     Toggle dither mode.

TSELECT - Selection test.
 - RGBA, SB.
 - Keys:
   ESC    Quit.
   LEFT   Rotate.
   RIGHT  Rotate.
   Z      Increase zoom factor.
   z      Decrease zoom factor.
   d      Zoom at current Mouse location.
   f      Print feedback information.
   l      Toggle outlined/filled polygon mode.
 - Mouse:
   Left   Recolor selected triangle.
   Center Enlarge selected triangle.
   Right  Delete selected triangle.

TSPEED - Speed test.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgb RGBA mode.
   -ci  Color index mode.
   -sb  Single buffer mode.
   -db  Double buffer mode.
 - Keys:
   ESC  Quit.
   a    Toggle anti-aliased mode.
   d    Toggle z buffering mode.
   f    Toggle fog mode.
   F    Toggle fog hint mode.
   s    Toggle flat/smooth shading mode.
   t    Toggle texturing mode.

TSPHERE - Spheremap test.
 - RGBA, SB/DB (SB default).
 - Command line options:
   -sb   Single buffer mode.
   -db   Double buffer mode.
   -f    <file name> Texture file.
   -3    Use RGB components.
   -4    Use RGBA components.
 - Keys:
   ESC   Quit.
   LEFT  Rotate about the y axis.
   RIGHT Rotate about the y axis.
   UP    Rotate about the x axis.
   DOWN  Rotate about the x axis.
   a     Toggle auto rotate mode.
   c     Toggle between cylinder or cube object.
   t     Use torus object.
   d     Use decal texture mode.
   m     Use modulate texture mode.
   l     Toggle lighted mode.
   f     Toggle fog mode.
   0     Use nearest magification filter.
   1     Use linear magification.
   2     Use nearest minification filter.
   3     Use linear minification filter.
   4     Use nearest-mipmap-nearest minification filter.
   5     Use nearest-mipmap-linear minification filter.
   6     Use linear-mipmap-nearest minification filter.
   7     Use linear-mipmap-linear minification filter.

TSTAR - Demo.
 - RGBA, SB/DB (SB default).
 - Command line options:
   -sb   Single buffer mode.
   -db   Double buffer mode.
 - Keys:
   ESC   Quit.
   SPACE Toggle weird movement mode.
   t     Toggle turbo mode.

TSTENCIL - Stencil test.
 - RGBA, SB.

TSTRETCH - Texture test.
 - RGBA, SB.
 - Command line options:
   -sb   Single buffer mode.
   -db   Double buffer mode.
   -f    <file name> Texture file.
 - Keys:
   ESC   Quit.
   SPACE Start animation.
 - Mouse:
   Left  Added stretch point.

TTEXTURE - Texture test.
 - RGBA, SB/DB (SB default).
 - Command line options:
   -sb   Single buffer mode.
   -db   Double buffer mode.
   -f    <file name> Texture file.
 - Keys:
   ESC   Quit.
   LEFT  Rotate.
   RIGHT Rotate.
   UP    Rotate.
   DOWN  Rotate.
   T     Translate.
   t     Translate.
   s     Toggle sphere map mode.
   0     Use nearest magification filter.
   1     Use linear magification filter.
   2     Use nearest minification filter.
   3     Use linear minification filter.
   4     Use nearest-mipmap-nearest minification filter.
   5     Use nearest-mipmap-linear minification filter.
   6     Use linear-mipmap-nearest minification filter.
   7     Use linear-mipmap-linear minification filter.

TTRI - Triangle test.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgb RGBA mode.
   -ci   Color index mode.
   -sb   Single buffer mode.
   -db   Double buffer mode.
 - Keys:
   ESC   Quit.
   LEFT  Translate.
   RIGHT Translate.
   Z     Increase zoom factor.
   z     Decrease zoom factor.
   1     Use point polygon mode.
   2     Use line polygon mode.
   3     Use filled polygon mode.
   4     Use point primitive.
   5     Use line-loop primitive.
   6     Use polygon primitive.
   7     Toggle cull mode.
   8     Use clockwise/counter-clockwise front face mode.
   9     Toggle front/back face cull mode.
   v     Toggle show verticies mode.
   s     Toggle flat/smooth shade mode.
   h     Toggle hide bottom triangle mode.
   o     Toggle outline mode.
   m     Toggle dither mode.
   0     Toggle anti-aliased mode.

TWAVE - Demo.
 - RGBA/CI (RGBA default), SB/DB (SB default).
 - Command line options:
   -rgb  RGBA mode.
   -ci   Color index mode.
   -sb   Single buffer mode.
   -db   Double buffer mode.
   -grid <x> <y>   Number of grids.
   -size <number>  Size of grid.
   -wave <number>  Height of wave (floating point number).
   -frames <count> Number of frames.
 - Keys:
   ESC   Quit.
   c     Toggle contouring mode.
   s     Toggle flat/smooth shade mode.
   l     Toggle lighting mode.
   d     Toggle depth checking mode.
   SPACE Toggle step/animation mode.
   n     Single step in step mode.
   a     Toggle spin mode.


TACCUM.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_1(void );
static void CALLBACK Key_2(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum doubleBuffer, directRender;
GLint thing1, thing2;

static void Init(void)
{

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClearAccum(0.0, 0.0, 0.0, 0.0);

    thing1 = glGenLists(1);
    glNewList(thing1, GL_COMPILE);
    glColor3f(1.0, 0.0, 0.0);
    glRectf(-1.0, -1.0, 1.0, 0.0);
    glEndList();

    thing2 = glGenLists(1);
    glNewList(thing2, GL_COMPILE);
    glColor3f(0.0, 1.0, 0.0);
    glRectf(0.0, -1.0, 1.0, 1.0);
    glEndList();
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

static void CALLBACK Key_1(void)
{
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

static void CALLBACK Key_2(void)
{
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}

static void CALLBACK Draw(void)
{

    glPushMatrix();

    glScalef(0.8, 0.8, 1.0);

    glClear(GL_COLOR_BUFFER_BIT);
    glCallList(thing1);
    glAccum(GL_LOAD, 0.5);

    glClear(GL_COLOR_BUFFER_BIT);
    glCallList(thing2);
    glAccum(GL_ACCUM, 0.5);

    glAccum(GL_RETURN, 1.0);

    glPopMatrix();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    auxInitPosition(0, 0, 300, 300);

    type = AUX_RGB | AUX_ACCUM;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Accum Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_1, Key_1);
    auxKeyFunc(AUX_2, Key_2);
    auxMainLoop(Draw);
}


TBITMAP1.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

#define OPENGL_WIDTH 24
#define OPENGL_HEIGHT 13

static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum rgb, doubleBuffer, windType;

float boxA[3] = {
    0, 0, 0
};
float boxB[3] = {
    -100, 0, 0
};
float boxC[3] = {
    100, 0, 0
};
float boxD[3] = {
    0, 95, 0
};
float boxE[3] = {
    0, -105, 0
};
GLubyte OpenGL_bits1[] = {
   0x00, 0x03, 0x00,
   0x7f, 0xfb, 0xff,
   0x7f, 0xfb, 0xff,
   0x00, 0x03, 0x00,
   0x3e, 0x8f, 0xb7,
   0x63, 0xdb, 0xb0,
   0x63, 0xdb, 0xb7,
   0x63, 0xdb, 0xb6,
   0x63, 0x8f, 0xf3,
   0x63, 0x00, 0x00,
   0x63, 0x00, 0x00,
   0x63, 0x00, 0x00,
   0x3e, 0x00, 0x00,
};
GLubyte OpenGL_bits2[] = {
   0x00, 0x00, 0x00,
   0xff, 0xff, 0x01,
   0xff, 0xff, 0x01,
   0x00, 0x00, 0x00,
   0xf9, 0xfc, 0x01,
   0x8d, 0x0d, 0x00,
   0x8d, 0x0d, 0x00,
   0x8d, 0x0d, 0x00,
   0xcc, 0x0d, 0x00,
   0x0c, 0x4c, 0x0a,
   0x0c, 0x4c, 0x0e,
   0x8c, 0xed, 0x0e,
   0xf8, 0x0c, 0x00,
};
GLubyte logo_bits[] = {
   0x00, 0x66, 0x66,
   0xff, 0x66, 0x66,
   0x00, 0x00, 0x00,
   0xff, 0x3c, 0x3c,
   0x00, 0x42, 0x40,
   0xff, 0x42, 0x40,
   0x00, 0x41, 0x40,
   0xff, 0x21, 0x20,
   0x00, 0x2f, 0x20,
   0xff, 0x20, 0x20,
   0x00, 0x10, 0x90,
   0xff, 0x10, 0x90,
   0x00, 0x0f, 0x10,
   0xff, 0x00, 0x00,
   0x00, 0x66, 0x66,
   0xff, 0x66, 0x66,
};

static void Init(void)
{

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClearIndex(0.0);
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-175, 175, -175, 175);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Draw(void)
{
    float mapI[2], mapIA[2], mapIR[2];

    glClear(GL_COLOR_BUFFER_BIT);

    mapI[0] = 0.0;
    mapI[1] = 1.0;
    mapIR[0] = 0.0;
    mapIR[1] = 0.0;
    mapIA[0] = 1.0;
    mapIA[1] = 1.0;

    glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 2, mapIR);
    glPixelMapfv(GL_PIXEL_MAP_I_TO_G, 2, mapI);
    glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 2, mapI);
    glPixelMapfv(GL_PIXEL_MAP_I_TO_A, 2, mapIA);
    glPixelTransferi(GL_MAP_COLOR, GL_TRUE);

    AUX_SETCOLOR(windType, AUX_WHITE);
    glRasterPos3fv(boxA);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, 24);
    glPixelStorei(GL_UNPACK_SKIP_PIXELS, 8);
    glPixelStorei(GL_UNPACK_SKIP_ROWS, 2);
    glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glBitmap(16, 12, 8.0, 0.0, 0.0, 0.0, logo_bits);

    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
    glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
    glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    AUX_SETCOLOR(windType, AUX_WHITE);
    glRasterPos3fv(boxB);
    glBitmap(OPENGL_WIDTH, OPENGL_HEIGHT, OPENGL_WIDTH, 0.0, OPENGL_WIDTH, 0.0,
         OpenGL_bits1);
    glBitmap(OPENGL_WIDTH, OPENGL_HEIGHT, OPENGL_WIDTH, 0.0, OPENGL_WIDTH, 0.0,
         OpenGL_bits2);

    AUX_SETCOLOR(windType, AUX_YELLOW);
    glRasterPos3fv(boxC);
    glBitmap(OPENGL_WIDTH, OPENGL_HEIGHT, OPENGL_WIDTH, 0.0, OPENGL_WIDTH, 0.0,
         OpenGL_bits1);
    glBitmap(OPENGL_WIDTH, OPENGL_HEIGHT, OPENGL_WIDTH, 0.0, OPENGL_WIDTH, 0.0,
         OpenGL_bits2);

    AUX_SETCOLOR(windType, AUX_CYAN);
    glRasterPos3fv(boxD);
    glBitmap(OPENGL_WIDTH, OPENGL_HEIGHT, OPENGL_WIDTH, 0.0, OPENGL_WIDTH, 0.0,
         OpenGL_bits1);
    glBitmap(OPENGL_WIDTH, OPENGL_HEIGHT, OPENGL_WIDTH, 0.0, OPENGL_WIDTH, 0.0,
         OpenGL_bits2);

    AUX_SETCOLOR(windType, AUX_RED);
    glRasterPos3fv(boxE);
    glBitmap(OPENGL_WIDTH, OPENGL_HEIGHT, OPENGL_WIDTH, 0.0, OPENGL_WIDTH, 0.0,
         OpenGL_bits1);
    glBitmap(OPENGL_WIDTH, OPENGL_HEIGHT, OPENGL_WIDTH, 0.0, OPENGL_WIDTH, 0.0,
         OpenGL_bits2);

    glFlush();

    if (doubleBuffer) {
        auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-ci") == 0) {
        rgb = GL_FALSE;
    } else if (strcmp(argv[i], "-rgb") == 0) {
        rgb = GL_TRUE;
    } else if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{

    if (Args(argc, argv) == GL_FALSE) {
        auxQuit();
    }

    auxInitPosition(0, 0, 300, 300);

    windType = (rgb) ? AUX_RGB : AUX_INDEX;
    windType |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(windType);

    if (auxInitWindow("Bitmap Test") == GL_FALSE) {
        auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxMainLoop(Draw);
}


TCOPY.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_Z(void );
static void CALLBACK Key_z(void );
static GLenum CALLBACK Mouse_left(struct _AUX_EVENTREC *event);
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum doubleBuffer;
GLint windW, windH;

char *fileName = 0;
AUX_RGBImageRec *xximage;
float point[3];
float zoom;
GLint x, y;

static void Init(void)
{

    glClearColor(0.0, 0.0, 0.0, 0.0);

    x = 0;
    y = windH;
    zoom = 1.8;
}

static void CALLBACK Reshape(int width, int height)
{

    windW = (GLint)width;
    windH = (GLint)height;

    glViewport(0, 0, windW, windH);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, windW, 0, windH);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Key_Z(void)
{
    zoom += 0.2;
}

static void CALLBACK Key_z(void)
{
    zoom -= 0.2;
    if (zoom < 0.2) {
        zoom = 0.2;
    }
}

static GLenum CALLBACK Mouse_left(AUX_EVENTREC *event)
{

    x = event->data[AUX_MOUSEX];
    y = event->data[AUX_MOUSEY];
    return GL_TRUE;
}

static void CALLBACK Draw(void)
{

    glClear(GL_COLOR_BUFFER_BIT);

    point[0] = (windW / 2) - (xximage->sizeX / 2);
    point[1] = (windH / 2) - (xximage->sizeY / 2);
    point[2] = 0;
    glRasterPos3fv(point);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glPixelZoom(1.0, 1.0);
    glDrawPixels(xximage->sizeX, xximage->sizeY, GL_RGB, GL_UNSIGNED_BYTE,
         xximage->data);

    point[0] = (float)x;
    point[1] = windH - (float)y;
    point[2] = 0.0;
    glRasterPos3fv(point);

    glPixelZoom(zoom, zoom);
    glCopyPixels((windW/2)-(xximage->sizeX/2),
         (windH/2)-(xximage->sizeY/2),
         xximage->sizeX, xximage->sizeY, GL_COLOR);

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else if (strcmp(argv[i], "-f") == 0) {
        if (i+1 >= argc || argv[i+1][0] == '-') {
        //printf("-f (No file name).\n");
        return GL_FALSE;
        } else {
        fileName = argv[++i];
        }
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    if (fileName == 0) {
    //printf("No image file.\n");
    auxQuit();
    }

    xximage = auxRGBImageLoad(fileName);

    windW = 300;
    windH = 300;
    auxInitPosition(0, 0, windW, windH);

    type = AUX_RGB;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Copy Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_Z, Key_Z);
    auxKeyFunc(AUX_z, Key_z);
    auxMouseFunc(AUX_LEFTBUTTON, AUX_MOUSEDOWN, Mouse_left);
    auxMainLoop(Draw);
}


TDEPTH.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

#define CI_OFFSET_1 32
#define CI_OFFSET_2 48

static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_1(void );
static void CALLBACK Key_2(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum rgb, doubleBuffer, directRender;

GLenum antiAlias, stipple;
GLubyte stippleBits[32*4] = {
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
};

static void Init(void)
{
    GLint i;

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClearIndex(0.0);

    if (!rgb) {
    for (i = 0; i < 16; i++) {
        auxSetOneColor(i+CI_OFFSET_1, 0.0, 0.0, i/15.0);
        auxSetOneColor(i+CI_OFFSET_2, 0.0, i/15.0, 0.0);
    }
    }

    antiAlias = GL_FALSE;
    stipple = GL_FALSE;
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.0, 1.0, -1.0, 1.0, -0.5, 1000.0);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Key_1(void)
{
    antiAlias = !antiAlias;
}

static void CALLBACK Key_2(void)
{
    stipple = !stipple;
}

static void CALLBACK Draw(void)
{
    GLint ci1, ci2;
    float blue[3] = { 0.0, 0.0, 1.0 };
    float green[3] = { 0.0, 1.0, 0.0 };

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    if (antiAlias) {
    ci1 = CI_OFFSET_1;
    ci2 = CI_OFFSET_2;
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glEnable(GL_BLEND);
    glEnable(GL_POLYGON_SMOOTH);
    glDisable(GL_DEPTH_TEST);
    } else {
    ci1 = AUX_BLUE;
    ci2 = AUX_GREEN;
    glDisable(GL_BLEND);
    glDisable(GL_POLYGON_SMOOTH);
    glEnable(GL_DEPTH_TEST);
    }

    if (stipple) {
    glPolygonStipple(stippleBits);
    glEnable(GL_POLYGON_STIPPLE);
    } else {
    glDisable(GL_POLYGON_STIPPLE);
    }

    glBegin(GL_TRIANGLES);
    (rgb) ? glColor3fv(blue) : glIndexi(ci1);
    glVertex3f( 0.9, -0.9, -30.0);
    glVertex3f( 0.9,  0.9, -30.0);
    glVertex3f(-0.9,  0.0, -30.0);
    (rgb) ? glColor3fv(green) : glIndexi(ci2);
    glVertex3f(-0.9, -0.9, -40.0);
    glVertex3f(-0.9,  0.9, -40.0);
    glVertex3f( 0.9,  0.0, -25.0);
    glEnd();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_FALSE;
    directRender = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-ci") == 0) {
        rgb = GL_FALSE;
    } else if (strcmp(argv[i], "-rgb") == 0) {
        rgb = GL_TRUE;
    } else if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else if (strcmp(argv[i], "-dr") == 0) {
        directRender = GL_TRUE;
    } else if (strcmp(argv[i], "-ir") == 0) {
        directRender = GL_FALSE;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    auxInitPosition(0, 0, 300, 300);

    type = AUX_DEPTH16;
    type |= (rgb) ? AUX_RGB : AUX_INDEX;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;
    auxInitDisplayMode(type);

    if (auxInitWindow("Depth Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_1, Key_1);
    auxKeyFunc(AUX_2, Key_2);
    auxMainLoop(Draw);
}


TEVAL.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <gl\glaux.h>

#define VORDER 10
#define CORDER 10
#define TORDER 3

#define VMAJOR_ORDER 2
#define VMINOR_ORDER 3

#define CMAJOR_ORDER 2
#define CMINOR_ORDER 2

#define TMAJOR_ORDER 2
#define TMINOR_ORDER 2

#define VDIM 4
#define CDIM 4
#define TDIM 2

#define ONE_D 1
#define TWO_D 2

#define EVAL 3
#define MESH 4

static void Init(void );
static void DrawPoints1(void );
static void DrawPoints2(void );
static void DrawMapEval1(float du);
static void DrawMapEval2(float du,float dv);
static void RenderEval(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_left(void );
static void CALLBACK Key_right(void );
static void CALLBACK Key_up(void );
static void CALLBACK Key_down(void );
static void CALLBACK Key_1(void );
static void CALLBACK Key_2(void );
static void CALLBACK Key_e(void );
static void CALLBACK Key_m(void );
static void CALLBACK Key_f(void );
static void CALLBACK Key_p(void );
static void CALLBACK Key_c(void );
static void CALLBACK Key_t(void );
static void CALLBACK Key_l(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum doubleBuffer;

float rotX = 0.0, rotY = 0.0, translateZ = -1.0;

GLenum arrayType = ONE_D;
GLenum colorType = GL_FALSE;
GLenum textureType = GL_FALSE;
GLenum polygonFilled = GL_FALSE;
GLenum lighting = GL_FALSE;
GLenum mapPoint = GL_FALSE;
GLenum mapType = EVAL;

double point1[10*4] = {
    -0.5, 0.0, 0.0, 1.0,
    -0.4, 0.5, 0.0, 1.0,
    -0.3,-0.5, 0.0, 1.0,
    -0.2, 0.5, 0.0, 1.0,
    -0.1,-0.5, 0.0, 1.0,
    0.0, 0.5, 0.0, 1.0,
    0.1,-0.5, 0.0, 1.0,
    0.2, 0.5, 0.0, 1.0,
    0.3,-0.5, 0.0, 1.0,
    0.4, 0.0, 0.0, 1.0,
};
double cpoint1[10*4] = {
    0.0, 0.0, 1.0, 1.0,
    0.3, 0.0, 0.7, 1.0,
    0.6, 0.0, 0.3, 1.0,
    1.0, 0.0, 0.0, 1.0,
    1.0, 0.3, 0.0, 1.0,
    1.0, 0.6, 0.0, 1.0,
    1.0, 1.0, 0.0, 1.0,
    1.0, 1.0, 0.5, 1.0,
    1.0, 1.0, 1.0, 1.0,
};
double tpoint1[11*4] = {
    0.0, 0.0, 0.0, 1.0,
    0.0, 0.1, 0.0, 1.0,
    0.0, 0.2, 0.0, 1.0,
    0.0, 0.3, 0.0, 1.0,
    0.0, 0.4, 0.0, 1.0,
    0.0, 0.5, 0.0, 1.0,
    0.0, 0.6, 0.0, 1.0,
    0.0, 0.7, 0.0, 1.0,
    0.0, 0.8, 0.0, 1.0,
    0.0, 0.9, 0.0, 1.0,
};
double point2[2*3*4] = {
    -0.5, -0.5,  0.5, 1.0,
     0.0,  1.0,  0.5, 1.0,
     0.5, -0.5,  0.5, 1.0,
    -0.5,  0.5, -0.5, 1.0,
     0.0, -1.0, -0.5, 1.0,
     0.5,  0.5, -0.5, 1.0,
};
double cpoint2[2*2*4] = {
    0.0, 0.0, 0.0, 1.0,
    0.0, 0.0, 1.0, 1.0,
    0.0, 1.0, 0.0, 1.0,
    1.0, 1.0, 1.0, 1.0,
};
double tpoint2[2*2*2] = {
    0.0, 0.0, 0.0, 1.0,
    1.0, 0.0, 1.0, 1.0,
};
float textureImage[4*2*4] =  {
    1.0, 1.0, 1.0, 1.0,
    1.0, 0.0, 0.0, 1.0,
    1.0, 0.0, 0.0, 1.0,
    1.0, 1.0, 1.0, 1.0,
    1.0, 1.0, 1.0, 1.0,
    1.0, 0.0, 0.0, 1.0,
    1.0, 0.0, 0.0, 1.0,
    1.0, 1.0, 1.0, 1.0,
};

static void Init(void)
{
    static float ambient[] = {0.1, 0.1, 0.1, 1.0};
    static float diffuse[] = {1.0, 1.0, 1.0, 1.0};
    static float position[] = {0.0, 0.0, -150.0, 0.0};
    static float front_mat_shininess[] = {30.0};
    static float front_mat_specular[] = {0.7, 0.7, 0.7, 1.0};
    static float front_mat_diffuse[] = {1.0, 0.2, 1.0, 1.0};
    static float back_mat_shininess[] = {50.0};
    static float back_mat_specular[] = {0.5, 0.5, 0.2, 1.0};
    static float back_mat_diffuse[] = {1.0, 1.0, 0.2, 1.0};
    static float lmodel_ambient[] = {1.0, 1.0, 1.0, 1.0};
    static float lmodel_twoside[] = {GL_TRUE};
    static float decal[] = {GL_DECAL};
    static float modulate[] = {GL_MODULATE};
    static float repeat[] = {GL_REPEAT};
    static float clamp[] = {GL_CLAMP};
    static float nr[] = {GL_NEAREST};
    static float ln[] = {GL_LINEAR};

    glFrontFace(GL_CCW);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);

    glMap1d(GL_MAP1_VERTEX_4, 0.0, 1.0, VDIM, VORDER, point1);
    glMap1d(GL_MAP1_COLOR_4, 0.0, 1.0, CDIM, CORDER, cpoint1);

    glMap2d(GL_MAP2_VERTEX_4, 0.0, 1.0, VMINOR_ORDER*VDIM, VMAJOR_ORDER, 0.0,
        1.0, VDIM, VMINOR_ORDER, point2);
    glMap2d(GL_MAP2_COLOR_4, 0.0, 1.0, CMINOR_ORDER*CDIM, CMAJOR_ORDER, 0.0,
        1.0, CDIM, CMINOR_ORDER, cpoint2);
    glMap2d(GL_MAP2_TEXTURE_COORD_2, 0.0, 1.0, TMINOR_ORDER*TDIM,
        TMAJOR_ORDER, 0.0, 1.0, TDIM, TMINOR_ORDER, tpoint2);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);

    glMaterialfv(GL_FRONT, GL_DIFFUSE, front_mat_diffuse);
    glMaterialfv(GL_BACK, GL_DIFFUSE, back_mat_diffuse);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);

    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, decal);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, nr);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, nr);
    glTexImage2D(GL_TEXTURE_2D, 0, 4, 2, 4, 0, GL_RGBA, GL_FLOAT,
         (GLvoid *)textureImage);
}

static void DrawPoints1(void)
{
    GLint i;

    glColor3f(0.0, 1.0, 0.0);
    glPointSize(2);
    glBegin(GL_POINTS);
    for (i = 0; i < VORDER; i++) {
        glVertex4dv(&point1[i*4]);
    }
    glEnd();
}

static void DrawPoints2(void)
{
    GLint i, j;

    glColor3f(1.0, 0.0, 1.0);
    glPointSize(2);
    glBegin(GL_POINTS);
    for (i = 0; i < VMAJOR_ORDER; i++) {
        for (j = 0; j < VMINOR_ORDER; j++) {
        glVertex4dv(&point2[i*4*VMINOR_ORDER+j*4]);
        }
    }
    glEnd();
}

static void DrawMapEval1(float du)
{
    float u;

    glColor3f(1.0, 0.0, 0.0);
    glBegin(GL_LINE_STRIP);
    for (u = 0.0; u < 1.0; u += du) {
        glEvalCoord1d(u);
    }
    glEvalCoord1d(1.0);
    glEnd();
}

static void DrawMapEval2(float du, float dv)
{
    float u, v, tmp;

    glColor3f(1.0, 0.0, 0.0);
    for (v = 0.0; v < 1.0; v += dv) {
    glBegin(GL_QUAD_STRIP);
        for (u = 0.0; u <= 1.0; u += du) {
        glEvalCoord2d(u,v);
        tmp = (v + dv < 1.0) ? (v + dv) : 1.0;
        glEvalCoord2d(u, tmp);
        }
        glEvalCoord2d(1.0, v);
        glEvalCoord2d(1.0, v+dv);
    glEnd();
    }
}

static void RenderEval(void)
{

    if (colorType) {
    glEnable(GL_MAP1_COLOR_4);
    glEnable(GL_MAP2_COLOR_4);
    } else {
    glDisable(GL_MAP1_COLOR_4);
    glDisable(GL_MAP2_COLOR_4);
    }

    if (textureType) {
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_MAP2_TEXTURE_COORD_2);
    } else {
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_MAP2_TEXTURE_COORD_2);
    }

    if (polygonFilled) {
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    } else {
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }

    glShadeModel(GL_SMOOTH);

    switch (mapType) {
      case EVAL:
    switch (arrayType) {
      case ONE_D:
        glDisable(GL_MAP2_VERTEX_4);
        glEnable(GL_MAP1_VERTEX_4);
        DrawPoints1();
        DrawMapEval1(0.1/VORDER);
        break;
      case TWO_D:
        glDisable(GL_MAP1_VERTEX_4);
        glEnable(GL_MAP2_VERTEX_4);
        DrawPoints2();
        DrawMapEval2(0.1/VMAJOR_ORDER,0.1/VMINOR_ORDER);
        break;
    }
    break;
      case MESH:
    switch (arrayType) {
      case ONE_D:
        DrawPoints1();
        glDisable(GL_MAP2_VERTEX_4);
        glEnable (GL_MAP1_VERTEX_4);
        glColor3f(0.0, 0.0, 1.0);
        glMapGrid1d(40, 0.0, 1.0);
        if (mapPoint) {
        glPointSize(2);
        glEvalMesh1(GL_POINT, 0, 40);
        } else {
        glEvalMesh1(GL_LINE, 0, 40);
        }
        break;
      case TWO_D:
        DrawPoints2();
        glDisable(GL_MAP1_VERTEX_4);
        glEnable(GL_MAP2_VERTEX_4);
        glColor3f(0.0, 0.0, 1.0);
        glMapGrid2d(20, 0.0, 1.0, 20, 0.0, 1.0);
        if (mapPoint) {
        glPointSize(2);
        glEvalMesh2(GL_POINT, 0, 20, 0, 20);
        } else if (polygonFilled) {
        glEvalMesh2(GL_FILL, 0, 20, 0, 20);
        } else {
        glEvalMesh2(GL_LINE, 0, 20, 0, 20);
        }
        break;
    }
    break;
    }
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.0, 1.0, -1.0, 1.0, -0.5, 10.0);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Key_left(void)
{
    rotY -= 30;
}

static void CALLBACK Key_right(void)
{
    rotY += 30;
}

static void CALLBACK Key_up(void)
{
    rotX -= 30;
}

static void CALLBACK Key_down(void)
{
    rotX += 30;
}

static void CALLBACK Key_1(void)
{
    arrayType = ONE_D;
}

static void CALLBACK Key_2(void)
{
    arrayType = TWO_D;
}

static void CALLBACK Key_e(void)
{
    mapType = EVAL;
}

static void CALLBACK Key_m(void)
{
    mapType = MESH;
}

static void CALLBACK Key_f(void)
{
    polygonFilled = !polygonFilled;
}

static void CALLBACK Key_p(void)
{
    mapPoint = !mapPoint;
}

static void CALLBACK Key_c(void)
{
    colorType = !colorType;
}

static void CALLBACK Key_t(void)
{
    textureType = !textureType;
}

static void CALLBACK Key_l(void)
{
    lighting =! lighting;
    if (lighting) {
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        glEnable(GL_AUTO_NORMAL);
    } else {
        glDisable(GL_LIGHTING);
        glDisable(GL_LIGHT0);
        glDisable(GL_AUTO_NORMAL);
    }
}

static void CALLBACK Draw(void)
{

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glPushMatrix();

    glTranslatef(0.0, 0.0 , translateZ);
    glRotatef(rotX, 1, 0, 0);
    glRotatef(rotY, 0, 1, 0);
    RenderEval();

    glPopMatrix();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    auxInitPosition(0, 0, 300, 300);

    type = AUX_RGB | AUX_DEPTH16;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Evaluator Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_LEFT, Key_left);
    auxKeyFunc(AUX_RIGHT, Key_right);
    auxKeyFunc(AUX_UP, Key_up);
    auxKeyFunc(AUX_DOWN, Key_down);
    auxKeyFunc(AUX_1, Key_1);
    auxKeyFunc(AUX_2, Key_2);
    auxKeyFunc(AUX_e, Key_e);
    auxKeyFunc(AUX_m, Key_m);
    auxKeyFunc(AUX_f, Key_f);
    auxKeyFunc(AUX_p, Key_p);
    auxKeyFunc(AUX_c, Key_c);
    auxKeyFunc(AUX_t, Key_t);
    auxKeyFunc(AUX_l, Key_l);
    auxMainLoop(Draw);
}


TFOG.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

static void Build_lists(void );
static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_UP(void );
static void CALLBACK Key_DOWN(void );
static void CALLBACK Key_LEFT(void );
static void CALLBACK Key_RIGHT(void );
static void CALLBACK Key_D(void );
static void CALLBACK Key_d(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum rgb, doubleBuffer;

double plane[4] = {
    1.0, 0.0, -1.0, 0.0
};
float rotX = 5.0, rotY = -5.0, zTranslate = -65.0;
float fogDensity = 0.02;
GLint cubeList = 1;

float scp[18][3] = {
    {
    1.000000, 0.000000, 0.000000
    },
    {
    1.000000, 0.000000, 5.000000
    },
    {
    0.707107, 0.707107, 0.000000
    },
    {
    0.707107, 0.707107, 5.000000
    },
    {
    0.000000, 1.000000, 0.000000
    },
    {
    0.000000, 1.000000, 5.000000
    },
    {
    -0.707107, 0.707107, 0.000000
    },
    {
    -0.707107, 0.707107, 5.000000
    },
    {
    -1.000000, 0.000000, 0.000000
    },
    {
    -1.000000, 0.000000, 5.000000
    },
    {
    -0.707107, -0.707107, 0.000000
    },
    {
    -0.707107, -0.707107, 5.000000
    },
    {
    0.000000, -1.000000, 0.000000
    },
    {
    0.000000, -1.000000, 5.000000
    },
    {
    0.707107, -0.707107, 0.000000
    },
    {
    0.707107, -0.707107, 5.000000
    },
    {
    1.000000, 0.000000, 0.000000
    },
    {
    1.000000, 0.000000, 5.000000
    },
};

static void Build_lists(void)
{

    glNewList(cubeList, GL_COMPILE);
    glBegin(GL_TRIANGLE_STRIP);
       glNormal3fv(scp[0]); glVertex3fv(scp[0]);
       glNormal3fv(scp[0]); glVertex3fv(scp[1]);
       glNormal3fv(scp[2]); glVertex3fv(scp[2]);
       glNormal3fv(scp[2]); glVertex3fv(scp[3]);
       glNormal3fv(scp[4]); glVertex3fv(scp[4]);
       glNormal3fv(scp[4]); glVertex3fv(scp[5]);
       glNormal3fv(scp[6]); glVertex3fv(scp[6]);
       glNormal3fv(scp[6]); glVertex3fv(scp[7]);
       glNormal3fv(scp[8]); glVertex3fv(scp[8]);
       glNormal3fv(scp[8]); glVertex3fv(scp[9]);
       glNormal3fv(scp[10]); glVertex3fv(scp[10]);
       glNormal3fv(scp[10]); glVertex3fv(scp[11]);
       glNormal3fv(scp[12]); glVertex3fv(scp[12]);
       glNormal3fv(scp[12]); glVertex3fv(scp[13]);
       glNormal3fv(scp[14]); glVertex3fv(scp[14]);
       glNormal3fv(scp[14]); glVertex3fv(scp[15]);
       glNormal3fv(scp[16]); glVertex3fv(scp[16]);
       glNormal3fv(scp[16]); glVertex3fv(scp[17]);
    glEnd();
    glEndList();
}

static void Init(void)
{
    static float ambient[] = {0.1, 0.1, 0.1, 1.0};
    static float diffuse[] = {1.0, 1.0, 1.0, 1.0};
    static float position[] = {90.0, 90.0, 0.0, 0.0};
    static float front_mat_shininess[] = {30.0};
    static float front_mat_specular[] = {0.0, 0.0, 0.0, 1.0};
    static float front_mat_diffuse[] = {0.0, 1.0, 0.0, 1.0};
    static float back_mat_shininess[] = {50.0};
    static float back_mat_specular[] = {0.0, 0.0, 1.0, 1.0};
    static float back_mat_diffuse[] = {1.0, 0.0, 0.0, 1.0};
    static float lmodel_ambient[] = {0.0, 0.0, 0.0, 1.0};
    static float fog_color[] = {0.8, 0.8, 0.8, 1.0};

    glFrontFace(GL_CW);

    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glMaterialfv(GL_FRONT, GL_SHININESS, front_mat_shininess);
    glMaterialfv(GL_FRONT, GL_SPECULAR, front_mat_specular);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, front_mat_diffuse);
    glMaterialfv(GL_BACK, GL_SHININESS, back_mat_shininess);
    glMaterialfv(GL_BACK, GL_SPECULAR, back_mat_specular);
    glMaterialfv(GL_BACK, GL_DIFFUSE, back_mat_diffuse);

    glEnable(GL_FOG);
    glFogi(GL_FOG_MODE, GL_EXP);
    glFogf(GL_FOG_DENSITY, fogDensity);
    if (rgb) {
    glFogfv(GL_FOG_COLOR, fog_color);
    glClearColor(0.8, 0.8, 0.8, 1.0);
    } else {
    glFogi(GL_FOG_INDEX, 1<<5);
    auxSetFogRamp(5, 3);
    glClearIndex(128);
    }

    Build_lists();
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, 1.0, 1.0, 200.0);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Key_UP(void)
{
    rotX -= 5;
}

static void CALLBACK Key_DOWN(void)
{
    rotX += 5;
}

static void CALLBACK Key_LEFT(void)
{
    rotY -= 5;
}

static void CALLBACK Key_RIGHT(void)
{
    rotY += 5;
}

static void CALLBACK Key_D(void)
{
    if (rgb) {
        fogDensity *= 1.10;
        glFogf(GL_FOG_DENSITY, fogDensity);
    }
}

static void CALLBACK Key_d(void)
{
    if (rgb) {
        fogDensity /= 1.10;
        glFogf(GL_FOG_DENSITY, fogDensity);
    }
}

static void CALLBACK Draw(void)
{

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glPushMatrix();

    glTranslatef(0, 0, zTranslate);
    glRotatef(rotY, 0,1,0);
    glRotatef(rotX, 1,0,0);
    glScalef(1.0, 1.0, 10.0);

    glCallList(cubeList);

    glPopMatrix();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-ci") == 0) {
        rgb = GL_FALSE;
    } else if (strcmp(argv[i], "-rgb") == 0) {
        rgb = GL_TRUE;
    } else if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    auxInitPosition(0, 0, 300, 300);

    type = AUX_DEPTH16;
    type |= (rgb) ? AUX_RGB : AUX_INDEX;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Fog Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_UP, Key_UP);
    auxKeyFunc(AUX_DOWN, Key_DOWN);
    auxKeyFunc(AUX_LEFT, Key_LEFT);
    auxKeyFunc(AUX_RIGHT, Key_RIGHT);
    auxKeyFunc(AUX_D, Key_D);
    auxKeyFunc(AUX_d, Key_d);
    auxMainLoop(Draw);
}


TLINE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

#define CI_OFFSET 16

static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_1(void );
static void CALLBACK Key_2(void );
static void CALLBACK Key_W(void );
static void CALLBACK Key_w(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum rgb, doubleBuffer, windType;

GLenum mode1, mode2;
GLint size;
float pntA[3] = {
    -160.0, 0.0, 0.0
};
float pntB[3] = {
    -130.0, 0.0, 0.0
};
float pntC[3] = {
    -40.0, -50.0, 0.0
};
float pntD[3] = {
    30.0, 60.0, 0.0
};

static void Init(void)
{
    GLint i;

    glClearColor(0.0, 0.0, 0.0, 0.0);

    glLineStipple(1, 0xF0E0);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);

    if (!rgb) {
    for (i = 0; i < 16; i++) {
        auxSetOneColor(i+CI_OFFSET, i/15.0, i/15.0, 0.0);
    }
    }

    mode1 = GL_FALSE;
    mode2 = GL_FALSE;
    size = 1;
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-175, 175, -175, 175);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Key_1(void)
{
    mode1 = !mode1;
}

static void CALLBACK Key_2(void)
{
    mode2 = !mode2;
}

static void CALLBACK Key_W(void)
{
    size++;
}

static void CALLBACK Key_w(void)
{
    size--;
}

static void CALLBACK Draw(void)
{
    GLint ci, i;

    glClear(GL_COLOR_BUFFER_BIT);

    glLineWidth(size);

    if (mode1) {
    glEnable(GL_LINE_STIPPLE);
    } else {
    glDisable(GL_LINE_STIPPLE);
    }

    if (mode2) {
    ci = CI_OFFSET;
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    } else {
    ci = AUX_YELLOW;
    glDisable(GL_LINE_SMOOTH);
    glDisable(GL_BLEND);
    }

    glPushMatrix();

    for (i = 0; i < 360; i += 5) {
    glRotatef(5.0, 0,0,1);

    (rgb) ? glColor3f(1.0, 1.0, 0.0) : glIndexi(ci);
    glBegin(GL_LINE_STRIP);
        glVertex3fv(pntA);
        glVertex3fv(pntB);
    glEnd();

    glPointSize(1);

    AUX_SETCOLOR(windType, AUX_GREEN);
    glBegin(GL_POINTS);
        glVertex3fv(pntA);
        glVertex3fv(pntB);
    glEnd();
    }

    glPopMatrix();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-ci") == 0) {
        rgb = GL_FALSE;
    } else if (strcmp(argv[i], "-rgb") == 0) {
        rgb = GL_TRUE;
    } else if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    auxInitPosition(0, 0, 300, 300);

    windType |= (rgb) ? AUX_RGB : AUX_INDEX;
    windType |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(windType);

    if (auxInitWindow("Line Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_1, Key_1);
    auxKeyFunc(AUX_2, Key_2);
    auxKeyFunc(AUX_W, Key_W);
    auxKeyFunc(AUX_w, Key_w);
    auxMainLoop(Draw);
}


TLOGO.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

#define PI 3.141592654

#define BLACK 0
#define GRAY 128
#define WHITE 255
#define BL 0x00
#define WH 0xFF
#define RD 0xA40000FF
#define WT 0xFFFFFFFF

#define CHECKIMAGEWIDTH 8
#define CHECKIMAGEHEIGHT 8
#define BRICKIMAGEWIDTH 16
#define BRICKIMAGEHEIGHT 16

static void SetUpAntiAliasedGrayScale(void );
static void BendForward(void );
static void BendLeft(void );
static void BendRight(void );
static void BuildSingleCylinder(void );
static void BuildDoubleCylinder(void );
static void BuildElbow(void );
static void BuildLogo(void );
static void BuildLists(void );
static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_LEFT(void );
static void CALLBACK Key_RIGHT(void );
static void CALLBACK Key_UP(void );
static void CALLBACK Key_DOWN(void );
static void CALLBACK Key_Z(void );
static void CALLBACK Key_z(void );
static void CALLBACK Key_1(void );
static void CALLBACK Key_2(void );
static void CALLBACK Key_3(void );
static void CALLBACK Key_p(void );
static void CALLBACK Key_4(void );
static void CALLBACK Key_5(void );
static void CALLBACK Key_6(void );
static void CALLBACK Key_8(void );
static void CALLBACK Key_9(void );
static void CALLBACK Key_0(void );
static void CALLBACK Key_q(void );
static void CALLBACK Key_w(void );
static void CALLBACK Key_e(void );
static void CALLBACK Key_r(void );
static void CALLBACK Key_t(void );
static void CALLBACK Key_y(void );
static void CALLBACK Key_u(void );
static void CALLBACK Key_a(void );
static void CALLBACK Key_s(void );
static void CALLBACK Key_d(void );
static void CALLBACK Key_f(void );
static void CALLBACK Key_g(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum rgb, doubleBuffer, directRender;

float black[3] = {0.0, 0.0, 0.0};
float white[3] = {1.0, 1.0, 1.0};
float gray[3] = {0.5, 0.5, 0.5};
float blue[3] = {0.0, 0.0, 1.0};
GLint colorIndexes[3] = {0, 200, 255};

GLenum polyMode;
GLenum dithering;
GLenum shade;
GLenum doStipple;

double plane[4] = {1.0, 0.0, -1.0, 0.0};
float xRotation = 30.0, yRotation = 30.0;
float zTranslation = -15.0;

GLint singleCylinder;
GLint doubleCylinder;
GLint elbow, logo;

GLubyte checkImage[3*CHECKIMAGEWIDTH*CHECKIMAGEHEIGHT] = {
    BL, BL, BL, WH, WH, WH, BL, BL, BL, WH, WH, WH, BL, BL, BL, WH,
    WH, WH, BL, BL, BL, WH, WH, WH, WH, WH, WH, BL, BL, BL, WH, WH,
    WH, BL, BL, BL, WH, WH, WH, BL, BL, BL, WH, WH, WH, BL, BL, BL,
    BL, BL, BL, WH, WH, WH, BL, BL, BL, WH, WH, WH, BL, BL, BL, WH,
    WH, WH, BL, BL, BL, WH, WH, WH, WH, WH, WH, BL, BL, BL, WH, WH,
    WH, BL, BL, BL, WH, WH, WH, BL, BL, BL, WH, WH, WH, BL, BL, BL,
    BL, BL, BL, WH, WH, WH, BL, BL, BL, WH, WH, WH, BL, BL, BL, WH,
    WH, WH, BL, BL, BL, WH, WH, WH, WH, WH, WH, BL, BL, BL, WH, WH,
    WH, BL, BL, BL, WH, WH, WH, BL, BL, BL, WH, WH, WH, BL, BL, BL,
    BL, BL, BL, WH, WH, WH, BL, BL, BL, WH, WH, WH, BL, BL, BL, WH,
    WH, WH, BL, BL, BL, WH, WH, WH, WH, WH, WH, BL, BL, BL, WH, WH,
    WH, BL, BL, BL, WH, WH, WH, BL, BL, BL, WH, WH, WH, BL, BL, BL,
};
GLuint brickImage[BRICKIMAGEWIDTH*BRICKIMAGEHEIGHT] = {
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT,
    RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD
};

GLubyte *image = checkImage;
GLint imageHeight = CHECKIMAGEHEIGHT;
GLint imageWidth = CHECKIMAGEWIDTH;

float decal[] = {
    GL_DECAL,
};
float modulate[] = {
    GL_MODULATE,
};
float repeat[] = {
    GL_REPEAT,
};
float nearest[] = {
    GL_NEAREST,
};

GLubyte stipple[4*32] = {
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,

    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,

    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,
    0x00, 0x0F, 0xF0, 0x00,

    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
};

float tscp[18][2] = {
    {
    0.0, 0.0
    },
    {
    1.0, 0.0
    },
    {
    0.0, 0.125
    },
    {
    1.0, 0.125
    },
    {
    0.0, 0.250
    },
    {
    1.0, 0.25
    },
    {
    0.0, 0.375
    },
    {
    1.0, 0.375
    },
    {
    0.0, 0.50
    },
    {
    1.0, 0.50
    },
    {
    0.0, 0.625
    },
    {
    1.0, 0.625
    },
    {
    0.0, 0.75
    },
    {
    1.0, 0.75
    },
    {
    0.0, 0.875
    },
    {
    1.0, 0.875
    },
    {
    0.0, 1.0
    },
    {
    1.0, 1.0
    }
};
float scp[18][3] = {
    {
    1.000000, 0.000000, 0.000000
    },
    {
    1.000000, 0.000000, 5.000000
    },
    {
    0.707107, 0.707107, 0.000000
    },
    {
    0.707107, 0.707107, 5.000000
    },
    {
    0.000000, 1.000000, 0.000000
    },
    {
    0.000000, 1.000000, 5.000000
    },
    {
    -0.707107, 0.707107, 0.000000
    },
    {
    -0.707107, 0.707107, 5.000000
    },
    {
    -1.000000, 0.000000, 0.000000
    },
    {
    -1.000000, 0.000000, 5.000000
    },
    {
    -0.707107, -0.707107, 0.000000
    },
    {
    -0.707107, -0.707107, 5.000000
    },
    {
    0.000000, -1.000000, 0.000000
    },
    {
    0.000000, -1.000000, 5.000000
    },
    {
    0.707107, -0.707107, 0.000000
    },
    {
    0.707107, -0.707107, 5.000000
    },
    {
    1.000000, 0.000000, 0.000000
    },
    {
    1.000000, 0.000000, 5.000000
    }
};
float dcp[18][3] = {
    {
    1.000000, 0.000000, 0.000000
    },
    {
    1.000000, 0.000000, 7.000000
    },
    {
    0.707107, 0.707107, 0.000000
    },
    {
    0.707107, 0.707107, 7.000000
    },
    {
    0.000000, 1.000000, 0.000000
    },
    {
    0.000000, 1.000000, 7.000000
    },
    {
    -0.707107, 0.707107, 0.000000
    },
    {
    -0.707107, 0.707107, 7.000000
    },
    {
    -1.000000, 0.000000, 0.000000
    },
    {
    -1.000000, 0.000000, 7.000000
    },
    {
    -0.707107, -0.707107, 0.000000
    },
    {
    -0.707107, -0.707107, 7.000000
    },
    {
    0.000000, -1.000000, 0.000000
    },
    {
    0.000000, -1.000000, 7.000000
    },
    {
    0.707107, -0.707107, 0.000000
    },
    {
    0.707107, -0.707107, 7.000000
    },
    {
    1.000000, 0.000000, 0.000000
    },
    {
    1.000000, 0.000000, 7.000000
    }
};
float ep[7][9][3] = {
    {
    {
        1.000000, 0.000000, 0.000000
    },
    {
        0.707107, 0.707107, 0.000000
    },
    {
        0.000000, 1.000000, 0.000000
    },
    {
        -0.707107, 0.707107, 0.000000
    },
    {
        -1.000000, 0.000000, 0.000000
    },
    {
        -0.707107, -0.707107, 0.000000
    },
    {
        0.000000, -1.000000, 0.000000
    },
    {
        0.707107, -0.707107, 0.000000
    },
    {
        1.000000, 0.000000, 0.000000
    }
    },
    {
    {
        1.000000, 0.034074, 0.258819
    },
    {
        0.707107, 0.717087, 0.075806
    },
    {
        0.000000, 1.000000, 0.000000
    },
    {
        -0.707107, 0.717087, 0.075806
    },
    {
        -1.000000, 0.034074, 0.258819
    },
    {
        -0.707107, -0.648939, 0.441832
    },
    {
        0.000000, -0.931852, 0.517638
    },
    {
        0.707107, -0.648939, 0.441832
    },
    {
        1.000000, 0.034074, 0.258819
    }
    },
    {
    {
        1.000000, 0.133975, 0.500000
    },
    {
        0.707107, 0.746347, 0.146447
    },
    {
        0.000000, 1.000000, 0.000000
    },
    {
        -0.707107, 0.746347, 0.146447
    },
    {
        -1.000000, 0.133975, 0.500000
    },
    {
        -0.707107, -0.478398, 0.853553
    },
    {
        0.000000, -0.732051, 1.000000
    },
    {
        0.707107, -0.478398, 0.853553
    },
    {
        1.000000, 0.133975, 0.500000
    }
    },
    {
    {
        1.000000, 0.292893, 0.707107
    },
    {
        0.707107, 0.792893, 0.207107
    },
    {
        0.000000, 1.000000, 0.000000
    },
    {
        -0.707107, 0.792893, 0.207107
    },
    {
        -1.000000, 0.292893, 0.707107
    },
    {
        -0.707107, -0.207107, 1.207107
    },
    {
        0.000000, -0.414214, 1.414214
    },
    {
        0.707107, -0.207107, 1.207107
    },
    {
        1.000000, 0.292893, 0.707107
    }
    },
    {
    {
        1.000000, 0.500000, 0.866025
    },
    {
        0.707107, 0.853553, 0.253653
    },
    {
        0.000000, 1.000000, 0.000000
    },
    {
        -0.707107, 0.853553, 0.253653
    },
    {
        -1.000000, 0.500000, 0.866025
    },
    {
        -0.707107, 0.146447, 1.478398
    },
    {
        0.000000, 0.000000, 1.732051
    },
    {
        0.707107, 0.146447, 1.478398
    },
    {
        1.000000, 0.500000, 0.866025
    }
    },
    {
    {
        1.000000, 0.741181, 0.965926
    },
    {
        0.707107, 0.924194, 0.282913
    },
    {
        0.000000, 1.000000, 0.000000
    },
    {
        -0.707107, 0.924194, 0.282913
    },
    {
        -1.000000, 0.741181, 0.965926
    },
    {
        -0.707107, 0.558168, 1.648939
    },
    {
        0.000000, 0.482362, 1.931852
    },
    {
        0.707107, 0.558168, 1.648939
    },
    {
        1.000000, 0.741181, 0.965926
    }
    },
    {
    {
        1.000000, 1.000000, 1.000000
    },
    {
        0.707107, 1.000000, 0.292893
    },
    {
        0.000000, 1.000000, 0.000000
    },
    {
        -0.707107, 1.000000, 0.292893
    },
    {
        -1.000000, 1.000000, 1.000000
    },
    {
        -0.707107, 1.000000, 1.707107
    },
    {
        0.000000, 1.000000, 2.000000
    },
    {
        0.707107, 1.000000, 1.707107
    },
    {
        1.000000, 1.000000, 1.000000
    }
    }
};
float en[7][9][3] = {
    {
    {
        1.000000, 0.000000, 0.000000
    },
    {
        0.707107, 0.707107, 0.000000
    },
    {
        0.000000, 1.000000, 0.000000
    },
    {
        -0.707107, 0.707107, 0.000000
    },
    {
        -1.000000, 0.000000, 0.000000
    },
    {
        -0.707107, -0.707107, 0.000000
    },
    {
        0.000000, -1.000000, 0.000000
    },
    {
        0.707107, -0.707107, 0.000000
    },
    {
        1.000000, 0.000000, 0.000000
    }
    },
    {
    {
        1.000000, 0.000000, 0.000000
    },
    {
        0.707107, 0.683013, -0.183013
    },
    {
        0.000000, 0.965926, -0.258819
    },
    {
        -0.707107, 0.683013, -0.183013
    },
    {
        -1.000000, 0.000000, 0.000000
    },
    {
        -0.707107, -0.683013, 0.183013
    },
    {
        0.000000, -0.965926, 0.258819
    },
    {
        0.707107, -0.683013, 0.183013
    },
    {
        1.000000, 0.000000, 0.000000
    }
    },
    {
    {
        1.000000, 0.000000, 0.000000
    },
    {
        0.707107, 0.612372, -0.353553
    },
    {
        0.000000, 0.866025, -0.500000
    },
    {
        -0.707107, 0.612372, -0.353553
    },
    {
        -1.000000, 0.000000, 0.000000
    },
    {
        -0.707107, -0.612372, 0.353553
    },
    {
        0.000000, -0.866025, 0.500000
    },
    {
        0.707107, -0.612372, 0.353553
    },
    {
        1.000000, 0.000000, 0.000000
    }
    },
    {
    {
        1.000000, 0.000000, 0.000000
    },
    {
        0.707107, 0.500000, -0.500000
    },
    {
        0.000000, 0.707107, -0.707107
    },
    {
        -0.707107, 0.500000, -0.500000
    },
    {
        -1.000000, 0.000000, 0.000000
    },
    {
        -0.707107, -0.500000, 0.500000
    },
    {
        0.000000, -0.707107, 0.707107
    },
    {
        0.707107, -0.500000, 0.500000
    },
    {
        1.000000, 0.000000, 0.000000
    }
    },
    {
    {
        1.000000, 0.000000, 0.000000
    },
    {
        0.707107, 0.353553, -0.612372
    },
    {
        0.000000, 0.500000, -0.866025
    },
    {
        -0.707107, 0.353553, -0.612372
    },
    {
        -1.000000, 0.000000, 0.000000
    },
    {
        -0.707107, -0.353553, 0.612372
    },
    {
        0.000000, -0.500000, 0.866025
    },
    {
        0.707107, -0.353553, 0.612372
    },
    {
        1.000000, 0.000000, 0.000000
    }
    },
    {
    {
        1.000000, 0.000000, 0.000000
    },
    {
        0.707107, 0.183013, -0.683013
    },
    {
        0.000000, 0.258819, -0.965926
    },
    {
        -0.707107, 0.183013, -0.683013
    },
    {
        -1.000000, 0.000000, 0.000000
    },
    {
        -0.707107, -0.183013, 0.683013
    },
    {
        0.000000, -0.258819, 0.965926
    },
    {
        0.707107, -0.183013, 0.683013
    },
    {
        1.000000, 0.000000, 0.000000
    }
    },
    {
    {
        1.000000, 0.000000, 0.000000
    },
    {
        0.707107, 0.000000, -0.707107
    },
    {
        0.000000, 0.000000, -1.000000
    },
    {
        -0.707107, 0.000000, -0.707107
    },
    {
        -1.000000, 0.000000, 0.000000
    },
    {
        -0.707107, 0.000000, 0.707107
    },
    {
        0.000000, 0.000000, 1.000000
    },
    {
        0.707107, 0.000000, 0.707107
    },
    {
        1.000000, 0.000000, 0.000000
    }
    }
};
float tep[7][9][2] = {
    {
    {
        0,     0.0
    },
    {
        0.125, 0.0
    },
    {
        0.25,  0.0
    },
    {
        0.375, 0.0
    },
    {
        0.5,   0.0
    },
    {
        0.625, 0.0
    },
    {
        0.75,  0.0
    },
    {
        0.875, 0.0
    },
    {
        1.0,   0.0
    }
    },
    {
    {
        0,     0.16667
    },
    {
        0.125, 0.16667
    },
    {
        0.25,  0.16667
    },
    {
        0.375, 0.16667
    },
    {
        0.5,   0.16667
    },
    {
        0.625, 0.16667
    },
    {
        0.75,  0.16667
    },
    {
        0.875, 0.16667
    },
    {
        1.0,   0.16667
    }
    },
    {
    {
        0,     0.33333
    },
    {
        0.125, 0.33333
    },
    {
        0.25,  0.33333
    },
    {
        0.375, 0.33333
    },
    {
        0.5,   0.33333
    },
    {
        0.625, 0.33333
    },
    {
        0.75,  0.33333
    },
    {
        0.875, 0.33333
    },
    {
        1.0,   0.33333
    }
    },
    {
    {
        0,     0.5
    },
    {
        0.125, 0.5
    },
    {
        0.25,  0.5
    },
    {
        0.375, 0.5
    },
    {
        0.5,   0.5
    },
    {
        0.625, 0.5
    },
    {
        0.75,  0.5
    },
    {
        0.875, 0.5
    },
    {
        1.0,   0.5
    }
    },
    {
    {
        0,     0.6667
    },
    {
        0.125, 0.6667
    },
    {
        0.25,  0.6667
    },
    {
        0.375, 0.6667
    },
    {
        0.5,   0.6667
    },
    {
        0.625, 0.6667
    },
    {
        0.75,  0.6667
    },
    {
        0.875, 0.6667
    },
    {
        1.0,   0.6667
    }
    },
    {
    {
        0,     0.83333
    },
    {
        0.125, 0.83333
    },
    {
        0.25,  0.83333
    },
    {
        0.375, 0.83333
    },
    {
        0.5,   0.83333
    },
    {
        0.625, 0.83333
    },
    {
        0.75,  0.83333
    },
    {
        0.875, 0.83333
    },
    {
        1.0,   0.83333
    }
    },
    {
    {
        0,     1.0
    },
    {
        0.125, 1.0
    },
    {
        0.25,  1.0
    },
    {
        0.375, 1.0
    },
    {
        0.5,   1.0
    },
    {
        0.625, 1.0
    },
    {
        0.75,  1.0
    },
    {
        0.875, 1.0
    },
    {
        1.0,   1.0
    }
    }
};

static void SetUpAntiAliasedGrayScale(void)
{
    float color;
    GLint i, j;

    for (i = 0; i < 16; i++) {
    color = (2 * i + 1) / 32.0;
    for (j = 0; j < 16; j++) {
        auxSetOneColor(i*16+j, color*j/15.0, color*j/15.0, color*j/15.0);
    }
    }
}

static void BendForward(void)
{

    glTranslatef(0.0, 1.0, 0.0);
    glRotatef(90.0, 1, 0, 0);
    glTranslatef(0.0, -1.0, 0.0);
}

static void BendLeft(void)
{

    glRotatef(-90.0, 0, 0, 1);
    glTranslatef(0.0, 1.0, 0.0);
    glRotatef(90.0, 1, 0, 0);
    glTranslatef(0.0, -1.0, 0.0);
}

static void BendRight(void)
{

    glRotatef(90.0, 0, 0, 1);
    glTranslatef(0.0, 1.0, 0.0);
    glRotatef(90.0, 1, 0, 0);
    glTranslatef(0.0, -1.0, 0.0);
}

static void BuildSingleCylinder(void)
{

    glNewList(singleCylinder, GL_COMPILE);

    glBegin(GL_TRIANGLE_STRIP);
       glNormal3fv(scp[0]); glTexCoord2fv(tscp[0]); glVertex3fv(scp[0]);
       glNormal3fv(scp[0]); glTexCoord2fv(tscp[1]); glVertex3fv(scp[1]);
       glNormal3fv(scp[2]); glTexCoord2fv(tscp[2]); glVertex3fv(scp[2]);
       glNormal3fv(scp[2]); glTexCoord2fv(tscp[3]); glVertex3fv(scp[3]);
       glNormal3fv(scp[4]); glTexCoord2fv(tscp[4]); glVertex3fv(scp[4]);
       glNormal3fv(scp[4]); glTexCoord2fv(tscp[5]); glVertex3fv(scp[5]);
       glNormal3fv(scp[6]); glTexCoord2fv(tscp[6]); glVertex3fv(scp[6]);
       glNormal3fv(scp[6]); glTexCoord2fv(tscp[7]); glVertex3fv(scp[7]);
       glNormal3fv(scp[8]); glTexCoord2fv(tscp[8]); glVertex3fv(scp[8]);
       glNormal3fv(scp[8]); glTexCoord2fv(tscp[9]); glVertex3fv(scp[9]);
       glNormal3fv(scp[10]); glTexCoord2fv(tscp[10]); glVertex3fv(scp[10]);
       glNormal3fv(scp[10]); glTexCoord2fv(tscp[11]); glVertex3fv(scp[11]);
       glNormal3fv(scp[12]); glTexCoord2fv(tscp[12]); glVertex3fv(scp[12]);
       glNormal3fv(scp[12]); glTexCoord2fv(tscp[13]); glVertex3fv(scp[13]);
       glNormal3fv(scp[14]); glTexCoord2fv(tscp[14]); glVertex3fv(scp[14]);
       glNormal3fv(scp[14]); glTexCoord2fv(tscp[15]); glVertex3fv(scp[15]);
       glNormal3fv(scp[16]); glTexCoord2fv(tscp[16]); glVertex3fv(scp[16]);
       glNormal3fv(scp[16]); glTexCoord2fv(tscp[17]); glVertex3fv(scp[17]);
    glEnd();

    glEndList();
}

static void BuildDoubleCylinder(void)
{

    glNewList(doubleCylinder, GL_COMPILE);

    glBegin(GL_TRIANGLE_STRIP);
    glNormal3fv(dcp[0]); glTexCoord2fv(tscp[0]); glVertex3fv(dcp[0]);
    glNormal3fv(dcp[0]); glTexCoord2fv(tscp[1]); glVertex3fv(dcp[1]);
    glNormal3fv(dcp[2]); glTexCoord2fv(tscp[2]); glVertex3fv(dcp[2]);
    glNormal3fv(dcp[2]); glTexCoord2fv(tscp[3]); glVertex3fv(dcp[3]);
    glNormal3fv(dcp[4]); glTexCoord2fv(tscp[4]); glVertex3fv(dcp[4]);
    glNormal3fv(dcp[4]); glTexCoord2fv(tscp[5]); glVertex3fv(dcp[5]);
    glNormal3fv(dcp[6]); glTexCoord2fv(tscp[6]); glVertex3fv(dcp[6]);
    glNormal3fv(dcp[6]); glTexCoord2fv(tscp[7]); glVertex3fv(dcp[7]);
    glNormal3fv(dcp[8]); glTexCoord2fv(tscp[8]); glVertex3fv(dcp[8]);
    glNormal3fv(dcp[8]); glTexCoord2fv(tscp[9]); glVertex3fv(dcp[9]);
    glNormal3fv(dcp[10]); glTexCoord2fv(tscp[10]); glVertex3fv(dcp[10]);
    glNormal3fv(dcp[10]); glTexCoord2fv(tscp[11]); glVertex3fv(dcp[11]);
    glNormal3fv(dcp[12]); glTexCoord2fv(tscp[12]); glVertex3fv(dcp[12]);
    glNormal3fv(dcp[12]); glTexCoord2fv(tscp[13]); glVertex3fv(dcp[13]);
    glNormal3fv(dcp[14]); glTexCoord2fv(tscp[14]); glVertex3fv(dcp[14]);
    glNormal3fv(dcp[14]); glTexCoord2fv(tscp[15]); glVertex3fv(dcp[15]);
    glNormal3fv(dcp[16]); glTexCoord2fv(tscp[16]); glVertex3fv(dcp[16]);
    glNormal3fv(dcp[16]); glTexCoord2fv(tscp[17]); glVertex3fv(dcp[17]);
    glEnd();

    glEndList();
}

static void BuildElbow(void)
{

    glNewList(elbow, GL_COMPILE);

    glBegin(GL_TRIANGLE_STRIP);
    glNormal3fv(en[0][0]); glTexCoord2fv(tep[0][0]); glVertex3fv(ep[0][0]);
    glNormal3fv(en[1][0]); glTexCoord2fv(tep[1][0]); glVertex3fv(ep[1][0]);
    glNormal3fv(en[0][1]); glTexCoord2fv(tep[0][1]); glVertex3fv(ep[0][1]);
    glNormal3fv(en[1][1]); glTexCoord2fv(tep[1][1]); glVertex3fv(ep[1][1]);
    glNormal3fv(en[0][2]); glTexCoord2fv(tep[0][2]); glVertex3fv(ep[0][2]);
    glNormal3fv(en[1][2]); glTexCoord2fv(tep[1][2]); glVertex3fv(ep[1][2]);
    glNormal3fv(en[0][3]); glTexCoord2fv(tep[0][3]); glVertex3fv(ep[0][3]);
    glNormal3fv(en[1][3]); glTexCoord2fv(tep[1][3]); glVertex3fv(ep[1][3]);
    glNormal3fv(en[0][4]); glTexCoord2fv(tep[0][4]); glVertex3fv(ep[0][4]);
    glNormal3fv(en[1][4]); glTexCoord2fv(tep[1][4]); glVertex3fv(ep[1][4]);
    glNormal3fv(en[0][5]); glTexCoord2fv(tep[0][5]); glVertex3fv(ep[0][5]);
    glNormal3fv(en[1][5]); glTexCoord2fv(tep[1][5]); glVertex3fv(ep[1][5]);
    glNormal3fv(en[0][6]); glTexCoord2fv(tep[0][6]); glVertex3fv(ep[0][6]);
    glNormal3fv(en[1][6]); glTexCoord2fv(tep[1][6]); glVertex3fv(ep[1][6]);
    glNormal3fv(en[0][7]); glTexCoord2fv(tep[0][7]); glVertex3fv(ep[0][7]);
    glNormal3fv(en[1][7]); glTexCoord2fv(tep[1][7]); glVertex3fv(ep[1][7]);
    glNormal3fv(en[0][8]); glTexCoord2fv(tep[0][8]); glVertex3fv(ep[0][8]);
    glNormal3fv(en[1][8]); glTexCoord2fv(tep[1][8]); glVertex3fv(ep[1][8]);
    glEnd();
    glBegin(GL_TRIANGLE_STRIP);
    glNormal3fv(en[1][0]); glTexCoord2fv(tep[1][0]); glVertex3fv(ep[1][0]);
    glNormal3fv(en[2][0]); glTexCoord2fv(tep[2][0]); glVertex3fv(ep[2][0]);
    glNormal3fv(en[1][1]); glTexCoord2fv(tep[1][1]); glVertex3fv(ep[1][1]);
    glNormal3fv(en[2][1]); glTexCoord2fv(tep[2][1]); glVertex3fv(ep[2][1]);
    glNormal3fv(en[1][2]); glTexCoord2fv(tep[1][2]); glVertex3fv(ep[1][2]);
    glNormal3fv(en[2][2]); glTexCoord2fv(tep[2][2]); glVertex3fv(ep[2][2]);
    glNormal3fv(en[1][3]); glTexCoord2fv(tep[1][3]); glVertex3fv(ep[1][3]);
    glNormal3fv(en[2][3]); glTexCoord2fv(tep[2][3]); glVertex3fv(ep[2][3]);
    glNormal3fv(en[1][4]); glTexCoord2fv(tep[1][4]); glVertex3fv(ep[1][4]);
    glNormal3fv(en[2][4]); glTexCoord2fv(tep[2][4]); glVertex3fv(ep[2][4]);
    glNormal3fv(en[1][5]); glTexCoord2fv(tep[1][5]); glVertex3fv(ep[1][5]);
    glNormal3fv(en[2][5]); glTexCoord2fv(tep[2][5]); glVertex3fv(ep[2][5]);
    glNormal3fv(en[1][6]); glTexCoord2fv(tep[1][6]); glVertex3fv(ep[1][6]);
    glNormal3fv(en[2][6]); glTexCoord2fv(tep[2][6]); glVertex3fv(ep[2][6]);
    glNormal3fv(en[1][7]); glTexCoord2fv(tep[1][7]); glVertex3fv(ep[1][7]);
    glNormal3fv(en[2][7]); glTexCoord2fv(tep[2][7]); glVertex3fv(ep[2][7]);
    glNormal3fv(en[1][8]); glTexCoord2fv(tep[1][8]); glVertex3fv(ep[1][8]);
    glNormal3fv(en[2][8]); glTexCoord2fv(tep[2][8]); glVertex3fv(ep[2][8]);
    glEnd();
    glBegin(GL_TRIANGLE_STRIP);
    glNormal3fv(en[2][0]); glTexCoord2fv(tep[2][0]); glVertex3fv(ep[2][0]);
    glNormal3fv(en[3][0]); glTexCoord2fv(tep[3][0]); glVertex3fv(ep[3][0]);
    glNormal3fv(en[2][1]); glTexCoord2fv(tep[2][1]); glVertex3fv(ep[2][1]);
    glNormal3fv(en[3][1]); glTexCoord2fv(tep[3][1]); glVertex3fv(ep[3][1]);
    glNormal3fv(en[2][2]); glTexCoord2fv(tep[2][2]); glVertex3fv(ep[2][2]);
    glNormal3fv(en[3][2]); glTexCoord2fv(tep[3][2]); glVertex3fv(ep[3][2]);
    glNormal3fv(en[2][3]); glTexCoord2fv(tep[2][3]); glVertex3fv(ep[2][3]);
    glNormal3fv(en[3][3]); glTexCoord2fv(tep[3][3]); glVertex3fv(ep[3][3]);
    glNormal3fv(en[2][4]); glTexCoord2fv(tep[2][4]); glVertex3fv(ep[2][4]);
    glNormal3fv(en[3][4]); glTexCoord2fv(tep[3][4]); glVertex3fv(ep[3][4]);
    glNormal3fv(en[2][5]); glTexCoord2fv(tep[2][5]); glVertex3fv(ep[2][5]);
    glNormal3fv(en[3][5]); glTexCoord2fv(tep[3][5]); glVertex3fv(ep[3][5]);
    glNormal3fv(en[2][6]); glTexCoord2fv(tep[2][6]); glVertex3fv(ep[2][6]);
    glNormal3fv(en[3][6]); glTexCoord2fv(tep[3][6]); glVertex3fv(ep[3][6]);
    glNormal3fv(en[2][7]); glTexCoord2fv(tep[2][7]); glVertex3fv(ep[2][7]);
    glNormal3fv(en[3][7]); glTexCoord2fv(tep[3][7]); glVertex3fv(ep[3][7]);
    glNormal3fv(en[2][8]); glTexCoord2fv(tep[2][8]); glVertex3fv(ep[2][8]);
    glNormal3fv(en[3][8]); glTexCoord2fv(tep[3][8]); glVertex3fv(ep[3][8]);
    glEnd();
    glBegin(GL_TRIANGLE_STRIP);
    glNormal3fv(en[3][0]); glTexCoord2fv(tep[3][0]); glVertex3fv(ep[3][0]);
    glNormal3fv(en[4][0]); glTexCoord2fv(tep[4][0]); glVertex3fv(ep[4][0]);
    glNormal3fv(en[3][1]); glTexCoord2fv(tep[3][1]); glVertex3fv(ep[3][1]);
    glNormal3fv(en[4][1]); glTexCoord2fv(tep[4][1]); glVertex3fv(ep[4][1]);
    glNormal3fv(en[3][2]); glTexCoord2fv(tep[3][2]); glVertex3fv(ep[3][2]);
    glNormal3fv(en[4][2]); glTexCoord2fv(tep[4][2]); glVertex3fv(ep[4][2]);
    glNormal3fv(en[3][3]); glTexCoord2fv(tep[3][3]); glVertex3fv(ep[3][3]);
    glNormal3fv(en[4][3]); glTexCoord2fv(tep[4][3]); glVertex3fv(ep[4][3]);
    glNormal3fv(en[3][4]); glTexCoord2fv(tep[3][4]); glVertex3fv(ep[3][4]);
    glNormal3fv(en[4][4]); glTexCoord2fv(tep[4][4]); glVertex3fv(ep[4][4]);
    glNormal3fv(en[3][5]); glTexCoord2fv(tep[3][5]); glVertex3fv(ep[3][5]);
    glNormal3fv(en[4][5]); glTexCoord2fv(tep[4][5]); glVertex3fv(ep[4][5]);
    glNormal3fv(en[3][6]); glTexCoord2fv(tep[3][6]); glVertex3fv(ep[3][6]);
    glNormal3fv(en[4][6]); glTexCoord2fv(tep[4][6]); glVertex3fv(ep[4][6]);
    glNormal3fv(en[3][7]); glTexCoord2fv(tep[3][7]); glVertex3fv(ep[3][7]);
    glNormal3fv(en[4][7]); glTexCoord2fv(tep[4][7]); glVertex3fv(ep[4][7]);
    glNormal3fv(en[3][8]); glTexCoord2fv(tep[3][8]); glVertex3fv(ep[3][8]);
    glNormal3fv(en[4][8]); glTexCoord2fv(tep[4][8]); glVertex3fv(ep[4][8]);
    glEnd();
    glBegin(GL_TRIANGLE_STRIP);
    glNormal3fv(en[4][0]); glTexCoord2fv(tep[4][0]); glVertex3fv(ep[4][0]);
    glNormal3fv(en[5][0]); glTexCoord2fv(tep[5][0]); glVertex3fv(ep[5][0]);
    glNormal3fv(en[4][1]); glTexCoord2fv(tep[4][1]); glVertex3fv(ep[4][1]);
    glNormal3fv(en[5][1]); glTexCoord2fv(tep[5][1]); glVertex3fv(ep[5][1]);
    glNormal3fv(en[4][2]); glTexCoord2fv(tep[4][2]); glVertex3fv(ep[4][2]);
    glNormal3fv(en[5][2]); glTexCoord2fv(tep[5][2]); glVertex3fv(ep[5][2]);
    glNormal3fv(en[4][3]); glTexCoord2fv(tep[4][3]); glVertex3fv(ep[4][3]);
    glNormal3fv(en[5][3]); glTexCoord2fv(tep[5][3]); glVertex3fv(ep[5][3]);

glNormal3fv(en[4][4]); glTexCoord2fv(tep[4][4]); glVertex3fv(ep[4][4]);
    glNormal3fv(en[5][4]); glTexCoord2fv(tep[5][4]); glVertex3fv(ep[5][4]);
    glNormal3fv(en[4][5]); glTexCoord2fv(tep[4][5]); glVertex3fv(ep[4][5]);
    glNormal3fv(en[5][5]); glTexCoord2fv(tep[5][5]); glVertex3fv(ep[5][5]);
    glNormal3fv(en[4][6]); glTexCoord2fv(tep[4][6]); glVertex3fv(ep[4][6]);
    glNormal3fv(en[5][6]); glTexCoord2fv(tep[5][6]); glVertex3fv(ep[5][6]);
    glNormal3fv(en[4][7]); glTexCoord2fv(tep[4][7]); glVertex3fv(ep[4][7]);
    glNormal3fv(en[5][7]); glTexCoord2fv(tep[5][7]); glVertex3fv(ep[5][7]);
    glNormal3fv(en[4][8]); glTexCoord2fv(tep[4][8]); glVertex3fv(ep[4][8]);
    glNormal3fv(en[5][8]); glTexCoord2fv(tep[5][8]); glVertex3fv(ep[5][8]);
    glEnd();
    glBegin(GL_TRIANGLE_STRIP);
    glNormal3fv(en[5][0]); glTexCoord2fv(tep[5][0]); glVertex3fv(ep[5][0]);
    glNormal3fv(en[6][0]); glTexCoord2fv(tep[6][0]); glVertex3fv(ep[6][0]);
    glNormal3fv(en[5][1]); glTexCoord2fv(tep[5][1]); glVertex3fv(ep[5][1]);
    glNormal3fv(en[6][1]); glTexCoord2fv(tep[6][1]); glVertex3fv(ep[6][1]);
    glNormal3fv(en[5][2]); glTexCoord2fv(tep[5][2]); glVertex3fv(ep[5][2]);
    glNormal3fv(en[6][2]); glTexCoord2fv(tep[6][2]); glVertex3fv(ep[6][2]);
    glNormal3fv(en[5][3]); glTexCoord2fv(tep[5][3]); glVertex3fv(ep[5][3]);
    glNormal3fv(en[6][3]); glTexCoord2fv(tep[6][3]); glVertex3fv(ep[6][3]);
    glNormal3fv(en[5][4]); glTexCoord2fv(tep[5][4]); glVertex3fv(ep[5][4]);
    glNormal3fv(en[6][4]); glTexCoord2fv(tep[6][4]); glVertex3fv(ep[6][4]);
    glNormal3fv(en[5][5]); glTexCoord2fv(tep[5][5]); glVertex3fv(ep[5][5]);
    glNormal3fv(en[6][5]); glTexCoord2fv(tep[6][5]); glVertex3fv(ep[6][5]);
    glNormal3fv(en[5][6]); glTexCoord2fv(tep[5][6]); glVertex3fv(ep[5][6]);
    glNormal3fv(en[6][6]); glTexCoord2fv(tep[6][6]); glVertex3fv(ep[6][6]);
    glNormal3fv(en[5][7]); glTexCoord2fv(tep[5][7]); glVertex3fv(ep[5][7]);
    glNormal3fv(en[6][7]); glTexCoord2fv(tep[6][7]); glVertex3fv(ep[6][7]);
    glNormal3fv(en[5][8]); glTexCoord2fv(tep[5][8]); glVertex3fv(ep[5][8]);
    glNormal3fv(en[6][8]); glTexCoord2fv(tep[6][8]); glVertex3fv(ep[6][8]);
    glEnd();

    glEndList();
}

static void BuildLogo(void)
{

    glNewList(logo, GL_COMPILE);

    glTranslatef(5.5, -3.5, 4.5);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -5.0);
    glCallList(singleCylinder);
    BendRight();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -5.0);
    glCallList(singleCylinder);
    BendLeft();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -5.0);
    glCallList(singleCylinder);
    BendRight();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -5.0);
    glCallList(singleCylinder);
    BendLeft();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -5.0);
    glCallList(singleCylinder);
    BendRight();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -7.0);
    glCallList(doubleCylinder);
    BendForward();
    glCallList(elbow);
    glTranslatef(0.0, 0.0, -5.0);
    glCallList(singleCylinder);
    BendLeft();
    glCallList(elbow);

    glEndList();
}

static void BuildLists(void)
{

    singleCylinder = glGenLists(1);
    doubleCylinder = glGenLists(1);
    elbow = glGenLists(1);
    logo = glGenLists(1);

    BuildSingleCylinder();
    BuildDoubleCylinder();
    BuildElbow();
    BuildLogo();
}

static void Init(void)
{
    static float ambient[] = {0.1, 0.1, 0.1, 1.0};
    static float diffuse[] = {0.5, 1.0, 1.0, 1.0};
    static float position[] = {90.0, 90.0, 150.0, 0.0};
    static float front_mat_shininess[] = {30.0};
    static float front_mat_specular[] = {0.2, 0.2, 0.2, 1.0};
    static float front_mat_diffuse[] = {0.5, 0.28, 0.38, 1.0};
    static float back_mat_shininess[] = {50.0};
    static float back_mat_specular[] = {0.5, 0.5, 0.2, 1.0};
    static float back_mat_diffuse[] = {1.0, 1.0, 0.2, 1.0};
    static float lmodel_ambient[] = {1.0, 1.0, 1.0, 1.0};
    static float lmodel_twoside[] = {GL_TRUE};

    glClearColor(0.0, 0.0, 0.0, 0.0);

    glFrontFace(GL_CW);

    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glMaterialfv(GL_FRONT, GL_SHININESS, front_mat_shininess);
    glMaterialfv(GL_FRONT, GL_SPECULAR, front_mat_specular);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, front_mat_diffuse);
    glMaterialfv(GL_BACK, GL_SHININESS, back_mat_shininess);
    glMaterialfv(GL_BACK, GL_SPECULAR, back_mat_specular);
    glMaterialfv(GL_BACK, GL_DIFFUSE, back_mat_diffuse);

    glEnable(GL_CLIP_PLANE0);

    if (rgb) {
    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, decal);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, nearest);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, nearest);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, CHECKIMAGEWIDTH, CHECKIMAGEHEIGHT, 0,
             GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)checkImage);
    glEnable(GL_TEXTURE_2D);

    glCullFace(GL_BACK);
    glEnable(GL_CULL_FACE);
    } else {
    auxSetGreyRamp();
    /*
    if (doubleBuffer) {
        colorIndexes[1] = 10;
        colorIndexes[2] = 15;
    } */

    glMaterialiv(GL_FRONT_AND_BACK, GL_COLOR_INDEXES, colorIndexes);
    }

    BuildLists();

    dithering = GL_TRUE;
    shade = GL_TRUE;
    doStipple = GL_FALSE;
    polyMode = GL_BACK;
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(90, 1.0, 1.0, 200.0);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Key_LEFT(void)
{
    yRotation += 0.5;
}

static void CALLBACK Key_RIGHT(void)
{
    yRotation -= 0.5;
}

static void CALLBACK Key_UP(void)
{
    plane[3] += 2.0;
}

static void CALLBACK Key_DOWN(void)
{
    plane[3] -= 2.0;
}

static void CALLBACK Key_Z(void)
{
    zTranslation -= 1.0;
}

static void CALLBACK Key_z(void)
{
    zTranslation += 1.0;
}

static void CALLBACK Key_1(void)
{
    glPolygonMode(polyMode, GL_POINT);
}

static void CALLBACK Key_2(void)
{
    glPolygonMode(polyMode, GL_LINE);
}

static void CALLBACK Key_3(void)
{
    glPolygonMode(polyMode, GL_FILL);
}

static void CALLBACK Key_p(void)
{
    switch (polyMode) {
      case GL_BACK:
        polyMode = GL_FRONT;
        break;
      case GL_FRONT:
        polyMode = GL_FRONT_AND_BACK;
        break;
      case GL_FRONT_AND_BACK:
        polyMode = GL_BACK;
        break;
    }
}

static void CALLBACK Key_4(void)
{
    glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
}

static void CALLBACK Key_5(void)
{
    glEnable(GL_POLYGON_SMOOTH);
    if (rgb) {
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
        glEnable(GL_BLEND);
        glDisable(GL_DEPTH_TEST);
    } else {
        SetUpAntiAliasedGrayScale();
    }
}

static void CALLBACK Key_6(void)
{
    glDisable(GL_POLYGON_SMOOTH);
    if (rgb) {
        glBlendFunc(GL_ONE, GL_ZERO);
        glDisable(GL_BLEND);
        glEnable(GL_DEPTH_TEST);
    } else {
        auxSetGreyRamp();
    }
}

static void CALLBACK Key_8(void)
{
    dithering = !dithering;
    (dithering) ? glEnable(GL_DITHER) : glDisable(GL_DITHER);
}

static void CALLBACK Key_9(void)
{
    doStipple = !doStipple;
    if (doStipple) {
        glPolygonStipple(stipple);
        glEnable(GL_POLYGON_STIPPLE);
    } else {
        glDisable(GL_POLYGON_STIPPLE);
    }
}

static void CALLBACK Key_0(void)
{
    shade = !shade;
    (shade) ? glShadeModel(GL_SMOOTH) : glShadeModel(GL_FLAT);
}

static void CALLBACK Key_q(void)
{
    glDisable(GL_CULL_FACE);
}

static void CALLBACK Key_w(void)
{
    glEnable(GL_CULL_FACE);
    glCullFace(GL_FRONT);
}

static void CALLBACK Key_e(void)
{
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
}

static void CALLBACK Key_r(void)
{
    glFrontFace(GL_CW);
}

static void CALLBACK Key_t(void)
{
    glFrontFace(GL_CCW);
}

static void CALLBACK Key_y(void)
{
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glPixelStorei(GL_UNPACK_LSB_FIRST, 0);
    glPolygonStipple(stipple);
}

static void CALLBACK Key_u(void)
{
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glPixelStorei(GL_UNPACK_LSB_FIRST, 1);
    glPolygonStipple(stipple);
}

static void CALLBACK Key_a(void)
{
    glEnable(GL_TEXTURE_2D);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, nearest);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, nearest);
    glTexImage2D(GL_TEXTURE_2D, 0, 4, BRICKIMAGEWIDTH,
             BRICKIMAGEHEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE,
             (GLvoid *)brickImage);
}

static void CALLBACK Key_s(void)
{
    glEnable(GL_TEXTURE_2D);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, nearest);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, nearest);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, CHECKIMAGEWIDTH,
             CHECKIMAGEHEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE,
             (GLvoid *)checkImage);
}

static void CALLBACK Key_d(void)
{
    glDisable(GL_TEXTURE_2D);
}

static void CALLBACK Key_f(void)
{
    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, decal);
}

static void CALLBACK Key_g(void)
{
    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, modulate);
}

static void CALLBACK Draw(void)
{

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glPushMatrix();

    glTranslatef(0, 0, zTranslation);
    glRotatef(30.0, 1, 0, 0);
    glRotatef(yRotation, 0, 1, 0);
    glClipPlane(GL_CLIP_PLANE0, plane);
    glCallList(logo);

    glPopMatrix();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_FALSE;
    directRender = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-ci") == 0) {
        rgb = GL_FALSE;
    } else if (strcmp(argv[i], "-rgb") == 0) {
        rgb = GL_TRUE;
    } else if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else if (strcmp(argv[i], "-dr") == 0) {
        directRender = GL_TRUE;
    } else if (strcmp(argv[i], "-ir") == 0) {
        directRender = GL_FALSE;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    auxInitPosition(0, 0, 300, 300);

    type = AUX_DEPTH16;
    type |= (rgb) ? AUX_RGB : AUX_INDEX;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;
    auxInitDisplayMode(type);

    if (auxInitWindow("Logo Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_LEFT, Key_LEFT);
    auxKeyFunc(AUX_RIGHT, Key_RIGHT);
    auxKeyFunc(AUX_UP, Key_UP);
    auxKeyFunc(AUX_DOWN, Key_DOWN);
    auxKeyFunc(AUX_Z, Key_Z);
    auxKeyFunc(AUX_z, Key_z);
    auxKeyFunc(AUX_1, Key_1);
    auxKeyFunc(AUX_2, Key_2);
    auxKeyFunc(AUX_3, Key_3);
    auxKeyFunc(AUX_p, Key_p);
    auxKeyFunc(AUX_4, Key_4);
    auxKeyFunc(AUX_5, Key_5);
    auxKeyFunc(AUX_6, Key_6);
    auxKeyFunc(AUX_8, Key_8);
    auxKeyFunc(AUX_9, Key_9);
    auxKeyFunc(AUX_0, Key_0);
    auxKeyFunc(AUX_q, Key_q);
    auxKeyFunc(AUX_w, Key_w);
    auxKeyFunc(AUX_e, Key_e);
    auxKeyFunc(AUX_r, Key_r);
    auxKeyFunc(AUX_t, Key_t);
    auxKeyFunc(AUX_y, Key_y);
    auxKeyFunc(AUX_u, Key_u);
    auxKeyFunc(AUX_a, Key_a);
    auxKeyFunc(AUX_s, Key_s);
    auxKeyFunc(AUX_d, Key_d);
    auxKeyFunc(AUX_f, Key_f);
    auxKeyFunc(AUX_g, Key_g);
    auxMainLoop(Draw);
}


TNURB.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
#include <gl\glaux.h>

#define INREAL float

#define S_NUMPOINTS 13
#define S_ORDER     3
#define S_NUMKNOTS  (S_NUMPOINTS + S_ORDER)
#define T_NUMPOINTS 3
#define T_ORDER     3
#define T_NUMKNOTS  (T_NUMPOINTS + T_ORDER)
#define SQRT_TWO    1.41421356237309504880

typedef INREAL Point[4];

static void CALLBACK ErrorCallback(GLenum which);
static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_DOWN(void );
static void CALLBACK Key_UP(void );
static void CALLBACK Key_LEFT(void );
static void CALLBACK Key_RIGHT(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum doubleBuffer;

GLenum expectedError;
GLint rotX = 40, rotY = 40;
INREAL sknots[S_NUMKNOTS] = {
    -1.0, -1.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0,
    4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 9.0, 9.0
};
INREAL tknots[T_NUMKNOTS] = {
    1.0, 1.0, 1.0, 2.0, 2.0, 2.0
};
Point ctlpoints[S_NUMPOINTS][T_NUMPOINTS] = {
    {
    {
        4.0, 2.0, 2.0, 1.0
    },
    {
        4.0, 1.6, 2.5, 1.0
    },
    {
        4.0, 2.0, 3.0, 1.0
    }
    },
    {
    {
        5.0, 4.0, 2.0, 1.0
    },
    {
        5.0, 4.0, 2.5, 1.0
    },
    {
        5.0, 4.0, 3.0, 1.0
    }
    },
    {
    {
        6.0, 5.0, 2.0, 1.0
    },
    {
        6.0, 5.0, 2.5, 1.0
    },
    {
        6.0, 5.0, 3.0, 1.0
    }
    },
    {
    {
        SQRT_TWO*6.0, SQRT_TWO*6.0, SQRT_TWO*2.0, SQRT_TWO
    },
    {
        SQRT_TWO*6.0, SQRT_TWO*6.0, SQRT_TWO*2.5, SQRT_TWO
    },
    {
        SQRT_TWO*6.0, SQRT_TWO*6.0, SQRT_TWO*3.0, SQRT_TWO
    }
    },
    {
    {
        5.2, 6.7, 2.0, 1.0
    },
    {
        5.2, 6.7, 2.5, 1.0
    },
    {
        5.2, 6.7, 3.0, 1.0
    }
    },
    {
    {
        SQRT_TWO*4.0, SQRT_TWO*6.0, SQRT_TWO*2.0, SQRT_TWO
    },
    {
        SQRT_TWO*4.0, SQRT_TWO*6.0, SQRT_TWO*2.5, SQRT_TWO
    },
    {
        SQRT_TWO*4.0, SQRT_TWO*6.0, SQRT_TWO*3.0, SQRT_TWO
    }
    },
    {
    {
        4.0, 5.2, 2.0, 1.0
    },
    {
        4.0, 4.6, 2.5, 1.0
    },
    {
        4.0, 5.2, 3.0, 1.0
    }
    },
    {
    {
        SQRT_TWO*4.0, SQRT_TWO*6.0, SQRT_TWO*2.0, SQRT_TWO
    },
    {
        SQRT_TWO*4.0, SQRT_TWO*6.0, SQRT_TWO*2.5, SQRT_TWO
    },
    {
        SQRT_TWO*4.0, SQRT_TWO*6.0, SQRT_TWO*3.0, SQRT_TWO
    }
    },
    {
    {
        2.8, 6.7, 2.0, 1.0
    },
    {
        2.8, 6.7, 2.5, 1.0
    },
    {
        2.8, 6.7, 3.0, 1.0
    }
    },
    {
    {
        SQRT_TWO*2.0, SQRT_TWO*6.0, SQRT_TWO*2.0, SQRT_TWO
    },
    {
        SQRT_TWO*2.0, SQRT_TWO*6.0, SQRT_TWO*2.5, SQRT_TWO
    },
    {
        SQRT_TWO*2.0, SQRT_TWO*6.0, SQRT_TWO*3.0, SQRT_TWO
    }
    },
    {
    {
        2.0, 5.0, 2.0, 1.0
    },
    {
        2.0, 5.0, 2.5, 1.0
    },
    {
        2.0, 5.0, 3.0, 1.0
    }
    },
    {
    {
        3.0, 4.0, 2.0, 1.0
    },
    {
        3.0, 4.0, 2.5, 1.0
    },
    {
        3.0, 4.0, 3.0, 1.0
    }
    },
    {
    {
        4.0, 2.0, 2.0, 1.0
    },
    {
        4.0, 1.6, 2.5, 1.0
    },
    {
        4.0, 2.0, 3.0, 1.0
    }
    }
};
GLUnurbsObj *theNurbs;

static void CALLBACK ErrorCallback(GLenum which)
{

    if (which != expectedError) {
    fprintf(stderr, "Unexpected error occured (%d):\n", which);
    fprintf(stderr, "    %s\n", gluErrorString(which));
    }
}

static void Init(void)
{

    theNurbs = gluNewNurbsRenderer();
    //gluNurbsCallback(theNurbs, GLU_ERROR, ErrorCallback);

    gluNurbsProperty(theNurbs, GLU_SAMPLING_TOLERANCE, 15.0);
    gluNurbsProperty(theNurbs, GLU_DISPLAY_MODE, GLU_OUTLINE_PATCH);

    expectedError = GLU_INVALID_ENUM;
    gluNurbsProperty(theNurbs, (GLenum)~0, 15.0);
    expectedError = GLU_NURBS_ERROR13;
    gluEndSurface(theNurbs);
    expectedError = 0;

    glColor3f(1.0, 1.0, 1.0);
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-2.0, 2.0, -2.0, 2.0, 0.8, 10.0);
    gluLookAt(7.0, 4.5, 4.0, 4.5, 4.5, 2.5, 6.0, -3.0, 2.0);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Key_DOWN(void)
{
    rotX -= 5;
}

static void CALLBACK Key_UP(void)
{
    rotX += 5;
}

static void CALLBACK Key_LEFT(void)
{
    rotY -= 5;
}

static void CALLBACK Key_RIGHT(void)
{
    rotY += 5;
}

static void CALLBACK Draw(void)
{

    glClear(GL_COLOR_BUFFER_BIT);

    glPushMatrix();

    glTranslatef(4.0, 4.5, 2.5);
    glRotatef(rotY, 1, 0, 0);
    glRotatef(rotX, 0, 1, 0);
    glTranslatef(-4.0, -4.5, -2.5);

    gluBeginSurface(theNurbs);
    gluNurbsSurface(theNurbs, S_NUMKNOTS, sknots, T_NUMKNOTS, tknots,
            4*T_NUMPOINTS, 4, &ctlpoints[0][0][0], S_ORDER,
            T_ORDER, GL_MAP2_VERTEX_4);
    gluEndSurface(theNurbs);

    glPopMatrix();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else {
        printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    auxInitPosition(0, 0, 300, 300);

    type = AUX_RGB;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("NURBS Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_DOWN, Key_DOWN);
    auxKeyFunc(AUX_UP, Key_UP);
    auxKeyFunc(AUX_LEFT, Key_LEFT);
    auxKeyFunc(AUX_RIGHT, Key_RIGHT);
    auxMainLoop(Draw);
}


TPOINT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

#define CI_RED AUX_RED
#define CI_ANTI_ALIAS_GREEN 16
#define CI_ANTI_ALIAS_YELLOW 32
#define CI_ANTI_ALIAS_RED 48

static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_1(void );
static void CALLBACK Key_W(void );
static void CALLBACK Key_w(void );
static void CALLBACK Key_LEFT(void );
static void CALLBACK Key_RIGHT(void );
static void CALLBACK Key_UP(void );
static void CALLBACK Key_DOWN(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum rgb, doubleBuffer, windType;
GLint windW, windH;

GLenum mode;
GLint size;
float point[3] = {
    1.0, 1.0, 0.0
};

static void Init(void)
{
    GLint i;

    glClearColor(0.0, 0.0, 0.0, 0.0);

    glBlendFunc(GL_SRC_ALPHA, GL_ZERO);

    if (!rgb) {
    for (i = 0; i < 16; i++) {
        auxSetOneColor(i+CI_ANTI_ALIAS_RED, i/15.0, 0.0, 0.0);
        auxSetOneColor(i+CI_ANTI_ALIAS_YELLOW, i/15.0, i/15.0, 0.0);
        auxSetOneColor(i+CI_ANTI_ALIAS_GREEN, 0.0, i/15.0, 0.0);
    }
    }

    mode = GL_FALSE;
    size = 1;
}

static void CALLBACK Reshape(int width, int height)
{

    windW = (GLint)width;
    windH = (GLint)height;

    glViewport(0, 0, width, height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-windW/2, windW/2, -windH/2, windH/2);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Key_1(void)
{
    mode = !mode;
}

static void CALLBACK Key_W(void)
{
    size++;
}

static void CALLBACK Key_w(void)
{
    size--;
    if (size < 1) {
        size = 1;
    }
}

static void CALLBACK Key_LEFT(void)
{
    point[0] -= 0.25;
}

static void CALLBACK Key_RIGHT(void)
{
    point[0] += 0.25;
}

static void CALLBACK Key_UP(void)
{
    point[1] += 0.25;
}

static void CALLBACK Key_DOWN(void)
{
    point[1] -= 0.25;
}

static void CALLBACK Draw(void)
{

    glClear(GL_COLOR_BUFFER_BIT);

    AUX_SETCOLOR(windType, AUX_YELLOW);
    glBegin(GL_LINE_STRIP);
    glVertex2f(-windW/2, 0);
    glVertex2f(windW/2, 0);
    glEnd();
    glBegin(GL_LINE_STRIP);
    glVertex2f(0, -windH/2);
    glVertex2f(0, windH/2);
    glEnd();

    if (mode) {
    glEnable(GL_BLEND);
    glEnable(GL_POINT_SMOOTH);
    } else {
    glDisable(GL_BLEND);
    glDisable(GL_POINT_SMOOTH);
    }

    glPointSize(size);
    if (mode) {
    (rgb) ? glColor3f(1.0, 0.0, 0.0) : glIndexf(CI_ANTI_ALIAS_RED);
    } else {
    (rgb) ? glColor3f(1.0, 0.0, 0.0) : glIndexf(CI_RED);
    }
    glBegin(GL_POINTS);
    glVertex3fv(point);
    glEnd();

    glDisable(GL_POINT_SMOOTH);

    glPointSize(1);
    AUX_SETCOLOR(windType, AUX_GREEN);
    glBegin(GL_POINTS);
    glVertex3fv(point);
    glEnd();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-ci") == 0) {
        rgb = GL_FALSE;
    } else if (strcmp(argv[i], "-rgb") == 0) {
        rgb = GL_TRUE;
    } else if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    windW = 300;
    windH = 300;
    auxInitPosition(0, 0, windW, windH);

    windType |= (rgb) ? AUX_RGB : AUX_INDEX;
    windType |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(windType);

    if (auxInitWindow("Point Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_1, Key_1);
    auxKeyFunc(AUX_W, Key_W);
    auxKeyFunc(AUX_w, Key_w);
    auxKeyFunc(AUX_LEFT, Key_LEFT);
    auxKeyFunc(AUX_RIGHT, Key_RIGHT);
    auxKeyFunc(AUX_UP, Key_UP);
    auxKeyFunc(AUX_DOWN, Key_DOWN);
    auxMainLoop(Draw);
}


TPRIM.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

#define PIXEL_CENTER(x) ((long)(x) + 0.5)

#define GAP 10
#define ROWS 3
#define COLS 4

#define OPENGL_WIDTH 48
#define OPENGL_HEIGHT 13

static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void RotateColorMask(void );
static void CALLBACK Key_1(void );
static void CALLBACK Key_2(void );
static void CALLBACK Key_3(void );
static void Viewport(GLint row, GLint column);
static void Point(void );
static void Lines(void );
static void LineStrip(void );
static void LineLoop(void );
static void Bitmap(void );
static void Triangles(void );
static void TriangleStrip(void );
static void TriangleFan(void );
static void Rect(void );
static void xPolygon(void );
static void Quads(void );
static void QuadStrip(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum rgb, doubleBuffer, windType;
GLint windW, windH;

GLenum mode1, mode2;
GLint boxW, boxH;
GLubyte OpenGL_bits[] = {
   0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
   0x7f, 0xfb, 0xff, 0xff, 0xff, 0x01,
   0x7f, 0xfb, 0xff, 0xff, 0xff, 0x01,
   0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
   0x3e, 0x8f, 0xb7, 0xf9, 0xfc, 0x01,
   0x63, 0xdb, 0xb0, 0x8d, 0x0d, 0x00,
   0x63, 0xdb, 0xb7, 0x8d, 0x0d, 0x00,
   0x63, 0xdb, 0xb6, 0x8d, 0x0d, 0x00,
   0x63, 0x8f, 0xf3, 0xcc, 0x0d, 0x00,
   0x63, 0x00, 0x00, 0x0c, 0x4c, 0x0a,
   0x63, 0x00, 0x00, 0x0c, 0x4c, 0x0e,
   0x63, 0x00, 0x00, 0x8c, 0xed, 0x0e,
   0x3e, 0x00, 0x00, 0xf8, 0x0c, 0x00,
};

static void Init(void)
{

    mode1 = GL_TRUE;
    mode2 = GL_TRUE;
}

static void CALLBACK Reshape(int width, int height)
{

    windW = (GLint)width;
    windH = (GLint)height;
}

static void RotateColorMask(void)
{
    static GLint rotation = 0;

    rotation = (rotation + 1) & 0x3;
    switch (rotation) {
      case 0:
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    glIndexMask( 0xff );
    break;
      case 1:
    glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
    glIndexMask(0xFE);
    break;
      case 2:
    glColorMask(GL_TRUE, GL_FALSE, GL_TRUE, GL_TRUE);
    glIndexMask(0xFD);
    break;
      case 3:
    glColorMask(GL_TRUE, GL_TRUE, GL_FALSE, GL_TRUE);
    glIndexMask(0xFB);
    break;
    }
}

static void CALLBACK Key_1(void)
{
    mode1 = !mode1;
}

static void CALLBACK Key_2(void)
{
    mode2 = !mode2;
}

static void CALLBACK Key_3(void)
{
    RotateColorMask();
}

static void Viewport(GLint row, GLint column)
{
    GLint x, y;

    boxW = (windW - (COLS + 1) * GAP) / COLS;
    boxH = (windH - (ROWS + 1) * GAP) / ROWS;

    x = GAP + column * (boxW + GAP);
    y = GAP + row * (boxH + GAP);

    glViewport(x, y, boxW, boxH);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-boxW/2, boxW/2, -boxH/2, boxH/2, 0.0, 1.0);
    glMatrixMode(GL_MODELVIEW);

    glEnable(GL_SCISSOR_TEST);
    glScissor(x, y, boxW, boxH);
}

static void Point(void)
{
    GLint i;

    glBegin(GL_POINTS);
    AUX_SETCOLOR(windType, AUX_WHITE);
    glVertex2i(0, 0);
    for (i = 1; i < 8; i++) {
        GLint j = i * 2;
        AUX_SETCOLOR(windType, i + AUX_RED - 1);
        glVertex2i(-j, -j);
        glVertex2i(-j, 0);
        glVertex2i(-j, j);
        glVertex2i(0, j);
        glVertex2i(j, j);
        glVertex2i(j, 0);
        glVertex2i(j, -j);
        glVertex2i(0, -j);
    }
    glEnd();
}

static void Lines(void)
{
    GLint i;

    glPushMatrix();

    glTranslatef(-12, 0, 0);
    for (i = 1; i < 8; i++) {
    AUX_SETCOLOR(windType, i + AUX_RED - 1);
    glBegin(GL_LINES);
        glVertex2i(-boxW/4, -boxH/4);
        glVertex2i(boxW/4, boxH/4);
    glEnd();
    glTranslatef(4, 0, 0);
    }

    glPopMatrix();

    glBegin(GL_LINES);
    glVertex2i(0, 0);
    glEnd();
}

static void LineStrip(void)
{

    glBegin(GL_LINE_STRIP);
    AUX_SETCOLOR(windType, AUX_RED);
    glVertex2f(PIXEL_CENTER(-boxW/4), PIXEL_CENTER(-boxH/4));
    AUX_SETCOLOR(windType, AUX_GREEN);
    glVertex2f(PIXEL_CENTER(-boxW/4), PIXEL_CENTER(boxH/4));
    AUX_SETCOLOR(windType, AUX_BLUE);
    glVertex2f(PIXEL_CENTER(boxW/4), PIXEL_CENTER(boxH/4));
    AUX_SETCOLOR(windType, AUX_WHITE);
    glVertex2f(PIXEL_CENTER(boxW/4), PIXEL_CENTER(-boxH/4));
    glEnd();

    glBegin(GL_LINE_STRIP);
    glVertex2i(0, 0);
    glEnd();
}

static void LineLoop(void)
{

    glBegin(GL_LINE_LOOP);
    AUX_SETCOLOR(windType, AUX_RED);
    glVertex2f(PIXEL_CENTER(-boxW/4), PIXEL_CENTER(-boxH/4));
    AUX_SETCOLOR(windType, AUX_GREEN);
    glVertex2f(PIXEL_CENTER(-boxW/4), PIXEL_CENTER(boxH/4));
    AUX_SETCOLOR(windType, AUX_BLUE);
    glVertex2f(PIXEL_CENTER(boxW/4), PIXEL_CENTER(boxH/4));
    AUX_SETCOLOR(windType, AUX_WHITE);
    glVertex2f(PIXEL_CENTER(boxW/4), PIXEL_CENTER(-boxH/4));
    glEnd();

    glEnable(GL_LOGIC_OP);
    glLogicOp(GL_XOR);

    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE);

    AUX_SETCOLOR(windType, AUX_MAGENTA);
    glBegin(GL_LINE_LOOP);
    glVertex2f(PIXEL_CENTER(-boxW/8), PIXEL_CENTER(-boxH/8));
    glVertex2f(PIXEL_CENTER(-boxW/8), PIXEL_CENTER(boxH/8));
    glEnd();
    glBegin(GL_LINE_LOOP);
    glVertex2f(PIXEL_CENTER(-boxW/8), PIXEL_CENTER(boxH/8+5));
    glVertex2f(PIXEL_CENTER(boxW/8), PIXEL_CENTER(boxH/8+5));
    glEnd();
    glDisable(GL_LOGIC_OP);
    glDisable(GL_BLEND);

    AUX_SETCOLOR(windType, AUX_GREEN);
    glBegin(GL_POINTS);
    glVertex2i(0, 0);
    glEnd();

    glBegin(GL_LINE_LOOP);
    glVertex2i(0, 0);
    glEnd();
}

static void Bitmap(void)
{

    glBegin(GL_LINES);
    AUX_SETCOLOR(windType, AUX_GREEN);
    glVertex2i(-boxW/2, 0);
    glVertex2i(boxW/2, 0);
    glVertex2i(0, -boxH/2);
    glVertex2i(0, boxH/2);
    AUX_SETCOLOR(windType, AUX_RED);
    glVertex2i(0, -3);
    glVertex2i(0, -3+OPENGL_HEIGHT);
    AUX_SETCOLOR(windType, AUX_BLUE);
    glVertex2i(0, -3);
    glVertex2i(OPENGL_WIDTH, -3);
    glEnd();

    AUX_SETCOLOR(windType, AUX_GREEN);

    glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    glRasterPos2i(0, 0);
    glBitmap(OPENGL_WIDTH, OPENGL_HEIGHT, 0, 3, 0.0, 0.0, OpenGL_bits);
}

static void Triangles(void)
{

    glBegin(GL_TRIANGLES);
    AUX_SETCOLOR(windType, AUX_GREEN);
    glVertex2i(-boxW/4, -boxH/4);
    AUX_SETCOLOR(windType, AUX_RED);
    glVertex2i(-boxW/8, -boxH/16);
    AUX_SETCOLOR(windType, AUX_BLUE);
    glVertex2i(boxW/8, -boxH/16);

    AUX_SETCOLOR(windType, AUX_GREEN);
    glVertex2i(-boxW/4, boxH/4);
    AUX_SETCOLOR(windType, AUX_RED);
    glVertex2i(-boxW/8, boxH/16);
    AUX_SETCOLOR(windType, AUX_BLUE);
    glVertex2i(boxW/8, boxH/16);
    glEnd();

    glBegin(GL_TRIANGLES);
    glVertex2i(0, 0);
    glVertex2i(-100, 100);
    glEnd();
}

static void TriangleStrip(void)
{

    glBegin(GL_TRIANGLE_STRIP);
    AUX_SETCOLOR(windType, AUX_GREEN);
    glVertex2i(-boxW/4, -boxH/4);
    AUX_SETCOLOR(windType, AUX_RED);
    glVertex2i(-boxW/4, boxH/4);
    AUX_SETCOLOR(windType, AUX_BLUE);
    glVertex2i(0, -boxH/4);
    AUX_SETCOLOR(windType, AUX_WHITE);
    glVertex2i(0, boxH/4);
    AUX_SETCOLOR(windType, AUX_CYAN);
    glVertex2i(boxW/4, -boxH/4);
    AUX_SETCOLOR(windType, AUX_YELLOW);
    glVertex2i(boxW/4, boxH/4);
    glEnd();

    glBegin(GL_TRIANGLE_STRIP);
    glVertex2i(0, 0);
    glVertex2i(-100, 100);
    glEnd();
}

static void TriangleFan(void)
{
    GLint vx[8][2];
    GLint x0, y0, x1, y1, x2, y2, x3, y3;
    GLint i;

    y0 = -boxH/4;
    y1 = y0 + boxH/2/3;
    y2 = y1 + boxH/2/3;
    y3 = boxH/4;
    x0 = -boxW/4;
    x1 = x0 + boxW/2/3;
    x2 = x1 + boxW/2/3;
    x3 = boxW/4;

    vx[0][0] = x0; vx[0][1] = y1;
    vx[1][0] = x0; vx[1][1] = y2;
    vx[2][0] = x1; vx[2][1] = y3;
    vx[3][0] = x2; vx[3][1] = y3;
    vx[4][0] = x3; vx[4][1] = y2;
    vx[5][0] = x3; vx[5][1] = y1;
    vx[6][0] = x2; vx[6][1] = y0;
    vx[7][0] = x1; vx[7][1] = y0;

    glBegin(GL_TRIANGLE_FAN);
    AUX_SETCOLOR(windType, AUX_WHITE);
    glVertex2i(0, 0);
    for (i = 0; i < 8; i++) {
        AUX_SETCOLOR(windType, AUX_WHITE-i);
        glVertex2iv(vx[i]);
    }
    glEnd();

    glBegin(GL_TRIANGLE_FAN);
    glVertex2i(0, 0);
    glVertex2i(-100, 100);
    glEnd();
}

static void Rect(void)
{

    AUX_SETCOLOR(windType, AUX_GREEN);
    glRecti(-boxW/4, -boxH/4, boxW/4, boxH/4);
}

static void xPolygon(void)
{
    GLint vx[8][2];
    GLint x0, y0, x1, y1, x2, y2, x3, y3;
    GLint i;

    y0 = -boxH/4;
    y1 = y0 + boxH/2/3;
    y2 = y1 + boxH/2/3;
    y3 = boxH/4;
    x0 = -boxW/4;
    x1 = x0 + boxW/2/3;
    x2 = x1 + boxW/2/3;
    x3 = boxW/4;

    vx[0][0] = x0; vx[0][1] = y1;
    vx[1][0] = x0; vx[1][1] = y2;
    vx[2][0] = x1; vx[2][1] = y3;
    vx[3][0] = x2; vx[3][1] = y3;
    vx[4][0] = x3; vx[4][1] = y2;
    vx[5][0] = x3; vx[5][1] = y1;
    vx[6][0] = x2; vx[6][1] = y0;
    vx[7][0] = x1; vx[7][1] = y0;

    glBegin(GL_POLYGON);
    for (i = 0; i < 8; i++) {
        AUX_SETCOLOR(windType, AUX_WHITE-i);
        glVertex2iv(vx[i]);
    }
    glEnd();

    glBegin(GL_POLYGON);
    glVertex2i(0, 0);
    glVertex2i(100, 100);
    glEnd();
}

static void Quads(void)
{

    glBegin(GL_QUADS);
    AUX_SETCOLOR(windType, AUX_GREEN);
    glVertex2i(-boxW/4, -boxH/4);
    AUX_SETCOLOR(windType, AUX_RED);
    glVertex2i(-boxW/8, -boxH/16);
    AUX_SETCOLOR(windType, AUX_BLUE);
    glVertex2i(boxW/8, -boxH/16);
    AUX_SETCOLOR(windType, AUX_WHITE);
    glVertex2i(boxW/4, -boxH/4);

    AUX_SETCOLOR(windType, AUX_GREEN);
    glVertex2i(-boxW/4, boxH/4);
    AUX_SETCOLOR(windType, AUX_RED);
    glVertex2i(-boxW/8, boxH/16);
    AUX_SETCOLOR(windType, AUX_BLUE);
    glVertex2i(boxW/8, boxH/16);
    AUX_SETCOLOR(windType, AUX_WHITE);
    glVertex2i(boxW/4, boxH/4);
    glEnd();

    glBegin(GL_QUADS);
    glVertex2i(0, 0);
    glVertex2i(100, 100);
    glVertex2i(-100, 100);
    glEnd();
}

static void QuadStrip(void)
{

    glBegin(GL_QUAD_STRIP);
    AUX_SETCOLOR(windType, AUX_GREEN);
    glVertex2i(-boxW/4, -boxH/4);
    AUX_SETCOLOR(windType, AUX_RED);
    glVertex2i(-boxW/4, boxH/4);
    AUX_SETCOLOR(windType, AUX_BLUE);
    glVertex2i(0, -boxH/4);
    AUX_SETCOLOR(windType, AUX_WHITE);
    glVertex2i(0, boxH/4);
    AUX_SETCOLOR(windType, AUX_CYAN);
    glVertex2i(boxW/4, -boxH/4);
    AUX_SETCOLOR(windType, AUX_YELLOW);
    glVertex2i(boxW/4, boxH/4);
    glEnd();

    glBegin(GL_QUAD_STRIP);
    glVertex2i(0, 0);
    glVertex2i(100, 100);
    glVertex2i(-100, 100);
    glEnd();
}

static void CALLBACK Draw(void)
{

    glViewport(0, 0, windW, windH);
    glDisable(GL_SCISSOR_TEST);

    glPushAttrib(GL_COLOR_BUFFER_BIT);

    glColorMask(1, 1, 1, 1);
    glIndexMask((GLuint)~0);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glPopAttrib();

    if (mode1) {
    glShadeModel(GL_SMOOTH);
    } else {
    glShadeModel(GL_FLAT);
    }

    if (mode2) {
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    } else {
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }

    Viewport(0, 0); Point();
    Viewport(0, 1); Lines();
    Viewport(0, 2); LineStrip();
    Viewport(0, 3); LineLoop();

    Viewport(1, 0); Bitmap();

    Viewport(1, 1); TriangleFan();
    Viewport(1, 2); Triangles();
    Viewport(1, 3); TriangleStrip();

    Viewport(2, 0); Rect();
    Viewport(2, 1); xPolygon();
    Viewport(2, 2); Quads();
    Viewport(2, 3); QuadStrip();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-ci") == 0) {
        rgb = GL_FALSE;
    } else if (strcmp(argv[i], "-rgb") == 0) {
        rgb = GL_TRUE;
    } else if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    windW = 600;
    windH = 300;
    auxInitPosition(0, 0, windW, windH);

    windType |= (rgb) ? AUX_RGB : AUX_INDEX;
    windType |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(windType);

    if (auxInitWindow("Primitive Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_1, Key_1);
    auxKeyFunc(AUX_2, Key_2);
    auxKeyFunc(AUX_3, Key_3);
    auxMainLoop(Draw);
}


TQUAD.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

#define PI 3.141592654
#define BLACK 0
#define GRAY 128
#define WHITE 255
#define RD 0xA40000FF
#define WT 0xFFFFFFFF
#define brickImageWidth 16
#define brickImageHeight 16

static void CALLBACK ErrorHandler(GLenum which);
static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_LEFT(void );
static void CALLBACK Key_RIGHT(void );
static void CALLBACK Key_UP(void );
static void CALLBACK Key_DOWN(void );
static void CALLBACK Key_X(void );
static void CALLBACK Key_x(void );
static void CALLBACK Key_1(void );
static void CALLBACK Key_2(void );
static void CALLBACK Key_3(void );
static void CALLBACK Key_4(void );
static void CALLBACK Key_0(void );
static void CALLBACK Key_A(void );
static void CALLBACK Key_a(void );
static void CALLBACK Key_S(void );
static void CALLBACK Key_s(void );
static void CALLBACK Key_d(void );
static void CALLBACK Key_f(void );
static void CALLBACK Key_G(void );
static void CALLBACK Key_g(void );
static void CALLBACK Key_J(void );
static void CALLBACK Key_j(void );
static void CALLBACK Key_H(void );
static void CALLBACK Key_h(void );
static void CALLBACK Key_K(void );
static void CALLBACK Key_k(void );
static void CALLBACK Key_L(void );
static void CALLBACK Key_l(void );
static void CALLBACK Key_z(void );
static void CALLBACK Key_q(void );
static void CALLBACK Key_w(void );
static void CALLBACK Key_e(void );
static void CALLBACK Key_r(void );
static void CALLBACK Key_t(void );
static void CALLBACK Key_y(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum rgb, doubleBuffer;

float black[3] = {
    0.0, 0.0, 0.0
};
float blue[3] =  {
    0.0, 0.0, 1.0
};
float gray[3] =  {
    0.5, 0.5, 0.5
};
float white[3] = {
    1.0, 1.0, 1.0
};

GLenum doDither = GL_TRUE;
GLenum shade = GL_TRUE;
GLenum texture = GL_TRUE;

float xRotation = 30.0, yRotation = 30.0, zRotation = 0.0;
GLint radius1, radius2;
GLdouble angle1, angle2;
GLint slices, stacks;
GLint height;
GLint orientation = GLU_OUTSIDE;
GLint whichQuadric;
GLUquadricObj *quadObj;

GLuint brickImage[brickImageWidth*brickImageHeight] = {
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD,
    WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD, RD,
    WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    RD, RD, RD, RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD,
    WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT, WT,
    RD, RD, RD, RD, WT, RD, RD, RD, RD, RD, RD, RD, RD, RD, WT, RD
};
char *texFileName = 0;

static void CALLBACK ErrorHandler(GLenum which)
{

    fprintf(stderr, "Quad Error: %s\n", gluErrorString(which));
}

static void Init(void)
{
    static GLint colorIndexes[3] = {0, 200, 255};
    static float ambient[] = {0.1, 0.1, 0.1, 1.0};
    static float diffuse[] = {0.5, 1.0, 1.0, 1.0};
    static float position[] = {90.0, 90.0, 150.0, 0.0};
    static float front_mat_shininess[] = {30.0};
    static float front_mat_specular[] = {0.2, 0.2, 0.2, 1.0};
    static float front_mat_diffuse[] = {0.5, 0.28, 0.38, 1.0};
    static float back_mat_shininess[] = {50.0};
    static float back_mat_specular[] = {0.5, 0.5, 0.2, 1.0};
    static float back_mat_diffuse[] = {1.0, 1.0, 0.2, 1.0};
    static float lmodel_ambient[] = {1.0, 1.0, 1.0, 1.0};
    static float lmodel_twoside[] = {GL_TRUE};
    static float decal[] = {GL_DECAL};
    static float modulate[] = {GL_MODULATE};
    static float repeat[] = {GL_REPEAT};
    static float nearest[] = {GL_NEAREST};
    AUX_RGBImageRec *image;

    if (!rgb) {
    auxSetGreyRamp();
    }
    glClearColor(0.0, 0.0, 0.0, 0.0);

    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glMaterialfv(GL_FRONT, GL_SHININESS, front_mat_shininess);
    glMaterialfv(GL_FRONT, GL_SPECULAR, front_mat_specular);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, front_mat_diffuse);
    glMaterialfv(GL_BACK, GL_SHININESS, back_mat_shininess);
    glMaterialfv(GL_BACK, GL_SPECULAR, back_mat_specular);
    glMaterialfv(GL_BACK, GL_DIFFUSE, back_mat_diffuse);
    if (!rgb) {
    glMaterialiv( GL_FRONT_AND_BACK, GL_COLOR_INDEXES, colorIndexes);
    }

    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, decal);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeat);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, nearest);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, nearest);
    if (texFileName) {
    image = auxRGBImageLoad(texFileName);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image->sizeX, image->sizeY,
              GL_RGB, GL_UNSIGNED_BYTE, image->data);
    } else {
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D, 0, 4, brickImageWidth, brickImageHeight,
             0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)brickImage);
    }

    quadObj = gluNewQuadric();
    //gluQuadricCallback(quadObj, GLU_ERROR, ErrorHandler);

    radius1 = 10;
    radius2 = 5;
    angle1 = 90;
    angle2 = 180;
    slices = 16;
    stacks = 10;
    height = 20;
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-1, 1, -1, 1, 1, 10);
    gluLookAt(2, 2, 2, 0, 0, 0, 0, 0, 1);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Key_LEFT(void)
{
    yRotation += 5;
}

static void CALLBACK Key_RIGHT(void)
{
    yRotation -= 5;
}

static void CALLBACK Key_UP(void)
{
    xRotation += 5;
}

static void CALLBACK Key_DOWN(void)
{
    xRotation -= 5;
}

static void CALLBACK Key_X(void)
{
    zRotation += 5;
}

static void CALLBACK Key_x(void)
{
    zRotation -= 5;
}

static void CALLBACK Key_1(void)
{
    gluQuadricDrawStyle(quadObj, GLU_FILL);
}

static void CALLBACK Key_2(void)
{
    gluQuadricDrawStyle(quadObj, GLU_POINT);
}

static void CALLBACK Key_3(void)
{
    gluQuadricDrawStyle(quadObj, GLU_LINE);
}

static void CALLBACK Key_4(void)
{
    gluQuadricDrawStyle(quadObj, GLU_SILHOUETTE);
}

static void CALLBACK Key_0(void)
{
    shade = !shade;
    if (shade) {
        glShadeModel(GL_SMOOTH);
        gluQuadricNormals(quadObj, GLU_SMOOTH);
    } else {
        glShadeModel(GL_FLAT);
        gluQuadricNormals(quadObj, GLU_FLAT);
    }
}

static void CALLBACK Key_A(void)
{
    stacks++;
}

static void CALLBACK Key_a(void)
{
    if (stacks > 1)
        stacks--;
}

static void CALLBACK Key_S(void)
{
    slices++;
}

static void CALLBACK Key_s(void)
{
    if (slices > 3)
        slices--;
}

static void CALLBACK Key_d(void)
{
    switch(orientation) {
      case GLU_OUTSIDE:
        orientation = GLU_INSIDE;
        break;
      case GLU_INSIDE:
      default:
        orientation = GLU_OUTSIDE;
        break;
    }
    gluQuadricOrientation(quadObj, orientation);
}

static void CALLBACK Key_f(void)
{
    whichQuadric = (whichQuadric + 1) & 0x03;
}

static void CALLBACK Key_G(void)
{
    radius1 += 1;
}

static void CALLBACK Key_g(void)
{
    if (radius1 > 0)
        radius1 -= 1;
}

static void CALLBACK Key_J(void)
{
    radius2 += 1;
}

static void CALLBACK Key_j(void)
{
    if (radius2 > 0)
        radius2 -= 1;
}

static void CALLBACK Key_H(void)
{
    height += 2;
}

static void CALLBACK Key_h(void)
{
    if (height > 0)
        height -= 2;
}

static void CALLBACK Key_K(void)
{
    angle1 += 5;
}

static void CALLBACK Key_k(void)
{
    angle1 -= 5;
}

static void CALLBACK Key_L(void)
{
    angle2 += 5;
}

static void CALLBACK Key_l(void)
{
    angle2 -= 5;
}

static void CALLBACK Key_z(void)
{
        texture = !texture;
    if (texture) {
        gluQuadricTexture(quadObj, GL_TRUE);
        glEnable(GL_TEXTURE_2D);
    } else {
        gluQuadricTexture(quadObj, GL_FALSE);
        glDisable(GL_TEXTURE_2D);
    }
}

static void CALLBACK Key_q(void)
{
    glDisable(GL_CULL_FACE);
}

static void CALLBACK Key_w(void)
{
    glEnable(GL_CULL_FACE);
    glCullFace(GL_FRONT);
}

static void CALLBACK Key_e(void)
{
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
}

static void CALLBACK Key_r(void)
{
    glFrontFace(GL_CW);
}

static void CALLBACK Key_t(void)
{
    glFrontFace(GL_CCW);
}

static void CALLBACK Key_y(void)
{
    doDither = !doDither;
    (doDither) ? glEnable(GL_DITHER) : glDisable(GL_DITHER);
}

static void CALLBACK Draw(void)
{

    glLoadIdentity();
    glRotatef(xRotation, 1, 0, 0);
    glRotatef(yRotation, 0, 1, 0);
    glRotatef(zRotation, 0, 0, 1);

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glColor3f(1.0, 1.0, 1.0);
    switch (whichQuadric) {
      case 0:
    glTranslatef(0, 0, -height/20.0);
    gluCylinder(quadObj, radius1/10.0, radius2/10.0, height/10.0,
            slices, stacks);
    break;
      case 1:
    gluSphere(quadObj, radius1/10.0, slices, stacks);
    break;
      case 2:
    gluPartialDisk(quadObj, radius2/10.0, radius1/10.0, slices,
               stacks, angle1, angle2);
    break;
      case 3:
    gluDisk(quadObj, radius2/10.0, radius1/10.0, slices, stacks);
    break;
    }

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-ci") == 0) {
        rgb = GL_FALSE;
    } else if (strcmp(argv[i], "-rgb") == 0) {
        rgb = GL_TRUE;
    } else if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else if (strcmp(argv[i], "-f") == 0) {
        if (i+1 >= argc || argv[i+1][0] == '-') {
        //printf("-f (No file name).\n");
        return GL_FALSE;
        } else {
        texFileName = argv[++i];
        }
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    auxInitPosition(0, 0, 300, 300);

    type = AUX_DEPTH16;
    type |= (rgb) ? AUX_RGB : AUX_INDEX;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Quad Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_LEFT, Key_LEFT);
    auxKeyFunc(AUX_RIGHT, Key_RIGHT);
    auxKeyFunc(AUX_UP, Key_UP);
    auxKeyFunc(AUX_DOWN, Key_DOWN);
    auxKeyFunc(AUX_X, Key_X);
    auxKeyFunc(AUX_x, Key_x);
    auxKeyFunc(AUX_1, Key_1);
    auxKeyFunc(AUX_2, Key_2);
    auxKeyFunc(AUX_3, Key_3);
    auxKeyFunc(AUX_4, Key_4);
    auxKeyFunc(AUX_0, Key_0);
    auxKeyFunc(AUX_A, Key_A);
    auxKeyFunc(AUX_a, Key_a);
    auxKeyFunc(AUX_S, Key_S);
    auxKeyFunc(AUX_s, Key_s);
    auxKeyFunc(AUX_d, Key_d);
    auxKeyFunc(AUX_f, Key_f);
    auxKeyFunc(AUX_G, Key_G);
    auxKeyFunc(AUX_g, Key_g);
    auxKeyFunc(AUX_J, Key_J);
    auxKeyFunc(AUX_j, Key_j);
    auxKeyFunc(AUX_H, Key_H);
    auxKeyFunc(AUX_h, Key_h);
    auxKeyFunc(AUX_K, Key_K);
    auxKeyFunc(AUX_k, Key_k);
    auxKeyFunc(AUX_L, Key_L);
    auxKeyFunc(AUX_l, Key_l);
    auxKeyFunc(AUX_z, Key_z);
    auxKeyFunc(AUX_Q, Key_q);
    auxKeyFunc(AUX_w, Key_w);
    auxKeyFunc(AUX_e, Key_e);
    auxKeyFunc(AUX_r, Key_r);
    auxKeyFunc(AUX_t, Key_t);
    auxKeyFunc(AUX_y, Key_y);
    auxMainLoop(Draw);
}


TSELECT.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <gl\glaux.h>

#define MAXOBJS 10000
#define MAXSELECT 100
#define MAXFEED 300
#define SOLID 1
#define LINE 2
#define POINT 3

static void InitObjects(GLint num);
static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void Render(GLenum mode);
static GLint DoSelect(GLint x, GLint y);
static void RecolorTri(GLint h);
static void DeleteTri(GLint h);
static void GrowTri(GLint h);
static GLenum CALLBACK Mouse_left(struct _AUX_EVENTREC *event);
static GLenum CALLBACK Mouse_middle(struct _AUX_EVENTREC *event);
static GLenum CALLBACK Mouse_right(struct _AUX_EVENTREC *event);
static void CALLBACK Draw(void );
static void DrawZoom(GLint x, GLint y);
static void DumpFeedbackVert(GLint *i, GLint n);
static void DrawFeedback(GLint n);
static void DoFeedback(void );
static void CALLBACK Key_LEFT(void );
static void CALLBACK Key_RIGHT(void );
static void CALLBACK Key_Z(void );
static void CALLBACK Key_z(void );
static void CALLBACK Key_f(void );
static void CALLBACK Key_d(void );
static void CALLBACK Key_l(void );

GLint windW, windH;

GLuint selectBuf[MAXSELECT];
GLfloat feedBuf[MAXFEED];
GLint vp[4];
float zRotation = 90.0;
float zoom = 1.0;
GLint objectCount;
GLint numObjects;
struct object {
    float v1[2];
    float v2[2];
    float v3[2];
    float color[3];
} objects[MAXOBJS];
GLenum linePoly = GL_FALSE;

static void InitObjects(GLint num)
{
    GLint i;
    float x, y;

    if (num > MAXOBJS) {
    num = MAXOBJS;
    }
    if (num < 1) {
    num = 1;
    }
    objectCount = num;

    srand((unsigned int)time(NULL));
    for (i = 0; i < num; i++) {
    x = (rand() % 300) - 150;
    y = (rand() % 300) - 150;

    objects[i].v1[0] = x + (rand() % 50) - 25;
    objects[i].v2[0] = x + (rand() % 50) - 25;
    objects[i].v3[0] = x + (rand() % 50) - 25;
    objects[i].v1[1] = y + (rand() % 50) - 25;
    objects[i].v2[1] = y + (rand() % 50) - 25;
    objects[i].v3[1] = y + (rand() % 50) - 25;
    objects[i].color[0] = ((rand() % 100) + 50) / 150.0;
    objects[i].color[1] = ((rand() % 100) + 50) / 150.0;
    objects[i].color[2] = ((rand() % 100) + 50) / 150.0;
    }
}

static void Init(void)
{

    numObjects = 10;
    InitObjects(numObjects);
    glGetIntegerv(GL_VIEWPORT, vp);
}

static void CALLBACK Reshape(int width, int height)
{

    windW = (GLint)width;
    windH = (GLint)height;
}

static void Render(GLenum mode)
{
    GLint i;

    for (i = 0; i < objectCount; i++) {
    if (mode == GL_SELECT) {
        glLoadName(i);
    }
    glColor3fv(objects[i].color);
    glBegin(GL_POLYGON);
        glVertex2fv(objects[i].v1);
        glVertex2fv(objects[i].v2);
        glVertex2fv(objects[i].v3);
    glEnd();
    }
}

static GLint DoSelect(GLint x, GLint y)
{
    GLint hits;

    glSelectBuffer(MAXSELECT, selectBuf);
    (void)glRenderMode(GL_SELECT);
    glInitNames();
    glPushName((GLuint)~0);

    glPushMatrix();

    glViewport(0, 0, windW, windH);
    glGetIntegerv(GL_VIEWPORT, vp);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPickMatrix(x, windH-y, 4, 4, vp);
    gluOrtho2D(-175, 175, -175, 175);
    glMatrixMode(GL_MODELVIEW);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glScalef(zoom, zoom, zoom);
    glRotatef(zRotation, 0, 0, 1);

    Render(GL_SELECT);

    glPopMatrix();

    hits = glRenderMode(GL_RENDER);
    if (hits <= 0) {
    return -1;
    }

    return selectBuf[(hits-1)*4+3];
}

static void RecolorTri(GLint h)
{

    objects[h].color[0] = ((rand() % 100) + 50) / 150.0;
    objects[h].color[1] = ((rand() % 100) + 50) / 150.0;
    objects[h].color[2] = ((rand() % 100) + 50) / 150.0;
}

static void DeleteTri(GLint h)
{

    objects[h] = objects[objectCount-1];
    objectCount--;
}

static void GrowTri(GLint h)
{
    float v[2];
    float *oldV;
    GLint i;

    v[0] = objects[h].v1[0] + objects[h].v2[0] + objects[h].v3[0];
    v[1] = objects[h].v1[1] + objects[h].v2[1] + objects[h].v3[1];
    v[0] /= 3;
    v[1] /= 3;

    for (i = 0; i < 3; i++) {
    switch (i) {
      case 0:
        oldV = objects[h].v1;
        break;
      case 1:
        oldV = objects[h].v2;
        break;
      case 2:
        oldV = objects[h].v3;
        break;
    }
    oldV[0] = 1.5 * (oldV[0] - v[0]) + v[0];
    oldV[1] = 1.5 * (oldV[1] - v[1]) + v[1];
    }
}

static GLenum CALLBACK Mouse_left(AUX_EVENTREC *event)
{
    GLint mouseX, mouseY;
    GLint hit;

    mouseX = event->data[AUX_MOUSEX];
    mouseY = event->data[AUX_MOUSEY];
    hit = DoSelect((GLint)mouseX, (GLint)mouseY);
    if (hit != -1) {
        RecolorTri(hit);
    }
    return GL_FALSE;
}

static GLenum CALLBACK Mouse_middle(AUX_EVENTREC *event)
{
    GLint mouseX, mouseY;
    GLint hit;

    mouseX = event->data[AUX_MOUSEX];
    mouseY = event->data[AUX_MOUSEY];
    hit = DoSelect((GLint)mouseX, (GLint)mouseY);
    if (hit != -1) {
        GrowTri(hit);
    }
    return GL_FALSE;
}

static GLenum CALLBACK Mouse_right(AUX_EVENTREC *event)
{
    GLint mouseX, mouseY;
    GLint hit;

    mouseX = event->data[AUX_MOUSEX];
    mouseY = event->data[AUX_MOUSEY];
    hit = DoSelect((GLint)mouseX, (GLint)mouseY);
    if (hit != -1) {
        DeleteTri(hit);
    }
    return GL_FALSE;
}

static void CALLBACK Draw(void)
{

    glPushMatrix();

    glViewport(0, 0, windW, windH);
    glGetIntegerv(GL_VIEWPORT, vp);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-175, 175, -175, 175);
    glMatrixMode(GL_MODELVIEW);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glScalef(zoom, zoom, zoom);
    glRotatef(zRotation, 0, 0, 1);

    Render(GL_RENDER);

    glPopMatrix();

    glFlush();
}

static void DrawZoom(GLint x, GLint y)
{

    glPushMatrix();

    glViewport(0, 0, windW, windH);
    glGetIntegerv(GL_VIEWPORT, vp);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPickMatrix(x, windH-y, 4, 4, vp);
    gluOrtho2D(-175, 175, -175, 175);
    glMatrixMode(GL_MODELVIEW);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glScalef(zoom, zoom, zoom);
    glRotatef(zRotation, 0, 0, 1);

    Render(GL_RENDER);

    glPopMatrix();
}

static void DumpFeedbackVert(GLint *i, GLint n)
{
    GLint index;

    index = *i;
    if (index+7 > n) {
    *i = n;
    //printf("  ???\n");
    return;
    }
/*
    printf("  (%g %g %g), color = (%4.2f %4.2f %4.2f)\n",
       feedBuf[index],
       feedBuf[index+1],
       feedBuf[index+2],
       feedBuf[index+3],
       feedBuf[index+4],
       feedBuf[index+5]);
*/
    index += 7;
    *i = index;
}

static void DrawFeedback(GLint n)
{
    GLint i;
    GLint verts;

    //printf("Feedback results (%d floats):\n", n);
    for (i = 0; i < n; i++) {
    switch ((GLint)feedBuf[i]) {
      case GL_POLYGON_TOKEN:
        //printf("Polygon");
        i++;
        if (i < n) {
        verts = (GLint)feedBuf[i];
        i++;
        //printf(": %d vertices", verts);
        } else {
        verts = 0;
        }
        //printf("\n");
        while (verts) {
        DumpFeedbackVert(&i, n);
        verts--;
        }
        i--;
        break;
      case GL_LINE_TOKEN:
        //printf("Line:\n");
        i++;
        DumpFeedbackVert(&i, n);
        DumpFeedbackVert(&i, n);
        i--;
        break;
      case GL_LINE_RESET_TOKEN:
        //printf("Line Reset:\n");
        i++;
        DumpFeedbackVert(&i, n);
        DumpFeedbackVert(&i, n);
        i--;
        break;
      default:
        //printf("%9.2f\n", feedBuf[i]);
        break;
    }
    }
/*
    if (i == MAXFEED) {
    printf("...\n");
    }
    printf("\n");
*/
}

static void DoFeedback(void)
{
    GLint x;

    glFeedbackBuffer(MAXFEED, GL_3D_COLOR, feedBuf);
    (void)glRenderMode(GL_FEEDBACK);

    glPushMatrix();

    glViewport(0, 0, windW, windH);
    glGetIntegerv(GL_VIEWPORT, vp);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-175, 175, -175, 175);
    glMatrixMode(GL_MODELVIEW);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glScalef(zoom, zoom, zoom);
    glRotatef(zRotation, 0, 0, 1);

    Render(GL_FEEDBACK);

    glPopMatrix();

    x = glRenderMode(GL_RENDER);
    if (x == -1) {
    x = MAXFEED;
    }

    DrawFeedback((GLint)x);
}

static void CALLBACK Key_LEFT(void)
{
    zRotation += 0.5;
}

static void CALLBACK Key_RIGHT(void)
{
    zRotation -= 0.5;
}

static void CALLBACK Key_Z(void)
{
    zoom /= 0.75;
}

static void CALLBACK Key_z(void)
{
    zoom *= 0.75;
}

static void CALLBACK Key_f(void)
{
    DoFeedback();
}

static void CALLBACK Key_d(void)
{
    GLint mouseX, mouseY;

    auxGetMouseLoc(&mouseX, &mouseY);
    DrawZoom((GLint)mouseX, (GLint)mouseY);
}

static void CALLBACK Key_l(void)
{
    linePoly = !linePoly;
    if (linePoly) {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    } else {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
}

void main(int argc, char **argv)
{
    GLenum type;

    windW = 300;
    windH = 300;
    auxInitPosition(0, 0, windW, windH);

    type = AUX_RGB | AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Select Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_LEFT, Key_LEFT);
    auxKeyFunc(AUX_RIGHT, Key_RIGHT);
    auxKeyFunc(AUX_Z, Key_Z);
    auxKeyFunc(AUX_z, Key_z);
    auxKeyFunc(AUX_f, Key_f);
    auxKeyFunc(AUX_d, Key_d);
    auxKeyFunc(AUX_l, Key_l);
    auxMouseFunc(AUX_LEFTBUTTON, AUX_MOUSEDOWN, Mouse_left);
    auxMouseFunc(AUX_MIDDLEBUTTON, AUX_MOUSEDOWN, Mouse_middle);
    auxMouseFunc(AUX_RIGHTBUTTON, AUX_MOUSEDOWN, Mouse_right);
    auxMainLoop(Draw);
}


TSPEED.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

#ifdef __unix
#include <sys/times.h>
#include <sys/param.h>
#endif
#ifdef WIN32
#include <time.h>
#endif

#define GAP 10
#define ROWS 1
#define COLS 4

static void CALLBACK SetWindSize(int width,int height);
static void CALLBACK Key_a(void );
static void CALLBACK Key_d(void );
static void CALLBACK Key_f(void );
static void CALLBACK Key_F(void );
static void CALLBACK Key_s(void );
static void CALLBACK Key_t(void );
static void Viewport(GLint row, GLint column);
static double Now(void );
static void Report(const char *msg,float elapsed);
static void Points(void );
static void Lines(void );
static void Triangles(void );
static void Rects(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum rgb, doubleBuffer, windType;
GLint windW, windH;

GLint boxW, boxH;

GLenum antialiasing = GL_FALSE;
GLenum depthTesting = GL_FALSE;
GLenum fogging = GL_FALSE, niceFogging = GL_FALSE;
GLenum lighting = GL_FALSE;
GLenum shading = GL_FALSE;
GLenum texturing = GL_FALSE;

GLint repeatCount = 100;
GLint loopCount = 10;

GLubyte texture[4*3] = {
    0xFF, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0xFF, 0,
};

static void CALLBACK SetWindSize(int width, int height)
{

    windW = (GLint)width;
    windH = (GLint)height;
}

static void CALLBACK Key_a(void)
{
    antialiasing = !antialiasing;
}

static void CALLBACK Key_d(void)
{
    depthTesting = !depthTesting;
}

static void CALLBACK Key_f(void)
{
    fogging = !fogging;
}

static void CALLBACK Key_F(void)
{
    niceFogging = !niceFogging;
}

static void CALLBACK Key_s(void)
{
    shading = !shading;
}

static void CALLBACK Key_t(void)
{
    texturing = !texturing;
}

static void Viewport(GLint row, GLint column)
{
    GLint x, y;

    boxW = (windW - (COLS + 1) * GAP) / COLS;
    boxH = (windH - (ROWS + 1) * GAP) / ROWS;

    x = GAP + column * (boxW + GAP);
    y = GAP + row * (boxH + GAP);

    glViewport(x, y, boxW, boxH);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-boxW/2, boxW/2, -boxH/2, boxH/2);
    glMatrixMode(GL_MODELVIEW);

    glEnable(GL_SCISSOR_TEST);
    glScissor(x, y, boxW, boxH);
}

static double Now(void)
{
#ifdef __unix
    struct tms tm;
    clock_t clk;

    clk = times(&tm);
    return (double)clk / (double)HZ;
#else
#ifdef WIN32
    long sec;

    sec = time(NULL);
    return(sec);
#else
    return 0;
#endif
#endif
}

static void Report(const char *msg, float elapsed)
{

    if (elapsed == 0.0) {
    printf("%s per second: Unknown, elapsed time is zero\n", msg);
    } else {
    printf("%s per second: %g\n", msg, repeatCount*loopCount/elapsed);
    }
}

static void Points(void)
{
    GLint i, j;
    float v1[3];
    double start;

    start = Now();
    for (i = 0; i < repeatCount; i++) {
    v1[0] = 10;
    v1[1] = 10;
    v1[2] = 10;
    glBegin(GL_POINTS);
        for (j = 0; j < loopCount; j++) {
        glVertex2fv(v1);
        }
    glEnd();
    }
    glFinish();
    //Report("Points", Now()-start);
}

static void Lines(void)
{
    GLint i, j;
    float v1[3], v2[3];
    double start;

    start = Now();
    for (i = 0; i < repeatCount; i++) {
    v1[0] = 10;
    v1[1] = 10;
    v1[2] = 10;
    v2[0] = 20;
    v2[1] = 20;
    v2[2] = 10;
    glBegin(GL_LINES);
        for (j = 0; j < loopCount; j++) {
        glVertex2fv(v1);
        glVertex2fv(v2);
        }
    glEnd();
    }
    glFinish();
    //Report("Lines", Now()-start);
}

static void Triangles(void)
{
    GLint i, j;
    float v1[3], v2[3], v3[3], t1[2], t2[2], t3[2];
    double start;

    start = Now();

    v1[0] = 10;
    v1[1] = 10;
    v1[2] = 10;
    v2[0] = 20;
    v2[1] = 20;
    v2[2] = 10;
    v3[0] = 10;
    v3[1] = 20;
    v3[2] = 10;

    t1[0] = 0;
    t1[1] = 0;
    t2[0] = 1;
    t2[1] = 1;
    t3[0] = 0;
    t3[1] = 1;

    for (i = 0; i < repeatCount; i++) {
    glBegin(GL_TRIANGLES);
        for (j = 0; j < loopCount; j++) {
        if (texturing) {
            glTexCoord2fv(t1);
        }
        glVertex2fv(v1);
        if (texturing) {
            glTexCoord2fv(t2);
        }
        glVertex2fv(v2);
        if (texturing) {
            glTexCoord2fv(t3);
        }
        glVertex2fv(v3);
        }
    glEnd();
    }
    glFinish();
    //Report("Triangles", Now()-start);
}

static void Rects(void)
{
    GLint i, j;
    float v1[2], v2[2];
    double start;

    start = Now();
    for (i = 0; i < repeatCount; i++) {
    v1[0] = 10;
    v1[1] = 10;
    v2[0] = 20;
    v2[1] = 20;
    for (j = 0; j < loopCount; j++) {
        glRectfv(v1, v2);
    }
    }
    glFinish();
    //Report("Rects", Now()-start);
}

static void CALLBACK Draw(void)
{
    glDisable(GL_SCISSOR_TEST);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    AUX_SETCOLOR(windType, AUX_YELLOW);

    if (antialiasing) {
    glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
    glEnable(GL_BLEND);

    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_POLYGON_SMOOTH);
    }
    if (depthTesting) {
    glEnable(GL_DEPTH_TEST);
    }
    if (fogging) {
    glEnable(GL_FOG);
    glHint(GL_FOG_HINT, (niceFogging) ? GL_NICEST : GL_FASTEST);
    }
    if (lighting) {
    static GLfloat ambient[4] = {1, 0.5, 0.5, 0};

    glEnable(GL_NORMALIZE);
    glNormal3f(1.0, 1.0, 1.0);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    }
    (shading) ? glShadeModel(GL_SMOOTH) : glShadeModel(GL_FLAT);
    if (texturing) {
    static GLfloat modulate[1] = {GL_DECAL};
    static GLfloat clamp[1] = {GL_CLAMP};
    static GLfloat linear[1] = {GL_LINEAR};

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE,
             (GLvoid *)texture);
    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, modulate);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear);
    glEnable(GL_TEXTURE_2D);
    }

    Viewport(0, 0); Points();
    Viewport(0, 1); Lines();
    Viewport(0, 2); Triangles();
    Viewport(0, 3); Rects();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-ci") == 0) {
        rgb = GL_FALSE;
    } else if (strcmp(argv[i], "-rgb") == 0) {
        rgb = GL_TRUE;
    } else if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    windW = 600;
    windH = 300;
    auxInitPosition(0, 0, windW, windH);

    windType = AUX_DEPTH16;
    windType |= (rgb) ? AUX_RGB : AUX_INDEX;
    windType |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(windType);

    if (auxInitWindow("Speed Test") == GL_FALSE) {
    auxQuit();
    }

    auxExposeFunc((AUXEXPOSEPROC)SetWindSize);
    auxReshapeFunc((AUXRESHAPEPROC)SetWindSize);
    auxKeyFunc(AUX_a, Key_a);
    auxKeyFunc(AUX_d, Key_d);
    auxKeyFunc(AUX_f, Key_f);
    auxKeyFunc(AUX_F, Key_F);
    auxKeyFunc(AUX_s, Key_s);
    auxKeyFunc(AUX_t, Key_t);
    auxMainLoop(Draw);
}


TSPHERE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <gl\glaux.h>

#define FALSE 0
#define TRUE  1
#define PI    3.14159265358979323846

extern void BuildCylinder(int numEdges);
extern void BuildTorus(float rc,int numc,float rt,int numt);
extern void BuildCage(void );
extern void BuildCube(void );
extern void BuildLists(void );
extern void SetDeepestColor(void );
extern void SetDefaultSettings(void );
extern unsigned char *AlphaPadImage(int bufSize,unsigned char *inData,int alpha);
extern void Init(void );
extern void ReInit(void );
extern void CALLBACK Draw(void );
extern void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_LEFT(void );
static void CALLBACK Key_RIGHT(void );
static void CALLBACK Key_UP(void );
static void CALLBACK Key_DOWN(void );
static void CALLBACK Key_a(void );
static void CALLBACK Key_c(void );
static void CALLBACK Key_d(void );
static void CALLBACK Key_m(void );
static void CALLBACK Key_l(void );
static void CALLBACK Key_f(void );
static void CALLBACK Key_t(void );
static void CALLBACK Key_0(void );
static void CALLBACK Key_1(void );
static void CALLBACK Key_2(void );
static void CALLBACK Key_3(void );
static void CALLBACK Key_4(void );
static void CALLBACK Key_5(void );
static void CALLBACK Key_6(void );
static void CALLBACK Key_7(void );
static GLenum Args(int argc,char **argv );

GLenum doubleBuffer;
int W = 400, H = 400;

char *imageFileName = 0;
AUX_RGBImageRec *image;

int numComponents;

float *minFilter, *magFilter, *sWrapMode, *tWrapMode;
float decal[] = {GL_DECAL};
float modulate[] = {GL_MODULATE};
float repeat[] = {GL_REPEAT};
float clamp[] = {GL_CLAMP};
float nearest[] = {GL_NEAREST};
float linear[] = {GL_LINEAR};
float nearest_mipmap_nearest[] = {GL_NEAREST_MIPMAP_NEAREST};
float nearest_mipmap_linear[] = {GL_NEAREST_MIPMAP_LINEAR};
float linear_mipmap_nearest[] = {GL_LINEAR_MIPMAP_NEAREST};
float linear_mipmap_linear[] = {GL_LINEAR_MIPMAP_LINEAR};
GLint sphereMap[] = {GL_SPHERE_MAP};

float xRotation = 0.0, yRotation = 0.0;
float zTranslate = -4.0;
GLenum autoRotate = TRUE;
GLenum deepestColor = AUX_GREEN;
GLenum isLit = TRUE;
GLenum isFogged = FALSE;
float *textureEnvironment = modulate;

struct MipMap {
    int width, height;
    unsigned char *data;
};

int cube, cage, cylinder, torus, genericObject;

float c[6][4][4][3] = {
    {
    {
        {
        1.0, 1.0, -1.0
        },
        {
        0.0, 1.0, -1.0
        },
        {
        0.0, 0.0, -1.0
        },
        {
        1.0, 0.0, -1.0
        },
    },
    {
        {
        0.0, 1.0, -1.0
        },
        {
        -1.0, 1.0, -1.0
        },
        {
        -1.0, 0.0, -1.0
        },
        {
        0.0, 0.0, -1.0
        },
    },
    {
        {
        0.0,  0.0, -1.0
        },
        {
        -1.0, 0.0, -1.0
        },
        {
        -1.0, -1.0, -1.0
        },
        {
        0.0, -1.0, -1.0
        },
    },
    {
        {
        1.0, 0.0, -1.0
        },
        {
        0.0, 0.0, -1.0
        },
        {
        0.0, -1.0, -1.0
        },
        {
        1.0, -1.0, -1.0
        },
    },
    },
    {
    {
        {
        1.0, 1.0, 1.0
        },
        {
        1.0, 1.0, 0.0
        },
        {
        1.0, 0.0, 0.0
        },
        {
        1.0, 0.0, 1.0
        },
    },
    {
        {
        1.0, 1.0, 0.0
        },
        {
        1.0, 1.0, -1.0
        },
        {
        1.0, 0.0, -1.0
        },
        {
        1.0, 0.0, 0.0
        },
    },
    {
        {
        1.0, 0.0, -1.0
        },
        {
        1.0, -1.0, -1.0
        },
        {
        1.0, -1.0, 0.0
        },
        {
        1.0, 0.0, 0.0
        },
    },
    {
        {
        1.0, 0.0, 0.0
        },
        {
        1.0, -1.0, 0.0
        },
        {
        1.0, -1.0, 1.0
        },
        {
        1.0, 0.0, 1.0
        },
    },
    },
    {
    {
        {
        -1.0, 1.0, 1.0
        },
        {
        0.0, 1.0, 1.0
        },
        {
        0.0, 0.0, 1.0
        },
        {
        -1.0, 0.0, 1.0
        },
    },
    {
        {
        0.0, 1.0, 1.0
        },
        {
        1.0, 1.0, 1.0
        },
        {
        1.0, 0.0, 1.0
        },
        {
        0.0, 0.0, 1.0
        },
    },
    {
        {
        1.0, 0.0, 1.0
        },
        {
        1.0, -1.0, 1.0
        },
        {
        0.0, -1.0, 1.0
        },
        {
        0.0, 0.0, 1.0
        },
    },
    {
        {
        0.0, -1.0, 1.0
        },
        {
        -1.0, -1.0, 1.0
        },
        {
        -1.0, 0.0, 1.0
        },
        {
        0.0, 0.0, 1.0
        },
    },
    },
    {
    {
        {
        -1.0, 1.0, -1.0
        },
        {
        -1.0, 1.0, 0.0
        },
        {
        -1.0, 0.0, 0.0
        },
        {
        -1.0, 0.0, -1.0
        },
    },
    {
        {
        -1.0, 1.0, 0.0
        },
        {
        -1.0, 1.0, 1.0
        },
        {
        -1.0, 0.0, 1.0
        },
        {
        -1.0, 0.0, 0.0
        },
    },
    {
        {
        -1.0, 0.0, 1.0
        },
        {
        -1.0, -1.0, 1.0
        },
        {
        -1.0, -1.0, 0.0
        },
        {
        -1.0, 0.0, 0.0
        },
    },
    {
        {
        -1.0, -1.0, 0.0
        },
        {
        -1.0, -1.0, -1.0
        },
        {
        -1.0, 0.0, -1.0
        },
        {
        -1.0, 0.0, 0.0
        },
    },
    },
    {
    {
        {
        -1.0, 1.0, 1.0
        },
        {
        -1.0, 1.0, 0.0
        },
        {
        0.0, 1.0, 0.0
        },
        {
        0.0, 1.0, 1.0
        },
    },
    {
        {
        -1.0, 1.0, 0.0
        },
        {
        -1.0, 1.0, -1.0
        },
        {
        0.0, 1.0, -1.0
        },
        {
        0.0, 1.0, 0.0
        },
    },
    {
        {
        0.0, 1.0, -1.0
        },
        {
        1.0, 1.0, -1.0
        },
        {
        1.0, 1.0, 0.0
        },
        {
        0.0, 1.0, 0.0
        },
    },
    {
        {
        1.0, 1.0, 0.0
        },
        {
        1.0, 1.0, 1.0
        },
        {
        0.0, 1.0, 1.0
        },
        {
        0.0, 1.0, 0.0
        },
    },
    },
    {
    {
        {
        -1.0, -1.0, -1.0
        },
        {
        -1.0, -1.0, 0.0
        },
        {
        0.0, -1.0, 0.0
        },
        {
        0.0, -1.0, -1.0
        },
    },
    {
        {
        -1.0, -1.0, 0.0
        },
        {
        -1.0, -1.0, 1.0
        },
        {
        0.0, -1.0, 1.0
        },
        {
        0.0, -1.0, 0.0
        },
    },
    {
        {
        0.0, -1.0, 1.0
        },
        {
        1.0, -1.0, 1.0
        },
        {
        1.0, -1.0, 0.0
        },
        {
        0.0, -1.0, 0.0
        },
    },
    {
        {
        1.0, -1.0, 0.0
        },
        {
        1.0, -1.0, -1.0
        },
        {
        0.0, -1.0, -1.0
        },
        {
        0.0, -1.0, 0.0
        },
    },
    }
};

float n[6][3] = {
    {
    0.0, 0.0, -1.0
    },
    {
    1.0, 0.0, 0.0
    },
    {
    0.0, 0.0, 1.0
    },
    {
    -1.0, 0.0, 0.0
    },
    {
    0.0, 1.0, 0.0
    },
    {
    0.0, -1.0, 0.0
    }
};

GLfloat identity[16] = {
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1,
};

void BuildCylinder(int numEdges)
{
    int i, top = 1.0, bottom = -1.0;
    float x[100], y[100], angle;

    for (i = 0; i <= numEdges; i++) {
    angle = i * 2.0 * PI / numEdges;
    x[i] = cos(angle);
    y[i] = sin(angle);
    }

    glNewList(cylinder, GL_COMPILE);
    glBegin(GL_TRIANGLE_STRIP);
    for (i = 0; i <= numEdges; i++) {
        glNormal3f(x[i], y[i], 0.0);
        glVertex3f(x[i], y[i], bottom);
        glVertex3f(x[i], y[i], top);
    }
    glEnd();
    glBegin(GL_TRIANGLE_FAN);
    glNormal3f(0.0, 0.0, 1.0);
    glVertex3f(0.0, 0.0, top);
    for (i = 0; i <= numEdges; i++) {
        glVertex3f(x[i], -y[i], top);
    }
    glEnd();
    glBegin(GL_TRIANGLE_FAN);
    glNormal3f(0.0, 0.0, -1.0);
    glVertex3f(0.0, 0.0, bottom);
    for (i = 0; i <= numEdges; i++) {
        glVertex3f(x[i], y[i], bottom);
    }
    glEnd();
    glEndList();
}

void BuildTorus(float rc, int numc, float rt, int numt)
{
    int i, j, k;
    double s, t;
    double x, y, z;
    double pi, twopi;

    pi = 3.14159265358979323846;
    twopi = 2.0 * pi;

    glNewList(torus, GL_COMPILE);
    for (i = 0; i < numc; i++) {
    glBegin(GL_QUAD_STRIP);
        for (j = 0; j <= numt; j++) {
        for (k = 0; k <= 1; k++) {
        s = (i + k) % numc + 0.5;
        t = j % numt;

        x = cos(t*twopi/numt) * cos(s*twopi/numc);
        y = sin(t*twopi/numt) * cos(s*twopi/numc);
        z = sin(s*twopi/numc);
        glNormal3f(x, y, z);

        x = (rt + rc * cos(s*twopi/numc)) * cos(t*twopi/numt);
        y = (rt + rc * cos(s*twopi/numc)) * sin(t*twopi/numt);
        z = rc * sin(s*twopi/numc);
        glVertex3f(x, y, z);
        }
        }
    glEnd();
    }
    glEndList();
}

void BuildCage(void)
{
    int i;
    float inc;
    float right, left, top, bottom, front, back;

    front  = 0.0;
    back   = -8.0;

    left   = -4.0;
    bottom = -4.0;
    right  = 4.0;
    top    = 4.0;

    inc = 2.0 * 4.0 * 0.1;

    glNewList(cage, GL_COMPILE);
    for (i = 0; i < 10; i++) {

    /*
    ** Back
    */
    glBegin(GL_LINES);
        glVertex3f(left+i*inc, top,    back);
        glVertex3f(left+i*inc, bottom, back);
    glEnd();
    glBegin(GL_LINES);
        glVertex3f(right, bottom+i*inc, back);
        glVertex3f(left,  bottom+i*inc, back);
    glEnd();

    /*
    ** Front
    */
    glBegin(GL_LINES);
        glVertex3f(left+i*inc, top,    front);
        glVertex3f(left+i*inc, bottom, front);
    glEnd();
    glBegin(GL_LINES);
        glVertex3f(right, bottom+i*inc, front);
        glVertex3f(left,  bottom+i*inc, front);
    glEnd();

    /*
    ** Left
    */
    glBegin(GL_LINES);
        glVertex3f(left, bottom+i*inc, front);
        glVertex3f(left, bottom+i*inc, back);
    glEnd();
    glBegin(GL_LINES);
        glVertex3f(left, top,    back+i*inc);
        glVertex3f(left, bottom, back+i*inc);
    glEnd();

    /*
    ** Right
    */
    glBegin(GL_LINES);
        glVertex3f(right, top-i*inc, front);
        glVertex3f(right, top-i*inc, back);
    glEnd();
    glBegin(GL_LINES);
        glVertex3f(right, top,    back+i*inc);
        glVertex3f(right, bottom, back+i*inc);
    glEnd();

    /*
    ** Top
    */
    glBegin(GL_LINES);
        glVertex3f(left+i*inc, top, front);
        glVertex3f(left+i*inc, top, back);
    glEnd();
    glBegin(GL_LINES);
        glVertex3f(right, top, back+i*inc);
        glVertex3f(left,  top, back+i*inc);
    glEnd();

    /*
    ** Bottom
    */
    glBegin(GL_LINES);
        glVertex3f(right-i*inc, bottom, front);
        glVertex3f(right-i*inc, bottom, back);
    glEnd();
    glBegin(GL_LINES);
        glVertex3f(right, bottom, back+i*inc);
        glVertex3f(left,  bottom, back+i*inc);
    glEnd();
    }
    glEndList();
}

void BuildCube(void)
{
    int i, j;

    glNewList(cube, GL_COMPILE);
    for (i = 0; i < 6; i++) {
    for (j = 0; j < 4; j++) {
        glNormal3fv(n[i]);
        glBegin(GL_POLYGON);
        glVertex3fv(c[i][j][0]);
        glVertex3fv(c[i][j][1]);
        glVertex3fv(c[i][j][2]);
        glVertex3fv(c[i][j][3]);
        glEnd();
    }
    }
    glEndList();
}

void BuildLists(void)
{

    cube = glGenLists(1);
    BuildCube();

    cage = glGenLists(2);
    BuildCage();

    cylinder = glGenLists(3);
    BuildCylinder(60);

    torus = glGenLists(4);
    BuildTorus(0.65, 20, .85, 65);

    genericObject = torus;
}

void SetDeepestColor(void)
{
    int redBits, greenBits, blueBits;

    glGetIntegerv(GL_RED_BITS, &redBits);
    glGetIntegerv(GL_GREEN_BITS, &greenBits);
    glGetIntegerv(GL_BLUE_BITS, &blueBits);

    deepestColor = (redBits >= greenBits) ? AUX_RED : AUX_GREEN;
    deepestColor = (deepestColor >= (GLenum)blueBits) ? deepestColor : AUX_BLUE;
}

void SetDefaultSettings(void)
{

    magFilter = nearest;
    minFilter = nearest;
    sWrapMode = repeat;
    tWrapMode = repeat;
    textureEnvironment = modulate;
    autoRotate = TRUE;
}

unsigned char *AlphaPadImage(int bufSize, unsigned char *inData, int alpha)
{
    unsigned char *outData, *out_ptr, *in_ptr;
    int i;

    outData = malloc(bufSize * 4);
    out_ptr = outData;
    in_ptr = inData;

    for (i = 0; i < bufSize; i++) {
    *out_ptr++ = *in_ptr++;
    *out_ptr++ = *in_ptr++;
    *out_ptr++ = *in_ptr++;
    *out_ptr++ = alpha;
    }

    free (inData);
    return outData;
}

void Init(void)
{
    float ambient[] = {0.0, 0.0, 0.0, 1.0};
    float diffuse[] = {0.0, 1.0, 0.0, 1.0};
    float specular[] = {1.0, 1.0, 1.0, 1.0};
    float position[] = {2.0, 2.0,  0.0, 1.0};
    float fog_color[] = {0.0, 0.0, 0.0, 1.0};
    float mat_ambient[] = {0.0, 0.0, 0.0, 1.0};
    float mat_shininess[] = {90.0};
    float mat_specular[] = {1.0, 1.0, 1.0, 1.0};
    float mat_diffuse[] = {1.0, 1.0, 1.0, 1.0};
    float lmodel_ambient[] = {0.0, 0.0, 0.0, 1.0};
    float lmodel_twoside[] = {GL_TRUE};
    float lmodel_local_viewer[] = {GL_FALSE};

    SetDeepestColor();
    SetDefaultSettings();

    if (numComponents == 4) {
    image = auxRGBImageLoad(imageFileName);
    image->data = AlphaPadImage(image->sizeX*image->sizeY,
                                    image->data, 128);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    gluBuild2DMipmaps(GL_TEXTURE_2D, numComponents,
              image->sizeX, image->sizeY,
              GL_RGBA, GL_UNSIGNED_BYTE, image->data);
    } else {
    image = auxRGBImageLoad(imageFileName);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    gluBuild2DMipmaps(GL_TEXTURE_2D, numComponents,
              image->sizeX, image->sizeY,
              GL_RGB, GL_UNSIGNED_BYTE, image->data);
    }

    glFogf(GL_FOG_DENSITY, 0.125);
    glFogi(GL_FOG_MODE, GL_LINEAR);
    glFogf(GL_FOG_START, 4.0);
    glFogf(GL_FOG_END, 9.0);
    glFogfv(GL_FOG_COLOR, fog_color);

    glDepthFunc(GL_LEQUAL);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
    glLightfv(GL_LIGHT0, GL_POSITION, position);

    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
    glShadeModel(GL_SMOOTH);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glViewport(0, 0, W, H);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);

    glFrontFace(GL_CW);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);

    glEnable(GL_TEXTURE_2D);
    glTexGeniv(GL_S, GL_TEXTURE_GEN_MODE, sphereMap);
    glTexGeniv(GL_T, GL_TEXTURE_GEN_MODE, sphereMap);
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);

    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sWrapMode);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tWrapMode);

    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, textureEnvironment);

    BuildLists();
}

void ReInit(void)
{

    if (genericObject == torus) {
    glEnable(GL_DEPTH_TEST);
    } else  {
    glDisable(GL_DEPTH_TEST);
    }
    if (isFogged) {
    textureEnvironment = modulate;
    }

    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, textureEnvironment);
}

void CALLBACK Draw(void)
{

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-0.2, 0.2, -0.2, 0.2, 0.15, 9.0);
    glMatrixMode(GL_MODELVIEW);

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    if (isFogged) {
    glEnable(GL_FOG);
    glColor3fv(auxRGBMap[deepestColor]);
    } else {
    glColor3fv(auxRGBMap[AUX_WHITE]);
    }
    glDisable(GL_LIGHTING);
    glDisable(GL_LIGHT0);
    glDisable(GL_TEXTURE_2D);
    glCallList(cage);

    glPushMatrix();
    glTranslatef(0.0, 0.0, zTranslate);
    glRotatef(xRotation, 1, 0, 0);
    glRotatef(yRotation, 0, 1, 0);

    if (isLit == TRUE) {
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    }

    glEnable(GL_TEXTURE_2D);
    if (isFogged) {
    glDisable(GL_FOG);
    }
    glPolygonMode(GL_FRONT, GL_FILL);
    glColor3fv(auxRGBMap[deepestColor]);
    glCallList(genericObject);

    glPopMatrix();
    glFlush();

    if (autoRotate) {
    xRotation += .75;
    yRotation += .375;
    }
    auxSwapBuffers();
}

void CALLBACK Reshape(int width, int height)
{

    W = width;
    H = height;
    ReInit();
}

static void CALLBACK Key_LEFT(void)
{
    yRotation -= 0.5;
    autoRotate = FALSE;
    ReInit();
}

static void CALLBACK Key_RIGHT(void)
{
    yRotation += 0.5;
    autoRotate = FALSE;
    ReInit();
}

static void CALLBACK Key_UP(void)
{
    xRotation -= 0.5;
    autoRotate = FALSE;
    ReInit();
}

static void CALLBACK Key_DOWN(void)
{
    xRotation += 0.5;
    autoRotate = FALSE;
    ReInit();
}

static void CALLBACK Key_a(void)
{
    autoRotate = !autoRotate;
    ReInit();
}

static void CALLBACK Key_c(void)
{
    genericObject = (genericObject == cube) ? cylinder : cube;
    ReInit();
}

static void CALLBACK Key_d(void)
{
    textureEnvironment = decal;
    ReInit();
}

static void CALLBACK Key_m(void)
{
    textureEnvironment = modulate;
    ReInit();
}

static void CALLBACK Key_l(void)
{
    isLit = !isLit;
    ReInit();
}

static void CALLBACK Key_f(void)
{
    isFogged = !isFogged;
    ReInit();
}

static void CALLBACK Key_t(void)
{
    genericObject = torus;
    ReInit();
}

static void CALLBACK Key_0(void)
{
    magFilter = nearest;
    ReInit();
}

static void CALLBACK Key_1(void)
{
    magFilter = linear;
    ReInit();
}

static void CALLBACK Key_2(void)
{
    minFilter = nearest;
    ReInit();
}

static void CALLBACK Key_3(void)
{
    minFilter = linear;
    ReInit();
}

static void CALLBACK Key_4(void)
{
    minFilter = nearest_mipmap_nearest;
    ReInit();
}

static void CALLBACK Key_5(void)
{
    minFilter = nearest_mipmap_linear;
    ReInit();
}

static void CALLBACK Key_6(void)
{
    minFilter = linear_mipmap_nearest;
    ReInit();
}

static void CALLBACK Key_7(void)
{
    minFilter = linear_mipmap_linear;
    ReInit();
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    doubleBuffer = GL_FALSE;

    numComponents = 4;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else if (strcmp(argv[i], "-f") == 0) {
        if (i+1 >= argc || argv[i+1][0] == '-') {
        //printf("-f (No file name).\n");
        return GL_FALSE;
        } else {
        imageFileName = argv[++i];
        }
    } else if (strcmp(argv[i], "-4") == 0) {
        numComponents = 4;
    } else if (strcmp(argv[i], "-3") == 0) {
        numComponents = 3;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    if (imageFileName == 0) {
    //printf("No image file.\n");
    auxQuit();
    }

    auxInitPosition(0, 0, W, H);

    type = AUX_RGB;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Texture Test") == GL_FALSE) {
        auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_LEFT, Key_LEFT);
    auxKeyFunc(AUX_RIGHT, Key_RIGHT);
    auxKeyFunc(AUX_UP, Key_UP);
    auxKeyFunc(AUX_DOWN, Key_DOWN);
    auxKeyFunc(AUX_a, Key_a);
    auxKeyFunc(AUX_c, Key_c);
    auxKeyFunc(AUX_d, Key_d);
    auxKeyFunc(AUX_m, Key_m);
    auxKeyFunc(AUX_l, Key_l);
    auxKeyFunc(AUX_f, Key_f);
    auxKeyFunc(AUX_t, Key_t);
    auxKeyFunc(AUX_0, Key_0);
    auxKeyFunc(AUX_1, Key_1);
    auxKeyFunc(AUX_2, Key_2);
    auxKeyFunc(AUX_3, Key_3);
    auxKeyFunc(AUX_4, Key_4);
    auxKeyFunc(AUX_5, Key_5);
    auxKeyFunc(AUX_6, Key_6);
    auxKeyFunc(AUX_7, Key_7);
    auxIdleFunc(Draw);

    auxMainLoop(NULL);
}


TSTAR.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "GL/gl.h"
#include "GL/glu.h"
#include <gl\glaux.h>

#define PI 3.141592657

extern float Sin(float angle);
extern float Cos(float angle);
extern void NewStar(GLint n, GLint d);
extern void RotatePoint(float *x,float *y,float rotation);
extern void MoveStars(void );
extern GLenum StarPoint(GLint n);
extern void ShowStars(void );
static void Init(void );
extern void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_SPACE(void );
static void CALLBACK Key_t(void );
extern void CALLBACK Idle(void );
static GLenum Args(int argc,char **argv );

enum {
    NORMAL = 0,
    WEIRD = 1
};

enum {
    STREAK = 0,
    CIRCLE = 1
};

#define MAXSTARS 400
#define MAXPOS 10000
#define MAXWARP 10
#define MAXANGLES 6000

typedef struct _starRec {
    GLint type;
    float x[2], y[2], z[2];
    float offsetX, offsetY, offsetR, rotation;
} starRec;

GLenum doubleBuffer;
GLint windW, windH;

GLenum flag = NORMAL;
GLint starCount = MAXSTARS / 2;
float speed = 1.0;
GLint nitro = 0;
starRec stars[MAXSTARS];
float sinTable[MAXANGLES];

float Sin(float angle)
{

    return (sinTable[(GLint)angle % MAXANGLES]);
}

float Cos(float angle)
{

    return (sinTable[((GLint)angle+(MAXANGLES/4))%MAXANGLES]);
}

void NewStar(GLint n, GLint d)
{

    if (rand()%4 == 0) {
    stars[n].type = CIRCLE;
    } else {
    stars[n].type = STREAK;
    }
    stars[n].x[0] = (float)(rand() % MAXPOS - MAXPOS / 2);
    stars[n].y[0] = (float)(rand() % MAXPOS - MAXPOS / 2);
    stars[n].z[0] = (float)(rand() % MAXPOS + d);
    if (rand()%4 == 0 && flag == WEIRD) {
    stars[n].offsetX = (float)(rand() % 100 - 100 / 2);
    stars[n].offsetY = (float)(rand() % 100 - 100 / 2);
    stars[n].offsetR = (float)(rand() % 25 - 25 / 2);
    } else {
    stars[n].offsetX = 0.0;
    stars[n].offsetY = 0.0;
    stars[n].offsetR = 0.0;
    }
    stars[n].rotation = 0.0;
}

void RotatePoint(float *x, float *y, float rotation)
{
    float tmpX, tmpY;

    tmpX = *x * Cos(rotation) - *y * Sin(rotation);
    tmpY = *y * Cos(rotation) + *x * Sin(rotation);
    *x = tmpX;
    *y = tmpY;
}

void MoveStars(void)
{
    float offset;
    GLint n;

    offset = speed * 60.0;

    for (n = 0; n < starCount; n++) {
        stars[n].x[1] = stars[n].x[0];
        stars[n].y[1] = stars[n].y[0];
        stars[n].z[1] = stars[n].z[0];
        stars[n].x[0] += stars[n].offsetX;
        stars[n].y[0] += stars[n].offsetY;
        stars[n].z[0] -= offset;
        stars[n].rotation += stars[n].offsetR;
        if (stars[n].rotation > MAXANGLES) {
            stars[n].rotation = 0.0;
        } else if (stars[n].rotation < 0.0) {
            stars[n].rotation = (float)MAXANGLES;
        }
    }
}

GLenum StarPoint(GLint n)
{
    float x0, y0, x1, y1, width;
    GLint i;

    x0 = stars[n].x[0] * windW / stars[n].z[0];
    y0 = stars[n].y[0] * windH / stars[n].z[0];
    RotatePoint(&x0, &y0, stars[n].rotation);
    x0 += windW / 2.0;
    y0 += windH / 2.0;

    if (x0 >= 0.0 && x0 < windW && y0 >= 0.0 && y0 < windH) {
    if (stars[n].type == STREAK) {
        x1 = stars[n].x[1] * windW / stars[n].z[1];
        y1 = stars[n].y[1] * windH / stars[n].z[1];
        RotatePoint(&x1, &y1, stars[n].rotation);
        x1 += windW / 2.0;
        y1 += windH / 2.0;

        glLineWidth(MAXPOS/100.0/stars[n].z[0]+1.0);
        glColor3f(1.0, (MAXWARP-speed)/MAXWARP, (MAXWARP-speed)/MAXWARP);
        if (fabs(x0-x1) < 1.0 && fabs(y0-y1) < 1.0) {
        glBegin(GL_POINTS);
            glVertex2f(x0, y0);
        glEnd();
        } else {
        glBegin(GL_LINES);
            glVertex2f(x0, y0);
            glVertex2f(x1, y1);
        glEnd();
        }
    } else {
        width = MAXPOS / 10.0 / stars[n].z[0] + 1.0;
        glColor3f(1.0, 0.0, 0.0);
        glBegin(GL_POLYGON);
        for (i = 0; i < 8; i++) {
            float x = x0 + width * Cos((float)i*MAXANGLES/8.0);
            float y = y0 + width * Sin((float)i*MAXANGLES/8.0);
            glVertex2f(x, y);
        };
        glEnd();
    }
    return GL_TRUE;
    } else {
    return GL_FALSE;
    }
}

void ShowStars(void)
{
    GLint n;

    glClear(GL_COLOR_BUFFER_BIT);

    for (n = 0; n < starCount; n++) {
    if (stars[n].z[0] > speed || (stars[n].z[0] > 0.0 && speed < MAXWARP)) {
        if (StarPoint(n) == GL_FALSE) {
        NewStar(n, MAXPOS);
        }
    } else {
        NewStar(n, MAXPOS);
    }
    }
}

static void Init(void)
{
    float angle;
    GLint n;

    srand((unsigned int)time(NULL));

    for (n = 0; n < MAXSTARS; n++) {
    NewStar(n, 100);
    }

    angle = 0.0;
    for (n = 0; n < MAXANGLES ; n++) {
    sinTable[n] = sin(angle);
        angle += PI / (MAXANGLES / 2.0);
    }

    glClearColor(0.0, 0.0, 0.0, 0.0);

    glDisable(GL_DITHER);
}

void CALLBACK Reshape(int width, int height)
{

    windW = (GLint)width;
    windH = (GLint)height;

    glViewport(0, 0, windW, windH);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-0.5, windW+0.5, -0.5, windH+0.5);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Key_SPACE(void)
{
    flag = (flag == NORMAL) ? WEIRD : NORMAL;
}

static void CALLBACK Key_t(void)
{
    nitro = 1;
}

void CALLBACK Idle(void)
{

    MoveStars();
    ShowStars();
    if (nitro > 0) {
    speed = (float)(nitro / 10) + 1.0;
    if (speed > MAXWARP) {
        speed = MAXWARP;
    }
    if (++nitro > MAXWARP*10) {
        nitro = -nitro;
    }
    } else if (nitro < 0) {
    nitro++;
    speed = (float)(-nitro / 10) + 1.0;
    if (speed > MAXWARP) {
        speed = MAXWARP;
    }
    }

    glFlush();
    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    windW = 300;
    windH = 300;
    auxInitPosition(0, 0, 300, 300);

    type = AUX_RGB;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Stars") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_SPACE, Key_SPACE);
    auxKeyFunc(AUX_t, Key_t);
    auxIdleFunc(Idle);
    auxMainLoop(NULL);
}


TSTENCIL.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <gl\glaux.h>

static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Draw(void );

static void Init(void)
{

    glClearColor(0.0, 0.0, 0.0, 0.0);

    glClearStencil(0);
    glStencilMask(1);
    glEnable(GL_STENCIL_TEST);
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-5.0, 5.0, -5.0, 5.0, -5.0, 5.0);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Draw(void)
{

    glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);

    glStencilFunc(GL_ALWAYS, 1, 1);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

    glColor3ub(200, 0, 0);
    glBegin(GL_POLYGON);
    glVertex3i(-4, -4, 0);
    glVertex3i( 4, -4, 0);
    glVertex3i( 0,  4, 0);
    glEnd();

    glStencilFunc(GL_EQUAL, 1, 1);
    glStencilOp(GL_INCR, GL_KEEP, GL_DECR);

    glColor3ub(0, 200, 0);
    glBegin(GL_POLYGON);
    glVertex3i(3, 3, 0);
    glVertex3i(-3, 3, 0);
    glVertex3i(-3, -3, 0);
    glVertex3i(3, -3, 0);
    glEnd();

    glStencilFunc(GL_EQUAL, 1, 1);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

    glColor3ub(0, 0, 200);
    glBegin(GL_POLYGON);
    glVertex3i(3, 3, 0);
    glVertex3i(-3, 3, 0);
    glVertex3i(-3, -3, 0);
    glVertex3i(3, -3, 0);
    glEnd();

    glFlush();
}

void main(int argc, char **argv)
{
    GLenum type;

    auxInitPosition(0, 0, 300, 300);

    type = AUX_RGB | AUX_SINGLE | AUX_STENCIL;

    auxInitDisplayMode(type);

    if (auxInitWindow("Stencil Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxMainLoop(Draw);
}


TSTRETCH.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gl\glaux.h>

#define STEPCOUNT 40
#define FALSE 0
#define TRUE 1
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))

enum {
    OP_NOOP = 0,
    OP_STRETCH,
    OP_DRAWPOINT,
    OP_DRAWIMAGE
};

typedef struct _cRec {
    float x, y;
} cRec;

typedef struct _vertexRec {
    float x, y;
    float dX, dY;
    float tX, tY;
} vertexRec;

extern void DrawImage(void );
extern void DrawPoint(void );
extern void InitVList(void );
extern void ScaleImage(int sizeX,int sizeY);
extern void SetPoint(int x,int y);
extern void Stretch(void );
static void CALLBACK Key_SPACE(void );
static GLenum CALLBACK Mouse_left(struct _AUX_EVENTREC *event);
extern void CALLBACK Animate(void );
static GLenum Args(int argc,char **argv );

GLenum doubleBuffer;
int imageSizeX, imageSizeY;
char *fileName = 0;
AUX_RGBImageRec *image;
cRec cList[50];
vertexRec vList[5];
int cCount, cIndex[2], cStep;
GLenum op = OP_NOOP;

void DrawImage(void)
{

    glRasterPos2i(0, 0);
    glDrawPixels(image->sizeX, image->sizeY, GL_RGB, GL_UNSIGNED_BYTE,
         image->data);

    auxSwapBuffers();

    glRasterPos2i(0, 0);
    glDrawPixels(image->sizeX, image->sizeY, GL_RGB, GL_UNSIGNED_BYTE,
         image->data);
}

void DrawPoint(void)
{
    int i;

    glColor3f(1.0, 0.0, 1.0);
    glPointSize(3.0);
    glBegin(GL_POINTS);
    for (i = 0; i < cCount; i++) {
        glVertex2f(cList[i].x, cList[i].y);
    }
    glEnd();

    auxSwapBuffers();
}

void InitVList(void)
{

    vList[0].x = 0.0;
    vList[0].y = 0.0;
    vList[0].dX = 0.0;
    vList[0].dY = 0.0;
    vList[0].tX = 0.0;
    vList[0].tY = 0.0;

    vList[1].x = (float)imageSizeX;
    vList[1].y = 0.0;
    vList[1].dX = 0.0;
    vList[1].dY = 0.0;
    vList[1].tX = 1.0;
    vList[1].tY = 0.0;

    vList[2].x = (float)imageSizeX;
    vList[2].y = (float)imageSizeY;
    vList[2].dX = 0.0;
    vList[2].dY = 0.0;
    vList[2].tX = 1.0;
    vList[2].tY = 1.0;

    vList[3].x = 0.0;
    vList[3].y = (float)imageSizeY;
    vList[3].dX = 0.0;
    vList[3].dY = 0.0;
    vList[3].tX = 0.0;
    vList[3].tY = 1.0;

    vList[4].x = cList[0].x;
    vList[4].y = cList[0].y;
    vList[4].dX = (cList[1].x - cList[0].x) / STEPCOUNT;
    vList[4].dY = (cList[1].y - cList[0].y) / STEPCOUNT;
    vList[4].tX = cList[0].x / (float)imageSizeX;
    vList[4].tY = cList[0].y / (float)imageSizeY;
}

void ScaleImage(int sizeX, int sizeY)
{
    GLubyte *buf;

    buf = (GLubyte *)malloc(3*sizeX*sizeY);
    gluScaleImage(GL_RGB, image->sizeX, image->sizeY, GL_UNSIGNED_BYTE,
                  image->data, sizeX, sizeY, GL_UNSIGNED_BYTE, buf);
    free(image->data);
    image->data = buf;
    image->sizeX = sizeX;
    image->sizeY = sizeY;
}

void SetPoint(int x, int y)
{

    cList[cCount].x = (float)x;
    cList[cCount].y = (float)y;
    cCount++;
}

void Stretch(void)
{

    glBegin(GL_TRIANGLES);
    glTexCoord2f(vList[0].tX, vList[0].tY);
    glVertex2f(vList[0].x, vList[0].y);
    glTexCoord2f(vList[1].tX, vList[1].tY);
    glVertex2f(vList[1].x, vList[1].y);
    glTexCoord2f(vList[4].tX, vList[4].tY);
    glVertex2f(vList[4].x, vList[4].y);
    glEnd();

    glBegin(GL_TRIANGLES);
    glTexCoord2f(vList[1].tX, vList[1].tY);
    glVertex2f(vList[1].x, vList[1].y);
    glTexCoord2f(vList[2].tX, vList[2].tY);
    glVertex2f(vList[2].x, vList[2].y);
    glTexCoord2f(vList[4].tX, vList[4].tY);
    glVertex2f(vList[4].x, vList[4].y);
    glEnd();

    glBegin(GL_TRIANGLES);
    glTexCoord2f(vList[2].tX, vList[2].tY);
    glVertex2f(vList[2].x, vList[2].y);
    glTexCoord2f(vList[3].tX, vList[3].tY);
    glVertex2f(vList[3].x, vList[3].y);
    glTexCoord2f(vList[4].tX, vList[4].tY);
    glVertex2f(vList[4].x, vList[4].y);
    glEnd();

    glBegin(GL_TRIANGLES);
    glTexCoord2f(vList[3].tX, vList[3].tY);
    glVertex2f(vList[3].x, vList[3].y);
    glTexCoord2f(vList[0].tX, vList[0].tY);
    glVertex2f(vList[0].x, vList[0].y);
    glTexCoord2f(vList[4].tX, vList[4].tY);
    glVertex2f(vList[4].x, vList[4].y);
    glEnd();

    auxSwapBuffers();

    if (++cStep < STEPCOUNT) {
    vList[4].x += vList[4].dX;
    vList[4].y += vList[4].dY;
    } else {
    cIndex[0] = cIndex[1];
    cIndex[1] = cIndex[1] + 1;
    if (cIndex[1] == cCount) {
        cIndex[1] = 0;
    }
    vList[4].dX = (cList[cIndex[1]].x - cList[cIndex[0]].x) / STEPCOUNT;
    vList[4].dY = (cList[cIndex[1]].y - cList[cIndex[0]].y) / STEPCOUNT;
    cStep = 0;
    }
}

static void CALLBACK Key_SPACE(void)
{
    if (cCount > 1) {
        InitVList();
        cIndex[0] = 0;
        cIndex[1] = 1;
        cStep = 0;
        glEnable(GL_TEXTURE_2D);
        op = OP_STRETCH;
    }
}

static GLenum CALLBACK Mouse_left(AUX_EVENTREC *event)
{
    GLint mouseX;
    GLint mouseY;

    mouseX = event->data[AUX_MOUSEX];
    mouseY = event->data[AUX_MOUSEY];
    if (op == OP_STRETCH) {
    glDisable(GL_TEXTURE_2D);
    cCount = 0;
    op = OP_DRAWIMAGE;
    } else {
    SetPoint(mouseX, imageSizeY-mouseY);
    op = OP_DRAWPOINT;
    }
    return GL_TRUE;
}

void CALLBACK Animate(void)
{

    switch (op) {
      case OP_STRETCH:
    Stretch();
    break;
      case OP_DRAWPOINT:
    DrawPoint();
    break;
      case OP_DRAWIMAGE:
    DrawImage();
    break;
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else if (strcmp(argv[i], "-f") == 0) {
        if (i+1 >= argc || argv[i+1][0] == '-') {
        //printf("-f (No file name).\n");
        return GL_FALSE;
        } else {
        fileName = argv[++i];
        }
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    if (fileName == 0) {
    //printf("No image file.\n");
    auxQuit();
    }

    image = auxRGBImageLoad(fileName);

    imageSizeX = (int)pow(2.0, (float)((int)(log(image->sizeX)/log(2.0))));
    imageSizeY = (int)pow(2.0, (float)((int)(log(image->sizeY)/log(2.0))));

    auxInitPosition(0, 0, imageSizeX, imageSizeY);

    type = AUX_RGB;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Stretch") == GL_FALSE) {
        auxQuit();
    }

    glViewport(0, 0, imageSizeX, imageSizeY);
    gluOrtho2D(0, imageSizeX, 0, imageSizeY);
    glClearColor(0.0, 0.0, 0.0, 0.0);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glPixelStorei(GL_PACK_ALIGNMENT, 1);

    ScaleImage(imageSizeX, imageSizeY);

    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, image->sizeX, image->sizeY, 0,
                 GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)image->data);

    cCount = 0;
    cIndex[0] = 0;
    cIndex[1] = 0;
    cStep = 0;
    op = OP_DRAWIMAGE;

    auxKeyFunc(AUX_SPACE, Key_SPACE);
    auxMouseFunc(AUX_LEFTBUTTON, AUX_MOUSEDOWN, Mouse_left);
    auxIdleFunc(Animate);
    auxMainLoop(NULL);
}


TTEXTURE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <gl\glaux.h>

static void BuildCube(void );
static void BuildLists(void );
static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_LEFT(void );
static void CALLBACK Key_RIGHT(void );
static void CALLBACK Key_UP(void );
static void CALLBACK Key_DOWN(void );
static void CALLBACK Key_T(void );
static void CALLBACK Key_t(void );
static void CALLBACK Key_s(void );
static void CALLBACK Key_0(void );
static void CALLBACK Key_1(void );
static void CALLBACK Key_2(void );
static void CALLBACK Key_3(void );
static void CALLBACK Key_4(void );
static void CALLBACK Key_5(void );
static void CALLBACK Key_6(void );
static void CALLBACK Key_7(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum doubleBuffer;

char *texFileName = 0;
AUX_RGBImageRec *image;

float *minFilter, *magFilter, *sWrapMode, *tWrapMode;
float decal[] = {GL_DECAL};
float modulate[] = {GL_MODULATE};
float repeat[] = {GL_REPEAT};
float clamp[] = {GL_CLAMP};
float nr[] = {GL_NEAREST};
float ln[] = {GL_LINEAR};
float nr_mipmap_nr[] = {GL_NEAREST_MIPMAP_NEAREST};
float nr_mipmap_ln[] = {GL_NEAREST_MIPMAP_LINEAR};
float ln_mipmap_nr[] = {GL_LINEAR_MIPMAP_NEAREST};
float ln_mipmap_ln[] = {GL_LINEAR_MIPMAP_LINEAR};
GLint sphereMap[] = {GL_SPHERE_MAP};

GLenum doSphere = GL_FALSE;
float xRotation = 0.0, yRotation = 0.0, zTranslate = -3.125;

GLint cube;
float c[6][4][3] = {
    {
    {
        1.0, 1.0, -1.0
    },
    {
        -1.0, 1.0, -1.0
    },
    {
        -1.0, -1.0, -1.0
    },
    {
        1.0, -1.0, -1.0
    }
    },
    {
    {
        1.0, 1.0, 1.0
    },
    {
        1.0, 1.0, -1.0
    },
    {
        1.0, -1.0, -1.0
    },
    {
        1.0, -1.0, 1.0
    }
    },
    {
    {
        -1.0, 1.0, 1.0
    },
    {
        1.0, 1.0, 1.0
    },
    {
        1.0, -1.0, 1.0
    },
    {
        -1.0, -1.0, 1.0
    }
    },
    {
    {
        -1.0, 1.0, -1.0
    },
    {
        -1.0, 1.0, 1.0
    },
    {
        -1.0, -1.0, 1.0
    },
    {
        -1.0, -1.0, -1.0
    }
    },
    {
    {
        -1.0, 1.0, 1.0
    },
    {
        -1.0, 1.0, -1.0
    },
    {
        1.0, 1.0, -1.0
    },
    {
        1.0, 1.0, 1.0
    }
    },
    {
    {
        -1.0, -1.0, -1.0
    },
    {
        -1.0, -1.0, 1.0
    },
    {
        1.0, -1.0, 1.0
    },
    {
        1.0, -1.0, -1.0
    }
    }
};
static float n[6][3] = {
    {
    0.0, 0.0, -1.0
    },
    {
    1.0, 0.0, 0.0
    },
    {
    0.0, 0.0, 1.0
    },
    {
    -1.0, 0.0, 0.0
    },
    {
    0.0, 1.0, 0.0
    },
    {
    0.0, -1.0, 0.0
    }
};
static float t[6][4][2] = {
    {
    {
        1.1,  1.1
    },
    {
        -0.1, 1.1
    },
    {
        -0.1, -0.1
    },
    {
        1.1,  -0.1
    }
    },
    {
    {
        1.1,  1.1
    },
    {
        -0.1, 1.1
    },
    {
        -0.1, -0.1
    },
    {
        1.1,  -0.1
    }
    },
    {
    {
        -0.1,  1.1
    },
    {
        1.1, 1.1
    },
    {
        1.1, -0.1
    },
    {
        -0.1,  -0.1
    }
    },
    {
    {
        1.1,  1.1
    },
    {
        -0.1, 1.1
    },
    {
        -0.1, -0.1
    },
    {
        1.1,  -0.1
    }
    },
    {
    {
        1.1,  1.1
    },
    {
        -0.1, 1.1
    },
    {
        -0.1, -0.1
    },
    {
        1.1,  -0.1
    }
    },
    {
    {
        1.1,  1.1
    },
    {
        -0.1, 1.1
    },
    {
        -0.1, -0.1
    },
    {
        1.1,  -0.1
    }
    },
};

static void BuildCube(void)
{
    GLint i;

    glNewList(cube, GL_COMPILE);
    for (i = 0; i < 6; i++) {
    glBegin(GL_POLYGON);
        glNormal3fv(n[i]); glTexCoord2fv(t[i][0]); glVertex3fv(c[i][0]);
        glNormal3fv(n[i]); glTexCoord2fv(t[i][1]); glVertex3fv(c[i][1]);
        glNormal3fv(n[i]); glTexCoord2fv(t[i][2]); glVertex3fv(c[i][2]);
        glNormal3fv(n[i]); glTexCoord2fv(t[i][3]); glVertex3fv(c[i][3]);
    glEnd();
    }
    glEndList();
}

static void BuildLists(void)
{

    cube = glGenLists(1);
    BuildCube();
}

static void Init(void)
{

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image->sizeX, image->sizeY,
              GL_RGB, GL_UNSIGNED_BYTE, image->data);
    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, decal);
    glEnable(GL_TEXTURE_2D);

    glFrontFace(GL_CCW);
    glCullFace(GL_FRONT);
    glEnable(GL_CULL_FACE);

    BuildLists();

    glClearColor(0.0, 0.0, 0.0, 0.0);

    magFilter = nr;
    minFilter = nr;
    sWrapMode = repeat;
    tWrapMode = repeat;
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(145.0, 1.0, 0.01, 1000);
    glMatrixMode(GL_MODELVIEW);
}

static void CALLBACK Key_LEFT(void)
{
    yRotation -= 0.5;
}

static void CALLBACK Key_RIGHT(void)
{
    yRotation += 0.5;
}

static void CALLBACK Key_UP(void)
{
    xRotation -= 0.5;
}

static void CALLBACK Key_DOWN(void)
{
    xRotation += 0.5;
}

static void CALLBACK Key_T(void)
{
    zTranslate += 0.25;
}

static void CALLBACK Key_t(void)
{
    zTranslate -= 0.25;
}

static void CALLBACK Key_s(void)
{
    doSphere = !doSphere;
    if (doSphere) {
        glTexGeniv(GL_S, GL_TEXTURE_GEN_MODE, sphereMap);
        glTexGeniv(GL_T, GL_TEXTURE_GEN_MODE, sphereMap);
        glEnable(GL_TEXTURE_GEN_S);
        glEnable(GL_TEXTURE_GEN_T);
    } else {
        glDisable(GL_TEXTURE_GEN_S);
        glDisable(GL_TEXTURE_GEN_T);
    }
}

static void CALLBACK Key_0(void)
{
    magFilter = nr;
}

static void CALLBACK Key_1(void)
{
    magFilter = ln;
}

static void CALLBACK Key_2(void)
{
    minFilter = nr;
}

static void CALLBACK Key_3(void)
{
    minFilter = ln;
}

static void CALLBACK Key_4(void)
{
    minFilter = nr_mipmap_nr;
}

static void CALLBACK Key_5(void)
{
    minFilter = nr_mipmap_ln;
}

static void CALLBACK Key_6(void)
{
    minFilter = ln_mipmap_nr;
}

static void CALLBACK Key_7(void)
{
    minFilter = ln_mipmap_ln;
}

static void CALLBACK Draw(void)
{

    glClear(GL_COLOR_BUFFER_BIT);

    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sWrapMode);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tWrapMode);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);

    glPushMatrix();

    glTranslatef(0.0, 0.0, zTranslate);
    glRotatef(xRotation, 1, 0, 0);
    glRotatef(yRotation, 0, 1, 0);
    glCallList(cube);

    glPopMatrix();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else if (strcmp(argv[i], "-f") == 0) {
        if (i+1 >= argc || argv[i+1][0] == '-') {
        //printf("-f (No file name).\n");
        return GL_FALSE;
        } else {
        texFileName = argv[++i];
        }
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    if (texFileName == 0) {
    //printf("No image file.\n");
    auxQuit();
    }

    image = auxRGBImageLoad(texFileName);

    auxInitPosition(0, 0, 300, 300);

    type = AUX_RGB;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Texture Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_LEFT, Key_LEFT);
    auxKeyFunc(AUX_RIGHT, Key_RIGHT);
    auxKeyFunc(AUX_UP, Key_UP);
    auxKeyFunc(AUX_DOWN, Key_DOWN);
    auxKeyFunc(AUX_T, Key_T);
    auxKeyFunc(AUX_t, Key_t);
    auxKeyFunc(AUX_s, Key_s);
    auxKeyFunc(AUX_0, Key_0);
    auxKeyFunc(AUX_1, Key_1);
    auxKeyFunc(AUX_2, Key_2);
    auxKeyFunc(AUX_3, Key_3);
    auxKeyFunc(AUX_4, Key_4);
    auxKeyFunc(AUX_5, Key_5);
    auxKeyFunc(AUX_6, Key_6);
    auxKeyFunc(AUX_7, Key_7);
    auxMainLoop(Draw);
}


TTRI.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gl\glaux.h>

#define SOLID 1
#define LINE 2
#define POINT 3

static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_LEFT(void );
static void CALLBACK Key_RIGHT(void );
static void CALLBACK Key_Z(void );
static void CALLBACK Key_z(void );
static void CALLBACK Key_1(void );
static void CALLBACK Key_2(void );
static void CALLBACK Key_3(void );
static void CALLBACK Key_4(void );
static void CALLBACK Key_5(void );
static void CALLBACK Key_6(void );
static void CALLBACK Key_7(void );
static void CALLBACK Key_8(void );
static void CALLBACK Key_9(void );
static void CALLBACK Key_v(void );
static void CALLBACK Key_s(void );
static void CALLBACK Key_h(void );
static void CALLBACK Key_o(void );
static void CALLBACK Key_m(void );
static void CALLBACK Key_0(void );
static void BeginPrim(void );
static void EndPrim(void );
static void CALLBACK Draw(void );
static GLenum Args(int argc,char **argv );

GLenum rgb, doubleBuffer, windType;
GLint windW, windH;

GLenum dithering = GL_TRUE;
GLenum showVerticies = GL_TRUE;
GLenum hideBottomTriangle = GL_FALSE;
GLenum outline = GL_TRUE;
GLenum culling = GL_FALSE;
GLenum winding = GL_FALSE;
GLenum face = GL_FALSE;
GLenum state = SOLID;
GLenum aaMode = GL_FALSE;
GLenum shade = GL_TRUE;

GLint color1, color2, color3;

float zRotation = 90.0;
float zoom = 1.0;

float boxA[3] = {-100, -100, 0};
float boxB[3] = { 100, -100, 0};
float boxC[3] = { 100,  100, 0};
float boxD[3] = {-100,  100, 0};

float p0[3] = {-125,-80, 0};
float p1[3] = {-125, 80, 0};
float p2[3] = { 172,  0, 0};

static void Init(void)
{
    float r, g, b;
    float percent1, percent2;
    GLint i, j;

    glClearColor(0.0, 0.0, 0.0, 0.0);

    glLineStipple(1, 0xF0F0);

    glEnable(GL_SCISSOR_TEST);

    if (!rgb) {
    for (j = 0; j <= 12; j++) {
        if (j <= 6) {
        percent1 = j / 6.0;
        r = 1.0 - 0.8 * percent1;
        g = 0.2 + 0.8 * percent1;
        b = 0.2;
        } else {
        percent1 = (j - 6) / 6.0;
        r = 0.2;
        g = 1.0 - 0.8 * percent1;
        b = 0.2 + 0.8 * percent1;
        }
        auxSetOneColor(j+18, r, g, b);
        for (i = 0; i < 16; i++) {
        percent2 = i / 15.0;
        auxSetOneColor(j*16+1+32, r*percent2, g*percent2, b*percent2);
        }
    }
    color1 = 18;
    color2 = 24;
    color3 = 30;
    }
}

static void CALLBACK Reshape(int width, int height)
{

    windW = (GLint)width;
    windH = (GLint)height;
}

static void CALLBACK Key_LEFT(void)
{
    zRotation += 0.5;
}

static void CALLBACK Key_RIGHT(void)
{
    zRotation -= 0.5;
}

static void CALLBACK Key_Z(void)
{
    zoom *= 0.75;
}

static void CALLBACK Key_z(void)
{
    zoom /= 0.75;
    if (zoom > 10) {
        zoom = 10;
    }
}

static void CALLBACK Key_1(void)
{
    glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
}

static void CALLBACK Key_2(void)
{
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}

static void CALLBACK Key_3(void)
{
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

static void CALLBACK Key_4(void)
{
    state = POINT;
}

static void CALLBACK Key_5(void)
{
    state = LINE;
}

static void CALLBACK Key_6(void)
{
    state = SOLID;
}

static void CALLBACK Key_7(void)
{
    culling = !culling;
}

static void CALLBACK Key_8(void)
{
    winding = !winding;
}

static void CALLBACK Key_9(void)
{
    face = !face;
}

static void CALLBACK Key_v(void)
{
    showVerticies = !showVerticies;
}

static void CALLBACK Key_s(void)
{
    shade = !shade;
    (shade) ? glShadeModel(GL_SMOOTH) : glShadeModel(GL_FLAT);
}

static void CALLBACK Key_h(void)
{
    hideBottomTriangle = !hideBottomTriangle;
}

static void CALLBACK Key_o(void)
{
    outline = !outline;
}

static void CALLBACK Key_m(void)
{
    dithering = !dithering;
}

static void CALLBACK Key_0(void)
{
    aaMode = !aaMode;
    if (aaMode) {
        glEnable(GL_POLYGON_SMOOTH);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
        if (!rgb) {
        color1 = 32;
        color2 = 128;
        color3 = 224;
        }
    } else {
        glDisable(GL_POLYGON_SMOOTH);
        glDisable(GL_BLEND);
        if (!rgb) {
        color1 = 18;
        color2 = 24;
        color3 = 30;
        }
    }
}

static void BeginPrim(void)
{

    switch (state) {
      case SOLID:
    glBegin(GL_POLYGON);
    break;
      case LINE:
    glBegin(GL_LINE_LOOP);
    break;
      case POINT:
    glBegin(GL_POINTS);
    break;
    }
}

static void EndPrim(void)
{

    glEnd();
}

static void CALLBACK Draw(void)
{
    float scaleX, scaleY;

    glViewport(0, 0, windW, windH);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-175, 175, -175, 175);
    glMatrixMode(GL_MODELVIEW);

    glScissor(0, 0, windW, windH);

    (culling) ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE);
    (winding) ? glFrontFace(GL_CCW) : glFrontFace(GL_CW);
    (face) ? glCullFace(GL_FRONT) : glCullFace(GL_BACK);

    (dithering) ? glEnable(GL_DITHER) : glDisable(GL_DITHER);

    glClear(GL_COLOR_BUFFER_BIT);

    AUX_SETCOLOR(windType, AUX_GREEN);
    glBegin(GL_LINE_LOOP);
    glVertex3fv(boxA);
    glVertex3fv(boxB);
    glVertex3fv(boxC);
    glVertex3fv(boxD);
    glEnd();

    if (!hideBottomTriangle) {
    glPushMatrix();

    glScalef(zoom, zoom, zoom);
    glRotatef(zRotation, 0, 0, 1);

    AUX_SETCOLOR(windType, AUX_BLUE);
    BeginPrim();
        glVertex3fv(p0);
        glVertex3fv(p1);
        glVertex3fv(p2);
    EndPrim();

    if (showVerticies) {
        (rgb) ? glColor3fv(auxRGBMap[AUX_RED]) : glIndexf(color1);
        glRectf(p0[0]-2, p0[1]-2, p0[0]+2, p0[1]+2);
        (rgb) ? glColor3fv(auxRGBMap[AUX_GREEN]) : glIndexf(color2);
        glRectf(p1[0]-2, p1[1]-2, p1[0]+2, p1[1]+2);
        (rgb) ? glColor3fv(auxRGBMap[AUX_BLUE]) : glIndexf(color3);
        glRectf(p2[0]-2, p2[1]-2, p2[0]+2, p2[1]+2);
    }

    glPopMatrix();
    }

    scaleX = (float)(windW - 20) / 2 / 175 * (175 - 100) + 10;
    scaleY = (float)(windH - 20) / 2 / 175 * (175 - 100) + 10;

    glViewport(scaleX, scaleY, windW-2*scaleX, windH-2*scaleY);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-100, 100, -100, 100);
    glMatrixMode(GL_MODELVIEW);

    glScissor(scaleX, scaleY, windW-2*scaleX, windH-2*scaleY);

    glPushMatrix();

    glScalef(zoom, zoom, zoom);
    glRotatef(zRotation, 0,0,1);

    glPointSize(10);
    glLineWidth(5);
    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_LINE_STIPPLE);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    AUX_SETCOLOR(windType, AUX_RED);
    BeginPrim();
    (rgb) ? glColor3fv(auxRGBMap[AUX_RED]) : glIndexf(color1);
    glVertex3fv(p0);
    (rgb) ? glColor3fv(auxRGBMap[AUX_GREEN]) : glIndexf(color2);
    glVertex3fv(p1);
    (rgb) ? glColor3fv(auxRGBMap[AUX_BLUE]) : glIndexf(color3);
    glVertex3fv(p2);
    EndPrim();

    glPointSize(1);
    glLineWidth(1);
    glDisable(GL_POINT_SMOOTH);
    glDisable(GL_LINE_STIPPLE);
    glBlendFunc(GL_ONE, GL_ZERO);

    if (outline) {
    AUX_SETCOLOR(windType, AUX_WHITE);
    glBegin(GL_LINE_LOOP);
        glVertex3fv(p0);
        glVertex3fv(p1);
        glVertex3fv(p2);
    glEnd();
    }

    glPopMatrix();

    glFlush();

    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_FALSE;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-ci") == 0) {
        rgb = GL_FALSE;
    } else if (strcmp(argv[i], "-rgb") == 0) {
        rgb = GL_TRUE;
    } else if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    windW = 600;
    windH = 300;
    auxInitPosition(0, 0, windW, windH);

    windType |= (rgb) ? AUX_RGB : AUX_INDEX;
    windType |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(windType);

    if (auxInitWindow("Triangle Test") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_LEFT, Key_LEFT);
    auxKeyFunc(AUX_RIGHT, Key_RIGHT);
    auxKeyFunc(AUX_Z, Key_Z);
    auxKeyFunc(AUX_z, Key_z);
    auxKeyFunc(AUX_1, Key_1);
    auxKeyFunc(AUX_2, Key_2);
    auxKeyFunc(AUX_3, Key_3);
    auxKeyFunc(AUX_4, Key_4);
    auxKeyFunc(AUX_5, Key_5);
    auxKeyFunc(AUX_6, Key_6);
    auxKeyFunc(AUX_7, Key_7);
    auxKeyFunc(AUX_8, Key_8);
    auxKeyFunc(AUX_9, Key_9);
    auxKeyFunc(AUX_v, Key_v);
    auxKeyFunc(AUX_s, Key_s);
    auxKeyFunc(AUX_h, Key_h);
    auxKeyFunc(AUX_o, Key_o);
    auxKeyFunc(AUX_m, Key_m);
    auxKeyFunc(AUX_0, Key_0);
    auxMainLoop(Draw);
}


TWAVE.C

/*
 *               1993-1995 Microsoft Corporation
 *
 * ALL RIGHTS RESERVED
 *
 * Please refer to OpenGL/readme.txt for additional information
 *
 */

#include "glos.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <gl\glaux.h>

#define PI 3.14159265358979323846

#define GETCOORD(frame, x, y) (&(theMesh.coords[frame*theMesh.numCoords+(x)+(y)*(theMesh.widthX+1)]))
#define GETFACET(frame, x, y) (&(theMesh.facets[frame*theMesh.numFacets+(x)+(y)*theMesh.widthX]))

static void CALLBACK Animate(void );
static void SetColorMap(void );
static void InitMesh(void );
static void InitMaterials(void );
static void InitTexture(void );
static void Init(void );
static void CALLBACK Reshape(int width,int height);
static void CALLBACK Key_c(void );
static void CALLBACK Key_s(void );
static void CALLBACK Key_l(void );
static void CALLBACK Key_d(void );
static void CALLBACK Key_SPACE(void );
static void CALLBACK Key_n(void );
static void CALLBACK Key_a(void );
static GLenum Args(int argc,char **argv );

GLenum rgb, doubleBuffer;

GLint colorIndexes1[3];
GLint colorIndexes2[3];
GLenum clearMask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;

GLenum smooth = GL_TRUE;
GLenum lighting = GL_TRUE;
GLenum depth = GL_TRUE;
GLenum stepMode = GL_FALSE;
GLenum spinMode = GL_FALSE;
GLint contouring = 0;

GLint widthX, widthY;
GLint checkerSize;
float height;

GLint frames, curFrame = 0, nextFrame = 0;

struct facet {
    float color[3];
    float normal[3];
};
struct coord {
    float vertex[3];
    float normal[3];
};
struct mesh {
    GLint widthX, widthY;
    GLint numFacets;
    GLint numCoords;
    GLint frames;
    struct coord *coords;
    struct facet *facets;
} theMesh;

GLubyte contourTexture1[] = {
    255, 255, 255, 255,
    255, 255, 255, 255,
    255, 255, 255, 255,
    127, 127, 127, 127,
};
GLubyte contourTexture2[] = {
    255, 255, 255, 255,
    255, 127, 127, 127,
    255, 127, 127, 127,
    255, 127, 127, 127,
};

static void CALLBACK Animate(void)
{
    struct coord *coord;
    struct facet *facet;
    float *lastColor;
    float *thisColor;
    GLint i, j;

    glClear(clearMask);

    if (nextFrame || !stepMode) {
    curFrame++;
    }
    if (curFrame >= theMesh.frames) {
    curFrame = 0;
    }

    if ((nextFrame || !stepMode) && spinMode) {
    glRotatef(5.0, 0.0, 0.0, 1.0);
    }
    nextFrame = 0;

    for (i = 0; i < theMesh.widthX; i++) {
    glBegin(GL_QUAD_STRIP);
    lastColor = NULL;
    for (j = 0; j < theMesh.widthY; j++) {
        facet = GETFACET(curFrame, i, j);
        if (!smooth && lighting) {
        glNormal3fv(facet->normal);
        }
        if (lighting) {
        if (rgb) {
            thisColor = facet->color;
            glColor3fv(facet->color);
        } else {
            thisColor = facet->color;
            glMaterialfv(GL_FRONT_AND_BACK, GL_COLOR_INDEXES,
                 facet->color);
        }
        } else {
        if (rgb) {
            thisColor = facet->color;
            glColor3fv(facet->color);
        } else {
            thisColor = facet->color;
            glIndexf(facet->color[1]);
        }
        }

        if (!lastColor || (thisColor[0] != lastColor[0] && smooth)) {
        if (lastColor) {
            glEnd();
            glBegin(GL_QUAD_STRIP);
        }
        coord = GETCOORD(curFrame, i, j);
        if (smooth && lighting) {
            glNormal3fv(coord->normal);
        }
        glVertex3fv(coord->vertex);

        coord = GETCOORD(curFrame, i+1, j);
        if (smooth && lighting) {
            glNormal3fv(coord->normal);
        }
        glVertex3fv(coord->vertex);
        }

        coord = GETCOORD(curFrame, i, j+1);
        if (smooth && lighting) {
        glNormal3fv(coord->normal);
        }
        glVertex3fv(coord->vertex);

        coord = GETCOORD(curFrame, i+1, j+1);
        if (smooth && lighting) {
        glNormal3fv(coord->normal);
        }
        glVertex3fv(coord->vertex);

        lastColor = thisColor;
    }
    glEnd();
    }

    glFlush();
    if (doubleBuffer) {
    auxSwapBuffers();
    }
}

static void SetColorMap(void)
{
    static float green[3] = {0.2, 1.0, 0.2};
    static float red[3] = {1.0, 0.2, 0.2};
    float *color, percent;
    GLint *indexes, entries, i, j;

    entries = auxGetColorMapSize();

    colorIndexes1[0] = 1;
    colorIndexes1[1] = 1 + (GLint)((entries - 1) * 0.3);
    colorIndexes1[2] = (GLint)((entries - 1) * 0.5);
    colorIndexes2[0] = 1 + (GLint)((entries - 1) * 0.5);
    colorIndexes2[1] = 1 + (GLint)((entries - 1) * 0.8);
    colorIndexes2[2] = entries - 1;

    for (i = 0; i < 2; i++) {
    switch (i) {
      case 0:
        color = green;
        indexes = colorIndexes1;
        break;
      case 1:
        color = red;
        indexes = colorIndexes2;
        break;
    }

    for (j = indexes[0]; j < indexes[1]; j++) {
        percent = 0.2 + 0.8 * (j - indexes[0]) /
              (float)(indexes[1] - indexes[0]);
        auxSetOneColor(j, percent*color[0], percent*color[1],
               percent*color[2]);
    }
    for (j=indexes[1]; j<=indexes[2]; j++) {
        percent = (j - indexes[1]) / (float)(indexes[2] - indexes[1]);
        auxSetOneColor(j, percent*(1-color[0])+color[0],
               percent*(1-color[1])+color[1],
               percent*(1-color[2])+color[2]);
    }
    }
}

static void InitMesh(void)
{
    struct coord *coord;
    struct facet *facet;
    float dp1[3], dp2[3];
    float *pt1, *pt2, *pt3;
    float angle, d, x, y;
    GLint numFacets, numCoords, frameNum, i, j;

    theMesh.widthX = widthX;
    theMesh.widthY = widthY;
    theMesh.frames = frames;

    numFacets = widthX * widthY;
    numCoords = (widthX + 1) * (widthY + 1);

    theMesh.numCoords = numCoords;
    theMesh.numFacets = numFacets;

    theMesh.coords = (struct coord *)malloc(frames*numCoords*
                        sizeof(struct coord));
    theMesh.facets = (struct facet *)malloc(frames*numFacets*
                        sizeof(struct facet));
    if (theMesh.coords == NULL || theMesh.facets == NULL) {
    //printf("Out of memory.\n");
    auxQuit();
    }

    for (frameNum = 0; frameNum < frames; frameNum++) {
    for (i = 0; i <= widthX; i++) {
        x = i / (float)widthX;
        for (j = 0; j <= widthY; j++) {
        y = j / (float)widthY;

        d = sqrt(x*x+y*y);
        if (d == 0.0) {
            d = 0.0001;
        }
        angle = 2 * PI * d + (2 * PI / frames * frameNum);

        coord = GETCOORD(frameNum, i, j);

        coord->vertex[0] = x - 0.5;
        coord->vertex[1] = y - 0.5;
        coord->vertex[2] = (height - height * d) * cos(angle);

        coord->normal[0] = -(height / d) * x * ((1 - d) * 2 * PI *
                   sin(angle) + cos(angle));
        coord->normal[1] = -(height / d) * y * ((1 - d) * 2 * PI *
                   sin(angle) + cos(angle));
        coord->normal[2] = -1;

        d = 1.0 / sqrt(coord->normal[0]*coord->normal[0]+
                   coord->normal[1]*coord->normal[1]+1);
        coord->normal[0] *= d;
        coord->normal[1] *= d;
        coord->normal[2] *= d;
        }
    }
    for (i = 0; i < widthX; i++) {
        for (j = 0; j < widthY; j++) {
        facet = GETFACET(frameNum, i, j);
        if (((i/checkerSize)%2)^(j/checkerSize)%2) {
            if (rgb) {
            facet->color[0] = 1.0;
            facet->color[1] = 0.2;
            facet->color[2] = 0.2;
            } else {
            facet->color[0] = colorIndexes1[0];
            facet->color[1] = colorIndexes1[1];
            facet->color[2] = colorIndexes1[2];
            }
        } else {
            if (rgb) {
            facet->color[0] = 0.2;
            facet->color[1] = 1.0;
            facet->color[2] = 0.2;
            } else {
            facet->color[0] = colorIndexes2[0];
            facet->color[1] = colorIndexes2[1];
            facet->color[2] = colorIndexes2[2];
            }
        }
        pt1 = GETCOORD(frameNum, i, j)->vertex;
        pt2 = GETCOORD(frameNum, i, j+1)->vertex;
        pt3 = GETCOORD(frameNum, i+1, j+1)->vertex;

        dp1[0] = pt2[0] - pt1[0];
        dp1[1] = pt2[1] - pt1[1];
        dp1[2] = pt2[2] - pt1[2];

        dp2[0] = pt3[0] - pt2[0];
        dp2[1] = pt3[1] - pt2[1];
        dp2[2] = pt3[2] - pt2[2];

        facet->normal[0] = dp1[1] * dp2[2] - dp1[2] * dp2[1];
        facet->normal[1] = dp1[2] * dp2[0] - dp1[0] * dp2[2];
        facet->normal[2] = dp1[0] * dp2[1] - dp1[1] * dp2[0];

        d = 1.0 / sqrt(facet->normal[0]*facet->normal[0]+
                   facet->normal[1]*facet->normal[1]+
                   facet->normal[2]*facet->normal[2]);

        facet->normal[0] *= d;
        facet->normal[1] *= d;
        facet->normal[2] *= d;
        }
    }
    }
}

static void InitMaterials(void)
{
    static float ambient[] = {0.1, 0.1, 0.1, 1.0};
    static float diffuse[] = {0.5, 1.0, 1.0, 1.0};
    static float position[] = {90.0, 90.0, 150.0, 0.0};
    static float front_mat_shininess[] = {60.0};
    static float front_mat_specular[] = {0.2, 0.2, 0.2, 1.0};
    static float front_mat_diffuse[] = {0.5, 0.28, 0.38, 1.0};
    static float back_mat_shininess[] = {60.0};
    static float back_mat_specular[] = {0.5, 0.5, 0.2, 1.0};
    static float back_mat_diffuse[] = {1.0, 1.0, 0.2, 1.0};
    static float lmodel_ambient[] = {1.0, 1.0, 1.0, 1.0};
    static float lmodel_twoside[] = {GL_TRUE};

    glMatrixMode(GL_PROJECTION);
    gluPerspective(450, 1.0, 0.5, 10.0);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glMaterialfv(GL_FRONT, GL_SHININESS, front_mat_shininess);
    glMaterialfv(GL_FRONT, GL_SPECULAR, front_mat_specular);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, front_mat_diffuse);
    glMaterialfv(GL_BACK, GL_SHININESS, back_mat_shininess);
    glMaterialfv(GL_BACK, GL_SPECULAR, back_mat_specular);
    glMaterialfv(GL_BACK, GL_DIFFUSE, back_mat_diffuse);
    if (rgb) {
    glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
    }

    if (rgb) {
    glEnable(GL_COLOR_MATERIAL);
    } else {
    SetColorMap();
    }
}

static void InitTexture(void)
{

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}

static void Init(void)
{

    glClearColor(0.0, 0.0, 0.0, 0.0);

    glShadeModel(GL_SMOOTH);

    glFrontFace(GL_CW);

    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);

    InitMaterials();
    InitTexture();
    InitMesh();

    glMatrixMode(GL_MODELVIEW);
    glTranslatef(0.0, 0.4, -1.8);
    glScalef(2.0, 2.0, 2.0);
    glRotatef(-35.0, 1.0, 0.0, 0.0);
    glRotatef(35.0, 0.0, 0.0, 1.0);
}

static void CALLBACK Reshape(int width, int height)
{

    glViewport(0, 0, (GLint)width, (GLint)height);
}

static void CALLBACK Key_c(void)
{
    contouring++;
    if (contouring == 1) {
        static GLfloat map[4] = {0, 0, 20, 0};

        glTexImage2D(GL_TEXTURE_2D, 0, 3, 4, 4, 0, GL_LUMINANCE,
             GL_UNSIGNED_BYTE, (GLvoid *)contourTexture1);
        glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
        glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
        glTexGenfv(GL_S, GL_OBJECT_PLANE, map);
        glTexGenfv(GL_T, GL_OBJECT_PLANE, map);
        glEnable(GL_TEXTURE_2D);
        glEnable(GL_TEXTURE_GEN_S);
        glEnable(GL_TEXTURE_GEN_T);
    } else if (contouring == 2) {
        static GLfloat map[4] = {0, 0, 20, 0};

        glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
        glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
        glPushMatrix();
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glTexGenfv(GL_S, GL_EYE_PLANE, map);
        glTexGenfv(GL_T, GL_EYE_PLANE, map);
        glPopMatrix();
    } else {
        contouring = 0;
        glDisable(GL_TEXTURE_GEN_S);
        glDisable(GL_TEXTURE_GEN_T);
        glDisable(GL_TEXTURE_2D);
    }
}

static void CALLBACK Key_s(void)
{
    smooth = !smooth;
    if (smooth) {
        glShadeModel(GL_SMOOTH);
    } else {
        glShadeModel(GL_FLAT);
    }
}

static void CALLBACK Key_l(void)
{
    lighting = !lighting;
    if (lighting) {
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        if (rgb) {
        glEnable(GL_COLOR_MATERIAL);
        }
    } else {
        glDisable(GL_LIGHTING);
        glDisable(GL_LIGHT0);
        if (rgb) {
        glDisable(GL_COLOR_MATERIAL);
        }
    }
}

static void CALLBACK Key_d(void)
{
    depth = !depth;
    if (depth) {
        glEnable(GL_DEPTH_TEST);
        clearMask |= GL_DEPTH_BUFFER_BIT;
    } else {
        glDisable(GL_DEPTH_TEST);
        clearMask &= ~GL_DEPTH_BUFFER_BIT;
    }
}

static void CALLBACK Key_SPACE(void)
{
    stepMode = !stepMode;

    if (stepMode) {
        auxIdleFunc(0);
/*      tkDisplayFunc(Animate); */
    } else {
        auxIdleFunc(Animate);
/*      tkDisplayFunc(0);*/
    }
}

static void CALLBACK Key_n(void)
{
    if (stepMode) {
        nextFrame = 1;
    }
}

static void CALLBACK Key_a(void)
{
    spinMode = !spinMode;
}

static GLenum Args(int argc, char **argv)
{
    GLint i;

    rgb = GL_TRUE;
    doubleBuffer = GL_TRUE;

    frames = 10;
    widthX = 10;
    widthY = 10;
    checkerSize = 2;
    height = 0.2;

    for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-ci") == 0) {
        rgb = GL_FALSE;
    } else if (strcmp(argv[i], "-rgb") == 0) {
        rgb = GL_TRUE;
    } else if (strcmp(argv[i], "-sb") == 0) {
        doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
        doubleBuffer = GL_TRUE;
    } else if (strcmp(argv[i], "-grid") == 0) {
        if (i+2 >= argc || argv[i+1][0] == '-' || argv[i+2][0] == '-') {
        //printf("-grid (No numbers).\n");
        return GL_FALSE;
        } else {
        widthX = atoi(argv[++i]);
        widthY = atoi(argv[++i]);
        }
    } else if (strcmp(argv[i], "-size") == 0) {
        if (i+1 >= argc || argv[i+1][0] == '-') {
        //printf("-checker (No number).\n");
        return GL_FALSE;
        } else {
        checkerSize = atoi(argv[++i]);
        }
    } else if (strcmp(argv[i], "-wave") == 0) {
        if (i+1 >= argc || argv[i+1][0] == '-') {
        //printf("-wave (No number).\n");
        return GL_FALSE;
        } else {
        height = atof(argv[++i]);
        }
    } else if (strcmp(argv[i], "-frames") == 0) {
        if (i+1 >= argc || argv[i+1][0] == '-') {
        //printf("-frames (No number).\n");
        return GL_FALSE;
        } else {
        frames = atoi(argv[++i]);
        }
    } else {
        //printf("%s (Bad option).\n", argv[i]);
        return GL_FALSE;
    }
    }
    return GL_TRUE;
}

void main(int argc, char **argv)
{
    GLenum type;

    if (Args(argc, argv) == GL_FALSE) {
    auxQuit();
    }

    auxInitPosition(0, 0, 300, 300);

    type = AUX_DEPTH16;
    type |= (rgb) ? AUX_RGB : AUX_INDEX;
    type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;

    auxInitDisplayMode(type);

    if (auxInitWindow("Wave Demo") == GL_FALSE) {
    auxQuit();
    }

    Init();

    auxExposeFunc((AUXEXPOSEPROC)Reshape);
    auxReshapeFunc((AUXRESHAPEPROC)Reshape);
    auxKeyFunc(AUX_c, Key_c);
    auxKeyFunc(AUX_s, Key_s);
    auxKeyFunc(AUX_l, Key_l);
    auxKeyFunc(AUX_d, Key_d);
    auxKeyFunc(AUX_SPACE, Key_SPACE);
    auxKeyFunc(AUX_n, Key_n);
    auxKeyFunc(AUX_a, Key_a);
    auxIdleFunc(Animate);
    auxMainLoop(Animate);
}


README.TXT

Sample screen savers that use OpenGL.


GENDROP.C

/******************************Module*Header*******************************\
* Module Name: gendrop.c
*
* The Splash style of the 3D Flying Objects screen saver.
*
* Simulation of a drop of water falling into a pool of water.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <stdlib.h>
#include <windows.h>
#include <GL\gl.h>
#include <string.h>
#include <math.h>
#include "ss3dfo.h"
#include "mesh.h"

#define FLOAT_SMALL (1e-6)

#define DROPPREC   10

// Remember from pre-calc:
//      x = r cos th
//      y = r sin th
// to convert from polar to rect, and that
//      x = x' cos th - y' sin th
//      y = x' sin th + y' cos th
// to rotate axes.
//
// Also, note that the equation for a lemniscate is:
//      r = sqrt(sin 2*th)
//

static POINT3D *circle;
static POINT3D *drop;
static POINT3D *curves;
static MESH waterMesh;
static MESH waterInmesh;
static MESH waterOutmesh;
static MESH waterBorderMesh;
static MESH *drops;
static int iPrec;
static float fRadiusFact = 0.35f;

static GLfloat light0Pos[] = {100.0f, 100.0f, 100.0f, 0.0f};
static dropList[DROPPREC];

void genCurves()
{
    int i;
    double angle;
    double step = -PI / (float)(iPrec - 1);
    double start = PI / 2.0;
    double rotSin = sin(PI / 4.0);
    double rotCos = cos(PI / 4.0);
    double aFract = 0.0;
    double bFract = 1.0;
    double fractInc = 1.0 / (double)(iPrec - 1);
    POINT3D *pt = curves;

    for (i = 0, angle = start; i < iPrec; i++, angle += step) {
        circle[i].x = (float) (0.5 * cos(angle));
        circle[i].y = (float) (0.5 * sin(angle));
    }

    step = (-PI / 4.0) / (float)(iPrec - 1);
    start = PI / 4.0;

    for (i = 0, angle = start; i < iPrec; i++, angle += step) {
        double x, y, r;
        double xrot, yrot;
        double sinVal;

        sinVal = sin(2.0 * angle);
        if (sinVal < 0.0)
            sinVal = -sinVal;

        r = 1.5 * sqrt(sinVal);
        x = r * cos(angle);
        y = r * sin(angle);

        xrot = x * rotCos - y * rotSin;
        yrot = x * rotSin + y * rotCos - 1.0;

        drop[i].x = (float) xrot;
        drop[i].y = (float) yrot;
    }

    for (i = 0; i < DROPPREC; i++) {
        int j;

        for (j = 0; j < iPrec; j++, pt++) {
            pt->x = (float) (aFract * circle[j].x +
                             bFract * drop[j].x);

            pt->y = (float) (aFract * circle[j].y +
                             bFract * drop[j].y);

            pt->z = 0.0f;
        }
        aFract += fractInc;
        bFract -= fractInc;
    }
}

#define NORMS(x, y) waterMesh.norms[((x) * iPrec) + y]
#define BNORMS(x, y) waterBorderMesh.norms[((x) * iPrec) + y]
#define INGRID(x, y)  waterInmesh.pts[((x) * iPrec) + y]
#define OUTGRID(x, y)  waterOutmesh.pts[((x) * iPrec) + y]
#define GRID(x, y)  waterMesh.pts[((x) * iPrec) + y]
#define BGRID(x, y)  waterBorderMesh.pts[((x) * iPrec) + y]

void genWater(double freq, double damp, double mag, double w, double minr)
{
    int i;
    int j;
    double r;
    double theta;
    double thetaInc = (2.0 * PI) / (float)iPrec;
    double posInc = 1.0 / (float)iPrec;
    int facecount;
    double xCenter = 0.0;
    double zCenter = 0.0;
    POINT3D norm;
    static BOOL first = TRUE;

    if (first) {
        for (i = 0, r = 0.0; i < iPrec; i++, r += posInc) {
            for (j = 0, theta = 0.0; j < iPrec; j++, theta += thetaInc) {
                float x, z;
                float dx, dz;
                float rr;

                x = (float) cos(theta);
                z = (float) sin(theta);

                dx = x - (float) xCenter;
                dz = z - (float) zCenter;

                rr = (float) sqrt((dx * dx) + (dz * dz));
                dx /= rr;
                dz /= rr;
                dx *= i / (float)(iPrec - 1);
                dz *= i / (float)(iPrec - 1);
                GRID(i, j).x = dx + (float) xCenter;
                GRID(i, j).z = dz + (float) zCenter;

                INGRID(i, j).y = 0.0f;
                OUTGRID(i, j).y = 0.0f;
            }
        }
    }

    for (i = (iPrec - 1), r = 1.0; i >= 0; i--, r -= posInc) {
        float val;

        if (i == 0) {
            if (minr != 0.0)
                val = (float) (-mag * cos(w + (r * freq)) * exp((-damp * r)/2.0));
            else
                val =  INGRID(0, 0).y * 0.95f;
        } else
            val = OUTGRID(i - 1, 0).y * 0.95f;

        for (j = 0; j < iPrec; j++)
            OUTGRID(i, j).y = val;
    }

    for (i = 0, r = 0.0; i < iPrec; i++, r += posInc) {
        for (j = 0; j < iPrec; j++) {
            if (i == iPrec-1)
                INGRID(i, j).y = -OUTGRID(i, j).y;
            else
                INGRID(i, j).y = INGRID(i + 1, j).y * 0.95f;
        }
    }

    waterMesh.numFaces = 0;
    waterBorderMesh.numFaces = 0;

    for (i = 0; i < iPrec; i++) {
        for (j = 0; j < iPrec; j++) {
            NORMS(i, j).x = 0.0f;
            NORMS(i, j).y = 0.0f;
            NORMS(i, j).z = 0.0f;
        }
    }

    for (i = 0, r = 0.0; i < iPrec; i++, r += posInc) {
        for (j = 0, theta = 0.0; j < iPrec; j++, theta += thetaInc) {
            GRID(i, j).y = OUTGRID(i, j).y + INGRID(i, j).y;

            if (i == (iPrec - 1)) {
                GRID(i, j).y = 0.0f;

                BGRID(0, j).x = GRID(i, j).x;
                BGRID(0, j).z = GRID(i, j).z;
                BGRID(0, j).y = GRID(i, j).y;

                BGRID(1, j).x = GRID(i, j).x;
                BGRID(1, j).z = GRID(i, j).z;
                BGRID(1, j).y = -0.5f;
            }
        }
    }

    for (i = 0; i < 2; i++) {
        for (j = 0; j < iPrec; j++) {
            BNORMS(i, j).x = 0.0f;
            BNORMS(i, j).y = 0.0f;
            BNORMS(i, j).z = 0.0f;
        }
    }

    for (facecount = 0, i = 0; i < (iPrec - 1); i++) {
        for (j = 0; j < iPrec; j++) {
            int k, l;

            k = i + 1;

            if (j == (iPrec - 1))
                l = 0;
            else
                l = j + 1;

            ss_calcNorm(&norm, &GRID(k, j), &GRID(i, j), &GRID(i, l));

            if (norm.x > -FLOAT_SMALL && norm.x < FLOAT_SMALL &&
                norm.y > -FLOAT_SMALL && norm.y < FLOAT_SMALL &&
                norm.z > -FLOAT_SMALL && norm.z < FLOAT_SMALL)
                ss_calcNorm(&norm, &GRID(i, l), &GRID(k, l), &GRID(k, j));

            waterMesh.faces[facecount].material = 0;
            waterMesh.faces[facecount].norm = norm;

            NORMS(i, j).x += norm.x;
            NORMS(i, j).y += norm.y;
            NORMS(i, j).z += norm.z;

            NORMS(k, j).x += norm.x;
            NORMS(k, j).y += norm.y;
            NORMS(k, j).z += norm.z;

            NORMS(i, l).x += norm.x;
            NORMS(i, l).y += norm.y;
            NORMS(i, l).z += norm.z;

            NORMS(k, l).x += norm.x;
            NORMS(k, l).y += norm.y;
            NORMS(k, l).z += norm.z;

            waterMesh.faces[facecount].p[0] = (k * iPrec) + j;
            waterMesh.faces[facecount].p[1] = (i * iPrec) + j;
            waterMesh.faces[facecount].p[2] = (k * iPrec) + l;
            waterMesh.faces[facecount].p[3] = (i * iPrec) + l;
            waterMesh.numFaces++;
            facecount++;
        }
    }

    waterMesh.numPoints = iPrec * iPrec;

    for (facecount = 0, i = 0; i < 1; i++) {
        for (j = 0; j < iPrec; j++) {
            int k, l;

            k = i + 1;

            if (j == (iPrec - 1))
                l = 0;
            else
                l = j + 1;

            ss_calcNorm(&norm, &BGRID(k, j), &BGRID(i, j), &BGRID(i, l));

            waterBorderMesh.faces[facecount].material = 0;
            waterBorderMesh.faces[facecount].norm = norm;

// Setting SMOOTH_BORDER will render the border (the sides of the "pool")
// with smooth shading.  This effect is good at higher tesselations, but
// doesn't really look that good for low tesselations.
//
// A possible enhancement for later: use smooth shading if tesselation
// exceeds some threshold.  Should we just pick some arbitrary threshold?
// Make it a setup option?  Things look pretty good now, so don't bother?

#if SMOOTH_BORDER
            BNORMS(i, j).x += norm.x;
            BNORMS(i, j).y += norm.y;
            BNORMS(i, j).z += norm.z;

            if (i) {
                BNORMS(i-1, j).x += norm.x;
                BNORMS(i-1, j).y += norm.y;
                BNORMS(i-1, j).z += norm.z;
            }
            if (j) {
                BNORMS(i, j-1).x += norm.x;
                BNORMS(i, j-1).y += norm.y;
                BNORMS(i, j-1).z += norm.z;
            }

            BNORMS(k, j).x += norm.x;
            BNORMS(k, j).y += norm.y;
            BNORMS(k, j).z += norm.z;

            BNORMS(i, l).x += norm.x;
            BNORMS(i, l).y += norm.y;
            BNORMS(i, l).z += norm.z;
#else
            BNORMS(i, j) = norm;

            if (i)
                BNORMS(i-1, j) = norm;
            if (j)
                BNORMS(i, j-1) = norm;

            BNORMS(k, j) = norm;
            BNORMS(i, l) = norm;
#endif

            waterBorderMesh.faces[facecount].p[0] = (k * iPrec) + j;
            waterBorderMesh.faces[facecount].p[1] = (i * iPrec) + j;
            waterBorderMesh.faces[facecount].p[2] = (k * iPrec) + l;
            waterBorderMesh.faces[facecount].p[3] = (i * iPrec) + l;
            waterBorderMesh.numFaces++;
            facecount++;
        }
    }
    waterBorderMesh.numPoints = 2 * iPrec;

    ss_normalizeNorms(waterBorderMesh.norms, waterBorderMesh.numPoints);
    ss_normalizeNorms(waterMesh.norms, waterMesh.numPoints);

    first = FALSE;
}

void initDropScene()
{
    int i;

    iPrec = (int)(fTesselFact * 10.5);
    if (iPrec < 4)
        iPrec = 4;

    if (fTesselFact > fRadiusFact)
        fRadiusFact = fTesselFact;

    circle = (POINT3D *)SaverAlloc(iPrec * sizeof(POINT3D));
    drop = (POINT3D *)SaverAlloc(iPrec * sizeof(POINT3D));
    curves = (POINT3D *)SaverAlloc(DROPPREC * iPrec * sizeof(POINT3D));
    drops = (MESH *)SaverAlloc(DROPPREC * sizeof(MESH));

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.5, 1.5, -1.5, 1.5, 0.0, 3.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glTranslatef(0.0f, 0.0f, -1.5f);

    glDisable(GL_CULL_FACE);

    newMesh(&waterInmesh, iPrec * iPrec, iPrec * iPrec + iPrec);
    newMesh(&waterOutmesh, iPrec * iPrec, iPrec * iPrec + iPrec);
    newMesh(&waterMesh, iPrec * iPrec, iPrec * iPrec + iPrec);
    newMesh(&waterBorderMesh, iPrec, 2 * iPrec);
    genCurves();

    for (i = 0; i < DROPPREC; i++)
        revolveSurface(&drops[i], &curves[i * iPrec], iPrec);
    for (i = 0; i < DROPPREC; i++) {
        GLuint id = 0x10 + i;
        dropList[i] = id;
        MakeList(id, &drops[i]);
    }
    for (i = 0; i < DROPPREC; i++) {
        delMesh(&drops[i]);
    }

    SaverFree(circle);
    SaverFree(drop);
    SaverFree(curves);
    SaverFree(drops);
}

void delDropScene()
{
    delMesh(&waterMesh);
    delMesh(&waterInmesh);
    delMesh(&waterOutmesh);
    delMesh(&waterBorderMesh);
}

void updateDropScene(int flags)
{
    static double zrot = 0.0;
    static double yrot = 0.0;
    static double mxrot = 0.0;
    static double myrot = 0.0;
    static double mzrot = 0.0;
    static double mxrotInc = 0.0;
    static double myrotInc = 0.1;
    static double zrotInc = 3.0;
    static double yrotInc = 1.5;
    static double mzrotInc = 0.0;
    static double ypos = 1.0;
    static int dropnum = 0;
    static double radius = 0.3;
    static double damp = 1.0;
    static double mag = 0.0;
    static double w = 1.0;
    static double freq = 1.0;
    static double dist;
    static double minr = 0.0;
    static int h = 0;
    RGBA color;

    glPushMatrix();

    zrot += zrotInc;
    if (zrot >= 45.0) {
        zrot = 45.0;
        zrotInc = -(2.0 + ((float)rand() / (float)RAND_MAX) * 3.0);
    } else if (zrot <= -45.0) {
        zrot = -45.0;
        zrotInc = 2.0 + ((float)rand() / (float)RAND_MAX) * 3.0;
    }

    yrot += yrotInc;
    if (yrot >= 10.0) {
        yrot = 10.0;
        yrotInc = -(1.0 + ((float)rand() / (float)RAND_MAX) * 2.0);
    } else if (zrot <= -10.0) {
        yrot = -10.0;
        yrotInc = 1.0 + ((float)rand() / (float)RAND_MAX) * 2.0;
    }

    if ((ypos + 0.5 < -radius) && (mag < 0.05)) {
        radius = (float)rand() / (6.0 * (float)RAND_MAX) + 0.1;
        ypos = 1.0;
        dropnum = 0;
    }

    dist = (ypos + 0.5);

    if ((dist > -radius / 2.0) && (dist < radius / 2.0)) {
        if (dist <= 0.0)
            dist = radius / 2.0;
        else
            dist = (radius / 2.0) - dist;
        freq = (0.25 * PI) / dist;
        if (freq < 0.2)
            freq = 0.2;

        minr = radius;

        damp = 20.0;
        mag = (0.35 / fRadiusFact) + 0.2 * dist;

        w = 0;
    } else {
        minr -= 0.05;
        if (minr < 0.0)
            minr = 0.0;

        mag = mag * 0.95;
        if (minr == 0.0) {
            w -= (PI / 6.0);
            mag *= 0.75;
        }
        if (damp > 0.0)
            damp -= 1.0;
    }

    genWater(freq, damp, mag, w, minr);

    glRotatef((GLfloat) zrot, 0.0f, 0.0f, 1.0f);
    glRotatef(30.0f, 1.0f, 0.0f, 0.0f);

    glPushMatrix();
    glTranslatef(0.0f, -0.5f, 0.0f);
    glRotatef((GLfloat) (myrot * (180.0 / PI)), 0.0f, 1.0f, 0.0f);

    if (bColorCycle) {
        ss_HsvToRgb((float)h, 1.0f, 1.0f, &color );

        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
                     (GLfloat *) &color);
        h++;
        h %= 360;
    } else {
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
                     (GLfloat *) &Material[6].kd);
    }

    updateObject(&waterMesh, bSmoothShading);

    if (bSmoothShading)
        glShadeModel(GL_FLAT);

    if (!bColorCycle)
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
                     (GLfloat *) &Material[2].kd);

    updateObject2(&waterBorderMesh, FALSE);
    glPopMatrix();

    if (bSmoothShading)
        glShadeModel(GL_SMOOTH);

    if (dist > -radius) {

        if (!bColorCycle)
            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
                         (GLfloat *) &Material[6].kd);
        glTranslatef(0.0f, (GLfloat) ypos, 0.0f);
        glScalef((GLfloat) radius, (GLfloat) radius, (GLfloat) radius);
        glRotatef(180.0f, 1.0f, 0.0f, 0.0f);
        glEnable(GL_NORMALIZE);
        glCallList(dropList[dropnum]);
        glDisable(GL_NORMALIZE);

    }

    myrot += myrotInc;

    ypos -= 0.08;
    dropnum = (int) ((DROPPREC - 1) - (ypos * (DROPPREC - 1)));
    if (dropnum > (DROPPREC - 1))
        dropnum = DROPPREC - 1;

    glPopMatrix();
}


GENEXPLD.C

/******************************Module*Header*******************************\
* Module Name: genexpld.c
*
* The Explode style of the 3D Flying Objects screen saver.
*
* Simulation of a sphere that occasionally explodes.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
#include <GL\gl.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "ss3dfo.h"

#define RADIUS         0.3
#define STEPS    30
#define MAXPREC20

static MATRIX *faceMat;
static float *xstep;
static float *ystep;
static float *zstep;
static float *xrot;
static float *yrot;
static float *zrot;
static MESH explodeMesh;
static int iPrec = 10;

static BOOL bOpenGL11;

// Data type accepted by glInterleavedArrays
typedef struct _POINT_N3F_V3F {
    POINT3D normal;
    POINT3D vertex;
} POINT_N3F_V3F;

static POINT_N3F_V3F *pN3V3;

static GLfloat matl1Diffuse[] = {1.0f, 0.8f, 0.0f, 1.0f};
static GLfloat matl2Diffuse[] = {0.8f, 0.8f, 0.8f, 1.0f};
static GLfloat matlSpecular[] = {1.0f, 1.0f, 1.0f, 1.0f};
static GLfloat light0Pos[] = {100.0f, 100.0f, 100.0f, 0.0f};

void genExplode()
{
    int i;
    POINT3D circle[MAXPREC+1];
    double angle;
    double step = -PI / (float)(iPrec - 1);
    double start = PI / 2.0;

    for (i = 0, angle = start; i < iPrec; i++, angle += step) {
        circle[i].x = (float) (RADIUS * cos(angle));
        circle[i].y = (float) (RADIUS * sin(angle));
        circle[i].z = 0.0f;
    }

    revolveSurface(&explodeMesh, circle, iPrec);

    for (i = 0; i < explodeMesh.numFaces; i++) {
        ss_matrixIdent(&faceMat[i]);
        xstep[i] = (float)(((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0));
        ystep[i] = (float)(((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0));
        zstep[i] = (float)(((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0));
        xrot[i] = 0.0f;
        yrot[i] = 0.0f;
        zrot[i] = 0.0f;
    }
}

void initExplodeScene()
{
    iPrec = (int)(fTesselFact * 10.5);
    if (iPrec < 5)
        iPrec = 5;
    if (iPrec > MAXPREC)
        iPrec = MAXPREC;

    faceMat = (MATRIX *)SaverAlloc((iPrec * iPrec) *
     (4 * 4 * sizeof(float)));
    xstep = SaverAlloc(iPrec * iPrec * sizeof(float));
    ystep = SaverAlloc(iPrec * iPrec * sizeof(float));
    zstep = SaverAlloc(iPrec * iPrec * sizeof(float));
    xrot = SaverAlloc(iPrec * iPrec * sizeof(float));
    yrot = SaverAlloc(iPrec * iPrec * sizeof(float));
    zrot = SaverAlloc(iPrec * iPrec * sizeof(float));

    genExplode();

    // Find out the OpenGL version that we are running on.
    bOpenGL11 = ss_fOnGL11();

    // Setup the data arrays.
    pN3V3 = SaverAlloc(explodeMesh.numFaces * 4 * sizeof(POINT_N3F_V3F));

    // If we are running on OpenGL 1.1, use the new vertex array functions.
    if (bOpenGL11) {
        glInterleavedArrays(GL_N3F_V3F, 0, pN3V3);
    }

    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, matl1Diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, matlSpecular);
    glMaterialf(GL_FRONT, GL_SHININESS, 100.0f);

    glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, matl2Diffuse);
    glMaterialfv(GL_BACK, GL_SPECULAR, matlSpecular);
    glMaterialf(GL_BACK, GL_SHININESS, 60.0f);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-0.33, 0.33, -0.33, 0.33, 0.3, 3.0);

    glTranslatef(0.0f, 0.0f, -1.5f);
}

void delExplodeScene()
{
    delMesh(&explodeMesh);

    SaverFree(faceMat);
    SaverFree(xstep);
    SaverFree(ystep);
    SaverFree(zstep);
    SaverFree(xrot);
    SaverFree(yrot);
    SaverFree(zrot);
    SaverFree(pN3V3);
}

void updateExplodeScene(int flags)
{
    static double mxrot = 0.0;
    static double myrot = 0.0;
    static double mzrot = 0.0;
    static double mxrotInc = 0.0;
    static double myrotInc = 0.1;
    static double mzrotInc = 0.0;
    static float maxR;
    static float r = 0.0f;
    static float rotZ = 0.0f;
    static int count = 0;
    static int direction = 1;
    static int restCount = 0;
    static float lightSpin = 0.0f;
    static float spinDelta = 5.0f;
    static int h = 0;
    static RGBA color;
    int i;
    MFACE *faces;
    POINT_N3F_V3F *pn3v3;

    if( gbBounce ) {
        // floating window bounced off an edge
        if (mxrotInc) {
            mxrotInc = 0.0;
            myrotInc = 0.1;
        } else if (myrotInc) {
            myrotInc = 0.0;
            mzrotInc = 0.1;
        } else if (mzrotInc) {
            mzrotInc = 0.0;
            mxrotInc = 0.1;
        }
        gbBounce = FALSE;
    }

    mxrot += mxrotInc;
    myrot += myrotInc;
    mzrot += mzrotInc;

    if (bColorCycle || h == 0) {
        ss_HsvToRgb((float)h, 1.0f, 1.0f, &color);

        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, (GLfloat *) &color);

        h++;
        h %= 360;
    }

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glRotatef(-lightSpin, 0.0f, 1.0f, 0.0f);
    glLightfv(GL_LIGHT0, GL_POSITION, light0Pos);
    lightSpin += spinDelta;
    if ((lightSpin > 90.0) || (lightSpin < 0.0))
        spinDelta = -spinDelta;
    glPopMatrix();

    if (!bOpenGL11) {
        glBegin(GL_QUADS);
    }

    for(
        i = 0, faces = explodeMesh.faces, pn3v3 = pN3V3;
        i < explodeMesh.numFaces;
i++, faces++, pn3v3 += 4
       ) {
        int a, b, c, d;
        int j;
        POINT3D vector;

        ss_matrixIdent(&faceMat[i]);
        ss_matrixRotate(&faceMat[i], xrot[i], yrot[i], zrot[i]);

        if (restCount)
            ;
        else {
            xrot[i] += (xstep[i]);
            yrot[i] += (ystep[i]);
            zrot[i] += (zstep[i]);
        }

        a = faces->p[0];
        b = faces->p[1];
        c = faces->p[3];
        d = faces->p[2];

        memcpy(&pn3v3[0].vertex, (explodeMesh.pts + a), sizeof(POINT3D));
        memcpy(&pn3v3[1].vertex, (explodeMesh.pts + b), sizeof(POINT3D));
        memcpy(&pn3v3[2].vertex, (explodeMesh.pts + c), sizeof(POINT3D));
        memcpy(&pn3v3[3].vertex, (explodeMesh.pts + d), sizeof(POINT3D));

        vector.x = pn3v3[0].vertex.x;
        vector.y = pn3v3[0].vertex.y;
        vector.z = pn3v3[0].vertex.z;

        for (j = 0; j < 4; j++) {
            pn3v3[j].vertex.x -= vector.x;
            pn3v3[j].vertex.y -= vector.y;
            pn3v3[j].vertex.z -= vector.z;
            ss_xformPoint((POINT3D *)&pn3v3[j].vertex, (POINT3D *)&pn3v3[j].vertex, &faceMat[i]);
            pn3v3[j].vertex.x += vector.x + (vector.x * r);
            pn3v3[j].vertex.y += vector.y + (vector.y * r);
            pn3v3[j].vertex.z += vector.z + (vector.z * r);
        }
        if (bSmoothShading) {
            memcpy(&pn3v3[0].normal, (explodeMesh.norms + a), sizeof(POINT3D));
            memcpy(&pn3v3[1].normal, (explodeMesh.norms + b), sizeof(POINT3D));
            memcpy(&pn3v3[2].normal, (explodeMesh.norms + c), sizeof(POINT3D));
            memcpy(&pn3v3[3].normal, (explodeMesh.norms + d), sizeof(POINT3D));

            for (j = 0; j < 4; j++)
                ss_xformNorm((POINT3D *)&pn3v3[j].normal, (POINT3D *)&pn3v3[j].normal, &faceMat[i]);
        } else {
            memcpy(&pn3v3[0].normal, &faces->norm, sizeof(POINT3D));
            ss_xformNorm((POINT3D *)&pn3v3[0].normal, (POINT3D *)&pn3v3[0].normal, &faceMat[i]);
            memcpy(&pn3v3[1].normal, &pn3v3[0].normal, sizeof(POINT3D));
            memcpy(&pn3v3[2].normal, &pn3v3[0].normal, sizeof(POINT3D));
            memcpy(&pn3v3[3].normal, &pn3v3[0].normal, sizeof(POINT3D));
        }

        if (!bOpenGL11) {
            if (bSmoothShading) {
                glNormal3fv((GLfloat *)&pn3v3[0].normal);
                glVertex3fv((GLfloat *)&pn3v3[0].vertex);
                glNormal3fv((GLfloat *)&pn3v3[1].normal);
                glVertex3fv((GLfloat *)&pn3v3[1].vertex);
                glNormal3fv((GLfloat *)&pn3v3[2].normal);
                glVertex3fv((GLfloat *)&pn3v3[2].vertex);
                glNormal3fv((GLfloat *)&pn3v3[3].normal);
                glVertex3fv((GLfloat *)&pn3v3[3].vertex);
            } else {
                glNormal3fv((GLfloat *)&pn3v3[0].normal);
                glVertex3fv((GLfloat *)&pn3v3[0].vertex);
                glVertex3fv((GLfloat *)&pn3v3[1].vertex);
                glVertex3fv((GLfloat *)&pn3v3[2].vertex);
                glVertex3fv((GLfloat *)&pn3v3[3].vertex);
            }
        }
    }

    if (bOpenGL11) {
        glDrawArrays(GL_QUADS, 0, explodeMesh.numFaces * 4);
    } else {
        glEnd();
    }

    if (restCount) {
        restCount--;
        goto resting;
    }

    if (direction) {
        maxR = r;
        r += (float) (0.3 * pow((double)(STEPS - count) / (double)STEPS, 4.0));
    } else {
        r -= (float) (maxR / (double)(STEPS));
    }

    count++;
    if (count > STEPS) {
        direction ^= 1;
        count = 0;

        if (direction == 1) {
            restCount = 10;
            r = 0.0f;

            for (i = 0; i < explodeMesh.numFaces; i++) {
                ss_matrixIdent(&faceMat[i]);
                xstep[i] = (float) (((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0));
                ystep[i] = (float) (((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0));
                zstep[i] = (float) (((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0));

                xrot[i] = 0.0f;
                yrot[i] = 0.0f;
                zrot[i] = 0.0f;
            }
        }
    }

resting:
    ;
}


GENLEM.C

/******************************Module*Header*******************************\
* Module Name: genlem.c
*
* The Twist style of the 3D Flying Objects screen saver.
*
* Solid model of a 3D lemniscate.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <stdlib.h>
#include <windows.h>
#include <GL\gl.h>
#include <string.h>
#include <math.h>
#include "ss3dfo.h"
#include "mesh.h"

#define ROT_PREC    10
#define NORMS(x, y) lemMesh.norms[((x) * iPrec) + y]
#define GRID(x, y)  lemMesh.pts[((x) * iPrec) + y]

static MESH lemMesh;
static POINT3D basis[ROT_PREC];
static double zrot = 0.2;
static int iPrec = 32;
static double *lemX;
static double *lemY;
static double *lemXT;
static double *lemYT;

static void getLem(double index, double max, double *angle, double *r)
{
    double a, sina;

    a = (index * PI) / (max - 1.0);
    if (a >= PI)
        a -= PI;
    if (a > PI / 2.0) {
        *angle = (2.0 * PI) - a;
        sina = sin( 2.0 * *angle );
        if( sina < 0.0 )
            sina = 0.0; // protect against sqrt fpe
        *r = 0.5 * sqrt(sina);
    } else {
        *angle = a;
        sina = sin( 2.0 * *angle );
        if( sina < 0.0 )
            sina = 0.0;
        *r = 0.5 * sqrt(sina);
    }
}

static void initLemCoords(int iMax)
{
    int i;
    double max = (double)iMax;
    double angle;
    double r;

    for (i = 0; i < iMax; i++) {
        getLem((double)i, (double)iPrec, &angle, &r);
        lemX[i] = r * cos(angle);
        lemY[i] = r * sin(angle);

        getLem((double)i + 0.00001, (double)iPrec, &angle, &r);
        lemXT[i] = r * cos(angle);
        lemYT[i] = r * sin(angle);

    }
}

void genLemniscate(void)
{
    int i;
    int j;
    double posInc = 2.0 / (float)iPrec;
    int facecount = 0;
    int ptcount = 0;
    POINT3D norm;
    static float twistFact = 0.0f;
    static float twistFactAdd = 0.05f;
    POINT3D a[ROT_PREC];
    POINT3D b[ROT_PREC];
    MATRIX matrix;
    MESH *mesh = &lemMesh;

    mesh->numPoints = 0;
    mesh->numFaces = 0;
    for (i = 0; i < (iPrec - 1) * (ROT_PREC - 1); i++)
        mesh->norms[i] = ss_ptZero;

    for (i = 0; i < (iPrec - 1); i++) {
        double x1, y1, x2, y2;
        double len;
        double sinAngle;
        double rotZ;
        int id[4];

        x1 = lemX[i];
        y1 = lemY[i];
        x2 = lemXT[i];
        y2 = lemYT[i];

        x2 -= x1;
        y2 -= y1;

        len = sqrt(x2 * x2 + y2 * y2);
        if (len > 0.0)
            sinAngle = y2 / len;
        else
            sinAngle = 0.0;
        if (y2 < 0.0)
            sinAngle = -sinAngle;
        rotZ = asin(sinAngle);
        if (x2 < 0.0)
            rotZ = PI - rotZ;
        if (y2 < 0.0)
            rotZ = -rotZ;
        if (rotZ < 0.0)
            rotZ = 2.0 * PI + rotZ;

        ss_matrixIdent(&matrix);
        ss_matrixRotate(&matrix, 0.0, 0.0, -rotZ);
        ss_matrixTranslate(&matrix, x1, y1,
                         twistFact * cos((2.0 * PI * (float)i) / ((float)iPrec - 1)));

        for (j = 0; j < ROT_PREC; j++)
            ss_xformPoint(&a[j], &basis[j], &matrix);

        x1 = lemX[i+1];
        y1 = lemY[i+1];

        x2 = lemXT[i+1];
        y2 = lemYT[i+1];

        x2 -= x1;
        y2 -= y1;

        len = sqrt(x2 * x2 + y2 * y2);

        if (len > 0.0)
            sinAngle = y2 / len;
        else
            sinAngle = 0.0;
        if (y2 < 0.0)
            sinAngle = -sinAngle;
        rotZ = asin(sinAngle);
        if (x2 < 0.0)
            rotZ = PI - rotZ;
        if (y2 < 0.0)
            rotZ = -rotZ;
        if (rotZ < 0.0)
            rotZ = 2.0 * PI + rotZ;

        ss_matrixIdent(&matrix);
        ss_matrixRotate(&matrix, 0.0, 0.0, -rotZ);
        ss_matrixTranslate(&matrix, x1, y1,
                         twistFact * cos((2.0 * PI * ((float)i + 1.0)) / ((float)iPrec - 1)));

        for (j = 0; j < ROT_PREC; j++)
            ss_xformPoint(&b[j], &basis[j], &matrix);

        memcpy(&mesh->pts[ptcount], &a, sizeof(POINT3D) * (ROT_PREC - 1));
        ptcount += (ROT_PREC - 1);
        mesh->numPoints += (ROT_PREC - 1);

        for (j = 0; j < (ROT_PREC - 1); j++) {
            int k;
            int jj;

            if (j == (ROT_PREC - 2))
                jj = 0;
            else
                jj = j + 1;

            ss_calcNorm(&norm, &b[j + 1], &b[j], &a[j]);

            mesh->faces[facecount].material = 3;
            mesh->faces[facecount].norm = norm;
            if (i == iPrec - 2) {
                id[0] = mesh->faces[facecount].p[0] = j;
                id[1] = mesh->faces[facecount].p[1] = jj;
            } else {
                id[0] = mesh->faces[facecount].p[0] = ptcount + j;
                id[1] = mesh->faces[facecount].p[1] = ptcount + jj;
            }
            id[2] = mesh->faces[facecount].p[2] = ptcount - (ROT_PREC - 1) + j;
            id[3] = mesh->faces[facecount].p[3] = ptcount - (ROT_PREC - 1) + jj;

            for (k = 0; k < 4; k++) {
                POINT3D *pn = &mesh->norms[id[k]];

                pn->x += norm.x;
                pn->y += norm.y;
                pn->z += norm.z;
            }
            mesh->numFaces++;
            facecount++;
        }
    }

    ss_normalizeNorms(lemMesh.norms, lemMesh.numPoints);

    if (twistFact >= 1.0f)
        twistFactAdd = -0.01f;
    else if (twistFact <= -1.0f)
        twistFactAdd = 0.01f;
    twistFact += twistFactAdd;

}

void initLemScene()
{
    int i;
    RGBA lightAmbient = {0.0f, 0.0f, 0.0f, 1.0f};

    iPrec = (int)(fTesselFact * 32.5);
    if (iPrec < 5)
        iPrec = 5;

    lemX = SaverAlloc(sizeof(double) * iPrec);
    lemY = SaverAlloc(sizeof(double) * iPrec);
    lemXT = SaverAlloc(sizeof(double) * iPrec);
    lemYT = SaverAlloc(sizeof(double) * iPrec);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.5, 1.5, -1.5, 1.5, 0.0, 3.0);
    glTranslatef(0.0f, 0.0f, -1.5f);

    newMesh(&lemMesh, (ROT_PREC - 1) * (iPrec - 1) ,
            (ROT_PREC - 1) * (iPrec - 1));
    for (i = 0; i < ROT_PREC; i++) {
        basis[i].x = 0.0f;
        basis[i].y = (float) (0.15 * cos((i * 2.0 * PI) / (ROT_PREC - 1.0)));
        basis[i].z = (float) (0.15 * sin((i * 2.0 * PI) / (ROT_PREC - 1.0)));
    }

    initLemCoords(iPrec);

    glFrontFace(GL_CW);
    glEnable(GL_CULL_FACE);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (GLfloat *) &lightAmbient);

    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
                 (GLfloat *) &Material[3].kd);
}

void delLemScene()
{
    delMesh(&lemMesh);

    SaverFree(lemX);
    SaverFree(lemY);
    SaverFree(lemXT);
    SaverFree(lemYT);
}

void updateLemScene(int flags)
{
    static double mxrot = 0.0;
    static double myrot = 0.0;
    static double mzrot = 0.0;
    static double mxrotInc = 0.0;
    static double myrotInc = 0.1;
    static double zrotInc = 0.1;
    static double mzrotInc = 0.0;
    static int h = 0;
    RGBA color;
    MATRIX model;

    mxrot += mxrotInc;
    myrot += myrotInc;
    mzrot += mzrotInc;

    if( gbBounce ) {
        // floating window bounced off an edge
        if (mxrotInc) {
            mxrotInc = 0.0;
            myrotInc = 0.1;
        } else if (myrotInc) {
            myrotInc = 0.0;
            mzrotInc = 0.1;
        } else if (mzrotInc) {
            mzrotInc = 0.0;
            mxrotInc = 0.1;
        }
        gbBounce = FALSE;
    }

    zrot += zrotInc;
    if (zrot >= PI / 4.0) {
        zrot = PI / 4.0;
        zrotInc = -0.03;
    } else if (zrot <= -PI / 4.0) {
        zrot = -PI / 4.0;
        zrotInc = 0.03;
    }

    genLemniscate();

    if (bColorCycle) {
        ss_HsvToRgb((float)h, 1.0f, 1.0f, &color );

        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, (GLfloat *) &color);

        h++;
        h %= 360;
    }

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.5, 1.5, -1.5, 1.5, 0.0, 3.0);
    glTranslatef(0.0f, 0.0f, -1.5f);
    glRotatef((GLfloat) (zrot * (180.0 / PI)), 0.0f, 1.0f, 0.0f);
    glRotatef(50.0f, 1.0f, 0.0f, 0.0f);
    glRotatef(50.0f, 0.0f, 0.0f, 1.0f);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0f, -0.5f, 0.0f);
    glRotatef((GLfloat) (mxrot * (180.0 / PI)), 1.0f, 0.0f, 0.0f);
    glRotatef((GLfloat) (myrot * (180.0 / PI)), 0.0f, 1.0f, 0.0f);
    glRotatef((GLfloat) (mzrot * (180.0 / PI)), 0.0f, 0.0f, 1.0f);

    ss_matrixIdent(&model);
    ss_matrixRotate(&model, mxrot, myrot, mzrot);
    ss_matrixTranslate(&model, 0.0, -0.5, 0.0);

    updateObject(&lemMesh, bSmoothShading);
}


GENSTRIP.C

/******************************Module*Header*******************************\
* Module Name: genstrip.c
*
* The Ribbon and 2 Ribbon styles of the 3D Flying Objects screen saver.
*
* Animation of 1 or 2 quad strips floating about.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
#include <GL\gl.h>
#include <string.h>
#include <math.h>
#include "ss3dfo.h"
#include "mesh.h"

static MESH stripMesh;
static int iPrec = 40;

void genStrip()
{
    static int counter = 0;
    int i;
    int facecount;
    // Use Hermite basis, pg 488, FVD
    static float M[4][4] = {{2.0f, -2.0f, 1.0f, 1.0f},
                            {-3.0f, 3.0f, -2.0f, -1.0f},
                            {0.0f, 0.0f, 1.0f, 0.0f},
                            {1.0f, 0.0f, 0.0f, 0.0f}};
    float xx[4], yy[4], zz[4];
    float cx[4], cy[4], cz[4];
    float d = 1.0f / (float) iPrec;
    float t = 0.0f;
    float t2, t3;
    POINT3D p1 = {-0.5f, 0.0f, 0.0f};
    POINT3D p2 = {0.5f, 0.0f, 0.0f};
    POINT3D v1 = {1.5f, 1.5f, 0.0f};
    POINT3D v2 = {0.0f, 3.0f, 0.0f};
    POINT3D norm;
    float sinVal;
    float angle;
    float angleStep = (float) (PI / iPrec);
    static float rotA = 0.0f;
    static float rotB = (float) (PI / 2.0);
    static float sideSin = 0.0f;
    float rotStepA = (float) (PI / (2.0 * iPrec));
    float rotStepB = (float) (PI / (4.0 * iPrec));
    MESH *mesh = &stripMesh;

#define NORMS(x, y) stripMesh.norms[((x) * iPrec) + y]
#define GRID(x, y)  stripMesh.pts[((x) * iPrec) + y]

    v1.x = (float) (4.0 * cos(rotA));
    v1.y = (float) (4.0 * sin(rotA));

    p2.x = (float) (0.5 * sin(rotB));
//    p2.y = (float) (0.5 * sin(rotB));

    rotA += rotStepA;
    rotB += rotStepB;
    counter++;
    if (counter >= (2 * iPrec)) {
        rotStepA = -rotStepA;
        counter = 0;
    }

    angle = sideSin;
    sideSin += (float) (PI / 80.0);

    xx[0] = p1.x;
    xx[1] = p2.x;
    xx[2] = v1.x;
    xx[3] = v2.x;

    yy[0] = p1.y;
    yy[1] = p2.y;
    yy[2] = v1.y;
    yy[3] = v2.y;

    zz[0] = p1.z;
    zz[1] = p2.z;
    zz[2] = v1.z;
    zz[3] = v2.z;

    for (i = 0; i < 4; i++) {
        cx[i] = xx[0] * M[i][0] + xx[1] * M[i][1] +
                xx[2] * M[i][2] + xx[3] * M[i][3];
        cy[i] = yy[0] * M[i][0] + yy[1] * M[i][1] +
                yy[2] * M[i][2] + yy[3] * M[i][3];
        cz[i] = zz[0] * M[i][0] + zz[1] * M[i][1] +
                zz[2] * M[i][2] + zz[3] * M[i][3];
    }

    for (i = 0; i < iPrec; i++) {
        float x, y;

        t += d;
        t2 = t * t;
        t3 = t2 * t;

        x = cx[0] * t3 + cx[1] * t2 + cx[2] * t + cx[3];
        y = cy[0] * t3 + cy[1] * t2 + cy[2] * t + cy[3];

        sinVal = (float) (sin(angle) / 5.0);
        if (sinVal < 0.0)
            sinVal = -sinVal;
        angle += angleStep;

        GRID(0, i).x = x;
        GRID(0, i).z = y;
        GRID(0, i).y = 0.25f;    // extrusion // + sinVal;
        GRID(1, i).x = x;
        GRID(1, i).z = y;
        GRID(1, i).y = -0.25f;   // - sinVal;
    }

    stripMesh.numFaces = 0;

    for (i = 0; i < 2 * iPrec; i++)
        mesh->norms[i] = ss_ptZero;

    for (facecount = 0, i = 0; i < (iPrec - 1); i++) {

        ss_calcNorm(&norm, &GRID(0, i + 1), &GRID(0, i), &GRID(1, i));
        stripMesh.faces[facecount].material = 0;
        stripMesh.faces[facecount].norm = norm;

        NORMS(0, i).x += norm.x;
        NORMS(0, i).y += norm.y;
        NORMS(0, i).z += norm.z;
        NORMS(1, i).x += norm.x;
        NORMS(1, i).y += norm.y;
        NORMS(1, i).z += norm.z;

        if (i != (iPrec - 1)) {
            NORMS(0, i+1).x += norm.x;
            NORMS(0, i+1).y += norm.y;
            NORMS(0, i+1).z += norm.z;
            NORMS(1, i+1).x += norm.x;
            NORMS(1, i+1).y += norm.y;
            NORMS(1, i+1).z += norm.z;
        }

        stripMesh.faces[facecount].p[0] = i;
        stripMesh.faces[facecount].p[1] = iPrec + i;
        stripMesh.faces[facecount].p[2] = i + 1;
        stripMesh.faces[facecount].p[3] = iPrec + i + 1;
        stripMesh.numFaces++;
        facecount++;
    }

    stripMesh.numPoints = 2 * iPrec;

    ss_normalizeNorms(stripMesh.norms, stripMesh.numPoints);
}

void initStripScene()
{
    iPrec = (int)(fTesselFact * 40.5);
    if (iPrec < 4)
        iPrec = 4;

    newMesh(&stripMesh, iPrec, 2 * iPrec);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.1, 1.1, -1.1, 1.1, 0.0, 3.0);
    glTranslatef(0.0f, 0.0f, -1.5f);
    glRotatef(50.0f, 1.0f, 0.0f, 0.0f);
    glRotatef(50.0f, 0.0f, 1.0f, 0.0f);
    glRotatef(12.0f, 0.0f, 0.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
}

void delStripScene()
{
    delMesh(&stripMesh);
}

void updateStripScene(int flags)
{
    static double mxrot = 0.0;
    static double myrot = 0.0;
    static double mzrot = 0.0;
    static double mxrotInc = 0.0;
    static double myrotInc = 0.1;
    static double mzrotInc = 0.0;
    static int h = 0;
    RGBA color;

    if( gbBounce ) {
        // floating window bounced off an edge
        if (mxrotInc) {
            mxrotInc = 0.0;
            myrotInc = 0.1;
        } else if (myrotInc) {
            myrotInc = 0.0;
            mzrotInc = 0.1;
        } else if (mzrotInc) {
            mzrotInc = 0.0;
            mxrotInc = 0.1;
        }
        gbBounce = FALSE;
    }

    glLoadIdentity();
    glRotatef((GLfloat) (mxrot * (180.0 / PI)), 1.0f, 0.0f, 0.0f);
    glRotatef((GLfloat) (myrot * (180.0 / PI)), 0.0f, 1.0f, 0.0f);
    glRotatef((GLfloat) (mzrot * (180.0 / PI)), 0.0f, 0.0f, 1.0f);

    genStrip();

    if (bColorCycle) {
        ss_HsvToRgb((float)h, 1.0f, 1.0f, &color );

        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
                     (GLfloat *) &color);

        h++;
        h %= 360;
    } else {
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
                     (GLfloat *) &Material[1].kd);
    }

    updateObject(&stripMesh, bSmoothShading);

    if (flags & 0x4) {
        glLoadIdentity();
        glTranslatef(0.05f, 0.0f, 0.0f);
        glRotatef((GLfloat) (myrot * (180.0 / PI)), 1.0f, 0.0f, 0.0f);
        glRotatef((GLfloat) (mxrot * (180.0 / PI)), 0.0f, 1.0f, 0.0f);
        glRotatef((GLfloat) (mzrot * (180.0 / PI)), 0.0f, 0.0f, 1.0f);

        if (bColorCycle) {
            color.r = 1.0f - color.r;
            color.g = 1.0f - color.g;
            color.b = 1.0f - color.b;

            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
                         (GLfloat *) &color);
        } else {
            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
                         (GLfloat *) &Material[2].kd);
        }

        updateObject(&stripMesh, bSmoothShading);
    }

    mxrot += mxrotInc;
    myrot += myrotInc;
    mzrot += mzrotInc;
}


GENTEX.C

/******************************Module*Header*******************************\
* Module Name: gentex.c
*
* The Textured Flag style of the 3D Flying Objects screen saver.
*
* Texture maps .BMP files onto a simulation of a flag waving in the breeze.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <stdlib.h>
#include <windows.h>
#include <GL\gl.h>
#include <GL\glu.h>
#include <string.h>
#include <math.h>
#include "ss3dfo.h"

static float winTotalwidth = (float)0.75;
static float winTotalheight = (float)0.75 * (float)0.75;

#define MAX_FRAMES 20

// IPREC is the number of faces in the mesh that models the flag.

#define IPREC   15

static int Frames = 10;
static MESH winMesh[MAX_FRAMES];
static float sinAngle = (float)0.0;
static float xTrans = (float)0.0;
static int curMatl = 0;

// Material properties.

static RGBA matlBrightSpecular = {1.0f, 1.0f, 1.0f, 1.0f};
static RGBA matlDimSpecular    = {0.5f, 0.5f, 0.5f, 1.0f};
static RGBA matlNoSpecular     = {0.0f, 0.0f, 0.0f, 0.0f};

// Lighting properties.

static GLfloat light0Pos[] = {20.0f, 5.0f, 20.0f, 0.0f};
static GLfloat light1Pos[] = {-20.0f, 5.0f, 0.0f, 0.0f};
static RGBA light1Ambient  = {0.0f, 0.0f, 0.0f, 0.0f};
static RGBA light1Diffuse  = {0.4f, 0.4f, 0.4f, 1.0f};
static RGBA light1Specular = {0.0f, 0.0f, 0.0f, 0.0f};

static RGBA flagColors[] = {{1.0f, 1.0f, 1.0f, 1.0f},
                            {0.94f, 0.37f, 0.13f, 1.0f},    // red
                           };

// Default texture resource

TEX_RES gTexRes = { TEX_BMP, IDB_DEFTEX };

/******************************Public*Routine******************************\
* iPtInList
*
* Add a vertex and its normal to the mesh.  If the vertex already exists,
* add in the normal to the existing normal (we to accumulate the average
* normal at each vertex).  Normalization of the normals is the
* responsibility of the caller.
*
\**************************************************************************/

static int iPtInList(MESH *mesh, int start,
                     POINT3D *p, POINT3D *norm, BOOL blend)
{
    int i;
    POINT3D *pts = mesh->pts + start;

    if (blend) {
        for (i = start; i < mesh->numPoints; i++, pts++) {
            if ((pts->x == p->x) && (pts->y == p->y) && (pts->z == p->z)) {
                mesh->norms[i].x += norm->x;
                mesh->norms[i].y += norm->y;
                mesh->norms[i].z += norm->z;
                return i;
            }
        }
    } else {
        i = mesh->numPoints;
    }

    mesh->pts[i] = *p;
    mesh->norms[i] = *norm;
    mesh->numPoints++;
    return i;
}

/******************************Public*Routine******************************\
* getZpos
*
* Get the z-position (depth) of the "wavy" flag component at the given x.
*
* The function used to model the wave is:
*
*        1/2
*   z = x    * sin((2*PI*x + sinAngle) / 4)
*
* The shape of the wave varies from frame to frame by changing the
* phase, sinAngle.
*
\**************************************************************************/

float getZpos(float x)
{
    float xAbs = x - xTrans;
    float angle = sinAngle + ((float) (2.0 * PI) * (xAbs / winTotalwidth));

    xAbs = winTotalwidth - xAbs;
//    xAbs += (winTotalwidth / 2.0);

    return (float)((sin((double)angle) / 4.0) *
                   sqrt((double)(xAbs / winTotalwidth )));
}

/******************************Public*Routine******************************\
* genTex
*
* Generate a mesh representing a frame of the flag.  The phase, sinAngle,
* is a global variable.
*
\**************************************************************************/

void genTex(MESH *winMesh)
{
    POINT3D pos;
    POINT3D pts[4];
    float w, h;
    int i;

    newMesh(winMesh, IPREC * IPREC, IPREC * IPREC);

// Width and height of each face

    w = (winTotalwidth) / (float)(IPREC + 1);
    h = winTotalheight;

// Generate the mesh data.  At equally spaced intervals along the x-axis,
// we compute the z-position of the flag surface.

    pos.y = (float) 0.0;
    pos.z = (float) 0.0;

    for (i = 0, pos.x = xTrans; i < IPREC; i++, pos.x += w) {
        int faceCount = winMesh->numFaces;

        pts[0].x = (float)pos.x;
        pts[0].y = (float)(pos.y);
        pts[0].z = getZpos(pos.x);

        pts[1].x = (float)pos.x;
        pts[1].y = (float)(pos.y + h);
        pts[1].z = getZpos(pos.x);

        pts[2].x = (float)(pos.x + w);
        pts[2].y = (float)(pos.y);
        pts[2].z = getZpos(pos.x + w);

        pts[3].x = (float)(pos.x + w);
        pts[3].y = (float)(pos.y + h);
        pts[3].z = getZpos(pos.x + w);

    // Compute the face normal.

        ss_calcNorm(&winMesh->faces[faceCount].norm, pts + 2, pts + 1, pts);

    // Add the face to the mesh.

        winMesh->faces[faceCount].material = 0;
        winMesh->faces[faceCount].p[0] = iPtInList(winMesh, 0, pts,
            &winMesh->faces[faceCount].norm, TRUE);
        winMesh->faces[faceCount].p[1] = iPtInList(winMesh, 0, pts + 1,
            &winMesh->faces[faceCount].norm, TRUE);
        winMesh->faces[faceCount].p[2] = iPtInList(winMesh, 0, pts + 2,
            &winMesh->faces[faceCount].norm, TRUE);
        winMesh->faces[faceCount].p[3] = iPtInList(winMesh, 0, pts + 3,
            &winMesh->faces[faceCount].norm, TRUE);

        winMesh->numFaces++;
    }

// Normalize the vertex normals in the mesh.

    ss_normalizeNorms(winMesh->norms, winMesh->numPoints);
}

/******************************Public*Routine******************************\
* initTexScene
*
* Initialize the screen saver.
*
* This function is exported to the main module in ss3dfo.c.
*
\**************************************************************************/

void initTexScene()
{
    int i;
    float angleDelta;
    TEXTURE tex;
    float aspectRatio;

    // Initialize the transform.

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-0.25, 1.0, -0.25, 1.0, 0.0, 3.0);
    glTranslatef(0.0f, 0.0f, -1.5f);

    // Initialize and turn on lighting.

    glLightfv(GL_LIGHT0, GL_POSITION, light0Pos);

    glLightfv(GL_LIGHT1, GL_AMBIENT, (GLfloat *) &light1Ambient);
    glLightfv(GL_LIGHT1, GL_DIFFUSE, (GLfloat *) &light1Diffuse);
    glLightfv(GL_LIGHT1, GL_SPECULAR, (GLfloat *) &light1Specular);
    glLightfv(GL_LIGHT1, GL_POSITION, light1Pos);
    glEnable(GL_LIGHT1);
    glDisable(GL_DEPTH_TEST);

    // Leave OpenGL in a state ready to accept the model view transform (we
    // are going to have the flag vary its orientation from frame to frame).

    glMatrixMode(GL_MODELVIEW);

    // Define orientation of polygon faces.

    glFrontFace(GL_CW);
    //    glEnable(GL_CULL_FACE);

    Frames = (int)((float)(MAX_FRAMES / 2) * fTesselFact);

    // Load user texture - if that fails load default texture resource

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    if( ss_LoadTextureFile( &gTexFile, &tex ) ||
        ss_LoadTextureResource( &gTexRes, &tex) )
    {
        glEnable(GL_TEXTURE_2D);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

        ss_SetTexture( &tex );

    // Correct aspect ratio of flag to match image.
    //
    // The 1.4 is a correction factor to account for the length of the
    // curve that models the surface ripple of the waving flag.  This
    // factor is the length of the curve at zero phase.  It would be
    // more accurate to determine the length of the curve at each phase,
    // but this is a sufficient approximation for our purposes.

        aspectRatio = ((float) tex.height / (float) tex.width)
                      * (float) 1.4;

        if (aspectRatio < (float) 1.0) {
            winTotalwidth  = (float)0.75;
            winTotalheight = winTotalwidth * aspectRatio;
        } else {
            winTotalheight = (float) 0.75;
            winTotalwidth  = winTotalheight / aspectRatio;
        };
    }

    if (Frames < 5)
        Frames = 5;
    if (Frames > MAX_FRAMES)
        Frames = MAX_FRAMES;

    // Generate the geometry data (stored in the array of mesh structures),
    // for each frame of the animation.  The shape of the flag is varied by
    // changing the global variable sinAngle.

    angleDelta = (float)(2.0 * PI) / (float)Frames;
    sinAngle = (float) 0.0;

    for (i = 0; i < Frames; i++) {
        genTex(&winMesh[i]);
        sinAngle += angleDelta;
    }
}

/******************************Public*Routine******************************\
* delTexScene
*
* Cleanup the data associated with this screen saver.
*
* This function is exported to the main module in ss3dfo.c.
*
\**************************************************************************/

void delTexScene()
{
    int i;

    for (i = 0; i < Frames; i++)
        delMesh(&winMesh[i]);
}

/******************************Public*Routine******************************\
* updateTexScene
*
* Generate a scene by taking one of the meshes and rendering it with
* OpenGL.
*
* This function is exported to the main module in ss3dfo.c.
*
\**************************************************************************/

void updateTexScene(int flags)
{
    MESH *mesh;
    MFACE *faces;
    int i;
    static double mxrot = 23.0;
    static double myrot = 23.0;
    static double mzrot = 5.7;
    static double mxrotInc = 0.0;
    static double myrotInc = 3.0;
    static double mzrotInc = 0.0;
    static int h = 0;
    static int frameNum = 0;
    POINT3D *pp;
    POINT3D *pn;
    int lastC, lastD;
    int aOffs, bOffs, cOffs, dOffs;
    int a, b;
    GLfloat s = (GLfloat) 0.0;
    GLfloat ds;

// In addition to having the flag wave (an effect acheived by switching
// meshes from frame to frame), the flag changes its orientation from
// frame to frame.  This is done by applying a model view transform.

    glLoadIdentity();
    glRotatef((float)mxrot, 1.0f, 0.0f, 0.0f);
    glRotatef((float)myrot, 0.0f, 1.0f, 0.0f);
    glRotatef((float)mzrot, 0.0f, 0.0f, 1.0f);

// Divide the texture into IPREC slices.  ds is the texture coordinate
// delta we apply as we move along the x-axis.

    ds = (GLfloat)1.0 / (GLfloat)IPREC;

// Setup the material property of the flag.  The material property, light
// properties, and polygon orientation will interact with the texture.

    curMatl = 0;
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, (GLfloat *) &flagColors[0]);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat *) &matlBrightSpecular);
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, (float) 60.0);

// Pick the mesh for the current frame.

    mesh = &winMesh[frameNum];

// Take the geometry data is the mesh and convert it to a single OpenGL
// quad strip.  If smooth shading is required, use the vertex normals stored
// in the mesh.  Otherwise, use the face normals.
//
// As we define each vertex, we also define a corresponding vertex and
// texture coordinate.

    glBegin(GL_QUAD_STRIP);

    pp = mesh->pts;
    pn = mesh->norms;

    for (i = 0, faces = mesh->faces, lastC = faces->p[0], lastD = faces->p[1];
         i < mesh->numFaces; i++, faces++) {

        a = faces->p[0];
        b = faces->p[1];

        if (!bSmoothShading) {
            // Since flag is a single quad strip, this isn't needed.
            // But lets keep it in case we ever change to a more
            // complex model (ie., one that uses more than one quad
            // strip).
            #if 0
            if ((a != lastC) || (b != lastD)) {
                glNormal3fv((GLfloat *)&(faces - 1)->norm);

                glTexCoord2f(s, (float) 0.0);
                glVertex3fv((GLfloat *)((char *)pp +
                            (lastC << 3) + (lastC << 2)));
                glTexCoord2f(s, (float) 1.0);
                glVertex3fv((GLfloat *)((char *)pp +
                            (lastD << 3) + (lastD << 2)));
                s += ds;
                glEnd();
                glBegin(GL_QUAD_STRIP);
            }
            #endif

            if (faces->material != curMatl) {
                curMatl = faces->material;
                glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,
                             (GLfloat *) &matlNoSpecular);
                glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
                             (GLfloat *) &flagColors[curMatl]);
            }

            glNormal3fv((GLfloat *)&faces->norm);
            glTexCoord2f(s, (float) 0.0);
            glVertex3fv((GLfloat *)((char *)pp + (a << 3) + (a << 2)));
            glTexCoord2f(s, (float) 1.0);
            glVertex3fv((GLfloat *)((char *)pp + (b << 3) + (b << 2)));
            s += ds;
        } else {
            // Since flag is a single quad strip, this isn't needed.
            // But lets keep it in case we ever change to a more
            // complex model (ie., one that uses more than one quad
            // strip).
            #if 0
            if ((a != lastC) || (b != lastD)) {
                cOffs = (lastC << 3) + (lastC << 2);
                dOffs = (lastD << 3) + (lastD << 2);

                glTexCoord2f(s, (float) 0.0);
                glNormal3fv((GLfloat *)((char *)pn + cOffs));
                glVertex3fv((GLfloat *)((char *)pp + cOffs));
                glTexCoord2f(s, (float) 1.0);
                glNormal3fv((GLfloat *)((char *)pn + dOffs));
                glVertex3fv((GLfloat *)((char *)pp + dOffs));
                s += ds;
                glEnd();
                glBegin(GL_QUAD_STRIP);
            }
            #endif

            aOffs = (a << 3) + (a << 2);
            bOffs = (b << 3) + (b << 2);

            if (faces->material != curMatl) {
                curMatl = faces->material;
                glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,
                             (GLfloat *) &matlNoSpecular);
                glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
                             (GLfloat *) &flagColors[curMatl]);
            }

            glTexCoord2f(s, (float) 0.0);
            glNormal3fv((GLfloat *)((char *)pn + aOffs));
            glVertex3fv((GLfloat *)((char *)pp + aOffs));
            glTexCoord2f(s, (float) 1.0);
            glNormal3fv((GLfloat *)((char *)pn + bOffs));
            glVertex3fv((GLfloat *)((char *)pp + bOffs));
            s += ds;
        }

        lastC = faces->p[2];
        lastD = faces->p[3];
    }

    if (!bSmoothShading) {
        glNormal3fv((GLfloat *)&(faces - 1)->norm);
        glTexCoord2f(s, (float) 0.0);
        glVertex3fv((GLfloat *)((char *)pp + (lastC << 3) + (lastC << 2)));
        glTexCoord2f(s, (float) 1.0);
        glVertex3fv((GLfloat *)((char *)pp + (lastD << 3) + (lastD << 2)));
    } else {
        cOffs = (lastC << 3) + (lastC << 2);
        dOffs = (lastD << 3) + (lastD << 2);

        glTexCoord2f(s, (float) 0.0);
        glNormal3fv((GLfloat *)((char *)pn + cOffs));
        glVertex3fv((GLfloat *)((char *)pp + cOffs));
        glTexCoord2f(s, (float) 1.0);
        glNormal3fv((GLfloat *)((char *)pn + dOffs));
        glVertex3fv((GLfloat *)((char *)pp + dOffs));
    }

    glEnd();

// Transfer the image to the floating OpenGL window.

// Determine the flag orientation for the next frame.
// What we are doing is an oscillating rotation about the y-axis
// (mxrotInc and mzrotInc are currently 0).

    mxrot += mxrotInc;
    myrot += myrotInc;
    mzrot += mzrotInc;

    if ((myrot < -65.0) || (myrot > 25.0))
        myrotInc = -myrotInc;

    frameNum++;
    if (frameNum >= Frames)
        frameNum = 0;
}


GENWIN.C

/******************************Module*Header*******************************\
* Module Name: genwin.c
*
* The Windows Logo style of the 3D Flying Objects screen saver.
*
* Animated 3D model of the Microsoft (R) Windows NT (TM) flag logo.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <stdlib.h>
#include <windows.h>
#include <GL\gl.h>
#include <string.h>
#include <math.h>
#include "ss3dfo.h"

#define WIN_TOP_BORDER      (float)0.1
#define WIN_RIGHT_BORDER    WIN_TOP_BORDER
#define WIN_CROSSBAR        (0.6522f * WIN_TOP_BORDER)
#define WIN_NUMPIECES       7
#define WIN_NUMCOLUMNS      6
#define WIN_GAP             (WIN_TOP_BORDER / 8.0f)
#define WIN_GAP_X           (2.0f * WIN_GAP)
#define WIN_HEIGHT          ((WIN_GAP * 6.0f) + \
                                (WIN_NUMPIECES * WIN_TOP_BORDER))
#define WIN_WIDTH           (0.7024f * WIN_HEIGHT)
#define WIN_THICKNESS       WIN_CROSSBAR

#define WIN_TOTALWIDTH      (WIN_TOP_BORDER * 1.1f * (float)WIN_NUMCOLUMNS + \
                             WIN_WIDTH)

#define BLOCK_TOP            0x0001
#define BLOCK_BOTTOM         0x0002
#define BLOCK_LEFT           0x0004
#define BLOCK_RIGHT          0x0008
#define BLOCK_FRONT          0x0010
#define BLOCK_BACK           0x0020
#define BLOCK_ALL            0x003f
#define DELTA_BLEND          0x2000
#define NO_BLEND             0x1000

#define CUBE_FACES          6
#define CUBE_POINTS         8

#define MAX_FRAMES 20

#define MAXPREC 15
#define S_IPREC 3

static int Frames = 10;
static MESH winMesh[MAX_FRAMES];
static MESH winStreamer[MAX_FRAMES];
static float sinAngle = 0.0f;
static float xTrans = 0.2f;
static int curMatl = 0;
static int iPrec = 10;

static RGBA matlBrightSpecular = {1.0f, 1.0f, 1.0f, 1.0f};
static RGBA matlDimSpecular = {0.5f, 0.5f, 0.5f, 1.0f};
static RGBA matlNoSpecular = {0.0f, 0.0f, 0.0f, 0.0f};
static GLfloat light0Pos[] = {20.0f, -10.0f, 20.0f, 0.0f};

static RGBA light1Ambient  = {0.0f, 0.0f, 0.0f, 0.0f};
static RGBA light1Diffuse  = {0.4f, 0.4f, 0.4f, 1.0f};
static RGBA light1Specular = {0.0f, 0.0f, 0.0f, 0.0f};
static GLfloat light1Pos[] = {-20.0f, 5.0f, 0.0f, 0.0f};

static RGBA winColors[] = {{0.3f, 0.3f, 0.3f, 1.0f},
                           {0.94f, 0.37f, 0.13f, 1.0f},    // red
                           {0.22f, 0.42f, 0.78f, 1.0f},    // blue
                           {0.35f, 0.71f, 0.35f, 1.0f},    // green
                           {0.95f, 0.82f, 0.12f, 1.0f}};   // yellow

static int iPtInList(MESH *mesh, int start,
                     POINT3D *p, POINT3D *norm, BOOL blend)
{
    int i;
    POINT3D *pts = mesh->pts + start;

    if (blend) {
        for (i = start; i < mesh->numPoints; i++, pts++) {
            if ((pts->x == p->x) && (pts->y == p->y) && (pts->z == p->z)) {
                mesh->norms[i].x += norm->x;
                mesh->norms[i].y += norm->y;
                mesh->norms[i].z += norm->z;
                return i;
            }
        }
    } else {
        i = mesh->numPoints;
    }

    mesh->pts[i] = *p;
    mesh->norms[i] = *norm;
    mesh->numPoints++;
    return i;
}

float getZPos(float x)
{
    float xAbs = x - xTrans;
    float angle = (float) (sinAngle + ((2.0 * PI) * (xAbs / WIN_TOTALWIDTH)));

    xAbs += (WIN_TOTALWIDTH / 2.0f);
    xAbs = WIN_TOTALWIDTH - xAbs;

    return (float)((sin((double)angle) / 4.0) *
                   sqrt((double)(xAbs / WIN_TOTALWIDTH )));
}

void AddFace(MESH *mesh, int startBlend, POINT3D *pos, float w, float h)
{
#define FACE_VERTEX(i) \
    iPtInList(mesh, startBlend, pts + i, &mesh->faces[faceCount].norm, TRUE)

    int faceCount = mesh->numFaces;
    int numPts = mesh->numPoints;
    POINT3D *pts = mesh->pts + numPts;
    float zLeft = getZPos(pos->x);
    float zRight = getZPos(pos->x + w);

    pts->x = (float)pos->x;
    pts->y = (float)pos->y;
    pts->z = zLeft;
    pts++;

    pts->x = (float)pos->x;
    pts->y = (float)(pos->y + h);
    pts->z = zLeft;
    pts++;

    pts->x = (float)(pos->x + w);
    pts->y = (float)(pos->y + h);
    pts->z = zRight;
    pts++;

    pts->x = (float)(pos->x + w);
    pts->y = (float)pos->y;
    pts->z = zRight;

    pts -= 3;

    mesh->faces[faceCount].material = curMatl;
    ss_calcNorm(&mesh->faces[faceCount].norm, pts + 2, pts + 1, pts);
    mesh->faces[faceCount].p[3] = FACE_VERTEX(0);
    mesh->faces[faceCount].p[2] = FACE_VERTEX(1);
    mesh->faces[faceCount].p[1] = FACE_VERTEX(2);
    mesh->faces[faceCount].p[0] = FACE_VERTEX(3);
    mesh->numFaces++;
}

#define BLOCK_VERTEX(face, i)\
{\
    if (flags & DELTA_BLEND) {\
        mesh->faces[faceCount].p[face] = \
            iPtInList(mesh, blendStart, &pts[i], &norms[((i & 0x2) >> 1)],\
                      bBlend);\
    } else\
        mesh->faces[faceCount].p[face] = \
            iPtInList(mesh, blendStart, &pts[i],\
                      &mesh->faces[faceCount].norm, bBlend);\
}

#define DELTA_FACT  (float)10.0

void AddBlock(MESH *mesh, int blendStart, POINT3D *pos,
              float w, float h, float d, ULONG flags)
{

    POINT3D pts[8];
    POINT3D ptsL[8];
    POINT3D ptsR[8];
    POINT3D norms[2];
    POINT3D posPrev;
    float zLeft = getZPos(pos->x);
    float zRight = getZPos(pos->x + w);
    int faceCount = mesh->numFaces;
    BOOL bBlend = ((flags & NO_BLEND) == 0);

    flags |= DELTA_BLEND;

    pts[0].x = (float)pos->x;
    pts[0].y = (float)(pos->y + h);
    pts[0].z = zLeft;

    pts[1].x = (float)pos->x;
    pts[1].y = (float)(pos->y + h);
    pts[1].z = zLeft + d;

    pts[2].x = (float)(pos->x + w);
    pts[2].y = (float)(pos->y + h);
    pts[2].z = zRight + d;

    pts[3].x = (float)(pos->x + w);
    pts[3].y = (float)(pos->y + h);
    pts[3].z = zRight;

    pts[4].x = (float)pos->x;
    pts[4].y = (float)pos->y;
    pts[4].z = zLeft;

    pts[5].x = (float)pos->x;
    pts[5].y = (float)pos->y;
    pts[5].z = zLeft + d;

    pts[6].x = (float)(pos->x + w);
    pts[6].y = (float)pos->y;
    pts[6].z = zRight + d;

    pts[7].x = (float)(pos->x + w);
    pts[7].y = (float)pos->y;
    pts[7].z = zRight;

    if (flags & DELTA_BLEND) {
float prevW = w;
        posPrev = *pos;

        w /= DELTA_FACT;
        zRight = getZPos(pos->x + w);

        ptsL[0].x = (float)pos->x;
        ptsL[0].y = (float)(pos->y + h);
        ptsL[0].z = zLeft;

        ptsL[1].x = (float)pos->x;
        ptsL[1].y = (float)(pos->y + h);
        ptsL[1].z = zLeft + d;

        ptsL[2].x = (float)(pos->x + w);
        ptsL[2].y = (float)(pos->y + h);
        ptsL[2].z = zRight + d;

        ptsL[3].x = (float)(pos->x + w);
        ptsL[3].y = (float)(pos->y + h);
        ptsL[3].z = zRight;

        ptsL[4].x = (float)pos->x;
        ptsL[4].y = (float)pos->y;
        ptsL[4].z = zLeft;

        ptsL[5].x = (float)pos->x;
        ptsL[5].y = (float)pos->y;
        ptsL[5].z = zLeft + d;

        ptsL[6].x = (float)(pos->x + w);
        ptsL[6].y = (float)pos->y;
        ptsL[6].z = zRight + d;

        ptsL[7].x = (float)(pos->x + w);
        ptsL[7].y = (float)pos->y;
        ptsL[7].z = zRight;

        pos->x += (prevW - w);
        zLeft = getZPos(pos->x);
        zRight = getZPos(pos->x + w);

        ptsR[0].x = (float)pos->x;
        ptsR[0].y = (float)(pos->y + h);
        ptsR[0].z = zLeft;

        ptsR[1].x = (float)pos->x;
        ptsR[1].y = (float)(pos->y + h);
        ptsR[1].z = zLeft + d;

        ptsR[2].x = (float)(pos->x + w);
        ptsR[2].y = (float)(pos->y + h);
        ptsR[2].z = zRight + d;

        ptsR[3].x = (float)(pos->x + w);
        ptsR[3].y = (float)(pos->y + h);
        ptsR[3].z = zRight;

        ptsR[4].x = (float)pos->x;
        ptsR[4].y = (float)pos->y;
        ptsR[4].z = zLeft;

        ptsR[5].x = (float)pos->x;
        ptsR[5].y = (float)pos->y;
        ptsR[5].z = zLeft + d;

        ptsR[6].x = (float)(pos->x + w);
        ptsR[6].y = (float)pos->y;
        ptsR[6].z = zRight + d;

        ptsR[7].x = (float)(pos->x + w);
        ptsR[7].y = (float)pos->y;
        ptsR[7].z = zRight;

        *pos = posPrev;

    }

    if (flags & BLOCK_TOP) {
        mesh->faces[faceCount].material = curMatl;
        ss_calcNorm(&mesh->faces[faceCount].norm, &pts[0], &pts[1], &pts[2]);
        if (flags & DELTA_BLEND) {
            ss_calcNorm(&norms[0], &ptsL[0], &ptsL[1], &ptsL[2]);
            ss_calcNorm(&norms[1], &ptsR[0], &ptsR[1], &ptsR[2]);
        }
        BLOCK_VERTEX(0, 0);
        BLOCK_VERTEX(1, 1);
        BLOCK_VERTEX(2, 2);
        BLOCK_VERTEX(3, 3);
        faceCount++;
        mesh->numFaces++;
    }

    if (flags & BLOCK_BOTTOM) {
        mesh->faces[faceCount].material = curMatl;
        ss_calcNorm(&mesh->faces[faceCount].norm, &pts[4], &pts[7], &pts[6]);
        if (flags & DELTA_BLEND) {
            ss_calcNorm(&norms[0], &ptsL[4], &ptsL[7], &ptsL[6]);
            ss_calcNorm(&norms[1], &ptsR[4], &ptsR[7], &ptsR[6]);
        }
        BLOCK_VERTEX(0, 4);
        BLOCK_VERTEX(1, 7);
        BLOCK_VERTEX(2, 6);
        BLOCK_VERTEX(3, 5);
        faceCount++;
        mesh->numFaces++;
    }

    if (flags & BLOCK_LEFT) {
        mesh->faces[faceCount].material = curMatl;
        ss_calcNorm(&mesh->faces[faceCount].norm, &pts[1], &pts[0], &pts[4]);
        if (flags & DELTA_BLEND) {
            ss_calcNorm(&norms[0], &ptsL[1], &ptsL[0], &ptsL[4]);
            ss_calcNorm(&norms[1], &ptsR[1], &ptsR[0], &ptsR[4]);
        }
        BLOCK_VERTEX(0, 1);
        BLOCK_VERTEX(1, 0);
        BLOCK_VERTEX(2, 4);
        BLOCK_VERTEX(3, 5);
        faceCount++;
        mesh->numFaces++;
    }

    if (flags & BLOCK_RIGHT) {
        mesh->faces[faceCount].material = curMatl;
        ss_calcNorm(&mesh->faces[faceCount].norm, &pts[3], &pts[2], &pts[6]);
        if (flags & DELTA_BLEND) {
            ss_calcNorm(&norms[0], &ptsL[3], &ptsL[2], &ptsL[6]);
            ss_calcNorm(&norms[1], &ptsR[3], &ptsR[2], &ptsR[6]);
        }
        BLOCK_VERTEX(0, 3);
        BLOCK_VERTEX(1, 2);
        BLOCK_VERTEX(2, 6);
        BLOCK_VERTEX(3, 7);
        faceCount++;
        mesh->numFaces++;
    }

    if (flags & BLOCK_FRONT) {
        mesh->faces[faceCount].material = curMatl;
        ss_calcNorm(&mesh->faces[faceCount].norm, &pts[0], &pts[3], &pts[7]);
        if (flags & DELTA_BLEND) {
            ss_calcNorm(&norms[0], &ptsL[0], &ptsL[3], &ptsL[7]);
            ss_calcNorm(&norms[1], &ptsR[0], &ptsR[3], &ptsR[7]);
        }
        BLOCK_VERTEX(0, 0);
        BLOCK_VERTEX(1, 3);
        BLOCK_VERTEX(2, 7);
        BLOCK_VERTEX(3, 4);
        faceCount++;
        mesh->numFaces++;
    }

    if (flags & BLOCK_BACK) {
        mesh->faces[faceCount].material = curMatl;
        ss_calcNorm(&mesh->faces[faceCount].norm, &pts[1], &pts[5], &pts[6]);
        if (flags & DELTA_BLEND) {
            ss_calcNorm(&norms[0], &ptsL[1], &ptsL[5], &ptsL[6]);
            ss_calcNorm(&norms[1], &ptsR[1], &ptsR[5], &ptsR[6]);
        }
        BLOCK_VERTEX(0, 1);
        BLOCK_VERTEX(1, 5);
        BLOCK_VERTEX(2, 6);
        BLOCK_VERTEX(3, 2);
        mesh->numFaces++;
    }

}

void genWin(MESH *winMesh, MESH *winStreamer)
{
    POINT3D pos, posCenter;
    float w, h, d;
    float wMax, hMax;
    float xpos;
    int i, j, prec;
    int startBlend;

    newMesh(winMesh, CUBE_FACES * iPrec * 20, CUBE_POINTS * iPrec * 20);

    //
    // create window frame
    //

    w = (WIN_WIDTH - WIN_TOP_BORDER) / (float)iPrec;
    h = (float)WIN_TOP_BORDER;
    d = (float)WIN_THICKNESS;

    // draw top and bottom portions

    pos.y = 0.0f;
    pos.z = 0.0f;

    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < iPrec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_TOP);
    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < iPrec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_BOTTOM);
    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < iPrec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_FRONT);
    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < iPrec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_BACK);
    pos.x = xTrans;
    AddBlock(winMesh, 0, &pos, w, h, d, BLOCK_LEFT | NO_BLEND);

    pos.y = WIN_HEIGHT - WIN_TOP_BORDER;
    pos.z = 0.0f;

    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < iPrec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_TOP);
    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < iPrec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_BOTTOM);
    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < iPrec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_FRONT);
    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < iPrec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_BACK);
    pos.x = xTrans;
    AddBlock(winMesh, 0, &pos, w, h, d, BLOCK_LEFT | NO_BLEND);

    // draw middle horizontal portions

    prec = (iPrec / 2);
    w = (WIN_WIDTH - WIN_TOP_BORDER - WIN_CROSSBAR) / 2.0f;
    w /= (float)prec;
    h = WIN_CROSSBAR;
    pos.y = (WIN_HEIGHT - WIN_CROSSBAR) / 2.0f;
    pos.z = 0.0f;

    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < prec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_TOP);
    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < prec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_BOTTOM);
    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < prec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_FRONT);
    for (i = 0, pos.x = xTrans, startBlend = winMesh->numPoints;
         i < prec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_BACK);

    xpos = pos.x + WIN_CROSSBAR;

    for (i = 0, pos.x = xpos, startBlend = winMesh->numPoints;
         i < prec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_TOP);
    for (i = 0, pos.x = xpos, startBlend = winMesh->numPoints;
         i < prec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_BOTTOM);
    for (i = 0, pos.x = xpos, startBlend = winMesh->numPoints;
         i < prec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_FRONT);
    for (i = 0, pos.x = xpos, startBlend = winMesh->numPoints;
         i < prec; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_BACK);

    pos.x = xTrans;
    AddBlock(winMesh, 0, &pos, w, h, d, BLOCK_LEFT | NO_BLEND);

    // Draw thick right-hand edge of frame

    pos.x = xpos = xTrans + WIN_WIDTH - WIN_RIGHT_BORDER;
    pos.y = 0.0f;
    pos.z = 0.0f;
    w = WIN_RIGHT_BORDER / (float)S_IPREC;
    h = WIN_HEIGHT;

    AddBlock(winMesh, winMesh->numPoints, &pos, w, h, d, BLOCK_LEFT);

    for (i = 0, pos.x = xpos, startBlend = winMesh->numPoints;
         i < S_IPREC; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_FRONT);
    for (i = 0, pos.x = xpos, startBlend = winMesh->numPoints;
         i < S_IPREC; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_BACK);
    for (i = 0, pos.x = xpos, startBlend = winMesh->numPoints;
         i < S_IPREC; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_TOP);
    pos.y = WIN_HEIGHT;
    for (i = 0, pos.x = xpos, startBlend = winMesh->numPoints;
         i < S_IPREC; i++, pos.x += w)
        AddBlock(winMesh, startBlend, &pos, w, h, d, BLOCK_BOTTOM);

    pos.y = 0.0f;
    pos.x = xTrans + WIN_WIDTH - w;
    AddBlock(winMesh, winMesh->numPoints, &pos, w, h, d, BLOCK_RIGHT);

    // draw middle-vertical portion of frame

    pos.x = xTrans + (WIN_WIDTH - WIN_RIGHT_BORDER) / 2.0f - (WIN_CROSSBAR / 2.0f);
    pos.y = WIN_TOP_BORDER;
    pos.z = 0.0f;
    w = WIN_CROSSBAR;
    h = WIN_HEIGHT - 2.0f * WIN_TOP_BORDER;
    AddBlock(winMesh, 0, &pos, w, h, d, BLOCK_ALL | NO_BLEND);

    //
    // add the panels
    //

    w = (WIN_WIDTH - WIN_RIGHT_BORDER - WIN_CROSSBAR) / 2.0f;
    h = (WIN_HEIGHT - 2.0f * WIN_TOP_BORDER - WIN_CROSSBAR) / 2.0f;

    w /= (float)(iPrec / 2);

    curMatl = 2;
    pos.x = xTrans;
    pos.y = WIN_TOP_BORDER;
    for (i = 0, startBlend = winMesh->numPoints; i < iPrec / 2; i++) {
        AddFace(winMesh, startBlend, &pos, w, h);
        pos.x += w;
    }
    curMatl = 4;
    pos.x += WIN_CROSSBAR;
    for (i = 0, startBlend = winMesh->numPoints; i < iPrec / 2; i++) {
        AddFace(winMesh, startBlend, &pos, w, h);
        pos.x += w;
    }

    curMatl = 1;
    pos.x = xTrans;
    pos.y = WIN_TOP_BORDER + h + WIN_CROSSBAR;
    for (i = 0, startBlend = winMesh->numPoints; i < iPrec / 2; i++) {
        AddFace(winMesh, startBlend, &pos, w, h);
        pos.x += w;
    }
    curMatl = 3;
    pos.x += WIN_CROSSBAR;
    for (i = 0, startBlend = winMesh->numPoints; i < iPrec / 2; i++) {
        AddFace(winMesh, startBlend, &pos, w, h);
        pos.x += w;
    }

    ss_normalizeNorms(winMesh->norms, winMesh->numPoints);

    newMesh(winStreamer, CUBE_FACES * WIN_NUMPIECES * WIN_NUMCOLUMNS,
            CUBE_POINTS * WIN_NUMPIECES * WIN_NUMCOLUMNS);

    h = hMax = WIN_TOP_BORDER;
    w = wMax = WIN_TOP_BORDER * 1.1f;

    posCenter.x = pos.x = xTrans - wMax - WIN_GAP_X;
    posCenter.y = pos.y = 0.0f;

    for (i = 0; i < WIN_NUMCOLUMNS; i++) {
        for (j = 0; j < WIN_NUMPIECES; j++) {
            if (((j % 3) == 0) || (i == 0))
                curMatl = 0;
            else if (j < 3)
                curMatl = 2;
            else
                curMatl = 1;
            AddBlock(winStreamer, 0, &pos, w, h, d, BLOCK_ALL);
            pos.y += (hMax + WIN_GAP);
        }

        posCenter.x -= (wMax + WIN_GAP_X);
        posCenter.y = 0.0f;

        h = h * 0.8f;
        w = w * 0.8f;

        pos.x = posCenter.x;
        pos.y = posCenter.y;

        pos.x += (wMax - w) / 2.0f;
        pos.y += (hMax - h) / 2.0f;
    }
    ss_normalizeNorms(winStreamer->norms, winStreamer->numPoints);
}

void initWinScene()
{
    int i;
    float angleDelta;

    iPrec = (int)(fTesselFact * 10.5);
    if (iPrec < 5)
        iPrec = 5;
    if (iPrec > MAXPREC)
        iPrec = MAXPREC;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.0, 1.0, -0.75, 1.25, 0.0, 3.0);
    glTranslatef(0.0f, 0.0f, -1.5f);
    glLightfv(GL_LIGHT0, GL_POSITION, light0Pos);

    glLightfv(GL_LIGHT1, GL_AMBIENT, (GLfloat *) &light1Ambient);
    glLightfv(GL_LIGHT1, GL_DIFFUSE, (GLfloat *) &light1Diffuse);
    glLightfv(GL_LIGHT1, GL_SPECULAR, (GLfloat *) &light1Specular);
    glLightfv(GL_LIGHT1, GL_POSITION, light1Pos);
    glEnable(GL_LIGHT1);

    glMatrixMode(GL_MODELVIEW);

    glFrontFace(GL_CCW);
    glEnable(GL_CULL_FACE);

    Frames = (int)((float)(MAX_FRAMES / 2) * fTesselFact);

    if (Frames < 5)
        Frames = 5;
    if (Frames > MAX_FRAMES)
        Frames = MAX_FRAMES;

    angleDelta = (float) ((2.0 * PI) / Frames);
    sinAngle = 0.0f;

    for (i = 0; i < Frames; i++) {
        genWin(&winMesh[i], &winStreamer[i]);
        sinAngle += angleDelta;
    }
}

void delWinScene()
{
    int i;

    for (i = 0; i < Frames; i++) {
        delMesh(&winMesh[i]);
        delMesh(&winStreamer[i]);
    }
}

void updateWinScene(int flags)
{
    MESH *mesh;
    MFACE *faces;
    int i;
    static double mxrot = 23.0;
    static double myrot = 23.0;
    static double mzrot = 5.7;
    static double mxrotInc = 0.0;
    static double myrotInc = 3.0;
    static double mzrotInc = 0.0;
    static int h = 0;
    static int frameNum = 0;
    POINT3D *pp;
    POINT3D *pn;
    int lastC, lastD;
    int aOffs, bOffs, cOffs, dOffs;
    int a, b;

    if (bColorCycle) {
        ss_HsvToRgb((float)h, 1.0f, 1.0f, &winColors[0] );

        h++;
        h %= 360;
    }

    glLoadIdentity();
    glRotatef((GLfloat) mxrot, 1.0f, 0.0f, 0.0f);
    glRotatef((GLfloat) myrot, 0.0f, 1.0f, 0.0f);
    glRotatef((GLfloat) mzrot, 0.0f, 0.0f, 1.0f);

    curMatl = 0;
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, (GLfloat *) &winColors[0]);
    glMaterialfv(GL_FRONT, GL_SPECULAR, (GLfloat *) &matlBrightSpecular);
    glMaterialf(GL_FRONT, GL_SHININESS, 60.0f);

    mesh = &winMesh[frameNum];

    glBegin(GL_QUAD_STRIP);

    pp = mesh->pts;
    pn = mesh->norms;

    for (i = 0, faces = mesh->faces, lastC = faces->p[0], lastD = faces->p[1];
         i < mesh->numFaces; i++, faces++) {

        a = faces->p[0];
        b = faces->p[1];

if (!bSmoothShading) {
            if ((a != lastC) || (b != lastD)) {
                glNormal3fv((GLfloat *)&(faces - 1)->norm);

                glVertex3fv((GLfloat *)((char *)pp +
                            (lastC << 3) + (lastC << 2)));
                glVertex3fv((GLfloat *)((char *)pp +
                            (lastD << 3) + (lastD << 2)));
                glEnd();
                glBegin(GL_QUAD_STRIP);
            }

            if (faces->material != curMatl) {
                curMatl = faces->material;
                glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,
                             (GLfloat *) &matlNoSpecular);
                glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
                             (GLfloat *) &winColors[curMatl]);
            }

            glNormal3fv((GLfloat *)&faces->norm);
            glVertex3fv((GLfloat *)((char *)pp + (a << 3) + (a << 2)));
            glVertex3fv((GLfloat *)((char *)pp + (b << 3) + (b << 2)));
        } else {
            if ((a != lastC) || (b != lastD)) {
                cOffs = (lastC << 3) + (lastC << 2);
                dOffs = (lastD << 3) + (lastD << 2);

                glNormal3fv((GLfloat *)((char *)pn + cOffs));
                glVertex3fv((GLfloat *)((char *)pp + cOffs));
                glNormal3fv((GLfloat *)((char *)pn + dOffs));
                glVertex3fv((GLfloat *)((char *)pp + dOffs));
                glEnd();
                glBegin(GL_QUAD_STRIP);
            }

            aOffs = (a << 3) + (a << 2);
            bOffs = (b << 3) + (b << 2);

            if (faces->material != curMatl) {
                curMatl = faces->material;
                glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,
                             (GLfloat *) &matlNoSpecular);
                glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
                             (GLfloat *) &winColors[curMatl]);
            }

            glNormal3fv((GLfloat *)((char *)pn + aOffs));
            glVertex3fv((GLfloat *)((char *)pp + aOffs));
            glNormal3fv((GLfloat *)((char *)pn + bOffs));
            glVertex3fv((GLfloat *)((char *)pp + bOffs));
        }

        lastC = faces->p[3];
        lastD = faces->p[2];
    }

    if (!bSmoothShading) {
        glNormal3fv((GLfloat *)&(faces - 1)->norm);
        glVertex3fv((GLfloat *)((char *)pp + (lastC << 3) + (lastC << 2)));
        glVertex3fv((GLfloat *)((char *)pp + (lastD << 3) + (lastD << 2)));
    } else {
        cOffs = (lastC << 3) + (lastC << 2);
        dOffs = (lastD << 3) + (lastD << 2);

        glNormal3fv((GLfloat *)((char *)pn + cOffs));
        glVertex3fv((GLfloat *)((char *)pp + cOffs));
        glNormal3fv((GLfloat *)((char *)pn + dOffs));
        glVertex3fv((GLfloat *)((char *)pp + dOffs));
    }

    glEnd();

    glMaterialfv(GL_FRONT, GL_SPECULAR, (GLfloat *) &matlDimSpecular);

    glBegin(GL_QUADS);

    mesh = &winStreamer[frameNum];

    for (i = 0, faces = mesh->faces;
         i < mesh->numFaces; i++, faces++) {
        int a, b, c, d;

        a = faces->p[0];
        b = faces->p[1];
        c = faces->p[2];
        d = faces->p[3];

        if (faces->material != curMatl) {
            curMatl = faces->material;
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
                         (GLfloat *)&winColors[curMatl]);
        }

        glNormal3fv((GLfloat *)&faces->norm);
        glVertex3fv((GLfloat *)(mesh->pts + a));
        glVertex3fv((GLfloat *)(mesh->pts + b));
        glVertex3fv((GLfloat *)(mesh->pts + c));
        glVertex3fv((GLfloat *)(mesh->pts + d));
    }

    glEnd();

    mxrot += mxrotInc;
    myrot += myrotInc;
    mzrot += mzrotInc;

    if ((myrot < -45.0) || (myrot > 45.0))
        myrotInc = -myrotInc;

    frameNum++;
    if (frameNum >= Frames)
        frameNum = 0;
}


MESH.C

/******************************Module*Header*******************************\
* Module Name: mesh.c
*
* Routines to create a mesh representation of a 3D object and to turn it
* into an OpenGL description.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/
#include <stdlib.h>
#include <windows.h>
#include <GL\gl.h>
#include <string.h>
#include <math.h>
#include "ss3dfo.h"
#include "mesh.h"

#define ZERO_EPS    0.00000001

/******************************Public*Routine******************************\
* newMesh
*
* Allocate memory for the mesh structure to accomodate the specified number
* of points and faces.
*
\**************************************************************************/

void newMesh(MESH *mesh, int numFaces, int numPts)
{
    mesh->numFaces = 0;
    mesh->numPoints = 0;

    if (numPts) {
        mesh->pts = SaverAlloc((LONG)numPts * (LONG)sizeof(POINT3D));
        mesh->norms = SaverAlloc((LONG)numPts * (LONG)sizeof(POINT3D));
    }
    mesh->faces = SaverAlloc((LONG)numFaces * (LONG)sizeof(MFACE));
}

/******************************Public*Routine******************************\
* delMesh
*
* Delete the allocated portions of the MESH structure.
*
\**************************************************************************/

void delMesh(MESH *mesh)
{
    SaverFree(mesh->pts);
    SaverFree(mesh->norms);
    SaverFree(mesh->faces);
}

/******************************Public*Routine******************************\
* iPtInList
*
* Add a vertex and its normal to the mesh.  If the vertex already exists,
* add in the normal to the existing normal (we to accumulate the average
* normal at each vertex).  Normalization of the normals is the
* responsibility of the caller.
*
\**************************************************************************/

static int iPtInList(MESH *mesh, POINT3D *p, POINT3D *norm, int start)
{
    int i;
    POINT3D *pts = mesh->pts + start;

    for (i = start; i < mesh->numPoints; i++, pts++)
    {
    // If the vertices are within ZERO_EPS of each other, then its the same
    // vertex.

        if ( fabs(pts->x - p->x) < ZERO_EPS &&
             fabs(pts->y - p->y) < ZERO_EPS &&
             fabs(pts->z - p->z) < ZERO_EPS )
        {
            mesh->norms[i].x += norm->x;
            mesh->norms[i].y += norm->y;
            mesh->norms[i].z += norm->z;
            return i;
        }
    }

    mesh->pts[i] = *p;
    mesh->norms[i] = *norm;
    mesh->numPoints++;
    return i;
}

/******************************Public*Routine******************************\
* revolveSurface
*
* Takes the set of points in curve and fills the mesh structure with a
* surface of revolution.  The surface consists of quads made up of the
* points in curve rotated about the y-axis.  The number of increments
* in the revolution is determined by the steps parameter.
*
\**************************************************************************/

#define MAXPREC 40

void revolveSurface(MESH *mesh, POINT3D *curve, int steps)
{
    int i;
    int j;
    int facecount = 0;
    double rotation = 0.0;
    double rotInc;
    double cosVal;
    double sinVal;
    int stepsSqr;
    POINT3D norm;
    POINT3D a[MAXPREC + 1];
    POINT3D b[MAXPREC + 1];

    if (steps > MAXPREC)
        steps = MAXPREC;
    rotInc = (2.0 * PI) / (double)(steps - 1);
    stepsSqr = steps * steps;
    newMesh(mesh, stepsSqr, 4 * stepsSqr);

    for (j = 0; j < steps; j++, rotation += rotInc) {
        cosVal = cos(rotation);
        sinVal = sin(rotation);
        for (i = 0; i < steps; i++) {
            a[i].x = (float) (curve[i].x * cosVal + curve[i].z * sinVal);
            a[i].y = (float) (curve[i].y);
            a[i].z = (float) (curve[i].z * cosVal - curve[i].x * sinVal);
        }

        cosVal = cos(rotation + rotInc);
        sinVal = sin(rotation + rotInc);
        for (i = 0; i < steps; i++) {
            b[i].x = (float) (curve[i].x * cosVal + curve[i].z * sinVal);
            b[i].y = (float) (curve[i].y);
            b[i].z = (float) (curve[i].z * cosVal - curve[i].x * sinVal);
        }

        for (i = 0; i < (steps - 1); i++) {
            ss_calcNorm(&norm, &b[i + 1], &b[i], &a[i]);
            if ((norm.x * norm.x) + (norm.y * norm.y) + (norm.z * norm.z) < 0.9)
                ss_calcNorm(&norm, &a[i], &a[i+1], &b[i + 1]);
            mesh->faces[facecount].material = j & 7;
            mesh->faces[facecount].norm = norm;
            mesh->faces[facecount].p[0] = iPtInList(mesh, &b[i], &norm, 0);
            mesh->faces[facecount].p[1] = iPtInList(mesh, &a[i], &norm, 0);
            mesh->faces[facecount].p[2] = iPtInList(mesh, &b[i + 1], &norm, 0);
            mesh->faces[facecount].p[3] = iPtInList(mesh, &a[i + 1], &norm, 0);
            mesh->numFaces++;
            facecount++;
        }
    }

    ss_normalizeNorms(mesh->norms, mesh->numPoints);
}

/******************************Public*Routine******************************\
* updateObject
*
* Takes the mesh structure and converts the data into OpenGL immediate
* mode commands.
*
\**************************************************************************/

void updateObject(MESH *mesh, BOOL bSmooth)
{
    int i;
    int a, b;
    int aOffs, bOffs, cOffs, dOffs;
    MFACE *faces;
    POINT3D *pp;
    POINT3D *pn;
    int lastC, lastD;

    pp = mesh->pts;
    pn = mesh->norms;

    glBegin(GL_QUAD_STRIP);
    for (i = 0, faces = mesh->faces, lastC = faces->p[0], lastD = faces->p[1];
         i < mesh->numFaces; i++, faces++) {

        a = faces->p[0];
        b = faces->p[1];

        if (!bSmooth) {
            if ((a != lastC) || (b != lastD)) {
                glNormal3fv((GLfloat *)&(faces - 1)->norm);

                glVertex3fv((GLfloat *)((char *)pp +
                            (lastC << 3) + (lastC << 2)));
                glVertex3fv((GLfloat *)((char *)pp +
                            (lastD << 3) + (lastD << 2)));
                glEnd();
                glBegin(GL_QUAD_STRIP);
            }

            glNormal3fv((GLfloat *)&faces->norm);
            glVertex3fv((GLfloat *)((char *)pp + (a << 3) + (a << 2)));
            glVertex3fv((GLfloat *)((char *)pp + (b << 3) + (b << 2)));
        } else {
            if ((a != lastC) || (b != lastD)) {
                cOffs = (lastC << 3) + (lastC << 2);
                dOffs = (lastD << 3) + (lastD << 2);

                glNormal3fv((GLfloat *)((char *)pn + cOffs));
                glVertex3fv((GLfloat *)((char *)pp + cOffs));
                glNormal3fv((GLfloat *)((char *)pn + dOffs));
                glVertex3fv((GLfloat *)((char *)pp + dOffs));
                glEnd();
                glBegin(GL_QUAD_STRIP);
            }

            aOffs = (a << 3) + (a << 2);
            bOffs = (b << 3) + (b << 2);

            glNormal3fv((GLfloat *)((char *)pn + aOffs));
            glVertex3fv((GLfloat *)((char *)pp + aOffs));
            glNormal3fv((GLfloat *)((char *)pn + bOffs));
            glVertex3fv((GLfloat *)((char *)pp + bOffs));
        }

        lastC = faces->p[2];
        lastD = faces->p[3];
    }

    if (!bSmooth) {
        glNormal3fv((GLfloat *)&(faces - 1)->norm);
        glVertex3fv((GLfloat *)((char *)pp + (lastC << 3) + (lastC << 2)));
        glVertex3fv((GLfloat *)((char *)pp + (lastD << 3) + (lastD << 2)));
    } else {
        cOffs = (lastC << 3) + (lastC << 2);
        dOffs = (lastD << 3) + (lastD << 2);

        glNormal3fv((GLfloat *)((char *)pn + cOffs));
        glVertex3fv((GLfloat *)((char *)pp + cOffs));
        glNormal3fv((GLfloat *)((char *)pn + dOffs));
        glVertex3fv((GLfloat *)((char *)pp + dOffs));
    }

    glEnd();
}

/******************************Public*Routine******************************\
* updateObject
*
* This is a special case that handles a mesh structure that represents
* a strip that is a 1 high loop.
*
* Takes the mesh structure and converts the data into OpenGL immediate
* mode commands.
*
\**************************************************************************/

void updateObject2(MESH *mesh, BOOL bSmooth)
{
    int i;
    int a, b;
    int aOffs, bOffs, cOffs, dOffs;
    MFACE *faces;
    POINT3D *pp;
    POINT3D *pn;
    int lastC, lastD;

    pp = mesh->pts;
    pn = mesh->norms;

    glBegin(GL_QUAD_STRIP);
    for (i = 0, faces = mesh->faces, lastC = faces->p[0], lastD = faces->p[1];
         i < mesh->numFaces; i++, faces++) {

        a = faces->p[0];
        b = faces->p[1];

        if (!bSmooth) {
            glNormal3fv((GLfloat *)&faces->norm);
            glVertex3fv((GLfloat *)((char *)pp + (a << 3) + (a << 2)));
            glVertex3fv((GLfloat *)((char *)pp + (b << 3) + (b << 2)));
        } else {
            aOffs = (a << 3) + (a << 2);
            bOffs = (b << 3) + (b << 2);

            glNormal3fv((GLfloat *)((char *)pn + aOffs));
            glVertex3fv((GLfloat *)((char *)pp + aOffs));
            glNormal3fv((GLfloat *)((char *)pn + bOffs));
            glVertex3fv((GLfloat *)((char *)pp + bOffs));
        }

        lastC = faces->p[2];
        lastD = faces->p[3];
    }

    if (!bSmooth) {
        glNormal3fv((GLfloat *)&(mesh->faces)->norm);
        glVertex3fv((GLfloat *)((char *)pp + (lastC << 3) + (lastC << 2)));
        glVertex3fv((GLfloat *)((char *)pp + (lastD << 3) + (lastD << 2)));
    } else {
        cOffs = (lastC << 3) + (lastC << 2);
        dOffs = (lastD << 3) + (lastD << 2);

        glNormal3fv((GLfloat *)((char *)pn + cOffs));
        glVertex3fv((GLfloat *)((char *)pp + cOffs));
        glNormal3fv((GLfloat *)((char *)pn + dOffs));
        glVertex3fv((GLfloat *)((char *)pp + dOffs));
    }

    glEnd();
}

/******************************Public*Routine******************************\
* MakeList
*
* Takes the mesh structure and converts the data into OpenGL display
* list.
*
\**************************************************************************/

void MakeList(GLuint listID, MESH *mesh)
{
    int i;
    int a, b;
    int aOffs, bOffs, cOffs, dOffs;
    MFACE *faces;
    BOOL bSmooth;
    POINT3D *pp;
    POINT3D *pn;
    GLint shadeModel;
    int lastC, lastD;

    glGetIntegerv(GL_SHADE_MODEL, &shadeModel);

    bSmooth = (shadeModel == GL_SMOOTH);

    glNewList(listID, GL_COMPILE);

    pp = mesh->pts;
    pn = mesh->norms;

    glBegin(GL_QUAD_STRIP);
    for (i = 0, faces = mesh->faces, lastC = faces->p[0], lastD = faces->p[1];
         i < mesh->numFaces; i++, faces++) {

        a = faces->p[0];
        b = faces->p[1];

        if (!bSmooth) {

            if ((a != lastC) || (b != lastD)) {
                glNormal3fv((GLfloat *)&((faces - 1)->norm));

                glVertex3fv((GLfloat *)((char *)pp +
                            (lastC << 3) + (lastC << 2)));
                glVertex3fv((GLfloat *)((char *)pp +
                            (lastD << 3) + (lastD << 2)));
                glEnd();
                glBegin(GL_QUAD_STRIP);
            }

            glNormal3fv((GLfloat *)&faces->norm);
            glVertex3fv((GLfloat *)((char *)pp + (a << 3) + (a << 2)));
            glVertex3fv((GLfloat *)((char *)pp + (b << 3) + (b << 2)));
        } else {
            if ((a != lastC) || (b != lastD)) {
                cOffs = (lastC << 3) + (lastC << 2);
                dOffs = (lastD << 3) + (lastD << 2);

                glNormal3fv((GLfloat *)((char *)pn + cOffs));
                glVertex3fv((GLfloat *)((char *)pp + cOffs));
                glNormal3fv((GLfloat *)((char *)pn + dOffs));
                glVertex3fv((GLfloat *)((char *)pp + dOffs));
                glEnd();
                glBegin(GL_QUAD_STRIP);
            }

            aOffs = (a << 3) + (a << 2);
            bOffs = (b << 3) + (b << 2);

            glNormal3fv((GLfloat *)((char *)pn + aOffs));
            glVertex3fv((GLfloat *)((char *)pp + aOffs));
            glNormal3fv((GLfloat *)((char *)pn + bOffs));
            glVertex3fv((GLfloat *)((char *)pp + bOffs));
        }

        lastC = faces->p[2];
        lastD = faces->p[3];
    }

    if (!bSmooth) {
        glNormal3fv((GLfloat *)&((faces - 1)->norm));
        glVertex3fv((GLfloat *)((char *)pp + (lastC << 3) + (lastC << 2)));
        glVertex3fv((GLfloat *)((char *)pp + (lastD << 3) + (lastD << 2)));
    } else {
        cOffs = (lastC << 3) + (lastC << 2);
        dOffs = (lastD << 3) + (lastD << 2);

        glNormal3fv((GLfloat *)((char *)pn + cOffs));
        glVertex3fv((GLfloat *)((char *)pp + cOffs));
        glNormal3fv((GLfloat *)((char *)pn + dOffs));
        glVertex3fv((GLfloat *)((char *)pp + dOffs));
    }

    glEnd();

    glEndList();
}


MESH.H

/******************************Module*Header*******************************\
* Module Name: mesh.h
*
* Declaration of the mesh routines.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

extern void newMesh(MESH *mesh, int numFaces, int numPts);
extern void delMesh(MESH *mesh);
extern void revolveSurface(MESH *mesh, POINT3D *curve, int steps);
extern void updateObject(MESH *mesh, BOOL bSmooth);
extern void updateObject2(MESH *mesh, BOOL bSmooth);
extern void MakeList(GLuint listID, MESH *mesh);


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by ss3dfo.rc
//
#define IDI_ICON1                       103
#define IDI_ICON2                       104
#define IDC_SLIDER1                     1013
#define IDC_STATIC                      -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        105
#define _APS_NEXT_COMMAND_VALUE         101
#define _APS_NEXT_CONTROL_VALUE         1014
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

#include "commctrl.h"
#include "ss3dfo.h"
#include "sscommon.h"
#include "sscommon.rc"


SS3DFO.C

/******************************Module*Header*******************************\
* Module Name: ss3dfo.c
*
* Dispatcher and dialog box for the OpenGL-based 3D Flying Objects screen
* saver.
*
* Created: 18-May-1994 14:54:59
*
* Copyright (c) 1994 Microsoft Corporation
\**************************************************************************/

#include <windows.h>
#include <scrnsave.h>
#include <GL\gl.h>
#include <math.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys\timeb.h>
#include <time.h>
#include <commdlg.h>
#include <commctrl.h>
#include "ss3dfo.h"

//#define SS_DEBUG 1

// Global strings.
#define GEN_STRING_SIZE 64

static SSContext gssc;
BOOL gbBounce = FALSE; // floating window bounce off side

// Global message loop variables.
MATERIAL Material[16];
#ifdef MEMDEBUG
ULONG totalMem = 0;
#endif

// Global screen saver settings.

void (*updateSceneFunc)(int); // current screen saver update function
void (*delSceneFunc)(void);         // current screen saver deletion function
BOOL bColorCycle = FALSE;           // color cycling flag
BOOL bSmoothShading = TRUE;         // smooth shading flag
UINT uSize = 100;                   // object size
float fTesselFact = 1.0f;           // object tessalation
int UpdateFlags = 0;                // extra data sent to update function
int Type = 0;                       // screen saver index (into function arrays)

// Texture file information
TEXFILE gTexFile = {0};

// Lighting properties.

static const RGBA lightAmbient   = {0.21f, 0.21f, 0.21f, 1.0f};
static const RGBA light0Ambient  = {0.0f, 0.0f, 0.0f, 1.0f};
static const RGBA light0Diffuse  = {0.7f, 0.7f, 0.7f, 1.0f};
static const RGBA light0Specular = {1.0f, 1.0f, 1.0f, 1.0f};
static const GLfloat light0Pos[]      = {100.0f, 100.0f, 100.0f, 0.0f};

// Material properties.

static RGBA matlColors[7] = {{1.0f, 0.0f, 0.0f, 1.0f},
                             {0.0f, 1.0f, 0.0f, 1.0f},
                             {0.0f, 0.0f, 1.0f, 1.0f},
                             {1.0f, 1.0f, 0.0f, 1.0f},
                             {0.0f, 1.0f, 1.0f, 1.0f},
                             {1.0f, 0.0f, 1.0f, 1.0f},
                             {0.235f, 0.0f, 0.78f, 1.0f},
                            };

extern void updateStripScene(int);
extern void updateDropScene(int);
extern void updateLemScene(int);
extern void updateExplodeScene(int);
extern void updateWinScene(int);
extern void updateTexScene(int);
extern void initStripScene(void);
extern void initDropScene(void);
extern void initLemScene(void);
extern void initExplodeScene(void);
extern void initWinScene(void);
extern void initTexScene(void);
extern void delStripScene(void);
extern void delDropScene(void);
extern void delLemScene(void);
extern void delExplodeScene(void);
extern void delWinScene(void);
extern void delTexScene(void);

typedef void (*PTRUPDATE)(int);
typedef void (*ptrdel)();
typedef void (*ptrinit)();

// Each screen saver style puts its hook functions into the function
// arrays below.  A consistent ordering of the functions is required.

static PTRUPDATE updateFuncs[] =
    {updateWinScene, updateExplodeScene,updateStripScene, updateStripScene,
     updateDropScene, updateLemScene, updateTexScene};
static ptrdel delFuncs[] =
    {delWinScene, delExplodeScene, delStripScene, delStripScene,
     delDropScene, delLemScene, delTexScene};
static ptrinit initFuncs[] =
    {initWinScene, initExplodeScene, initStripScene, initStripScene,
     initDropScene, initLemScene, initTexScene};
static int idsStyles[] =
    {IDS_LOGO, IDS_EXPLODE, IDS_RIBBON, IDS_2RIBBON,
     IDS_SPLASH, IDS_TWIST, IDS_FLAG};

#define MAX_TYPE    ( sizeof(initFuncs) / sizeof(ptrinit) - 1 )

// Each screen saver style can choose which dialog box controls it wants
// to use.  These flags enable each of the controls.  Controls not choosen
// will be disabled.

#define OPT_COLOR_CYCLE     0x00000001
#define OPT_SMOOTH_SHADE    0x00000002
#define OPT_TESSEL          0x00000008
#define OPT_SIZE            0x00000010
#define OPT_TEXTURE         0x00000020
#define OPT_STD             ( OPT_COLOR_CYCLE | OPT_SMOOTH_SHADE | OPT_TESSEL | OPT_SIZE )

static ULONG gflConfigOpt[] = {
     OPT_STD,               // Windows logo
     OPT_STD,               // Explode
     OPT_STD,               // Strip
     OPT_STD,               // Strip
     OPT_STD,               // Drop
     OPT_STD,               // Twist (lemniscate)
     OPT_SMOOTH_SHADE | OPT_TESSEL | OPT_SIZE | OPT_TEXTURE  // Texture mapped flag
};

static void updateDialogControls(HWND hDlg);

#ifdef MEMDEBUG
void xprintf(char *str, ...)
{
    va_list ap;
    char buffer[256];

    va_start(ap, str);
    vsprintf(buffer, str, ap);

    OutputDebugString(buffer);
    va_end(ap);
}
#endif

void *SaverAlloc(ULONG size)
{
    void *mPtr;

    mPtr = malloc(size);
#ifdef MEMDEBUG
    totalMem += size;
    xprintf("malloc'd %x, size %d\n", mPtr, size);
#endif
    return mPtr;
}

void SaverFree(void *pMem)
{
#ifdef MEMDEBUG
    totalMem -= _msize(pMem);
    xprintf("free %x, size = %d, total = %d\n", pMem, _msize(pMem), totalMem);
#endif
    free(pMem);
}

/******************************Public*Routine******************************\
* getIniSettings
*
* Get the screen saver configuration options from .INI file/registry.
*
* History:
*  10-May-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

static void getIniSettings()
{
    int    options;
    int    optMask = 1;
    TCHAR  szDefaultBitmap[MAX_PATH];
    int    tessel=0;

    LoadString(hMainInstance, IDS_GENNAME, szScreenSaver, sizeof(szScreenSaver) / sizeof(TCHAR));

    if( ss_RegistrySetup( hMainInstance, IDS_SAVERNAME, IDS_INIFILE ) )
    {
        options = ss_GetRegistryInt( IDS_OPTIONS, -1 );
        if (options >= 0)
        {
            bSmoothShading = ((options & optMask) != 0);
            optMask <<= 1;
            bColorCycle = ((options & optMask) != 0);
            UpdateFlags = (bColorCycle << 1);
        }

        Type = ss_GetRegistryInt( IDS_OBJTYPE, 0 );

        // Sanity check Type.  Don't want to index into function arrays
        // with a bad index!
        Type = min(Type, MAX_TYPE);

        // Set flag so that updateStripScene will do two strips instead
        // of one.

        if (Type == 3)
            UpdateFlags |= 0x4;

        tessel = ss_GetRegistryInt( IDS_TESSELATION, 100 );
        SS_CLAMP_TO_RANGE2( tessel, 0, 200 );

        if (tessel <= 100)
            fTesselFact  = (float)tessel / 100.0f;
        else
            fTesselFact = 1.0f + (((float)tessel - 100.0f) / 100.0f);

        uSize = ss_GetRegistryInt( IDS_SIZE, 50 );
        SS_CLAMP_TO_RANGE2( uSize, 0, 100 );

        // Determine the default .bmp file

        ss_GetDefaultBmpFile( szDefaultBitmap );

        // Is there a texture specified in the registry that overrides the
        // default?

        ss_GetRegistryString( IDS_TEXTURE, szDefaultBitmap, gTexFile.szPathName,
                              MAX_PATH);

        gTexFile.nOffset = ss_GetRegistryInt( IDS_TEXTURE_FILE_OFFSET, 0 );
    }
}

/******************************Public*Routine******************************\
* saveIniSettings
*
* Save the screen saver configuration option to the .INI file/registry.
*
* History:
*  10-May-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

static void saveIniSettings(HWND hDlg)
{
    if( ss_RegistrySetup( hMainInstance, IDS_SAVERNAME, IDS_INIFILE ) )
    {
        int pos, options;
        int optMask = 1;

        bSmoothShading = IsDlgButtonChecked(hDlg, DLG_SETUP_SMOOTH);
        bColorCycle = IsDlgButtonChecked(hDlg, DLG_SETUP_CYCLE);
        options = bColorCycle;
        options <<= 1;
        options |= bSmoothShading;
        ss_WriteRegistryInt( IDS_OPTIONS, options );

        Type = SendDlgItemMessage(hDlg, DLG_SETUP_TYPES, CB_GETCURSEL,
                                  0, 0);
        ss_WriteRegistryInt( IDS_OBJTYPE, Type );

        pos = ss_GetTrackbarPos( hDlg, DLG_SETUP_TESSEL );
        ss_WriteRegistryInt( IDS_TESSELATION, pos );

        pos = ss_GetTrackbarPos( hDlg, DLG_SETUP_SIZE );
        ss_WriteRegistryInt( IDS_SIZE, pos );

        ss_WriteRegistryString( IDS_TEXTURE, gTexFile.szPathName );
        ss_WriteRegistryInt( IDS_TEXTURE_FILE_OFFSET, gTexFile.nOffset );
    }
}

/******************************Public*Routine******************************\
* setupDialogControls
*
* Setup the dialog controls initially.
*
\**************************************************************************/

static void
setupDialogControls(HWND hDlg)
{
    int pos;

    InitCommonControls();

    if ( gflConfigOpt[Type] & OPT_TESSEL )
    {
        if (fTesselFact <= 1.0f)
            pos = (int)(fTesselFact * 100.0f);
        else
            pos = 100 + (int) ((fTesselFact - 1.0f) * 100.0f);

        ss_SetupTrackbar( hDlg, DLG_SETUP_TESSEL, 0, 200, 1, 10, pos );
    }

    if ( gflConfigOpt[Type] & OPT_SIZE )
    {
        ss_SetupTrackbar( hDlg, DLG_SETUP_SIZE, 0, 100, 1, 10, uSize );
    }

    updateDialogControls( hDlg );
}

/******************************Public*Routine******************************\
* updateDialogControls
*
* Update the dialog controls based on the current global state.
*
\**************************************************************************/

static void
updateDialogControls(HWND hDlg)
{
    CheckDlgButton(hDlg, DLG_SETUP_SMOOTH, bSmoothShading);
    CheckDlgButton(hDlg, DLG_SETUP_CYCLE, bColorCycle);

    EnableWindow(GetDlgItem(hDlg, DLG_SETUP_SMOOTH),
                 gflConfigOpt[Type] & OPT_SMOOTH_SHADE );
    EnableWindow(GetDlgItem(hDlg, DLG_SETUP_CYCLE),
                 gflConfigOpt[Type] & OPT_COLOR_CYCLE );

    EnableWindow(GetDlgItem(hDlg, DLG_SETUP_TESSEL),
                 gflConfigOpt[Type] & OPT_TESSEL);
    EnableWindow(GetDlgItem(hDlg, IDC_STATIC_TESS),
                 gflConfigOpt[Type] & OPT_TESSEL);
    EnableWindow(GetDlgItem(hDlg, IDC_STATIC_TESS_MIN),
                 gflConfigOpt[Type] & OPT_TESSEL);
    EnableWindow(GetDlgItem(hDlg, IDC_STATIC_TESS_MAX),
                 gflConfigOpt[Type] & OPT_TESSEL);

    EnableWindow(GetDlgItem(hDlg, DLG_SETUP_SIZE),
                 gflConfigOpt[Type] & OPT_SIZE);
    EnableWindow(GetDlgItem(hDlg, IDC_STATIC_SIZE),
                 gflConfigOpt[Type] & OPT_SIZE);
    EnableWindow(GetDlgItem(hDlg, IDC_STATIC_SIZE_MIN),
                 gflConfigOpt[Type] & OPT_SIZE);
    EnableWindow(GetDlgItem(hDlg, IDC_STATIC_SIZE_MAX),
                 gflConfigOpt[Type] & OPT_SIZE);

    EnableWindow(GetDlgItem(hDlg, DLG_SETUP_TEXTURE),
                 gflConfigOpt[Type] & OPT_TEXTURE);
}

BOOL WINAPI RegisterDialogClasses(HANDLE hinst)
{
    return TRUE;
}

/******************************Public*Routine******************************\
* ScreenSaverConfigureDialog
*
* Processes messages for the configuration dialog box.
*
\**************************************************************************/

BOOL ScreenSaverConfigureDialog(HWND hDlg, UINT message,
                                WPARAM wParam, LPARAM lParam)
{
    int wTmp;
    TCHAR szString[GEN_STRING_SIZE];

    switch (message) {
        case WM_INITDIALOG:
            getIniSettings();

            setupDialogControls(hDlg);

            for (wTmp = 0; wTmp <= MAX_TYPE; wTmp++) {
                LoadString(hMainInstance, idsStyles[wTmp], szString, GEN_STRING_SIZE);
                SendDlgItemMessage(hDlg, DLG_SETUP_TYPES, CB_ADDSTRING, 0,
                                   (LPARAM) szString);
            }
            SendDlgItemMessage(hDlg, DLG_SETUP_TYPES, CB_SETCURSEL, Type, 0);

            return TRUE;

        case WM_COMMAND:
            switch (LOWORD(wParam)) {
                case DLG_SETUP_TYPES:
                    switch (HIWORD(wParam))
                    {
                        case CBN_EDITCHANGE:
                        case CBN_SELCHANGE:
                            Type = SendDlgItemMessage(hDlg, DLG_SETUP_TYPES,
                                                      CB_GETCURSEL, 0, 0);
                            updateDialogControls(hDlg);
                            break;
                        default:
                            break;
                    }
                    return FALSE;

                case DLG_SETUP_TEXTURE:
                    ss_SelectTextureFile( hDlg, &gTexFile );
                    break;

                case IDOK:
                    saveIniSettings(hDlg);
                    EndDialog(hDlg, TRUE);
                    break;

                case IDCANCEL:
                    EndDialog(hDlg, FALSE);
                    break;

                case DLG_SETUP_SMOOTH:
                case DLG_SETUP_CYCLE:
                default:
                    break;
            }
            return TRUE;
            break;

        default:
            return 0;
    }
    return 0;
}

/******************************Public*Routine******************************\
* SetFloaterInfo
*
* Set the size and motion of the floating window
* It stays square in shape
*
\**************************************************************************/

static void
SetFloaterInfo( ISIZE *pParentSize, CHILD_INFO *pChild )
{
    float sizeFact;
    float sizeScale;
    int size;
    ISIZE *pChildSize = &pChild->size;
    MOTION_INFO *pMotion = &pChild->motionInfo;

    sizeScale = (float)uSize / 100.0f; // 0..1
    sizeFact = 0.25f + (0.30f * sizeScale);
    size = (int) (sizeFact * ( ((float)(pParentSize->width + pParentSize->height)) / 2.0f ));
    SS_CLAMP_TO_RANGE2( size, 0, pParentSize->width );
    SS_CLAMP_TO_RANGE2( size, 0, pParentSize->height );

    pChildSize->width = pChildSize->height = size;

    // Floater motion
    pMotion->posInc.x = .01f * (float) size;
    if( pMotion->posInc.x < 1.0f )
        pMotion->posInc.x = 1.0f;
    pMotion->posInc.y = pMotion->posInc.x;
    pMotion->posIncVary.x = .4f * pMotion->posInc.x;
    pMotion->posIncVary.y = pMotion->posIncVary.x;
}

/******************************Public*Routine******************************\
* initMaterial
*
* Initialize the material properties.
*
\**************************************************************************/

void initMaterial(int id, float r, float g, float b, float a)
{
    Material[id].ka.r = r;
    Material[id].ka.g = g;
    Material[id].ka.b = b;
    Material[id].ka.a = a;

    Material[id].kd.r = r;
    Material[id].kd.g = g;
    Material[id].kd.b = b;
    Material[id].kd.a = a;

    Material[id].ks.r = 1.0f;
    Material[id].ks.g = 1.0f;
    Material[id].ks.b = 1.0f;
    Material[id].ks.a = 1.0f;

    Material[id].specExp = 128.0f;
}

/******************************Public*Routine******************************\
* _3dfo_Init
*
\**************************************************************************/

static void
_3dfo_Init(void *data)
{
    int i;

    for (i = 0; i < 7; i++)
        initMaterial(i, matlColors[i].r, matlColors[i].g,
                     matlColors[i].b, matlColors[i].a);

    // Set the OpenGL clear color to black.

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
#ifdef SS_DEBUG
    glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
#endif

    // Enable the z-buffer.

    glEnable(GL_DEPTH_TEST);

    // Select the shading model.

    if (bSmoothShading)
        glShadeModel(GL_SMOOTH);
    else
        glShadeModel(GL_FLAT);

    // Setup the OpenGL matrices.

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // Setup the lighting.

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (GLfloat *) &lightAmbient);
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);
    glLightfv(GL_LIGHT0, GL_AMBIENT, (GLfloat *) &light0Ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, (GLfloat *) &light0Diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, (GLfloat *) &light0Specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light0Pos);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    // Setup the material properties.

    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat *) &Material[0].ks);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, (GLfloat *) &Material[0].specExp);

    // call specific objects init func
    (*initFuncs[Type])();
    updateSceneFunc = updateFuncs[Type];
}

/******************************Public*Routine******************************\
* _3dfo_Draw
*
\**************************************************************************/

static void
_3dfo_Draw(void *data)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    (*updateSceneFunc)(UpdateFlags);
}

/******************************Public*Routine******************************\
* _3dfo_FloaterBounce
*
\**************************************************************************/

static void
_3dfo_FloaterBounce(void *data)
{
    gbBounce = TRUE;
}

/******************************Public*Routine******************************\
* ss_Init
*
* Screen saver entry point.  Pre-GL initialization
*
\**************************************************************************/

SSContext *
ss_Init(void)
{
    getIniSettings();

    // Make sure the selected texture file is OK.

    if ( gflConfigOpt[Type] & OPT_TEXTURE )
    {
        // Verify texture file
        ss_DisableTextureErrorMsgs();
        ss_VerifyTextureFile( &gTexFile );
    }

    // set callbacks

    ss_InitFunc( _3dfo_Init );
    ss_UpdateFunc( _3dfo_Draw );

    // set configuration info to return

    gssc.bDoubleBuf = TRUE;
    gssc.depthType = SS_DEPTH16;

    gssc.bFloater = TRUE;
    gssc.floaterInfo.bMotion = TRUE;
    gssc.floaterInfo.ChildSizeFunc = SetFloaterInfo;
    ss_FloaterBounceFunc( _3dfo_FloaterBounce );

    return &gssc;
}

/**************************************************************************\
* ConfigInit
*
* Dialog box version of ss_Init.  Used for setting up any gl drawing on the
* dialog.
*
\**************************************************************************/
BOOL
ss_ConfigInit( HWND hDlg )
{
    return TRUE;
}


SS3DFO.H

/******************************Module*Header*******************************\
* Module Name: ssopengl.h
*
* Global header for the 3D Flying Objects screen saver.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include "sscommon.h"

// redefine PI as a double for all 3dfo code
#undef PI
#define PI PI_D

#define PALETTE_PER_MATL    32
#define PALETTE_PER_DIFF    26
#define PALETTE_PER_SPEC    6
#define MATL_MAX            7

typedef struct strFACE {
    POINT3D p[4];
    POINT3D n[4];
    POINT3D fn;
    int idMatl;
} FACE;

typedef struct strMFACE {
    int p[4];
    int material;
    POINT3D norm;
} MFACE;

typedef struct strMESH {
    int numFaces;
    int numPoints;
    POINT3D *pts;
    POINT3D *norms;
    MFACE *faces;
    GLint listID;
} MESH;

extern BOOL bSmoothShading;
extern BOOL bFalseColor;
extern BOOL bColorCycle;
extern float fTesselFact;
extern TEXFILE gTexFile;
extern BOOL gbBounce;

extern MATERIAL Material[];
extern int NumLights;

extern void newMesh(MESH *, int numFaces, int numPts);
extern void delMesh(MESH *);
extern void revolveSurface(MESH *, POINT3D *curve, int steps);

extern void *SaverAlloc(ULONG);
extern void SaverFree(void *);

// Resource constants

#define IDS_SAVERNAME           1002
#define IDS_FALSECOLOR          1004
#define IDS_SMOOTHSHADING       1005
#define IDS_OPTIONS             1007
#define IDS_OBJTYPE             1008
#define IDS_SCREENSAVERTITLE    1020

#define IDS_LOGO                1100
#define IDS_EXPLODE             1101
#define IDS_RIBBON              1102
#define IDS_2RIBBON             1103
#define IDS_SPLASH              1104
#define IDS_TWIST               1105
#define IDS_FLAG                1106

#define DLG_SETUP_HELP          2001
#define DLG_SETUP_TYPES         2002
#define DLG_SETUP_BITMAP        2003
#define DLG_SETUP_FCOLOR        2004
#define DLG_SETUP_SMOOTH        2005
#define DLG_SETUP_CYCLE         2006
#define DLG_SETUP_ABOUT         2007
#define DLG_SETUP_TESSEL        2008
#define DLG_SETUP_SIZE          2009
#define DLG_SETUP_TEXTURE       2010
#define IDC_STATIC_TESS         2011
#define IDC_STATIC_TESS_MIN     2012
#define IDC_STATIC_TESS_MAX     2013
#define IDC_STATIC_SIZE         2014
#define IDC_STATIC_SIZE_MIN     2015
#define IDC_STATIC_SIZE_MAX     2016

#define IDB_DEFTEX              3000

#define SHELP_CONTENTS          01
#define SHELP_SHAPES            02
#define SHELP_PASSWORD          03
#define SHELP_COLOR             04
#define SHELP_MISC              05
#define SHELP_OVERVIEW          06


CLEAR.CXX

/******************************Module*Header*******************************\
* Module Name: clear.cxx
*
* Window clearing functions
*
* Copyright 1996 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <sys/timeb.h>
#include <GL/gl.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include <math.h>
#include "ssintrnl.hxx"
#include "util.hxx"
#include "clear.hxx"

#define SS_CLEAR_BASE_DIV 32
#define SS_CLEAR_BASE_SIZE 16

/******************************Public*Routine******************************\
* ss_RectWipeClear
*
* Clears by drawing top, bottom, left, right rectangles that shrink in size
* towards the center.
*
* Calibration is used to try to maintain an ideal clear time.
*
\**************************************************************************/

int
ss_RectWipeClear( int width, int height, int repCount )
{
    int i, j, xinc, yinc, numDivs;
    int xmin, xmax, ymin, ymax;
    int w, h;
    BOOL bCalibrate = FALSE;
    double elapsed;
    static double idealTime = 0.7;
    SS_TIMER timer;

    xinc = 1;
    yinc = 1;
    numDivs = height; // assumes height <= width

    xmin = ymin = 0;
    xmax = width-1;
    ymax = height-1;

    glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );

    glEnable( GL_SCISSOR_TEST );

    if( repCount == 0 ) {
        bCalibrate = TRUE;
        repCount = 1;
        timer.Start();
    }

    for( i = 0; i < (numDivs/2); i ++ ) {
      w = xmax - xmin + 1;
      h = ymax - ymin + 1;

      for( j = 0; j < repCount; j ++ ) {
        // bottom
        glScissor( xmin, ymin, w, 1 );
        glClear( GL_COLOR_BUFFER_BIT );

        // left
        glScissor( xmin, ymin, 1, h );
        glClear( GL_COLOR_BUFFER_BIT );

        // right
        glScissor( xmax, ymin, 1, h );
        glClear( GL_COLOR_BUFFER_BIT );

        // top
        glScissor( xmin, ymax, w, 1 );
        glClear( GL_COLOR_BUFFER_BIT );

        glFlush(); // to eliminate 'bursts'
      }

      xmin += xinc;
      xmax -= xinc;
      ymin += yinc;
      ymax -= yinc;
    }

    if( bCalibrate ) {
        elapsed = timer.Stop();

        // try to maintain ideal clear time
        if( elapsed < idealTime ) {
            // increase repCount to slow down the clear
            if( elapsed == 0.0 )
                repCount = 10;  // just in case
            else
                repCount = (int) ((idealTime / elapsed) + 0.5);
        }
    }

    glDisable( GL_SCISSOR_TEST );

    return repCount;
}

#define SS_CLEAR_BASE_DIV 32

/******************************Public*Routine******************************\
*
* SS_DIGITAL_DISSOLVE_CLEAR constructor
*
\**************************************************************************/

SS_DIGITAL_DISSOLVE_CLEAR::
SS_DIGITAL_DISSOLVE_CLEAR()
{
    rectBuf = NULL;
    rectBufSize = 0;
    rectSize = SS_CLEAR_BASE_SIZE;
}

/******************************Public*Routine******************************\
*
* SS_DIGITAL_DISSOLVE_CLEAR destructor
*
\**************************************************************************/

SS_DIGITAL_DISSOLVE_CLEAR::
~SS_DIGITAL_DISSOLVE_CLEAR()
{
    if( rectBuf )
        LocalFree( rectBuf );
}

/******************************Public*Routine******************************\
* CalibrateClear
*
* Try to calibrate the clear so it takes the specified time
\**************************************************************************/

int SS_DIGITAL_DISSOLVE_CLEAR::
CalibrateClear( int width, int height, float fClearTime )
{
    float factor;
    int idealNRects;
    int nRects;
    int baseSize;
    SS_TIMER timer;
    float elapsed;

    baseSize = (width < height ? width : height) / SS_CLEAR_BASE_DIV;
    if( baseSize == 0 )
        baseSize = 1;

    timer.Start();

#ifdef AUTO_CALIBRATE
    // Clear a small region (quarter-screen) and extrapolate
    Clear( width >> 1, height >> 1, baseSize );
#else
    Clear( width, height, baseSize );
#endif

    elapsed = timer.Stop();
#ifdef AUTO_CALIBRATE
    // extrapolate to full screen time
    // this approximation resulted in clears being somewhat less than ideal
    elapsed *= 4.0f;
#endif

    // Adjust size of rects for ideal clear time

    if( elapsed <= 0.0f ) {
        rectSize = 1;
        return rectSize;
    }

    nRects = RectangleCount( width, height, baseSize );
    factor = fClearTime / elapsed;
    idealNRects = (int) (factor * (float)nRects);
    rectSize = (int) (sqrt( (double)(width*height) / (double)idealNRects ) + 0.5);
    if( rectSize == 0 )
        rectSize = 1;

    return rectSize;
}

/******************************Public*Routine******************************\
*
* SS_DIGITAL_DISSOLVE_CLEAR::Clear
*
* Clears by drawing random rectangles
*
\**************************************************************************/

BOOL SS_DIGITAL_DISSOLVE_CLEAR::
Clear( int width, int height )
{
    return Clear( width, height, rectSize );
}

BOOL SS_DIGITAL_DISSOLVE_CLEAR::
Clear( int width, int height, int size )
{
    BOOL *pRect;
    BOOL bCalibrate = FALSE;
    int count, nRects;
    int i, xdim, ydim;
    static float idealTime = 2.0f;

    if( (size <= 0) || !width || !height )
        return FALSE;

    // determine xdim, ydim from size
    xdim = SS_ROUND_UP( (float)width / (float)size );
    ydim = SS_ROUND_UP( (float)height / (float) size );

    // figure out how many rects needed
    count = nRects = xdim * ydim;

    // make sure enough room
    if( !ValidateBufSize( nRects ) )
        return FALSE;

    // reset the rect array to uncleared

    pRect = rectBuf;
    for( i = 0; i < count; i ++, pRect++ )
        *pRect = FALSE;

    // Clear random rectangles

    glEnable( GL_SCISSOR_TEST );

    while( count ) {
        // pick a random rect
        i = ss_iRand( nRects );

        if( rectBuf[i] ) {
            // This rect has already been cleared - find an empty one
            // Scan up and down from x,y, looking at the array linearly

            int up, down;
            BOOL searchUp;

            up = down = i;

            pRect = rectBuf;
            while( *(pRect + i) ) {
                if( searchUp ) {
                    // search up side
                    if( up < (nRects-1) ) {
                        up++;
                    }
                    i = up;
                } else {
                    // search down side
                    if( down > 0 ) {
                        down--;
                    }
                    i = down;
                }
                searchUp = !searchUp;
            }
        }

        // clear the x,y rect
        glScissor( (i % xdim)*size, (i / xdim)*size, size, size );
        glClear( GL_COLOR_BUFFER_BIT );
        glFlush();

        rectBuf[i] = TRUE; // mark as taken
        count--;
    }

    glDisable( GL_SCISSOR_TEST );

    return TRUE;
}

/******************************Public*Routine******************************\
* RectangleCount
*
\**************************************************************************/

int SS_DIGITAL_DISSOLVE_CLEAR::
RectangleCount( int width, int height, int size )
{
    return  SS_ROUND_UP( (float)width / (float)size ) *
            SS_ROUND_UP( (float)height / (float) size );
}

/******************************Public*Routine******************************\
* ValidateBufSize
*
\**************************************************************************/

BOOL SS_DIGITAL_DISSOLVE_CLEAR::
ValidateBufSize( int nRects )
{
    if( nRects > rectBufSize ) {
        // need a bigger rect buf
        BOOL *r = (BOOL *) LocalAlloc( LMEM_FIXED, sizeof(BOOL) * nRects );
        if( !r )
            return FALSE;
        if( rectBuf )
            LocalFree( rectBuf );
        rectBuf = r;
        rectBufSize = nRects;
    }
    return TRUE;
}

/******************************Public*Routine******************************\
* DrawGdiRect
*
* Clears the rect with the brush
\**************************************************************************/

void
DrawGdiRect( HDC hdc, HBRUSH hbr, RECT *pRect )
{
    if( pRect == NULL )
        return;

    FillRect( hdc, pRect, hbr );
    GdiFlush();
}

#ifdef SS_INITIAL_CLEAR
/*-----------------------------------------------------------------------
|
|    RectWipeClear(width, height):
|       - Does a rectangular wipe (or clear) by drawing in a sequence
|         of rectangles using Gdi
|       MOD: add calibrator capability to adjust speed for different
|            architectures
|       MOD: this can be further optimized by caching the brush
|
-----------------------------------------------------------------------*/
void
ss_GdiRectWipeClear( HWND hwnd, int width, int height )
{
    HDC hdc;
    HBRUSH hbr;
    RECT rect;
    int i, j, xinc, yinc, numDivs = 500;
    int xmin, xmax, ymin, ymax;
    int repCount = 10;

    xinc = 1;
    yinc = 1;
    numDivs = height;
    xmin = ymin = 0;
    xmax = width;
    ymax = height;

    hdc = GetDC( hwnd );

    hbr = CreateSolidBrush( RGB( 0, 0, 0 ) );

    for( i = 0; i < (numDivs/2 - 1); i ++ ) {
      for( j = 0; j < repCount; j ++ ) {
        rect.left = xmin; rect.top = ymin;
        rect.right = xmax; rect.bottom = ymin + yinc;
        FillRect( hdc, &rect, hbr );
        rect.top = ymax - yinc;
        rect.bottom = ymax;
        FillRect( hdc, &rect, hbr );
        rect.top = ymin + yinc;
        rect.right = xmin + xinc; rect.bottom = ymax - yinc;
        FillRect( hdc, &rect, hbr );
        rect.left = xmax - xinc; rect.top = ymin + yinc;
        rect.right = xmax; rect.bottom = ymax - yinc;
        FillRect( hdc, &rect, hbr );
      }

      xmin += xinc;
      xmax -= xinc;
      ymin += yinc;
      ymax -= yinc;
    }

    // clear last square in middle

    rect.left = xmin; rect.top = ymin;
    rect.right = xmax; rect.bottom = ymax;
    FillRect( hdc, &rect, hbr );

    DeleteObject( hbr );

    ReleaseDC( hwnd, hdc );

    GdiFlush();
}
#endif // SS_INITIAL_CLEAR


COLOR.C

#include <windows.h>
#include <math.h>
#include <GL/gl.h>
#include "sscommon.h"

/******************************Public*Routine******************************\
* HsvToRgb
*
* HSV to RGB color space conversion.  From pg. 593 of Foley & van Dam.
*
\**************************************************************************/

void
ss_HsvToRgb(float h, float s, float v, RGBA *color )
{
    float i, f, p, q, t;

    // set alpha value, so caller doesn't have to worry about undefined value
    color->a = 1.0f;

    if (s == 0.0f)     // assume h is undefined
        color->r = color->g = color->b = v;
    else {
        if (h >= 360.0f)
            h = 0.0f;
        h = h / 60.0f;
        i = (float) floor(h);
        f = h - i;
        p = v * (1.0f - s);
        q = v * (1.0f - (s * f));
        t = v * (1.0f - (s * (1.0f - f)));
        switch ((int)i) {
        case 0:
            color->r = v;
            color->g = t;
            color->b = p;
            break;
        case 1:
            color->r = q;
            color->g = v;
            color->b = p;
            break;
        case 2:
            color->r = p;
            color->g = v;
            color->b = t;
            break;
        case 3:
            color->r = p;
            color->g = q;
            color->b = v;
            break;
        case 4:
            color->r = t;
            color->g = p;
            color->b = v;
            break;
        case 5:
            color->r = v;
            color->g = p;
            color->b = q;
            break;
        default:
            break;
        }
    }
}


DIALOG.C

/******************************Module*Header*******************************\
* Module Name: dialog.c
*
* Dialog helper functions
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <commdlg.h>
#include <commctrl.h>
#include "sscommon.h"

#define BUF_SIZE 30
static TCHAR  szSectName[BUF_SIZE];
static TCHAR  szItemName[BUF_SIZE];
static TCHAR  szFname[BUF_SIZE];
static TCHAR  szTmp[BUF_SIZE];
static HINSTANCE hInstance = 0;

/******************************Public*Routine******************************\
* ss_RegistrySetup
*
* Setup for registry access
*
\**************************************************************************/

BOOL ss_RegistrySetup( HINSTANCE hinst, int section, int file )
{
    if( LoadString(hInstance, section, szSectName, BUF_SIZE) &&
        LoadString(hInstance, file, szFname, BUF_SIZE) )
    {
        hInstance = hinst;
        return TRUE;
    }
    return FALSE;
}

/******************************Public*Routine******************************\
* ss_GetRegistryInt
*
* Retrieve integer value from registry
*
\**************************************************************************/

int  ss_GetRegistryInt( int name, int iDefault )
{
    if( LoadString( hInstance, name, szItemName, BUF_SIZE ) ) {
        return GetPrivateProfileInt(szSectName, szItemName, iDefault, szFname);
    }
    return 0;
}

/******************************Public*Routine******************************\
* ss_GetRegistryString
*
* Retrieve string from registry
*
\**************************************************************************/

void ss_GetRegistryString( int name, LPTSTR lpDefault, LPTSTR lpDest,
                           int bufSize )
{
    if( LoadString( hInstance, name, szItemName, BUF_SIZE ) ) {
        GetPrivateProfileString(szSectName, szItemName, lpDefault, lpDest,
                                bufSize, szFname);
    }
}

/******************************Public*Routine******************************\
* ss_WriteRegistryInt
*
* Write integer value to registry
*
\**************************************************************************/

void ss_WriteRegistryInt( int name, int iVal )
{
    if( LoadString(hInstance, name, szItemName, BUF_SIZE) ) {
        wsprintf(szTmp, TEXT("%ld"), iVal);
        WritePrivateProfileString(szSectName, szItemName, szTmp, szFname);
    }
}

/******************************Public*Routine******************************\
* ss_WriteRegistryString
*
* Write string value to registry
*
\**************************************************************************/

void ss_WriteRegistryString( int name, LPTSTR lpString )
{
    if( LoadString(hInstance, name, szItemName, BUF_SIZE) ) {
        WritePrivateProfileString(szSectName, szItemName, lpString, szFname);
    }
}

/******************************Public*Routine******************************\
* GetTrackbarPos
*
* Get the current position of a common control trackbar
\**************************************************************************/

int
ss_GetTrackbarPos( HWND hDlg, int item )
{
    return
        SendDlgItemMessage(
            hDlg,
            item,
            TBM_GETPOS,
            0,
            0
        );
}

/******************************Public*Routine******************************\
* SetupTrackbar
*
* Setup a common control trackbar
\**************************************************************************/

void
ss_SetupTrackbar( HWND hDlg, int item, int lo, int hi, int lineSize,
                  int pageSize, int pos )
{
    SendDlgItemMessage(
        hDlg,
        item,
        TBM_SETRANGE,
        (WPARAM) TRUE,
        (LPARAM) MAKELONG( lo, hi )
    );
    SendDlgItemMessage(
        hDlg,
        item,
        TBM_SETPOS,
        (WPARAM) TRUE,
        (LPARAM) pos
    );
    SendDlgItemMessage(
        hDlg,
        item,
        TBM_SETPAGESIZE,
        (WPARAM) 0,
        (LPARAM) pageSize
    );
    SendDlgItemMessage(
        hDlg,
        item,
        TBM_SETLINESIZE,
        (WPARAM) 0,
        (LPARAM) lineSize
    );
}


DLGDRAW.CXX

/******************************Module*Header*******************************\
* Module Name: dlgdraw.c
*
* For gl drawing in dialog boxes
*
* Copyright 1995 - 1998 Microsoft Corporation
\**************************************************************************/

#include <windows.h>
#include <commdlg.h>
#include <scrnsave.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "ssintrnl.hxx"
#include "dlgdraw.hxx"

// Define this if want each TEX_BUTTON to be a separate window.  This is
// necessary if the main dialog window has WS_CLIP_CHILDREN, but so far this
// doesn't seem to be the case.

static void CalcGLViewport( HWND hwndParent, HWND hwndChild, IPOINT2D *pOrigin, ISIZE *pSize );

//materials for varying intensities

enum{
    MAT_INTENSITY_LOW = 0,
    MAT_INTENSITY_MID,
    MAT_INTENSITY_HIGH,
    MAT_COUNT
};

MATERIAL gMat[MAT_COUNT] = {
    {{0.3f, 0.3f, 0.3f}, {0.6f, 0.6f, 0.6f}, {0.2f, 0.2f, 0.2f}, 0.3f },
    {{0.2f, 0.2f, 0.2f}, {0.8f, 0.8f, 0.8f}, {0.2f, 0.2f, 0.2f}, 0.3f },
    {{0.2f, 0.2f, 0.2f}, {1.0f, 1.0f, 1.0f}, {0.2f, 0.2f, 0.2f}, 0.3f }
};

float colorBlack[3] = {0.0f, 0.0f, 0.0f};

/**************************************************************************\
* SS_TEX_BUTTON constructor
*
* This allows drawing GL textures on a button
*
* For optimum performance, GL is configured on the main dialog window, and
* 'viewported' to the button.
* Defining SS_MULTIWINDOW results in the texture being drawn in the actual
* button window.
*
* Note: this only works for buttons on the main dialog window for now.
\**************************************************************************/

SS_TEX_BUTTON::SS_TEX_BUTTON( HWND hDlg, HWND hDlgBtn )
{
    PSSW psswParent = gpss->sswTable.PsswFromHwnd( hDlg );
    SS_ASSERT( psswParent, "SS_TEX_BUTTON constructor: NULL psswParent\n" );

    // The parent needs to have an hrc context, since we will be using it
    // for drawing.

    SS_GL_CONFIG GLc = { 0, 0, NULL };
    if( !psswParent->ConfigureForGL( &GLc ) ) {
        SS_WARNING( "SS_TEX_BUTTON constructor: ConfigureForGL failed\n" );
        return;
    }

#ifdef SS_MULTIWINDOW
    // Each button is a separate GL window, using its parents hrc
    pssw = new SSW( psswParent, hDlgBtn );

    SS_ASSERT( pssw, "SS_TEX_BUTTON constructor: pssw alloc failure\n" );

    // Configure the pssw for GL

    GLc.pfFlags = 0;
    GLc.hrc = psswParent->GetHRC();
    GLc.pStretch = NULL;

    if( ! pssw->ConfigureForGL( &GLc ) ) {
        SS_WARNING( "SS_TEX_BUTTON constructor: ConfigureForGL failed\n" );
        return;
    }
#else
    // Make the button a 'subwindow' of the parent
    pssw = NULL;

    // Calculate the viewport to draw to

    CalcGLViewport( hDlg, hDlgBtn, &origin, &size );
#endif

    // Init various GL stuff
    InitGL();

    pCurTex = NULL;
    bEnabled = TRUE;
}

/**************************************************************************\
* SS_TEX_BUTTON destructor
*
\**************************************************************************/

SS_TEX_BUTTON::~SS_TEX_BUTTON()
{
    if( pssw )
        delete pssw;
}

/**************************************************************************\
* InitGL
*
\**************************************************************************/

void
SS_TEX_BUTTON::InitGL()
{
    float ambient[] = {0.2f, 0.2f, 0.2f, 1.0f};
    float diffuse[] = {0.7f, 0.7f, 0.7f, 1.0f};
    float position[] = {0.0f, 0.0f, -150.0f, 1.0f};
    float lmodel_ambient[] = {1.0f, 1.0f, 1.0f, 1.0f};

    // lighting, for intensity levels

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glEnable(GL_LIGHT0);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);

    glCullFace( GL_BACK );
    glEnable(GL_CULL_FACE);
    glFrontFace( GL_CW );
    glShadeModel( GL_FLAT );

    glColor3f( 1.0f, 1.0f, 1.0f );
    gluOrtho2D( -1.0, 1.0, -1.0, 1.0 );

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}

/**************************************************************************\
* SetTexture
*
* Set a current texture for the button
*
* Note this is a pointer to a texture, so any texture memory management is
* done by the caller.
\**************************************************************************/

void
SS_TEX_BUTTON::SetTexture( TEXTURE *pTex )
{
    pCurTex = pTex;
}

/**************************************************************************\
* Draw
*
\**************************************************************************/

void
SS_TEX_BUTTON::Draw( TEXTURE *pTex )
{
    if( pTex != NULL ) {
        glEnable(GL_TEXTURE_2D);

        ss_SetTexture( pTex ); // doesn't look at iPalRot yet
        // Set the texture palette if it exists
        if( pTex->pal && pTex->iPalRot )
            ss_SetTexturePalette( pTex, pTex->iPalRot );
    }
    // else white rectangle will be drawn

    if( bEnabled )
        intensity = DLG_INTENSITY_HIGH;
    else
        intensity = DLG_INTENSITY_LOW;

    switch( intensity ) {
        case DLG_INTENSITY_LOW:
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            glEnable(GL_LIGHTING);
            glColor3f( 0.5f, 0.5f, 0.5f );
            break;
        case DLG_INTENSITY_MID:
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            glEnable(GL_LIGHTING);
            glColor3f( 0.7f, 0.7f, 0.7f );
            break;
        case DLG_INTENSITY_HIGH:
        default:
            glColor3f( 1.0f, 1.0f, 1.0f );
            glDisable(GL_LIGHTING);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    }

    // Set the viewport
#ifdef SS_MULTIWINDOW
    glViewport( 0, 0, pssw->size.width, pssw->size.height );
#else
    glViewport( origin.x, origin.y, size.width, size.height );
#endif

    glBegin( GL_QUADS );
        glTexCoord2f( 0.0f, 1.0f );
        glVertex2f( -1.0f, 1.0f );
        glTexCoord2f( 1.0f, 1.0f );
        glVertex2f(  1.0f,  1.0f );
        glTexCoord2f( 1.0f, 0.0f );
        glVertex2f(  1.0f, -1.0f );
        glTexCoord2f( 0.0f, 0.0f );
        glVertex2f(  -1.0f, -1.0f );
    glEnd();

    glDisable( GL_TEXTURE_2D);

    glFlush();
}

void
SS_TEX_BUTTON::Draw()
{
    Draw( pCurTex );
}

/**************************************************************************\
* CalcGLViewport
*
* Calculate viewport for the child window
*
\**************************************************************************/

static void
CalcGLViewport( HWND hwndParent, HWND hwndChild, IPOINT2D *pOrigin, ISIZE *pSize )
{
    RECT childRect, parentRect;

    // Get size of the child window

    GetClientRect( hwndChild, &childRect );
    pSize->width = childRect.right;
    pSize->height = childRect.bottom;

    // Calc origin of the child window wrt its parents client area
    // Note that the y-coord must be inverted for GL

    // Map the child client rect to the parent client coords
    MapWindowPoints( hwndChild, hwndParent, (POINT *) &childRect, 2 );
    pOrigin->x = childRect.left;
    // invert y coord
    GetClientRect( hwndParent, &parentRect );
    pOrigin->y = parentRect.bottom - childRect.bottom;
}


FASTDIB.C

/******************************Module*Header*******************************\
* Module Name: fastdib.c
*
* CreateCompatibleDIB implementation.
*
* Copyright 1996 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

#include "sscommon.h"

BOOL APIENTRY GetDIBTranslationVector(HDC hdcMem, HPALETTE hpal, BYTE *pbVector);
static BOOL bFillBitmapInfo(HDC hdc, HPALETTE hpal, BITMAPINFO *pbmi);
static BOOL bFillColorTable(HDC hdc, HPALETTE hpal, BITMAPINFO *pbmi);
static UINT MyGetSystemPaletteEntries(HDC hdc, UINT iStartIndex, UINT nEntries,
                                      LPPALETTEENTRY lppe);
static BOOL bComputeLogicalToSurfaceMap(HDC hdc, HPALETTE hpal,
                                        BYTE *pajVector);

/******************************Public*Routine******************************\
* CreateCompatibleDIB
*
* Create a DIB section with an optimal format w.r.t. the specified hdc.
*
* If DIB <= 8bpp, then the DIB color table is initialized based on the
* specified palette.  If the palette handle is NULL, then the system
* palette is used.
*
* Note: The hdc must be a direct DC (not an info or memory DC).
*
* Note: On palettized displays, if the system palette changes the
*       UpdateDIBColorTable function should be called to maintain
*       the identity palette mapping between the DIB and the display.
*
* Returns:
*   Valid bitmap handle if successful, NULL if error.
\**************************************************************************/

//HBITMAP APIENTRY
HBITMAP
SSDIB_CreateCompatibleDIB(HDC hdc, HPALETTE hpal, ULONG ulWidth, ULONG ulHeight,
                    PVOID *ppvBits)
{
    HBITMAP hbmRet = (HBITMAP) NULL;
    BYTE aj[sizeof(BITMAPINFO) + (sizeof(RGBQUAD) * 255)];
    BITMAPINFO *pbmi = (BITMAPINFO *) aj;

    //
    // Validate hdc.
    //

    if ( GetObjectType(hdc) != OBJ_DC )
    {
        SS_DBGPRINT("CreateCompatibleDIB: not OBJ_DC\n");
        return hbmRet;
    }

    memset(aj, 0, sizeof(aj));
    if ( bFillBitmapInfo(hdc, hpal, pbmi) )
    {
        //
        // Change bitmap size to match specified dimensions.
        //

        pbmi->bmiHeader.biWidth = ulWidth;
        pbmi->bmiHeader.biHeight = ulHeight;
        if (pbmi->bmiHeader.biCompression == BI_RGB)
        {
            pbmi->bmiHeader.biSizeImage = 0;
        }
        else
        {
            if ( pbmi->bmiHeader.biBitCount == 16 )
                pbmi->bmiHeader.biSizeImage = ulWidth * ulHeight * 2;
            else if ( pbmi->bmiHeader.biBitCount == 32 )
                pbmi->bmiHeader.biSizeImage = ulWidth * ulHeight * 4;
            else
                pbmi->bmiHeader.biSizeImage = 0;
        }
        pbmi->bmiHeader.biClrUsed = 0;
        pbmi->bmiHeader.biClrImportant = 0;

        //
        // Create the DIB section.  Let Win32 allocate the memory and return
        // a pointer to the bitmap surface.
        //

        hbmRet = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
        GdiFlush();

        if ( !hbmRet )
        {
            SS_DBGPRINT("CreateCompatibleDIB: CreateDIBSection failed\n");
        }
    }
    else
    {
        SS_DBGPRINT("CreateCompatibleDIB: bFillBitmapInfo failed\n");
    }

    return hbmRet;
}

/******************************Public*Routine******************************\
* UpdateDIBColorTable
*
* Synchronize the DIB color table to the specified palette hpal.
* If hpal is NULL, then use the system palette.
*
* Returns:
*   TRUE if successful, FALSE otherwise.
\**************************************************************************/

BOOL APIENTRY
SSDIB_UpdateColorTable(HDC hdcMem, HDC hdc, HPALETTE hpal)
{
    BOOL bRet = FALSE;
    HBITMAP hbm;
    DIBSECTION ds;
    BYTE aj[(sizeof(RGBQUAD) + sizeof(PALETTEENTRY)) * 256];
    LPPALETTEENTRY lppe = (LPPALETTEENTRY) aj;
    LPRGBQUAD prgb = (LPRGBQUAD) (lppe + 256);
    ULONG cColors;

    //
    // Validate hdc.
    //

    if ( GetObjectType(hdc) != OBJ_DC )
    {
        SS_DBGPRINT("UpdateDIBColorTable: not OBJ_DC\n");
        return bRet;
    }
    if ( GetObjectType(hdcMem) != OBJ_MEMDC )
    {
        SS_DBGPRINT("UpdateDIBColorTable: not OBJ_MEMDC\n");
        return bRet;
    }

    //
    // Get the bitmap handle out of the memdc.
    //

    hbm = GetCurrentObject(hdcMem, OBJ_BITMAP);

    //
    // Validate bitmap (must be DIB section).
    //

    if ( (GetObject(hbm, sizeof(ds), &ds) == sizeof(ds)) &&
         ds.dsBm.bmBits )
    {
        //
        // Get palette entries from specified palette or system palette.
        //

        cColors = 1 << ds.dsBmih.biBitCount;

        if ( hpal ? GetPaletteEntries(hpal, 0, cColors, lppe)
                  : MyGetSystemPaletteEntries(hdc, 0, cColors, lppe)
           )
        {
            UINT i;

            //
            // Convert to RGBQUAD.
            //

            for (i = 0; i < cColors; i++)
            {
                prgb[i].rgbRed      = lppe[i].peRed;
                prgb[i].rgbGreen    = lppe[i].peGreen;
                prgb[i].rgbBlue     = lppe[i].peBlue;
                prgb[i].rgbReserved = 0;
            }

            //
            // Set the DIB color table.
            //

            bRet = (BOOL) SetDIBColorTable(hdcMem, 0, cColors, prgb);

            if (!bRet)
            {
                SS_DBGPRINT("UpdateDIBColorTable: SetDIBColorTable failed\n");
            }
        }
        else
        {
            SS_DBGPRINT("UpdateDIBColorTable: MyGetSystemPaletteEntries failed\n");
        }
    }
    else
    {
        SS_DBGPRINT("UpdateDIBColorTable: GetObject failed\n");
    }

    return bRet;
}

/******************************Public*Routine******************************\
* GetCompatibleDIBInfo
*
* Copies pointer to bitmap origin to ppvBase and bitmap stride to plStride.
* Win32 DIBs can be created bottom-up (the default) with the origin at the
* lower left corner or top-down with the origin at the upper left corner.
* If the bitmap is top-down, *plStride is positive; if bottom-up, *plStride
* us negative.
*
* Also, because of restrictions on the alignment of scan lines the width
* the bitmap is often not the same as the stride (stride is the number of
* bytes between vertically adjacent pixels).
*
* The ppvBase and plStride value returned will allow you to address any
* given pixel (x, y) in the bitmap as follows:
*
* PIXEL *ppix;
*
* ppix = (PIXEL *) (((BYTE *)*ppvBase) + (y * *plStride) + (x * sizeof(PIXEL)));
*
* Returns:
*   TRUE if successful, FALSE otherwise.
\**************************************************************************/

BOOL APIENTRY
GetCompatibleDIBInfo(HBITMAP hbm, PVOID *ppvBase, LONG *plStride)
{
    BOOL bRet = FALSE;
    DIBSECTION ds;

    //
    // Call GetObject to return a DIBSECTION.  If successful, the
    // bitmap is a DIB section and we can retrieve the pointer to
    // the bitmap bits and other parameters.
    //

    if ( (GetObject(hbm, sizeof(ds), &ds) == sizeof(ds))
         && ds.dsBm.bmBits )
    {
        // For backwards compatibility with Get/SetBitmapBits, GDI does
        // not accurately report the bitmap pitch in bmWidthBytes.  It
        // always computes bmWidthBytes assuming WORD-aligned scanlines
        // regardless of the platform.
        //
        // Therefore, if the platform is WinNT, which uses DWORD-aligned
        // scanlines, adjust the bmWidthBytes value.

        {
            OSVERSIONINFO osvi;

            osvi.dwOSVersionInfoSize = sizeof(osvi);
            if (GetVersionEx(&osvi))
            {
                if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_NT )
                {
                    ds.dsBm.bmWidthBytes = (ds.dsBm.bmWidthBytes + 3) & ~3;
                }
            }
            else
            {
                SS_DBGPRINT1("GetCompatibleDIBInfo: GetVersionEx failed with %d\n", GetLastError());
                return bRet;
            }
        }

        //
        // If biHeight is positive, then the bitmap is a bottom-up DIB.
        // If biHeight is negative, then the bitmap is a top-down DIB.
        //

        if ( ds.dsBmih.biHeight > 0 )
        {
            *ppvBase  = (PVOID) (((int) ds.dsBm.bmBits) + (ds.dsBm.bmWidthBytes * (ds.dsBm.bmHeight - 1)));
            *plStride = (ULONG) (-ds.dsBm.bmWidthBytes);
        }
        else
        {
            *ppvBase  = ds.dsBm.bmBits;
            *plStride = ds.dsBm.bmWidthBytes;
        }

        bRet = TRUE;
    }
    else
    {
        SS_DBGPRINT("GetCompatibleDIBInfo: cannot get pointer to DIBSECTION bmBits\n");
    }

    return bRet;
}

/******************************Public*Routine******************************\
* GetDIBTranslationVector
*
* Copies the translation vector that maps colors in the specified palette,
* hpal, to the DIB selected into the specified DC, hdcMem.
*
* Effects:
*
* Returns:
*   TRUE if successful, FALSE otherwise.
\**************************************************************************/

BOOL APIENTRY
GetDIBTranslationVector(HDC hdcMem, HPALETTE hpal, BYTE *pbVector)
{
    BOOL bRet = FALSE;
    HBITMAP hbm;
    DIBSECTION ds;

    //
    // Validate parameters.
    //

    if ( GetObjectType(hdcMem) != OBJ_MEMDC ||
         GetObjectType(hpal) != OBJ_PAL ||
         !pbVector )
    {
        SS_DBGPRINT("GetDIBTranslationVector: bad parameter\n");
        return bRet;
    }

    //
    // The function bComputeLogicalToSurfaceMap cannot handle palettes
    // greater than 256 entries.
    //

    if ( GetPaletteEntries(hpal, 0, 1, NULL) > 256 )
    {
        SS_DBGPRINT("GetDIBTranslationVector: palette too big\n");
        return bRet;
    }

    //
    // The DIB must have a color table.
    //

    hbm = GetCurrentObject(hdcMem, OBJ_BITMAP);
    if ( (GetObject(hbm, sizeof(ds), &ds) == sizeof(ds))
         && (ds.dsBmih.biBitCount <= 8) )
    {
        bRet = bComputeLogicalToSurfaceMap(hdcMem, hpal, pbVector);
    }
    else
    {
        SS_DBGPRINT("GetDIBTranslationVector: not a DIB section\n");
        return bRet;
    }

    return bRet;
}

//////////////////// Below here are internal-only routines ////////////////////

/******************************Public*Routine******************************\
* bFillBitmapInfo
*
* Fills in the fields of a BITMAPINFO so that we can create a bitmap
* that matches the format of the display.
*
* This is done by creating a compatible bitmap and calling GetDIBits
* to return the color masks.  This is done with two calls.  The first
* call passes in biBitCount = 0 to GetDIBits which will fill in the
* base BITMAPINFOHEADER data.  The second call to GetDIBits (passing
* in the BITMAPINFO filled in by the first call) will return the color
* table or bitmasks, as appropriate.
*
* Returns:
*   TRUE if successful, FALSE otherwise.
\**************************************************************************/

static BOOL
bFillBitmapInfo(HDC hdc, HPALETTE hpal, BITMAPINFO *pbmi)
{
    HBITMAP hbm;
    BOOL    bRet = FALSE;

    //
    // Create a dummy bitmap from which we can query color format info
    // about the device surface.
    //

    if ( (hbm = CreateCompatibleBitmap(hdc, 1, 1)) != NULL )
    {
        pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

        //
        // Call first time to fill in BITMAPINFO header.
        //

        GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);

        if ( pbmi->bmiHeader.biBitCount <= 8 )
        {
            bRet = bFillColorTable(hdc, hpal, pbmi);
        }
        else
        {
            if ( pbmi->bmiHeader.biCompression == BI_BITFIELDS )
            {
                //
                // Call a second time to get the color masks.
                // It's a GetDIBits Win32 "feature".
                //

                GetDIBits(hdc, hbm, 0, pbmi->bmiHeader.biHeight, NULL, pbmi,
                          DIB_RGB_COLORS);
            }

            bRet = TRUE;
        }

        DeleteObject(hbm);
    }
    else
    {
        SS_DBGPRINT("bFillBitmapInfo: CreateCompatibleBitmap failed\n");
    }

    return bRet;
}

/******************************Public*Routine******************************\
* bFillColorTable
*
* Initialize the color table of the BITMAPINFO pointed to by pbmi.  Colors
* are set to the current system palette.
*
* Note: call only valid for displays of 8bpp or less.
*
* Returns:
*   TRUE if successful, FALSE otherwise.
\**************************************************************************/

static BOOL
bFillColorTable(HDC hdc, HPALETTE hpal, BITMAPINFO *pbmi)
{
    BOOL bRet = FALSE;
    BYTE aj[sizeof(PALETTEENTRY) * 256];
    LPPALETTEENTRY lppe = (LPPALETTEENTRY) aj;
    RGBQUAD *prgb = (RGBQUAD *) &pbmi->bmiColors[0];
    ULONG cColors;

    cColors = 1 << pbmi->bmiHeader.biBitCount;
    if ( cColors <= 256 )
    {
        if ( hpal ? GetPaletteEntries(hpal, 0, cColors, lppe)
                  : MyGetSystemPaletteEntries(hdc, 0, cColors, lppe) )
        {
            UINT i;

            for (i = 0; i < cColors; i++)
            {
                prgb[i].rgbRed      = lppe[i].peRed;
                prgb[i].rgbGreen    = lppe[i].peGreen;
                prgb[i].rgbBlue     = lppe[i].peBlue;
                prgb[i].rgbReserved = 0;
            }

            bRet = TRUE;
        }
        else
        {
            SS_DBGPRINT("bFillColorTable: MyGetSystemPaletteEntries failed\n");
        }
    }

    return bRet;
}

/******************************Public*Routine******************************\
* MyGetSystemPaletteEntries
*
* Internal version of GetSystemPaletteEntries.
*
* GetSystemPaletteEntries fails on some 4bpp devices.  This version
* will detect the 4bpp case and supply the hardcoded 16-color VGA palette.
* Otherwise, it will pass the call on to GDI's GetSystemPaletteEntries.
*
* It is expected that this call will only be called in the 4bpp and 8bpp
* cases as it is not necessary for OpenGL to query the system palette
* for > 8bpp devices.
\**************************************************************************/

static PALETTEENTRY gapeVgaPalette[16] =
{
    { 0,   0,   0,    0 },
    { 0x80,0,   0,    0 },
    { 0,   0x80,0,    0 },
    { 0x80,0x80,0,    0 },
    { 0,   0,   0x80, 0 },
    { 0x80,0,   0x80, 0 },
    { 0,   0x80,0x80, 0 },
    { 0x80,0x80,0x80, 0 },
    { 0xC0,0xC0,0xC0, 0 },
    { 0xFF,0,   0,    0 },
    { 0,   0xFF,0,    0 },
    { 0xFF,0xFF,0,    0 },
    { 0,   0,   0xFF, 0 },
    { 0xFF,0,   0xFF, 0 },
    { 0,   0xFF,0xFF, 0 },
    { 0xFF,0xFF,0xFF, 0 }
};

static UINT
MyGetSystemPaletteEntries(HDC hdc, UINT iStartIndex, UINT nEntries,
                          LPPALETTEENTRY lppe)
{
    int nDeviceBits;

    nDeviceBits = GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES);

    //
    // Some 4bpp displays will fail the GetSystemPaletteEntries call.
    // So if detected, return the hardcoded table.
    //

    if ( nDeviceBits == 4 )
    {
        if ( lppe )
        {
            nEntries = min(nEntries, (16 - iStartIndex));

            memcpy(lppe, &gapeVgaPalette[iStartIndex],
                   nEntries * sizeof(PALETTEENTRY));
        }
        else
            nEntries = 16;

        return nEntries;
    }
    else
    {
        return GetSystemPaletteEntries(hdc, iStartIndex, nEntries, lppe);
    }
}

/******************************Public*Routine******************************\
* bComputeLogicalToSurfaceMap
*
* Copy logical palette to surface palette translation vector to the buffer
* pointed to by pajVector.  The logical palette is specified by hpal.  The
* surface is specified by hdc.
*
* Note: The hdc may identify either a direct (display) dc or a DIB memory dc.
* If hdc is a display dc, then the surface palette is the system palette.
* If hdc is a memory dc, then the surface palette is the DIB color table.
\**************************************************************************/

static BOOL bComputeLogicalToSurfaceMap(HDC hdc, HPALETTE hpal, BYTE *pajVector)
{
    BOOL bRet = FALSE;
    HPALETTE hpalSurf;
    ULONG cEntries, cSysEntries;
    DWORD dwDcType = GetObjectType(hdc);
    LPPALETTEENTRY lppeTmp, lppeEnd;

    BYTE aj[sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 512) + (sizeof(RGBQUAD) * 256)];
    LOGPALETTE *ppal = (LOGPALETTE *) aj;
    LPPALETTEENTRY lppeSurf = &ppal->palPalEntry[0];
    LPPALETTEENTRY lppe = lppeSurf + 256;
    RGBQUAD *prgb = (RGBQUAD *) (lppe + 256);

    //
    // Determine number of colors in each palette.
    //

    cEntries = GetPaletteEntries(hpal, 0, 1, NULL);
    if ( dwDcType == OBJ_DC )
        cSysEntries = MyGetSystemPaletteEntries(hdc, 0, 1, NULL);
    else
        cSysEntries = 256;

    //
    // Get the logical palette entries.
    //

    cEntries = GetPaletteEntries(hpal, 0, cEntries, lppe);

    //
    // Get the surface palette entries.
    //

    if ( dwDcType == OBJ_DC )
    {
        cSysEntries = MyGetSystemPaletteEntries(hdc, 0, cSysEntries, lppeSurf);

        lppeTmp = lppeSurf;
        lppeEnd = lppeSurf + cSysEntries;

        for (; lppeTmp < lppeEnd; lppeTmp++)
            lppeTmp->peFlags = 0;
    }
    else
    {
        RGBQUAD *prgbTmp;

        //
        // First get RGBQUADs from DIB color table...
        //

        cSysEntries = GetDIBColorTable(hdc, 0, cSysEntries, prgb);

        //
        // ...then convert RGBQUADs into PALETTEENTRIES.
        //

        prgbTmp = prgb;
        lppeTmp = lppeSurf;
        lppeEnd = lppeSurf + cSysEntries;

        while ( lppeTmp < lppeEnd )
        {
            lppeTmp->peRed   = prgbTmp->rgbRed;
            lppeTmp->peGreen = prgbTmp->rgbGreen;
            lppeTmp->peBlue  = prgbTmp->rgbBlue;
            lppeTmp->peFlags = 0;

            lppeTmp++;
            prgbTmp++;

        }
    }

    //
    // Construct a translation vector by using GetNearestPaletteIndex to
    // map each entry in the logical palette to the surface palette.
    //

    if ( cEntries && cSysEntries )
    {
        //
        // Create a temporary logical palette that matches the surface
        // palette retrieved above.
        //

        ppal->palVersion = 0x300;
        ppal->palNumEntries = (USHORT) cSysEntries;

        if ( hpalSurf = CreatePalette(ppal) )
        {
            //
            // Translate each logical palette entry into a surface palette
            // index.
            //

            lppeTmp = lppe;
            lppeEnd = lppe + cEntries;

            for ( ; lppeTmp < lppeEnd; lppeTmp++, pajVector++)
            {
                *pajVector = (BYTE) GetNearestPaletteIndex(
                                        hpalSurf,
                                        RGB(lppeTmp->peRed,
                                            lppeTmp->peGreen,
                                            lppeTmp->peBlue)
                                        );
            }

            bRet = TRUE;

            DeleteObject(hpalSurf);
        }
        else
        {
            SS_DBGPRINT("bComputeLogicalToSurfaceMap: CreatePalette failed\n");
        }
    }
    else
    {
        SS_DBGPRINT("bComputeLogicalToSurfaceMap: failed to get pal info\n");
    }

    return bRet;
}


GLSCRNSV.CXX

/******************************Module*Header*******************************\
* Module Name: glscrnsv.c
*
* Companion file to scrnsave.c.  Hooks out any changes in functionality
* defined as GL_SCRNSAVE in scrnsave.c, and does general intialization.
*
* Copyright 1996 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include "scrnsave.h"

#include "glscrnsv.h"
#include "ssintrnl.hxx"
#include "sswindow.hxx"
#include "glscrnsv.hxx"
#include "sswproc.hxx"

static UINT (*KeyDownFunc)(int)        = NULL;

// Global ptr to screen saver instance
SCRNSAVE *gpss = NULL;

// Global strings.
#define GEN_STRING_SIZE 64
TCHAR szScreenSaverTitle[GEN_STRING_SIZE];
extern TCHAR szClassName[];  // from sswindow.cxx
LPCTSTR pszWindowClass = TEXT("WindowsScreenSaverClass");  // main class name
TCHAR szClassName[] = TEXT("ScreenSaverClass"); // child class name

// forward declarations of internal fns
static BOOL RegisterMainClass( WNDPROC wndProc, HBRUSH hbrBg, HCURSOR hCursor );
static BOOL RegisterChildClass();
static BOOL AttemptResolutionSwitch( int width, int height, ISIZE *pNewSize );

// externs
extern void InitRealScreenSave(); // scrnsave.cxx
extern LRESULT WINAPI
    RealScreenSaverProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
extern VOID UnloadPwdDLL(VOID);
extern BOOL GLScreenSaverConfigureDialog( HWND hDlg, UINT msg, WPARAM wParam,
                              LPARAM lParam ); // sswproc.cxx

#ifdef SS_INITIAL_CLEAR
static void InitialClear( PSSW *pssw );
#endif

#ifdef SS_WIN95
#define SS_WIN95_MULTIWINDOW_WORKAROUND 1
#define SS_WIN95_DESKTOP_REDRAW_FIX 1
static BOOL bPowerSaveEnabled();
#endif

/**************************************************************************\
* GLDoScreenSave
*
* Runs the screen saver in the specified mode
*
* GL version of DoScreenSave in scrnsave.c
*
* Does basic init, creates initial set of windows, and starts the message
* loop, which runs until terminated by some event.
*
\**************************************************************************/

static int
GLDoScreenSave( int winType, LPARAM lParam )
{
    MSG msg;

    // Create screen saver instance - this calls ss_Init()
    SCRNSAVE ss( winType, lParam );

    // Setup all the windows and start the message loop

    if( ss.SetupInitialWindows() )
    {
        // Send message to main window to start the drawing timer
#ifdef SS_DELAYED_START_KLUGE
        // Kluge to work around 'window-not-ready' problem in child
        // preview mode - trigger off of WM_PAINT instead
        if( ! SS_DELAY_START(winType) )
            SendMessage( ss.psswMain->hwnd, SS_WM_START, 0, 0 );
#else
        SendMessage( ss.psswMain->hwnd, SS_WM_START, 0, 0 );
#endif

        while( GetMessage( &msg, NULL, 0, 0 ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
    }

    // We're done - screen saver exiting.

    // free password-handling DLL if loaded
    UnloadPwdDLL();

    return msg.wParam;
}

/**************************************************************************\
* SCRNSAVE constructors
*
\**************************************************************************/

SCRNSAVE::SCRNSAVE( int typeArg, LPARAM lParam )
{
    type = typeArg;
    initParam = lParam;
    Init();
}

SCRNSAVE::SCRNSAVE( int typeArg )
{
    type = typeArg;
    initParam = 0;
    Init();
}

void
SCRNSAVE::Init()
{
    psswMain    = NULL;
    psswGL      = NULL;
    bResSwitch  = FALSE;
    pssc        = NULL;
    pssPal      = NULL;
    flags       = 0;
#ifdef SS_DEBUG
    bDoTiming   = type == SS_TYPE_NORMAL ? TRUE : FALSE;
#endif

    // Global ptr to the screen saver instance
    gpss = this;

    // Platform detections

    ss_QueryOSVersion();

    // Initialize randomizer

    ss_RandInit();

    // Create multi-purpose black bg brush

    hbrBg = (HBRUSH) GetStockObject( BLACK_BRUSH );

    // Call client ss's init function, to get ptr to its configuration
    // request

    if( type == SS_TYPE_CONFIG ) {
        // This case handled differently
        return;
    }

    pssc = ss_Init();
    SS_ASSERT( pssc, "SCRNSAVE constructor failure\n" );

    // Set GL config structure from pssc

    GLc.pfFlags = 0;
    GLc.hrc = 0;
    GLc.pStretch = NULL;
    switch( pssc->depthType ) {
        case SS_DEPTH16 :
            GLc.pfFlags |= SS_DEPTH16_BIT;
            break;
        case SS_DEPTH32 :
            GLc.pfFlags |= SS_DEPTH32_BIT;
            break;
    }
    if( pssc->bDoubleBuf )
        GLc.pfFlags |= SS_DOUBLEBUF_BIT;
    if( pssc->bStretch )
        GLc.pStretch = &pssc->stretchInfo;

}

/**************************************************************************\
* SetupInitialWindows
*
* Create / Configure all required windows.
*
\**************************************************************************/

BOOL
SCRNSAVE::SetupInitialWindows()
{
    // Create the windows

    if( ! CreateInitialWindows() ) {
        SS_WARNING( "SCRNSAVE:: Couldn't create windows\n" );
        return FALSE;
    }

    // Initial window clear

#ifdef SS_INITIAL_MAIN_WINDOW_CLEAR
    if( type == SS_TYPE_PREVIEW ) {
    // Make sure the screen is cleared to black before we start drawing
    // anything, as sometimes the background WM_PAINT doesn't get to us right
    // away.  This is only a problem in preview mode
       psswMain->GdiClear();
    }
#endif

    // Configure and Init the windows, if applicable

#ifdef SS_DELAYED_START_KLUGE
    // delay start for some configurations
    if( ! SS_DELAY_START(type) ) {
        SendMessage( psswMain->hwnd, SS_WM_INITGL, 0, 0 );
    }
#else
    SendMessage( psswMain->hwnd, SS_WM_INITGL, 0, 0 );
#endif // SS_DELAYED_START_KLUGE

    return TRUE;
}

/**************************************************************************\
* CreateInitialWindows
*
* Create the intitial set of windows.
*
\**************************************************************************/

BOOL
SCRNSAVE::CreateInitialWindows()
{
    PSSW    pssw;

    if( !pssc )
        return FALSE;

    // Handle any request for resolution change

#define SS_RESOLUTION_SWITCH 1
#ifdef SS_RESOLUTION_SWITCH
    if( pssc->bStretch && (type == SS_TYPE_FULLSCREEN)
        && ss_fOnWin95() ) {

        STRETCH_INFO *pStretch = &pssc->stretchInfo;
        ISIZE newSize;

        // Try and change screen resolution to match stretch size
        bResSwitch = AttemptResolutionSwitch( pStretch->baseWidth,
                                               pStretch->baseHeight, &newSize );
        // Is stretching still necessary if resolution changed ?
        if( bResSwitch ) {
            if( (newSize.width == pStretch->baseWidth) &&
                (newSize.height == pStretch->baseHeight) )
                // exact match, no stretching now necessary
                pssc->bStretch = FALSE;
        }
    }
#endif

    // Currently the bitmaps used in stretch mode don't support palette
    // messages, so disable any stretching when in PREVIEW mode (where we
    // need to support palette interaction).
    // actually this is only a consideration in 8-bit mode...

    if( (type == SS_TYPE_PREVIEW) && pssc->bStretch )
        pssc->bStretch = FALSE;

    // Create the main ss window

    if( ! CreateMainWindow() )
        return FALSE;

#ifdef SS_INITIAL_CLEAR
    // If main window is transparent, can do an initial clear here before
    // any other windows are created or palettes modified
    // This is bogus on NT, as system switches to secure desktop when screen
    // saver kicks in automatically.
    InitialClear( pssw );
#endif

    // For now, simple window environment is described by pssc, so things
    // like bFloater and bStretch are mutually exclusive.

    SS_GL_CONFIG *pGLc = &gpss->GLc;

    if( pssc->bFloater ) {

        if( !(pssw = CreateChildWindow( &pssc->floaterInfo )) )
            return FALSE;

        pssw->pGLc = pGLc;
        psswGL = pssw; // support old-style
    } else {
        psswMain->pGLc = pGLc;
        psswGL = psswMain; // support old-style
    }

    return TRUE;
}

/**************************************************************************\
* NormalWindowScreenSaverProc
*
* Highest level window proc, used only in normal window (/w) mode.
*
\**************************************************************************/

LRESULT WINAPI
NormalWindowScreenSaverProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
   switch( uMsg )
   {
      case WM_SETTEXT:
         // RealScreenSaverProc won't allow this - bypass it
         return ScreenSaverProc( hWnd, uMsg, wParam, lParam );

      case WM_CHAR:
            if( KeyDownFunc ) {
                int key = wParam;
                (*KeyDownFunc)(key);
            }
            break;

      case WM_KEYDOWN:
            if( wParam == VK_ESCAPE ) {
                PostMessage( hWnd, WM_CLOSE, 0, 0l );
                break;
            } else if( KeyDownFunc ) {
                (*KeyDownFunc)(wParam);
                return 0;
            }
            return 0; // ??

      default:
         break;
   }

   return RealScreenSaverProc( hWnd, uMsg, wParam, lParam );
}

/**************************************************************************\
* DoScreenSave
*
* Hooked out version of DoScreenSave in standard scrnsave.c
*
\**************************************************************************/

int
DoScreenSave( HWND hwndParent )
{
    return GLDoScreenSave( hwndParent ? SS_TYPE_PREVIEW : SS_TYPE_FULLSCREEN,
                           (LPARAM) hwndParent );
}

/**************************************************************************\
* DoWindowedScreenSave
*
* Called when screen saver invoked with /w (window mode) parameter
*
\**************************************************************************/

int
DoWindowedScreenSave( LPCTSTR szArgs )
{
    return GLDoScreenSave( SS_TYPE_NORMAL, (LPARAM) szArgs );
}

/**************************************************************************\
* DoConfigBox
*
* Hooked out version of DoConfigBox in standard scrnsave.c
*
\**************************************************************************/

int
DoConfigBox( HWND hwndParent )
{
    // let the consumer register any special controls for the dialog
    if( !RegisterDialogClasses( hMainInstance ) )
        return FALSE;

    // Create screen saver instance
    SCRNSAVE ss( SS_TYPE_CONFIG );

    int retVal = DialogBox( hMainInstance,
                           MAKEINTRESOURCE( DLG_SCRNSAVECONFIGURE ),
                           hwndParent, (DLGPROC)GLScreenSaverConfigureDialog );

    return retVal;
}

/**************************************************************************\
* CreateMainWindow
*
* Creates main screen saver window based on the window type
*
\**************************************************************************/

BOOL
SCRNSAVE::CreateMainWindow()
{
    WNDPROC  wndProc;
    int      nCx, nCy;
    ISIZE    size;
    UINT     uStyle = 0;
    UINT     uExStyle = 0;
    IPOINT2D pos;
    LPCTSTR  pszWindowTitle;
    HCURSOR  hCursor = NULL;
    HBRUSH   hbrBgMain;
    PSSW     pssw;
    HWND    hwndParent = NULL;

    wndProc = RealScreenSaverProc;

    switch( type ) {
        case SS_TYPE_FULLSCREEN:
          {
            HWND hOther;

            nCx = GetSystemMetrics( SM_CXSCREEN );
            nCy = GetSystemMetrics( SM_CYSCREEN );
#ifdef SS_DEBUG
//#define SS_FULLSCREEN_DEBUG 1
#ifdef SS_FULLSCREEN_DEBUG
            // Reduce window size so we can see debugger
            nCx /= 2;
            nCy /= 2;
#endif
#endif
            uStyle = WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
            uExStyle = WS_EX_TOPMOST;

            pszWindowTitle = TEXT("Screen Saver"); // MUST differ from preview

            // if there is another NORMAL screen save instance, switch to it
            hOther = FindWindow( szClassName, pszWindowTitle );

            if( hOther && IsWindow( hOther ) )
            {
               SetForegroundWindow( hOther );
               return FALSE;
            }

            InitRealScreenSave();
          }
          break;

        case SS_TYPE_PREVIEW:
          {
            RECT rcParent;

            hwndParent = (HWND) initParam;

            GetClientRect( hwndParent, &rcParent );

            fChildPreview = TRUE;
            nCx = rcParent.right;
            nCy = rcParent.bottom;
            uStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN;

            pszWindowTitle = TEXT("Preview");   // MUST differ from full screen
          }
          break;

        case SS_TYPE_NORMAL:

          {
            // We set fChildPreview even when we're running in a normal window,
            // as this flag is used in scrnsave.c to differentiate from full
            // screen.
            fChildPreview = TRUE;

            // init size to half screen by default
            nCx = GetSystemMetrics( SM_CXSCREEN ) >> 1;
            nCy = GetSystemMetrics( SM_CYSCREEN ) >> 1;

            if( initParam ) {
                // get size of window from args
                LPCTSTR szArgs = (LPCTSTR) initParam;
            }

            LoadString(hMainInstance, IDS_DESCRIPTION, szScreenSaverTitle,
               sizeof(szScreenSaverTitle) / sizeof(TCHAR));
            pszWindowTitle = szScreenSaverTitle; // MUST differ from preview

            uStyle = WS_VISIBLE | WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN |
                   WS_CLIPSIBLINGS;

            hCursor = LoadCursor( NULL, IDC_ARROW );

            // Wrap RealScreenSaverProc
            wndProc = NormalWindowScreenSaverProc;
          }
          break;

        default:
          break;
    }

    size.width = nCx;
    size.height = nCy;
#ifdef SS_INITIAL_CLEAR
    hbrBgMain = NULL;
#else
    hbrBgMain = hbrBg;
#endif

    pos.x = pos.y = 0;

    // Create SSW window wrapper

    pssw = new SSW( NULL,       // parent
                    &size,
                    &pos,
                    FALSE,      // bMotion
                    NULL       // ChildSizeFunc
                  );
    if( !pssw )
        return FALSE;

    // Initialize the window class and create the window

    if( !RegisterMainClass
        (
            wndProc,
            hbrBgMain,
            hCursor
        ) ||

        !pssw->CreateSSWindow
        (
            hMainInstance,
            uStyle,
            uExStyle,
            szScreenSaverTitle,
            wndProc,
            pszWindowClass,
            hwndParent     // mf: ! hwndParentOverride
        ) )
    {
        delete pssw;
        return FALSE;
    }

    if( type != SS_TYPE_PREVIEW )
#ifndef SS_DEBUG
        SetForegroundWindow( pssw->hwnd );
#else
    {
        if( !SetForegroundWindow( pssw->hwnd ) )
            SS_DBGPRINT( "Main_Proc: SetForegroundWindow failed\n" );
    }
#endif

    // Always configure the main window for gdi
    pssw->ConfigureForGdi();

    psswMain = pssw;
    return TRUE;
}

/**************************************************************************\
* CreateChildWindow
*
* Creates a child window of the parent window
*
* This is a kind of wrapper-constructor
\**************************************************************************/

PSSW
SCRNSAVE::CreateChildWindow( FLOATER_INFO *pFloater )
{
    pFloater->bSubWindow = FALSE; // default is no logical subwin's

#ifdef SS_WIN95_MULTIWINDOW_WORKAROUND
    if( ss_fOnWin95() && ss_fFullScreenMode() && bPowerSaveEnabled() ) {
        // Make the floater a logical sub-window of the main window, not a
        // separate window.  This works around a USER bug where the power
        // save feature was not getting activated for multi-windowed screen
        // savers
        SS_DBGINFO( "SCRNSAVE::CreateChildWindow : Using logical sub-windows\n" );
        pFloater->bSubWindow = TRUE;
    }
#endif // SS_WIN95_MULTIWINDOW_WORKAROUND

    // Register child window class
    // This only has to be done once, since so far, all child window
    // classes are the same
    if( !pFloater->bSubWindow && !RegisterChildClass() )
        return NULL;

    return CreateChildWindow( psswMain, pFloater );
}

PSSW
SCRNSAVE::CreateChildWindow( PSSW psswParent, FLOATER_INFO *pFloater )
{
    UINT     uStyle = 0;
    UINT     uExStyle = 0;
    PSSW     pssw;

    uStyle = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

    // Size and position are NULL here, as the SSW will call the size
    // function callback in pFloater to get these and other values

    // Create the SSW window wrapper

    pssw = new SSW( psswParent,      // parent
                    NULL,            // size
                    NULL,            // position
                    pFloater->bMotion,
                    pFloater->ChildSizeFunc
                  );
    if( !pssw )
        return NULL;

    if( pFloater->bSubWindow )
        // Don't need to create win32 window
        return pssw;

    // Create a window

    if( !pssw->CreateSSWindow
        (
            hMainInstance,
            uStyle,
            0,                      // uExStyle
            szScreenSaverTitle ,
            SS_ScreenSaverProc,
            szClassName,
            NULL                    // hwndParentOverride
        ) )
    {
        delete pssw;
        return NULL;
    }

    return pssw;
}

/**************************************************************************\
* RegisterMainClass
*
* Registers class of the main SS window
\**************************************************************************/

static BOOL
RegisterMainClass( WNDPROC wndProc, HBRUSH hbrBg, HCURSOR hCursor )
{
    WNDCLASS cls;

    cls.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_OWNDC;
    cls.lpfnWndProc = wndProc;
    cls.cbClsExtra = 0;
    cls.cbWndExtra = 0;
    cls.hInstance = hMainInstance;
    cls.hIcon = LoadIcon( hMainInstance, MAKEINTATOM( ID_APP ) );
    cls.hCursor = hCursor;
    cls.hbrBackground = hbrBg;
    cls.lpszMenuName = (LPTSTR)NULL;
    cls.lpszClassName = (LPTSTR)pszWindowClass;
    return RegisterClass(&cls);
}

/**************************************************************************\
* RegisterChildClass
*
* Registers class of a standard child window
\**************************************************************************/

static BOOL
RegisterChildClass()
{
    static BOOL bRegistered = FALSE;

    if( bRegistered )
        return TRUE;

    WNDCLASS cls;

    cls.style = CS_VREDRAW | CS_HREDRAW;
    cls.lpfnWndProc = SS_ScreenSaverProc;
    cls.cbClsExtra = 0;
    cls.cbWndExtra = 0;
    cls.hInstance = hMainInstance;
    cls.hIcon = NULL;
    cls.hCursor = NULL;
    cls.hbrBackground = NULL;
    cls.lpszMenuName = (LPTSTR)NULL;
    cls.lpszClassName = (LPTSTR)szClassName;

    if( !RegisterClass(&cls) )
        return FALSE;

    // success
    bRegistered = TRUE;
    return TRUE;
}

/******************************Public*Routine******************************\
* AttemptResolutionSwitch
*
* Try doing resolution switching to match or get close to the desired size.
*
\**************************************************************************/

static BOOL
AttemptResolutionSwitch( int width, int height, ISIZE *pNewSize )
{
    BOOL bChanged = FALSE;

    // Try doing resolution switching to match or get close to the
    // desired width and height

    // Try switching to requested size
    if( 0 ) {
        // for now force failure of user request and try standard 640x480
        bChanged = TRUE;
    } else {
        // Can't switch to requested size, try for best match
        width = 640;
        height = 480;
        // If screen already this size or less, leave be
        if( (GetSystemMetrics( SM_CXSCREEN ) <= width) &&
            (GetSystemMetrics( SM_CYSCREEN ) <= height) )
            return FALSE;

        // use this when trying for best match
        // ss_QueryDisplaySettings();

        if( ss_ChangeDisplaySettings( width, height, 0 ) )
            bChanged = TRUE;
    }

    if( bChanged ) {
        pNewSize->width = width;
        pNewSize->height = height;
    }
    return bChanged;
}

#ifdef SS_INITIAL_CLEAR
static void
InitialClear( PSSW *pssw )
{
    ss_GdiRectWipeClear( pssw->hwnd, pssw->size.width, pssw->size.height );
}
#endif // SS_INITIAL_CLEAR

/**************************************************************************\
* CloseWindows
*
* Close down any open windows.
*
* This sends a WM_CLOSE message to the top-level window if it is still open.  If
* the window has any children, they are also closed.  For each window, the
* SSW destructor is called.
\**************************************************************************/

void
SCRNSAVE::CloseWindows()
{
    if( psswMain ) {
        if( psswMain->bOwnWindow )
            DestroyWindow( psswMain->hwnd );
        else
            delete psswMain;
    }
}

/**************************************************************************\
* SCRNSAVE destructor
*
\**************************************************************************/

SCRNSAVE::~SCRNSAVE()
{
    // Close any open windows (there might be some open if errors occurred)
    CloseWindows();

#ifdef SS_WIN95_DESKTOP_REDRAW_FIX
    // On win95, sometimes the desktop is drawn incorrectly after we have
    // taken over the palette
    if( flags & SS_PALETTE_TAKEOVER ) {
        SS_DBGINFO( "SCRNSAVE destructor: Redrawing desktop\n" );
        ss_RedrawDesktop();
    }
#endif

    if( bResSwitch ) {
        // Restore previous display settings
        ChangeDisplaySettings(NULL, CDS_FULLSCREEN);
    }

    gpss = NULL;
}

#ifdef SS_WIN95_MULTIWINDOW_WORKAROUND

// Macro to convert string to integer
#ifdef UNICODE
#define sztoi( pszStr ) _wtoi( (wchar_t *) (pszStr) )
#else
#define sztoi( pszStr ) atoi( (char *) (pszStr) )
#endif

/**************************************************************************\
* GetRegValueBool
*
* Get registry value and convert to BOOL
\**************************************************************************/

#define SS_REG_BUF_SIZE 30

static BOOL
bGetRegValueBool( HKEY hKey, LPTSTR szValue, BOOL *pbEnabled )
{
    TCHAR szBuf[SS_REG_BUF_SIZE];
    DWORD dataSize = SS_REG_BUF_SIZE;
    DWORD dwType;

    // Get the value

    if( ! ( RegQueryValueEx(
              hKey,
              szValue,
              (LPDWORD) NULL,
              (LPDWORD) &dwType,
              (LPBYTE) szBuf,
              (LPDWORD) &dataSize ) == ERROR_SUCCESS
          )
      )
        return FALSE;

    // Convert value to BOOL (the expected type is REG_SZ)

    switch( dwType ) {
        case REG_SZ :
            // Convert string to BOOL
            *pbEnabled = (BOOL) sztoi( szBuf );
            break;

        case REG_DWORD :
            // Handle numerical value
            *pbEnabled = (BOOL) *( (LPDWORD) szBuf );
            break;

        default:
            SS_WARNING( "GetRegValueBool : Unexpected type\n" );
            return FALSE;
    }

    return TRUE;
}

/**************************************************************************\
* bPowerSaveEnabled
*
* Detect if power saving is enabled by checking registry values.
*
\**************************************************************************/

static BOOL
bPowerSaveEnabled()
{
    BOOL bLowPowerActive;
    BOOL bPowerOffActive;
    HKEY hKey;

    // Open desktop key

    if( ! ( RegOpenKeyEx(
              HKEY_CURRENT_USER,
              (LPCTSTR) TEXT( "Control Panel\\desktop"),
              0,
              KEY_QUERY_VALUE,
              &hKey  ) == ERROR_SUCCESS
          )
      )
    {
        return FALSE;  // Can't open desktop key
    }

    // Check values - power saving is enabled if either ScreenSaveLowPowerActive
    // or ScreenSavePowerOffActive is enabled.

    if( (
          bGetRegValueBool(
            hKey,
            (LPTSTR) TEXT("ScreenSaveLowPowerActive"),
            &bLowPowerActive )
          &&
          bLowPowerActive
        )
        ||
        (
          bGetRegValueBool(
            hKey,
            (LPTSTR) TEXT("ScreenSavePowerOffActive"),
            &bPowerOffActive )
          &&
          bPowerOffActive
        )
      )
    {
        return TRUE;
    }

    return FALSE;
}
#endif // SS_WIN95_MULTIWINDOW_WORKAROUND


GLSCRNSV.H

/******************************Module*Header*******************************\
* Module Name: glscrnsv.h
*
* Include for modified scrnsave.c
*
* Copyright 1996 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __glscrnsv_h__
#define __glscrnsv_h__

#include "sscommon.hxx"
#include "sswproc.hxx"

#ifdef __cplusplus
extern "C" {
#endif

extern int DoScreenSave( HWND hwndParent );
extern int DoWindowedScreenSave( LPCTSTR szArgs );
extern int DoConfigBox( HWND hwndParent );

#ifdef __cplusplus
}
#endif

#endif // __glscrnsv_h__


MATERIAL.C

/******************************Module*Header*******************************\
* Module Name: material.c
*
* Material selection functions.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include <windows.h>
#include <GL/gl.h>

#include "sscommon.h"

//#define NUM_TEA_MATERIALS 24

#define NUM_GOOD_MATERIALS 16  // 'good' ones among the 24 tea materials

//#define NUM_TEX_MATERIALS 4  // materials for texture

int goodMaterials[NUM_GOOD_MATERIALS] = {
        EMERALD, JADE, PEARL, RUBY, TURQUOISE, BRASS, BRONZE,
        COPPER, GOLD, SILVER, CYAN_PLASTIC, WHITE_PLASTIC, YELLOW_PLASTIC,
        CYAN_RUBBER, GREEN_RUBBER, WHITE_RUBBER };

/*  materials:  emerald, jade, obsidian, pearl, ruby, turquoise
 *              brass, bronze, chrome, copper, gold, silver
 *              black, cyan, green, red, white, yellow plastic
 *              black, cyan, green, red, white, yellow rubber

    description: ambient(RGB), diffuse(RGB), specular(RGB), shininess
 *
 */
// 'tea' materials, from aux teapots program
static GLfloat teaMaterialData[NUM_TEA_MATERIALS][10] =
{
     0.0215f, 0.1745f, 0.0215f,
        0.07568f, 0.61424f, 0.07568f, 0.633f, 0.727811f, 0.633f, 0.6f,
     0.135f, 0.2225f, 0.1575f,
        0.54f, 0.89f, 0.63f, 0.316228f, 0.316228f, 0.316228f, 0.1f,
     0.05375f, 0.05f, 0.06625f, // XX
        0.18275f, 0.17f, 0.22525f, 0.332741f, 0.328634f, 0.346435f, 0.3f,
     0.25f, 0.20725f, 0.20725f,
        1.0f, 0.829f, 0.829f, 0.296648f, 0.296648f, 0.296648f, 0.088f,
     0.1745f, 0.01175f, 0.01175f,
        0.61424f, 0.04136f, 0.04136f, 0.727811f, 0.626959f, 0.626959f, 0.6f,
     0.1f, 0.18725f, 0.1745f,
        0.396f, 0.74151f, 0.69102f, 0.297254f, 0.30829f, 0.306678f, 0.1f,
     0.329412f, 0.223529f, 0.027451f,
        0.780392f, 0.568627f, 0.113725f, 0.992157f, 0.941176f, 0.807843f,
        0.21794872f,
     0.2125f, 0.1275f, 0.054f,
        0.714f, 0.4284f, 0.18144f, 0.393548f, 0.271906f, 0.166721f, 0.2f,
     0.25f, 0.25f, 0.25f,  // XX
        0.4f, 0.4f, 0.4f, 0.774597f, 0.774597f, 0.774597f, 0.6f,
     0.19125f, 0.0735f, 0.0225f,
        0.7038f, 0.27048f, 0.0828f, 0.256777f, 0.137622f, 0.086014f, 0.1f,
     0.24725f, 0.1995f, 0.0745f,
        0.75164f, 0.60648f, 0.22648f, 0.628281f, 0.555802f, 0.366065f, 0.4f,
     0.19225f, 0.19225f, 0.19225f,
        0.50754f, 0.50754f, 0.50754f, 0.508273f, 0.508273f, 0.508273f, 0.4f,
     0.0f, 0.0f, 0.0f, 0.01f, 0.01f, 0.01f,
        0.50f, 0.50f, 0.50f, .25f,
     0.0f, 0.1f, 0.06f, 0.0f, 0.50980392f, 0.50980392f,
        0.50196078f, 0.50196078f, 0.50196078f, .25f,
     0.0f, 0.0f, 0.0f,
        0.1f, 0.35f, 0.1f, 0.45f, 0.55f, 0.45f, .25f,
     0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, // XX
        0.7f, 0.6f, 0.6f, .25f,
     0.0f, 0.0f, 0.0f, 0.55f, 0.55f, 0.55f,
        0.70f, 0.70f, 0.70f, .25f,
     0.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.0f,
        0.60f, 0.60f, 0.50f, .25f,
     0.02f, 0.02f, 0.02f, 0.01f, 0.01f, 0.01f, // XX
        0.4f, 0.4f, 0.4f, .078125f,
     0.0f, 0.05f, 0.05f, 0.4f, 0.5f, 0.5f,
        0.04f, 0.7f, 0.7f, .078125f,
     0.0f, 0.05f, 0.0f, 0.4f, 0.5f, 0.4f,
        0.04f, 0.7f, 0.04f, .078125f,
     0.05f, 0.0f, 0.0f, 0.5f, 0.4f, 0.4f,
        0.7f, 0.04f, 0.04f, .078125f,
     0.05f, 0.05f, 0.05f, 0.5f, 0.5f, 0.5f,
        0.7f, 0.7f, 0.7f, .078125f,
     0.05f, 0.05f, 0.0f, 0.5f, 0.5f, 0.4f,
        0.7f, 0.7f, 0.04f, .078125f
};

// generally white materials for texturing

static GLfloat texMaterialData[NUM_TEX_MATERIALS][10] = {
// bright white
     0.2f, 0.2f, 0.2f,
        1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f,
// less bright white
     0.2f, 0.2f, 0.2f,
        0.9f, 0.9f, 0.9f, 0.9f, 0.9f, 0.9f, 0.5f,
// warmish white
     0.3f, 0.2f, 0.2f,
        1.0f, 0.9f, 0.8f, 1.0f, 0.9f, 0.8f, 0.5f,
// coolish white
     0.2f, 0.2f, 0.3f,
        0.8f, 0.9f, 1.0f, 0.8f, 0.9f, 1.0f, 0.5f
};

MATERIAL Material[NUM_TEA_MATERIALS + NUM_TEX_MATERIALS];

// pure black material

MATERIAL ss_BlackMat = {0.0f};

/**************************************************************************\
* InitMaterials
*
* Initialize MATERIAL structures with data
* - Set alpha to 0.5 for all materials
*
\**************************************************************************/

static void
InitMaterials( MATERIAL *pm, float *pd, int count )
{
    int i;

    for( i = 0; i < count; i++, pm++ ) {
        pm->ka.r = *pd++;
        pm->ka.g = *pd++;
        pm->ka.b = *pd++;
        pm->ka.a = 0.5f;
        pm->kd.r = *pd++;
        pm->kd.g = *pd++;
        pm->kd.b = *pd++;
        pm->kd.a = 0.5f;
        pm->ks.r = *pd++;
        pm->ks.g = *pd++;
        pm->ks.b = *pd++;
        pm->ks.a = 0.5f;
        pm->specExp = *pd++;
    }
}

/**************************************************************************\
* ss_InitMaterials
*
* Initialize all materials
*
\**************************************************************************/

void ss_InitMaterials()
{
    ss_InitTeaMaterials();
    ss_InitTexMaterials();
}

/**************************************************************************\
* ss_InitTeaMaterials
*
* Initialize Material structure with data in TeaMaterialData
*
\**************************************************************************/

void ss_InitTeaMaterials()
{
    InitMaterials( Material, teaMaterialData[0], NUM_TEA_MATERIALS );
}

/**************************************************************************\
* ss_InitTexMaterials
*
* Initialize Material structure with data in TexMaterialData (tex materials
* follow the tea materials in Material)
*
\**************************************************************************/

void ss_InitTexMaterials()
{
    InitMaterials( Material+NUM_TEA_MATERIALS, texMaterialData[0],
                   NUM_TEX_MATERIALS );
}

/**************************************************************************\
* ss_SetMaterial
*
* Set specified material with MATERIAL structure
*
\**************************************************************************/

void ss_SetMaterial( MATERIAL *pMat )
{
    glMaterialfv( GL_FRONT, GL_AMBIENT, (GLfloat*) &pMat->ka );
    glMaterialfv( GL_BACK, GL_AMBIENT, (GLfloat*) &pMat->ka );
    glMaterialfv( GL_FRONT, GL_DIFFUSE, (GLfloat *) &pMat->kd );
    glMaterialfv( GL_BACK, GL_DIFFUSE, (GLfloat *) &pMat->kd );
    glMaterialfv( GL_FRONT, GL_SPECULAR, (GLfloat *) &pMat->ks );
    glMaterialfv( GL_BACK, GL_SPECULAR, (GLfloat *) &pMat->ks );
    glMaterialf ( GL_FRONT, GL_SHININESS, pMat->specExp*128.0f);
    glMaterialf ( GL_BACK, GL_SHININESS, pMat->specExp*128.0f);
}

/**************************************************************************\
* ss_SetMaterialIndex
*
* Set material using its index name
*
\**************************************************************************/

void ss_SetMaterialIndex( int index )
{
    if( (index < 0) || (index >= (NUM_TEA_MATERIALS+NUM_TEX_MATERIALS)) )
        return;
    ss_SetMaterial( &Material[index] );
}

/**************************************************************************\
* ss_CreateMaterialGradient
*
* Given 2 materials and a count, create an incremental material, such
* that adding it 'count' times to the first material will generate the
* 2nd material.
*
\**************************************************************************/
void
ss_CreateMaterialGradient( MATERIAL *matInc, MATERIAL *startMat,
                        MATERIAL *endMat, int transCount )
{
    FLOAT fTransCount;

    if( !transCount ) {
        *matInc = *endMat;
        return;
    }

    /*
     * Add fudge factor to avoid round-off error, which could result in
     * invalid material values.
     */
#define FMAT_GRAD_FUDGE 1.0001f
    fTransCount = (FLOAT) transCount * FMAT_GRAD_FUDGE;

    matInc->ka.r = (endMat->ka.r - startMat->ka.r) / fTransCount;
    matInc->ka.g = (endMat->ka.g - startMat->ka.g) / fTransCount;
    matInc->ka.b = (endMat->ka.b - startMat->ka.b) / fTransCount;
    matInc->ks.r = (endMat->ks.r - startMat->ks.r) / fTransCount;
    matInc->ks.g = (endMat->ks.g - startMat->ks.g) / fTransCount;
    matInc->ks.b = (endMat->ks.b - startMat->ks.b) / fTransCount;
    matInc->kd.r = (endMat->kd.r - startMat->kd.r) / fTransCount;
    matInc->kd.g = (endMat->kd.g - startMat->kd.g) / fTransCount;
    matInc->kd.b = (endMat->kd.b - startMat->kd.b) / fTransCount;
    matInc->specExp = (endMat->specExp - startMat->specExp) / fTransCount;
}

/**************************************************************************\
* ss_TransitionMaterial
*
* Given a material, add to it the incremental material, and set the
* result as the current material
*
\**************************************************************************/
void
ss_TransitionMaterial( MATERIAL *transMat, MATERIAL *transMatInc )
{
    transMat->ka.r += transMatInc->ka.r;
    transMat->ka.g += transMatInc->ka.g;
    transMat->ka.b += transMatInc->ka.b;
    transMat->kd.r += transMatInc->kd.r;
    transMat->kd.g += transMatInc->kd.g;
    transMat->kd.b += transMatInc->kd.b;
    transMat->ks.r += transMatInc->ks.r;
    transMat->ks.g += transMatInc->ks.g;
    transMat->ks.b += transMatInc->ks.b;
    transMat->specExp += transMatInc->specExp;

    ss_SetMaterial( transMat );
}

/**************************************************************************\
* ss_RandomTeaMaterial
*
* Select a random TeaMaterial
* Return ptr to material
*
\**************************************************************************/
MATERIAL *ss_RandomTeaMaterial( BOOL bSet )
{
    int index;
    MATERIAL *pMat;

    index =  goodMaterials[ ss_iRand(NUM_GOOD_MATERIALS) ];
    pMat = &Material[ index ];
    if( bSet )
        ss_SetMaterial( pMat );
    return pMat;
}

/**************************************************************************\
* ss_RandomTeaMaterialIndex
*
* Select a random TeaMaterial
* Return material index
*
\**************************************************************************/
int ss_RandomTeaMaterialIndex( BOOL bSet )
{
    int index;

    index =  goodMaterials[ ss_iRand(NUM_GOOD_MATERIALS) ];
    if( bSet )
        ss_SetMaterial( &Material[ index ] );
    return index;
}

/**************************************************************************\
* ss_RandomTexMaterial
*
* Select a random TexMaterial
* Return ptr to material
*
\**************************************************************************/
MATERIAL *ss_RandomTexMaterial( BOOL bSet )
{
    int index;
    MATERIAL *pMat;

    index =  NUM_TEA_MATERIALS + ss_iRand(NUM_TEX_MATERIALS);
    pMat = &Material[ index ];
    if( bSet )
        ss_SetMaterial( pMat );
    return pMat;
}

/**************************************************************************\
* ss_RandomTexMaterialIndex
*
* Select a random TexMaterial
*
\**************************************************************************/
int ss_RandomTexMaterialIndex( BOOL bSet )
{
    int index;

    index =  NUM_TEA_MATERIALS + ss_iRand(NUM_TEX_MATERIALS);
    if( bSet )
        ss_SetMaterial( &Material[ index ] );
    return index;
}


MATH.C

/******************************Module*Header*******************************\
* Module Name: math.c
*
* Misc. useful math utility functions.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
#include <GL\gl.h>
#include <math.h>
#include "sscommon.h"

#define ZERO_EPS    0.00000001

POINT3D ss_ptZero = {0.0f, 0.0f, 0.0f};

void ss_xformPoint(POINT3D *ptOut, POINT3D *ptIn, MATRIX *mat)
{
    double x, y, z;

    x = (ptIn->x * mat->M[0][0]) + (ptIn->y * mat->M[0][1]) +
        (ptIn->z * mat->M[0][2]) + mat->M[0][3];

    y = (ptIn->x * mat->M[1][0]) + (ptIn->y * mat->M[1][1]) +
        (ptIn->z * mat->M[1][2]) + mat->M[1][3];

    z = (ptIn->x * mat->M[2][0]) + (ptIn->y * mat->M[2][1]) +
        (ptIn->z * mat->M[2][2]) + mat->M[2][3];

    ptOut->x = (float) x;
    ptOut->y = (float) y;
    ptOut->z = (float) z;
}

void ss_xformNorm(POINT3D *ptOut, POINT3D *ptIn, MATRIX *mat)
{
    double x, y, z;
    double len;

    x = (ptIn->x * mat->M[0][0]) + (ptIn->y * mat->M[0][1]) +
        (ptIn->z * mat->M[0][2]);

    y = (ptIn->x * mat->M[1][0]) + (ptIn->y * mat->M[1][1]) +
        (ptIn->z * mat->M[1][2]);

    z = (ptIn->x * mat->M[2][0]) + (ptIn->y * mat->M[2][1]) +
        (ptIn->z * mat->M[2][2]);

    len = (x * x) + (y * y) + (z * z);
    if (len >= ZERO_EPS)
        len = 1.0 / sqrt(len);
    else
        len = 1.0;

    ptOut->x = (float) (x * len);
    ptOut->y = (float) (y * len);
    ptOut->z = (float) (z * len);
    return;
}

void ss_matrixIdent(MATRIX *mat)
{
    mat->M[0][0] = 1.0f; mat->M[0][1] = 0.0f;
    mat->M[0][2] = 0.0f; mat->M[0][3] = 0.0f;

    mat->M[1][0] = 0.0f; mat->M[1][1] = 1.0f;
    mat->M[1][2] = 0.0f; mat->M[1][3] = 0.0f;

    mat->M[2][0] = 0.0f; mat->M[2][1] = 0.0f;
    mat->M[2][2] = 1.0f; mat->M[2][3] = 0.0f;

    mat->M[3][0] = 0.0f; mat->M[3][1] = 0.0f;
    mat->M[3][2] = 0.0f; mat->M[3][3] = 1.0f;
}

void ss_matrixRotate(MATRIX *m, double xTheta, double yTheta, double zTheta)
{
    float xScale, yScale, zScale;
    float sinX, cosX;
    float sinY, cosY;
    float sinZ, cosZ;

    xScale = m->M[0][0];
    yScale = m->M[1][1];
    zScale = m->M[2][2];
    sinX = (float) sin(xTheta);
    cosX = (float) cos(xTheta);
    sinY = (float) sin(yTheta);
    cosY = (float) cos(yTheta);
    sinZ = (float) sin(zTheta);
    cosZ = (float) cos(zTheta);

    m->M[0][0] = (float) ((cosZ * cosY) * xScale);
    m->M[0][1] = (float) ((cosZ * -sinY * -sinX + sinZ * cosX) * yScale);
    m->M[0][2] = (float) ((cosZ * -sinY * cosX + sinZ * sinX) * zScale);

    m->M[1][0] = (float) (-sinZ * cosY * xScale);
    m->M[1][1] = (float) ((-sinZ * -sinY * -sinX + cosZ * cosX) * yScale);
    m->M[1][2] = (float) ((-sinZ * -sinY * cosX + cosZ * sinX) * zScale);

    m->M[2][0] = (float) (sinY * xScale);
    m->M[2][1] = (float) (cosY * -sinX * yScale);
    m->M[2][2] = (float) (cosY * cosX * zScale);
}

void ss_matrixTranslate(MATRIX *m, double xTrans, double yTrans,
                     double zTrans)
{
    m->M[0][3] = (float) xTrans;
    m->M[1][3] = (float) yTrans;
    m->M[2][3] = (float) zTrans;
}

void ss_matrixMult( MATRIX *m1, MATRIX *m2, MATRIX *m3 )
{
    int i, j;

    for( j = 0; j < 4; j ++ ) {
    for( i = 0; i < 4; i ++ ) {
    m1->M[j][i] = m2->M[j][0] * m3->M[0][i] +
  m2->M[j][1] * m3->M[1][i] +
  m2->M[j][2] * m3->M[2][i] +
  m2->M[j][3] * m3->M[3][i];
}
    }
}

void ss_calcNorm(POINT3D *norm, POINT3D *p1, POINT3D *p2, POINT3D *p3)
{
    float crossX, crossY, crossZ;
    float abX, abY, abZ;
    float acX, acY, acZ;
    float sqrLength;
    float invLength;

    abX = p2->x - p1->x;       // calculate p2 - p1
    abY = p2->y - p1->y;
    abZ = p2->z - p1->z;

    acX = p3->x - p1->x;       // calculate p3 - p1
    acY = p3->y - p1->y;
    acZ = p3->z - p1->z;

    crossX = (abY * acZ) - (abZ * acY);    // get cross product
    crossY = (abZ * acX) - (abX * acZ);    // (p2 - p1) X (p3 - p1)
    crossZ = (abX * acY) - (abY * acX);

    sqrLength = (crossX * crossX) + (crossY * crossY) +
                 (crossZ * crossZ);

    if (sqrLength > ZERO_EPS)
        invLength = (float) (1.0 / sqrt(sqrLength));
    else
        invLength = 1.0f;

    norm->x = crossX * invLength;
    norm->y = crossY * invLength;
    norm->z = crossZ * invLength;
}

void ss_normalizeNorm( POINT3D *n )
{
    float len;

    len = (n->x * n->x) + (n->y * n->y) + (n->z * n->z);
    if (len > ZERO_EPS)
        len = (float) (1.0 / sqrt(len));
    else
        len = 1.0f;

    n->x *= len;
    n->y *= len;
    n->z *= len;
}

void ss_normalizeNorms(POINT3D *p, ULONG cPts)
{
    float len;
    ULONG i;

    for (i = 0; i < cPts; i++, p++) {
        len = (p->x * p->x) + (p->y * p->y) + (p->z * p->z);
        if (len > ZERO_EPS)
            len = (float) (1.0 / sqrt(len));
        else
            len = 1.0f;

        p->x *= len;
        p->y *= len;
        p->z *= len;
    }
}


MATNAME.H

/******** materials ********************************/

// 24 tea materials (from teapots app)

#define NUM_TEA_MATERIALS 24

enum {
    EMERALD = 0,
    JADE,
    OBSIDIAN,
    PEARL,
    RUBY,
    TURQUOISE,
    BRASS,
    BRONZE,
    CHROME,
    COPPER,
    GOLD,
    SILVER,
    BLACK_PLASTIC,
    CYAN_PLASTIC,
    GREEN_PLASTIC,
    RED_PLASTIC,
    WHITE_PLASTIC,
    YELLOW_PLASTIC,
    BLACK_RUBBER,
    CYAN_RUBBER,
    GREEN_RUBBER,
    RED_RUBBER,
    WHITE_RUBBER,
    YELLOW_RUBBER
};

// 'white' materials, for texturing

#define NUM_TEX_MATERIALS 4

enum {
    BRIGHT_WHITE = NUM_TEA_MATERIALS,
    WHITE,
    WARM_WHITE,
    COOL_WHITE
};


PALETTE.CXX

/******************************Module*Header*******************************\
* Module Name: palette.cxx
*
* Palette processing functions
*
* Copyright 1996 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <GL/gl.h>
#include "ssintrnl.hxx"
#include "palette.hxx"

#if(WINVER < 0x0400)
// Ordinarily not defined for versions before 4.00.
#define COLOR_3DDKSHADOW        21
#define COLOR_3DLIGHT           22
#define COLOR_INFOTEXT          23
#define COLOR_INFOBK            24
#endif

#define TKASSERT(x) SS_ASSERT( x, "palette processing failure\n" )

/******************************************************************************/

// Fixed palette support.

#define BLACK   PALETTERGB(0,0,0)
#define WHITE   PALETTERGB(255,255,255)
#define MAX_STATIC_COLORS   (COLOR_INFOBK - COLOR_SCROLLBAR + 1)
static int gNumStaticColors = MAX_STATIC_COLORS;

// TRUE if static system color settings have been replaced with B&W settings.

// TRUE if original static colors saved
static BOOL tkStaticColorsSaved = FALSE;

// saved system static colors (initialize with default colors)
static COLORREF gacrSave[MAX_STATIC_COLORS];

// new B&W system static colors
static COLORREF gacrBlackAndWhite[] = {
    WHITE,  // COLOR_SCROLLBAR
    BLACK,  // COLOR_BACKGROUND
    BLACK,  // COLOR_ACTIVECAPTION
    WHITE,  // COLOR_INACTIVECAPTION
    WHITE,  // COLOR_MENU
    WHITE,  // COLOR_WINDOW
    BLACK,  // COLOR_WINDOWFRAME
    BLACK,  // COLOR_MENUTEXT
    BLACK,  // COLOR_WINDOWTEXT
    WHITE,  // COLOR_CAPTIONTEXT
    WHITE,  // COLOR_ACTIVEBORDER
    WHITE,  // COLOR_INACTIVEBORDER
    WHITE,  // COLOR_APPWORKSPACE
    BLACK,  // COLOR_HIGHLIGHT
    WHITE,  // COLOR_HIGHLIGHTTEXT
    WHITE,  // COLOR_BTNFACE
    BLACK,  // COLOR_BTNSHADOW
    BLACK,  // COLOR_GRAYTEXT
    BLACK,  // COLOR_BTNTEXT
    BLACK,  // COLOR_INACTIVECAPTIONTEXT
    BLACK,  // COLOR_BTNHIGHLIGHT
    BLACK,  // COLOR_3DDKSHADOW
    WHITE,  // COLOR_3DLIGHT
    BLACK,  // COLOR_INFOTEXT
    WHITE   // COLOR_INFOBK
    };
static INT gaiStaticIndex[] = {
    COLOR_SCROLLBAR          ,
    COLOR_BACKGROUND         ,
    COLOR_ACTIVECAPTION      ,
    COLOR_INACTIVECAPTION    ,
    COLOR_MENU               ,
    COLOR_WINDOW             ,
    COLOR_WINDOWFRAME        ,
    COLOR_MENUTEXT           ,
    COLOR_WINDOWTEXT         ,
    COLOR_CAPTIONTEXT        ,
    COLOR_ACTIVEBORDER       ,
    COLOR_INACTIVEBORDER     ,
    COLOR_APPWORKSPACE       ,
    COLOR_HIGHLIGHT          ,
    COLOR_HIGHLIGHTTEXT      ,
    COLOR_BTNFACE            ,
    COLOR_BTNSHADOW          ,
    COLOR_GRAYTEXT           ,
    COLOR_BTNTEXT            ,
    COLOR_INACTIVECAPTIONTEXT,
    COLOR_BTNHIGHLIGHT       ,
    COLOR_3DDKSHADOW         ,
    COLOR_3DLIGHT            ,
    COLOR_INFOTEXT           ,
    COLOR_INFOBK
    };

#define RESTORE_FROM_REGISTRY   1
#if RESTORE_FROM_REGISTRY
// Registry names for the system colors.
static CHAR *gaszSysClrNames[] = {
    "Scrollbar",      // COLOR_SCROLLBAR              0
    "Background",     // COLOR_BACKGROUND             1   (also COLOR_DESKTOP)
    "ActiveTitle",    // COLOR_ACTIVECAPTION          2
    "InactiveTitle",  // COLOR_INACTIVECAPTION        3
    "Menu",           // COLOR_MENU                   4
    "Window",         // COLOR_WINDOW                 5
    "WindowFrame",    // COLOR_WINDOWFRAME            6
    "MenuText",       // COLOR_MENUTEXT               7
    "WindowText",     // COLOR_WINDOWTEXT             8
    "TitleText",      // COLOR_CAPTIONTEXT            9
    "ActiveBorder",   // COLOR_ACTIVEBORDER          10
    "InactiveBorder", // COLOR_INACTIVEBORDER        11
    "AppWorkspace",   // COLOR_APPWORKSPACE          12
    "Hilight",        // COLOR_HIGHLIGHT             13
    "HilightText",    // COLOR_HIGHLIGHTTEXT         14
    "ButtonFace",     // COLOR_BTNFACE               15   (also COLOR_3DFACE)
    "ButtonShadow",   // COLOR_BTNSHADOW             16   (also COLOR_3DSHADOW)
    "GrayText",       // COLOR_GRAYTEXT              17
    "ButtonText",     // COLOR_BTNTEXT               18
    "InactiveTitleText", // COLOR_INACTIVECAPTIONTEXT   19
    "ButtonHilight",  // COLOR_BTNHIGHLIGHT          20   (also COLOR_3DHILIGHT)
    "ButtonDkShadow", // COLOR_3DDKSHADOW            21
    "ButtonLight",    // COLOR_3DLIGHT               22
    "InfoText",       // COLOR_INFOTEXT              23
    "InfoWindow"      // COLOR_INFOBK                24
};

static BOOL GetRegistrySysColors(COLORREF *, int);
#endif

unsigned char ss_ComponentFromIndex(int i, int nbits, int shift );
static int ss_PixelFormatDescriptorFromDc( HDC hdc, PIXELFORMATDESCRIPTOR *Pfd );

/******************************************************************************/

#if RESTORE_FROM_REGISTRY
/******************************Public*Routine******************************\
* GetRegistrySysColors
*
* Reads the Control Panel's color settings from the registry and stores
* those values in pcr.  If we fail to get any value, then the corresponding
* entry in pcr is not modified.
\**************************************************************************/

static BOOL GetRegistrySysColors(COLORREF *pcr, int nColors)
{
    BOOL bRet = FALSE;
    long lRet;
    HKEY hkSysColors = (HKEY) NULL;
    int i;
    DWORD dwDataType;
    char achColor[64];
    DWORD cjColor;

    TKASSERT(nColors <= gNumStaticColors);

// Open the key for the system color settings.

    lRet = RegOpenKeyExA(HKEY_CURRENT_USER,
                         "Control Panel\\Colors",
                         0,
                         KEY_QUERY_VALUE,
                         &hkSysColors);

    if ( lRet != ERROR_SUCCESS )
    {
        goto GetRegistrySysColors_exit;
    }

// Read each system color value.  The names are stored in the global
// array of char *, gaszSysClrNames.

    for (i = 0; i < nColors; i++)
    {
        cjColor = sizeof(achColor);
        lRet = RegQueryValueExA(hkSysColors,
                                (LPSTR) gaszSysClrNames[i],
                                (LPDWORD) NULL,
                                &dwDataType,
                                (LPBYTE) achColor,
                                &cjColor);

        TKASSERT(lRet != ERROR_MORE_DATA);

        if ( lRet == ERROR_SUCCESS && dwDataType == REG_SZ )
        {
            DWORD r, g, b;

            sscanf(achColor, "%ld %ld %ld", &r, &g, &b);
            pcr[i] = RGB(r, g, b);
        }
    }

    bRet = TRUE;

GetRegistrySysColors_exit:
    if (hkSysColors)
        RegCloseKey(hkSysColors);

    return bRet;
}
#endif

/******************************Public*Routine******************************\
* GrabStaticEntries
*
* Support routine for Realize to manage the static system color
* usage.
*
* This function will save the current static system color usage state.
* It will fail if:
*
*   1.  TK is not in "sys color in use state but system palette is in
*       SYSPAL_NOSTATIC mode.  This means that another app still possesses
*       the static system colors.  If this happens <TBD>
*
* Side effect:
*   If system colors are changed, then WM_SYSCOLORCHANGE message is
*   broadcast to all top level windows.
*
* Returns:
*   TRUE if successful, FALSE otherwise (see above).
\**************************************************************************/

BOOL
SS_PAL::GrabStaticEntries()
{
    int i;
    BOOL bRet = FALSE;

    // Do nothing if sys colors already in use.

    if ( !bSystemColorsInUse )
    {
    // Take possession only if no other app has the static colors.
    // How can we tell?  If the return from SetSystemPaletteUse is
    // SYSPAL_STATIC, then no other app has the statics.  If it is
    // SYSPAL_NOSTATIC, someone else has them and we must fail.
    //
    // SetSystemPaletteUse is properly synchronized internally
    // so that it is atomic.
    //
    // Because we are relying on SetSystemPaletteUse to synchronize TK,
    // it is important to observe the following order for grabbing and
    // releasing:
    //
    //      Grab        call SetSystemPaletteUse and check for SYSPAL_STATIC
    //                  save sys color settings
    //                  set new sys color settings
    //
    //      Release     restore sys color settings
    //                  call SetSystemPaletteUse

// potential pitfall here, if a 'bad' app has not released the static
// colors on deactivation.
        if ( SetSystemPaletteUse( hdc, SYSPAL_NOSTATIC ) == SYSPAL_STATIC )
        {
        // Save current sys color settings.

            for (i = COLOR_SCROLLBAR; i <= COLOR_BTNHIGHLIGHT; i++)
                gacrSave[i - COLOR_SCROLLBAR] = GetSysColor(i);

            bSystemColorsInUse = TRUE;

            // Set b&w sys color settings.

            SetSysColors(gNumStaticColors, gaiStaticIndex, gacrBlackAndWhite);

            // Inform all other top-level windows of the system color change.

            PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);

            bRet = TRUE;
        } else {
            // handle case where can't get sys colors
        }
    }
    else
        bRet = TRUE;

    return bRet;
}

/******************************Public*Routine******************************\
* ReleaseStaticEntries
*
* Support routine for Realize to manage the static system color
* usage.
*
* This function will reset the current static system color usage state.
* It will fail if:
*
*   1.  TK is not in a "sys colors in use" state.  If we are in this case,
*       then the static system colors do not need to be released.
*
* Side effect:
*   If system colors are changed, then WM_SYSCOLORCHANGE message is
*   broadcast to all top level windows.
*
* Returns:
*   TRUE if successful, FALSE otherwise (see above).
\**************************************************************************/

BOOL
SS_PAL::ReleaseStaticEntries()
{
    BOOL bRet = FALSE;

    // Do nothing if sys colors not in use.

    if ( bSystemColorsInUse )
    {
#if RESTORE_FROM_REGISTRY
    // Replace saved system colors with registry values.  We do it now
    // rather than earlier because someone may have changed registry while
    // TK app was running in the foreground (very unlikely, but it could
    // happen).
    //
    // Also, we still try to save current setting in GrabStaticEntries so
    // that if for some reason we fail to grab one or more of the colors
    // from the registry, we can still fall back on what we grabbed via
    // GetSysColors (even though there is a chance its the wrong color).

        GetRegistrySysColors(gacrSave, gNumStaticColors);
#endif

        // Do this now, since SetSysColors() generates WM_SYSCOLORCHANGE,
        // which can cause this routine to be re-entered
        // back to here.
        bSystemColorsInUse = FALSE;

        // Return the system palette to SYSPAL_STATIC.

        SetSystemPaletteUse( hdc, SYSPAL_STATIC );

        // Restore the saved system color settings.

        SetSysColors(gNumStaticColors, gaiStaticIndex, gacrSave);

        // Inform all other top-level windows of the system color change.

        PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);

        // Reset the "sys colors in use" state and return success.

        bSystemColorsInUse = FALSE;
        bRet = TRUE;
    }

    return bRet;
}

// Gamma correction factor * 10
#define GAMMA_CORRECTION 14

// Maximum color distance with 8-bit components
#define MAX_COL_DIST (3*256*256L)

// Number of static colors
#define STATIC_COLORS 20

// Flags used when matching colors
#define EXACT_MATCH 1
#define COLOR_USED 1

// Conversion tables for n bits to eight bits

#if GAMMA_CORRECTION == 10
// These tables are corrected for a gamma of 1.0
static unsigned char abThreeToEight[8] =
{
    0, 0111 >> 1, 0222 >> 1, 0333 >> 1, 0444 >> 1, 0555 >> 1, 0666 >> 1, 0377
};
static unsigned char abTwoToEight[4] =
{
    0, 0x55, 0xaa, 0xff
};
static unsigned char abOneToEight[2] =
{
    0, 255
};
#else
// These tables are corrected for a gamma of 1.4
static unsigned char abThreeToEight[8] =
{
    0, 63, 104, 139, 171, 200, 229, 255
};
static unsigned char abTwoToEight[4] =
{
    0, 116, 191, 255
};
static unsigned char abOneToEight[2] =
{
    0, 255
};
#endif

// Table which indicates which colors in a 3-3-2 palette should be
// replaced with the system default colors
#if GAMMA_CORRECTION == 10
static int aiDefaultOverride[STATIC_COLORS] =
{
    0, 4, 32, 36, 128, 132, 160, 173, 181, 245,
    247, 164, 156, 7, 56, 63, 192, 199, 248, 255
};
#else
static int aiDefaultOverride[STATIC_COLORS] =
{
    0, 3, 24, 27, 64, 67, 88, 173, 181, 236,
    247, 164, 91, 7, 56, 63, 192, 199, 248, 255
};
#endif

unsigned char
ss_ComponentFromIndex(int i, int nbits, int shift)
{
    unsigned char val;

    TKASSERT(nbits >= 1 && nbits <= 3);

    val = i >> shift;
    switch (nbits)
    {
    case 1:
        return abOneToEight[val & 1];

    case 2:
        return abTwoToEight[val & 3];

    case 3:
        return abThreeToEight[val & 7];
    }
    return 0;
}

// System default colors
static PALETTEENTRY apeDefaultPalEntry[STATIC_COLORS] =
{
    { 0,   0,   0,    0 },
    { 0x80,0,   0,    0 },
    { 0,   0x80,0,    0 },
    { 0x80,0x80,0,    0 },
    { 0,   0,   0x80, 0 },
    { 0x80,0,   0x80, 0 },
    { 0,   0x80,0x80, 0 },
    { 0xC0,0xC0,0xC0, 0 },

    { 192, 220, 192,  0 },
    { 166, 202, 240,  0 },
    { 255, 251, 240,  0 },
    { 160, 160, 164,  0 },

    { 0x80,0x80,0x80, 0 },
    { 0xFF,0,   0,    0 },
    { 0,   0xFF,0,    0 },
    { 0xFF,0xFF,0,    0 },
    { 0,   0,   0xFF, 0 },
    { 0xFF,0,   0xFF, 0 },
    { 0,   0xFF,0xFF, 0 },
    { 0xFF,0xFF,0xFF, 0 }
};

/******************************Public*Routine******************************\
*
* UpdateStaticMapping
*
* Computes the best match between the current system static colors
* and a 3-3-2 palette
*
* History:
*  Tue Aug 01 18:18:12 1995-by-Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

static void
UpdateStaticMapping(PALETTEENTRY *pe332Palette)
{
    HPALETTE hpalStock;
    int iStatic, i332;
    int iMinDist, iDist;
    int iDelta;
    int iMinEntry;
    PALETTEENTRY *peStatic, *pe332;

    hpalStock = (HPALETTE) GetStockObject(DEFAULT_PALETTE);

    // The system should always have one of these
    TKASSERT(hpalStock != NULL);
    // Make sure there's the correct number of entries
    TKASSERT(GetPaletteEntries(hpalStock, 0, 0, NULL) == STATIC_COLORS);

    // Get the current static colors
    GetPaletteEntries(hpalStock, 0, STATIC_COLORS, apeDefaultPalEntry);

    // Zero the flags in the static colors because they are used later
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        peStatic->peFlags = 0;
        peStatic++;
    }

    // Zero the flags in the incoming palette because they are used later
    pe332 = pe332Palette;
    for (i332 = 0; i332 < 256; i332++)
    {
        pe332->peFlags = 0;
        pe332++;
    }

    // Try to match each static color exactly
    // This saves time by avoiding the least-squares match for each
    // exact match
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        pe332 = pe332Palette;
        for (i332 = 0; i332 < 256; i332++)
        {
            if (peStatic->peRed == pe332->peRed &&
                peStatic->peGreen == pe332->peGreen &&
                peStatic->peBlue == pe332->peBlue)
            {
                TKASSERT(pe332->peFlags != COLOR_USED);

                peStatic->peFlags = EXACT_MATCH;
                pe332->peFlags = COLOR_USED;
                aiDefaultOverride[iStatic] = i332;

                break;
            }

            pe332++;
        }

        peStatic++;
    }

    // Match each static color as closely as possible to an entry
    // in the 332 palette by minimized the square of the distance
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        // Skip colors already matched exactly
        if (peStatic->peFlags == EXACT_MATCH)
        {
            peStatic++;
            continue;
        }

        iMinDist = MAX_COL_DIST+1;
#if DBG
        iMinEntry = -1;
#endif

        pe332 = pe332Palette;
        for (i332 = 0; i332 < 256; i332++)
        {
            // Skip colors already used
            if (pe332->peFlags == COLOR_USED)
            {
                pe332++;
                continue;
            }

            // Compute Euclidean distance squared
            iDelta = pe332->peRed-peStatic->peRed;
            iDist = iDelta*iDelta;
            iDelta = pe332->peGreen-peStatic->peGreen;
            iDist += iDelta*iDelta;
            iDelta = pe332->peBlue-peStatic->peBlue;
            iDist += iDelta*iDelta;

            if (iDist < iMinDist)
            {
                iMinDist = iDist;
                iMinEntry = i332;
            }

            pe332++;
        }

        TKASSERT(iMinEntry != -1);

        // Remember the best match
        aiDefaultOverride[iStatic] = iMinEntry;
        pe332Palette[iMinEntry].peFlags = COLOR_USED;

        peStatic++;
    }

    // Zero the flags in the static colors because they may have been
    // set.  We want them to be zero so the colors can be remapped
    peStatic = apeDefaultPalEntry;
    for (iStatic = 0; iStatic < STATIC_COLORS; iStatic++)
    {
        peStatic->peFlags = 0;
        peStatic++;
    }

    // Reset the 332 flags because we may have set them
    pe332 = pe332Palette;
    for (i332 = 0; i332 < 256; i332++)
    {
        pe332->peFlags = PC_NOCOLLAPSE;
        pe332++;
    }
}

/******************************Public*Routine******************************\
* FlushPalette
*
* Because of Win 3.1 compatibility, GDI palette mapping always starts
* at zero and stops at the first exact match.  So if there are duplicates,
* the higher colors aren't mapped to--which is often a problem if we
* are trying to make to any of the upper 10 static colors.  To work around
* this, we flush the palette to all black.
*
\**************************************************************************/

void
SS_PAL::Flush()
{
    LOGPALETTE *pPal;
    HPALETTE hpalBlack, hpalOld;
    int i;

    if( nEntries == 256 )
    {
        pPal = (LOGPALETTE *) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
                         sizeof(LOGPALETTE) + nEntries * sizeof(PALETTEENTRY));

        if (pPal)
        {
        pPal->palVersion = 0x300;
        pPal->palNumEntries = nEntries;

            // Mark everything PC_NOCOLLAPSE and PC_RESERVED to force every
            // thing into the palette.  Colors are already black because
            // we zero initialized during memory allocation.

            for (i = 0; i < nEntries; i++)
            {
                pPal->palPalEntry[i].peFlags = PC_NOCOLLAPSE | PC_RESERVED;
            }

            hpalBlack = CreatePalette(pPal);
            LocalFree(pPal);

            hpalOld = SelectPalette(hdc, hpalBlack, FALSE);
            RealizePalette(hdc);

            SelectPalette(hdc, hpalOld, FALSE);
            DeleteObject(hpalBlack);
        }
    }
}

/******************************Public*Routine******************************\
* Realize
*
* Select the given palette in background or foreground mode (as specified
* by the bForceBackground flag), and realize the palette.
*
* If static system color usage is set, the system colors are replaced.
\**************************************************************************/

long
SS_PAL::Realize( HWND hwndArg, HDC hdcArg, BOOL bForceBackground )
{
    // cache:
    hwnd = hwndArg;
    hdc = hdcArg;

    if( bTakeOver ) {
        // Easy case
        SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC);
        SelectPalette(hdc, hPal, bForceBackground );
        RealizePalette(hdc);
        return 1;
    }

    if( bFlush ) {
        Flush();
        bFlush = FALSE;
    }

    return Realize( bForceBackground );
}

long
SS_PAL::Realize( BOOL bForceBackground )
{
    long Result = -1;
    BOOL bHaveSysPal = TRUE;

    SS_DBGLEVEL2( SS_LEVEL_INFO, "SS_PAL::Realize: %d for %d\n", bForceBackground, hwnd );

    // If static system color usage is set, prepare to take over the
    // system palette.

    if( bUseStatic )
    {
        // If foreground, take over the static colors.  If background, release
        // the static colors.

        if ( !bForceBackground )
        {
            // If GrabStaticEntries succeeds, then it is OK to take over the
            // static colors.  If not <mf:TBD>

            bHaveSysPal = GrabStaticEntries();
        }
        else
        {
            // If we are currently using the system colors (bSystemColorsInUse)
            // and Realize was called with bForceBackground set, we
            // are being deactivated and must release the static system colors.

            ReleaseStaticEntries();
        }

        // Rerealize the palette.
        //
        // If set to TRUE, bForceBackground will force the palette to be
        // realized as a background palette, regardless of focus.  This
        // will happen anyway if the TK window does not have the keyboard focus.

        if ( (bForceBackground || bHaveSysPal) &&
             UnrealizeObject( hPal ) &&
             NULL != SelectPalette( hdc, hPal, bForceBackground ) )
        {
            Result = RealizePalette( hdc );
        }

        // If some rude app still has the system colors and we're in the
        // foreground, make the best of it.
        if( !bForceBackground && !bHaveSysPal ) {
            if( UnrealizeObject( hPal ) &&
                NULL != SelectPalette( hdc, hPal, TRUE ) )
            {
                Result = RealizePalette( hdc );
            }
        }
    }
    else
    {
        if ( NULL != SelectPalette( hdc, hPal, FALSE ) )
        {
            Result = RealizePalette( hdc );
        }
    }

    return( Result );
}

/******************************Public*Routine******************************\
* SS_PAL constructor
*
* This creates the palette, but does not select or realize it
*
\**************************************************************************/

SS_PAL::SS_PAL( HDC hdcArg, PIXELFORMATDESCRIPTOR *ppfd, BOOL bTakeOverPalette )
{
    hwnd = 0;
    hPal = 0;
    bUseStatic = FALSE;
    bSystemColorsInUse = FALSE;
    pfd = *ppfd;  // this is for palette purposes only (other fields may not apply)
    hdc = hdcArg;
    bTakeOver = bTakeOverPalette;  // mf: for now, when this is set, it means
        // the screen saver is running in full screen mode - implying that
        // interaction with other apps not necessary

    if( bTakeOver ) {
        bFlush = FALSE;
        bUseStatic = TRUE;
    } else {
        bFlush = TRUE;
        bUseStatic = ppfd->dwFlags & PFD_NEED_SYSTEM_PALETTE;
    }

    if( bUseStatic )
        // save current static palette usage so we can restore it
        uiOldStaticUse = GetSystemPaletteUse( hdc );

    paletteManageProc = NullPaletteManageProc;

    // Now create the palette and return
    hPal = MakeRGBPalette();
    SS_ASSERT( hPal, "SS_PAL constructor failure\n" );
}

/******************************Public*Routine******************************\
* SS_PAL destructor

\**************************************************************************/

SS_PAL::~SS_PAL()
{
    if( bUseStatic )
    {
        if( uiOldStaticUse )
            SetSystemPaletteUse(hdc, uiOldStaticUse);

        PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);
    }
    if( hPal ) {
        SelectPalette( hdc, (HPALETTE) GetStockObject(DEFAULT_PALETTE), TRUE );
        DeleteObject( hPal );
    }
}

/******************************Public*Routine******************************\
* ReCreateRGBPalette
*
*
\**************************************************************************/

void
SS_PAL::ReCreateRGBPalette()
{
    if( bTakeOver )
        return;

    HPALETTE hPalTmp = hPal;
    hPal = MakeRGBPalette();
    if( hPal ) {
        DeleteObject( hPalTmp );
        bFlush = TRUE;
    }
}

/******************************Public*Routine******************************\
* MakeRGBPalette
*
* Creates an HPALETTE with values required for a logical rgb palette.
* If bUseStatic is TRUE, the static system
* colors will be overridden.  Otherwise, the PALETTEENTRY array will be
* fixed up to contain the default static system colors.
*
\**************************************************************************/

HPALETTE
SS_PAL::MakeRGBPalette()
{
    LOGPALETTE *pPal;
    HPALETTE hpal;
    int count, i;
    PIXELFORMATDESCRIPTOR *ppfd = &pfd;

    count = 1 << ppfd->cColorBits;
    nEntries = count;
    pPal = (PLOGPALETTE)LocalAlloc(LMEM_FIXED, sizeof(LOGPALETTE) +
            count * sizeof(PALETTEENTRY));
    if( !pPal )
        return (HPALETTE) 0;

    pPal->palVersion = 0x300;
    pPal->palNumEntries = count;

    PALETTEENTRY *pEntry = pPal->palPalEntry;

    for ( i = 0; i < count ; i++, pEntry++ )
    {
        pEntry->peRed   = ss_ComponentFromIndex(i, ppfd->cRedBits,
                                ppfd->cRedShift);
        pEntry->peGreen = ss_ComponentFromIndex(i, ppfd->cGreenBits,
                                ppfd->cGreenShift);
        pEntry->peBlue  = ss_ComponentFromIndex(i, ppfd->cBlueBits,
                                ppfd->cBlueShift);
        pEntry->peFlags = PC_NOCOLLAPSE;
    }

    if( count == 256 )
    {
    // If app set static system color usage for fixed palette support,
    // setup to take over the static colors.  Otherwise, fixup the
    // static system colors.

        if ( bUseStatic )
        {
        // Black and white already exist as the only remaining static
        // colors.  Let those remap.  All others should be put into
        // the palette (i.e., set PC_NOCOLLAPSE).

            pPal->palPalEntry[0].peFlags = 0;
            pPal->palPalEntry[255].peFlags = 0;
        }
        else
        {
        // The defaultOverride array is computed assuming a 332
        // palette where red has zero shift, etc.

            if ( (3 == ppfd->cRedBits)   && (0 == ppfd->cRedShift)   &&
                 (3 == ppfd->cGreenBits) && (3 == ppfd->cGreenShift) &&
                 (2 == ppfd->cBlueBits)  && (6 == ppfd->cBlueShift) )
            {
                pEntry = pPal->palPalEntry;
                UpdateStaticMapping( pEntry );

                for ( i = 0 ; i < STATIC_COLORS ; i++)
                {
                    pEntry[aiDefaultOverride[i]] = apeDefaultPalEntry[i];
                }
            }
        }
    }

    hpal = CreatePalette(pPal);
    LocalFree(pPal);
    return hpal;
}


SCRNSAVE.CXX

//----------------------------------------------------------------------------
//
// SCRNSAVE.C      --    skeleton for screen saver application
//
//----------------------------------------------------------------------------

#define GL_SCRNSAVE 1

#define WIN31
#include <windows.h>
#include <windowsx.h>
#include "scrnsave.h"
#include <regstr.h>
#include    <imm.h>
#ifdef GL_SCRNSAVE
#include "glscrnsv.h"
#endif

#define DBG_MSGS    0

const TCHAR szScreenSaverKey[] = REGSTR_PATH_SCREENSAVE;
TCHAR szPasswordActiveValue[] = REGSTR_VALUE_USESCRPASSWORD;
const TCHAR szPasswordValue[] = REGSTR_VALUE_SCRPASSWORD;
TCHAR szPwdDLL[] = TEXT("PASSWORD.CPL");
CHAR szFnName[] = "VerifyScreenSavePwd";        // Proc name, must be ANSI
TCHAR szImmDLL[] = TEXT("IMM32.DLL");
CHAR szImmFnc[] = "ImmAssociateContext";        // Proc name, must be ANSI

typedef BOOL (FAR PASCAL * VERIFYPWDPROC) (HWND);
typedef BOOL (FAR PASCAL * IMMASSOCPROC) (HWND,HIMC);

//----------------------------------------------------------------------------
// variables declared in SCRNSAVE.H
HINSTANCE hMainInstance = 0;
HWND hMainWindow = 0;
BOOL fChildPreview = FALSE;

//----------------------------------------------------------------------------
// other globals
POINT ptMouse;
BOOL fClosing = FALSE;
BOOL fCheckingPassword = FALSE;
HINSTANCE hInstPwdDLL = NULL;
VERIFYPWDPROC VerifyPassword = NULL;
static BOOL preview_like_fullscreen = FALSE;
HINSTANCE hInstImm = NULL;
IMMASSOCPROC ImmFnc = NULL;
HIMC hPrevImc = (HIMC)0L;

static BOOL fOnWin95 = FALSE;  //TRUE if on Chicago, FALSE if on Cairo

//----------------------------------------------------------------------------
DWORD dwWakeThreshold = 4;  //default to slight movement
DWORD dwPasswordDelay = 0;
DWORD dwBlankTime = 0;
#define MAX_PASSWORD_DELAY_IN_SECONDS (60)

//----------------------------------------------------------------------------
// forward declarations of internal fns
#ifndef GL_SCRNSAVE
// These are hooked out to glscrnsv.cxx
static int DoScreenSave( HWND hParent );
static int DoConfigBox( HWND hParent );
#endif
static int DoSaverPreview( LPCTSTR szUINTHandle );
static int DoChangePw( LPCTSTR szUINTHandle );
static BOOL DoPasswordCheck( HWND hParent );
VOID LoadPwdDLL(VOID);
VOID UnloadPwdDLL(VOID);

//----------------------------------------------------------------------------
// helper for time
static DWORD
GetElapsedTime(DWORD from, DWORD to)
{
    return (to >= from)? (to - from) : (1 + to + (((DWORD)-1) - from));
}

//----------------------------------------------------------------------------
// helper to convert text to unsigned int
static UINT
atoui( LPCTSTR szUINT )
{
   UINT uValue = 0;

   while( ( *szUINT >= TEXT('0') ) && ( *szUINT <= TEXT('9') ) )
      uValue = ( ( uValue * 10 ) + ( *szUINT++ - TEXT('0') ) );

   return uValue;
}

//----------------------------------------------------------------------------
// Local reboot and hotkey control (on Win95)
static void
HogMachine( BOOL value )
{
    BOOL dummy;

    //
    // NT is always secure, therefore we don't need to call this on Cairo/NT
    //
    if (fOnWin95) {
        SystemParametersInfo( SPI_SCREENSAVERRUNNING, value, &dummy, 0 );
    }
}

//----------------------------------------------------------------------------
// entry point
int PASCAL
WinMainN( HINSTANCE hInst, HINSTANCE hPrev, LPTSTR szCmdLine, int nCmdShow )
{
   LPCTSTR pch = szCmdLine;
   HWND hParent = 0;
   OSVERSIONINFO osvi;

   hMainInstance = hInst;

   osvi.dwOSVersionInfoSize = sizeof(osvi);
   fOnWin95 = (GetVersionEx(&osvi) &&
                osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);

   _try
   {
      for(;;) switch( *pch )
      {
          case TEXT('S'):
          case TEXT('s'):
              return DoScreenSave( NULL );

#ifdef GL_SCRNSAVE
          case TEXT('W'):
          case TEXT('w'):
              do pch++; while( *pch == TEXT(' ') );  // size parameters
              return DoWindowedScreenSave( pch );
#endif

          case TEXT('L'):
          case TEXT('l'):
              // special switch for tests such as WinBench
              // this is NOT a hack to make bechmarks look good
              // it's a hack to allow you to benchmark a screen saver
              // many bechmarking apps require the whole screen in foreground
              // which makes it hard to measure how a screensaver adds CPU load
              // you must provide a parent window (just like preview mode)
              preview_like_fullscreen = TRUE;
          case TEXT('P'):
          case TEXT('p'):
              do pch++; while( *pch == TEXT(' ') );  // skip to the good stuff
              return DoSaverPreview( pch );

          case TEXT('A'):
          case TEXT('a'):
              if (!fOnWin95)
                  return -1;
              do pch++; while( *pch == TEXT(' ') );  // skip to the good stuff
              return DoChangePw( pch );

          case TEXT('C'):
          case TEXT('c'):
              return DoConfigBox( GetForegroundWindow() );

          case TEXT('\0'):
              return DoConfigBox( NULL );

          case TEXT(' '):
          case TEXT('-'):
          case TEXT('/'):
              pch++;   // skip spaces and common switch prefixes
              break;

          default:
              return -1;
      }
   }
   _except(UnhandledExceptionFilter(GetExceptionInformation()))
   {
      // don't leave local reboot and hotkeys disabled on Win95
      HogMachine( FALSE );
   }

}

//----------------------------------------------------------------------------
// default screen-saver proc, declared in SCRNSAVE.H
// intended to be called by the consumer's ScreenSaverProc where
// DefWindowProc would normally be called
LRESULT WINAPI
DefScreenSaverProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
#if DBG_MSGS
    TCHAR szBuff[80];

    wsprintf( szBuff, TEXT("*** DefSSP received:\t0x%04lx 0x%08lx 0x%08lx\n"), uMsg, wParam, lParam );
    OutputDebugString(szBuff);
#endif

   if( !fChildPreview && !fClosing )
   {
      switch( uMsg )
      {
         case WM_CLOSE:
            //
            // Only do password check if on Windows 95.  WinNT (Cairo) has
            // the password check built into the security desktop for
            // C2 compliance.
            //
            if (fOnWin95) {
                if( !DoPasswordCheck( hWnd ) )
                {
                    GetCursorPos( &ptMouse );  // re-establish
                    return FALSE;
                }
            }
#ifdef GL_SCRNSAVE
            // We need to know when we're being terminated, so we can do
            // various clean-up stuff
            SendMessage( hWnd, SS_WM_CLOSING, 0, 0 );
#endif
            break;

         case SCRM_VERIFYPW:
            if (fOnWin95)
                return ( VerifyPassword? (LRESULT)VerifyPassword( hWnd ) : 1L );
            break;

         default:
         {
            POINT ptMove, ptCheck;

            if( fCheckingPassword )
                break;

            switch( uMsg )
            {
                case WM_SHOWWINDOW:
                    if( (BOOL)wParam )
                    SetCursor( NULL );
                    break;

                case WM_SETCURSOR:
                    SetCursor( NULL );
                    return TRUE;

                case WM_MOUSEMOVE:
                    GetCursorPos( &ptCheck );
                    if( ( ptMove.x = ptCheck.x - ptMouse.x ) && ( ptMove.x < 0 ) )
                        ptMove.x *= -1;
                    if( ( ptMove.y = ptCheck.y - ptMouse.y ) && ( ptMove.y < 0 ) )
                        ptMove.y *= -1;
                    if( ((DWORD)ptMove.x + (DWORD)ptMove.y) > dwWakeThreshold )
                    {
                        PostMessage( hWnd, WM_CLOSE, 0, 0l );
                        ptMouse = ptCheck;
                    }
                    break;

                case WM_ACTIVATEAPP:
                    if( wParam ) break;
                case WM_LBUTTONDOWN:
                case WM_MBUTTONDOWN:
                case WM_RBUTTONDOWN:
                case WM_KEYDOWN:
                case WM_SYSKEYDOWN:
                    PostMessage( hWnd, WM_CLOSE, 0, 0l );
                    break;
            }
         }
      }
   }

   return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

//----------------------------------------------------------------------------
// This window procedure takes care of important stuff before calling the
// consumer's ScreenSaverProc.
LRESULT WINAPI
RealScreenSaverProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
   switch( uMsg )
   {
      case WM_CREATE:
         // screen saver does not need the IME
         if ((hInstImm = GetModuleHandle(szImmDLL)) &&
             (ImmFnc = (IMMASSOCPROC)GetProcAddress(hInstImm,szImmFnc)))
             hPrevImc = ImmFnc(hWnd, (HIMC)NULL);

         // establish the mouse position
         GetCursorPos( &ptMouse );

         if( !fChildPreview )
            SetCursor( NULL );

         break;

      case WM_DESTROY:
         // screen saver does not need the IME
         if( hInstImm && ImmFnc && hPrevImc )
            ImmFnc(hWnd, hPrevImc);

         PostQuitMessage( 0 );
         break;

      case WM_SETTEXT:
         // we need to be able to use FindWindow() to find running instances
         // of full-screen windows screen savers
         // NOTE: USER slams our title in during WM_NCCREATE by calling the
         // defproc for WM_SETTEXT directly, so the initial title will get
         // there.  If this ever changes, we can simply set a bypass flag
         // during WM_NCCREATE processing.
         return FALSE;

      case WM_SYSCOMMAND:
         if (!fChildPreview)
         {
            switch (wParam)
            {
               case SC_NEXTWINDOW:       // no Alt-tabs
               case SC_PREVWINDOW:       // no shift-alt-tabs
               case SC_SCREENSAVE:       // no more screensavers
                  return FALSE;
            }
         }
         break;

      case WM_HELP:
      case WM_CONTEXTMENU:
         if( fChildPreview )
         {
            // if we're in preview mode, pump the help stuff to our owner
            HWND hParent = GetParent( hWnd );

            if( hParent && IsWindow( hParent ) )
               PostMessage( hParent, uMsg, (WPARAM)hParent, lParam );
               return TRUE;
         }
         break;

      case WM_TIMER:
         if( fClosing )
            return FALSE;
         Sleep( 0 );
         break;

      case WM_MOUSEMOVE:
      case WM_LBUTTONDOWN:
      case WM_MBUTTONDOWN:
      case WM_RBUTTONDOWN:
      case WM_KEYDOWN:
      case WM_SYSKEYDOWN:
         if( fClosing )
            return DefWindowProc( hWnd, uMsg, wParam, lParam );
         break;

      case WM_PAINT:
         if( fClosing )
            return DefWindowProc( hWnd, uMsg, wParam, lParam );
         if( !fChildPreview )
            SetCursor( NULL );
         break;
   }

   return ScreenSaverProc( hWnd, uMsg, wParam, lParam );
}

#ifdef GL_SCRNSAVE
void
#else
static void
#endif
InitRealScreenSave()
{

   LoadPwdDLL();
}

//----------------------------------------------------------------------------

#ifndef GL_SCRNSAVE

static int
DoScreenSave( HWND hParent )
{
   LPCTSTR pszWindowClass = TEXT("WindowsScreenSaverClass");
   LPCTSTR pszWindowTitle;

   WNDCLASS cls;
   MSG      msg;
   UINT     uStyle;
   UINT     uExStyle;
   int      nCx, nCy;

   cls.hCursor        = NULL;
   cls.hIcon          = LoadIcon( hMainInstance, MAKEINTATOM( ID_APP ) );
   cls.lpszMenuName   = NULL;
   cls.lpszClassName  = pszWindowClass;
   cls.hbrBackground  = GetStockObject( BLACK_BRUSH );
   cls.hInstance      = hMainInstance;
   cls.style          = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_OWNDC;
   cls.lpfnWndProc    = RealScreenSaverProc;
   cls.cbWndExtra     = 0;
   cls.cbClsExtra     = 0;

   if( hParent )
   {
      RECT rcParent;
      GetClientRect( hParent, &rcParent );
      nCx = rcParent.right;
      nCy = rcParent.bottom;
      uStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN;
      uExStyle = 0;

      fChildPreview = TRUE;
      pszWindowTitle = TEXT("Preview");      // MUST differ from full screen
   }
   else
   {
      HWND hOther;

      nCx = GetSystemMetrics( SM_CXSCREEN );
      nCy = GetSystemMetrics( SM_CYSCREEN );
      uStyle = WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
      uExStyle = WS_EX_TOPMOST;

      pszWindowTitle = TEXT("Screen Saver"); // MUST differ from preview

      // if there is another NORMAL screen save instance, switch to it
      hOther = FindWindow( pszWindowClass, pszWindowTitle );

      if( hOther && IsWindow( hOther ) )
      {
         SetForegroundWindow( hOther );
         return 0;
      }

      InitRealScreenSave();
   }

   if( RegisterClass( &cls ) )
   {
      hMainWindow = CreateWindowEx( uExStyle, pszWindowClass, pszWindowTitle,
                        uStyle, 0, 0, nCx, nCy, hParent, (HMENU)NULL,
                        hMainInstance, (LPVOID)NULL );
   }

   if( hMainWindow )
   {
      if( !fChildPreview )
         SetForegroundWindow( hMainWindow );

      while( GetMessage( &msg, NULL, 0, 0 ) )
      {
         TranslateMessage( &msg );
         DispatchMessage( &msg );
      }
   }

   // free password-handling DLL if loaded
   UnloadPwdDLL();

   return msg.wParam;
}
#endif

//----------------------------------------------------------------------------

static int
DoSaverPreview( LPCTSTR szUINTHandle )
{
   // get parent handle from string
   HWND hParent = (HWND)atoui( szUINTHandle );

   // only preview on a valid parent window (NOT full screen)
   return ( (hParent && IsWindow( hParent ))? DoScreenSave( hParent ) : -1 );
}

//----------------------------------------------------------------------------

#ifndef GL_SCRNSAVE

static int
DoConfigBox( HWND hParent )
{
   // let the consumer register any special controls for the dialog
   if( !RegisterDialogClasses( hMainInstance ) )
      return FALSE;

   return DialogBox( hMainInstance, MAKEINTRESOURCE( DLG_SCRNSAVECONFIGURE ),
                     hParent, (DLGPROC)ScreenSaverConfigureDialog );
}
#endif

//----------------------------------------------------------------------------

static int
DoChangePw( LPCTSTR szUINTHandle )
{
   // get parent handle from string
   HWND hParent = (HWND)atoui( szUINTHandle );

   if( !hParent || !IsWindow( hParent ) )
      hParent = GetForegroundWindow();

   // allow the library to be hooked
   ScreenSaverChangePassword( hParent );
   return 0;
}

static const TCHAR szMprDll[] = TEXT("MPR.DLL");       // not to be localized
static const TCHAR szProviderName[] = TEXT("SCRSAVE"); // not to be localized

#ifdef UNICODE
static const CHAR szPwdChangePW[] = "PwdChangePasswordW"; // not to be localized
#else
static const CHAR szPwdChangePW[] = "PwdChangePasswordA"; // not to be localized
#endif

// bogus prototype
typedef DWORD (FAR PASCAL *PWCHGPROC)( LPCTSTR, HWND, DWORD, LPVOID );

void WINAPI
ScreenSaverChangePassword( HWND hParent )
{
   HINSTANCE mpr = LoadLibrary( szMprDll );

   if( mpr )
   {
      PWCHGPROC pwd = (PWCHGPROC)GetProcAddress( mpr, szPwdChangePW );

      if( pwd )
         pwd( szProviderName, hParent, 0, NULL );

      FreeLibrary( mpr );
   }
}

//----------------------------------------------------------------------------

static BOOL
DoPasswordCheck( HWND hParent )
{
   // don't reenter and don't check when we've already decided
   if( fCheckingPassword || fClosing )
      return FALSE;

   if( VerifyPassword )
   {
      static DWORD lastcheck = (DWORD)-1;
      DWORD curtime = GetTickCount();
      MSG msg;

      if (dwPasswordDelay &&
         (GetElapsedTime(dwBlankTime, curtime) < dwPasswordDelay))
      {
         fClosing = TRUE;
         goto _didcheck;
      }

      // no rapid checking...
      if ((lastcheck != (DWORD)-1) &&
         (GetElapsedTime(lastcheck, curtime) < 200))
      {
         goto _didcheck;
      }

      // do the check
      fCheckingPassword = TRUE;

#ifdef GL_SCRNSAVE
      // Put ss in idle mode during password dialog processing
      SendMessage( hParent, SS_WM_IDLE, SS_IDLE_ON, 0L );
#endif

      // flush WM_TIMER messages before putting up the dialog
      PeekMessage( &msg, hParent, WM_TIMER, WM_TIMER, PM_REMOVE | PM_NOYIELD );
      PeekMessage( &msg, hParent, WM_TIMER, WM_TIMER, PM_REMOVE | PM_NOYIELD );

      // call the password verify proc
      fClosing = (BOOL)SendMessage( hParent, SCRM_VERIFYPW, 0, 0L );

      fCheckingPassword = FALSE;

#ifdef GL_SCRNSAVE
      // Restore normal display mode
      SendMessage( hParent, SS_WM_IDLE, SS_IDLE_OFF, 0L );
#endif

      if (!fClosing)
         SetCursor(NULL);

      // curtime may be outdated by now
      lastcheck = GetTickCount();
   }
   else
   {
      // passwords disabled or unable to load handler DLL, always allow exit
      fClosing = TRUE;
   }

_didcheck:
   return fClosing;
}

//----------------------------------------------------------------------------

int _stdcall
DummyEntry( void )
{
    int i;
    STARTUPINFO si;
    LPTSTR pszCmdLine = GetCommandLine();

    if ( *pszCmdLine == TEXT('\"')) {
        /*
         * Scan, and skip over, subsequent characters until
         * another double-quote or a null is encountered.
         */
        while (*(pszCmdLine = CharNext(pszCmdLine)) &&
              (*pszCmdLine != TEXT('\"')) );
        /*
         * If we stopped on a double-quote (usual case), skip
         * over it.
         */
        if ( *pszCmdLine == TEXT('\"') )
            pszCmdLine++;
    }
    else {
        while ((UINT)*pszCmdLine > (UINT)TEXT(' '))
            pszCmdLine = CharNext(pszCmdLine);
    }

    /*
     * Skip past any white space preceeding the second token.
     */
    while (*pszCmdLine && ((UINT)*pszCmdLine <= (UINT)TEXT(' '))) {
        pszCmdLine = CharNext(pszCmdLine);
    }

    si.dwFlags = 0;
    GetStartupInfo(&si);

    i = WinMainN(GetModuleHandle(NULL), NULL, pszCmdLine,
        si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT);

    ExitProcess(i);
    return i;   // We never comes here.
}

//----------------------------------------------------------------------------
// main() entry point to satisfy old NT screen savers
void _cdecl main( int argc, char *argv[] ) {
    DummyEntry();
}

//----------------------------------------------------------------------------
// WinMain() entry point to satisfy old NT screen savers
int PASCAL WinMain( HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow ) {
    DummyEntry();
    return 0;

    // reference unreferenced parameters
    (void)hInst;
    (void)hPrev;
    (void)szCmdLine;
    (void)nCmdShow;
}

VOID LoadPwdDLL(VOID)
{
    HKEY hKey;

    if (!fOnWin95)
        return;

    if (hInstPwdDLL)
        UnloadPwdDLL();

    // look in registry to see if password turned on, otherwise don't
    // bother to load password handler DLL
    if (RegOpenKey(HKEY_CURRENT_USER,szScreenSaverKey,&hKey) ==
        ERROR_SUCCESS)
    {
        DWORD dwVal,dwSize=sizeof(dwVal);

        if ((RegQueryValueEx(hKey,szPasswordActiveValue,
            NULL,NULL,(BYTE *) &dwVal,&dwSize) == ERROR_SUCCESS)
            && dwVal)
        {

            // try to load the DLL that contains password proc.
            hInstPwdDLL = LoadLibrary(szPwdDLL);
            if (hInstPwdDLL)
            {
                VerifyPassword = (VERIFYPWDPROC) GetProcAddress(hInstPwdDLL,
                    szFnName);

                if( VerifyPassword )
                    HogMachine( TRUE );
                else
                    UnloadPwdDLL();
            }
        }

        RegCloseKey(hKey);
    }

}

VOID UnloadPwdDLL(VOID)
{
    if (!fOnWin95)
        return;

    if (hInstPwdDLL)
    {
        FreeLibrary(hInstPwdDLL);
        hInstPwdDLL = NULL;

        if( VerifyPassword )
        {
            VerifyPassword = NULL;
            HogMachine( FALSE );
        }
    }
}

//----------------------------------------------------------------------------
// compatbility stuff  (to make porting easier)
TCHAR szAppName[ APPNAMEBUFFERLEN ];
TCHAR szName[ TITLEBARNAMELEN ];
TCHAR szIniFile[ MAXFILELEN ];
TCHAR szScreenSaver[ 22 ];
TCHAR szHelpFile[ MAXFILELEN ];
TCHAR szNoHelpMemory[ BUFFLEN ];

// Quick fix for old screen savers that don't know about context
// sensitive help
UINT  MyHelpMessage = WM_HELP;


SSA8.C

/******************************Module*Header*******************************\
* Module Name: ssa8.c
*
* Operations on .a8 files
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sscommon.h"

#define ESCAPE 0
#define ESC_ENDLINE 0
#define ESC_ENDBITMAP 1
#define ESC_DELTA 2
#define ESC_RANDOM 3
#define RANDOM_COUNT(c) ((c)-(ESC_RANDOM-1))

#define COUNT_SIZE 256
#define MAXRUN (COUNT_SIZE-1)
#define MAXRND (RANDOM_COUNT(COUNT_SIZE)-1)

typedef unsigned char *HwMem;
typedef unsigned long HwMemSize;
#define HW_UNALIGNED UNALIGNED

static HwMemSize HwuRld(HwMem src, HwMem dest, HwMemSize stride)
{
    unsigned short code;
    unsigned char run, esc;
    size_t len;
    HwMem s;
    HwMem f;

    s = src;
    f = dest;
    for (;;)
    {
        code = *((unsigned short HW_UNALIGNED *)s)++;
        run = code & 0xff;
        esc = code >> 8;
        if (run == ESCAPE)
        {
            if (esc == ESC_ENDBITMAP)
            {
                break;
            }
            else if (esc == ESC_DELTA)
            {
                len = *((unsigned short HW_UNALIGNED *)s)++;
                while (len-- > 0)
                {
                    *f = 0;
                    f += stride;
                }
            }
            else if (esc >= ESC_RANDOM)
            {
                len = RANDOM_COUNT(esc);
                while (len-- > 0)
                {
                    *f = *s++;
                    f += stride;
                }
            }
        }
        else
        {
            while (run-- > 0)
            {
                *f = esc;
                f += stride;
            }
        }
    }

    return s-src;
}

static HwMemSize HwuRldTo32(HwMem src, HwMem dest, HwMemSize stride,
                            DWORD *translate)
{
    unsigned short code;
    unsigned char run, esc;
    size_t len;
    HwMem s;
    DWORD *f, tran;

    s = src;
    f = (DWORD *)dest;
    for (;;)
    {
        code = *((unsigned short HW_UNALIGNED *)s)++;
        run = code & 0xff;
        esc = code >> 8;
        if (run == ESCAPE)
        {
            if (esc == ESC_ENDBITMAP)
            {
                break;
            }
            else if (esc == ESC_DELTA)
            {
                len = *((unsigned short HW_UNALIGNED *)s)++;
                while (len-- > 0)
                {
                    *f = translate[0];
                    f += stride;
                }
            }
            else if (esc >= ESC_RANDOM)
            {
                len = RANDOM_COUNT(esc);
                while (len-- > 0)
                {
                    *f = translate[*s++];
                    f += stride;
                }
            }
        }
        else
        {
            tran = translate[esc];
            while (run-- > 0)
            {
                *f = tran;
                f += stride;
            }
        }
    }

    return s-src;
}

#define ALPHA_SIGNATURE 0xa0a1a2a3
#define COMPRESS_NONE 0
#define COMPRESS_RLE  1

BOOL ss_A8ImageLoad(void *pvResource, TEXTURE *ptex)
{
    DWORD compress;
    DWORD size;
    DWORD *pal;
    DWORD *pdwA8;

    pdwA8 = (DWORD *)pvResource;

    // Check data signature for alpha texture format
    if (*pdwA8 != ALPHA_SIGNATURE)
    {
        return FALSE;
    }
    pdwA8++;

    ptex->width = *pdwA8++;
    ptex->height = *pdwA8++;

    // Make sure depth is 8bpp
    if (*pdwA8 != 8)
    {
        return FALSE;
    }
    pdwA8++;

    size = ptex->width*ptex->height;

    // Compression type
    compress = *pdwA8++;
    // Compressed data size only if compressed, not used
    pdwA8++;

    // Remember pointer to palette data
    pal = pdwA8;
    pdwA8 += 256;

    if (ss_PalettedTextureEnabled())
    {
        // Allocate data for final image
        ptex->data = malloc(size);
        if (ptex->data == NULL)
        {
            return FALSE;
        }

        ptex->pal_size = 256;
        ptex->pal = malloc(ptex->pal_size*sizeof(RGBQUAD));
        if (ptex->pal == NULL)
        {
            free(ptex->data);
            return FALSE;
        }
        memcpy(ptex->pal, pal, ptex->pal_size*sizeof(RGBQUAD));

        // Unpack 8bpp data into final image
        if (compress == COMPRESS_NONE)
        {
            memcpy(ptex->data, pdwA8, size);
        }
        else
        {
            HwuRld((HwMem)pdwA8, ptex->data, 1);
        }

        ptex->format = GL_COLOR_INDEX;
        ptex->components = GL_COLOR_INDEX8_EXT;
    }
    else
    {
        // Allocate data for final image
        ptex->data = malloc(size*sizeof(DWORD));
        if (ptex->data == NULL)
        {
            return FALSE;
        }

        ptex->pal_size = 0;
        ptex->pal = NULL;

        // Unpack 8bpp data into final image
        if (compress == COMPRESS_NONE)
        {
            DWORD i;
            BYTE *src;
            DWORD *dst;

            src = (BYTE *)pdwA8;
            dst = (DWORD *)ptex->data;
            for (i = 0; i < size; i++)
            {
                *dst++ = pal[*src++];
            }
        }
        else
        {
            HwuRldTo32((HwMem)pdwA8, ptex->data, 1, pal);
        }

        ptex->format = GL_BGRA_EXT;
        ptex->components = 4;
    }

    return TRUE;
}


SSCOMMON.H

/******************************Module*Header*******************************\
* Module Name: sscommon.h
*
* Defines and externals for screen saver common shell
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#ifndef __sscommon_h__
#define __sscommon_h__

#include <GL\gl.h>
#include <GL\glaux.h>
#include <assert.h>
#include "matname.h"  // material names

#ifdef __cplusplus
extern "C" {
#endif

#include "ssdebug.h"

#define FAILURE             0
#define SUCCESS             1

// Maximum texture bitmap dimensions.

#define TEX_WIDTH_MAX   1280
#define TEX_HEIGHT_MAX  1024

#define PI 3.14159265358979323846f
// double version of PI
#define PI_D 3.14159265358979323846264338327950288419716939937510
#define ONE_OVER_PI (1.0f / PI)
#define ROOT_TWO 1.414213562373f

#define GEN_STRING_SIZE 64

// texture quality level
enum {
    TEXQUAL_DEFAULT = 0,
    TEXQUAL_HIGH
};

typedef struct _point2d {
    GLfloat x;
    GLfloat y;
} POINT2D;

typedef struct _ipoint2d {
    int x;
    int y;
} IPOINT2D;

typedef struct _point3d {
    GLfloat x;
    GLfloat y;
    GLfloat z;
} POINT3D;

typedef struct _ipoint3d {
    int x;
    int y;
    int z;
} IPOINT3D;

typedef struct _texpoint2d {
    GLfloat s;
    GLfloat t;
} TEX_POINT2D;

typedef struct _isize {
    int width;
    int height;
} ISIZE;

typedef struct _fsize {
    GLfloat width;
    GLfloat height;
} FSIZE;

typedef struct _glrect {
    int x, y;
    int width, height;
} GLRECT;

// texture data
typedef struct {
    int     width;
    int     height;
    GLenum  format;
    GLsizei components;
    float   origAspectRatio; // original width/height aspect ratio
    unsigned char *data;
    GLuint  texObj;          // texture object
    int     pal_size;
    int     iPalRot;         // current palette rotation (not used yet)
    RGBQUAD *pal;
} TEXTURE, *HTEXTURE;

#ifndef GL_EXT_paletted_texture
#define GL_COLOR_INDEX1_EXT                   0x80E2
#define GL_COLOR_INDEX2_EXT                   0x80E3
#define GL_COLOR_INDEX4_EXT                   0x80E4
#define GL_COLOR_INDEX8_EXT                   0x80E5
#define GL_COLOR_INDEX12_EXT                  0x80E6
#define GL_COLOR_INDEX16_EXT                  0x80E7
typedef void (APIENTRY * PFNGLCOLORTABLEEXTPROC)
    (GLenum target, GLenum internalFormat, GLsizei width, GLenum format,
     GLenum type, const GLvoid *data);
typedef void (APIENTRY * PFNGLCOLORSUBTABLEEXTPROC)
    (GLenum target, GLsizei start, GLsizei count, GLenum format,
     GLenum type, GLvoid *data);
#endif

// texture resource

#define RT_RGB          99
#define RT_MYBMP        100
#define RT_A8           101

// texture resource types
enum {
    TEX_UNKNOWN = 0,
    TEX_RGB,
    TEX_BMP,
    TEX_A8
};

typedef struct {
    int     type;
    int     name;
} TEX_RES;

typedef struct _MATRIX {
    GLfloat M[4][4];
} MATRIX;

typedef struct strRGBA {
    GLfloat r;
    GLfloat g;
    GLfloat b;
    GLfloat a;
} RGBA;

typedef struct {
    BYTE r;
    BYTE g;
    BYTE b;
} RGB8;

typedef struct {
    BYTE r;
    BYTE g;
    BYTE b;
    BYTE a;
} RGBA8;

// DlgDraw

enum {
    DLG_INTENSITY_LOW = 0,
    DLG_INTENSITY_MID,
    DLG_INTENSITY_HIGH,
};

// Callback function defines
typedef void (CALLBACK* SSINITPROC)( void *);
typedef void (CALLBACK* SSRESHAPEPROC)(int, int, void *);
typedef void (CALLBACK* SSREPAINTPROC)( LPRECT, void *);
typedef void (CALLBACK* SSUPDATEPROC)( void *);
typedef void (CALLBACK* SSFINISHPROC)( void *);
typedef void (CALLBACK* SSFLOATERFAILPROC)(void *);
typedef void (CALLBACK* SSFLOATERBOUNCEPROC)( void *);

// Defines for pixel format (internal use)
#define SS_DOUBLEBUF_BIT    (1 << 0)
#define SS_DEPTH16_BIT      (1 << 1)
#define SS_DEPTH32_BIT      (1 << 2)
#define SS_ALPHA_BIT        (1 << 3)
#define SS_BITMAP_BIT       (1 << 4)
#define SS_NO_SYSTEM_PALETTE_BIT       (1 << 5)
#define SS_GENERIC_UNACCELERATED_BIT   (1 << 6)

#define SS_HAS_DOUBLEBUF(x) ((x) & SS_DOUBLEBUF_BIT)
#define SS_HAS_DEPTH16(x)((x) & SS_DEPTH16_BIT)
#define SS_HAS_DEPTH32(x)((x) & SS_DEPTH32_BIT)
#define SS_HAS_ALPHA(x)     ((x) & SS_ALPHA_BIT)
#define SS_HAS_BITMAP(x)    ((x) & SS_BITMAP_BIT)

// Depth types
enum {
    SS_DEPTH_NONE = 0,
    SS_DEPTH16,
    SS_DEPTH32,
};

// ss context structures

// Note: all *_INFO structures are requests between common and client ss

typedef struct {
    POINT2D    posInc;          // base position increment
    POINT2D    posIncVary;      // +/- variation for posInc on bounce
} MOTION_INFO;

// internal motion structure
typedef struct {
    POINT2D    pos;             // position
    POINT2D    posInc;          // base position increment
    POINT2D    posIncVary;      // +/- variation for posInc on bounce
    POINT2D    posIncCur;       // Current position increment
} MOTION;

// Size and motion attributes of the OpenGL window that floats around.
typedef struct {
    ISIZE      size;
    IPOINT2D    pos;             // position
    MOTION_INFO     motionInfo;
} CHILD_INFO;

typedef void (CALLBACK* SSCHILDSIZEPROC)( ISIZE *, CHILD_INFO * );

// Size and motion attributes of the OpenGL window that floats around.
typedef struct {
    BOOL       bMotion;         // if floater should move or not
    BOOL       bSubWindow;      // If floater is logical sub-window
    SSCHILDSIZEPROC   ChildSizeFunc; // Callback to set size based on parent
} FLOATER_INFO;

// SS_BITMAP already defined in winuser.h
#if defined( SS_BITMAP )
#undef SS_BITMAP
#endif

typedef struct {
    HDC     hdc;
    ISIZE   size;
    HBITMAP hbm;
    HBITMAP hbmOld; // ? necessary ?
} SS_BITMAP;

// Attributes for StretchBlt mode
typedef struct {
    BOOL       bRatioMode;      // use ratio or base width
    float      widthRatio;      // e.g. 2 means use base of (window width / 2)
    float      heightRatio;
    int        baseWidth;
    int        baseHeight;
    SS_BITMAP  ssbm;
} STRETCH_INFO;

typedef struct {
    BOOL bFloater;
    FLOATER_INFO floaterInfo;
    BOOL bStretch;
    STRETCH_INFO stretchInfo;
    BOOL bDoubleBuf;
    int  depthType;
} SSContext, SSC, *PSSC;

typedef struct _MATERIAL {
    RGBA ka;
    RGBA kd;
    RGBA ks;
    GLfloat specExp;
} MATERIAL;

// texture file info

typedef struct {
    int     nOffset;  // filename offset into pathname
    TCHAR   szPathName[MAX_PATH];  // texture pathname
} TEXFILE;

// texture file processing messages

typedef struct {
    TCHAR   szWarningMsg[MAX_PATH];
    TCHAR   szBitmapSizeMsg[MAX_PATH];
    TCHAR   szBitmapInvalidMsg[MAX_PATH];
    TCHAR   szSelectAnotherBitmapMsg[MAX_PATH];
    TCHAR   szTextureDialogTitle[GEN_STRING_SIZE];
    TCHAR   szTextureFilter[2*GEN_STRING_SIZE];
    TCHAR   szBmp[GEN_STRING_SIZE];
    TCHAR   szDotBmp[GEN_STRING_SIZE];
} TEX_STRINGS;

// Resource constants common to all screen savers

#define IDS_COPYRIGHT           9001
#define IDS_GENNAME             9003
#define IDS_INIFILE             9006
#define IDS_HELPFILE            9009
#define IDS_WARNING             9014
#define IDS_ERROR               9015
#define IDS_BITMAP_SIZE         9016
#define IDS_BITMAP_INVALID      9017
#define IDS_SELECT_ANOTHER_BITMAP 9018
#define IDS_START_FAILED        9019
#define IDS_TEXTUREFILTER       9021
#define IDS_TEXTUREDIALOGTITLE  9022
#define IDS_BMP                 9023
#define IDS_DOTBMP              9024
#define IDS_STARDOTBMP          9025
#define IDS_RGB                 9030
#define IDS_DOTRGB              9031
#define IDS_STARDOTRGB          9032
#define IDS_TEXTURE             9126
#define IDS_TEXTURE_FILE_OFFSET 9127
#define IDS_SIZE                9129
#define IDS_TESSELATION         9130

// Useful macros

#define SS_MAX( a, b ) \
    ( a > b ? a : b )

#define SS_MIN( a, b ) \
    ( a < b ? a : b )

// macro to round up floating values
#define SS_ROUND_UP( fval ) \
    ( (((fval) - (FLOAT)(int)(fval)) > 0.0f) ? (int) ((fval)+1.0f) : (int) (fval) )

// macros to clamp a value within a range
#define SS_CLAMP_TO_RANGE( a, lo, hi ) ( (a < lo) ? lo : ((a > hi) ? hi : a) )
#define SS_CLAMP_TO_RANGE2( a, lo, hi ) \
    ( a = (a < lo) ? lo : ((a > hi) ? hi : a) )

// degree<->radian macros
#define ONE_OVER_180 (1.0f / 180.0f)
#define SS_DEG_TO_RAD( a ) ( (a*PI) * ONE_OVER_180 )
#define SS_RAD_TO_DEG( a ) ( (a*180.0f) * ONE_OVER_PI )

extern MATERIAL TeaMaterial[], TexMaterial[], ss_BlackMat;

// window handling

extern void ss_InitFunc(SSINITPROC);
extern void ss_ReshapeFunc(SSRESHAPEPROC);
extern void ss_RepaintFunc(SSREPAINTPROC);
extern void ss_UpdateFunc(SSUPDATEPROC);
extern void ss_FinishFunc(SSFINISHPROC);
extern void ss_FloaterBounceFunc(SSFLOATERBOUNCEPROC);

// This function *must* be defined by the screen saver
extern SSContext* ss_Init(void);
extern BOOL ss_ConfigInit( HWND hDlg );

extern void ss_DataPtr( void * );

extern BOOL ss_SetWindowAspectRatio( FLOAT aspect );
extern void ss_RandomWindowPos( void );

extern HWND ss_GetHWND();
extern HWND ss_GetGLHWND();
extern void ss_GetScreenSize( ISIZE *size );

extern HBITMAP
SSDIB_CreateCompatibleDIB(HDC hdc, HPALETTE hpal, ULONG ulWidth, ULONG ulHeight,
                    PVOID *ppvBits);
extern BOOL APIENTRY SSDIB_UpdateColorTable(HDC hdcMem, HDC hdc, HPALETTE hpal);

// Palette manage procs
extern LONG MainPaletteManageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
extern LONG PaletteManageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
extern LONG FullScreenPaletteManageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
extern LONG NullPaletteManageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

// material processing

extern void ss_InitTeaMaterials();
extern void ss_InitTexMaterials();
extern void ss_InitMaterials();
extern void ss_SetMaterial( MATERIAL *pMat );
extern void ss_SetMaterialIndex( int index );
extern MATERIAL *ss_RandomTeaMaterial( BOOL bSet );
extern int  ss_RandomTeaMaterialIndex( BOOL bSet );
extern MATERIAL *ss_RandomTexMaterial( BOOL bSet );
extern int  ss_RandomTexMaterialIndex( BOOL bSet );
extern void ss_CreateMaterialGradient( MATERIAL *matInc, MATERIAL *startMat,
                        MATERIAL *endMat, int transCount );
extern void ss_TransitionMaterial( MATERIAL *transMat, MATERIAL *transMatInc );

// color

extern void ss_HsvToRgb(float h, float s, float v, RGBA *color );

// clear

extern int ss_RectWipeClear( int width, int height, int repCount );
extern int ss_DigitalDissolveClear( int width, int height, int size );

// utility

extern void ss_RandInit( void );
extern int ss_iRand( int max );
extern int ss_iRand2( int min, int max );
extern FLOAT ss_fRand( FLOAT min, FLOAT max );
extern BOOL ss_ChangeDisplaySettings( int width, int height, int bitDepth );
extern void ss_QueryDisplaySettings( void );
extern void ss_QueryOSVersion( void );
extern void ss_QueryGLVersion( void );
extern BOOL ss_fOnWin95( void );
extern BOOL ss_fOnNT35( void );
extern BOOL ss_fOnGL11( void );
extern BOOL ss_fPreviewMode( void );
extern BOOL ss_fFullScreenMode( void );
extern BOOL ss_fWindowMode( void );
extern BOOL ss_fConfigMode( void );
extern BOOL ss_RedrawDesktop( void );

// texture file processing

extern int  ss_LoadBMPTextureFile( LPCTSTR pszBmpfile, TEXTURE *pTex );
extern int  ss_LoadTextureFile( TEXFILE *texFile, TEXTURE *pTex );
extern int  ss_LoadTextureResource( TEX_RES *pTexRes, TEXTURE *pTex );
extern BOOL ss_CopyTexture( TEXTURE *pTexDst, TEXTURE *pTexSrc );
extern BOOL ss_SetTextureTransparency( TEXTURE *pTex, float alpha, BOOL bSet );
extern void ss_DisableTextureErrorMsgs();
extern void ss_SetTexture( TEXTURE *pTex );
extern void ss_SetTexturePalette( TEXTURE *pTex, int index );
extern void ss_DeleteTexture( TEXTURE *pTex );
extern BOOL ss_LoadTextureResourceStrings();
extern BOOL ss_DIBImageLoad(PVOID pvFile, TEXTURE *ptex);
extern BOOL ss_RGBImageLoad(PVOID pvFile, TEXTURE *ptex);
extern BOOL ss_A8ImageLoad(PVOID pvFile, TEXTURE *ptex);
extern BOOL ss_VerifyTextureFile( TEXFILE *ptf );
extern BOOL ss_SelectTextureFile( HWND hDlg, TEXFILE *ptf );
extern void ss_GetDefaultBmpFile( LPTSTR pszBmpFile );
extern void ss_InitAutoTexture( TEX_POINT2D *pTexRep );

// texture objects

extern BOOL ss_TextureObjectsEnabled( void );

// Paletted texture support
extern BOOL ss_PalettedTextureEnabled(void);
extern BOOL ss_QueryPalettedTextureEXT(void);

// math functions

extern POINT3D ss_ptZero;
extern void ss_xformPoint(POINT3D *ptOut, POINT3D *ptIn, MATRIX *);
extern void ss_xformNorm(POINT3D *ptOut, POINT3D *ptIn, MATRIX *);
extern void ss_matrixIdent(MATRIX *);
extern void ss_matrixRotate(MATRIX *m, double xTheta, double yTheta, double zTheta);
extern void ss_matrixTranslate(MATRIX *, double xTrans, double yTrans, double zTrans);
extern void ss_matrixMult( MATRIX *m1, MATRIX *m2, MATRIX *m3 );
extern void ss_calcNorm(POINT3D *norm, POINT3D *p1, POINT3D *p2, POINT3D *p3);
extern void ss_normalizeNorm(POINT3D *);
extern void ss_normalizeNorms(POINT3D *, ULONG);

// registry functions

extern BOOL ss_RegistrySetup( HINSTANCE hinst, int section, int file );
extern int  ss_GetRegistryInt( int name, int iDefault );
extern void ss_GetRegistryString( int name, LPTSTR lpDefault, LPTSTR lpDest, int bufSize );
extern void ss_WriteRegistryInt( int name, int iVal );
extern void ss_WriteRegistryString( int name, LPTSTR lpString );

// dialog helper functions

extern int ss_GetTrackbarPos( HWND hDlg, int item );
extern void ss_SetupTrackbar( HWND hDlg, int item, int lo, int hi, int lineSize, int pageSize, int pos );

extern BOOL gbTextureObjects; // from texture.c

#ifdef __cplusplus
}
#endif

#endif // __sscommon_h__


SSDEBUG.H

/******************************Module*Header*******************************\
* Module Name: ssdebug.h
*
* Debugging stuff
*
* Copyright 1996 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __ssdebug_h__
#define __ssdebug_h__

ULONG DbgPrint(PCH Format, ...);

#define SS_LEVEL_ERROR 1L
#define SS_LEVEL_INFO  2L
#define SS_LEVEL_ENTRY 8L

#if DBG

extern long ssDebugMsg;
extern long ssDebugLevel;

#define SS_DBGPRINT( str )          DbgPrint("SS: " str )
#define SS_DBGPRINT1( str, a )      DbgPrint("SS: " str, a )
#define SS_DBGPRINT2( str, a, b )   DbgPrint("SS: " str, a, b )

#define SS_WARNING(str)             DbgPrint("SS: " str )
#define SS_WARNING1(str,a)          DbgPrint("SS: " str,a)
#define SS_WARNING2(str,a,b)        DbgPrint("SS: " str,a,b)

#define SS_RIP(str)                 {SS_WARNING(str); DebugBreak();}
#define SS_RIP1(str,a)              {SS_WARNING1(str,a); DebugBreak();}
#define SS_RIP2(str,a,b)            {SS_WARNING2(str,a,b); DebugBreak();}

#define SS_ASSERT(expr,str)            if(!(expr)) SS_RIP(str)
#define SS_ASSERT1(expr,str,a)         if(!(expr)) SS_RIP1(str,a)
#define SS_ASSERT2(expr,str,a,b)       if(!(expr)) SS_RIP2(str,a,b)

#define SS_ALLOC_FAILURE(str) \
    DbgPrint( "%s : Memory allocation failure\n", str );

#define SS_DBGMSG( str )         if( ssDebugMsg ) SS_DBGPRINT( str )
#define SS_DBGMSG1( str, a )     if( ssDebugMsg ) SS_DBGPRINT1( str, a )
#define SS_DBGMSG2( str, a, b )  if( ssDebugMsg ) SS_DBGPRINT2( str, a, b )

//
// Use SS_DBGLEVEL for general purpose debug messages gated by an
// arbitrary warning level.
//
#define SS_DBGLEVEL(n,str)         if (ssDebugLevel >= (n)) SS_DBGPRINT(str)
#define SS_DBGLEVEL1(n,str,a)      if (ssDebugLevel >= (n)) SS_DBGPRINT1(str,a)
#define SS_DBGLEVEL2(n,str,a,b)    if (ssDebugLevel >= (n)) SS_DBGPRINT2(str,a,b)

#define SS_ERROR(str)              SS_DBGLEVEL( SS_LEVEL_ERROR, str )
#define SS_ERROR1(str,a)           SS_DBGLEVEL1( SS_LEVEL_ERROR, str, a )
#define SS_ERROR2(str,a)           SS_DBGLEVEL2( SS_LEVEL_ERROR, str, a, b )

#define SS_DBGINFO(str)            SS_DBGLEVEL( SS_LEVEL_INFO, str )
#define SS_DBGINFO1(str,a)         SS_DBGLEVEL1( SS_LEVEL_INFO, str, a )
#define SS_DBGINFO2(str,a,b)       SS_DBGLEVEL2( SS_LEVEL_INFO, str, a, b )

#else

#define SS_DBGPRINT( str )
#define SS_DBGPRINT1( str, a )
#define SS_DBGPRINT2( str, a, b )

#define SS_WARNING(str)
#define SS_WARNING1(str,a)
#define SS_WARNING2(str,a,b)

#define SS_RIP(str)
#define SS_RIP1(str,a)
#define SS_RIP2(str,a,b)

#define SS_ASSERT(expr,str)         assert( expr )
#define SS_ASSERT1(expr,str,a)      assert( expr )
#define SS_ASSERT2(expr,str,a,b)    assert( expr )

#define SS_ALLOC_FAILURE(str)

#define SS_DBGMSG( str )
#define SS_DBGMSG1( str, a )
#define SS_DBGMSG2( str, a, b )

#define SS_DBGLEVEL(n,str)
#define SS_DBGLEVEL1(n,str,a)
#define SS_DBGLEVEL2(n,str,a,b)

#define SS_ERROR(str)
#define SS_ERROR1(str,a)
#define SS_ERROR2(str,a)

#define SS_DBGINFO(str)
#define SS_DBGINFO1(str,a)
#define SS_DBGINFO2(str,a)

#endif // DBG

#endif // __ssdebug_h__


SSDIB.C

/******************************Module*Header*******************************\
* Module Name: ssdib.c
*
* Operations on .bmp files
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sscommon.h"

#define BFT_BITMAP  0x4d42  // 'BM' -- indicates structure is BITMAPFILEHEADER

#define OFFSET_bfType       0
#define OFFSET_bfSize       2
#define OFFSET_bfReserved1  6
#define OFFSET_bfReserved2  8
#define OFFSET_bfOffBits    10
#define SIZEOF_BITMAPFILEHEADER 14

// Read a WORD-aligned DWORD.  Needed because BITMAPFILEHEADER has
// WORD-alignment.
#define READDWORD(pv)   ( (DWORD)((PWORD)(pv))[0]               \
                          | ((DWORD)((PWORD)(pv))[1] << 16) )   \

// Computes the number of BYTES needed to contain n number of bits.
#define BITS2BYTES(n)   ( ((n) + 7) >> 3 )

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : DibNumColors(VOID FAR * pv)                                *
 *                                                                          *
 *  PURPOSE    : Determines the number of colors in the DIB by looking at   *
 *               the BitCount filed in the info block.                      *
 *                                                                          *
 *  RETURNS    : The number of colors in the DIB.                           *
 *                                                                          *
 * Stolen from SDK ShowDIB example.                                         *
 ****************************************************************************/

static WORD DibNumColors(VOID FAR * pv)
{
    WORD                bits;
    BITMAPINFOHEADER UNALIGNED *lpbi;
    BITMAPCOREHEADER UNALIGNED *lpbc;

    lpbi = ((LPBITMAPINFOHEADER)pv);
    lpbc = ((LPBITMAPCOREHEADER)pv);

    /*  With the BITMAPINFO format headers, the size of the palette
     *  is in biClrUsed, whereas in the BITMAPCORE - style headers, it
     *  is dependent on the bits per pixel ( = 2 raised to the power of
     *  bits/pixel).
     *
     *  Because of the way we use this call, BITMAPINFOHEADER may be out
     *  of alignment if it follows a BITMAPFILEHEADER.  So use the macro
     *  to safely access DWORD fields.
     */
    if (READDWORD(&lpbi->biSize) != sizeof(BITMAPCOREHEADER)){
        if (READDWORD(&lpbi->biClrUsed) != 0)
        {
            return (WORD) READDWORD(&lpbi->biClrUsed);
        }
        bits = lpbi->biBitCount;
    }
    else
        bits = lpbc->bcBitCount;

    switch (bits){
        case 1:
            return 2;
        case 4:
            return 16;
        case 8:
            return 256;
        default:
            /* A 24 bitcount DIB has no color table */
            return 0;
    }
}

/******************************Public*Routine******************************\
* ss_DIBImageLoad
*
* Hacked form of tk_DIBImageLoad(), for reading a .bmp file from a resource
*
* Loads a DIB file (specified as either an ANSI or Unicode filename,
* depending on the bUnicode flag) and converts it into a TK image format.
*
* The technique used is based on CreateDIBSection and SetDIBits.
* CreateDIBSection is used to create a DIB with a format easily converted
* into the TK image format (packed 24BPP RGB).  The only conversion
* required is swapping R and B in each RGB triplet (see history below)
* The resulting bitmap is selected into a memory DC.
*
* The DIB file is mapped into memory and SetDIBits called to initialize
* the memory DC bitmap.  It is during this step that GDI converts the
* arbitrary DIB file format to RGB format.
*
* Finally, the RGB data in the DIB section is read out and repacked
* as 24BPP 'BGR'.
*
* Returns:
*   BOOL.  If an error occurs, a diagnostic error
*   message is put into the error stream and tkQuit() is called,
*   terminating the app.
*
\**************************************************************************/

BOOL ss_DIBImageLoad(PVOID pv, TEXTURE *ptex)
{
    BOOL             fSuccess = FALSE;
    WORD             wNumColors;    // Number of colors in color table
    BITMAPFILEHEADER *pbmf;         // Ptr to file header
    BITMAPINFOHEADER UNALIGNED *pbmihFile;
    BITMAPCOREHEADER UNALIGNED *pbmchFile; // Ptr to file's core header (if it exists)
    PVOID            pvBits;    // Ptr to bitmap bits in file
    PBYTE            pjBitsRGB;     // Ptr to 24BPP RGB image in DIB section
    PBYTE            pjTKBits = (PBYTE) NULL;   // Ptr to final TK image bits
    PBYTE            pjSrc;         // Ptr to image file used for conversion
    PBYTE            pjDst;         // Ptr to TK image used for conversion

    // These need to be cleaned up when we exit:
    HDC        hdcMem = (HDC) NULL;                 // 24BPP mem DC
    HBITMAP    hbmRGB = (HBITMAP) NULL;             // 24BPP RGB bitmap
    BITMAPINFO *pbmiSource = (BITMAPINFO *) NULL;   // Ptr to source BITMAPINFO
    BITMAPINFO *pbmiRGB = (BITMAPINFO *) NULL;      // Ptr to file's BITMAPINFO

    int i, j;
    int padBytes;

// Otherwise, this may be a raw BITMAPINFOHEADER or BITMAPCOREHEADER
// followed immediately with the color table and the bitmap bits.

    pbmf = (BITMAPFILEHEADER *) pv;

    if ( pbmf->bfType == BFT_BITMAP )
    {
        pbmihFile = (BITMAPINFOHEADER *) ((PBYTE) pbmf + SIZEOF_BITMAPFILEHEADER);

    // BITMAPFILEHEADER is WORD aligned, so use safe macro to read DWORD
    // bfOffBits field.

        pvBits = (PVOID *) ((PBYTE) pbmf
                                + READDWORD((PBYTE) pbmf + OFFSET_bfOffBits));
    }
    else
    {
        pbmihFile = (BITMAPINFOHEADER *) pv;

    // Determination of where the bitmaps bits are needs to wait until we
    // know for sure whether we have a BITMAPINFOHEADER or a BITMAPCOREHEADER.
    }

// Determine the number of colors in the DIB palette.  This is non-zero
// only for 8BPP or less.

    wNumColors = DibNumColors(pbmihFile);

// Create a BITMAPINFO (with color table) for the DIB file.  Because the
// file may not have one (BITMAPCORE case) and potential alignment problems,
// we will create a new one in memory we allocate.
//
// We distinguish between BITMAPINFO and BITMAPCORE cases based upon
// BITMAPINFOHEADER.biSize.

    pbmiSource = (BITMAPINFO *)
        LocalAlloc(LMEM_FIXED, sizeof(BITMAPINFO)
                               + wNumColors * sizeof(RGBQUAD));
    if (!pbmiSource)
    {
        goto tkDIBLoadImage_cleanup;
    }

    // Note: need to use safe READDWORD macro because pbmihFile may
    // have only WORD alignment if it follows a BITMAPFILEHEADER.

    switch (READDWORD(&pbmihFile->biSize))
    {
    case sizeof(BITMAPINFOHEADER):

    // Convert WORD-aligned BITMAPINFOHEADER to aligned BITMAPINFO.

        pbmiSource->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
        pbmiSource->bmiHeader.biWidth         = READDWORD(&pbmihFile->biWidth);
        pbmiSource->bmiHeader.biHeight        = READDWORD(&pbmihFile->biHeight);
        pbmiSource->bmiHeader.biPlanes        = pbmihFile->biPlanes;
        pbmiSource->bmiHeader.biBitCount      = pbmihFile->biBitCount;
        pbmiSource->bmiHeader.biCompression   =
                                        READDWORD(&pbmihFile->biCompression);
        pbmiSource->bmiHeader.biSizeImage     =
                                        READDWORD(&pbmihFile->biSizeImage);
        pbmiSource->bmiHeader.biXPelsPerMeter =
                                        READDWORD(&pbmihFile->biXPelsPerMeter);
        pbmiSource->bmiHeader.biYPelsPerMeter =
                                        READDWORD(&pbmihFile->biYPelsPerMeter);
        pbmiSource->bmiHeader.biClrUsed       =
                                        READDWORD(&pbmihFile->biClrUsed);
        pbmiSource->bmiHeader.biClrImportant  =
                                        READDWORD(&pbmihFile->biClrImportant);

    // Copy color table.  It immediately follows the BITMAPINFOHEADER.

        memcpy((PVOID) &pbmiSource->bmiColors[0], (PVOID) (pbmihFile + 1),
               wNumColors * sizeof(RGBQUAD));

    // If we haven't already determined the position of the image bits,
    // we may now assume that they immediately follow the color table.

        if (!pvBits)
            pvBits = (PVOID) ((PBYTE) (pbmihFile + 1)
                         + wNumColors * sizeof(RGBQUAD));
        break;

    case sizeof(BITMAPCOREHEADER):
        pbmchFile = (BITMAPCOREHEADER *) pbmihFile;

    // Convert BITMAPCOREHEADER to BITMAPINFOHEADER.

        pbmiSource->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
        pbmiSource->bmiHeader.biWidth         = (DWORD) pbmchFile->bcWidth;
        pbmiSource->bmiHeader.biHeight        = (DWORD) pbmchFile->bcHeight;
        pbmiSource->bmiHeader.biPlanes        = pbmchFile->bcPlanes;
        pbmiSource->bmiHeader.biBitCount      = pbmchFile->bcBitCount;
        pbmiSource->bmiHeader.biCompression   = BI_RGB;
        pbmiSource->bmiHeader.biSizeImage     = 0;
        pbmiSource->bmiHeader.biXPelsPerMeter = 0;
        pbmiSource->bmiHeader.biYPelsPerMeter = 0;
        pbmiSource->bmiHeader.biClrUsed       = wNumColors;
        pbmiSource->bmiHeader.biClrImportant  = wNumColors;

    // Convert RGBTRIPLE color table into RGBQUAD color table.

        {
            RGBQUAD *rgb4 = pbmiSource->bmiColors;
            RGBTRIPLE *rgb3 = (RGBTRIPLE *) (pbmchFile + 1);

            for (i = 0; i < wNumColors; i++)
            {
                rgb4->rgbRed   = rgb3->rgbtRed  ;
                rgb4->rgbGreen = rgb3->rgbtGreen;
                rgb4->rgbBlue  = rgb3->rgbtBlue ;
                rgb4->rgbReserved = 0;

                rgb4++;
                rgb3++;
            }
        }

    // If we haven't already determined the position of the image bits,
    // we may now assume that they immediately follow the color table.

        if (!pvBits)
            pvBits = (PVOID) ((PBYTE) (pbmihFile + 1)
                         + wNumColors * sizeof(RGBTRIPLE));
        break;

    default:
        goto tkDIBLoadImage_cleanup;
    }

// Fill in default values (for fields that can have defaults).

    if (pbmiSource->bmiHeader.biSizeImage == 0)
        pbmiSource->bmiHeader.biSizeImage =
            BITS2BYTES( (DWORD) pbmiSource->bmiHeader.biWidth *
                                pbmiSource->bmiHeader.biBitCount ) *
                                pbmiSource->bmiHeader.biHeight;
    if (pbmiSource->bmiHeader.biClrUsed == 0)
        pbmiSource->bmiHeader.biClrUsed = wNumColors;

// Create memory DC.

    hdcMem = CreateCompatibleDC(NULL);
    if (!hdcMem) {
        goto tkDIBLoadImage_cleanup;
    }

// Create a 24BPP RGB DIB section and select it into the memory DC.

    pbmiRGB = (BITMAPINFO *)
              LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(BITMAPINFO) );
    if (!pbmiRGB)
    {
        goto tkDIBLoadImage_cleanup;
    }

    pbmiRGB->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
    pbmiRGB->bmiHeader.biWidth         = pbmiSource->bmiHeader.biWidth;
    pbmiRGB->bmiHeader.biHeight        = pbmiSource->bmiHeader.biHeight;
    pbmiRGB->bmiHeader.biPlanes        = 1;
    pbmiRGB->bmiHeader.biBitCount      = 24;
    pbmiRGB->bmiHeader.biCompression   = BI_RGB;
    pbmiRGB->bmiHeader.biSizeImage     = pbmiRGB->bmiHeader.biWidth
                                         * abs(pbmiRGB->bmiHeader.biHeight) * 3;

    hbmRGB = CreateDIBSection(hdcMem, pbmiRGB, DIB_RGB_COLORS,
                              (PVOID *) &pjBitsRGB, NULL, 0);

    if (!hbmRGB)
    {
        goto tkDIBLoadImage_cleanup;
    }

    if (!SelectObject(hdcMem, hbmRGB))
    {
        goto tkDIBLoadImage_cleanup;
    }

// Slam the DIB file image into the memory DC.  GDI will do the work of
// translating whatever format the DIB file has into RGB format.

    if (!SetDIBits(hdcMem, hbmRGB, 0, pbmiSource->bmiHeader.biHeight,
                   pvBits, pbmiSource, DIB_RGB_COLORS))
    {
        goto tkDIBLoadImage_cleanup;
    }
    GdiFlush();     // make sure that SetDIBits executes

// Convert to TK image format (packed RGB format).
// Allocate with malloc to be consistent with tkRGBImageLoad (i.e., app
// can deallocate with free()).

    pjTKBits = (PBYTE) malloc(pbmiRGB->bmiHeader.biSizeImage);
    if (!pjTKBits)
    {
        goto tkDIBLoadImage_cleanup;
    }

    pjSrc = pjBitsRGB;
    pjDst = pjTKBits;
    // src lines end on LONG boundary - so need to skip over any padding bytes
    padBytes = pbmiSource->bmiHeader.biWidth % sizeof(LONG);
    for (i = 0; i < pbmiSource->bmiHeader.biHeight; i++)
    {
        for (j = 0; j < pbmiSource->bmiHeader.biWidth; j++)
        {
            // swap R and B
            *pjDst++ = pjSrc[2];
            *pjDst++ = pjSrc[1];
            *pjDst++ = pjSrc[0];
            pjSrc += 3;
        }
        pjSrc += padBytes;
    }

// Initialize the texture structure

    // If we get to here, we have suceeded!
    ptex->width = pbmiSource->bmiHeader.biWidth;
    ptex->height = pbmiSource->bmiHeader.biHeight;
    ptex->format = GL_RGB;
    ptex->components = 3;
    ptex->data = pjTKBits;
    ptex->pal_size = 0;
    ptex->pal = NULL;

    fSuccess = TRUE;

// Cleanup objects.

tkDIBLoadImage_cleanup:
    {
        if (hdcMem)
            DeleteDC(hdcMem);

        if (hbmRGB)
            DeleteObject(hbmRGB);

        if (pbmiRGB)
            LocalFree(pbmiRGB);

        if (pbmiSource)
            LocalFree(pbmiSource);
    }

// Check for error.

    if (!fSuccess)
    {
        if (pjTKBits)
            free(pjTKBits);
    }

    return fSuccess;
}

/******************************Public*Routine******************************\
*
* bVerifyDIB
*
* Stripped down version of tkDIBImageLoadAW that verifies that a bitmap
* file is valid and, if so, returns the bitmap dimensions.
*
* Returns:
*   TRUE if valid bitmap file; otherwise, FALSE.
*
\**************************************************************************/

BOOL
bVerifyDIB(LPTSTR pszFileName, ISIZE *pSize )
{
    BOOL bRet = FALSE;
    BITMAPFILEHEADER *pbmf;         // Ptr to file header
    BITMAPINFOHEADER *pbmihFile;    // Ptr to file's info header (if it exists)
    BITMAPCOREHEADER *pbmchFile;    // Ptr to file's core header (if it exists)

    // These need to be cleaned up when we exit:
    HANDLE     hFile = INVALID_HANDLE_VALUE;        // File handle
    HANDLE     hMap = (HANDLE) NULL;                // Mapping object handle
    PVOID      pvFile = (PVOID) NULL;               // Ptr to mapped file

// Map the DIB file into memory.

    hFile = CreateFile((LPTSTR) pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
    if (hFile == INVALID_HANDLE_VALUE)
        goto bVerifyDIB_cleanup;

    hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    if (!hMap)
        goto bVerifyDIB_cleanup;

    pvFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
    if (!pvFile)
        goto bVerifyDIB_cleanup;

// Check the file header.  If the BFT_BITMAP magic number is there,
// then the file format is a BITMAPFILEHEADER followed immediately
// by either a BITMAPINFOHEADER or a BITMAPCOREHEADER.  The bitmap
// bits, in this case, are located at the offset bfOffBits from the
// BITMAPFILEHEADER.
//
// Otherwise, this may be a raw BITMAPINFOHEADER or BITMAPCOREHEADER
// followed immediately with the color table and the bitmap bits.

    pbmf = (BITMAPFILEHEADER *) pvFile;

    if ( pbmf->bfType == BFT_BITMAP )
        pbmihFile = (BITMAPINFOHEADER *) ((PBYTE) pbmf + SIZEOF_BITMAPFILEHEADER);
    else
        pbmihFile = (BITMAPINFOHEADER *) pvFile;

// Get the width and height from whatever header we have.
//
// We distinguish between BITMAPINFO and BITMAPCORE cases based upon
// BITMAPINFOHEADER.biSize.

    // Note: need to use safe READDWORD macro because pbmihFile may
    // have only WORD alignment if it follows a BITMAPFILEHEADER.

    switch (READDWORD(&pbmihFile->biSize))
    {
    case sizeof(BITMAPINFOHEADER):

        if( pSize != NULL ) {
            pSize->width  = READDWORD(&pbmihFile->biWidth);
            pSize->height = READDWORD(&pbmihFile->biHeight);
        }
        bRet = TRUE;

        break;

    case sizeof(BITMAPCOREHEADER):
        pbmchFile = (BITMAPCOREHEADER *) pbmihFile;

    // Convert BITMAPCOREHEADER to BITMAPINFOHEADER.

        if( pSize != NULL ) {
            pSize->width  = (DWORD) pbmchFile->bcWidth;
            pSize->height = (DWORD) pbmchFile->bcHeight;
        }
        bRet = TRUE;

        break;

    default:
        break;
    }

bVerifyDIB_cleanup:

    if (pvFile)
        UnmapViewOfFile(pvFile);

    if (hMap)
        CloseHandle(hMap);

    if (hFile != INVALID_HANDLE_VALUE)
        CloseHandle(hFile);

    return bRet;
}


SSIMAGE.C

/******************************Module*Header*******************************\
* Module Name: ssimage.c
*
* Operations on .rgb files
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sscommon.h"

#define IMAGIC      0x01da
#define IMAGIC_SWAP 0xda01

#define SWAP_SHORT_BYTES(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
#define SWAP_LONG_BYTES(x) (((((x) & 0xff) << 24) | (((x) & 0xff00) << 8)) | \
                            ((((x) & 0xff0000) >> 8) | (((x) & 0xff000000) >> 24)))

typedef struct _rawImageRec {
    unsigned short imagic;
    unsigned short type;
    unsigned short dim;
    unsigned short sizeX, sizeY, sizeZ;
    unsigned long min, max;
    unsigned long wasteBytes;
    char name[80];
    unsigned long colorMap;
    HANDLE file;
    unsigned char *tmp, *tmpR, *tmpG, *tmpB;
    unsigned long rleEnd;
    unsigned long *rowStart;
    long *rowSize;
    // !!! Hack to stick in a pointer to the resource data - shouldn't be
    // a problem, since rgb files always have 512 byte header
    unsigned char *data;
} rawImageRec;

static void RawImageClose(rawImageRec *raw);

/**************************************************************************\
*
* Hacked form of tk_RGBImageLoad(), for reading a .rgb file from a resource
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
static rawImageRec *RawImageOpen( PVOID pv )
{
    rawImageRec *raw;
    unsigned long *rowStart, *rowSize, ulTmp;
    int x;

    raw = (rawImageRec *)malloc(sizeof(rawImageRec));
    if (raw == NULL) {
        return 0;
    }

    // Make a copy of the resource header, since we may be doing some byte
    // swapping, and resources are read-only
    *raw = *((rawImageRec *) pv);

    if (raw->imagic == IMAGIC_SWAP) {
        raw->type = SWAP_SHORT_BYTES(raw->type);
        raw->dim = SWAP_SHORT_BYTES(raw->dim);
        raw->sizeX = SWAP_SHORT_BYTES(raw->sizeX);
        raw->sizeY = SWAP_SHORT_BYTES(raw->sizeY);
        raw->sizeZ = SWAP_SHORT_BYTES(raw->sizeZ);
    }

    raw->tmp = (unsigned char *)malloc(raw->sizeX*256);
    raw->tmpR = (unsigned char *)malloc(raw->sizeX*256);
    raw->tmpG = (unsigned char *)malloc(raw->sizeX*256);
    raw->tmpB = (unsigned char *)malloc(raw->sizeX*256);
    if (raw->tmp == NULL || raw->tmpR == NULL || raw->tmpG == NULL ||
        raw->tmpB == NULL) {
        RawImageClose(raw);
        return 0;
    }

    if ((raw->type & 0xFF00) == 0x0100) {
        x = raw->sizeY * raw->sizeZ * sizeof(long);
        raw->rowStart = (unsigned long *)malloc(x);
        raw->rowSize = (long *)malloc(x);
        if (raw->rowStart == NULL || raw->rowSize == NULL) {
            RawImageClose(raw);
            return 0;
        }
        raw->rleEnd = 512 + (2 * x);

        raw->data = ((unsigned char *) pv);
        RtlCopyMemory( raw->rowStart, raw->data + 512, x );
        RtlCopyMemory( raw->rowSize, raw->data + 512 + x, x );

        if (raw->imagic == IMAGIC_SWAP) {
            x /= sizeof(long);
            rowStart = raw->rowStart;
            rowSize = (unsigned long *) raw->rowSize;
            while (x--) {
                ulTmp = *rowStart;
                *rowStart++ = SWAP_LONG_BYTES(ulTmp);
                ulTmp = *rowSize;
                *rowSize++ = SWAP_LONG_BYTES(ulTmp);
            }
        }
    }
    return raw;
}

static void RawImageClose(rawImageRec *raw)
{
    if( !raw )
        return;
    if( raw->tmp ) free(raw->tmp);
    if( raw->tmpR ) free(raw->tmpR);
    if( raw->tmpG ) free(raw->tmpG);
    if( raw->tmpB ) free(raw->tmpB);
    free(raw);
}

static void RawImageGetRow(rawImageRec *raw, unsigned char *buf, int y, int z)
{
    unsigned char *iPtr, *oPtr, pixel;
    int count;

    if ((raw->type & 0xFF00) == 0x0100) {
        RtlCopyMemory(raw->tmp, raw->data + raw->rowStart[y+z*raw->sizeY],
                 (unsigned int)raw->rowSize[y+z*raw->sizeY] );
        iPtr = raw->tmp;
        oPtr = buf;
        while (1) {
            pixel = *iPtr++;
            count = (int)(pixel & 0x7F);
            if (!count) {
                return;
            }
            if (pixel & 0x80) {
                while (count--) {
                    *oPtr++ = *iPtr++;
                }
            } else {
                pixel = *iPtr++;
                while (count--) {
                    *oPtr++ = pixel;
                }
            }
        }
    } else {
        iPtr = raw->data + 512 + (y*raw->sizeX)+(z*raw->sizeX*raw->sizeY);
        RtlCopyMemory( buf, iPtr, raw->sizeX );
    }
}

static void RawImageGetData(rawImageRec *raw, TEXTURE *ptex)
{
    unsigned char *ptr;
    int i, j;

    ptex->data = (unsigned char *)malloc((raw->sizeX+1)*(raw->sizeY+1)*4);
    if (ptex->data == NULL) {
        return;
    }

    ptr = ptex->data;
    for (i = 0; i < raw->sizeY; i++) {
        RawImageGetRow(raw, raw->tmpR, i, 0);
        RawImageGetRow(raw, raw->tmpG, i, 1);
        RawImageGetRow(raw, raw->tmpB, i, 2);
        for (j = 0; j < raw->sizeX; j++) {
            *ptr++ = *(raw->tmpR + j);
            *ptr++ = *(raw->tmpG + j);
            *ptr++ = *(raw->tmpB + j);
        }
    }
}

BOOL ss_RGBImageLoad( PVOID pv, TEXTURE *ptex )
{
    rawImageRec *raw;

    if( !(raw = RawImageOpen( pv )) )
        return FALSE;

    ptex->width = raw->sizeX;
    ptex->height = raw->sizeY;
    ptex->format = GL_RGB;
    ptex->components = 3;
    ptex->pal_size = 0;
    ptex->pal = NULL;
    RawImageGetData(raw, ptex);
    RawImageClose(raw);
    return TRUE;
}

/******************************Public*Routine******************************\
*
* bVerifyRGB
*
* Stripped down version of tkRGBImageLoadAW that verifies that an rgb
* file is valid and, if so, returns the bitmap dimensions.
*
* Returns:
*   TRUE if valid rgb file; otherwise, FALSE.
*
\**************************************************************************/

BOOL
bVerifyRGB(LPTSTR pszFileName, ISIZE *pSize )
{
    rawImageRec *raw;
    DWORD dwBytesRead;
    BOOL bRet = FALSE;

    raw = (rawImageRec *)
          LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof(rawImageRec) );

    if (raw == NULL) {
        goto bVerifyRGB_cleanup;
    }

    raw->file = CreateFile((LPTSTR) pszFileName, GENERIC_READ, FILE_SHARE_READ,
                            NULL, OPEN_EXISTING, 0, 0);

    if (raw->file == INVALID_HANDLE_VALUE) {
        goto bVerifyRGB_cleanup;
    }

    ReadFile(raw->file, (LPVOID) raw, 12, &dwBytesRead, (LPOVERLAPPED) NULL);

    if( raw->imagic == IMAGIC_SWAP ) {
        raw->sizeX = SWAP_SHORT_BYTES(raw->sizeX);
        raw->sizeY = SWAP_SHORT_BYTES(raw->sizeY);
        bRet = TRUE;
    } else if( raw->imagic == IMAGIC)
        bRet = TRUE;

bVerifyRGB_cleanup:

    if( bRet && pSize ) {
        pSize->width = raw->sizeX;
        pSize->height = raw->sizeY;
    }

    if( raw->file )
        CloseHandle( raw->file );

    if( raw )
        LocalFree( raw );

    return bRet;
}


SSINIT.CXX

/******************************Module*Header*******************************\
* Module Name: ssinit.cxx
*
* Main code for common screen saver functions.
*
* Copyright (c) 1994 Microsoft Corporation
\**************************************************************************/

#include <windows.h>
#include <commdlg.h>
#include <scrnsave.h>
#include <GL\gl.h>
#include <math.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
#include "ssintrnl.hxx"

void     *gDataPtr = NULL; // data ptr used with callbacks

// function protos
void (*gReshapeFunc)(int, int, void *)      = NULL;
void (*gRepaintFunc)(LPRECT, void *)      = NULL;
void (*gUpdateFunc)( void *)           = NULL;
void (*gInitFunc)( void *)             = NULL;
void (*gFinishFunc)( void *)           = NULL;
void (*gFloaterBounceFunc)( void *)      = NULL;

// Debug stuff
#if DBG
#ifdef SS_DEBUG
long ssDebugMsg = 1;
long ssDebugLevel = SS_LEVEL_INFO;
#else
long ssDebugMsg = 0;
long ssDebugLevel = SS_LEVEL_ERROR;
#endif
#endif

// Callback functions:

/******************************Public*Routine******************************\
* ss_InitFunc
*
\**************************************************************************/

void
ss_InitFunc(SSINITPROC Func)
{
    gInitFunc = Func;
}

/******************************Public*Routine******************************\
* ss_ReshapeFunc
*
\**************************************************************************/

void
ss_ReshapeFunc(SSRESHAPEPROC Func)
{
    gReshapeFunc = Func;
    if( gpss->psswGL )
        gpss->psswGL->ReshapeFunc = gReshapeFunc;
}

/******************************Public*Routine******************************\
* ss_RepaintFunc
*
\**************************************************************************/

void
ss_RepaintFunc(SSREPAINTPROC Func)
{
    gRepaintFunc = Func;
    if( gpss->psswGL )
        gpss->psswGL->RepaintFunc = gRepaintFunc;
}

/******************************Public*Routine******************************\
* ss_UpdateFunc
*
\**************************************************************************/

void
ss_UpdateFunc(SSUPDATEPROC Func)
{
    gUpdateFunc = Func;
    if( gpss->psswGL )
        gpss->psswGL->UpdateFunc = gUpdateFunc;
}

/******************************Public*Routine******************************\
* ss_FinishFunc
*
\**************************************************************************/

void
ss_FinishFunc(SSFINISHPROC Func)
{
    gFinishFunc = Func;
    if( gpss->psswGL )
        gpss->psswGL->FinishFunc = gFinishFunc;
}

/******************************Public*Routine******************************\
* ss_FloaterBounceFunc
*
\**************************************************************************/

void
ss_FloaterBounceFunc(SSFLOATERBOUNCEPROC Func)
{
    gFloaterBounceFunc = Func;
    if( gpss->psswGL )
        gpss->psswGL->FloaterBounceFunc = gFloaterBounceFunc;
}

/******************************Public*Routine******************************\
* ss_DataPtr
*
* Sets data ptr to be sent with callbacks
*
\**************************************************************************/

void
ss_DataPtr( void *data )
{
    gDataPtr = data;
    if( gpss->psswGL )
        gpss->psswGL->DataPtr = gDataPtr;
}

/******************************Public*Routine******************************\
* RandomWindowPos
*
* Sets a new random window position and direction.
*
\**************************************************************************/

void
ss_RandomWindowPos()
{
    if( gpss->psswGL )
        gpss->psswGL->RandomWindowPos();
}

/******************************Public*Routine******************************\
* ss_SetWindowAspectRatio
*
* Resize the window to conform to the supplied aspect ratio.  We do this by
* maintaining the existing width, and adjusting the height.
*
* Window resize seems to be executed synchronously, so gl should be able to
* immediately validate its buffer dimensions (we count on it).
*
* Returns TRUE if new height is different from last, else FALSE.
\**************************************************************************/

BOOL
ss_SetWindowAspectRatio( FLOAT aspect )
{
    if( gpss->psswGL )
        return gpss->psswGL->SetAspectRatio( aspect );
    return FALSE;
}

/******************************Public*Routine******************************\
* ss_GetScreenSize
*
* Returns size of screen saver window
*
\**************************************************************************/

void
ss_GetScreenSize( ISIZE *size )
{
    if( gpss->psswMain )
        *size = gpss->psswMain->size;
}

/******************************Public*Routine******************************\
* ss_GetHWND
*
* Return HWND of the main window
\**************************************************************************/

HWND
ss_GetHWND()
{
    if( gpss->psswMain )
        return gpss->psswMain->hwnd;
    return NULL;
}

/******************************Public*Routine******************************\
* ss_GetGLHWND
*
* Return HWND of the GL window
\**************************************************************************/

HWND
ss_GetGLHWND()
{
    if( gpss->psswGL )
        return gpss->psswGL->hwnd;
    return NULL;
}

/******************************Public*Routine******************************\
* ss_GetMainPSSW
*
* Return PSSW of top level window
\**************************************************************************/

PSSW
ss_GetMainPSSW()
{
    if( gpss->psswMain )
        return gpss->psswMain;
    return NULL;
}


SSUTIL.CXX

/******************************Module*Header*******************************\
* Module Name: ssutil.cxx
*
* Screen-saver utility functions
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
#include <commdlg.h>
#include <scrnsave.h>
#include <GL\gl.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
#include "ssintrnl.hxx"

#include "ssutil.hxx"

static OSVERSIONINFO gosvi = {0};
static BOOL gbGLv1_1 = FALSE;  // GL version 1.1 boolean

/******************************Public*Routine******************************\
* SSU_ChoosePixelFormat
*
* Local implementation of ChoosePixelFormat
*
* Choose pixel format based on flags.
* This allows us a little a more control than just calling ChoosePixelFormat
\**************************************************************************/

static int
SSU_ChoosePixelFormat( HDC hdc, int flags )
{
    int MaxPFDs;
    int iBest = 0;
    PIXELFORMATDESCRIPTOR pfd;

    // Always choose native pixel depth
    int cColorBits =
                GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES);

    BOOL bDoubleBuf = flags & SS_DOUBLEBUF_BIT;

    int cDepthBits = 0;
    if( SS_HAS_DEPTH16(flags) )
        cDepthBits = 16;
    else if( SS_HAS_DEPTH32(flags) )
        cDepthBits = 32;

    int i = 1;
    do
    {
        MaxPFDs = DescribePixelFormat(hdc, i, sizeof(pfd), &pfd);
        if ( MaxPFDs <= 0 )
            return 0;

        if( ! (pfd.dwFlags & PFD_SUPPORT_OPENGL) )
            continue;

        if( flags & SS_BITMAP_BIT ) {
            // need bitmap pixel format
            if( ! (pfd.dwFlags & PFD_DRAW_TO_BITMAP) )
                continue;
        } else {
            // need window pixel format
            if( ! (pfd.dwFlags & PFD_DRAW_TO_WINDOW) )
                continue;
            // a window can be double buffered...
            if( ( bDoubleBuf && !(pfd.dwFlags & PFD_DOUBLEBUFFER) ) ||
                ( !bDoubleBuf && (pfd.dwFlags & PFD_DOUBLEBUFFER) ) )
                continue;
        }

        if ( pfd.iPixelType != PFD_TYPE_RGBA )
            continue;
        if( pfd.cColorBits != cColorBits )
            continue;

        if( (flags & SS_GENERIC_UNACCELERATED_BIT) &&
            ((pfd.dwFlags & (PFD_GENERIC_FORMAT|PFD_GENERIC_ACCELERATED))
    != PFD_GENERIC_FORMAT) )
            continue;

        if( (flags & SS_NO_SYSTEM_PALETTE_BIT) &&
            (pfd.dwFlags & PFD_NEED_SYSTEM_PALETTE) )
            continue;

        if( cDepthBits ) {
            if( pfd.cDepthBits < cDepthBits )
                continue;
        } else {
            // No depth buffer required, but use it if nothing better
            if( pfd.cDepthBits ) {
                iBest = i;
                continue;
            }
        }

        // We have found something useful
        return i;

    } while (++i <= MaxPFDs);

    if( iBest )
        // not an exact match, but good enough
        return iBest;

    // If we reach here, we have failed to find a suitable pixel format.
    // See if the system can find us one.

    memset( &pfd, 0, sizeof( PIXELFORMATDESCRIPTOR ) );
    pfd.nSize = sizeof( PIXELFORMATDESCRIPTOR );
    pfd.cColorBits = cColorBits;
    pfd.cDepthBits = cDepthBits;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.dwFlags = PFD_SUPPORT_OPENGL;
    if( bDoubleBuf )
        pfd.dwFlags |= PFD_DOUBLEBUFFER;
    if( flags & SS_BITMAP_BIT )
        pfd.dwFlags |= PFD_DRAW_TO_BITMAP;
    else
        pfd.dwFlags |= PFD_DRAW_TO_WINDOW;

    if( (flags & SS_GENERIC_UNACCELERATED_BIT) ||
        (flags & SS_NO_SYSTEM_PALETTE_BIT) )
        // If either of these flags are set, we should be safe specifying a
        // 'slow' pixel format that supports bitmap drawing
        pfd.dwFlags |= PFD_DRAW_TO_BITMAP;

    SS_WARNING( "SSU_ChoosePixelFormat failed, calling ChoosePIxelFormat\n" );

    return ChoosePixelFormat( hdc, &pfd );
}

/******************************Public*Routine******************************\
* SSU_SetupPixelFormat
*
* Choose pixel format according to supplied flags.  If ppfd is non-NULL,
* call DescribePixelFormat with it.
*
\**************************************************************************/

BOOL
SSU_SetupPixelFormat(HDC hdc, int flags, PIXELFORMATDESCRIPTOR *ppfd )
{
    int pixelFormat;
    int nTryAgain = 4;

    do{
        if( (pixelFormat = SSU_ChoosePixelFormat(hdc, flags)) &&
            SetPixelFormat(hdc, pixelFormat, NULL) ) {
            SS_DBGLEVEL1( SS_LEVEL_INFO,
               "SSU_SetupPixelFormat: Setting pixel format %d\n", pixelFormat );
            if( ppfd )
                DescribePixelFormat(hdc, pixelFormat,
                                sizeof(PIXELFORMATDESCRIPTOR), ppfd);
            return TRUE; // Success
        }
        // Failed to set pixel format.  Try again after waiting a bit (win95
        // bug with full screen dos box)
        Sleep( 1000 ); // Wait a second between attempts
    } while( nTryAgain-- );

    return FALSE;
}

/******************************Public*Routine******************************\
* SSU_bNeedPalette
*
\**************************************************************************/

BOOL
SSU_bNeedPalette( PIXELFORMATDESCRIPTOR *ppfd )
{
    if (ppfd->dwFlags & PFD_NEED_PALETTE)
        return TRUE;
    else
        return FALSE;
}

/******************************Public*Routine******************************\
* SSU_PixelFormatDescriptorFromDc
*
\**************************************************************************/

int
SSU_PixelFormatDescriptorFromDc( HDC hdc, PIXELFORMATDESCRIPTOR *Pfd )
{
    int PfdIndex;

    if ( 0 < (PfdIndex = GetPixelFormat( hdc )) )
    {
        if ( 0 < DescribePixelFormat( hdc, PfdIndex, sizeof(*Pfd), Pfd ) )
        {
            return(PfdIndex);
        }
    }
    return 0;
}

/******************************Public*Routine******************************\
* ss_ChangeDisplaySettings
*
* Try changing display settings.
* If bitDepth is 0, use current bit depth
*
\**************************************************************************/

BOOL
ss_ChangeDisplaySettings( int width, int height, int bitDepth )
{
    int change;
    DEVMODE dm = {0};

dm.dmSize       = sizeof(dm);
    dm.dmFields     = DM_PELSWIDTH | DM_PELSHEIGHT;
dm.dmPelsWidth  = width;
dm.dmPelsHeight = height;

    if( bitDepth != 0 ) {
    dm.dmFields |= DM_BITSPERPEL;
    dm.dmBitsPerPel = bitDepth;
    }

//    change = ChangeDisplaySettings(&dm, CDS_TEST);
    change = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);

    if( change == DISP_CHANGE_SUCCESSFUL )
        return TRUE;
    else
        return FALSE;
}

/******************************Public*Routine******************************\
* ss_QueryDisplaySettings
*
* Find out what diplay resolutions are available.
*
\**************************************************************************/

void
ss_QueryDisplaySettings( void )
{
    int i = 0;
    DEVMODE devMode = {0};

    while( EnumDisplaySettings( NULL, i, &devMode ) ) {
        i++;
    }
}

/******************************Public*Routine******************************\
* ss_QueryGLVersion
*
* Find out what GL version is being loaded.  If it's 1.1, set various
* global capabilities.
*
\**************************************************************************/

void
ss_QueryGLVersion( void )
{
    // Get GL version

    if( strstr( (char *) glGetString(GL_VERSION), "1.1") ) {
        gbGLv1_1 = TRUE;
        gbTextureObjects = TRUE;
    } else {
        gbGLv1_1 = FALSE;
        gbTextureObjects = FALSE;
    }
#ifdef SS_DEBUG
    if( !gbTextureObjects )
        DbgPrint( "ss_QueryGLVersion: Texture Objects disabled\n" );
#endif
}

/******************************Public*Routine******************************\
* ss_fOnGL11
*
* True if running on OpenGL v.1.1x
*
\**************************************************************************/

BOOL
ss_fOnGL11( void )
{
    return gbGLv1_1;
}

/******************************Public*Routine******************************\
* ss_QueryOSVersion
*
* Query the OS version
*
\**************************************************************************/

void
ss_QueryOSVersion( void )
{
    gosvi.dwOSVersionInfoSize = sizeof(gosvi);
    GetVersionEx(&gosvi);
}

/******************************Public*Routine******************************\
* ss_fOnNT35
*
* True if running on NT version 3.51 or less
*
\**************************************************************************/

BOOL
ss_fOnNT35( void )
{
    static fOnNT35;
    static bInited = FALSE;

    if( !bInited ) {
        if( !gosvi.dwOSVersionInfoSize )
            ss_QueryOSVersion();
        fOnNT35 =
        (
            (gosvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
            (gosvi.dwMajorVersion == 3 && gosvi.dwMinorVersion <= 51)
        );
        bInited = TRUE;
    }
    return fOnNT35;
}

/******************************Public*Routine******************************\
* ss_fOnWin95
*
* True if running on Windows 95
*
\**************************************************************************/

BOOL
ss_fOnWin95( void )
{
    static fOnWin95;
    static bInited = FALSE;

    if( !bInited ) {
        if( !gosvi.dwOSVersionInfoSize )
            ss_QueryOSVersion();
        fOnWin95 = ( gosvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS );
        bInited = TRUE;
    }
    return fOnWin95;
}

/******************************Public*Routine******************************\
* ss_fPreviewMode
*
* True if running in Display setting's child preview window
*
\**************************************************************************/

BOOL
ss_fPreviewMode( void )
{
    return gpss->type == SS_TYPE_PREVIEW;
}

/******************************Public*Routine******************************\
* ss_fFullScreenMode
*
* True if running full screen (/s option)
*
\**************************************************************************/

BOOL
ss_fFullScreenMode( void )
{
    return gpss->type == SS_TYPE_FULLSCREEN;
}

BOOL
ss_fConfigMode( void )
{
    return gpss->type == SS_TYPE_CONFIG;
}

BOOL
ss_fWindowMode( void )
{
    return gpss->type == SS_TYPE_NORMAL;
}

/******************************Public*Routine******************************\
* ss_RedrawDesktop
*
* Causes the entire desktop to be redrawn
*
\**************************************************************************/

BOOL
ss_RedrawDesktop( void )
{
    return RedrawWindow( NULL, NULL, NULL, RDW_ERASE | RDW_INVALIDATE |
                            RDW_ERASENOW | RDW_UPDATENOW | RDW_ALLCHILDREN );
}


SSWINDOW.CXX

/******************************Module*Header*******************************\
* Module Name: sswindow.cxx
*
* Copyright 1996 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
#include <windowsx.h>
#include "scrnsave.h"

#include "glscrnsv.h"
#include "ssintrnl.hxx"
#include "sswindow.hxx"
#include "ssutil.hxx"

static void (__stdcall *glAddSwapHintRect)(GLint, GLint, GLint, GLint);

// externs from ssinit.cxx
extern void *gDataPtr;
extern void (*gReshapeFunc)(int, int, void *);
extern void (*gRepaintFunc)( LPRECT, void *);
extern void (*gUpdateFunc)( void *);
extern void (*gInitFunc)( void *);
extern void (*gFinishFunc)( void *);
extern void (*gFloaterBounceFunc)( void *);

// forwards
static void GetWindowSize( HWND hwnd, ISIZE *pSize );
static void ss_QueryAddSwapHintRect();
static void DrawGdiDeltaRect( HDC hdc, HBRUSH hbr, RECT *pRect1, RECT *pRect2 );
static void DrawGLDeltaRect( GLRECT *pRect1, GLRECT *pRect2 );

/**************************************************************************\
* SSW constructor
*
\**************************************************************************/

SSW::SSW( PSSW psswParentArg, ISIZE *pSize, IPOINT2D *pPos, BOOL bMotion,
          SSCHILDSIZEPROC ChildSizeFuncArg )
{
    // Basic initialization

    Reset();

    // Initialization based on constructor parameters

    psswParent = psswParentArg;
    ChildSizeFunc = ChildSizeFuncArg;

    if( pSize )
        size = *pSize;
    if( pPos )
        pos = *pPos;
    else
        pos.x = pos.y = 0;

    if( bMotion && psswParent ) {
        // Allocate motion structure
        pMotion = (MOTION *)
                LocalAlloc( LMEM_ZEROINIT | LMEM_FIXED, sizeof(MOTION) );
        // If pMotion is NULL, then motion is disabled
    }

    // Call back to the client screen saver to determine size and motion
    // characteristics of child based on its parent size.
    GetChildInfo(); // this can set pos and size

    if( pMotion ) {
        if( pPos == NULL ) {
            // Set a random window pos
            pos.x = ss_iRand2( 0, (psswParent->size.width - size.width) );
            pos.y = ss_iRand2( 0, (psswParent->size.height - size.height) );

            // Set the motion parameters
            ResetMotion();
        }
        // Have to make sure parent has an hdc so it can draw background when
        // this child moves
        if( !psswParent->hdc )
            psswParent->hdc = GetDC( psswParent->hwnd );
    }

    if( psswParent ) {
        // Need to add this pssw to its parent's list
        psswParent->AddChild( this );
        // Default is to be subWindow of parent
        psswParent->iSubWindow++;  // increment reference count
    }
}

/**************************************************************************\
* SSW constructor
*
* Used when wrapping an SSW around an already existing window
* (as when drawing GL on dialog buttons)
\**************************************************************************/

SSW::SSW( PSSW psswParentArg, HWND hwndArg )
{
    Reset();

    psswParent = psswParentArg;
    if( psswParent )
        // Need to add this pssw to its parent's list
        psswParent->AddChild( this );

    hwnd = hwndArg;
    if( !hwnd ) {
        SS_ERROR( "SSW::SSW : NULL hwnd\n" );
        return;
    }

    bOwnWindow = FALSE;
    // Get the window size
    GetWindowSize( hwnd, &size );

    gpss->sswTable.Register( hwnd, this );
}

/**************************************************************************\
* Reset
*
* Reset parameters to default init state
\**************************************************************************/

void
SSW::Reset()
{
    // Basic initialization

    bOwnWindow = TRUE;
    iSubWindow = 0;
    bValidateBg = FALSE;
    wFlags = 0;
    hwnd = 0;
    hdc = 0;
    hrc = 0;
    pos.x = pos.y = 0;
    size.width = size.height = 0;
    psswParent =    NULL;
    psswSibling =   NULL;
    psswChildren =  NULL;
    bDoubleBuf =    FALSE;
    pStretch =      NULL;
    pMotion =       NULL;
    pGLc =          NULL;

    InitFunc =      NULL;
    UpdateFunc =    NULL;
    ReshapeFunc =   NULL;
    RepaintFunc =   NULL;
    FloaterBounceFunc = NULL;
    FinishFunc =    NULL;
    ChildSizeFunc = NULL;
    DataPtr =       NULL;
}

/**************************************************************************\
* SSW destructor
*
* This can be called when a window is closed, or by the ss client
*
\**************************************************************************/

SSW::~SSW()
{
    // If this window has any children, they will have to be terminated too

    if( psswChildren ) {
        PSSW psswChild = psswChildren;
        while( psswChild ) {
            // Delete first child in list
            if( psswChild->hwnd && bOwnWindow ) {
                // We created this window, we must destroy it
    DestroyWindow( psswChild->hwnd );
            } else {
                delete psswChild;
            }
            // Next child is now first child in list
            psswChild = psswChildren;
        }
    }

    if( psswParent )
        // Need to remove this pssw from its parent's list
        psswParent->RemoveChild( this );

    if( hwnd ) {
        // Remove from SSWTable
        gpss->sswTable.Remove( hwnd );
    } else {
        // subWindow
        if( psswParent ) {
            SS_ASSERT1( (psswParent->iSubWindow > 0),
                 "Invalid subWindow reference count for pssw=0x%x\n", this );
            psswParent->iSubWindow--;  // decrement subWindow reference count
        }
    }

    // Clean up GL

    if( hrc ) {
        // FinishFunc still needs gl
        if( FinishFunc )
            (*FinishFunc)( DataPtr );

        wglMakeCurrent( NULL, NULL );
        if( ! (wFlags & SS_HRC_PROXY_BIT) )
            wglDeleteContext( hrc );
    }

    // Clean up any bitmaps

    if( pStretch ) {
        SS_BITMAP *pssbm = &pStretch->ssbm;

        DeleteObject(SelectObject(pssbm->hdc, pssbm->hbmOld));
        DeleteDC(pssbm->hdc);
    }

    //  Release the dc
    if( hdc ) {
        HWND hwndForHdc = hwnd ? hwnd : psswParent ? psswParent->hwnd : NULL;
        ReleaseDC(hwndForHdc, hdc);
    }
}

/**************************************************************************\
* AddChild
*
* Add the supplied child SSW to this SSW.
\**************************************************************************/

void
SSW::AddChild( PSSW psswChild )
{
    if( !psswChildren ) {
        psswChildren = psswChild;
        return;
    }

    // Else travel along the sibling chain of psswChildren and deposit
    // psswChild at the end

    PSSW pssw = psswChildren;
    while( pssw->psswSibling )
        pssw = pssw->psswSibling;
    pssw->psswSibling = psswChild;
}

/**************************************************************************\
* RemoveChild
*
* Remove this child from the parent's list
*
* Whoever calls this needs to update SSW_TABLE too...
\**************************************************************************/

BOOL
SSW::RemoveChild( PSSW psswChild )
{
    if( !psswChildren ) {
        // Something wrong - this window has no children
        SS_ERROR( "SSW::RemoveChild : no children\n" );
        return FALSE;
    }

    PSSW psswPrev;
    PSSW pssw = psswChildren;

    while( pssw != NULL ) {
        if( pssw == psswChild ) {
            // found it !
            if( psswChild == psswChildren )
                // The child being removed is the first in the list
                psswChildren = psswChild->psswSibling;
            else
                psswPrev->psswSibling = pssw->psswSibling;
            return TRUE;
        }
        // Move up the pointers
        psswPrev = pssw;
        pssw = psswPrev->psswSibling;
    }

    SS_ERROR( "SSW::RemoveChild : child not found\n" );
    return FALSE;
}

/**************************************************************************\
* GetWindowSize
*
\**************************************************************************/

static void
GetWindowSize( HWND hwnd, ISIZE *pSize )
{
    RECT clientRect;

    GetClientRect( hwnd, &clientRect );

    pSize->width = clientRect.right - clientRect.left + 1;
    pSize->height = clientRect.bottom - clientRect.top + 1;
}

/**************************************************************************\
* CreateSSWindow
*
* Create OpenGL floater window.  This window floats on top of the screen
* saver window, bouncing off each of the screen edges.
*
\**************************************************************************/

BOOL
SSW::CreateSSWindow(HINSTANCE hMainInstance,  UINT uStyle, UINT uExStyle ,
                  LPCTSTR pszWindowTitle, WNDPROC wndProcArg, LPCTSTR pszClassName, HWND hwndParentOverride )
{
    IPOINT2D startPos;
    HWND hwndParent;

    if( hwndParentOverride )
        hwndParent = hwndParentOverride;
    else
        hwndParent =  psswParent ? psswParent->hwnd : NULL;

    wndProc = wndProcArg;

    if( !pMotion )
        startPos = pos;
    else {
        // Initialize start position off screen to work around win95 screen
        // validation bug
        startPos.x = pos.x - psswParent->size.width;
        startPos.y = pos.y - psswParent->size.height;
    }

    hwnd = CreateWindowEx(
                                 uExStyle,
                                 pszClassName,
                                 pszWindowTitle,
                                 uStyle,
                                 startPos.x,
                                 startPos.y,
                                 size.width,     // width
                                 size.height,    // height
                                 hwndParent,
                                 NULL,               // menu
                                 hMainInstance,
                                 (LPVOID) this
                                );

    if (!hwnd) {
        SS_WARNING( "SSW::CreateSSWindow : CreateWindowEx failure\n" );
        return FALSE;
    }

    // This window is on its own now
    if( psswParent ) {
        SS_ASSERT1( (psswParent->iSubWindow > 0),
                 "Invalid subWindow reference count for pssw=0x%x\n", this );
        psswParent->iSubWindow--;  // decrement subWindow reference count
    }

    ShowWindow(hwnd, SW_SHOW);

    return TRUE;
}

/**************************************************************************\
* GetChildInfo
*
* Call the window's ChildSizeFunc
\**************************************************************************/

void
SSW::GetChildInfo( )
{
    if( !ChildSizeFunc )
        return;

    CHILD_INFO childInfo;

    // Call the client's SizeFunc to get required info

    (*ChildSizeFunc)( &psswParent->size, &childInfo );

    // Pull required values into pssw and validate them

    size = childInfo.size;
    ValidateChildSize();

    if( !pMotion ) {
        pos = childInfo.pos;
        bValidateChildPos();
    } else {
        pMotion->posInc = childInfo.motionInfo.posInc;
        pMotion->posIncVary = childInfo.motionInfo.posIncVary;
        pMotion->posIncCur = pMotion->posInc;
    }
}

/**************************************************************************\
* ConfigureForGdi
*
* Creates an hdc for the window
*
\**************************************************************************/

BOOL
SSW::ConfigureForGdi()
{
    if( hdc )
        // already configured
        return TRUE;

    // Figure window to get hdc from
    HWND hwndForHdc = hwnd ? hwnd : psswParent ? psswParent->hwnd : NULL;

    if( !hwndForHdc || !(hdc = GetDC(hwndForHdc)) ) {
        SS_WARNING( "SSW::ConfigureForGdi failed\n" );
        return FALSE;
    }
    return TRUE;
}

/**************************************************************************\
* ConfigureForGL
*
* Creates a GL rendering context for the specified window
*
\**************************************************************************/

BOOL
SSW::ConfigureForGL( SS_GL_CONFIG *pGLcArg )
{
    pGLc = pGLcArg;
    return ConfigureForGL();
}

BOOL
SSW::ConfigureForGL()
{
    if( hrc )
        return TRUE;

    if( ConfigureForGdi() &&
        (hrc = hrcSetupGL()) )
        return TRUE;

    SS_WARNING( "SSW::ConfigureForGL failed\n" );
    return FALSE;
}

/**************************************************************************\
* hrcSetupGL
*
* Setup OpenGL.
*
\**************************************************************************/

#define NULL_RC ((HGLRC) 0)

HGLRC
SSW::hrcSetupGL()
{
    if( !pGLc )
        return NULL_RC;

    HGLRC hrc;
    HDC hgldc;
    int pfFlags = pGLc->pfFlags;
    PIXELFORMATDESCRIPTOR pfd = {0};

    pStretch = pGLc->pStretch;
    if( pStretch ) {
        if( NeedStretchedWindow() ) {
            // Only need single buffered pixel format
            pfFlags &= ~SS_DOUBLEBUF_BIT;
            pfFlags |= SS_BITMAP_BIT; // yup, BOTH window and bitmap need this
        } else
            // Turn off stretching
            pStretch = NULL;
    }

    // If preview mode or config mode, don't allow pixel formats that need
    // the system palette, as this will create much ugliness.
    if( ss_fPreviewMode() || ss_fConfigMode() )
        pfFlags |= SS_NO_SYSTEM_PALETTE_BIT;

    // If config mode, force a non-accelerated pixel format, as WNDOBJ's
    // seem to have problems with MCD, ICD
    if( ss_fConfigMode() )
        pfFlags |= SS_GENERIC_UNACCELERATED_BIT;

    bDoubleBuf = SS_HAS_DOUBLEBUF( pfFlags );

    if( !SSU_SetupPixelFormat( hdc, pfFlags, &pfd ) )
        return NULL_RC;

    // Update pGLc->pfFlags based on pfd returned
    // (for now, the only ones we care about are the generic/accelerated flags)
    if(  (pfd.dwFlags & (PFD_GENERIC_FORMAT|PFD_GENERIC_ACCELERATED))
 == PFD_GENERIC_FORMAT )
        pGLc->pfFlags |= SS_GENERIC_UNACCELERATED_BIT;

    if( SSU_bNeedPalette( &pfd ) ) {
        // Note: even if bStretch, need to set up palette here so they match
        if( !gpss->pssPal ) {
            SS_PAL *pssPal;
            BOOL bTakeOverPalette = ss_fFullScreenMode() ? TRUE : FALSE;

            // The global palette has not been created yet - do it
            // SS_PAL creation requires pixel format descriptor for color bit
            // information, etc. (the pfd is cached in SS_PAL, since for
            // palette purposes it is the same for all windows)
            pssPal = new SS_PAL( hdc, &pfd, bTakeOverPalette );
            if( !pssPal )
                return NULL_RC;
            // Set approppriate palette manage proc
            if( ss_fFullScreenMode() )
                pssPal->paletteManageProc = FullScreenPaletteManageProc;
            else
                // use regular palette manager proc
                pssPal->paletteManageProc = PaletteManageProc;
            gpss->pssPal = pssPal;
        }
        // Realize the global palette in this window
        // assume we're realizing in foreground
        HWND hwndPal = hwnd ? hwnd : psswParent ? psswParent->hwnd : NULL;
        if( hwndPal )
            gpss->pssPal->Realize( hwndPal, hdc, FALSE );
    }

    if( pStretch ) {
        // Stretch blt mode: For every frame, we'll be doing a StretchBlt
        // from a DIB to the screen.  Need to set up a compatible memdc.
        SS_BITMAP *pssbm;

        pssbm = &pStretch->ssbm;

        pssbm->hdc = CreateCompatibleDC(hdc);
        if( !pssbm->hdc )
            return NULL_RC;
        ResizeStretch(); // this creates the DIB Section
        pfFlags = 0;
        pfFlags |= SS_BITMAP_BIT;
        if( !SSU_SetupPixelFormat( pssbm->hdc, pfFlags, &pfd ) ) {
            return NULL_RC;
        }
        // this ppfd's palette bits must match the window's !!
        // If window needs palette, so does bitmap...
        if( gpss->pssPal ) {
            SS_PAL *pssPal = gpss->pssPal;
            extern void ssw_UpdateDIBColorTable( HDC, HDC );

            ssw_UpdateDIBColorTable( pssbm->hdc, hdc );
        }
        hgldc = pssbm->hdc;
    } else {
        hgldc = hdc;
    }

    if( pGLc->hrc ) {
        // Use the supplied hrc
        hrc = pGLc->hrc;
        // Set flag so we don't delete this borrowed hrc when the SSW terminates
        wFlags |= SS_HRC_PROXY_BIT;
    } else
        // Create a new hrc
        hrc = wglCreateContext(hgldc);

    if( !hrc || !wglMakeCurrent(hgldc, hrc) ) {
        SS_WARNING( "SSW::hrcSetupGL : hrc context failure\n" );
        return NULL_RC;
    }

    if( !hwnd && (bDoubleBuf || pStretch) ) {

        // enable scissoring
        glEnable( GL_SCISSOR_TEST );

        if( !(pGLc->pfFlags & SS_GENERIC_UNACCELERATED_BIT) ) {
            // MCD or ICD, possible hardware implementation - we maintain
            // a lastRect to handle SwapBuffer issues
            lastRect.x = lastRect.y = lastRect.width = lastRect.height = 0;
        }
    }

    SS_DBGLEVEL2( SS_LEVEL_INFO,
        "SSW::hrcSetupGL: wglMakeCurrent( hrc=0x%x, hwnd=0x%x )\n", hrc, hwnd );

// Note that these queries are based on a single gl window screen saver.  In
// a more complicated scenario, these capabilities could be queried on a
// per-window basis (since support could vary with pixel formats).
    // Query the GL version - sets support for any new (e.g. 1.1) functionality
    ss_QueryGLVersion();

    // Query paletted texture extension
    ss_QueryPalettedTextureEXT();

    // Query the AddSwapHintRect WIN extension
    ss_QueryAddSwapHintRect();

    // Pull in any Func's that were already defined (for compatibility with
    // old mechanism)

    InitFunc = gInitFunc;
    UpdateFunc = gUpdateFunc;
    ReshapeFunc = gReshapeFunc;
    RepaintFunc = gRepaintFunc;
    FloaterBounceFunc = gFloaterBounceFunc;
    FinishFunc = gFinishFunc;
    DataPtr = gDataPtr;

    return hrc;
}

/**************************************************************************\
* MakeCurrent
*
* Call wglMakeCurrent for this window's hrc.  Note: an ss client may have
* more than one hrc (e.g. pipes), in which case it is the client's
* responsibility to make current.
\**************************************************************************/

void
SSW::MakeCurrent()
{
    if( ! wglMakeCurrent( hdc, hrc ) )
        SS_WARNING( "SSW::MakeCurrent : wglMakeCurrent failure\n" );
}

/**************************************************************************\
* InitGL
*
* Call the window's GL Init Func
*
* Priority is raised to expedite any initialization (e.g. loading and
* processing textures can take a while.
*
* A Reshape msg is sent to the client ss, as this is required for setting
* glViewport, etc.
\**************************************************************************/

void
SSW::InitGL()
{
    PSSW psswChild = psswChildren;

    // Configure the window for GL if pGLc non-NULL
    if( pGLc && (! ConfigureForGL()) ) {
        // This is fatal for this window - if it is the main window,
        // the ss will terminate
        if( hwnd )
            PostMessage( hwnd, WM_CLOSE, 0, 0l );
        return;
    }

    // If window configured for GL, hrc will have been set...

    // Call the InitFunc
    if( hrc && InitFunc ) {
        DWORD oldPriority;

        // Bump up priority during initialization phase
        oldPriority = GetPriorityClass( GetCurrentProcess() );
        SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS );

        SS_DBGLEVEL1( SS_LEVEL_INFO,
                "SSW::InitGL: Calling client GLInit for 0x%x\n", hwnd );

        (*InitFunc)( DataPtr );

        // restore original priority
        SetPriorityClass( GetCurrentProcess(), oldPriority );
    }

    /* Send another Reshape, since initial one triggered by window
     * creation would have been received before GL init'd
     */
    Reshape();

    // Next, init any child windows.  This has to be done after the parent
    // window initialization.

    while( psswChild ) {
        if( psswChild->hwnd )
            SendMessage( psswChild->hwnd, SS_WM_INITGL, 0, 0 );
        else
            // Must be a logical sub-window
            psswChild->InitGL();
        psswChild = psswChild->psswSibling;
    }
}

/**************************************************************************\
* ResizeStretch
*
* Resize the compatible bitmap for stretch blt mode
*
* There are 2 sizing modes.  If bRatioMode, then we set the bitmap size
* by dividing the window dimensions by the supplied ratios.  In this case,
* the base* values set a lower limit for the bitmap dimensions.  Otherwise, the
* base* values determine the bitmap dimensions.
*
\**************************************************************************/

void
SSW::ResizeStretch()
{
    HBITMAP hbmNew;
    PVOID pvBits;
    int width, height;
    int cwidth, cheight; // client area size
    SS_BITMAP *pssbm = &pStretch->ssbm;

    cwidth = size.width;
    cheight = size.height;

    if( pStretch->bRatioMode ) {
        width = (int) ( (float)cwidth / pStretch->widthRatio );
        height = (int) ( (float)cheight / pStretch->heightRatio );
        if( width < pStretch->baseWidth )
            width = pStretch->baseWidth;
        if( height < pStretch->baseHeight )
            height = pStretch->baseHeight ;
    } else {
        width = pStretch->baseWidth;
        height = pStretch->baseHeight;
    }

    // Limit width, height to window dimensions
    if( width > cwidth )
        width = cwidth;
    if( height > cheight )
        height = cheight;

    // If same size, get out
    if( (width == pssbm->size.width) && (height == pssbm->size.height) )
        return;

    pssbm->size.width = width;
    pssbm->size.height = height;

    // Use system palette
    hbmNew = SSDIB_CreateCompatibleDIB(hdc, NULL, width, height, &pvBits);

    if (hbmNew)
    {
        if (pssbm->hbm != (HBITMAP) 0)
        {
            SelectObject( pssbm->hdc, pssbm->hbmOld );
            DeleteObject( pssbm->hbm );
        }

        pssbm->hbm = hbmNew;
        pssbm->hbmOld = (HBITMAP) SelectObject( pssbm->hdc, pssbm->hbm );
    }
}

/**************************************************************************\
* Resize
*
* Resize wrapper
*
* Called in response to WM_SIZE.
*
\**************************************************************************/

void
SSW::Resize( int width, int height )
{
    size.width  = width;
    size.height = height;

    if( pStretch ) {
        // May have to resize associated bitmap
        ResizeStretch();
    }

    if( psswChildren ) {
        // May need to resize any children
        PSSW pssw = psswChildren;
        while( pssw ) {
            // Get new size/motion for the floater
            pssw->GetChildInfo();
            pssw->SetSSWindowPos();
            if( !pssw->hwnd ) {
                // Handle sub-window case
                // Need to call Reshape, since win32 system won't send WM_SIZE.
                pssw->Reshape();
            }
            pssw = pssw->psswSibling;
        }
    }

    Reshape();
}

/**************************************************************************\
* Repaint
*
* Repaint wrapper
*
* Called in response to WM_PAINT.
*
\**************************************************************************/

#define NULL_UPDATE_RECT( pRect ) \
     (  ((pRect)->left == 0) && \
        ((pRect)->right == 0) && \
        ((pRect)->top == 0) && \
        ((pRect)->bottom == 0) )

void
SSW::Repaint( BOOL bCheckUpdateRect )
{
    if( !hwnd )
        return;

    RECT rect, *pRect = NULL;

    if( bCheckUpdateRect ) {
        GetUpdateRect( hwnd, &rect, FALSE );
        // Above supposed to return NULL if rect is all 0's,
        // but this doesn't happen
        if( NULL_UPDATE_RECT( &rect ) )
            return;
        pRect = &rect;
    }

    if( RepaintFunc )
        (*RepaintFunc)( pRect, DataPtr );
}

/**************************************************************************\
* NeedStretchedWindow
*
* Check if stretch mode is necessary
*
\**************************************************************************/

BOOL
SSW::NeedStretchedWindow()
{
    if( (pStretch->baseWidth >= size.width) &&
        (pStretch->baseHeight >= size.height) ) {
        return FALSE;
    }
    return TRUE;
}

/**************************************************************************\
* SetSSWindowPos
*
* Set new size and position for an SSW
*
\**************************************************************************/

void
SSW::SetSSWindowPos()
{
    SetSSWindowPos( 0 );
}

void
SSW::SetSSWindowPos( int flags )
{
    if( hwnd ) {
        SetWindowPos(hwnd, 0, pos.x, pos.y,
                 size.width, size.height,
                 SWP_NOCOPYBITS | SWP_NOZORDER | SWP_NOACTIVATE | flags );
    // Note: If flags does not specify SWP_NOREDRAW, this generates a WM_PAINT
    // msg for the entire window region
    } else if( psswParent && !pStretch ) {
        // Set viewport for new position
        // (if pStretch, shouldn't need to do anything, as viewport should
        // have been already set to the bitmap)
        int posy = GLPosY();
        glViewport( pos.x, posy, size.width, size.height );
        glScissor( pos.x, posy, size.width, size.height );
        glAddSwapHintRect( pos.x, posy, size.width, size.height );
    }
}

/******************************Public*Routine******************************\
* SetAspectRatio
*
* Resize a child window to conform to the supplied aspect ratio.  We do this by
* maintaining the existing width, and adjusting the height.
*
* Window resize seems to be executed synchronously, so gl should be able to
* immediately validate its buffer dimensions (we count on it).
*
* Returns TRUE if new height is different from last, else FALSE.
\**************************************************************************/

BOOL
SSW::SetAspectRatio( FLOAT fAspect )
{
    if( !psswParent )
        return FALSE;

    int oldHeight;
    UINT uFlags = 0;

    // Check for zero fAspect
    if( fAspect == 0.0f ) fAspect = 1.0f;

    oldHeight = size.height;
    // Set the new height, based on desired aspect ratio
    size.height = (int) ((FLOAT) size.width / fAspect);
    // make sure new height not TOO big!
    ValidateChildSize();

    if( size.height == oldHeight )
        // no change
        return FALSE;

    // make sure current position still valid when height changes
    if( !bValidateChildPos() ) {
        // current position OK, don't need to move it
        uFlags |= SWP_NOMOVE;
    }
    SetSSWindowPos( uFlags );

    // ! remember not to call the client's ReshapeFunc here, as SetAspectRatio
    // may be called by the client in response to a window resize, resulting in
    // an infinite loop
    // but if there is no size change (above) we return, avoiding the
    // infinite loop
    if( !hwnd )
        // Need to call Reshape, since win32 system won't send WM_SIZE.  Need
        // to fix to avoid possible infinite loops
        Reshape();

    return TRUE;
}

/**************************************************************************\
* CalcNextWindowPos
*
* Calculate the next position for a moving window, using the pMotion data.
* If the new position would cause the window to bounce off the
* edge of its parent, return TRUE.
*
\**************************************************************************/

BOOL
SSW::CalcNextWindowPos()
{
    POINT2D *fpos = &pMotion->pos;
    IPOINT2D *ipos = &pos;
    POINT2D *posInc = &pMotion->posInc;
    POINT2D *posIncV = &pMotion->posIncVary;
    POINT2D *posIncCur = &pMotion->posIncCur;
    BOOL bounce = FALSE;

    if( !psswParent )
        return FALSE;

    // Compute the next window position.

    fpos->x += posIncCur->x;
    fpos->y += posIncCur->y;
    ipos->x = (int) fpos->x;
    ipos->y = (int) fpos->y;

    if ( (ipos->x + size.width) > psswParent->size.width) {
        // Right hit
        ipos->x = psswParent->size.width - size.width;

fpos->x = (float) ipos->x;
        posIncCur->x =
            - ss_fRand( posInc->x - posIncV->x, posInc->x + posIncV->x );
        if( posIncCur->x > -0.5f ) posIncCur->x = -0.5f;
        bounce = TRUE;
    } else if (ipos->x < 0) {
        // Left hit
        ipos->x = 0;
        fpos->x = 0.0f;
        posIncCur->x =
            ss_fRand( posInc->x - posIncV->x, posInc->x + posIncV->x );
        if( posIncCur->x < 0.5f ) posIncCur->x = 0.5f;
        bounce = TRUE;
    }

    if ( (ipos->y + size.height) > psswParent->size.height) {
        // Bottom hit
        ipos->y = psswParent->size.height - size.height;
        fpos->y = (float) (ipos->y);
        posIncCur->y =
            - ss_fRand( posInc->y - posIncV->y, posInc->y + posIncV->y );
        if( posIncCur->y > -0.5f ) posIncCur->y = -0.5f;
        bounce = TRUE;
    } else if (ipos->y < 0) {
        // Top hit
        ipos->y = 0;
        fpos->y = 0.0f;
        posIncCur->y =
            ss_fRand( posInc->y - posIncV->y, posInc->y + posIncV->y );
        if( posIncCur->y < 0.5f ) posIncCur->y = 0.5f;
        bounce = TRUE;
    }
    return bounce;
}

/**************************************************************************\
* MoveSSWindow
*
* This is the function that moves the OpenGL window around, causing it to
* bounce around.  Each time the window is moved, the contents of the
* window are updated from the hidden (or back) buffer by SwapBuffers().
*
* The bRedrawBg flag determines whether the area that was covered by the old
* position should be updated by the parent window.
*
\**************************************************************************/

void
SSW::MoveSSWindow( BOOL bRedrawBg )
{
    BOOL bounce;
    int flags = SWP_NOSIZE;

    // Synchronize with OpenGL.  Flush OpenGL commands and wait for completion.

    glFinish();

    // Move the window

    bounce = CalcNextWindowPos();

    if( bounce && FloaterBounceFunc )
        // The window bounced off one of the sides
        // ! This function should *not* be used for rendering - for
        // for informational purposes only (.e.g. changing the spin of a
        // rotating object).
        (*FloaterBounceFunc)( DataPtr );

    if( !bRedrawBg )
        flags |= SWP_NOREDRAW;
    SetSSWindowPos( flags );
}

/**************************************************************************\
* UpdateWindow
*
* Update the window
*
* Ccurrently this assumes all windows are being animated (i.e. not showing
*   a static image)
*
* Things *must* happen in the order defined here, so they work on generic as
* well as hardware implementations.
* Note: Move must happen after SwapBuf, and will cause some encroaching on
* the current display, as the parent window repaints after the move.  Therefore
* apps must take care to leave an empty border around their rendered image,
* equal to the maximum window move delta.
*
\**************************************************************************/

void
SSW::UpdateWindow()
{
    // update any children first

    PSSW pssw = psswChildren;
    while( pssw ) {
        pssw->UpdateWindow();
        pssw = pssw->psswSibling;
    }

    // If this window is a subWindow in a non-generic implementation, the
    // background of the parent may be invalid (this may eventually be
    // useful for regular windows, which is why we don't && !hwnd)
    if( psswParent &&
        psswParent->bValidateBg &&
        pGLc &&
        !(pGLc->pfFlags & SS_GENERIC_UNACCELERATED_BIT) )
    {
        // Clear the entire parent window
        // mf: I think this is only needed for double-buffered schemes, since
        // the windowing system should repaint the front buffer in this case.
        glDisable( GL_SCISSOR_TEST );
        glClear( GL_COLOR_BUFFER_BIT );
        glEnable( GL_SCISSOR_TEST );
        psswParent->bValidateBg = FALSE;
    }

    if( !UpdateFunc )
        return;

    // bDoubleBuf and pStretch should be mutually exclusive...

    if( bDoubleBuf || pStretch ) {
        UpdateDoubleBufWin();
    } else {
        if( pMotion )
            MoveSSWindow( TRUE );
        (*UpdateFunc)( DataPtr );
    }
}

/**************************************************************************\
* UpdateDoubleBufWin
*
* This is used when moving a double buffered window around.  It will
* work for all configurations.
*
\**************************************************************************/

void
SSW::UpdateDoubleBufWin()
{
    if( !hwnd ) {
        UpdateDoubleBufSubWin();
        return;
    }

    RECT updateRect;

    // Move the window

    if( pMotion ) {
        // Save child update rect before move
        GetSSWindowRect( &updateRect );
        // Move window, without repainting exposed area
        MoveSSWindow( FALSE );
    }

    // Update the back buffer

    (*UpdateFunc)( DataPtr );

    if( pMotion ) {
        // (pMotion will be NULL if this window has no parent)
        if( hwnd ) {
            // Paint the exposed area with bg brush (the current image will
            // be partially erased momentarily, until the SwapBuffers() call
            // comes through)
            // (This rect should be clipped to our new window position...)
            DrawGdiRect( psswParent->hdc, gpss->hbrBg, &updateRect );
        } else {
// currently this path not possible, since if !hwnd, we use one of the
// UpdateDoubleBufSubWin* functions
            SS_WARNING( "SSW::UpdateDoubleBufWin: no hwnd\n" );
            // sub-window case : need to do our own clipping
            RECT newRect;
            GetSSWindowRect( &newRect );
            DrawGdiDeltaRect( psswParent->hdc, gpss->hbrBg, &updateRect, &newRect );
        }
    }

    // Swap to the new window position
    SwapSSBuffers();
}

/**************************************************************************\
* UpdateDoubleBufSubWin
*
* Used for generic double buffered gl sub-windows.
*
\**************************************************************************/

void
SSW::UpdateDoubleBufSubWin()
{
    GLRECT curRect, newRect;

    // AddSwapHintRect for current position
    glAddSwapHintRect( pos.x, GLPosY(), size.width, size.height );

    if( pMotion ) {

        // Save current rect
        curRect.x = pos.x;
        curRect.y = GLPosY();
        curRect.width = size.width;
        curRect.height = size.height;

        // Move window, without repainting exposed area
        MoveSSWindow( FALSE );

        // Get new rect
        newRect.x = pos.x;
        newRect.y = GLPosY();
        newRect.width = size.width;
        newRect.height = size.height;

        DrawGLDeltaRect( &curRect, &newRect );

        // Have to consider previous rect for ICD or MCD
        if( !(pGLc->pfFlags & SS_GENERIC_UNACCELERATED_BIT) ) {
            DrawGLDeltaRect( &lastRect, &newRect );
            lastRect = curRect;
        }

        // Reset scissor to new rect (this *was* set by MoveSSWindow, but
        // DrawGLDeltaRect sets scissor to do its clearing
        glScissor( newRect.x, newRect.y, newRect.width, newRect.height );
    }

    // Update the back buffer

    (*UpdateFunc)( DataPtr );

    // Swap to the new window position
    SwapSSBuffers();
}

/******************************Public*Routine******************************\
* RandomWindowPos
*
* Sets a new random window position and motion
*
\**************************************************************************/

void
SSW::RandomWindowPos()
{
    if( psswParent ) {
        if( !hwnd ) {
            // sub-window : manually clear old window rect
            if( bDoubleBuf ) {
                glClear( GL_COLOR_BUFFER_BIT );
            } else {
                RECT oldRect;
                GetSSWindowRect( &oldRect );
                DrawGdiRect( psswParent->hdc, gpss->hbrBg, &oldRect );
            }
        }

        // Calc and set new position
        pos.x = ss_iRand2( 0, (psswParent->size.width - size.width) );
        pos.y = ss_iRand2( 0, (psswParent->size.height - size.height) );
        SetSSWindowPos( SWP_NOSIZE );

        // Reset motion
        if( pMotion )
            ResetMotion();
    }
}

/**************************************************************************\
* ResetMotion
*
* Calculate a random position and motion vector for the floater window
* Note that a floating point position is maintained for DDA window movement
*
\**************************************************************************/

void
SSW::ResetMotion()
{
    if( !psswParent || !pMotion )
        // Only child windows can be reset
        return;

    // Set floating point pos also, for DDA
    pMotion->pos.x = (float) pos.x;
    pMotion->pos.y = (float) pos.y;

    // also reset the window motion directions

    if( ss_iRand(2) )  // 0 or 1
        pMotion->posIncCur.x = - pMotion->posIncCur.x;
    if( ss_iRand(2) )
        pMotion->posIncCur.y = - pMotion->posIncCur.y;
}

/**************************************************************************\
* ValidateChildSize
*
* Make sure it's not bigger than its parent
*
\**************************************************************************/

void
SSW::ValidateChildSize()
{
    if( !psswParent )
        return;

    SS_CLAMP_TO_RANGE2( size.width, 0, psswParent->size.width );
    SS_CLAMP_TO_RANGE2( size.height, 0, psswParent->size.height );
}

/**************************************************************************\
* bValidateChildPos
*
* Make sure that with the current window position, none of the floating
* window extends beyond the parent window.
*
\**************************************************************************/

BOOL
SSW::bValidateChildPos()
{
    BOOL bRet = FALSE;

    if( !psswParent )
        return FALSE;

    if ( (pos.x + size.width) > psswParent->size.width) {
        pos.x = psswParent->size.width - size.width;
        bRet = TRUE;
    }

    if ( (pos.y + size.height) > psswParent->size.height) {
        pos.y = psswParent->size.height - size.height;
        bRet = TRUE;
    }
    return bRet;
}

/**************************************************************************\
* GetSSWindowRect
*
* Return window position and size in supplied RECT structure
*
* this rect is relative to the parent
\**************************************************************************/

void
SSW::GetSSWindowRect( LPRECT lpRect )
{
    lpRect->left = pos.x;
    lpRect->top = pos.y;
    lpRect->right = pos.x + size.width;
    lpRect->bottom = pos.y + size.height;
}

/**************************************************************************\
* GLPosY
*
* Return y-coord of window position in GL coordinates (a win32 window position
* (starts from top left, while GL starts from bottom left)
*
\**************************************************************************/

int
SSW::GLPosY()
{
    if( !psswParent )
        return 0;

    return psswParent->size.height - size.height - pos.y;
}

/**************************************************************************\
* SwapStretchBuffers
*
* Swaps from the stretch buffer to the GL window, using StretchBlt
*
\**************************************************************************/

void
SSW::SwapStretchBuffers()
{
    SS_BITMAP *pssbm = &pStretch->ssbm;

    if( (size.width == pssbm->size.width) &&
        (size.height == pssbm->size.height) ) // buffers same size
    {
        BitBlt(hdc, 0, 0, size.width, size.height,
               pssbm->hdc, 0, 0, SRCCOPY);
    }
    else
    {
        StretchBlt(hdc, 0, 0, size.width, size.height,
                   pssbm->hdc, 0, 0, pssbm->size.width, pssbm->size.height,
                   SRCCOPY);
    }
    GdiFlush();
}

/**************************************************************************\
* SwapBuffers
*
* Wrapper for SwapBuffers / SwapStretchBuffers
*
\**************************************************************************/

void
SSW::SwapSSBuffers()
{
    if( pStretch )
        SwapStretchBuffers();
    else if( bDoubleBuf )
        SwapBuffers( hdc );
}

/**************************************************************************\
* Reshape
*
* Reshape wrapper

* Sends reshape msg to screen saver
* This is the size of the surface that gl renders onto, which can be a bitmap.
*
\**************************************************************************/

void
SSW::Reshape()
{
    // Point to size of window, or bitmap if it has one
    ISIZE *pSize = &size;
    if( pStretch )
        pSize = &pStretch->ssbm.size;

    // If the window has an hrc, set default viewport

    if( hrc ) {
        if( hwnd )
            glViewport( 0, 0, pSize->width, pSize->height );
        else if ( psswParent ) {
            // sub-window (only 1 level of sub-windowing supported)

            // clear entire window to black
            glDisable( GL_SCISSOR_TEST );
            glClear( GL_COLOR_BUFFER_BIT );
            glEnable( GL_SCISSOR_TEST );

            // Convert win32 y-coord to GL
            glViewport( pos.x, GLPosY(), pSize->width, pSize->height );
        }
    }

    if( ReshapeFunc ) {
        (*ReshapeFunc)( pSize->width, pSize->height, DataPtr );
    }
}

/******************************Public*Routine******************************\
* GdiClear
*
* Clears window using Gdi FillRect
\**************************************************************************/

void
SSW::GdiClear()
{
    if( !hdc )
        return;

    RECT rect;

    GetSSWindowRect( &rect );

    FillRect( hdc, &rect, gpss->hbrBg );
    GdiFlush();
}

/******************************Public*Routine******************************\
* MyAddSwapHintRect
*
\**************************************************************************/

static void _stdcall
MyAddSwapHintRect(GLint xs, GLint ys, GLint xe, GLint ye)
{
    return;
}

/******************************Public*Routine******************************\
* QueryAddSwapHintRectWIN
*
\**************************************************************************/

static void
ss_QueryAddSwapHintRect()
{
    glAddSwapHintRect = (PFNGLADDSWAPHINTRECTWINPROC)
        wglGetProcAddress("glAddSwapHintRectWIN");
    if (glAddSwapHintRect == NULL) {
        glAddSwapHintRect = MyAddSwapHintRect;
    }
}

/******************************Public*Routine******************************\
* DrawGdiDeltaRect
*
* Draw the exposed area by transition from rect1 to rect2
\**************************************************************************/

static void
DrawGdiDeltaRect( HDC hdc, HBRUSH hbr, RECT *pRect1, RECT *pRect2 )
{
    if( (pRect1 == NULL) || (pRect2 == NULL) ) {
        SS_WARNING( "DrawGdiDeltaRect : one or both rects are NULL\n" );
        return;
    }

    // Draw 2 rects

    RECT rect;

    // Rect exposed in x-direction:

    rect.top = pRect1->top;
    rect.bottom = pRect1->bottom;
    if( pRect2->left > pRect1->left ) {
        // moving right
        rect.left = pRect1->left;
        rect.right = pRect2->left;
    } else {
        // moving left
        rect.left = pRect2->right;
        rect.right = pRect1->right;
    }
    FillRect( hdc, &rect, hbr );

    // Rect exposed in y-direction:

    rect.left = pRect1->left;
    rect.right = pRect1->right;
    if( pRect2->bottom > pRect1->bottom ) {
        // moving down
        rect.top = pRect1->top;
        rect.bottom = pRect2->top;
    } else {
        // moving up
        rect.top = pRect2->bottom;
        rect.bottom = pRect1->bottom;
    }
    FillRect( hdc, &rect, hbr );

    GdiFlush();
}

/******************************Public*Routine******************************\
* DrawGLDeltaRect
*
* Draw the exposed area by transition from rect1 to rect2
\**************************************************************************/

static void
DrawGLDeltaRect( GLRECT *pRect1, GLRECT *pRect2 )
{
    if( (pRect1 == NULL) || (pRect2 == NULL) ) {
        SS_WARNING( "DrawGLDeltaRect : one or both rects are NULL\n" );
        return;
    }

    // Draw 2 rects :

//mf: !!! this assumes rect1 and rect2 have same dimensions !

    GLRECT rect;

    // Rect exposed in x-direction:

    rect.height = pRect1->height;
    rect.y = pRect1->y;

    if( pRect2->x > pRect1->x ) {
        // moving right
        rect.width = pRect2->x - pRect1->x;
        rect.x = pRect1->x;
    } else {
        // moving left
        rect.width = pRect1->x - pRect2->x;
        rect.x = pRect2->x + pRect2->width;
    }

    glScissor( rect.x, rect.y, rect.width, rect.height );
    glClear( GL_COLOR_BUFFER_BIT );

    // Rect exposed in y-direction:

    rect.width = pRect1->width;
    rect.x = pRect1->x;

    if( pRect2->y > pRect1->y ) {
        // moving up
        rect.height = pRect2->y - pRect1->y;
        rect.y = pRect1->y;
    } else {
        // moving down
        rect.height = pRect1->y - pRect2->y;
        rect.y = pRect2->y + pRect2->height;
    }

    glScissor( rect.x, rect.y, rect.width, rect.height );
    glClear( GL_COLOR_BUFFER_BIT );
}


SSWPROC.CXX

/******************************Module*Header*******************************\
* Module Name: sswproc.cxx
*
* Window procedure functions.
*
* Copyright 1996 - 1998 Microsoft Corporation
\**************************************************************************/

#include <windows.h>
#include <commdlg.h>
#include <scrnsave.h>
#include <GL\gl.h>
#include <math.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>

#include "ssintrnl.hxx"
#include "sswproc.hxx"
#include "palette.hxx"
#include "clear.hxx"

// forward declarations of internal functions

static void ss_TimerProc();
LONG SS_ScreenSaverProc(HWND, UINT, WPARAM, LPARAM);

LONG
FullScreenPaletteManageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

static void
ssw_RelayMessageToChildren( PSSW pssw, UINT msg, WPARAM wParam, LPARAM lParam);

static void ssw_RealizePalette( PSSW pssw, BOOL bBackground );
static void ssw_DeletePalette( PSSW pssw );

/**************************************************************************\
* ScreenSaverProc
*
* Processes messages for the top level screen saver window.
*
* Unhandled msgs are sent to DefScreenSaverProc
\**************************************************************************/

LONG
ScreenSaverProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static BOOL bInited = FALSE;
    static UINT idTimer = 0;
    // Draw timer time-out interval
#ifdef SS_DEBUG
    static UINT uiTimeOut = 2;  // Let it rip !
#else
    static UINT uiTimeOut = 16; // Cap at ~60 fps
#endif
    static BOOL bSuspend = FALSE;
#ifdef SS_WIN95_TIMER_HACK
    static BOOL bIdle = FALSE;
#endif
    PSSW pssw;

    switch (message)
    {
        case WM_CREATE:
        case WM_ERASEBKGND:
        case SS_WM_INITGL:
            return SS_ScreenSaverProc( hwnd, message, wParam, lParam);

        case WM_ACTIVATE:
            if ( LOWORD(wParam) == WA_INACTIVE ) {
                SS_DBGMSG( "Main_Proc: WM_ACTIVATE inactive\n" );
                gpss->bInForeground = FALSE;
            } else {
                SS_DBGMSG( "Main_Proc: WM_ACTIVATE active\n" );
                gpss->bInForeground = TRUE;
            }

            // fall thru

        case WM_QUERYNEWPALETTE:
        case WM_PALETTECHANGED:
        case WM_SYSCOLORCHANGE:
        case SS_WM_PALETTE:
            return( MainPaletteManageProc( hwnd, message, wParam, lParam ) );

        case SS_WM_START:
            SS_DBGMSG( "Main_Proc: SS_WM_START\n" );
            // This is the main GL startup point. The global animation timer
            // is started, and SS_WM_START is relayed to the window chain.

// for 'delayed background paint' problem in preview mode - in
// floater type ss's, the main window gets this delayed bg paint thing that
// makes the floater obvious.  By relenquishing our time slice here, the
// problem goes away...
            if( gpss->type == SS_TYPE_PREVIEW ) {
                Sleep(0);
            }

            // Initialize the animation timer - it should start up once we
            // return from here

            idTimer = 1;
            SetTimer(hwnd, idTimer, uiTimeOut, 0);

#ifdef SS_DEBUG
            // Start timer for calculating update rate
            gpss->timer.Start();
#endif
            if( !bInited )
                bInited = TRUE;

            // Process SS_WM_START for the window chain
            return SS_ScreenSaverProc( hwnd, message, wParam, lParam);

#ifdef SS_WIN95_TIMER_HACK
        case SS_WM_IDLE :
            if( wParam == SS_IDLE_ON )
                bIdle = TRUE;
            else if( wParam == SS_IDLE_OFF )
                bIdle = FALSE;
            break;
#endif

        case WM_DESTROY:
            if (idTimer) {
                KillTimer(hwnd, idTimer);
                idTimer = 0;
            }

            // Destroy any children of the top level window

            pssw = gpss->sswTable.PsswFromHwnd( hwnd );

            PSSW psswChild;
            psswChild = pssw->psswChildren;

            while( psswChild ) {
                if( psswChild->hwnd )
                    DestroyWindow( psswChild->hwnd );
                psswChild = pssw->psswSibling;
            }

            // Handle any palette stuff

// Before deleting this top level window, we need to use its
// still-valid dc to things like restore SystemPaletteUse mode.  Ideally this
// should be done after ~SSW has dumped GL, but ~SSW also releases the DC.
// If this is a problem, we can create a new function SSW::DeleteGL that just
// gets rid of the GL part

            if( gpss->pssPal )
                ssw_DeletePalette( pssw );

            // Dump the main pssw
            delete pssw;

            // All pssw's have now been deleted - remove global ptr to top of
            // window chain.
            gpss->psswMain = NULL;

            PostQuitMessage(0);
            return 0;

        case SS_WM_CLOSING:
            // This message is sent when the screen saver receives a WM_CLOSE
            // msg, *after* any password protection routines.
            // For now, only sent in /s mode
            SS_DBGMSG( "Main_Proc: SS_WM_CLOSING\n" );

            if( gpss->bResSwitch ) {
                // pssw->GdiClear();
                // mf: untested for child window case
                // Restore previous display settings
                // Note that this is also checked for in ~SCRNSAVE, in
                // case this message is not hit.
                ChangeDisplaySettings(NULL, CDS_FULLSCREEN);
                gpss->bResSwitch = FALSE;
            }
            return 0;

        case WM_SETFOCUS:
            SS_DBGMSG( "Main_Proc: WM_FOCUS\n" );
            // this catches some of the win95 passwd dialog problems, where
            // we don't get repaint msgs when dialogs end
            if( ss_fOnWin95() && ss_fFullScreenMode() ) {
                pssw = gpss->sswTable.PsswFromHwnd( hwnd );
                pssw->Repaint( FALSE );
            }
            break;

#if DBG
        case WM_SHOWWINDOW:
            SS_DBGMSG( "Main_Proc: WM_SHOWWINDOW\n" );
            break;
#endif

        case WM_PAINT:
            SS_DBGMSG( "Main_Proc: WM_PAINT\n" );

            pssw = gpss->sswTable.PsswFromHwnd( hwnd );
            pssw->Repaint( TRUE );

#ifdef SS_DO_PAINT
            // We do the painting rather than letting the system do it
            hdc = BeginPaint(hwnd, &ps);

            // This is case where bg brush is NULL and we have to do repaint
            // We only do it after bInited, as this will avoid the first
            // WM_PAINT for the entire window.
            if( bInited )
                DrawGdiRect( hdc, gpss->hbrBg, &ps.rcPaint );
            EndPaint(hwnd, &ps);
#endif // SS_DO_PAINT

#ifdef SS_DELAYED_START_KLUGE
            if( !bInited && SS_DELAY_START(gpss->type) ) {
                bInited = TRUE;

                // Do initial GL configuration
                PostMessage( hwnd, SS_WM_INITGL, 0, 0 );
                // Start drawing
                PostMessage( hwnd, SS_WM_START, 0, 0 );
            }
#endif // SS_DELAYED_START_KLUGE

            if( pssw->iSubWindow ) {
                // If this window has sub windows, mark the bg for validation,
                // since for hardware double buffered schemes, Swapbuffers
                // may swap in ugly garbage.
                pssw->bValidateBg = TRUE;
            }

#ifdef SS_DO_PAINT
            return 0; // painting has been handled by us
#endif // SS_DO_PAINT

            break;

        case WM_SIZE:
            // Suspend drawing if minimized
            if( wParam == SIZE_MINIMIZED )
                bSuspend = TRUE;
            else  // either SIZE_RESTORED or SIZE_MAXIMIZED
                bSuspend = FALSE;

            return SS_ScreenSaverProc( hwnd, message, wParam, lParam);

        case WM_MOVE:
            SS_DBGMSG( "Main_Proc: WM_MOVE\n" );
            // See note for WM_PAINT for subWindows
            pssw = gpss->sswTable.PsswFromHwnd( hwnd );

            //mf: kluge for non-floater workaround : see above
            if( pssw->iSubWindow )
                pssw->bValidateBg = TRUE;

            break;

        case WM_TIMER:
            if( bSuspend )
                return 0;

#ifdef SS_WIN95_TIMER_HACK
            if( bIdle ) {
                // We are in an idle state, and don't want to flood the queue
                // with WM_TIMER mesages.  So we kill the timer, do our
                // drawing, then start another timer.

                // Kill current timer
                if (idTimer)
                    KillTimer(hwnd, idTimer);
                else
                    // unlikely, but what the hay
                    return 0;
            }
#endif

            ss_TimerProc();

#ifdef SS_WIN95_TIMER_HACK
            if( bIdle ) {
                // Start another animation timer after we've done drawing
                SetTimer(hwnd, idTimer, uiTimeOut, 0);
            }
#endif
            return 0;
    }

    return DefScreenSaverProc(hwnd, message, wParam, lParam);
}

/**************************************************************************\
* SS_ScreenSaverProc
*
* Wndproc for child windows, and some messages from top-level window
*
* Unhandled msgs are sent to DefWindowProc
\**************************************************************************/

LONG
SS_ScreenSaverProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    PSSW pssw;

    switch (message)
    {
        case WM_CREATE:
            SS_DBGMSG1( "SS_Proc: WM_CREATE for 0x%x\n", hwnd );

            pssw = (PSSW) ( ((LPCREATESTRUCT)lParam)->lpCreateParams );
            gpss->sswTable.Register( hwnd, pssw );

            pssw->size.width  = ((LPCREATESTRUCT)lParam)->cx;
            pssw->size.height = ((LPCREATESTRUCT)lParam)->cy;
            pssw->hwnd = hwnd;
            break;

        case SS_WM_INITGL:
            SS_DBGMSG1( "SS_Proc: SS_WM_INITGL for 0x%x\n", hwnd );

            pssw = gpss->sswTable.PsswFromHwnd( hwnd );
            pssw->InitGL();

            break;

#ifdef SS_MULTI_WINDOW_TIMERS
// Enable this section and fill it in if windows start their own animation
// timers.  For now, there is just one timer on the main window.
        case SS_WM_START:
            SS_DBGMSG1( "SS_Proc: SS_WM_START for 0x%x\n", hwnd );

            pssw = gpss->sswTable.PsswFromHwnd( hwnd );

            // Send SS_WM_START to any children of this window
            ssw_RelayMessageToChildren( pssw, SS_WM_START, 0, 0 );

            // Nothing really to do here yet...

            break;
#endif

        case SS_WM_PALETTE:
            return( MainPaletteManageProc( hwnd, message, wParam, lParam ) );

        case WM_DESTROY:
            pssw = gpss->sswTable.PsswFromHwnd( hwnd );

            SS_DBGMSG1( "SS_Proc: WM_DESTROY for 0x%x\n", hwnd );
            // Kill off any children of this window first

            PSSW psswChild;
            psswChild = pssw->psswChildren;

            while( psswChild ) {
                if( psswChild->hwnd )
                    DestroyWindow( psswChild->hwnd );
                else
                    delete psswChild;
                psswChild = pssw->psswSibling;
            }
            // Delete the pssw - this does all necessary cleanup

            delete pssw;
            break;

        case WM_ERASEBKGND:
            SS_DBGMSG1( "SS_Proc: WM_ERASEBKGRND for 0x%x\n", hwnd );
            pssw = gpss->sswTable.PsswFromHwnd( hwnd );
            pssw->Repaint( FALSE );
            // Don't process this message
            return DefWindowProc(hwnd, message, wParam, lParam);

        case WM_PAINT:
            // We get this msg every time window moves, since SWP_NOCOPYBITS is
            // specified with the window move.
            hdc = BeginPaint(hwnd, &ps);
            EndPaint(hwnd, &ps);
            break;

        case WM_SIZE:
            pssw = gpss->sswTable.PsswFromHwnd( hwnd );
            pssw->Resize( LOWORD(lParam), HIWORD(lParam) );
            break;

        // these msg's are never received by the child window ?
        case WM_ACTIVATE:
        case WM_QUERYNEWPALETTE:
        case WM_PALETTECHANGED:
            return( MainPaletteManageProc( hwnd, message, wParam, lParam ) );

        case WM_SYSCOMMAND:
        case WM_SETCURSOR:
        case WM_ACTIVATEAPP:
        case WM_MOUSEMOVE:
        case WM_LBUTTONDOWN:
        case WM_MBUTTONDOWN:
        case WM_RBUTTONDOWN:
        case WM_KEYDOWN:
        case WM_SYSKEYDOWN:
            pssw = gpss->sswTable.PsswFromHwnd( hwnd );
            return DefScreenSaverProc(pssw->psswParent->hwnd, message, wParam, lParam);

        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

#ifdef SS_DEBUG
/**************************************************************************\
* PrintUpdateRate
*
* Print number of updates per second in the title bar
*
\**************************************************************************/

static void
PrintUpdateRate( double elapsed, long updateCount )
{
    char buf[100];
    double updateRate;

    if( elapsed == 0.0 )
        updateRate = 0.0;
    else
        updateRate = updateCount / elapsed;

    sprintf( buf, "Updates per second = %4.1f", updateRate );
    SendMessage(gpss->psswMain->hwnd, WM_SETTEXT, 0, (LPARAM)buf);
}
#endif

/**************************************************************************\
* ss_TimerProc
*
* Every time a timer event fires off, update all active windows
*
\**************************************************************************/

static void
ss_TimerProc()
{
    static int busy = FALSE;
#ifdef SS_DEBUG
    static long updateCount = 0;
    static double updateInterval = 2.0;
    SS_TIMER *pTimer = &gpss->timer;
#endif

    if (busy)
        return;
    busy = TRUE;

    gpss->psswMain->UpdateWindow();

#ifdef SS_DEBUG
    updateCount++;
    if( gpss->bDoTiming &&
        (( (double) pTimer->ElapsedTime() ) >= updateInterval) )
    {
        double elapsed = pTimer->Stop();
        PrintUpdateRate( elapsed, updateCount );
        updateCount = 0;
        pTimer->Start();
    }
#endif

    busy = FALSE;
}

/**************************************************************************\
* RelayMessageToChildren
*
* Pass along the message to any child windows
\**************************************************************************/

static void
ssw_RelayMessageToChildren( PSSW pssw, UINT msg, WPARAM wParam, LPARAM lParam)
{
    PSSW psswChild = pssw->psswChildren;

    while( psswChild ) {
        if( psswChild->hwnd )
            SendMessage( psswChild->hwnd, msg, wParam, lParam );
        psswChild = psswChild->psswSibling;
    }
}

/**************************************************************************\
* UpdateDIBColorTable
*
* Wrapper for SSDIB_UpdateColorTable.
*
* This controls the hPal parameter for SSDIB_UpdateColorTable.
*
\**************************************************************************/

void
ssw_UpdateDIBColorTable( HDC hdcbm, HDC hdcwin )
{
    SS_PAL *pssPal = gpss->pssPal;

    if( !pssPal )
        return;
    HPALETTE hpal =  pssPal->hPal;

    SSDIB_UpdateColorTable( hdcbm, hdcwin, hpal );
}

/**************************************************************************\
* RealizePalette
*
\**************************************************************************/

static void
ssw_RealizePalette( PSSW pssw, BOOL bBackground )
{
    // assumed pssPal valid if get here
    SS_PAL *pssPal = gpss->pssPal;

    if( !pssw->hrc ) {
        // Can assume this window doesn't need to worry about palettes, but
        // if any of its children are subWindows, it will have to take care
        // of it *for* them.
        if( ! pssw->iSubWindow )
            return; // no hrc and no subWindow children
    }
    pssPal->Realize( pssw->hwnd, pssw->hdc, bBackground );

    if( pssw->pStretch && pssw->pStretch->ssbm.hdc ) {
        SS_BITMAP *pssbm = &pssw->pStretch->ssbm;
        ssw_UpdateDIBColorTable( pssbm->hdc, pssw->hdc );
    }
}

/**************************************************************************\
* ssw_DeletePalette
*
\**************************************************************************/

static void
ssw_DeletePalette( PSSW pssw )
{
    SS_PAL *pssPal = gpss->pssPal;

    if( pssPal->bTakeOver ) {
        // We took over the system palette - make a note of this
        // for any special ss termination conditions.
        gpss->flags |= SS_PALETTE_TAKEOVER;
    }
    pssPal->SetDC( pssw->hdc );
    delete pssPal;
    gpss->pssPal = NULL;
}

/**************************************************************************\
* PaletteManage Procs
\**************************************************************************/

/* palette related msgs's:
    - WM_ACTIVATE:
        The WM_ACTIVATE message is sent when a window is being activated or
        deactivated. This message is sent first to the window procedure of
        the top-level window being deactivated; it is then sent to the
        window procedure of the top-level window being activated.

    - WM_QUERYNEWPALETTE:
        The WM_QUERYNEWPALETTE message informs a window that it is about
        to receive the keyboard focus, giving the window the opportunity
        to realize its logical palette when it receives the focus.

        If the window realizes its logical palette, it must return TRUE;
        otherwise, it must return FALSE.

    - WM_PALETTECHANGED:
        The WM_PALETTECHANGED message is sent to all top-level and overlapped
        windows after the window with the keyboard focus has realized its
        logical palette, thereby changing the system palette. This message
        enables a window that uses a color palette but does not have the
        keyboard focus to realize its logical palette and update its client
        area.

        This message must be sent to all top-level and overlapped windows,
        including the one that changed the system palette. If any child
        windows use a color palette, this message must be passed on to them
        as well.
        To avoid creating an infinite loop, a window that receives this
        message must not realize its palette, unless it determines that
        wParam does not contain its own window handle.

    - WM_SYSCOLORCHANGE:
        The WM_SYSCOLORCHANGE message is sent to all top-level windows when
        a change is made to a system color setting.

    - SS_WM_PALETTE:
        Internal msg.  Uses:
        - In fullscreen mode, we send this from Main wndproc to main
          window's children on WM_ACTIVATE.
        - When this is received in SS_ScreenSaverProc, if fullscreen,
          it does:
                    UnrealizeObject( pssPal->hPal );
                    RealizePalette( hdc );
          otherwise, it is passed to PaletteManageProc, where
          Realize is called (for 'floater' windows to realize
          their palettes).
        - It is also sent by DelayPaletteRealization() when it can't get
          the system palette.

*/

/**************************************************************************\
* MainPaletteManageProc
*
* Top-level palette management proc.

* Returns immediately if no palette set - otherwise calls through
* paletteManageProc function pointer
*
\**************************************************************************/

LONG
MainPaletteManageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if( !gpss->pssPal )
        // No palette management required
        return 0;

    // else call approppriate palette manage proc
    return (*gpss->pssPal->paletteManageProc)(hwnd, message, wParam, lParam);
}

LONG
NullPaletteManageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return 0;
}

/**************************************************************************\
* FullScreenPaletteManageProc
*
* Processes messages relating to palette management in full screen mode.
*
\**************************************************************************/

LONG
FullScreenPaletteManageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    SS_PAL *pssPal;
    PSSW pssw;

    switch (message)
    {
        case WM_ACTIVATE:

#if SS_DEBUG
            if ( LOWORD(wParam) == WA_INACTIVE )
                SS_DBGMSG1( "FullScreen_PMProc: WM_ACTIVATE : inactive for 0x%x\n",
                           hwnd );
            else
                SS_DBGMSG1( "FullScreen_PMProc: WM_ACTIVATE : active for 0x%x\n",
                           hwnd );
#endif

            // !! This msg is only sent to main top level window
            pssw = gpss->sswTable.PsswFromHwnd( hwnd );

            pssPal = gpss->pssPal;
            if ( pssPal->bUseStatic ) {
                HDC hdc = pssw->hdc; // hdc *always* valid for top-level pssw
                // Note: wParam = 0 when window going *inactive*
                SetSystemPaletteUse( hdc, wParam ? SYSPAL_NOSTATIC
                                                : pssPal->uiOldStaticUse);
            }

            // Send SS_WM_PALETTE msg to main window
            SendMessage( hwnd, SS_WM_PALETTE, wParam, 0);
            break;

        case SS_WM_PALETTE:

            SS_DBGMSG1( "FullScreen_PMProc: SS_WM_PALETTE for 0x%x\n", hwnd );

            pssw = gpss->sswTable.PsswFromHwnd( hwnd );

            ssw_RelayMessageToChildren( pssw, SS_WM_PALETTE, wParam, 0 );

            HDC hdc;
            if( hdc = pssw->hdc )
            {
                pssPal = gpss->pssPal;

// this should call thru ssw_RealizePalette for bitmap case ? (for now
// don't need to, since we take over palette...)
                // This resets the logical palette, causing remapping of
                // logical palette to system palette
                UnrealizeObject( pssPal->hPal );
                RealizePalette( hdc );
            }
            break;
    }
    return 0;
}

/**************************************************************************\
* PaletteManageProc
*
* Processes messages relating to palette management for the general case.
*
* Note: this msg handling strategy is based loosely on the tk, so any changes
* there should be reflected here
\**************************************************************************/

LONG
PaletteManageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // One global palette for all windows
    if( !gpss->pssPal )
        return 0;

    switch (message)
    {
      case WM_ACTIVATE:

        SendMessage( hwnd, SS_WM_PALETTE, gpss->bInBackground(), 0);

        // Allow DefWindowProc() to finish the default processing (which
        // includes changing the keyboard focus).

        break;

      case WM_QUERYNEWPALETTE:

        SS_DBGMSG1( "Palette_Proc: WM_QUERYNEWPALETTE for 0x%x\n", hwnd );
        // We don't actually realize palette here (we do it at WM_ACTIVATE
        // time), but we need the system to think that we have so that a
        // WM_PALETTECHANGED message is generated.

        // This is the only msg preview mode gets wrt palettes !
        if( !ss_fPreviewMode() )
            return (1);

        // We are in preview mode - realize the palette
        SendMessage( hwnd, SS_WM_PALETTE, gpss->bInBackground(), 0);
        break;

      case WM_PALETTECHANGED:

        SS_DBGMSG1( "Palette_Proc: WM_PALETTECHANGED for 0x%x\n", hwnd );
        // Respond to this message only if the window that changed the palette
        // is not this app's window.

        // We are not the foreground window, so realize palette in the
        // background.  We cannot call Realize to do this because
        // we should not do any of the gbUseStaticColors processing while
        // in background.

        // Actually, we *can* be the fg window, so don't realize if
        // we're in foreground

        if( (hwnd != (HWND) wParam) && gpss->bInBackground() )
            SendMessage( hwnd, SS_WM_PALETTE, TRUE, 0);

        break;

      case WM_SYSCOLORCHANGE:

        // If the system colors have changed and we have a palette
        // for an RGB surface then we need to recompute the static
        // color mapping because they might have been changed in
        // the process of changing the system colors.

          SS_DBGMSG1( "Palette_Proc: WM_SYSCOLORCHANGE for 0x%x\n", hwnd );
          gpss->pssPal->ReCreateRGBPalette();
          SendMessage( hwnd, SS_WM_PALETTE, gpss->bInBackground(), 0);
          break;

      case SS_WM_PALETTE:

          SS_DBGMSG2( "Palette_Proc: SS_WM_PALETTE for 0x%x, bg = %d\n",
                          hwnd, wParam );

          // Realize palette for this window and its children
          // wParam = TRUE if realize as bg

          PSSW pssw = gpss->sswTable.PsswFromHwnd( hwnd );
          ssw_RelayMessageToChildren( pssw, message, wParam, lParam );
          ssw_RealizePalette( pssw, wParam );
          break;
    }
    return 0;
}

/******************************Public*Routine******************************\
* GLScreenSaverConfigureDialog
*
* This is a wrapper for ScreenSaverConfigureDialog, which is the main dialog
* proc for all the GL screen savers in config mode.
*
* We call the client's ss_ConfigInit() routine on the first WM_PAINT, since
* the dialog will have focus at this point (can realize palette) and all
* buttons should have been created.
\**************************************************************************/

BOOL
GLScreenSaverConfigureDialog( HWND hDlg, UINT msg, WPARAM wParam,
                              LPARAM lParam )
{
    static BOOL bInited = 0;

    switch( msg ) {
        case WM_INITDIALOG :
          {
            SS_DBGMSG( "GLScreenSaverConfigureDialog: WM_INITDIALOG\n" );
            // Create wrapper pssw for the dialog box

            PSSW pssw;
            pssw = new SSW( NULL,       // ssw parent
                            hDlg
                          );
            SS_ASSERT( pssw, "GLScreenSaverConfigureDialog : alloc failure for psswMain\n" );

            gpss->psswMain = pssw;

            // Load any resource strings common to all the dialogs
            BOOL bStringsLoaded = ss_LoadTextureResourceStrings();
            // If this doesn't work, things are seriously wrong and we
            // shouldn't continue
            SS_ASSERT( bStringsLoaded, "GLScreenSaverConfigureDialog : failure loading common resource strings\n" );
          }
          break;

        case WM_PAINT:

            if( !bInited ) {
                // Call client's ss_ConfigInit()
                if( !ss_ConfigInit( hDlg ) ) {
                    SS_WARNING( "ConfigInit failed\n" );
                    // Send WM_CLOSE to the dialog - this will enable any
                    // cleanup code to be called by the client
                    SendMessage( hDlg, WM_CLOSE, 0, 0l );
                }
                bInited = TRUE;
            }
            break;

        case WM_ACTIVATE:
        case WM_QUERYNEWPALETTE:
        case WM_PALETTECHANGED:
        case SS_WM_PALETTE:
            return( MainPaletteManageProc( hDlg, msg, wParam, lParam ) );

    }
    return ScreenSaverConfigureDialog( hDlg, msg, wParam, lParam );
}

/******************************Public*Routine******************************\
* SSW_TABLE constructor
*
\**************************************************************************/

SSW_TABLE::SSW_TABLE()
{
    nEntries = 0;
}

/******************************Public*Routine******************************\
* Register
*
* Register a HWND/PSSW pair.
\**************************************************************************/

void
SSW_TABLE::Register( HWND hwnd, PSSW pssw )
{
    SSW_TABLE_ENTRY *pEntry;

    // Check if already in table
    if( PsswFromHwnd( hwnd ) )
        return;

    // put hwnd/pssw pair in the table
    pEntry = &sswTable[nEntries];
    pEntry->hwnd = hwnd;
    pEntry->pssw = pssw;
    nEntries++;
}

/******************************Public*Routine******************************\
* PsswFromHwnd
*
* Return PSSW for the HWND

\**************************************************************************/

PSSW
SSW_TABLE::PsswFromHwnd( HWND hwnd )
{
    int count = nEntries;
    SSW_TABLE_ENTRY *pEntry = sswTable;

    while( count-- ) {
        if( pEntry->hwnd == hwnd )
            return pEntry->pssw;
        pEntry++;
    }
    return NULL;
}

/******************************Public*Routine******************************\
* Remove
*
* Remove HWND/PSSW entry from table
\**************************************************************************/

BOOL
SSW_TABLE::Remove( HWND hwnd )
{
    SSW_TABLE_ENTRY *pEntry = sswTable;

    // Locate the hwnd/pssw pair

    for( int count = 0 ; count < nEntries ; count++, pEntry++ ) {
        if( pEntry->hwnd == hwnd )
            break;
    }

    if( count == nEntries )
        // couldn't find it in the table
        return FALSE;

    // Remove entry / shuffle up other entries
    for( int i = count; i < nEntries-1; i ++ ) {
        sswTable[i] = sswTable[i+1];
    }

    nEntries--;
    return TRUE;
}


TEXTURE.C

/******************************Module*Header*******************************\
* Module Name: texture.c
*
* Texture handling functions
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include <windows.h>
#include <scrnsave.h>
#include <commdlg.h>
#include <GL/gl.h>

#include "scrnsave.h"  // for hMainInstance
#include "sscommon.h"
#include "texture.h"

static int ProcessTexture( TEXTURE *pTex );
static int ProcessTkTexture( AUX_RGBImageRec *image, TEXTURE *pTex );
static int VerifyTextureFile( TEXFILE *pTexFile );
static int GetTexFileType( TEXFILE *pTexFile );

static TEX_STRINGS gts = {0};
BOOL gbTextureObjects = FALSE;
static BOOL gbPalettedTexture = FALSE;
static PFNGLCOLORTABLEEXTPROC pfnColorTableEXT;
static PFNGLCOLORSUBTABLEEXTPROC pfnColorSubTableEXT;

static BOOL gbEnableErrorMsgs = FALSE;

/******************************Public*Routine******************************\
*
* ss_LoadTextureResourceStrings
*
* Load various messages and strings that are used in processing textures,
* into global TEX_STRINGS structure
*
\**************************************************************************/

BOOL
ss_LoadTextureResourceStrings()
{
    LPTSTR pszStr;

    // title for choose texture File dialog
    LoadString(hMainInstance, IDS_TEXTUREDIALOGTITLE, gts.szTextureDialogTitle,
                GEN_STRING_SIZE);
    LoadString(hMainInstance, IDS_BMP, gts.szBmp, GEN_STRING_SIZE);
    LoadString(hMainInstance, IDS_DOTBMP, gts.szDotBmp, GEN_STRING_SIZE);
    LoadString(hMainInstance, IDS_TEXTUREFILTER, gts.szTextureFilter,
                GEN_STRING_SIZE);
    pszStr = &gts.szTextureFilter[lstrlen(gts.szTextureFilter)+1];
    LoadString(hMainInstance, IDS_STARDOTBMP, pszStr, GEN_STRING_SIZE);
    pszStr += lstrlen(pszStr);
    *pszStr++ = TEXT(';');
    LoadString(hMainInstance, IDS_STARDOTRGB, pszStr, GEN_STRING_SIZE);
    pszStr += lstrlen(pszStr);
    pszStr++;
    *pszStr = TEXT('\0');

    LoadString(hMainInstance, IDS_WARNING, gts.szWarningMsg, MAX_PATH);
    LoadString(hMainInstance, IDS_SELECT_ANOTHER_BITMAP,
                gts.szSelectAnotherBitmapMsg, MAX_PATH );

    LoadString(hMainInstance, IDS_BITMAP_INVALID,
                gts.szBitmapInvalidMsg, MAX_PATH );
    LoadString(hMainInstance, IDS_BITMAP_SIZE,
                gts.szBitmapSizeMsg, MAX_PATH );

    // assumed here that all above calls loaded properly
    return TRUE;
}

/******************************Public*Routine******************************\
*
*
\**************************************************************************/

void
ss_DisableTextureErrorMsgs()
{
    gbEnableErrorMsgs = FALSE;
}

/******************************Public*Routine******************************\
*
* ss_LoadBMPTextureFile
*
* Loads a BMP file and prepares it for GL usage
*
\**************************************************************************/

int
ss_LoadBMPTextureFile( LPCTSTR pszBmpfile, TEXTURE *pTex )
{
    AUX_RGBImageRec *image = (AUX_RGBImageRec *) NULL;

#ifdef UNICODE
    image = auxDIBImageLoad( (LPCWSTR) pszBmpfile);
#else
    image = auxDIBImageLoad( (LPCSTR) pszBmpfile);
#endif

    if( !image )  {
        return 0;
    }
    return ProcessTkTexture( image, pTex );
}

/******************************Public*Routine******************************\
*
* ss_LoadTextureFile
*
* Loads a BMP file and prepares it for GL usage
*
\**************************************************************************/

int
ss_LoadTextureFile( TEXFILE *pTexFile, TEXTURE *pTex )
{
    AUX_RGBImageRec *image = (AUX_RGBImageRec *) NULL;
    LPTSTR pszBmpfile = pTexFile->szPathName;
    int type;

    // Verify file / set type

    if( !(type = VerifyTextureFile( pTexFile )) )
        return 0;

    if( type == TEX_BMP ) {
#ifdef UNICODE
        image = auxDIBImageLoad( (LPCWSTR) pszBmpfile);
#else
        image = auxDIBImageLoad( (LPCSTR) pszBmpfile);
#endif
    } else {
#ifdef UNICODE
        image = auxRGBImageLoad( (LPCWSTR) pszBmpfile);
#else
        image = auxRGBImageLoad( (LPCSTR) pszBmpfile);
#endif
    }

    if( !image )  {
        return 0;
    }
    return ProcessTkTexture( image, pTex );
}

/******************************Public*Routine******************************\
*
* ss_LoadTextureResource
*
* Loads a BMP or RGB texture resource and prepares it for GL usage
*
\**************************************************************************/

int
ss_LoadTextureResource( TEX_RES *pTexRes, TEXTURE *pTex )
{
    HMODULE ghmodule;
    HRSRC hr;
    HGLOBAL hg;
    LPVOID pv;
    LPCTSTR lpType;
    BOOL fLoaded = FALSE;

    ghmodule = GetModuleHandle(NULL);
    switch(pTexRes->type)
    {
    case TEX_RGB:
        lpType = MAKEINTRESOURCE(RT_RGB);
        break;
    case TEX_BMP:
        lpType = MAKEINTRESOURCE(RT_MYBMP);
        break;
    case TEX_A8:
        lpType = MAKEINTRESOURCE(RT_A8);
        break;
    }

    hr = FindResource(ghmodule, MAKEINTRESOURCE(pTexRes->name), lpType);
    if (hr == NULL)
    {
        goto EH_NotFound;
    }
    hg = LoadResource(ghmodule, hr);
    if (hg == NULL)
    {
        goto EH_FreeResource;
    }
    pv = (PSZ)LockResource(hg);
    if (pv == NULL)
    {
        goto EH_FreeResource;
    }

    switch(pTexRes->type)
    {
    case TEX_RGB:
        fLoaded = ss_RGBImageLoad( pv, pTex );
        break;
    case TEX_BMP:
        fLoaded = ss_DIBImageLoad( pv, pTex );
        break;
    case TEX_A8:
        fLoaded = ss_A8ImageLoad( pv, pTex );
        break;
    }

 EH_FreeResource:
    FreeResource(hr);
 EH_NotFound:

    if( !fLoaded )  {
        return 0;
    }

    return ProcessTexture( pTex );
}

/******************************Public*Routine******************************\
*
* ValidateTextureSize
*
* - Scales the texture to powers of 2
*
\**************************************************************************/

static BOOL
ValidateTextureSize( TEXTURE *pTex )
{
    double xPow2, yPow2;
    int ixPow2, iyPow2;
    int xSize2, ySize2;
    GLint glMaxTexDim;

    if( (pTex->width <= 0) || (pTex->height <= 0) ) {
        SS_WARNING( "ValidateTextureSize : invalid texture dimensions\n" );
        return FALSE;
    }

    pTex->origAspectRatio = (float) pTex->height / (float) pTex->width;

    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTexDim);
    if( glMaxTexDim <= 0 )
        return FALSE;

    if( pTex->format != GL_COLOR_INDEX ) {

        // We limit the max dimension here for performance reasons
        glMaxTexDim = min(256, glMaxTexDim);

        if (pTex->width <= glMaxTexDim)
            xPow2 = log((double)pTex->width) / log((double)2.0);
        else
            xPow2 = log((double)glMaxTexDim) / log((double)2.0);

        if (pTex->height <= glMaxTexDim)
            yPow2 = log((double)pTex->height) / log((double)2.0);
        else
            yPow2 = log((double)glMaxTexDim) / log((double)2.0);

        ixPow2 = (int)xPow2;
        iyPow2 = (int)yPow2;

        // Always scale to higher nearest power
        if (xPow2 != (double)ixPow2)
            ixPow2++;
        if (yPow2 != (double)iyPow2)
            iyPow2++;

        xSize2 = 1 << ixPow2;
        ySize2 = 1 << iyPow2;

        if (xSize2 != pTex->width ||
            ySize2 != pTex->height)
        {
            BYTE *pData;

            pData = (BYTE *) malloc(xSize2 * ySize2 * pTex->components * sizeof(BYTE));
            if (!pData) {
                SS_WARNING( "ValidateTextureSize : can't alloc pData\n" );
                return FALSE;
            }

            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

            if( gluScaleImage(pTex->format, pTex->width, pTex->height,
                      GL_UNSIGNED_BYTE, pTex->data,
                      xSize2, ySize2, GL_UNSIGNED_BYTE,
                      pData) )
            {
                // glu failure
                SS_WARNING( "ValidateTextureSize : gluScaleImage failure\n" );
                return FALSE;
            }

            // set the new width,height,data
            pTex->width = xSize2;
            pTex->height = ySize2;
            free(pTex->data);
            pTex->data = pData;
        }
    } else {  // paletted texture case
        // paletted texture: must be power of 2 - but might need to enforce
        // here if not done in a8 load.  Also have to check against
        // GL_MAX_TEXTURE_SIZE.  Could then clip it to power of 2 size
    }
    return TRUE;
}

/******************************Public*Routine******************************\
*
* SetDefaultTextureParams
*
\**************************************************************************/

static void
SetDefaultTextureParams()
{
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}

/******************************Public*Routine******************************\
*
* ProcessTexture
*
* - Verifies texture size
* - Fills out TEXTURE structure with required data
* - Creates a texture object if extension exists
*
\**************************************************************************/

static int
ProcessTexture( TEXTURE *pTex )
{
    // Enforce proper texture size (power of 2, etc.)

    if( !ValidateTextureSize( pTex ) )
        return 0;

    // if texturing objects available, init the object
    if( gbTextureObjects ) {
        glGenTextures( 1, &pTex->texObj );
        glBindTexture( GL_TEXTURE_2D, pTex->texObj );

        // Default attributes for texObj
        SetDefaultTextureParams();

        glTexImage2D( GL_TEXTURE_2D, 0, pTex->components,
                      pTex->width, pTex->height, 0, pTex->format,
                      GL_UNSIGNED_BYTE, pTex->data );

        if (gbPalettedTexture && pTex->pal != NULL)
        {
            pfnColorTableEXT(GL_TEXTURE_2D, GL_RGBA, pTex->pal_size,
                             GL_BGRA_EXT, GL_UNSIGNED_BYTE, pTex->pal);
        }
    } else
        pTex->texObj = 0;

    return 1;
}

/******************************Public*Routine******************************\
*
* ProcessTkTexture
*
* Simple wrapper for ProcessTexture which fills out a TEXTURE
* from a AUX_RGBImageRec
*
* Frees the ImageRec if ProcessTexture succeeds
*
\**************************************************************************/

static int
ProcessTkTexture( AUX_RGBImageRec *image, TEXTURE *pTex )
{

    pTex->width = image->sizeX;
    pTex->height = image->sizeY;
    pTex->format = GL_RGB;
    pTex->components = 3;
    pTex->data = image->data;
    pTex->pal_size = 0;
    pTex->pal = NULL;

    if( ProcessTexture( pTex ) )
    {
        free(image);
        return 1;
    }
    else
    {
        return 0;
    }
}

/******************************Public*Routine******************************\
*
* ss_SetTexture
*
\**************************************************************************/

void
ss_SetTexture( TEXTURE *pTex )
{
    if( pTex == NULL )
        return;

    if( gbTextureObjects && pTex->texObj ) {
        glBindTexture( GL_TEXTURE_2D, pTex->texObj );
        return;
    }

    glTexImage2D( GL_TEXTURE_2D, 0, pTex->components,
                  pTex->width, pTex->height, 0, pTex->format,
                  GL_UNSIGNED_BYTE, pTex->data );

    if (gbPalettedTexture && pTex->pal != NULL)
    {
        pfnColorTableEXT(GL_TEXTURE_2D, GL_RGBA, pTex->pal_size,
                         GL_BGRA_EXT, GL_UNSIGNED_BYTE, pTex->pal);
    }
}

/******************************Public*Routine******************************\
*
* ss_CopyTexture
*
* Make a copy of a texture.
*
\**************************************************************************/

BOOL
ss_CopyTexture( TEXTURE *pTexDst, TEXTURE *pTexSrc )
{
    int size;

    if( (pTexDst == NULL) || (pTexSrc == NULL) )
        return FALSE;

    *pTexDst = *pTexSrc;

    if( gbTextureObjects && pTexSrc->texObj ) {
        glGenTextures( 1, &pTexDst->texObj );
    }

    // copy image data

    size = pTexSrc->width * pTexSrc->height;
    if( pTexSrc->components != GL_COLOR_INDEX8_EXT )
        size *= pTexSrc->components; // since data format always UNSIGNED_BYTE

    pTexDst->data = (unsigned char *) malloc( size );
    if( pTexDst->pal == NULL )
        return FALSE;
    memcpy( pTexDst->data, pTexSrc->data, size );

    // copy palette data

    if( gbPalettedTexture && pTexSrc->pal != NULL )
    {
        size = pTexSrc->pal_size*sizeof(RGBQUAD);
        pTexDst->pal = (RGBQUAD *) malloc(size);
        if( pTexDst->pal == NULL )
        {
            free(pTexDst->data);
            return FALSE;
        }
        memcpy( pTexDst->pal, pTexSrc->pal, size );
    }

    if( gbTextureObjects ) {
        glBindTexture( GL_TEXTURE_2D, pTexDst->texObj );

        // Default attributes for texObj
        SetDefaultTextureParams();

        glTexImage2D( GL_TEXTURE_2D, 0, pTexDst->components,
                      pTexDst->width, pTexDst->height, 0, pTexDst->format,
                      GL_UNSIGNED_BYTE, pTexDst->data );

        if( gbPalettedTexture && (pTexDst->pal != NULL) )
        {
            pfnColorTableEXT(GL_TEXTURE_2D, GL_RGBA, pTexDst->pal_size,
                             GL_BGRA_EXT, GL_UNSIGNED_BYTE, pTexDst->pal);
        }
    }
    return TRUE;
}

/******************************Public*Routine******************************\
*
* ss_SetTexturePalette
*
* Set a texture's palette according to the supplied index. This index
* indicates the start of the palette, which then wraps around if necessary.
* Of course this only works on paletted textures.
*
\**************************************************************************/

void
ss_SetTexturePalette( TEXTURE *pTex, int index )
{
    if( pTex == NULL )
        return;

    if( gbTextureObjects )
        ss_SetTexture( pTex );

    if( gbPalettedTexture && pTex->pal != NULL )
    {
        int start, count;

        start = index & (pTex->pal_size - 1);
        count = pTex->pal_size - start;
        pfnColorSubTableEXT(GL_TEXTURE_2D, 0, count, GL_BGRA_EXT,
                            GL_UNSIGNED_BYTE, pTex->pal + start);
        if (start != 0)
        {
            pfnColorSubTableEXT(GL_TEXTURE_2D, count, start, GL_BGRA_EXT,
                                GL_UNSIGNED_BYTE, pTex->pal);
        }
    }
}

/******************************Public*Routine******************************\
*
* SetTextureAlpha
*
* Set a constant alpha value for the texture
* Again, don't overwrite any existing 0 alpha values, as explained in
* ss_SetTextureTransparency
*
\**************************************************************************/

static void
SetTextureAlpha( TEXTURE *pTex, float fAlpha )
{
    int i;
    unsigned char *pData = pTex->data;
    RGBA8 *pColor = (RGBA8 *) pTex->data;
    BYTE bAlpha = (BYTE) (fAlpha * 255.0f);

    if( pTex->components != 4 )
        return;

    for( i = 0; i < pTex->width*pTex->height; i ++, pColor++ ) {
        if( pColor->a != 0 )
            pColor->a = bAlpha;
    }
}

/******************************Public*Routine******************************\
*
* ConvertTextureToRGBA
*
* Convert RGB texture to RGBA
*
\**************************************************************************/

static void
ConvertTextureToRGBA( TEXTURE *pTex, float fAlpha )
{
    unsigned char *pNewData;
    int count = pTex->width * pTex->height;
    unsigned char *src, *dst;
    BYTE bAlpha = (BYTE) (fAlpha * 255.0f);
    int i;

    pNewData = (unsigned char *) LocalAlloc(LMEM_FIXED, count * sizeof(RGBA8));
    if( !pNewData )
        return;

    src = pTex->data;
    dst = pNewData;
    // Note: the color ordering is ABGR, where R is lsb
    for( i = 0; i < count; i ++ ) {
        *((RGB8 *)dst) = *((RGB8 *)src);
        dst += sizeof(RGB8);
        src += sizeof(RGB8);
        *dst++ = bAlpha;
    }
    LocalFree( pTex->data );
    pTex->data = pNewData;
    pTex->components = 4;
    pTex->format = GL_RGBA;
}

/******************************Public*Routine******************************\
*
* ss_SetTextureTransparency
*
* Set transparency for a texture by adding or modifying the alpha data.
* Transparency value must be between 0.0 (opaque) and 1.0 (fully transparent)
* If the texture data previously had no alpha, add it in.
* If bSet is TRUE, make this the current texture.
*
* Note: Currently fully transparent pixels (alpha=0) will not be altered, since
* it is assumed these should be permanently transparent (could make this an
* option? - bPreserveTransparentPixels )
*
\**************************************************************************/

BOOL
ss_SetTextureTransparency( TEXTURE *pTex, float fTransp, BOOL bSet )
{
    int i;
    float fAlpha;

    if( pTex == NULL )
        return FALSE;

    SS_CLAMP_TO_RANGE2( fTransp, 0.0f, 1.0f );
    fAlpha = 1 - fTransp;

    if( pTex->format == GL_COLOR_INDEX )
    {
        // just need to modify the palette
            RGBQUAD *pPal = pTex->pal;
            BYTE bAlpha = (BYTE) (fAlpha * 255.0f);

            if( !pPal )
                return FALSE;

            for( i = 0; i < pTex->pal_size; i ++, pPal++ ) {
                if( pPal->rgbReserved != 0 )
                    pPal->rgbReserved = bAlpha;
            }

            // need to send down the new palette for texture objects
            if( gbTextureObjects && gbPalettedTexture )
            {
                glBindTexture( GL_TEXTURE_2D, pTex->texObj );
                pfnColorTableEXT(GL_TEXTURE_2D, GL_RGBA, pTex->pal_size,
                                 GL_BGRA_EXT, GL_UNSIGNED_BYTE, pTex->pal);
            }
    }
    else {
        // Need to setup new texture data
        if( pTex->components != 4 ) {
            // Make room for alpha component
            ConvertTextureToRGBA( pTex, fAlpha );
        } else {
            // Set alpha component
            SetTextureAlpha( pTex, fAlpha );
        }
        // Send down new data if texture objects
        if( gbTextureObjects )
        {
            glBindTexture( GL_TEXTURE_2D, pTex->texObj );
            glTexImage2D( GL_TEXTURE_2D, 0, pTex->components,
                          pTex->width, pTex->height, 0, pTex->format,
                          GL_UNSIGNED_BYTE, pTex->data );
        }
    }

    if( bSet )
        ss_SetTexture( pTex );

    return TRUE;
}

/******************************Public*Routine******************************\
*
* ss_DeleteTexture
*
\**************************************************************************/

void
ss_DeleteTexture( TEXTURE *pTex )
{
    if( pTex == NULL )
        return;

    if( gbTextureObjects && pTex->texObj ) {
        glDeleteTextures( 1, &pTex->texObj );
        pTex->texObj = 0;
    }
    if (pTex->pal != NULL)
    {
        free(pTex->pal);
    }
    if( pTex->data )
        free( pTex->data );
}

/******************************Public*Routine******************************\
*
* ss_TextureObjectsEnabled
*
* Returns BOOL set by ss_QueryGLVersion (Texture Objects only supported on
* GL v.1.1 or greater)
*
\**************************************************************************/

BOOL
ss_TextureObjectsEnabled( void )
{
    return gbTextureObjects;
}

/******************************Public*Routine******************************\
*
* ss_PalettedTextureEnabled
*
* Returns result from ss_QueryPalettedTextureEXT
*
\**************************************************************************/

BOOL
ss_PalettedTextureEnabled( void )
{
    return gbPalettedTexture;
}

/******************************Public*Routine******************************\
*
* ss_QueryPalettedTextureEXT
*
* Queries the OpenGL implementation to see if paletted texture is supported
* Typically called once at app startup.
*
\**************************************************************************/

BOOL
ss_QueryPalettedTextureEXT( void )
{
    PFNGLGETCOLORTABLEPARAMETERIVEXTPROC pfnGetColorTableParameterivEXT;
    int size;

    pfnColorTableEXT = (PFNGLCOLORTABLEEXTPROC)
        wglGetProcAddress("glColorTableEXT");
    if (pfnColorTableEXT == NULL)
        return FALSE;
    pfnColorSubTableEXT = (PFNGLCOLORSUBTABLEEXTPROC)
        wglGetProcAddress("glColorSubTableEXT");
    if (pfnColorSubTableEXT == NULL)
        return FALSE;

    // Check color table size
    pfnGetColorTableParameterivEXT = (PFNGLGETCOLORTABLEPARAMETERIVEXTPROC)
        wglGetProcAddress("glGetColorTableParameterivEXT");
    if (pfnGetColorTableParameterivEXT == NULL)
        return FALSE;
    // For now, the only paletted textures supported in this lib are TEX_A8,
    // with 256 color table entries.  Make sure the device supports this.
    pfnColorTableEXT(GL_PROXY_TEXTURE_2D, GL_RGBA, 256,
                     GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL );
    pfnGetColorTableParameterivEXT( GL_PROXY_TEXTURE_2D,
                                    GL_COLOR_TABLE_WIDTH_EXT, &size );
    if( size != 256 )
        // The device does not support a color table size of 256, so we don't
        // enable paletted textures in general.
        return FALSE;

    return gbPalettedTexture=TRUE;
}

/******************************Public*Routine******************************\
*
* ss_VerifyTextureFile
*
* Validates texture bmp or rgb file, by checking for valid pathname and
* correct format.
*
\**************************************************************************/

BOOL
ss_VerifyTextureFile( TEXFILE *ptf )
{
    // Make sure the selected texture file is OK.

    TCHAR szFileName[MAX_PATH];
    PTSTR pszString;
    TCHAR szString[MAX_PATH];

    lstrcpy(szFileName, ptf->szPathName);

    if ( SearchPath(NULL, szFileName, NULL, MAX_PATH,
                     ptf->szPathName, &pszString)
       )
    {
        ptf->nOffset = pszString - ptf->szPathName;
        return TRUE;
    }
    else
    {
        lstrcpy(ptf->szPathName, szFileName);    // restore

        if( !ss_fOnWin95() && gbEnableErrorMsgs )
        {
            wsprintf(szString, gts.szSelectAnotherBitmapMsg, ptf->szPathName);
            MessageBox(NULL, szString, gts.szWarningMsg, MB_OK);
        }
        return FALSE;
    }
}

/******************************Public*Routine******************************\
*
* ss_SelectTextureFile
*
* Use the common dialog GetOpenFileName to get the name of a bitmap file
* for use as a texture.  This function will not return until the user
* either selects a valid bitmap or cancels.  If a valid bitmap is selected
* by the user, the global array szPathName will have the full path
* to the bitmap file and the global value nOffset will have the
* offset from the beginning of szPathName to the pathless file name.
*
* If the user cancels, szPathName and nOffset will remain
* unchanged.
*
\**************************************************************************/

BOOL
ss_SelectTextureFile( HWND hDlg, TEXFILE *ptf )
{
    OPENFILENAME ofn;
    TCHAR dirName[MAX_PATH];
    TEXFILE newTexFile;
    LPTSTR pszFileName = newTexFile.szPathName;
    TCHAR origPathName[MAX_PATH];
    PTSTR pszString;
    BOOL bTryAgain, bFileSelected;

    gbEnableErrorMsgs = TRUE;

    // Make a copy of the original file path name, so we can tell if
    // it changed or not
    lstrcpy( origPathName, ptf->szPathName );

    // Make dialog look nice by parsing out the initial path and
    // file name from the full pathname.  If this isn't done, then
    // dialog has a long ugly name in the file combo box and
    // directory will end up with the default current directory.

    if (ptf->nOffset) {
    // Separate the directory and file names.

        lstrcpy(dirName, ptf->szPathName);
        dirName[ptf->nOffset-1] = L'\0';
        lstrcpy(pszFileName, &ptf->szPathName[ptf->nOffset]);
    }
    else {
    // If nOffset is zero, then szPathName is not a full path.
    // Attempt to make it a full path by calling SearchPath.

        if ( SearchPath(NULL, ptf->szPathName, NULL, MAX_PATH,
                         dirName, &pszString) )
        {
        // Successful.  Go ahead a change szPathName to the full path
        // and compute the filename offset.

            lstrcpy(ptf->szPathName, dirName);
            ptf->nOffset = pszString - dirName;

        // Break the filename and directory paths apart.

            dirName[ptf->nOffset-1] = TEXT('\0');
            lstrcpy(pszFileName, pszString);
        }

    // Give up and use the Windows system directory.

        else
        {
            GetWindowsDirectory(dirName, MAX_PATH);
            lstrcpy(pszFileName, ptf->szPathName);
        }
    }

    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hDlg;
    ofn.hInstance = NULL;
    ofn.lpstrFilter = gts.szTextureFilter;
    ofn.lpstrCustomFilter = (LPTSTR) NULL;
    ofn.nMaxCustFilter = 0;
    ofn.nFilterIndex = 1;
    ofn.lpstrFile = pszFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.lpstrFileTitle = (LPTSTR) NULL;
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = dirName;
    ofn.lpstrTitle = gts.szTextureDialogTitle;
    ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    ofn.nFileOffset = 0;
    ofn.nFileExtension = 0;
    ofn.lpstrDefExt = gts.szBmp;
    ofn.lCustData = 0;
    ofn.lpfnHook = (LPOFNHOOKPROC) NULL;
    ofn.lpTemplateName = (LPTSTR) NULL;

    do {
    // Invoke the common file dialog.  If it succeeds, then validate
    // the bitmap file.  If not valid, make user try again until either
    // they pick a good one or cancel the dialog.

        bTryAgain = FALSE;

        if ( bFileSelected = GetOpenFileName(&ofn) ) {

            newTexFile.nOffset = ofn.nFileOffset;
            if( VerifyTextureFile( &newTexFile ) ) {
                // copy in new file and offset
                *ptf = newTexFile;
            }
            else {
                bTryAgain = TRUE;
            }
        }

    // If need to try again, recompute dir and file name so dialog
    // still looks nice.

        if (bTryAgain && ofn.nFileOffset) {
            lstrcpy(dirName, pszFileName);
            dirName[ofn.nFileOffset-1] = L'\0';
            lstrcpy(pszFileName, &pszFileName[ofn.nFileOffset]);
        }

    } while (bTryAgain);

    gbEnableErrorMsgs = FALSE;

    if( bFileSelected ) {
        if( lstrcmpi( origPathName, ptf->szPathName ) )
            // a different file was selected
            return TRUE;
    }
    return FALSE;
}

/******************************Public*Routine******************************\
*
* ss_GetDefaultBmpFile
*
* Determine a default bitmap file to use for texturing, if none
* exists yet in the registry.
*
* Put default in BmpFile parameter.   DotBmp parameter is the bitmap
* extension (usually .bmp).
*
* We have to synthesise the name from the ProductType registry entry.
* Currently, this can be WinNT, LanmanNT, or Server.  If it is
* WinNT, the bitmap is winnt.bmp.  If it is LanmanNT or Server,
* the bitmap is lanmannt.bmp.
*
\**************************************************************************/

void
ss_GetDefaultBmpFile( LPTSTR pszBmpFile )
{
    HKEY   hkey;
    LONG   cjDefaultBitmap = MAX_PATH;

    if( ss_fOnWin95() )
        // There is no 'nice' bmp file on standard win95 installations
        lstrcpy( pszBmpFile, TEXT("") );
    else {
        if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                 (LPCTSTR) TEXT("System\\CurrentControlSet\\Control\\ProductOptions"),
                 0,
                 KEY_QUERY_VALUE,
                 &hkey) == ERROR_SUCCESS )
        {

            if ( RegQueryValueEx(hkey,
                                  TEXT("ProductType"),
                                  (LPDWORD) NULL,

(LPDWORD) NULL,
                                  (LPBYTE) pszBmpFile,
                                  (LPDWORD) &cjDefaultBitmap) == ERROR_SUCCESS
                 && (cjDefaultBitmap / sizeof(TCHAR) + 4) <= MAX_PATH )
                lstrcat( pszBmpFile, gts.szDotBmp );
            else
                lstrcpy( pszBmpFile, TEXT("winnt.bmp") );

            RegCloseKey(hkey);
        }
        else
            lstrcpy( pszBmpFile, TEXT("winnt.bmp") );

    // If its not winnt.bmp, then its lanmannt.bmp.  (This would be a lot
    // cleaner both in the screen savers and for usersrv desktop bitmap
    // initialization if the desktop bitmap name were stored in the
    // registry).

        if ( lstrcmpi( pszBmpFile, TEXT("winnt.bmp") ) != 0 )
            lstrcpy( pszBmpFile, TEXT("lanmannt.bmp") );
    }
}

/******************************Public*Routine******************************\
*
* VerifyTextureFile
*
* Verify that a bitmap or rgb file is valid
*
* Returns:
*   File type (RGB or BMP) if valid file; otherwise, 0.
*
\**************************************************************************/

static int
VerifyTextureFile( TEXFILE *pTexFile )
{
    int type;
    ISIZE size;
    BOOL bValid;
    TCHAR szString[2 * MAX_PATH]; // May contain a pathname

    // check for 0 offset and null strings
    if( (pTexFile->nOffset == 0) || (*pTexFile->szPathName == 0) )
        return 0;

    type = GetTexFileType( pTexFile );

    switch( type ) {
        case TEX_BMP:
            bValid = bVerifyDIB( pTexFile->szPathName, &size );
            break;
        case TEX_RGB:
            bValid = bVerifyRGB( pTexFile->szPathName, &size );
            break;
        case TEX_UNKNOWN:
        default:
            bValid = FALSE;
    }

    if( !bValid ) {
        if( gbEnableErrorMsgs ) {
            wsprintf(szString, gts.szSelectAnotherBitmapMsg, pTexFile->szPathName);
            MessageBox(NULL, szString, gts.szWarningMsg, MB_OK);
        }
        return 0;
    }

    // Check size ?

    if ( (size.width > TEX_WIDTH_MAX)     ||
         (size.height > TEX_HEIGHT_MAX) )
    {
        if( gbEnableErrorMsgs )
        {
            wsprintf(szString, gts.szBitmapSizeMsg,
                      TEX_WIDTH_MAX, TEX_HEIGHT_MAX);
            MessageBox(NULL, szString, gts.szWarningMsg, MB_OK);
        }
        return 0;
    }

    return type;
}

/******************************Public*Routine******************************\
*
* ss_InitAutoTexture
*
* Generate texture coordinates automatically.
* If pTexRep is not NULL, use it to set the repetition of the generated
* texture.
*
\**************************************************************************/

void
ss_InitAutoTexture( TEX_POINT2D *pTexRep )
{
    GLfloat sgenparams[] = {1.0f, 0.0f, 0.0f, 0.0f};
    GLfloat tgenparams[] = {0.0f, 1.0f, 0.0f, 0.0f};

    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
    if( pTexRep )
        sgenparams[0] = pTexRep->s;
    glTexGenfv(GL_S, GL_OBJECT_PLANE, sgenparams );

    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
    if( pTexRep )
        tgenparams[0] = pTexRep->t;
    glTexGenfv(GL_T, GL_OBJECT_PLANE, tgenparams );

    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    glEnable( GL_TEXTURE_2D );
}

/******************************Public*Routine******************************\
*
* GetTexFileType
*
* Determine if a texture file is rgb or bmp, based on extension.  This is
* good enough, as the open texture dialog only shows files with these
* extensions.
*
\**************************************************************************/

static int
GetTexFileType( TEXFILE *pTexFile )
{
    LPTSTR pszStr;

#ifdef UNICODE
    pszStr = wcsrchr( pTexFile->szPathName + pTexFile->nOffset,
             (USHORT) L'.' );
#else
    pszStr = strrchr( pTexFile->szPathName + pTexFile->nOffset,
             (USHORT) L'.' );
#endif
    if( !pszStr || (lstrlen(++pszStr) == 0) )
        return TEX_UNKNOWN;

    if( !lstrcmpi( pszStr, TEXT("bmp") ) )
        return TEX_BMP;
    else if( !lstrcmpi( pszStr, TEXT("rgb") ) )
        return TEX_RGB;
    else
        return TEX_UNKNOWN;
}


TEXTURE.H

/******************************Module*Header*******************************\
* Module Name: texture.h
*
* Local texture processing functions
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __texture_h__
#define __texture_h__

#include "sscommon.h"

#ifdef __cplusplus
extern "C" {
#endif

extern BOOL bVerifyDIB(LPTSTR pszFileName, ISIZE *pSize );
extern BOOL bVerifyRGB(LPTSTR pszFileName, ISIZE *pSize );

#ifdef __cplusplus
}
#endif

#endif // __texture_h__


UTIL.CXX

/******************************Module*Header*******************************\
* Module Name: util.cxx
*
* Misc. utility functions
*
* Copyright 1996 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <sys/timeb.h>
#include <GL/gl.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include <math.h>
#include "ssintrnl.hxx"
#include "util.hxx"

/******************************Public*Routine******************************\
* SS_TIME class
*
\**************************************************************************/

void
SS_TIME::Update()
{
    struct _timeb time;

    _ftime( &time );
    seconds = time.time + time.millitm/1000.0;
}

void
SS_TIME::Zero()
{
    seconds = 0.0;
}

double
SS_TIME::Seconds()
{
    return seconds;
}

SS_TIME
SS_TIME::operator+( SS_TIME addTime )
{
    return( *this += addTime );
}

SS_TIME
SS_TIME::operator-( SS_TIME subTime )
{
    return( *this -= subTime );
}

SS_TIME
SS_TIME::operator+=( SS_TIME addTime )
{
    seconds += addTime.seconds;
    return *this;
}

SS_TIME
SS_TIME::operator-=( SS_TIME subTime )
{
    seconds -= subTime.seconds;
    return *this;
}

/******************************Public*Routine******************************\
* SS_TIMER class
*
\**************************************************************************/

void
SS_TIMER::Start()
{
    startTime.Update();
}

SS_TIME
SS_TIMER::Stop()
{
    elapsed.Update();
    return elapsed - startTime;
}

void
SS_TIMER::Reset()
{
    elapsed.Zero();
}

SS_TIME
SS_TIMER::ElapsedTime()
{
    elapsed.Update();
    return elapsed - startTime;
}

/******************************Public*Routine******************************\
* ss_Rand
*
* Generates integer random number 0..(max-1)
*
\**************************************************************************/

int ss_iRand( int max )
{
    return (int) ( max * ( ((float)rand()) / ((float)(RAND_MAX+1)) ) );
}

/******************************Public*Routine******************************\
* ss_Rand2
*
* Generates integer random number min..max
*
\**************************************************************************/

int ss_iRand2( int min, int max )
{
    if( min == max )
        return min;
    else if( max < min ) {
        int temp = min;
        min = max;
        max = temp;
    }

    return min + (int) ( (max-min+1) * ( ((float)rand()) / ((float)(RAND_MAX+1)) ) );
}

/******************************Public*Routine******************************\
* ss_fRand
*
* Generates float random number min...max
*
\**************************************************************************/

FLOAT ss_fRand( FLOAT min, FLOAT max )
{
    FLOAT diff;

    diff = max - min;
    return min + ( diff * ( ((float)rand()) / ((float)(RAND_MAX)) ) );
}

/******************************Public*Routine******************************\
* ss_RandInit
*
* Initializes the randomizer
*
\**************************************************************************/

void ss_RandInit( void )
{
    struct _timeb time;

    _ftime( &time );
    srand( time.millitm );

    for( int i = 0; i < 10; i ++ )
        rand();
}

#if DBG
/******************************Public*Routine******************************\
* DbgPrint
*
* Formatted string output to the debugger.
*
* History:
*  26-Jan-1996 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

ULONG
DbgPrint(PCH DebugMessage, ...)
{
    va_list ap;
    char buffer[256];

    va_start(ap, DebugMessage);

    vsprintf(buffer, DebugMessage, ap);

    OutputDebugStringA(buffer);

    va_end(ap);

    return(0);
}
#endif


GEOM.C

/**********************************Module**********************************\
*
* geom.c
*
* 3D FlowerBox screen saver
* Geometry routines
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include "precomp.h"
#pragma hdrstop

// Sphere radius
#define RADIUS 1

// Values to map a 2D point onto a 3D plane
// Base point and axes to map X and Y coordinates onto
typedef struct _PLANE_MAP
{
    PT3 base, x_axis, y_axis;
} PLANE_MAP;

// Data area used by the current geometry
// Base points and generated points
PT3 pts[MAXPTS], npts[MAXPTS];
// Scaling factor for spherical projection
FLT vlen[MAXPTS];
// Normals
PT3 normals[MAXPTS];
// Vertex data indices
int index[MAXPTS*2];
// Triangle strip sizes
int strip_size[MAXSIDES*MAXSUBDIV];

void InitCube(GEOMETRY *geom);
void InitTetra(GEOMETRY *geom);
void InitPyramids(GEOMETRY *geom);
void InitCylinder(GEOMETRY *geom);
void InitSpring(GEOMETRY *geom);

GEOMETRY cube_geom = {InitCube};
GEOMETRY tetra_geom = {InitTetra};
GEOMETRY pyramids_geom = {InitPyramids};
GEOMETRY cylinder_geom = {InitCylinder};
GEOMETRY spring_geom = {InitSpring};

GEOMETRY *geom_table[] =
{
    &cube_geom,
    &tetra_geom,
    &pyramids_geom,
    &cylinder_geom,
    &spring_geom
};

extern BOOL bOgl11;
extern BOOL bCheckerOn;

/******************************Public*Routine******************************\
*
* InitVlen
*
* Precomputes scaling factor for spherical projection
*
\**************************************************************************/

void InitVlen(GEOMETRY *geom, int npts, PT3 *pts)
{
    FLT d;
    FLT *vl;

    vl = vlen;
    while (npts-- > 0)
    {
        d = V3Len(pts);

        // Don't allow really close points because this leads to
        // numeric instability and really large objects
        assert(d > 0.01f);

        // Geometries are created with size one, filling the area
        // from -.5 to .5.  This leads to distances generally less
        // than one, which leaves off half of the interesting morphing
        // effects due to the projection
        // Scaling up the scaling factor allows the values to
        // be both above and below one
        d *= geom->init_sf;

        assert(d > 0.0001f);

        *vl++ = (RADIUS-d)/d;

#if 0
        dprintf(("Distance is %f, vl %f\n", d, *(vl-1)));
#endif

        pts++;
    }
}

/******************************Public*Routine******************************\
*
* MapToSide
*
* Takes x,y coordinates in the range 0-1 and maps them onto the given
* side plane for the current geometry
*
\**************************************************************************/

void MapToSide(PLANE_MAP *map, FLT x, FLT y, PT3 *pt)
{
    pt->x = x*map->x_axis.x+y*map->y_axis.x+map->base.x;
    pt->y = x*map->x_axis.y+y*map->y_axis.y+map->base.y;
    pt->z = x*map->x_axis.z+y*map->y_axis.z+map->base.z;

}

void DrawWithVArrays (GEOMETRY *geom)
{
    int side, ss, idc, k;
    unsigned int *idx;

    for (side = 0; side < geom->nsides; side++) {
        geom->sides[side].dBuf = (GLuint *) LocalAlloc (LMEM_FIXED,
                                                        sizeof (GLuint) *
                                                        3 * MAXPTS * 2);
        k = 0;
        idx = geom->sides[side].strip_index;
        for (ss = 0; ss < geom->sides[side].nstrips; ss++) {
            if (geom->sides[side].strip_size[ss] < 3) continue;
            for (idc = 2; idc < geom->sides[side].strip_size[ss]; idc++) {
                if (!(idc % 2)) { //even
                    geom->sides[side].dBuf[k++] = *(idx+idc-2);
                    geom->sides[side].dBuf[k++] = *(idx+idc-1);
                } else {
                    geom->sides[side].dBuf[k++] = *(idx+idc-1);
                    geom->sides[side].dBuf[k++] = *(idx+idc-2);
                }
                geom->sides[side].dBuf[k++] = *(idx+idc);
            }
            idx += geom->sides[side].strip_size[ss];
        }
        geom->sides[side].num_eles = k;
    }
    glNormalPointer (GL_FLOAT, sizeof (PT3),
                     (GLfloat *)&(geom->normals[0].x));
    glVertexPointer (3, GL_FLOAT, sizeof (PT3),
                     (GLfloat *)&(geom->npts[0].x));
    glEnableClientState (GL_VERTEX_ARRAY);
    glEnableClientState (GL_NORMAL_ARRAY);

    glDisableClientState (GL_COLOR_ARRAY);
    glDisableClientState (GL_INDEX_ARRAY);
    glDisableClientState (GL_EDGE_FLAG_ARRAY);
    glDisableClientState (GL_TEXTURE_COORD_ARRAY);
}
/******************************Public*Routine******************************\
*
* InitCube
*
* Initialize the cube's geometry
*
\**************************************************************************/

#define CUBE_SIDES 6

PLANE_MAP cube_planes[CUBE_SIDES] =
{
    -0.5f, -0.5f,  0.5f,  1.0f,  0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
     0.5f, -0.5f, -0.5f, -1.0f,  0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
     0.5f,  0.5f, -0.5f, -1.0f,  0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  1.0f,  0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
     0.5f, -0.5f, -0.5f,  0.0f,  1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f
};

#define CUBE_IDX(side, x, y) ((side)*side_pts+(x)*(config.subdiv+1)+(y))

void InitCube(GEOMETRY *geom)
{
    int side, x, y;
    PT3 *pt;
    unsigned int *sz, *idx;
    int side_pts;

    side_pts = (config.subdiv+1)*(config.subdiv+1);

    geom->nsides = CUBE_SIDES;
    geom->pts = &pts[0];
    geom->npts = &npts[0];
    geom->normals = &normals[0];

    geom->min_sf = -1.1f;
    geom->max_sf = 5.1f;
    geom->sf_inc = 0.05f;
    geom->init_sf = 2.0f;

    // Generate triangle strip data
    sz = &strip_size[0];
    idx = &index[0];
    for (side = 0; side < geom->nsides; side++)
    {
        geom->sides[side].nstrips = config.subdiv;
        geom->sides[side].strip_size = sz;
        geom->sides[side].strip_index = idx;

        for (x = 0; x < config.subdiv; x++)
        {
            *sz++ = (config.subdiv+1)*2;

            for (y = 0; y < config.subdiv+1; y++)
            {
                *idx++ = CUBE_IDX(side, x, y);
                *idx++ = CUBE_IDX(side, x+1, y);
            }
        }
    }

    assert(sz-strip_size <= DIMA(strip_size));
    assert(idx-index <= DIMA(index));

    // Generate base vertices
    pt = geom->pts;
    for (side = 0; side < geom->nsides; side++)
    {
        for (x = 0; x < config.subdiv+1; x++)
        {
            for (y = 0; y < config.subdiv+1; y++)
            {
                MapToSide(&cube_planes[side],
                          (FLT)x/config.subdiv, (FLT)y/config.subdiv,
                          pt);
                pt++;
            }
        }
    }

    assert(pt-pts <= DIMA(pts));

    geom->total_pts = geom->nsides*side_pts;
}

/******************************Public*Routine******************************\
*
* InitTetra
*
* Initialize the tetrahedron's geometry
*
\**************************************************************************/

#define TETRA_SIDES 4

#define SQRT3 1.73205f
#define SQRT3_2 (SQRT3/2.0f)
#define SQRT3_3 (SQRT3/3.0f)
#define SQRT3_6 (SQRT3/6.0f)
#define SQRT3_12 (SQRT3/12.0f)

#define TETRA_BASE (-SQRT3/8.0f)

PLANE_MAP tetra_planes[TETRA_SIDES] =
{
    -0.5f, TETRA_BASE, SQRT3_6,
    1.0f, 0.0f, 0.0f, 0.0f, SQRT3_2, -SQRT3_6,

    0.0f, TETRA_BASE, -SQRT3_3,
    -0.5f, 0.0f, SQRT3_2, 0.25f, SQRT3_2, SQRT3_12,

    0.5f, TETRA_BASE, SQRT3_6,
    -0.5f, 0.0f, -SQRT3_2, -0.25f, SQRT3_2, SQRT3_12,

    0.5f, TETRA_BASE, SQRT3_6,
    -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -SQRT3_2
};

void InitTetra(GEOMETRY *geom)
{
    int side, x, y;
    PT3 *pt;
    int *sz, *idx;
    int side_pts;
    int base_pt;
    int row_pts;
    FLT fx;

    side_pts = (config.subdiv+2)*(config.subdiv+1)/2;

    geom->nsides = TETRA_SIDES;
    geom->pts = &pts[0];
    geom->npts = &npts[0];
    geom->normals = &normals[0];

    geom->min_sf = -1.1f;
    geom->max_sf = 5.2f;
    geom->sf_inc = 0.05f;
    geom->init_sf = 3.75f;

    // Generate triangle strip data
    sz = &strip_size[0];
    idx = &index[0];
    base_pt = 0;
    for (side = 0; side < geom->nsides; side++)
    {
        geom->sides[side].nstrips = config.subdiv;
        geom->sides[side].strip_size = sz;
        geom->sides[side].strip_index = idx;

        for (x = 0; x < config.subdiv; x++)
        {
            row_pts = config.subdiv-x+1;
            *sz++ = row_pts*2-1;

            *idx++ = base_pt;
            for (y = 0; y < row_pts-1; y++)
            {
                *idx++ = base_pt+row_pts+y;
                *idx++ = base_pt+1+y;
            }

            base_pt += row_pts;
        }

        base_pt++;
    }

    assert(sz-strip_size <= DIMA(strip_size));
    assert(idx-index <= DIMA(index));

    // Generate base vertices
    pt = geom->pts;
    for (side = 0; side < geom->nsides; side++)
    {
        for (x = 0; x < config.subdiv+1; x++)
        {
            fx = (FLT)x/config.subdiv;
            for (y = 0; y < config.subdiv-x+1; y++)
            {
                MapToSide(&tetra_planes[side],
                          fx+(FLT)y/(config.subdiv*2),
                          (FLT)y/config.subdiv,
                          pt);
                pt++;
            }
        }
    }

    assert(pt-pts <= DIMA(pts));

    geom->total_pts = geom->nsides*side_pts;
}

/******************************Public*Routine******************************\
*
* InitPyramids
*
* Initializes double pyramid geometry
*
\**************************************************************************/

#define PYRAMIDS_SIDES 8

PLANE_MAP pyramids_planes[PYRAMIDS_SIDES] =
{
    -0.5f, 0.0f,  0.5f,  1.0f, 0.0f,  0.0f,  0.0f,  0.5f, -0.5f,
     0.5f, 0.0f,  0.5f, -1.0f, 0.0f,  0.0f,  0.0f, -0.5f, -0.5f,
     0.5f, 0.0f,  0.5f,  0.0f, 0.0f, -1.0f, -0.5f,  0.5f,  0.0f,
     0.5f, 0.0f, -0.5f,  0.0f, 0.0f,  1.0f, -0.5f, -0.5f,  0.0f,
     0.5f, 0.0f, -0.5f, -1.0f, 0.0f,  0.0f,  0.0f,  0.5f,  0.5f,
    -0.5f, 0.0f, -0.5f,  1.0f, 0.0f,  0.0f,  0.0f, -0.5f,  0.5f,
    -0.5f, 0.0f, -0.5f,  0.0f, 0.0f,  1.0f,  0.5f,  0.5f,  0.0f,
    -0.5f, 0.0f,  0.5f,  0.0f, 0.0f, -1.0f,  0.5f, -0.5f,  0.0f
};

void InitPyramids(GEOMETRY *geom)
{
    int side, x, y;
    PT3 *pt;
    int *sz, *idx;
    int side_pts;
    int base_pt;
    int row_pts;
    FLT fx;

    side_pts = (config.subdiv+2)*(config.subdiv+1)/2;

    geom->nsides = PYRAMIDS_SIDES;
    geom->pts = &pts[0];
    geom->npts = &npts[0];
    geom->normals = &normals[0];

    geom->min_sf = -1.1f;
    geom->max_sf = 5.2f;
    geom->sf_inc = 0.05f;
    geom->init_sf = 3.0f;

    // Generate triangle strip data
    sz = &strip_size[0];
    idx = &index[0];
    base_pt = 0;

    for (side = 0; side < geom->nsides; side++) {
        geom->sides[side].nstrips = config.subdiv;
        geom->sides[side].strip_size = sz;
        geom->sides[side].strip_index = idx;

        for (x = 0; x < config.subdiv; x++) {
            row_pts = config.subdiv-x+1;
            *sz++ = row_pts*2-1;

            *idx++ = base_pt;
            for (y = 0; y < row_pts-1; y++) {
                *idx++ = base_pt+row_pts+y;
                *idx++ = base_pt+1+y;
            }

            base_pt += row_pts;
        }

        base_pt++;
    }

    assert(sz-strip_size <= DIMA(strip_size));
    assert(idx-index <= DIMA(index));

    // Generate base vertices
    pt = geom->pts;
    for (side = 0; side < geom->nsides; side++)
    {
        for (x = 0; x < config.subdiv+1; x++)
        {
            fx = (FLT)x/config.subdiv;
            for (y = 0; y < config.subdiv-x+1; y++)
            {
                MapToSide(&pyramids_planes[side],
                          fx+(FLT)y/(config.subdiv*2),
                          (FLT)y/config.subdiv,
                          pt);
                pt++;
            }
        }
    }

    assert(pt-pts <= DIMA(pts));

    geom->total_pts = geom->nsides*side_pts;
}

/******************************Public*Routine******************************\
*
* InitCylinder
*
* Initializes the cylinder geometry
*
\**************************************************************************/

void InitCylinder(GEOMETRY *geom)
{
    int side, x, y;
    PT3 *pt;
    int *sz, *idx;
    int base_pt;
    int row_pts;
    FLT fx, fz;
    double ang;

    geom->nsides = 1;
    geom->pts = &pts[0];
    geom->npts = &npts[0];
    geom->normals = &normals[0];

    geom->min_sf = -2.5f;
    geom->max_sf = 8.5f;
    geom->sf_inc = 0.05f;
    geom->init_sf = 2.1f;

    // Generate triangle strip data
    // If version 1.1 then allocate the index buffer for glDrawElements
    sz = &strip_size[0];
    idx = &index[0];
    side = 0;
    geom->sides[side].nstrips = config.subdiv;
    geom->sides[side].strip_size = sz;
    geom->sides[side].strip_index = idx;

    row_pts = config.subdiv+1;
    base_pt = 0;
    for (x = 0; x < config.subdiv; x++) {
        *sz++ = row_pts*2;

        for (y = 0; y < row_pts; y++) {
            // Wrap around at the edge so the cylinder normals
            // are properly averaged
            if (x == config.subdiv-1) {
                *idx++ = y;
            }
            else {
                *idx++ = base_pt+row_pts+y;
            }
            *idx++ = base_pt+y;
        }

        base_pt += row_pts;
    }

    assert(sz-strip_size <= DIMA(strip_size));
    assert(idx-index <= DIMA(index));

    // Generate base vertices
    pt = geom->pts;
    ang = 0;
    for (x = 0; x < config.subdiv; x++)
    {
        fx = (FLT)cos(ang)*0.5f;
        fz = (FLT)sin(ang)*0.5f;
        for (y = 0; y < config.subdiv+1; y++)
        {
            pt->x = fx;
            pt->y = (FLT)y/config.subdiv-0.5f;
            pt->z = fz;
            pt++;
        }
        ang += (2*PI)/config.subdiv;
    }

    assert(pt-pts <= DIMA(pts));

    geom->total_pts = geom->nsides*(config.subdiv+1)*config.subdiv;
}

/******************************Public*Routine******************************\
*
* InitSpring
*
* Initializes the spring geometry
*
\**************************************************************************/

#define SPRING_RADIUS 0.1f
#define SPRING_CENTER (0.5f-SPRING_RADIUS)

void InitSpring(GEOMETRY *geom)
{
    int side, x, y;
    PT3 *pt;
    int *sz, *idx;
    double ang_center, ang_surf;
    FLT cs, sn;
    FLT rad;
    PLANE_MAP plane;
    int spin_pts;
    int row_pts;

    geom->nsides = 1;
    geom->pts = &pts[0];
    geom->npts = &npts[0];
    geom->normals = &normals[0];

    geom->min_sf = -2.2f;
    geom->max_sf = 0.2f;
    geom->sf_inc = 0.05f;
    geom->init_sf = 1.0f;

    // Generate triangle strip data
    // If version 1.1 then allocate the index buffer for glDrawElements
    sz = &strip_size[0];
    idx = &index[0];
    side = 0;
    geom->sides[side].nstrips = config.subdiv;
    geom->sides[side].strip_size = sz;
    geom->sides[side].strip_index = idx;

    row_pts = config.subdiv;
    spin_pts = 4*config.subdiv+1;
    for (x = 0; x < config.subdiv; x++) {
        *sz++ = spin_pts*2;

        for (y = 0; y < spin_pts; y++) {
            *idx++ = x+row_pts*y;
            // Wrap around at the edge so the cylindrical surface
            // of the tube is seamless.  Without this the normal
            // averaging would be incorrect and a seam would be visible
            if (x == config.subdiv-1) {
                *idx++ = row_pts*y;
            }
            else {
                *idx++ = x+row_pts*y+1;
            }
        }
    }

    assert(sz-strip_size <= DIMA(strip_size));
    assert(idx-index <= DIMA(index));

    // Generate base vertices
    pt = geom->pts;
    ang_center = 0;
    plane.y_axis.x = 0.0f;
    plane.y_axis.y = SPRING_RADIUS;
    plane.y_axis.z = 0.0f;
    plane.x_axis.y = 0.0f;
    for (x = 0; x < spin_pts; x++)
    {
        cs = (FLT)cos(ang_center);
        sn = (FLT)sin(ang_center);
        rad = 0.5f-(FLT)x/(spin_pts-1)*(SPRING_CENTER/2);
        plane.base.x = cs*rad;
        plane.base.y = -0.5f+(FLT)x/(spin_pts-1);
        plane.base.z = sn*rad;
        plane.x_axis.x = cs*SPRING_RADIUS;
        plane.x_axis.z = sn*SPRING_RADIUS;

        ang_surf = 0;
        for (y = 0; y < config.subdiv; y++)
        {
            MapToSide(&plane,
                      (FLT)cos(ang_surf), (FLT)sin(ang_surf),
                      pt);
            pt++;
            ang_surf += (2*PI)/config.subdiv;
        }

        ang_center += (4*PI)/(spin_pts-1);
    }

    assert(pt-pts <= DIMA(pts));

    geom->total_pts = geom->nsides*spin_pts*config.subdiv;
}

/******************************Public*Routine******************************\
*
* DrawGeom
*
* Draw the current geometry
*
\**************************************************************************/

void DrawGeom(GEOMETRY *geom)
{
    int side, strip, k;
    int *idx, idc, idxv;

    if (config.smooth_colors)
    {
        glShadeModel(GL_SMOOTH);
    }
    else
    {
        glShadeModel(GL_FLAT);
    }

    if (config.color_pick == ID_COL_SINGLE)
    {
        glMaterialfv(config.two_sided, GL_DIFFUSE, solid_cols);
    }

    if (!(bOgl11 && !bCheckerOn)) {
        for (side = 0; side < geom->nsides; side++) {
            if (config.color_pick == ID_COL_PER_SIDE) {
                glMaterialfv(config.two_sided, GL_DIFFUSE, side_cols[side]);
            }

            idx = geom->sides[side].strip_index;
            for (strip = 0; strip < geom->sides[side].nstrips; strip++) {
                glBegin(GL_TRIANGLE_STRIP);

                for (idc = 0; idc < geom->sides[side].strip_size[strip];
                     idc++) {
                    idxv = *idx++;

                    assert(idxv >=0 && idxv < geom->total_pts);

                    if (config.color_pick == ID_COL_CHECKER) {
                        if (config.triangle_colors) {
                            glMaterialfv(config.two_sided, GL_DIFFUSE,
                                         checker_cols[side][(idc+1)/2+strip &
                                                           1]);
                        }
                        else {
                            glMaterialfv(config.two_sided, GL_DIFFUSE,
                                         checker_cols[side][idc/2+strip & 1]);
                        }
                    }

                    glNormal3fv((GLfloat *)&geom->normals[idxv]);
                    glVertex3fv((GLfloat *)&geom->npts[idxv]);
                }

                glEnd();
            }
        }
    } else {
        k = 0;
        for (side = 0; side < geom->nsides; side++) {
            if (config.color_pick == ID_COL_PER_SIDE)
                glMaterialfv(config.two_sided, GL_DIFFUSE, side_cols[side]);

            glDrawElements (GL_TRIANGLES, geom->sides[side].num_eles,
                            GL_UNSIGNED_INT, &(geom->sides[side].dBuf[0]));
            k += geom->sides[side].num_eles;
        }
    }
}

/******************************Public*Routine******************************\
*
* ComputeAveragedNormals
*
* Compute face-averaged normals for each vertex
*
\**************************************************************************/

void ComputeAveragedNormals(GEOMETRY *geom)
{
    int side, strip;
    int *sz;
    int *idx, idx1, idx2, idx3;
    int tc, idc;
    PT3 v1, v2, n1;

    memset(geom->normals, 0, sizeof(PT3)*geom->total_pts);

    for (side = 0; side < geom->nsides; side++)
    {
        idx = geom->sides[side].strip_index;
        sz = geom->sides[side].strip_size;
        for (strip = 0; strip < geom->sides[side].nstrips; strip++)
        {
            idx1 = *idx++;
            idx2 = *idx++;

            assert(idx1 >= 0 && idx1 < geom->total_pts &&
                   idx2 >= 0 && idx2 < geom->total_pts);

            tc = (*sz++)-2;
            for (idc = 0; idc < tc; idc++)
            {
                idx3 = *idx++;

                assert(idx3 >= 0 && idx3 < geom->total_pts);

                V3Sub(&geom->npts[idx3], &geom->npts[idx1], &v1);
                V3Sub(&geom->npts[idx2], &geom->npts[idx1], &v2);
                V3Cross(&v1, &v2, &n1);
                // Triangle strip ordering causes half of the triangles
                // to be oriented oppositely from the others
                // Those triangles need to have their normals flipped
                // so the whole triangle strip has consistent normals
                if ((idc & 1) == 0)
                {
                    n1.x = -n1.x;
                    n1.y = -n1.y;
                    n1.z = -n1.z;
                }

                V3Add(&geom->normals[idx1], &n1, &geom->normals[idx1]);
                V3Add(&geom->normals[idx2], &n1, &geom->normals[idx2]);
                V3Add(&geom->normals[idx3], &n1, &geom->normals[idx3]);

                idx1 = idx2;
                idx2 = idx3;
            }
        }
    }
}

/******************************Public*Routine******************************\
*
* UpdatePts
*
* Project the point array through a sphere according to the given scale factor
*
\**************************************************************************/

void UpdatePts(GEOMETRY *geom, FLT sf)
{
    int pt;
    FLT f, *vl;
    PT3 *v;
    PT3 *p;

    vl = vlen;
    p = &geom->pts[0];
    v = &geom->npts[0];
    for (pt = 0; pt < geom->total_pts; pt++)
    {
        f = (*vl++)*sf+1;
        v->x = p->x*f;
        v->y = p->y*f;
        v->z = p->z*f;
        p++;
        v++;
    }

    ComputeAveragedNormals(geom);
}


GEOM.H

/**********************************Module**********************************\
*
* geom.h
*
* 3D FlowerBox screen saver
* Geometry header file
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __GEOM_H__
#define __GEOM_H__

// Geometry of a shape

// A side of a shape
typedef struct _SIDE
{
    // Number of triangle strips in this side
    int nstrips;
    // Number of vertices per strip
    int *strip_size;
    // Indices for each point in the triangle strips
    unsigned int *strip_index;

    // The number of elements for glDrawElements call
    int num_eles;
    // Index buffer for glDrawElements
    GLuint *dBuf;
} SIDE;

typedef struct _GEOMETRY
{
    void (*init)(struct _GEOMETRY *geom);

    // Number of sides
    int nsides;
    // Sides
    SIDE sides[MAXSIDES];

    // Data for each vertex in the shape
    PT3 *pts, *npts;
    PT3 *normals;

    // Total number of vertices
    int total_pts;

    // Scaling control
    FLT min_sf, max_sf, sf_inc;

    // Initial scale factor setup control
    FLT init_sf;

} GEOMETRY;

#define GEOM_CUBE       0
#define GEOM_TETRA      1
#define GEOM_PYRAMIDS   2

extern GEOMETRY *geom_table[];

void InitVlen(GEOMETRY *geom, int npts, PT3 *pts);
void UpdatePts(GEOMETRY *geom, FLT sf);
void DrawGeom(GEOMETRY *geom);
extern void DrawWithVArrays (GEOMETRY *geom);

#endif // __GEOM_H__


PRECOMP.H

/**********************************Module**********************************\
*
* precomp.h
*
* Precompiled header file
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>

#include <windows.h>
#include <math.h>
#include <commctrl.h>
#include <scrnsave.h>

#include <gl\gl.h>
#include <gl\glu.h>
#include <gl\glaux.h>

#include <sscommon.h>

#include "rsrc.h"
#include "ssflwbox.h"
#include "geom.h"


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by ssflwbox.rc
//

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC                     1
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1002
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


RSRC.H

/**********************************Module**********************************\
*
* rsrc.h
*
* Resource definitions
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __RSRC_H__
#define __RSRC_H__

#define ID_COMPLEXITY              401
#define ID_IMAGE_SIZE              402

#define ID_COL_PICK_FIRST          ID_COL_CHECKER
#define ID_COL_CHECKER             500
#define ID_COL_PER_SIDE            501
#define ID_COL_SINGLE              502
#define ID_COL_PICK_LAST           ID_COL_SINGLE
#define ID_COL_PICK_COUNT          (ID_COL_PICK_LAST-ID_COL_PICK_FIRST+1)

#define ID_COL_SMOOTH              550
#define ID_COL_TRIANGLE            551
#define ID_COL_CYCLE               552

#define ID_SPIN                    600
#define ID_BLOOM                   601
#define ID_TWO_SIDED               602

#define ID_GEOM                    650

#define IDS_CONFIG_SMOOTH_COLORS        1000
#define IDS_CONFIG_TRIANGLE_COLORS      1001
#define IDS_CONFIG_CYCLE_COLORS         1002
#define IDS_CONFIG_SPIN                 1003
#define IDS_CONFIG_BLOOM                1004
#define IDS_CONFIG_SUBDIV               1005
#define IDS_CONFIG_COLOR_PICK           1006
#define IDS_CONFIG_IMAGE_SIZE           1007
#define IDS_CONFIG_GEOM                 1008
#define IDS_CONFIG_TWO_SIDED            1009

#define IDS_GEOM_FIRST                  IDS_GEOM_CUBE
#define IDS_GEOM_CUBE                   1025
#define IDS_GEOM_TETRA                  1026
#define IDS_GEOM_PYRAMIDS               1027
#define IDS_GEOM_CYLINDER               1028
#define IDS_GEOM_SPRING                 1029
#define IDS_GEOM_LAST                   IDS_GEOM_SPRING
#define IDS_GEOM_COUNT                  (IDS_GEOM_LAST-IDS_GEOM_FIRST+1)

#define IDS_INI_SECTION                 1051

#endif // __RSRC_H__


SSFLWBOX.C

/**********************************Module**********************************\
*
* ssflwbox.c
*
* 3D FlowerBox screen saver
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include "precomp.h"
#pragma hdrstop

// Minimum and maximum image sizes
#define MINIMAGESIZE 10
#define MAXIMAGESIZE 100

// Color tables for checkboard, per-side and single color modes
GLfloat base_checker_cols[MAXSIDES][NCCOLS][4] =
{
    1.0f, 0.0f, 0.0f, 1.0f,
    0.0f, 1.0f, 0.0f, 1.0f,
    0.0f, 1.0f, 0.0f, 1.0f,
    0.0f, 0.0f, 1.0f, 1.0f,
    0.0f, 0.0f, 1.0f, 1.0f,
    1.0f, 0.0f, 1.0f, 1.0f,
    1.0f, 0.0f, 1.0f, 1.0f,
    0.0f, 1.0f, 1.0f, 1.0f,
    0.0f, 1.0f, 1.0f, 1.0f,
    1.0f, 1.0f, 0.0f, 1.0f,
    1.0f, 1.0f, 0.0f, 1.0f,
    0.5f, 0.5f, 1.0f, 1.0f,
    0.5f, 0.5f, 1.0f, 1.0f,
    1.0f, 0.5f, 0.5f, 1.0f,
    1.0f, 0.5f, 0.5f, 1.0f,
    1.0f, 0.0f, 0.0f, 1.0f
};
GLfloat checker_cols[MAXSIDES][NCCOLS][4];

GLfloat base_side_cols[MAXSIDES][4] =
{
    1.0f, 0.0f, 0.0f, 1.0f,
    0.0f, 1.0f, 0.0f, 1.0f,
    0.0f, 0.0f, 1.0f, 1.0f,
    1.0f, 0.0f, 1.0f, 1.0f,
    0.0f, 1.0f, 1.0f, 1.0f,
    1.0f, 1.0f, 0.0f, 1.0f,
    0.5f, 0.5f, 1.0f, 1.0f,
    1.0f, 0.5f, 0.5f, 1.0f
};
GLfloat side_cols[MAXSIDES][4];

GLfloat base_solid_cols[4] =
{
    1.0f, 1.0f, 1.0f, 1.0f
};
GLfloat solid_cols[4];

// Current geometry
GEOMETRY *cur_geom;

// Set when a rendering context is available
BOOL gbGlInit = FALSE;

// Common library context
SSContext gssc;

// Spin rotations
double xr = 0, yr = 0, zr = 0;
// Scale factor and increment
FLT sf;
FLT sfi;
// Color cycling hue phase
FLT phase = 0.0f;

// Default configuration
CONFIG config =
{
    TRUE, FALSE, FALSE, TRUE, TRUE, MAXSUBDIV, ID_COL_PER_SIDE,
    (MAXIMAGESIZE+MINIMAGESIZE)/2, GEOM_CUBE, GL_FRONT
};

// A slider range
typedef struct _RANGE
{
    int min_val;
    int max_val;
    int step;
    int page_step;
} RANGE;

RANGE complexity_range = {MINSUBDIV, MAXSUBDIV, 1, 2};
RANGE image_size_range = {MINIMAGESIZE, MAXIMAGESIZE, 1, 10};

// True if the current OpenGL version is 1.1
BOOL bOgl11;

// True if checkered mode is on
BOOL bCheckerOn;

/******************************Public*Routine******************************\
*
* dprintf
*
* Debug output printf
*
\**************************************************************************/

#if DBG
void dprintf_out(char *fmt, ...)
{
    va_list args;
    char dbg[256];

    va_start(args, fmt);
    vsprintf(dbg, fmt, args);
    va_end(args);
    OutputDebugStringA(dbg);
}
#endif

/******************************Public*Routine******************************\
*
* assert_failed
*
* Assertion failure handler
*
\**************************************************************************/

#if DBG
void assert_failed(char *file, int line, char *msg)
{
    dprintf(("Assertion failed %s(%d): %s\n", file, line, msg));
    DebugBreak();
}
#endif

/******************************Public*Routine******************************\
*
* V3Len
*
* Vector length
*
\**************************************************************************/

FLT V3Len(PT3 *v)
{
    return (FLT)sqrt(v->x*v->x+v->y*v->y+v->z*v->z);
}

/******************************Public*Routine******************************\
*
* ComputeHsvColors
*
* Compute a smooth range of colors depending on the current color mode
*
\**************************************************************************/

void ComputeHsvColors(void)
{
    GLfloat *cols;
    int ncols;
    FLT ang, da;
    int hex;
    FLT fhex, frac;
    FLT p, q, t;
    FLT sat, val;

    switch(config.color_pick)
    {
    case ID_COL_CHECKER:
        ncols = MAXSIDES*NCCOLS;
        cols = &checker_cols[0][0][0];
        break;
    case ID_COL_PER_SIDE:
        ncols = MAXSIDES;
        cols = &side_cols[0][0];
        break;
    case ID_COL_SINGLE:
        ncols = 1;
        cols = &solid_cols[0];
        break;
    }

    ang = phase;
    da = (FLT)((2*PI)/ncols);
    val = sat = 1.0f;

    while (ncols > 0)
    {
        fhex = (FLT)(6*ang/(2*PI));
        hex = (int)fhex;
        frac = fhex-hex;
        hex = hex % 6;

p = val*(1-sat);
q = val*(1-sat*frac);
t = val*(1-sat*(1-frac));

switch(hex)
{
case 0:
            cols[0] = val;
            cols[1] = t;
            cols[2] = p;
    break;
case 1:
            cols[0] = q;
            cols[1] = val;
            cols[2] = p;
    break;
case 2:
            cols[0] = p;
            cols[1] = val;
            cols[2] = t;
    break;
case 3:
            cols[0] = p;
            cols[1] = q;
            cols[2] = val;
    break;
case 4:
            cols[0] = t;
            cols[1] = p;
            cols[2] = val;
            break;
case 5:
            cols[0] = val;
            cols[1] = p;
            cols[2] = q;
    break;
}

        ang += da;
        cols += 4;
        ncols--;
    }
}

/******************************Public*Routine******************************\
*
* Draw
*
* Draw everything
*
\**************************************************************************/

void Draw(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLoadIdentity();

    glRotated(xr, 1, 0, 0);
    glRotated(yr, 0, 1, 0);
    glRotated(zr, 0, 0, 1);

    DrawGeom(cur_geom);

    glFlush();
}

/******************************Public*Routine******************************\
*
* Update
*
* Update all varying values, called by the common library
*
\**************************************************************************/

void Update(void *data)
{
    if (config.spin)
    {
        xr += 3;
        yr += 2;
    }

    if (config.bloom)
    {
        sf += sfi;
        if (sf > cur_geom->max_sf ||
            sf < cur_geom->min_sf)
        {
            sfi = -sfi;
        }
        UpdatePts(cur_geom, sf);
    }

    if (config.cycle_colors)
    {
        ComputeHsvColors();
        phase += (FLT)(2.5*PI/180.);
    }

    Draw();
}

// String storage
TCHAR geom_names[IDS_GEOM_COUNT][20];

/******************************Public*Routine******************************\
* getIniSettings
*
* Get the screen saver configuration options from .INI file/registry.
*
\**************************************************************************/

static void
getIniSettings()
{
    // Get registry settings

    if( ! ss_RegistrySetup( GetModuleHandle(NULL), IDS_INI_SECTION,
                            IDS_INIFILE ) )
        return;

    config.smooth_colors =
        ss_GetRegistryInt( IDS_CONFIG_SMOOTH_COLORS, config.smooth_colors );
    config.triangle_colors =
        ss_GetRegistryInt( IDS_CONFIG_TRIANGLE_COLORS, config.triangle_colors );
    config.cycle_colors =
        ss_GetRegistryInt( IDS_CONFIG_CYCLE_COLORS, config.cycle_colors );
    config.spin =
        ss_GetRegistryInt( IDS_CONFIG_SPIN, config.spin );
    config.bloom =
        ss_GetRegistryInt( IDS_CONFIG_BLOOM, config.bloom );
    config.subdiv =
        ss_GetRegistryInt( IDS_CONFIG_SUBDIV, config.subdiv );
    config.color_pick =
        ss_GetRegistryInt( IDS_CONFIG_COLOR_PICK, config.color_pick );
    config.image_size =
        ss_GetRegistryInt( IDS_CONFIG_IMAGE_SIZE, config.image_size );
    config.geom =
        ss_GetRegistryInt( IDS_CONFIG_GEOM, config.geom );
    config.two_sided =
        ss_GetRegistryInt( IDS_CONFIG_TWO_SIDED, config.two_sided );
}

/******************************Public*Routine******************************\
* saveIniSettings
*
* Save the screen saver configuration option to the .INI file/registry.
*
\**************************************************************************/

static void
saveIniSettings()
{
    if( ! ss_RegistrySetup( GetModuleHandle(NULL), IDS_INI_SECTION,
                            IDS_INIFILE ) )
        return;

    ss_WriteRegistryInt( IDS_CONFIG_SMOOTH_COLORS, config.smooth_colors );

    ss_WriteRegistryInt( IDS_CONFIG_TRIANGLE_COLORS, config.triangle_colors );
    ss_WriteRegistryInt( IDS_CONFIG_CYCLE_COLORS, config.cycle_colors );
    ss_WriteRegistryInt( IDS_CONFIG_SPIN, config.spin );
    ss_WriteRegistryInt( IDS_CONFIG_BLOOM, config.bloom );
    ss_WriteRegistryInt( IDS_CONFIG_SUBDIV, config.subdiv );
    ss_WriteRegistryInt( IDS_CONFIG_COLOR_PICK, config.color_pick );
    ss_WriteRegistryInt( IDS_CONFIG_IMAGE_SIZE, config.image_size );
    ss_WriteRegistryInt( IDS_CONFIG_GEOM, config.geom );
    ss_WriteRegistryInt( IDS_CONFIG_TWO_SIDED, config.two_sided );
}

/******************************Public*Routine******************************\
*
* NewConfig
*
* Set up a new configuration
*
\**************************************************************************/

void NewConfig(CONFIG *cnf)
{
    // Set new config
    config = *cnf;

    // Save to ini file
    saveIniSettings();

    // Reset colors
    memcpy(checker_cols, base_checker_cols, sizeof(checker_cols));
    memcpy(side_cols, base_side_cols, sizeof(side_cols));
    memcpy(solid_cols, base_solid_cols, sizeof(solid_cols));

    // Reset geometry
    cur_geom = geom_table[config.geom];
    cur_geom->init(cur_geom);
    if (bOgl11 && !bCheckerOn) DrawWithVArrays (cur_geom);

    assert(cur_geom->total_pts <= MAXPTS);

    InitVlen(cur_geom, cur_geom->total_pts, cur_geom->pts);
    sf = 0.0f;
    sfi = cur_geom->sf_inc;
    UpdatePts(cur_geom, sf);

    // Reset OpenGL parameters according to configuration
    // Only done if GL has been initialized
    if (gbGlInit)
    {
        GLfloat fv4[4];

        if (config.two_sided == GL_FRONT_AND_BACK)
        {
            glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
            glDisable(GL_CULL_FACE);
        }
        else
        {
            glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
            glEnable(GL_CULL_FACE);
        }

        fv4[0] = fv4[1] = fv4[2] = .8f;
        fv4[3] = 1.0f;
        glMaterialfv(config.two_sided, GL_SPECULAR, fv4);
        glMaterialf(config.two_sided, GL_SHININESS, 30.0f);
    }
}

/******************************Public*Routine******************************\
*
* RegisterDialogClasses
*
* Standard screensaver hook
*
\**************************************************************************/

BOOL WINAPI RegisterDialogClasses(HANDLE hinst)
{
    return TRUE;
}

// Temporary configuration for when the configuration dialog is active
// If the dialog is ok'ed then this becomes the current configuration,
// otherwise it is discarded
CONFIG temp_config;

/******************************Public*Routine******************************\
*
* ScreenSaverConfigureDialog
*
* Standard screensaver hook
*
\**************************************************************************/

BOOL CALLBACK ScreenSaverConfigureDialog(HWND hdlg, UINT msg,
                                         WPARAM wpm, LPARAM lpm)
{
    HWND hCtrl;
    int i;

    switch(msg)
    {
    case WM_INITDIALOG:

        InitCommonControls();

        getIniSettings();

        temp_config = config;

        CheckRadioButton(hdlg, ID_COL_PICK_FIRST, ID_COL_PICK_LAST,
                         config.color_pick);
        CheckDlgButton(hdlg, ID_COL_SMOOTH, config.smooth_colors);
        CheckDlgButton(hdlg, ID_COL_TRIANGLE, config.triangle_colors);
        CheckDlgButton(hdlg, ID_COL_CYCLE, config.cycle_colors);
        CheckDlgButton(hdlg, ID_SPIN, config.spin);
        CheckDlgButton(hdlg, ID_BLOOM, config.bloom);
        CheckDlgButton(hdlg, ID_TWO_SIDED,
                       config.two_sided == GL_FRONT_AND_BACK);

        ss_SetupTrackbar( hdlg, ID_COMPLEXITY, MINSUBDIV, MAXSUBDIV,
                          complexity_range.step,
                          complexity_range.page_step,
                          config.subdiv);

        ss_SetupTrackbar( hdlg, ID_IMAGE_SIZE, MINIMAGESIZE, MAXIMAGESIZE,
                          image_size_range.step,
                          image_size_range.page_step,
                          config.image_size);

        hCtrl = GetDlgItem(hdlg, ID_GEOM);
        SendMessage(hCtrl, CB_RESETCONTENT, 0, 0);
        for (i = 0; i < IDS_GEOM_COUNT; i++)
        {
            LoadString( hMainInstance, i+IDS_GEOM_FIRST, geom_names[i],
                        sizeof(geom_names)/IDS_GEOM_COUNT );
            SendMessage(hCtrl, CB_ADDSTRING, 0, (LPARAM)geom_names[i]);
        }
        SendMessage(hCtrl, CB_SETCURSEL, config.geom, 0);

        SetFocus(GetDlgItem(hdlg, ID_COMPLEXITY));
        return FALSE;

    case WM_COMMAND:
        switch(LOWORD(wpm))
        {
        case ID_COL_CHECKER:
        case ID_COL_PER_SIDE:
        case ID_COL_SINGLE:
            temp_config.color_pick = LOWORD(wpm);
            break;

        case ID_COL_SMOOTH:
            temp_config.smooth_colors = !temp_config.smooth_colors;
            break;
        case ID_COL_TRIANGLE:
            temp_config.triangle_colors = !temp_config.triangle_colors;
            break;
        case ID_COL_CYCLE:
            temp_config.cycle_colors = !temp_config.cycle_colors;
            break;

        case ID_SPIN:
            temp_config.spin = !temp_config.spin;
            break;
        case ID_BLOOM:
            temp_config.bloom = !temp_config.bloom;
            break;
        case ID_TWO_SIDED:
            temp_config.two_sided =
                temp_config.two_sided == GL_FRONT_AND_BACK ? GL_FRONT :
                GL_FRONT_AND_BACK;
            break;

        case IDOK:
            temp_config.subdiv =
                ss_GetTrackbarPos(hdlg, ID_COMPLEXITY);
            temp_config.image_size =
                ss_GetTrackbarPos(hdlg, ID_IMAGE_SIZE);
            temp_config.geom =
                SendMessage(GetDlgItem(hdlg, ID_GEOM), CB_GETCURSEL, 0, 0);
            NewConfig(&temp_config);
            // Fall through
        case IDCANCEL:
            EndDialog(hdlg, LOWORD(wpm));
            break;
        }
        return TRUE;

    }

    return FALSE;
}

/******************************Public*Routine******************************\
*
* Init
*
* Drawing initialization, called by common library
*
\**************************************************************************/

void Init(void *data)
{
    GLfloat fv4[4];

    gbGlInit = TRUE;

    bOgl11 = ss_fOnGL11();

    if (config.color_pick == ID_COL_CHECKER) bCheckerOn = TRUE;
    else bCheckerOn = FALSE;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45, 1, 2, 5); // for object range -1.5 to 1.5
    gluLookAt(0, 0, 3.5, 0, 0, 0, 0, 1, 0);
    glMatrixMode(GL_MODELVIEW);

    glEnable(GL_DEPTH_TEST);
    glClearDepth(1);

    glCullFace(GL_BACK);

    fv4[0] = 2.0f;
    fv4[1] = 2.0f;
    fv4[2] = 10.0f;
    fv4[3] = 1.0f;
    glLightfv(GL_LIGHT0, GL_POSITION, fv4);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glEnable(GL_NORMALIZE);

    // Make default configuration current
    NewConfig(&config);
}

/******************************Public*Routine******************************\
* SetFloaterInfo
*
* Set the size and motion of the floating window
*
\**************************************************************************/
void
SetFloaterInfo( ISIZE *pParentSize, CHILD_INFO *pChild )
{
    float sizeFact;
    float sizeScale;
    int size;
    ISIZE *pChildSize = &pChild->size;
    MOTION_INFO *pMotion = &pChild->motionInfo;

    sizeScale = (float)config.image_size / 100.0f;
    sizeFact = 0.25f + (0.5f * sizeScale);     // range 25-75%
    size = (int) (sizeFact * ( ((float)(pParentSize->width + pParentSize->height )) / 2.0f ));
    SS_CLAMP_TO_RANGE2( size, 0, pParentSize->width );
    SS_CLAMP_TO_RANGE2( size, 0, pParentSize->height );

    pChildSize->width = pChildSize->height = size;

    // Floater motion
    pMotion->posInc.x = .005f * (float) size;
    if( pMotion->posInc.x < 1.0f )
        pMotion->posInc.x = 1.0f;
    pMotion->posInc.y = pMotion->posInc.x;
    pMotion->posIncVary.x = .4f * pMotion->posInc.x;
    pMotion->posIncVary.y = pMotion->posIncVary.x;
}

/******************************Public*Routine******************************\
*
* FloaterFail
*
* Called when the floating window cannot be created
*
\**************************************************************************/
void FloaterFail(void *data)
{
    HINSTANCE hinst;
    TCHAR error_str[20];
    TCHAR start_failed[80];

    hinst = GetModuleHandle(NULL);
    if (LoadString(hinst, IDS_ERROR,
                   error_str, sizeof(error_str)) &&
        LoadString(hinst, IDS_START_FAILED,
                   start_failed, sizeof(start_failed)))
    {
        MessageBox(NULL, start_failed, error_str, MB_OK);
    }
}

/******************************Public*Routine******************************\
*
* ss_Init
*
* Screensaver initialization routine, called at startup by common library
*
\**************************************************************************/

SSContext *ss_Init(void)
{
    getIniSettings();

    ss_InitFunc(Init);
    ss_UpdateFunc(Update);

    gssc.bFloater = TRUE;
    gssc.floaterInfo.bMotion = TRUE;
    gssc.floaterInfo.ChildSizeFunc = SetFloaterInfo;

    gssc.bDoubleBuf = TRUE;
    gssc.depthType = SS_DEPTH16;

    return &gssc;
}

/**************************************************************************\
* ConfigInit
*
* Dialog box version of ss_Init.  Used for setting up any gl drawing on the
* dialog.
*
\**************************************************************************/
BOOL
ss_ConfigInit( HWND hDlg )
{
    return TRUE;
}


SSFLWBOX.H

/**********************************Module**********************************\
*
* ssflwbox.h
*
* 3D FlowerBox screen saver
* Base header file
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __SSFLWBOX_H__
#define __SSFLWBOX_H__

#ifndef PI
#define PI 3.14159265358979323846
#endif

// Minimum and maximum number of side subdivisions
#define MINSUBDIV 2
#define MAXSUBDIV 10

// Maximum values allowed
#define MAXSIDES 8
#define MAXSPTS ((MAXSUBDIV+1)*(MAXSUBDIV+1))
#define MAXPTS (MAXSIDES*MAXSPTS)
#define MAXSFACES (MAXSUBDIV*MAXSUBDIV)
#define MAXFACES (MAXSIDES*MAXSFACES)
#define MAXFPTS 4

// Number of colors used in checkerboarding
#define NCCOLS 2

// Allow floating point type configurability
typedef GLfloat FLT;
typedef struct
{
    FLT x, y, z;
} PT3;

// Configurable options
typedef struct _CONFIG
{
    BOOL smooth_colors;
    BOOL triangle_colors;
    BOOL cycle_colors;
    BOOL spin;
    BOOL bloom;
    int subdiv;
    int color_pick;
    int image_size;
    int geom;
    int two_sided;
} CONFIG;

extern CONFIG config;

extern GLfloat checker_cols[MAXSIDES][NCCOLS][4];
extern GLfloat side_cols[MAXSIDES][4];
extern GLfloat solid_cols[4];

#if defined(assert)
#undef assert
#endif
#if DBG
#define dprintf(args) dprintf_out args
void dprintf_out(char *fmt, ...);
#define assert(e) if (!(e)) assert_failed(__FILE__, __LINE__, #e); else 0
void assert_failed(char *file, int line, char *msg);
#else
#define dprintf(args)
#define assert(e)
#endif

#define DIMA(a) (sizeof(a)/sizeof(a[0]))

/******************************Public*Routine******************************\
*
* Basic vector math macros
*
* History:
*  Wed Jul 19 14:49:49 1995-by-Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

#define V3Sub(a, b, r) \
    ((r)->x = (a)->x-(b)->x, (r)->y = (a)->y-(b)->y, (r)->z = (a)->z-(b)->z)
#define V3Add(a, b, r) \
    ((r)->x = (a)->x+(b)->x, (r)->y = (a)->y+(b)->y, (r)->z = (a)->z+(b)->z)
#define V3Cross(a, b, r) \
    ((r)->x = (a)->y*(b)->z-(b)->y*(a)->z,\
     (r)->y = (a)->z*(b)->x-(b)->z*(a)->x,\
     (r)->z = (a)->x*(b)->y-(b)->x*(a)->y)
extern FLT V3Len(PT3 *v);

#endif // __SSFLWBOX_H__


DIALOG.CXX

/******************************Module*Header*******************************\
* Module Name: dialog.cxx
*
* Dialog box functions
*
* Copyright 1995 - 1998 Microsoft Corporation
\**************************************************************************/

#include "pch.c"
#pragma hdrstop

#include <commdlg.h>
#include <commctrl.h>
#include <scrnsave.h>
#include "mazedlg.h"

#include "sscommon.hxx"

// Global string buffers for message box.

// local funtions

static void updateDialogControls(HWND hDlg);

HWND    ghDlg; // main dialog window handle
int     giSize;
int     giImageQual;
BOOL    gbTurboMode;
int     gnBitsPerPixel;

static void DrawTexture( int surface );
static void CleanUp( HWND hwnd );

// Texture information for each surface: used in both configure and run mode

TEX_INFO gTexInfo[NUM_SURFACES] = {0};

// default surface texture cache
static TEXTURE gDefTex[NUM_DEF_SURFACE_TEXTURES] = {0};

// Per-surface texture information relevant only to configure mode

typedef struct {
    TEXTURE userTex;
    int  iDefTex;
    int  defTexIDS;
    int  fileIDS;
    int  offsetIDS;
    int  dlgSpinTexID;
    int  iPalRot;  // texture palette rotation index
    SS_TEX_BUTTON *pTexBtn;  // GL texture drawing button thing
} TEX_DLG;

static TEX_DLG gTex[NUM_SURFACES] = {
    {
      {0},
      BRICK_TEXTURE,
      IDS_DEF_WALL_TEXTURE,
      IDS_WALL_TEXTURE_FILE,
      IDS_WALL_TEXTURE_OFFSET,
      DLG_SPIN_WALLS,
      NULL
    },
    {
      {0},
      WOOD_TEXTURE,
      IDS_DEF_FLOOR_TEXTURE,
      IDS_FLOOR_TEXTURE_FILE,
      IDS_FLOOR_TEXTURE_OFFSET,
      DLG_SPIN_FLOOR,
      NULL
    },
    {
      {0},
      CASTLE_TEXTURE,
      IDS_DEF_CEILING_TEXTURE,
      IDS_CEILING_TEXTURE_FILE,
      IDS_CEILING_TEXTURE_OFFSET,
      DLG_SPIN_CEILING,
      NULL
    }
};

static UINT idTimer = 0;

static BOOL CALLBACK TextureConfigureDialog(HWND hDlg, UINT message,
                            WPARAM wParam, LPARAM lParam);

/******************************Public*Routine******************************\
* getIniSettings
*
* - Get the screen saver configuration options from .INI file/registry.
* - Called by both dialog box and screen saver
*
\**************************************************************************/

void
getIniSettings()
{
    int    option;
    int    i;

    // Load resources

    LoadString(hMainInstance, IDS_GENNAME, szScreenSaver,
               sizeof(szScreenSaver) / sizeof(TCHAR));

    // Get registry settings

    if( ss_RegistrySetup( hMainInstance, IDS_SAVERNAME, IDS_INIFILE ) )
    {
        // get wall/floor/ceiling texture enables
        // For now, texturing always on
        for( i = 0; i < NUM_SURFACES; i++ )
            gTexInfo[i].bTex = TRUE;

        option = ss_GetRegistryInt( IDS_DEFAULT_TEXTURE_ENABLE, (1 << NUM_SURFACES)-1 );
        for( i = 0; i < NUM_SURFACES; i++, option >>= 1 )
            gTexInfo[i].bDefTex = option & 1;

        // get default texture indices

        for( i = 0; i < NUM_SURFACES; i++ ) {
            gTexInfo[i].iDefTex =
                    ss_GetRegistryInt( gTex[i].defTexIDS, gTex[i].iDefTex );
            SS_CLAMP_TO_RANGE2( gTexInfo[i].iDefTex, 0,
                                                NUM_DEF_SURFACE_TEXTURES-1 );
        }

        // get user texture files

        for( i = 0; i < NUM_SURFACES; i++ ) {
            ss_GetRegistryString( gTex[i].fileIDS, 0,
                                  gTexInfo[i].texFile.szPathName, MAX_PATH);
            gTexInfo[i].texFile.nOffset = ss_GetRegistryInt( gTex[i].offsetIDS, 0 );
        }

        // get overlay
        maze_options.top_view = ss_GetRegistryInt( IDS_OVERLAY, 0 );

        // Get rat population
        maze_options.nrats = ss_GetRegistryInt(IDS_NRATS, 1);

        // get image quality

        giImageQual = ss_GetRegistryInt( IDS_IMAGEQUAL, IMAGEQUAL_DEFAULT );
        SS_CLAMP_TO_RANGE2( giImageQual, IMAGEQUAL_DEFAULT, IMAGEQUAL_HIGH );

        // get size

        giSize = ss_GetRegistryInt( IDS_SIZE, 0 );
        SS_CLAMP_TO_RANGE2( giSize, MIN_SLIDER, MAX_SLIDER );

        // get turbo mode
        gbTurboMode = ss_GetRegistryInt( IDS_TURBOMODE, 1 );
    }
}

/******************************Public*Routine******************************\
* saveIniSettings
*
* Save the screen saver configuration option to the .INI file/registry.
\**************************************************************************/

static void
saveIniSettings(HWND hDlg)
{
    if( ss_RegistrySetup( hMainInstance, IDS_SAVERNAME, IDS_INIFILE ) )
    {
        int i, option = 0;

        // write enables

        for( i = NUM_SURFACES-1, option = 0; i >= 0; i--, option <<= 1 )
            option |= gTexInfo[i].bDefTex & 1;
        ss_WriteRegistryInt( IDS_DEFAULT_TEXTURE_ENABLE, option >>= 1 );

        // Write default texture indices

        for( i = 0; i < NUM_SURFACES; i++ ) {
            ss_WriteRegistryInt( gTex[i].defTexIDS, gTexInfo[i].iDefTex );
        }

        // write user texture files

        for( i = 0; i < NUM_SURFACES; i++ ) {
            ss_WriteRegistryString( gTex[i].fileIDS,
                                    gTexInfo[i].texFile.szPathName );
            ss_WriteRegistryInt( gTex[i].offsetIDS, gTexInfo[i].texFile.nOffset);
        }

        // write size
        ss_WriteRegistryInt( IDS_SIZE,
                    ss_GetTrackbarPos(hDlg, DLG_SLIDER_SIZE) );

        // write overlay enable
        ss_WriteRegistryInt( IDS_OVERLAY, maze_options.top_view );

        // Write rat population
        ss_WriteRegistryInt( IDS_NRATS, maze_options.nrats );

        // write image quality
        ss_WriteRegistryInt( IDS_IMAGEQUAL, giImageQual );

        // turbot mod
        ss_WriteRegistryInt( IDS_TURBOMODE, gbTurboMode );
    }
}

/******************************Public*Routine******************************\
* setupDialogControls
*
* Do initial setup of dialog controls.
\**************************************************************************/

static void
setupDialogControls(HWND hDlg)
{
    int i;
    int idsImageQual;
    TCHAR szStr[GEN_STRING_SIZE];

    InitCommonControls();

    // setup size slider

    ss_SetupTrackbar( hDlg, DLG_SLIDER_SIZE, MIN_SLIDER, MAX_SLIDER, 1, 9,
                      giSize );

    // setup default texture spins

    for( i = 0; i < NUM_SURFACES; i ++ ) {
        SendDlgItemMessage( hDlg, gTex[i].dlgSpinTexID, UDM_SETRANGE, 0,
                            MAKELONG(NUM_DEF_SURFACE_TEXTURES-1, 0) );
        SendDlgItemMessage( hDlg, gTex[i].dlgSpinTexID, UDM_SETPOS, 0,
                            MAKELONG(gTexInfo[i].iDefTex, 0) );
    }

    // setup image quality combo box

    idsImageQual = IDS_IMAGEQUAL_DEFAULT;
    for( i = 0; i < IMAGEQUAL_COUNT; i++, idsImageQual++ ) {
        LoadString(hMainInstance, idsImageQual, szStr,
                    GEN_STRING_SIZE);
        SendDlgItemMessage(hDlg, DLG_COMBO_IMAGEQUAL, CB_ADDSTRING, 0,
                           (LPARAM) szStr);
    }
    SendDlgItemMessage(hDlg, DLG_COMBO_IMAGEQUAL, CB_SETCURSEL,
                       giImageQual, 0);

    // Disable Quality box when running on > 16 bits per pixel (since it only
    // affects dithering)
    gnBitsPerPixel = GetDeviceCaps( GetDC(hDlg), BITSPIXEL );
    EnableWindow( GetDlgItem(hDlg, DLG_COMBO_IMAGEQUAL), gnBitsPerPixel <= 16 );
    EnableWindow( GetDlgItem(hDlg, IDC_STATIC_IMAGEQUAL), gnBitsPerPixel <= 16 );

    // set palette rotation index for each surface for a8 textures
    for( i = 0; i < NUM_SURFACES; i ++ )
        gTex[i].iPalRot = ss_iRand(0xff);

    // set state of other controls

    CheckDlgButton(hDlg, DLG_CHECK_OVERLAY, maze_options.top_view );

    CheckDlgButton(hDlg, DLG_CHECK_TURBOMODE, gbTurboMode );

    updateDialogControls(hDlg);
}

/******************************Public*Routine******************************\
* updateDialogControls
*
* Updates dialog controls according to current state
\**************************************************************************/

static void
updateDialogControls(HWND hDlg)
{
    int i;
    BOOL bDither;

    static int dlgSpinTexDef[NUM_SURFACES] = {
        DLG_SPIN_WALLS,
        DLG_SPIN_FLOOR,
        DLG_SPIN_CEILING};

    for( i = 0; i < NUM_SURFACES; i ++ ) {
        EnableWindow( GetDlgItem(hDlg, gTex[i].dlgSpinTexID), gTexInfo[i].bDefTex );
    }

    EnableWindow( GetDlgItem(hDlg, DLG_SLIDER_SIZE), !gbTurboMode );
    EnableWindow( GetDlgItem(hDlg, IDC_STATIC_SIZE), !gbTurboMode );
    EnableWindow( GetDlgItem(hDlg, IDC_STATIC_MIN), !gbTurboMode );
    EnableWindow( GetDlgItem(hDlg, IDC_STATIC_MAX), !gbTurboMode );

    // Dithering looks bad with turbo mode on and 8 bits/pixel
    bDither = (gnBitsPerPixel <= 16) && !gbTurboMode;
    EnableWindow( GetDlgItem(hDlg, DLG_COMBO_IMAGEQUAL), bDither );
    EnableWindow( GetDlgItem(hDlg, IDC_STATIC_IMAGEQUAL), bDither );
}

/******************************Public*Routine******************************\
* updateGLState
*
\**************************************************************************/

static void
updateGLState()
{
    if( giImageQual == IMAGEQUAL_DEFAULT )
        glDisable( GL_DITHER );
    else
        glEnable( GL_DITHER );
}

/******************************Public*Routine******************************\
* ValidateTexture
*
*
\**************************************************************************/

static TEXTURE *
ValidateTexture( int surface )
{
    TEXTURE *pTex = NULL;
    TEX_INFO *pTexInfo = &gTexInfo[surface];
    extern TEX_RES gTexResSurf[]; // from glmaze.c

    if( !pTexInfo->bDefTex ) {
        // Try to draw user texture
        pTex = &gTex[surface].userTex;
        if( !pTex->data ) {
            // Load user texture - this can fail!
            // If nOffset = 0, we assume user has never specified a texture,
            // so we silently switch to the default texture.
            // If the load of user texture fails, we use default texture
            if( (pTexInfo->texFile.nOffset == 0) ||
                (! ss_LoadTextureFile( &pTexInfo->texFile, pTex )) ) {

                pTexInfo->bDefTex = TRUE;  // draw default texture resource

                // Enable the spin control
                EnableWindow( GetDlgItem(ghDlg, gTex[surface].dlgSpinTexID),
                              TRUE );
            }
        }
    }

    if( pTexInfo->bDefTex ) {
        // Draw default texture resource
        pTex = &gDefTex[pTexInfo->iDefTex];
        if( !pTex->data ) {
            if( !ss_LoadTextureResource( &gTexResSurf[pTexInfo->iDefTex], pTex ))
                pTex = NULL;
        }
    }

    return pTex;
}

/******************************Public*Routine******************************\
* DrawTextures
*
* Draw all the textures
*
\**************************************************************************/

static void
DrawTextures()
{
    for( int i = 0; i < NUM_SURFACES; i++ ) {
        DrawTexture( i );
    }
}

/******************************Public*Routine******************************\
* DrawTexture
*
* Draw appropriate texture in supplied window (e.g. a button)
* - Use TEX_INFO state to determine texture
* - Load texture if not in cache
*
\**************************************************************************/

static void
DrawTexture( int surface )
{
    TEXTURE *pTex;

    // Make sure valid texture loaded for this surface
    if( ! (pTex = ValidateTexture( surface )) )
        return;

    SS_TEX_BUTTON *pTexBtn = gTex[surface].pTexBtn;
    // pTexBtn never NULL, else ss_ConfigInit fails

    // If palette rotation for this texture, slam in the current rotation,
    // to be used by pTexBtn->Draw
    if( pTex->pal )
        pTex->iPalRot = gTex[surface].iPalRot;

    pTexBtn->Draw( pTex );
}

/******************************Public*Routine******************************\
* RegisterDialogClasses
*
\**************************************************************************/

BOOL WINAPI RegisterDialogClasses(HANDLE hinst)
{
    return TRUE;
}

/******************************Public*Routine******************************\
* HandlePreviewDraw
*
\**************************************************************************/

static BOOL
HandlePreviewDraw( UINT idCtl, LPDRAWITEMSTRUCT lpdis )
{
    int surface = DLG_PREVIEW_TO_SURFACE( idCtl );

    if( lpdis->itemAction == ODA_DRAWENTIRE ) {
        DrawTexture( surface );
        return TRUE;
    }
    return FALSE;
}

/******************************Public*Routine******************************\
* HandleTexButtonNewTexture
*
* - Handle when a user selects a new texture
*
* If the new texture fails to load (unlikely, since we validate a good chunk
* of it with the selection dialog), then the current default texture is
* used.
\**************************************************************************/

static void
HandleTexButtonNewTexture( int surface )
{
    TEXTURE *pTex = &gTex[surface].userTex;

    // Delete the old texture
    ss_DeleteTexture( pTex );

    // Load up the new texture
    if( ! ss_LoadTextureFile( &gTexInfo[surface].texFile, pTex ) )
        gTexInfo[surface].bDefTex = TRUE;
}

/******************************Public*Routine******************************\
* MazeDlgTimerProc
*
* Runs off of WM_TIMER message.  Used for any gl animation in the dialog
*
\**************************************************************************/

static void
MazeDlgTimerProc()
{
    extern TEX_RES gTexResSurf[]; // from glmaze.c

    if( ss_PalettedTextureEnabled() ) {
        TEX_INFO *pti = gTexInfo;
        int i;

        // rotate texture palettes for surfaces with a8 palettes

        for( i = 0; i < NUM_SURFACES; i++, pti++ ) {
            if( pti->bTex &&
                pti->bDefTex &&
                ( gTexResSurf[pti->iDefTex].type == TEX_A8 ) )
            {
                // Increment palette rotation for this surface
                gTex[i].iPalRot++;
                // Draw texture associated with the surface
                DrawTexture( i );
            }
        }
    }
}

/******************************Public*Routine******************************\
* ConfigInit
*
* Do Initialization for Config mode
*
* This is the config equivalent of ss_Init
*
* Setup SS_TEX_BUTTON wrappers for each of the 3 texture preview buttons.
\**************************************************************************/

BOOL
ss_ConfigInit( HWND hDlg )
{
    SS_DBGLEVEL1( SS_LEVEL_INFO, "ConfigInit for %d\n", hDlg );

    // Create GL texture buttons to draw the surface previews

    SS_TEX_BUTTON *pTexBtn;

    for( int i = 0; i < NUM_SURFACES; i++ ) {
        pTexBtn = new SS_TEX_BUTTON( hDlg,
                            GetDlgItem(hDlg, DLG_SURFACE_TO_PREVIEW(i)) );
        if( !pTexBtn ) {
            return FALSE;
        }
        gTex[i].pTexBtn = pTexBtn;
    }

    updateGLState();

    // Start a timer for animating texture palettes
    idTimer = 1;
    SetTimer(hDlg, idTimer, 16, 0);

    // Note: no textures are loaded here, they are 'demand-loaded'
    return TRUE;
}

/******************************Public*Routine******************************\
* ScreenSaverConfigureDialog
*
* Processes messages for the configuration dialog box.
*
\**************************************************************************/

BOOL ScreenSaverConfigureDialog(HWND hDlg, UINT message,
                                WPARAM wParam, LPARAM lParam)
{
    int wTmp, surface;
    static BOOL bInited = 0;

    switch (message) {
        case WM_INITDIALOG:
            getIniSettings();
            setupDialogControls(hDlg);
            // cache the window handle
            ghDlg = hDlg;
            return TRUE;

        case WM_TIMER:
            MazeDlgTimerProc();
            return 0;
            break;

        case WM_DRAWITEM:
            switch( (UINT) wParam ) {
                case DLG_PREVIEW_WALLS:
                case DLG_PREVIEW_FLOOR:
                case DLG_PREVIEW_CEILING:
                    if( HandlePreviewDraw( (UINT) wParam,
                                             (LPDRAWITEMSTRUCT) lParam ) )
                        return TRUE;
                default:
                    break;
            }
            break;

        case WM_VSCROLL:
            switch(LOWORD(wParam))
            {
                case SB_THUMBPOSITION:
                    // get new value
                    wTmp = HIWORD(wParam);
                    {
                        int id;
                        HWND hwndScroll = (HWND) lParam;

                        id = GetDlgCtrlID( hwndScroll );
                        surface = DLG_SPIN_TEX_TO_SURFACE( id );
                        gTexInfo[surface].iDefTex = wTmp;
                        DrawTexture( surface );
                    }
                    break;
                default:
                    break;
            }
            break;

        case WM_COMMAND:
            switch (LOWORD(wParam)) {

                case DLG_BUTTON_WALLS_TEX:
                case DLG_BUTTON_FLOOR_TEX:
                case DLG_BUTTON_CEILING_TEX:
                    surface = DLG_BUTTON_TEX_TO_SURFACE( LOWORD(wParam) );
                    if( DialogBoxParam(
                        hMainInstance,
                        (LPTSTR) MAKEINTRESOURCE( DLG_TEXTURE_CONFIGURE ),
                        hDlg, (DLGPROC) TextureConfigureDialog, surface ) ) {
                    // If anything changed here, we have to draw it, as it
                    // seems when choose texture dialog box terminates, we
                    // don't get any DRAW_ITEM messages.
                    // Draw preview area
                        DrawTexture( surface );
                    }
                    break;

                case DLG_CHECK_OVERLAY:
                    maze_options.top_view = !maze_options.top_view;
                    CheckDlgButton(hDlg, DLG_CHECK_OVERLAY,
                                   maze_options.top_view );
                    break;

                case DLG_CHECK_TURBOMODE:
                    gbTurboMode = !gbTurboMode;
                    CheckDlgButton(hDlg, DLG_CHECK_TURBOMODE, gbTurboMode );
                    break;

                case DLG_COMBO_IMAGEQUAL:
                    switch (HIWORD(wParam))
                    {
                        case CBN_EDITCHANGE:
                        case CBN_SELCHANGE:
                          {
                            int oldImageQual = giImageQual;
                            giImageQual =
                                SendDlgItemMessage(hDlg, DLG_COMBO_IMAGEQUAL,
                                                      CB_GETCURSEL, 0, 0);
                            if( giImageQual != oldImageQual ) {
                                // change has occurred - redraw any gl objects
                                updateGLState();
                                DrawTextures();
                            }
                          }
                          break;
                        default:
                            return FALSE;
                    }
                    break;

                case IDOK:
                    saveIniSettings(hDlg);
                    // fall thru...

                case IDCANCEL:
                    CleanUp( hDlg );
                    EndDialog(hDlg, FALSE);
                    break;

                default:
                    return 0;
                    break;
            }
            updateDialogControls(hDlg);
            return TRUE;
            break;

        default:
            return 0;
    }
    return 0;
}

/******************************Public*Routine******************************\
* updateTextureConfigControls
*
* Updates dialog controls according to current state
*
\**************************************************************************/

static void
updateTextureConfigControls(HWND hDlg, BOOL bDefTex )
{
    CheckDlgButton(hDlg, IDC_RADIO_TEX_DEFAULT, bDefTex );
    CheckDlgButton(hDlg, IDC_RADIO_TEX_CHOOSE, !bDefTex );

    EnableWindow(GetDlgItem(hDlg, DLG_BUTTON_TEX_CHOOSE ), !bDefTex );
}

/******************************Public*Routine******************************\
* TextureConfigureDialog
*
* Processes messages for the texture configure dialog box.
*
* - Call EndDialog with TRUE if texture changed, otherwise FALSE
*
\**************************************************************************/

static BOOL CALLBACK
TextureConfigureDialog(HWND hDlg, UINT message,
                            WPARAM wParam, LPARAM lParam)
{
    // static state ok here, cuz only one of these dialogs can be active
    static int surface;
    static BOOL bDefTex; // temporary state

    switch (message) {
        case WM_INITDIALOG:
            // Cache some initial values
            surface = (int) lParam;
            bDefTex = gTexInfo[surface].bDefTex;
            updateTextureConfigControls(hDlg, bDefTex );
            return TRUE;

        case WM_COMMAND:
            switch (LOWORD(wParam)) {

                case DLG_BUTTON_TEX_CHOOSE:
                    if( ss_SelectTextureFile( hDlg,
                                         &gTexInfo[surface].texFile ) ) {
                        // New user texture was selected
                        HandleTexButtonNewTexture( surface );

                        // For now, let's end the dialog right here if a
                        // new one chosen - otherwise we'd have to make the
                        // new texture discardable if user entered Cancel
                        EndDialog(hDlg, TRUE );
                    }
                    break;

                case IDC_RADIO_TEX_DEFAULT:
                    bDefTex = TRUE;
                    break;

                case IDC_RADIO_TEX_CHOOSE:
                    bDefTex = FALSE;
                    break;

                case IDOK:
                    // save state
                    gTexInfo[surface].bDefTex = bDefTex;
                    EndDialog(hDlg, TRUE);
                    break;

                case IDCANCEL:
                    EndDialog(hDlg, FALSE);
                    break;

                default:
                    return 0;
                    break;
            }
            updateTextureConfigControls(hDlg, bDefTex );
            return TRUE;
            break;

        default:
            return 0;
    }
    return 0;
}

/******************************Public*Routine******************************\
* CleanUp
*
\**************************************************************************/

static void
CleanUp( HWND hwnd )
{
    int i;

    if (idTimer) {
        KillTimer(hwnd, idTimer);
        idTimer = 0;
    }

    // delete any textures and tex buttons created

    for( i = 0; i < NUM_SURFACES; i ++ ) {
        if( gTex[i].userTex.data )
            ss_DeleteTexture( &gTex[i].userTex );
        if( gDefTex[i].data )
            ss_DeleteTexture( &gDefTex[i] );
        if( gTex[i].pTexBtn )
            delete gTex[i].pTexBtn;
    }

}


FIXED.H

#ifndef __FIXED_H__
#define __FIXED_H__

#include <math.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifndef PI
#define PI (3.1415926535897932384626433832795028841971693993751)
#endif

/* Default table size for precomputed sincos table */
#define FA_TABLE_SIZE 360

/*
   Flags for initialization
   FA_CARTESIAN_Y       - Y axis is positive up
 */
#define FA_DEFAULT      0
#define FA_CARTESIAN_Y  1

#if defined(FX_DOUBLE) || defined(FX_SINGLE)

#ifdef FX_DOUBLE
typedef double FxValue;

#define FX_MAX_VALUE (1e100)
#define FX_MIN_VALUE (1e-10)
#else
typedef float FxValue;

#define FX_MAX_VALUE (1e38f)
#define FX_MIN_VALUE (1e-7f)
#endif

#define FxVal(i) ((FxValue)(i))
#define FxInt(v) ((int)(v))
#define FxFltVal(f) ((FxValue)(f))
#define FxFlt(v) ((double)(v))
#define FxPromote(v) (v)
#define FxDemote(v) (v)

#define FxMul(a, b) ((a)*(b))
#define FxDemotedMul(a, b) FxMul(a, b)
#define FxDiv(a, b) ((a)/(b))
#define FxDemotedDiv(a, b) FxDiv(a, b)
#define FxMulToInt(a, b) FxInt((a)*(b))
#define FxDivToInt(a, b) ((int)((a)/(b)))
#define FxMulDiv(a, m, d) (((a)*(m))/(d))
#define FxSqrt(v) ((FxValue)sqrt((double)(v)))
#define FxDemotedSqrt(v) FxSqrt(v)

typedef FxValue FaAngle;

#define FaAng(a) (a)

#define FaSin(v) ((FxValue)-sin((double)(v)))
#define FaCos(v) ((FxValue)cos((double)(v)))

#define FaAdd(a, d) ((a)+(d))
FaAngle FaNorm(FaAngle a);
#define FaDeg(da) ((da)*(FxValue)(PI/180.0))
#define FaRad(ra) (ra)
#define FaAngVal(aa) (aa)
#define FaFltDegVal(a) ((a)*180.0/PI)
#define FaFltRadVal(a) (a)

#define FxInitialize(table_size, flags) ((flags) == FA_DEFAULT)
#define FxEnd()

#else

/* If integer sqrt isn't interesting, define FX_PRECISE_SQRT
   and the floating point sqrt will be used */

#ifndef FX_SHIFT
#define FX_SHIFT 10
#endif
#define FX_MULT (1L << FX_SHIFT)

typedef long FxValue;

#define FX_MAX_VALUE (0x7fffffff)
#define FX_MIN_VALUE (1)

#define FxVal(i) FxPromote((FxValue)(i))
#define FxInt(v) ((int)FxDemote(v))
#define FxFltVal(f) ((FxValue)((f)*(double)FX_MULT))
#define FxFlt(v) (((double)(v))/(double)FX_MULT)
#define FxPromote(v) ((v) << FX_SHIFT)
#define FxDemote(v) ((v) >> FX_SHIFT)

#if FX_SHIFT != 16
/* These can overflow if the shift and numbers are too large */
#define FxMul(a, b) FxDemote((a)*(b))
#define FxDiv(a, b) (FxPromote(a)/(b))
#define FxMulToInt(a, b) FxInt(FxDemote((a)*(b)))
#define FxMulDiv(a, m, d) (((a)*(m))/(d))
#else
/* For FX_SHIFT == 16 and certain platforms, assembly routines are
   provided which do 64-bit intermediate math, preserving accuracy
   There is still a danger of overflow if the results don't fit in
   32 bits, though */
FxValue FxMul(FxValue a, FxValue b);
FxValue FxDiv(FxValue a, FxValue b);
int FxMulToInt(FxValue a, FxValue b);
FxValue FxMulDiv(FxValue a, FxValue m, FxValue d);
#endif

#ifndef FX_PRECISE_SQRT
FxValue FxSqrt(FxValue v);
/* Computing the square root of a demoted value leaves it out of
   adjustment by sqrt(FX_MULT) so shift by FX_SHIFT/2 to
   restore fixed point
   FX_SHIFT should be even for this to work */
#define FxDemotedSqrt(v) (FxSqrt(FxDemote(v)) << (FX_SHIFT/2))
#else
#define FxSqrt(v) FxFltVal(sqrt(FxFlt(v)))
#define FxDemotedSqrt(v) FxSqrt(v)
#endif

#define FxDemotedMul(a, b) (FxDemote(a)*(b))
#define FxDemotedDiv(a, b) FxPromote((a)/(b))
#define FxDivToInt(a, b) ((int)((a)/(b)))

/* One unit of angle is the table quantum
   One unit of angle equals 360/_fa_table_size degrees */
typedef FxValue FaAngle;

extern int _fa_table_size;

extern FxValue *_fa_sines;
extern FxValue *_fa_cosines;

#define FaAng(a) FxDemote(a)
#define FaSin(a) _fa_sines[(int)FaAng(a)]
#define FaCos(a) _fa_cosines[(int)FaAng(a)]

FaAngle FaAdd(FaAngle a, FaAngle d);
FaAngle FaBisectingAngle(FaAngle f, FaAngle t);
#define FaNorm(a) FaAdd(FxVal(0), a)
#define FaDeg(da) \
    FaNorm(FxMulDiv(FxVal(da), _fa_table_size, 360))
#define FaRad(ra) FaNorm(FxFltVal((ra)*_fa_table_size/PI2))
#define FaAngVal(aa) FaNorm(FxVal(aa))
#define FaFltDegVal(ang) FxFltVal(FxMulDiv(ang, 360, _fa_table_size))
#define FaFltRadVal(ang) (FxFltVal(ang)*PI2/_fa_table_size)

BOOL FxInitialize(int table_size, ULONG flags);
void FxEnd(void);

#endif

typedef struct _FxPt2
{
    FxValue x, y;
} FxPt2;
typedef FxPt2 FxVec2;

typedef struct _FxBox2
{
    FxPt2 min, max;
} FxBox2;

typedef struct _FxPt3
{
    FxValue x, y, z;
} FxPt3;
typedef FxPt3 FxVec3;

typedef struct _FxBox3
{
    FxPt3 min, max;
} FxBox3;

typedef struct _FxPt4
{
    FxValue x, y, z, w;
} FxPt4;
typedef FxPt4 FxVec4;

void FxBBox2Empty(FxBox2 *bb);
void FxBBox2AddPt(FxBox2 *bb, FxPt2 *pt);

void FxBBox3Empty(FxBox3 *bb);
void FxBBox3AddPt(FxBox3 *bb, FxPt3 *pt);

#define FxV2Set(v, xv, yv) \
    ((v)->x = (xv), (v)->y = (yv))
#define FxV2Add(a, b, r) \
    ((r)->x = (a)->x+(b)->x, (r)->y = (a)->y+(b)->y)
#define FxV2Sub(a, b, r) \
    ((r)->x = (a)->x-(b)->x, (r)->y = (a)->y-(b)->y)
#define FxV2Dot(a, b) \
    (FxMul((a)->x, (b)->x)+FxMul((a)->y, (b)->y))
#define FxV2Neg(v, r) \
    ((r)->x = -(v)->x, (r)->y = -(v)->y)
#define FxV2NegV(v) FxV2Neg(v, v)
#define FxV2NormV(v) FxV2Norm(v, v)

FxValue FxV2Len(FxVec2 *v);
FxValue FxV2Norm(FxVec2 *v, FxVec2 *r);

#define FxvV2Set(v, xv, yv) FxV2Set(&(v), xv, yv)
#define FxvV2Add(a, b, r) FxV2Add(&(a), &(b), &(r))
#define FxvV2Sub(a, b, r) FxV2Sub(&(a), &(b), &(r))
#define FxvV2Dot(a, b) FxV2Dot(&(a), &(b))
#define FxvV2Neg(v, r) FxV2Neg(&(v), &(r))
#define FxvV2NegV(v) FxV2NegV(&(v))
#define FxvV2Len(v) FxV2Len(&(v))
#define FxvV2Norm(v, r) FxV2Norm(&(v), &(r))
#define FxvV2NormV(v) FxV2NormV(&(v))

#define FxV3Set(v, xv, yv, zv) \
    ((v)->x = (xv), (v)->y = (yv), (v)->z = (zv))
#define FxV3Add(a, b, r) \
    ((r)->x = (a)->x+(b)->x, (r)->y = (a)->y+(b)->y, (r)->z = (a)->z+(b)->z)
#define FxV3Sub(a, b, r) \
    ((r)->x = (a)->x-(b)->x, (r)->y = (a)->y-(b)->y, (r)->z = (a)->z-(b)->z)
#define FxV3Dot(a, b) \
    (FxMul((a)->x, (b)->x)+FxMul((a)->y, (b)->y)+FxMul((a)->z, (b)->z))
#define FxV3Neg(v, r) \
    ((r)->x = -(v)->x, (r)->y = -(v)->y, (r)->z = -(v)->z)
#define FxV3NegV(v) FxV3Neg(v, v)
#define FxV3Cross(a, b, r) \
    ((r)->x = (a)->y*(b)->z-(b)->y*(a)->z,\
     (r)->y = (a)->z*(b)->x-(b)->z*(a)->x,\
     (r)->z = (a)->x*(b)->y-(b)->x*(a)->y)
#define FxV3NormV(v) FxV3Norm(v, v)

FxValue FxV3Len(FxVec3 *v);
FxValue FxV3Norm(FxVec3 *v, FxVec3 *r);

#define FxvV3Set(v, xv, yv, zv) FxV3Set(&(v), xv, yv, zv)
#define FxvV3Add(a, b, r) FxV3Add(&(a), &(b), &(r))
#define FxvV3Sub(a, b, r) FxV3Sub(&(a), &(b), &(r))
#define FxvV3Dot(a, b) FxV3Dot(&(a), &(b))
#define FxvV3Neg(v, r) FxV3Neg(&(v), &(r))
#define FxvV3NegV(v) FxV3NegV(&(v))
#define FxvV3Cross(a, b, r) FxV3Cross(&(a), &(b), &(r))
#define FxvV3Len(v) FxV3Len(&(v))
#define FxvV3Norm(v, r) FxV3Norm(&(v), &(r))
#define FxvV3NormV(v) FxV3NormV(&(v))

typedef FxValue FxMatrix2[2][2];
typedef FxValue FxMatrix3[3][3];
typedef FxValue FxMatrix4[4][4];

typedef FxValue FxTMatrix2[2][3];
typedef FxValue FxTMatrix3[3][4];

void FxT2Ident(FxTMatrix2 m);
void FxT2Mul(FxTMatrix2 a, FxTMatrix2 b, FxTMatrix2 r);
void FxT2Vec2(FxTMatrix2 m, int n, FxVec2 *f, FxVec2 *t);

void FxT3Ident(FxTMatrix3 m);
void FxT3Mul(FxTMatrix3 a, FxTMatrix3 b, FxTMatrix3 r);
void FxT3Vec3(FxTMatrix3 m, int n, FxVec3 *f, FxVec3 *t);

#ifdef __cplusplus
}
#endif

#endif


GENMAZE.C

#include "pch.c"
#pragma hdrstop

/*
  Maze generation based on Kruskal's algorithm

  Consider all possible paths as sets of connected cells.  Initially
  all cells are unconnected and all walls are present.  Pick a wall
  at random and determine if the cells on either side of the wall are
  connected by any possible path.  If they are, try again.  If not,
  knock out the wall and union the two path sets together.
  */
#include <stdlib.h>
#include <memory.h>

#include "genmaze.h"

typedef struct _MazeCell
{
    /* Pointer to the head of the set of cells reachable from this one.
       Easily identifies an entire set for comparison */
    struct _MazeCell *set;
    /* Pointer to the next cell in this connected set */
    struct _MazeCell *next;
} MazeCell;

typedef struct _MazeWall
{
    BYTE wall;
    int x, y;
} MazeWall;

/*
  Union two connection sets together by setting all elements in one
  to the set of the other and then appending it to the end of the
  set list
  */
static void ConnectSets(MazeCell *set, MazeCell *add)
{
    /* Locate end of set */
    while (set->next != NULL)
    {
        set = set->next;
    }

    /* Change to point to head of set */
    add = add->set;

    /* Append new cells */
    set->next = add;

    /* Change new cells' set identity */
    while (add != NULL)
    {
        add->set = set->set;
        add = add->next;
    }
}

/*
  Determine whether the two cells are already connected.  With the
  existing data structures this is a trivial comparison of set
  identities
  */
#define SetsAreConnected(a, b) \
    ((a)->set == (b)->set)

/* Locate a cell in a known array */
#define CellAt(x, y) (cells+(x)+(y)*width)

/* Locate a cell in the output array */
#define MazeAt(x, y) (maze+(x)+(y)*(width+1))

/*
  Generate a maze by deleting walls at random
  Width and height are the counts of cells

  Result is stored in the walls array, formatted as (width+1)*(height+1)
  cells with MAZE_WALL_HORZ | MAZE_WALL_VERT set appropriately.
  X coordinates are one byte apart, so maze is treated like
  maze[height+1][width+1]
  */
BOOL GenerateMaze(int width, int height, BYTE *maze)
{
    MazeCell *cells;
    MazeWall *walls;
    int ncells;
    int nwalls;
    int i, x, y;
    MazeCell *ca, *cb;
    MazeWall *w, wt;

    ncells = width*height;
    cells = (MazeCell *)malloc(sizeof(MazeCell)*ncells);
    if (cells == NULL)
    {
        return FALSE;
    }

    nwalls = (width-1)*height+(height-1)*width;
    walls = (MazeWall *)malloc(sizeof(MazeWall)*nwalls);
    if (walls == NULL)
    {
        free(cells);
        return FALSE;
    }

    /* Initialize all cells to be unique sets */
    ca = cells;
    for (i = 0; i < ncells; i++)
    {
        ca->set = ca;
        ca->next = NULL;
        ca++;
    }

    /* Add all internal horizontal and vertical walls.
       All edge walls will be present in the final maze so
       they aren't considered here */
    w = walls;
    for (x = 1; x < width; x++)
    {
        for (y = 0; y < height; y++)
        {
            w->wall = MAZE_WALL_VERT;
            w->x = x;
            w->y = y;
            w++;
        }
    }
    for (y = 1; y < height; y++)
    {
        for (x = 0; x < width; x++)
        {
            w->wall = MAZE_WALL_HORZ;
            w->x = x;
            w->y = y;
            w++;
        }
    }

    /* Randomize the wall array */
    for (i = nwalls-1; i > 0; i--)
    {
        w = walls+(rand() % i);
        wt = *w;
        *w = walls[i];
        walls[i] = wt;
    }

    /* Now walk the random list of walls, knocking walls out to
       join cells together */
    w = walls;
    for (i = 0; i < nwalls; i++)
    {
        /* Determine the two cells separated by the current wall */
        ca = CellAt(w->x, w->y);
        if (w->wall == MAZE_WALL_HORZ)
        {
            cb = CellAt(w->x, w->y-1);
        }
        else
        {
            cb = CellAt(w->x-1, w->y);
        }

        /* If the two cells aren't connected, connect them by knocking
           out the wall */
        if (!SetsAreConnected(ca, cb))
        {
            ConnectSets(ca, cb);
            w->wall = 0;
        }

        w++;
    }

    /* Initialize output to empty */
    memset(maze, 0, sizeof(BYTE)*(width+1)*(height+1));

    /* Set all edge walls in the output */
    for (x = 0; x < width; x++)
    {
        *MazeAt(x, 0) |= MAZE_WALL_HORZ;
        *MazeAt(x, height) |= MAZE_WALL_HORZ;
    }
    for (y = 0; y < height; y++)
    {
        *MazeAt(0, y) |= MAZE_WALL_VERT;
        *MazeAt(width, y) |= MAZE_WALL_VERT;
    }

    /* Copy remaining walls into the output array */
    w = walls;
    for (i = 0; i < nwalls; i++)
    {
        *MazeAt(w->x, w->y) |= w->wall;
        w++;
    }

    free(cells);
    free(walls);

    return TRUE;
}


GENMAZE.H

#ifndef __GENMAZE_H__
#define __GENMAZE_H__

#define MAZE_WALL_VERT 1
#define MAZE_WALL_HORZ 2

BOOL GenerateMaze(int width, int height, BYTE *walls);

#endif


GLMAZE.C

#include "pch.c"
#pragma hdrstop

#include <time.h>
#include "mazedlg.h"
#include <assert.h>

MazeView vw;
MazeSolution sol;

MazeOptions maze_options =
{
    FALSE,          // depth_test
    RENDER_FLAT,    // render mode (WALLS)
    RENDER_FLAT,    // render mode (FLOOR)
    RENDER_FLAT,    // render mode (CEILING)
    FALSE,           // frame_count
    FALSE,          // top_view
    TRUE,           // eye_view
    FALSE,          // single_step
    FALSE,          // all_alpha
    FALSE,          // bDither
    1               // nrats
};

float gfAspect = 1.0f;  // aspect ratio

#define SST_NEW_MAZE    0
#define SST_MAZE_GROW   1
#define SST_SOLVING     2
#define SST_MAZE_SHRINK 3
#define SST_ROTATE      4

int solve_state = SST_NEW_MAZE;

// Table of textures used.  The first entries contain 1 texture for each main
// surface, followed by textures used for various objects
TEXTURE all_textures[NUM_TEXTURES];

// Environment associated with the texture (repetition, palette rotation,..)
// For now, have one for each of the textures in all_textures.  But, this
// could be a per-object thing.
TEX_ENV gTexEnv[NUM_TEXTURES];

// texture environment mode
int gTexEnvMode;

// Texture resources for the main surfaces
TEX_RES gTexResSurf[NUM_DEF_SURFACE_TEXTURES] = {
    {TEX_BMP, IDB_BRICK},       // default textures
    {TEX_BMP, IDB_WOOD},
    {TEX_BMP, IDB_CASTLE},
    {TEX_A8,  IDB_CURL4},       // mandelbrot textures
    {TEX_A8,  IDB_BHOLE4},
    {TEX_A8,  IDB_SNOWFLAK},
    {TEX_A8,  IDB_SWIRLX4}
};

// Texture resources for objects
TEX_RES gTexResObject[NUM_OBJECT_TEXTURES] = {
    {TEX_A8,  IDB_START},
    {TEX_A8,  IDB_END},
    {TEX_A8,  IDB_RAT},
    {TEX_A8,  IDB_AD},
    {TEX_BMP, IDB_COVER}
};
SSContext gssc;

void maze_Init( void *data );
void RotateTexturePalettes( TEX_ENV *pTexEnv, int count, int iRot );

enum {
    TIMER_START = 0,
    TIMER_STOP
};

void Draw(void)
{
    GLbitfield mask;

    mask = 0;
    if (maze_options.depth_test)
    {
        mask |= GL_DEPTH_BUFFER_BIT;
    }
    if (maze_options.render[FLOOR] == RENDER_NONE ||
        maze_options.render[CEILING] == RENDER_NONE ||
        maze_options.render[WALLS] == RENDER_NONE ||
        maze_height < 1.0f)
    {
        mask |= GL_COLOR_BUFFER_BIT;
    }
    if (mask != 0)
    {
        glClear(mask);
    }

    // Rotate the palettes of any paletted texures by 1
    RotateTexturePalettes( gTexEnv, NUM_TEXTURES, 1 );

    if (maze_options.eye_view)
    {
        DrawMaze(&vw);
    }

    if (maze_options.top_view)
    {
        DrawTopView(&vw);
    }

    glFlush();
}

MazeGoal maze_goals[MAX_GOALS];
int ngoals;

Object special_obj[MAX_SPECIALS];

#define MAX_LETTERS 3
Object letters_obj[MAX_LETTERS];

typedef struct _Rat
{
    Object obj;
    MazeView vw;
    MazeSolution sol;
} Rat;
Rat rats[MAX_RATS];

void NewMazeList(void)
{
    glNewList(maze_walls_list, GL_COMPILE);
    DrawMazeWalls();
    glEndList();
}

void UpdateRatPosition(Rat *rat)
{
    MoveObject(&rat->obj, rat->vw.pos.x, rat->vw.pos.y);
    // Invert the angle because view angles move opposite to
    // object angles
    rat->obj.ang = FaAdd(0, -rat->vw.ang);
}

// Simple routines to pick cells at random while guaranteeing
// that those cells are unoccupied

static int rnd_cells[MAZE_CELLS];
static int nrnd_cells;

void InitRandomCells(void)
{
    int i;

    nrnd_cells = MAZE_CELLS;
    for (i = 0; i < nrnd_cells; i++)
    {
        rnd_cells[i] = i;
    }
}

void PickRandomCell(IntPt2 *pos)
{
    int idx, t;

#if DBG
    if (nrnd_cells == 0)
    {
        MessageBox(GetDesktopWindow(), TEXT("Out of random cells"),
                   NULL, MB_OK);
        pos->x = 0;
        pos->y = 0;
        return;
    }
#endif

    idx = rand() % nrnd_cells;
    nrnd_cells--;
    t = rnd_cells[idx];
    rnd_cells[idx] = rnd_cells[nrnd_cells];
    pos->x = t % MAZE_GRID;
    pos->y = t / MAZE_GRID;
}

void RemoveRandomCell(IntPt2 *pos)
{
    int i, idx;

    for (i = 0; i < nrnd_cells; i++)
    {
        idx = rnd_cells[i];
        if ((idx % MAZE_GRID) == pos->x &&
            (idx / MAZE_GRID) == pos->y)
        {
            nrnd_cells--;
            rnd_cells[i] = rnd_cells[nrnd_cells];
            return;
        }
    }
}

void NewMaze(void)
{
    IntPt2 cell, start_cell;
    int i, nspecials, nads;
    static BOOL firstMaze = TRUE;

    // If not in full screen mode, move the maze window around after it's solved
    if( !gbTurboMode && !firstMaze )
        ss_RandomWindowPos();

    InitRandomCells();

    if (!InitMaze(&start_cell, maze_goals, &ngoals))
    {
        printf("InitMaze failed\n");
        exit(1);
    }

    RemoveRandomCell(&start_cell);
    cell.x = maze_goals[GOAL_END].clx;
    cell.y = maze_goals[GOAL_END].cly;
    RemoveRandomCell(&cell);

    nspecials = rand() % MAX_SPECIALS;
    for (i = 0; i < nspecials; i++)
    {
        PickRandomCell(&cell);
        maze_goals[ngoals].clx = cell.x;
        maze_goals[ngoals].cly = cell.y;
        maze_goals[ngoals].user = &special_obj[i];

        special_obj[i].w = FMAZE_CELL_SIZE/4;
        special_obj[i].h = FxFltVal(.25);
        special_obj[i].z = special_obj[i].h;
        special_obj[i].col = 15;
        special_obj[i].draw_style = DRAW_SPECIAL;
        special_obj[i].draw_arg = rand() % SPECIAL_ARG_COUNT;
        special_obj[i].ang = FaDeg(0);
        special_obj[i].user1 = (rand() % 6)+2;
        special_obj[i].user2 = rand() % 5;
        special_obj[i].user3 = 0;
        PlaceObject(&special_obj[i],
                    CellToMfx(maze_goals[ngoals].clx)+FMAZE_CELL_SIZE/2,
                    CellToMfx(maze_goals[ngoals].cly)+FMAZE_CELL_SIZE/2);
        ngoals++;
    }
    while (i < MAX_SPECIALS)
    {
        special_obj[i].cell = NULL;
        i++;
    }

    nads = (rand() % (MAX_LETTERS*2))-MAX_LETTERS+1;
    for (i = 0; i < nads; i++)
    {
        PickRandomCell(&cell);

        letters_obj[i].w = FMAZE_CELL_SIZE/3;
        letters_obj[i].h = FxFltVal(.33);
        letters_obj[i].z = FxFltVal(.5);
        letters_obj[i].col = 15;
        letters_obj[i].draw_style = DRAW_POLYGON;
        letters_obj[i].pTexEnv = &gTexEnv[ TEX_AD ];
        letters_obj[i].ang = FaDeg(0);
        PlaceObject(&letters_obj[i],
                    CellToMfx(cell.x)+FMAZE_CELL_SIZE/2,
                    CellToMfx(cell.y)+FMAZE_CELL_SIZE/2);
    }
    while (i < MAX_LETTERS)
    {
        letters_obj[i].cell = NULL;
        i++;
    }

    for (i = 0; i < maze_options.nrats; i++)
    {
        PickRandomCell(&cell);

        rats[i].obj.w = FMAZE_CELL_SIZE/4;
        rats[i].obj.h = FxFltVal(.125);
        rats[i].obj.z = rats[i].obj.h;
        rats[i].obj.col = 16;
        rats[i].obj.draw_style = DRAW_POLYGON;
        rats[i].obj.pTexEnv = &gTexEnv[ TEX_RAT ];
        SolveMazeStart(&rats[i].vw, &maze_cells[0][0], MAZE_GRID, MAZE_GRID,
                       &cell, SOL_DIR_LEFT,
                       NULL, 0,
                       (rand() % 1000) > 500 ? SOL_TURN_LEFT : SOL_TURN_RIGHT,
                       &rats[i].sol);
        // Need to force this to NULL when a new maze is generated so
        // that moving doesn't try to use old maze data
        rats[i].obj.cell = NULL;
        UpdateRatPosition(&rats[i]);
    }

    NewMazeList();

    SolveMazeStart(&vw, &maze_cells[0][0], MAZE_GRID, MAZE_GRID,
                   &start_cell, SOL_DIR_RIGHT,
                   maze_goals, ngoals,
                   (rand() % 1000) > 500 ? SOL_TURN_LEFT : SOL_TURN_RIGHT,
                   &sol);

    solve_state = SST_MAZE_GROW;
    firstMaze = FALSE;
}

/**************************************************************************\
* Step
*
* Main draw proc
*
\**************************************************************************/

static MazeGoal *found_goal = NULL;
static int rot_step;

void Step(void *data)
{
    int i;
    MazeGoal *goal;

    switch(solve_state)
    {
    case SST_NEW_MAZE:
        view_rot = 0;
        maze_height = 0.0f;
        NewMaze();
        break;

    case SST_SOLVING:
        for (i = 0; i < maze_options.nrats; i++)
        {
            SolveMazeStep(&rats[i].vw, &rats[i].sol);
            UpdateRatPosition(&rats[i]);
        }

        goal = SolveMazeStep(&vw, &sol);
        if (goal == &maze_goals[GOAL_END])
        {
            solve_state = SST_MAZE_SHRINK;
        }
        else if (goal != NULL)
        {
            solve_state = SST_ROTATE;
            found_goal = goal;
            rot_step = 0;
        }
        break;

    case SST_MAZE_GROW:
        maze_height += .025f;
        if (maze_height >= 1.0f)
        {
            solve_state = SST_SOLVING;
        }
        break;

    case SST_MAZE_SHRINK:
        maze_height -= .025f;
        if (maze_height <= 0.0f)
        {
            solve_state = SST_NEW_MAZE;
        }
        break;

    case SST_ROTATE:
        view_rot += 10.0;
        if (++rot_step == 18)
        {
            Object *sp_obj;

            sp_obj = (Object *)found_goal->user;
            RemoveObject(sp_obj);

            solve_state = SST_SOLVING;
            ngoals--;
            if (found_goal < maze_goals+ngoals)
            {
                memmove(found_goal, found_goal+1,
                        sizeof(MazeGoal)*(ngoals-(found_goal-maze_goals)));
            }
            SolveMazeSetGoals(&sol, maze_goals, ngoals);
            found_goal = NULL;

            if (view_rot >= 360.0)
            {
                view_rot = 0.0;
            }
            else
            {
                view_rot = 180.0;
            }
        }
        break;
    }

    Draw();

    for (i = 0; i < MAX_SPECIALS; i++)
    {
        // Increment rotations of any specials still present in the maze
        if (special_obj[i].cell != NULL)
        {
            special_obj[i].ang = FaAdd(special_obj[i].ang,
                                       FaDeg(special_obj[i].user1));
            special_obj[i].user3 += special_obj[i].user2;
        }
    }
}

void UpdateModes(void)
{
    if (maze_options.depth_test)
    {
        glEnable(GL_DEPTH_TEST);
    }
    else
    {
        glDisable(GL_DEPTH_TEST);
    }
}

/**************************************************************************\
* Reshape
*
*       - called on resize, expose
*       - always called on app startup
*
\**************************************************************************/

void
Reshape(int width, int height, void *data )
{
    gfAspect = height == 0 ? 1.0f: (float) width / (float) height;
}

/******************************Public*Routine******************************\
* VerifyTextureFiles
*
* Check that any user specified textures are valid
* - MUST be called at ss_Init, or any error msgs will not display properly
*
\**************************************************************************/

void VerifyTextureFiles(void)
{
    int i;

    ss_DisableTextureErrorMsgs();

    for (i = 0; i < NUM_SURFACES; i++) {
        if( gTexInfo[i].bTex && !gTexInfo[i].bDefTex ) {
            if( !ss_VerifyTextureFile( &gTexInfo[i].texFile ) )
                // use default texture
                gTexInfo[i].bDefTex = TRUE;
        }
    }
}

/******************************Public*Routine******************************\
* CalcRep
*
* Figure out repetion based on size.
*
* - Use 128 pixels as a unit repetition reference
* - Size is always a power of 2
*
\**************************************************************************/

static int
CalcRep( int size )
{
    double pow2;
    int pow2ref = 8;
    int rep;

    pow2 = log((double)size) / log((double)2.0);
    rep = 1 + pow2ref - (int)pow2;
    return rep;
}

// tradeoff
#define MAX_TEX_DIM 128
//#define MAX_TEX_DIM 256

/******************************Public*Routine******************************\
* CalcTexRep
*
* Figure out texture repetition based on texture size.
*
* - mf: ? double for walls/ceiling ?
*
\**************************************************************************/

static void
CalcTexRep( TEXTURE *pTex, IPOINT2D *pTexRep )
{
    if( pTex->width >= MAX_TEX_DIM )
        pTexRep->x = 1;
    else
        pTexRep->x = CalcRep( pTex->width );

    if( pTex->height >= MAX_TEX_DIM )
        pTexRep->y = 1;
    else
        pTexRep->y = CalcRep( pTex->height );
}

/******************************Public*Routine******************************\
* LoadTextures
*
* Load textures from .bmp files or from embedded resources, using
* global texture info structure.
*
* For now, this also sets render modes, since if texturing is off for a
* surface, we always use RENDER_FLAT
*
\**************************************************************************/

static void
LoadTextures(void)
{
    int i;
    TEXTURE *pTex = all_textures;
    TEX_ENV *pTexEnv = gTexEnv;
    TEX_RES *pTexRes;

    assert( (NUM_SURFACES + NUM_OBJECT_TEXTURES) == NUM_TEXTURES );

    // load surface textures (wall, floor, ceiling)

    for (i = 0; i < NUM_SURFACES; i++, pTex++, pTexEnv++) {
        maze_options.render[i] = RENDER_FLAT;
        if( gTexInfo[i].bTex ) {
            pTexEnv->pTex = pTex;
            pTexEnv->bTransp = FALSE;

            // Load user or default texture for the surface

            if( !gTexInfo[i].bDefTex &&
                ss_LoadTextureFile( &gTexInfo[i].texFile, pTex )) {
            } else
            {
                // Load default resource texture

                pTexRes = &gTexResSurf[ gTexInfo[i].iDefTex ];
                if( !ss_LoadTextureResource( pTexRes, pTex ) )
                    continue;

                if( ss_PalettedTextureEnabled() &&
                    (pTexRes->type == TEX_A8) )
                {
                    pTexEnv->bPalRot = TRUE;
                    pTexEnv->iPalRot = ss_iRand( 0xff );
                } else
                    pTexEnv->bPalRot = FALSE;
            }

            maze_options.render[i] = RENDER_TEXTURED;

            // Figure out texture repetition
            CalcTexRep( pTex, &pTexEnv->texRep );
            // We would have to set texture parameters per object here,
            // but we just use default ones already set by ss_LoadTexture*
        }
    }

    // load object textures

    pTexRes = gTexResObject;
    for( i = 0; i < NUM_OBJECT_TEXTURES; i++, pTex++, pTexEnv++, pTexRes++ ) {
        if( ss_LoadTextureResource( pTexRes, pTex ) )
            pTexEnv->pTex = pTex;
        else
            pTexEnv->pTex = NULL;

        pTexEnv->bTransp = FALSE;

        // For now we don't do palrot's on any of the object textures
        pTexEnv->bPalRot = FALSE;
        // No repetition
        pTexEnv->texRep.x = pTexEnv->texRep.y = 1;
    }

    // Set transparency for some of the textures
    ss_SetTextureTransparency( gTexEnv[TEX_START].pTex, 0.42f, FALSE );
    ss_SetTextureTransparency( gTexEnv[TEX_END].pTex, 0.4f, FALSE );
    ss_SetTextureTransparency( gTexEnv[TEX_AD].pTex, 0.4f, FALSE );

    // Enable transparency for some of the texture environments
    gTexEnv[TEX_START].bTransp = TRUE;
    gTexEnv[TEX_END].bTransp = TRUE;
    gTexEnv[TEX_AD].bTransp = TRUE;
}

void UseTextureEnv( TEX_ENV *pTexEnv )
{
    static HTEXTURE hCurTex = (HTEXTURE) -1;
    HTEXTURE hTex = pTexEnv->pTex;

    // We cache the current texture for 'no texture object' case

    if( !ss_TextureObjectsEnabled() &&
        (hCurTex == hTex) )
        return; // same texture, no need to send it down

    // Make this texture current

    ss_SetTexture( hTex );

    // Set texture palette if necessary

    if( pTexEnv->bPalRot &&
        !ss_TextureObjectsEnabled() &&
        ss_PalettedTextureEnabled() ) {

        // We need to send down the (rotated) palette
        ss_SetTexturePalette( hTex, pTexEnv->iPalRot );
    }

    hCurTex = hTex;
}

/******************************Public*Routine******************************\
* RotateTexturePalettes
*
* If paletted texturing is enabled, go through the supplied list of texture
* environments, and if any have a palette, increment its rotation by the
* supplied iRot value.
*
\**************************************************************************/

void
RotateTexturePalettes( TEX_ENV *pTexEnv, int count, int iRot )
{
    if( !ss_PalettedTextureEnabled() || !pTexEnv )
        return;

    for( ; count; count--, pTexEnv++ ) {

        if( !pTexEnv->pTex || !pTexEnv->pTex->pal )
            continue;

        if( pTexEnv->bPalRot ) {
            // increment palette rotation
            pTexEnv->iPalRot += iRot;
            if( pTexEnv->iPalRot >= pTexEnv->pTex->pal_size )
                pTexEnv->iPalRot = 0;
            // Only send down the new palette if texture objects are enabled,
            // since otherwise it will be sent down when texture is
            // 'made current'
            if( ss_TextureObjectsEnabled() ) {
                ss_SetTexturePalette( pTexEnv->pTex, pTexEnv->iPalRot );
            }
        }
    }
}

/******************************Public*Routine******************************\
* InitStretchInfo
*
\**************************************************************************/

static void
InitStretchInfo( STRETCH_INFO *pStretch )
{
    pStretch->baseWidth = 320;
    pStretch->baseHeight = 200;
    pStretch->bRatioMode = FALSE;
}

/******************************Public*Routine******************************\
* SetFloaterInfo
*
* Set the size and position of the floating child window
*
\**************************************************************************/

static void
SetFloaterInfo( ISIZE *pParentSize, CHILD_INFO *pChild )
{
    float hdtvAspect = 9.0f / 16.0f;
    ISIZE *pChildSize = &pChild->size;

    // Set width according to user-specified size
    // (giSize range is 0..100)
    // set min size as 1/3 parent width
    pChildSize->width =
            (int) ((0.333f + 2.0f*giSize/300.0f) * pParentSize->width);

    // Scale height for hdtv aspect ratio
    pChildSize->height = (int) (hdtvAspect * pChildSize->width + 0.5f);
    // Ensure height not too big
    SS_CLAMP_TO_RANGE2( pChildSize->height, 0, pParentSize->height );

    pChild->pos.x = (pParentSize->width - pChildSize->width) / 2;
    pChild->pos.y = (pParentSize->height - pChildSize->height) / 2;
}

/******************************Public*Routine******************************\
* ss_Init
*
* Initialize - called on first entry into ss.
* Called BEFORE gl is initialized!
* Just do basic stuff here, like set up callbacks, verify dialog stuff, etc.
*
* Fills global SSContext structure with required data, and returns ptr
* to it.
*
\**************************************************************************/

SSContext *
ss_Init( void )
{
    getIniSettings();

    VerifyTextureFiles();

    // Set callbacks

    ss_InitFunc( maze_Init );
    ss_UpdateFunc( Step );
    ss_ReshapeFunc( Reshape );

    gssc.depthType = (maze_options.depth_test) ? SS_DEPTH16 : SS_DEPTH_NONE;

    // Currently stretch and floater mutex
    gssc.bDoubleBuf = TRUE; // will get turned off if stretch used
    if( gbTurboMode ) {
        // We stretch out the drawing area to the full size of the main window
        gssc.bFloater = FALSE;
        gssc.bStretch = TRUE;
        InitStretchInfo( &gssc.stretchInfo );
    } else {
        // A static centered floating window is created in the main window
        FLOATER_INFO *pFloater = &gssc.floaterInfo;

        gssc.bFloater = TRUE;
        gssc.bStretch = FALSE;
        pFloater->bMotion = FALSE;
        pFloater->ChildSizeFunc = SetFloaterInfo;
    }

    return &gssc;
}

/******************************Public*Routine******************************\
* maze_Init
*
* Initializes OpenGL state
*
\**************************************************************************/
void
maze_Init( void *data )
{
    float fv4[4];

    if (!FxInitialize(FA_TABLE_SIZE, 0))
    {
        printf("FxInit failed\n");
        exit(1);
    }

    glShadeModel( GL_FLAT );
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    glClearDepth(1);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

    if( (giImageQual == IMAGEQUAL_DEFAULT) || gbTurboMode ) {
        maze_options.bDither = FALSE;
        glDisable( GL_DITHER );
    } else {
        maze_options.bDither = TRUE;
        glEnable( GL_DITHER );
    }

    // Load textures and set render modes
    LoadTextures();

    fv4[0] = MAZE_SIZE/2.0f;
    fv4[1] = MAZE_SIZE/2.0f;
    fv4[2] = 10.0f;
    fv4[3] = 1.0f;
    glLightfv(GL_LIGHT0, GL_POSITION, fv4);
    glEnable(GL_LIGHT0);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // Check which texture environment function to use for objects
    if( ss_fOnGL11() )
        gTexEnvMode = GL_REPLACE;
    else
        gTexEnvMode = GL_MODULATE;

    maze_walls_list = glGenLists(1);

    UpdateModes();
}


GLMAZE.H

#ifndef __GLMAZE_H__
#define __GLMAZE_H__

#include "sscommon.h"
#include "maze_std.h"

#ifdef __cplusplus
extern "C" {
#endif

enum {
    TEX_WALL  = 0,
    TEX_FLOOR,
    TEX_CEILING,
    TEX_START,
    TEX_END,
    TEX_RAT,
    TEX_AD,
    TEX_COVER,
    NUM_TEXTURES
};

void UseTextureEnv( TEX_ENV *pTexEnv );

#define MAX_RATS 10

extern MazeOptions maze_options;
extern TEX_RES gTexResSurf[];

#ifdef __cplusplus
}
#endif

#endif


MAZE.H

#ifndef __MAZE_H__
#define __MAZE_H__

#include "maze_std.h"

#define MAZE_GRID 12
#define MAZE_CELLS (MAZE_GRID*MAZE_GRID)
#define MAZE_ARRAY (MAZE_GRID+1)
#define MAZE_ARRAY_CELLS (MAZE_ARRAY*MAZE_ARRAY)
#define N_MAZE_PTS MAZE_ARRAY_CELLS
#define N_MAZE_WALLS (2*MAZE_ARRAY_CELLS)

#define MAZE_WALL_LEFT          0x0004
#define MAZE_WALL_RIGHT         0x0008
#define MAZE_WALL_UP            0x0010
#define MAZE_WALL_DOWN          0x0020
#define MAZE_CONTENTS           0x0040

// Partial flags must be shifted versions of the standard flags
#define MAZE_WALL_LEFT_PARTIAL  0x0080
#define MAZE_WALL_RIGHT_PARTIAL 0x0100
#define MAZE_WALL_UP_PARTIAL    0x0200
#define MAZE_WALL_DOWN_PARTIAL  0x0400
#define MAZE_PARTIAL_SHIFT      5

#define WIDX_LEFT       0
#define WIDX_RIGHT      1
#define WIDX_UP         2
#define WIDX_DOWN       3

#define MAZE_CELL_SIZE 1
#define MAZE_SIZE (MAZE_GRID*MAZE_CELL_SIZE)
#define FMAZE_CELL_SIZE FxVal(MAZE_CELL_SIZE)
#define FMAZE_SIZE FxVal(MAZE_SIZE)

#define MfxToCell(mfx) FxInt(mfx)
#define CellToMfx(cl) FxVal(cl)

struct _Cell;

#define DRAW_POLYGON    0
#define DRAW_SPECIAL    1

#define SPECIAL_ARG_ICOSAHEDRON         0
#define SPECIAL_ARG_OCTAHEDRON          1
#define SPECIAL_ARG_DODECAHEDRON        2
#define SPECIAL_ARG_TETRAHEDRON         3
#define SPECIAL_ARG_COUNT               4

typedef struct _Object
{
    FxPt2 p;
    FxValue z;
    FaAngle ang;
    FxValue w, h;
    int col;
    int draw_style;
    int draw_arg;
    TEX_ENV *pTexEnv;  // ptr to texture environment
    int user1, user2, user3;
    struct _Object *next;
    struct _Cell *cell;
} Object;

typedef unsigned short WallFlags;

typedef struct _Cell
{
    WallFlags can_see;
    WallFlags unseen;
    Object *contents;
    struct _Wall *walls[4];
} Cell;

extern Cell maze_cells[MAZE_GRID][MAZE_GRID];

extern float maze_height;
extern double view_rot;
extern int maze_walls_list;

extern int gTexEnvMode;

#define MAX_GOALS       10
#define MAX_SPECIALS    (MAX_GOALS-1)

#define GOAL_END        0
#define GOAL_SPECIALS   1

BOOL InitMaze(IntPt2 *start_cell, struct _MazeGoal *goals, int *ngoals);

void PlaceObject(Object *obj, FxValue x, FxValue y);
void RemoveObject(Object *obj);
void MoveObject(Object *obj, FxValue x, FxValue y);

void DrawMaze(MazeView *vw);
void DrawMazeWalls(void);
void DrawTopView(MazeView *vw);

#endif


MAZE_STD.H

#ifndef __MAZE_STD_H__
#define __MAZE_STD_H__

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "fixed.h"
#include "genmaze.h"
#include "sscommon.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct _IntPt2
{
    int x, y;
} IntPt2;

#define DIR_RIGHT 0
#define DIR_UP 1
#define DIR_LEFT 2
#define DIR_DOWN 3
#define DIR_COUNT 4

typedef struct
{
    TEXTURE  *pTex;   // ptr to texture
    IPOINT2D texRep;  // texture repetition in s and t directions
    BOOL     bPalRot; // whether should rotate texture palette
    int      iPalRot; // current palette rotation start point
    BOOL     bTransp; // transparency on/off for RGBA textures
} TEX_ENV;

typedef struct _MazeView
{
    FxPt2 pos;
    FaAngle ang;
} MazeView;

#define RENDER_NONE     0
#define RENDER_FLAT     1
#define RENDER_SMOOTH   2
#define RENDER_TEXTURED 3
#define RENDER_COUNT    4

// surfaces
enum {
    WALLS = 0,
    FLOOR,
    CEILING,
    NUM_SURFACES
};

// default surface textures
enum {
    BRICK_TEXTURE = 0,
    WOOD_TEXTURE,
    CASTLE_TEXTURE,
    CURL4_TEXTURE,
    BHOLE4_TEXTURE,
    SNOWFLAK_TEXTURE,
    SWIRLX4_TEXTURE,
    NUM_DEF_SURFACE_TEXTURES
};

// textured objects
enum {
    START = 0,
    END,
    RAT,
    AD,
    COVER,
    NUM_OBJECT_TEXTURES
};

enum {
    IMAGEQUAL_DEFAULT = 0,
    IMAGEQUAL_HIGH,
    IMAGEQUAL_COUNT
};

typedef struct _MazeOptions
{
    BOOL depth_test;
    int render[NUM_SURFACES];
    BOOL frame_count;
    BOOL top_view;
    BOOL eye_view;
    BOOL single_step;
    BOOL all_alpha;
    BOOL bDither;
    int nrats;
} MazeOptions;

extern MazeOptions maze_options;

#ifdef __cplusplus
}
#endif

#endif // __MAZE_STD_H__


MAZEDLG.H

/******************************Module*Header*******************************\
* Module Name: mazedlg.h
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __dialog_h__
#define __dialog_h__

#include "dlgs.h"
#include "sscommon.h"
#include "maze_std.h"

#ifdef __cplusplus
extern "C" {
#endif

#define IDS_SCREENSAVERTITLE    1020
#define IDS_SAVERNAME           1002

// structure to pull in texture info from registry
typedef struct {
    BOOL    bTex;       // texture enable/disable
    BOOL    bDefTex;    // default texture enable/disable
    int     iDefTex;    // default texture index
    TEXFILE texFile;    // user texture file
} TEX_INFO;

// registry strings

#define IDS_TEXTURE_ENABLE              1100
#define IDS_DEFAULT_TEXTURE_ENABLE      1101

#define IDS_WALL_TEXTURE_FILE           1110
#define IDS_FLOOR_TEXTURE_FILE          1111
#define IDS_CEILING_TEXTURE_FILE        1112

#define IDS_WALL_TEXTURE_OFFSET         1120
#define IDS_FLOOR_TEXTURE_OFFSET        1121
#define IDS_CEILING_TEXTURE_OFFSET      1122

#define IDS_DEF_WALL_TEXTURE            1130
#define IDS_DEF_FLOOR_TEXTURE           1131
#define IDS_DEF_CEILING_TEXTURE         1132

#define IDS_OVERLAY                     1200

#define IDS_TURBOMODE                   1250

#define IDS_IMAGEQUAL                   1300

#define IDS_NRATS                       1400

// Dialog box defines

// Imagequal combobox strings
#define IDS_IMAGEQUAL_DEFAULT     1301
#define IDS_IMAGEQUAL_HIGH        1302

#define MIN_SLIDER              0
#define MAX_SLIDER              100

// Size slider
#define DLG_SLIDER_SIZE         2000
#define IDC_STATIC_SIZE         2001
#define IDC_STATIC_MIN          2002
#define IDC_STATIC_MAX          2003

// Maze overly check box
#define DLG_CHECK_OVERLAY       2008

// Turbo mode check box
#define DLG_CHECK_TURBOMODE     2009

// Buttons to choose textures
#define DLG_BUTTON_WALLS_TEX    3100
#define DLG_BUTTON_FLOOR_TEX    3101
#define DLG_BUTTON_CEILING_TEX  3102
#define DLG_BUTTON_TEX_TO_SURFACE(n)    ( (n) - DLG_BUTTON_WALLS_TEX )

// Previews for surfaces (now just texture, but could show like colors)
#define DLG_PREVIEW_WALLS        3200
#define DLG_PREVIEW_FLOOR        3201
#define DLG_PREVIEW_CEILING      3202
#define DLG_PREVIEW_TO_SURFACE(n)    ( (n) - DLG_PREVIEW_WALLS )
#define DLG_SURFACE_TO_PREVIEW(n)    ( (n) + DLG_PREVIEW_WALLS )

// Spin controls for texture previews
//mf: add TEX suffix
#define DLG_SPIN_WALLS           3300
#define DLG_SPIN_FLOOR           3301
#define DLG_SPIN_CEILING         3302
#define DLG_SPIN_TEX_TO_SURFACE(n)    ( (n) - DLG_SPIN_WALLS )

// Resource bitmap id's
#define IDB_BRICK                100
#define IDB_WOOD                 101
#define IDB_CASTLE               102
#define IDB_START                103
#define IDB_END                  104
#define IDB_RAT                  105
#define IDB_AD                   106
#define IDB_COVER                107

#define IDB_CURL4                120
#define IDB_BHOLE4               121
#define IDB_SNOWFLAK             125
#define IDB_SWIRLX4               127

// Image quality box
#define DLG_COMBO_IMAGEQUAL      3500
#define IDC_STATIC_IMAGEQUAL     3501

// Choose texture dialog box

#define DLG_TEXTURE_CONFIGURE   4000

#define IDC_RADIO_TEX_DEFAULT   4010
#define IDC_RADIO_TEX_CHOOSE    4011
#define DLG_BUTTON_TEX_CHOOSE   4012

extern void getIniSettings();
extern TEX_INFO gTexInfo[];
extern int      giSize; // window size
extern BOOL     gbTurboMode;  // turbo mode enable/disable
extern int      giImageQual;  // Image quality (dithering)

#ifdef __cplusplus
}
#endif

#endif // __dialog_h__


PCH.C

#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <gl\glaux.h>

#include "sscommon.h"
#include "maze_std.h"
#include "glmaze.h"
#include "maze.h"
#include "slvmaze.h"


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by maze.rc
//
#include "windows.h"
#include "scrnsave.h"
// Note: vc4 embeds sscommon.rc into maze.rc
#include "sscommon.rc"
#include "mazedlg.h"
#include "commctrl.h"

#define IDD_DIALOG1                     101
#define IDB_BITMAP1                     102
#define IDC_CHECK1                      1000
#define IDC_CHECK2                      1001
#define IDC_CHECK3                      1002
#define IDC_SCROLLBAR1                  1003
#define IDC_CHECK4                      1005
#define IDC_RADIO1                      1011
#define IDC_RADIO2                      1012
#define IDC_BUTTON1                     1013
#define IDC_STATIC                      -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        104
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1014
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SLVMAZE.C

#include "pch.c"
#pragma hdrstop

static int left_turn[SOL_DIRS] =
{
    SOL_DIR_DOWN, SOL_DIR_LEFT, SOL_DIR_UP, SOL_DIR_RIGHT
};

static int right_turn[SOL_DIRS] =
{
    SOL_DIR_UP, SOL_DIR_RIGHT, SOL_DIR_DOWN, SOL_DIR_LEFT
};

static BYTE dir_wall[SOL_DIRS] =
{
    MAZE_WALL_LEFT, MAZE_WALL_UP, MAZE_WALL_RIGHT, MAZE_WALL_DOWN
};

static int dir_cloff[SOL_DIRS][2] =
{
    -1,0, 0,-1, 1,0, 0,1
};

static FaAngle dir_ang[SOL_DIRS];
static FxPt2 dir_off[SOL_DIRS];

/* We want to traverse one quarter of a circle in the given number of
   steps.  The distance is the arc length which is r*pi/2.  Divide that
   by the number of steps to get the distance each step should travel */
#define ARC_STEP 5
#define ARC_STEPS (90/ARC_STEP)

#define REVERSE_STEP (2*ARC_STEP)
#define REVERSE_STEPS (180/REVERSE_STEP)

static void SetView(MazeSolution *sol, MazeView *vw)
{
    vw->ang = dir_ang[sol->dir];
    vw->pos.x = CellToMfx(sol->clx)+dir_off[sol->dir].x;
    vw->pos.y = CellToMfx(sol->cly)+dir_off[sol->dir].y;
}

void SolveMazeSetGoals(MazeSolution *sol, MazeGoal *goals, int ngoals)
{
    sol->goals = goals;
    sol->ngoals = ngoals;
}

void SolveMazeStart(MazeView *vw,
                    Cell *maze, int w, int h,
                    IntPt2 *start, int start_dir,
                    MazeGoal *goals, int ngoals,
                    int turn_to,
                    MazeSolution *sol)
{
    dir_ang[SOL_DIR_LEFT] = FaDeg(180);
    dir_ang[SOL_DIR_UP] = FaDeg(90);
    dir_ang[SOL_DIR_RIGHT] = FaDeg(0);
    dir_ang[SOL_DIR_DOWN] = FaDeg(270);

    dir_off[SOL_DIR_LEFT].x = CellToMfx(1)-FX_MIN_VALUE;
    dir_off[SOL_DIR_LEFT].y = CellToMfx(1)/2;
    dir_off[SOL_DIR_UP].x = CellToMfx(1)/2;
    dir_off[SOL_DIR_UP].y = CellToMfx(1)-FX_MIN_VALUE;
    dir_off[SOL_DIR_RIGHT].x = FxVal(0);
    dir_off[SOL_DIR_RIGHT].y = CellToMfx(1)/2;
    dir_off[SOL_DIR_DOWN].x = CellToMfx(1)/2;
    dir_off[SOL_DIR_DOWN].y = FxVal(0);

    sol->clx = start->x;
    sol->cly = start->y;
    sol->dir = start_dir;
    sol->maze = maze;
    sol->w = w;
    sol->h = h;
    sol->ani_state = ANI_STATE_NONE;

    switch(turn_to)
    {
    case SOL_TURN_RIGHT:
        sol->turn_to = left_turn;
        sol->turn_away = right_turn;
        sol->dir_sign = 1;
        break;

    case SOL_TURN_LEFT:
        sol->turn_to = right_turn;
        sol->turn_away = left_turn;
        sol->dir_sign = -1;
        break;
    }

    SolveMazeSetGoals(sol, goals, ngoals);

    SetView(sol, vw);
}

#define MazeAt(x, y) (sol->maze+(x)+(y)*(sol->w))

MazeGoal *SolveMazeStep(MazeView *vw, MazeSolution *sol)
{
    Cell *cell;
    int i, dir, turn_to;

    if (sol->ani_state != ANI_STATE_NONE)
    {
        if (--sol->ani_count == 0)
        {
            sol->ani_state = ANI_STATE_NONE;
            SetView(sol, vw);
        }
    }

    switch(sol->ani_state)
    {
    case ANI_STATE_TURN_TO:
        vw->pos.x += FxMulDiv(FaCos(vw->ang),
                              FxFltVal(PI*MAZE_CELL_SIZE/2),
                              FxVal(ARC_STEPS*2));
        vw->pos.y += FxMulDiv(FaSin(vw->ang),
                              FxFltVal(PI*MAZE_CELL_SIZE/2),
                              FxVal(ARC_STEPS*2));
        vw->ang = FaAdd(vw->ang, sol->dir_sign*FaDeg(ARC_STEP));
        return NULL;

    case ANI_STATE_TURN_AWAY:
        vw->pos.x += FxMulDiv(FaCos(vw->ang),
                              FxFltVal(PI*MAZE_CELL_SIZE/2),
                              FxVal(ARC_STEPS*2));
        vw->pos.y += FxMulDiv(FaSin(vw->ang),
                              FxFltVal(PI*MAZE_CELL_SIZE/2),
                              FxVal(ARC_STEPS*2));
        vw->ang = FaAdd(vw->ang, sol->dir_sign * -FaDeg(ARC_STEP));
        return NULL;

    case ANI_STATE_FORWARD:
        vw->pos.x += FxMulDiv(FaCos(vw->ang), MAZE_CELL_SIZE, ARC_STEPS);
        vw->pos.y += FxMulDiv(FaSin(vw->ang), MAZE_CELL_SIZE, ARC_STEPS);
        return NULL;

    case ANI_STATE_REVERSE:
        vw->ang = FaAdd(vw->ang, sol->dir_sign*FaDeg(REVERSE_STEP));
        return NULL;
    }

    for (i = 0; i < sol->ngoals; i++)
    {
        if (sol->clx == sol->goals[i].clx &&
            sol->cly == sol->goals[i].cly)
        {
            return &sol->goals[i];
        }
    }

    cell = MazeAt(sol->clx, sol->cly);

    dir = sol->dir;
    for (i = 0; i < SOL_DIRS-1; i++)
    {
        turn_to = sol->turn_to[dir];
        if ((dir_wall[turn_to] & cell->can_see) == 0)
        {
            /* No wall present when turned, so turn that way */
            sol->clx += dir_cloff[turn_to][0];
            sol->cly += dir_cloff[turn_to][1];
            sol->dir = turn_to;

            sol->ani_count = ARC_STEPS;
            switch(i)
            {
            case 0:
                sol->ani_state = ANI_STATE_TURN_TO;
                break;

            case 1:
                sol->ani_state = ANI_STATE_FORWARD;
                break;

            case 2:
                sol->ani_state = ANI_STATE_TURN_AWAY;
                break;
            }
            break;
        }
        else
        {
            /* Wall present, turn away and check again */
            dir = sol->turn_away[dir];
        }
    }

    if (i == SOL_DIRS-1)
    {
        /* Dead end.  Turn around */
        dir = sol->turn_to[sol->turn_to[sol->dir]];
        sol->clx += dir_cloff[dir][0];
        sol->cly += dir_cloff[dir][1];
        sol->dir = dir;

        sol->ani_state = ANI_STATE_REVERSE;
        sol->ani_count = REVERSE_STEPS;
    }

    return NULL;
}


SLVMAZE.H

#ifndef __SLVMAZE_H__
#define __SLVMAZE_H__

#define ANI_STATE_NONE          0
#define ANI_STATE_TURN_TO       1
#define ANI_STATE_TURN_AWAY     2
#define ANI_STATE_FORWARD       3
#define ANI_STATE_REVERSE       4

#define SOL_DIR_LEFT    0
#define SOL_DIR_UP      1
#define SOL_DIR_RIGHT   2
#define SOL_DIR_DOWN    3
#define SOL_DIRS        4

#define SOL_TURN_LEFT   0
#define SOL_TURN_RIGHT  1

typedef struct _MazeGoal
{
    int clx, cly;
    void *user;
} MazeGoal;

typedef struct _MazeSolution
{
    int clx, cly;
    int dir;
    Cell *maze;
    int w, h;
    MazeGoal *goals;
    int ngoals;
    int ani_state;
    int ani_count;
    int *turn_to;
    int *turn_away;
    int dir_sign;
} MazeSolution;

void SolveMazeStart(MazeView *vw,
                    Cell *maze, int w, int h,
                    IntPt2 *start, int start_dir,
                    MazeGoal *goals, int ngoals,
                    int turn_to,
                    MazeSolution *sol);
void SolveMazeSetGoals(MazeSolution *sol, MazeGoal *goals, int ngoals);
MazeGoal *SolveMazeStep(MazeView *vw, MazeSolution *sol);

#endif


SSMAZE.C

#include "pch.c"
#pragma hdrstop
#include "maze_std.h"

#define VIEW_ANG 90

float maze_height;
double view_rot;
int maze_walls_list;

extern TEX_ENV gTexEnv[];

typedef struct _FxRay2
{
    FxPt2 p;
    FxVec2 d;
} FxRay2;

BYTE maze_desc[MAZE_ARRAY][MAZE_ARRAY];

Cell maze_cells[MAZE_GRID][MAZE_GRID];
#define CellAt(x, y) (&maze_cells[y][x])

typedef struct _Wall
{
    FxPt2 f, t;
    int col;
    TEX_ENV *pTexEnv;  // points to texture environment
} Wall;

FxPt2 fmaze_pts[N_MAZE_PTS];
Wall maze[N_MAZE_WALLS];
int nwalls;

typedef struct _WallHit
{
    Cell *cell;
    int cx, cy;
    WallFlags flag;
} WallHit;

void AddObject(Object *obj, Cell *cell)
{
    obj->next = cell->contents;
    cell->contents = obj;
    obj->cell = cell;
}

void PlaceObject(Object *obj, FxValue x, FxValue y)
{
    Cell *cell;
    int cx, cy;

    cx = MfxToCell(x);
    cy = MfxToCell(y);
    cell = CellAt(cx, cy);

    obj->p.x = x;
    obj->p.y = y;

    AddObject(obj, cell);
}

void RemoveObject(Object *obj)
{
    Object *o, *op;

    if (obj->cell != NULL)
    {
        op = NULL;
        for (o = obj->cell->contents; o != obj; o = o->next)
        {
            op = o;
        }

        if (op == NULL)
        {
            obj->cell->contents = obj->next;
        }
        else
        {
            op->next = obj->next;
        }

        obj->cell = NULL;
    }
}

void MoveObject(Object *obj, FxValue x, FxValue y)
{
    int cx, cy;
    Cell *cell;

    obj->p.x = x;
    obj->p.y = y;

    cx = MfxToCell(x);
    cy = MfxToCell(y);
    cell = CellAt(cx, cy);

    if (cell == obj->cell)
    {
        return;
    }

    RemoveObject(obj);
    AddObject(obj, cell);
}

Object start_obj, end_obj;

BOOL InitMaze(IntPt2 *start_cell, MazeGoal *goals, int *ngoals)
{
    int i, j, n;
    FxPt2 p;

    if (!GenerateMaze(MAZE_GRID, MAZE_GRID, &maze_desc[0][0]))
    {
        return FALSE;
    }

    p.y = FxVal(0);
    n = 0;
    for (i = 0; i < MAZE_ARRAY; i++)
    {
        p.x = FxVal(0);
        for (j = 0; j < MAZE_ARRAY; j++)
        {
            fmaze_pts[n].x = p.x;
            fmaze_pts[n++].y = p.y;
            p.x += FMAZE_CELL_SIZE;
        }
        p.y += FMAZE_CELL_SIZE;
    }

    nwalls = 0;
    for (i = 0; i < MAZE_ARRAY; i++)
    {
        for (j = 0; j < MAZE_ARRAY; j++)
        {
            if (i < MAZE_ARRAY-1 && j < MAZE_ARRAY-1)
            {
                maze_cells[i][j].can_see = 0;
                maze_cells[i][j].contents = NULL;
                memset(maze_cells[i][j].walls, 0, 4*sizeof(Wall *));
            }

            if (maze_desc[i][j] & MAZE_WALL_HORZ)
            {
                if (j == MAZE_ARRAY-1)
                {
                    printf("MAZE_WALL_HORZ at right edge\n");
                    return FALSE;
                }

                maze[nwalls].f = fmaze_pts[i*MAZE_ARRAY+j];
                maze[nwalls].t = fmaze_pts[i*MAZE_ARRAY+j+1];
                maze[nwalls].col = (i+j+1) & 1;
                maze[nwalls].pTexEnv = &gTexEnv[TEX_WALL];

                if (i > 0)
                {
                    maze_cells[i-1][j].can_see |= MAZE_WALL_DOWN;
                    maze_cells[i-1][j].walls[WIDX_DOWN] = &maze[nwalls];
                }
                if (i < MAZE_ARRAY-1)
                {
                    maze_cells[i][j].can_see |= MAZE_WALL_UP;
                    maze_cells[i][j].walls[WIDX_UP] = &maze[nwalls];
                }

                nwalls++;
            }

            if (maze_desc[i][j] & MAZE_WALL_VERT)
            {
                if (i == MAZE_ARRAY-1)
                {
                    printf("MAZE_WALL_VERT at bottom edge\n");
                    return FALSE;
                }

                maze[nwalls].f = fmaze_pts[i*MAZE_ARRAY+j];
                maze[nwalls].t = fmaze_pts[(i+1)*MAZE_ARRAY+j];
                maze[nwalls].col = (i+j) & 1;
                maze[nwalls].pTexEnv = &gTexEnv[TEX_WALL];

                if (j > 0)
                {
                    maze_cells[i][j-1].can_see |= MAZE_WALL_RIGHT;
                    maze_cells[i][j-1].walls[WIDX_RIGHT] = &maze[nwalls];
                }
                if (j < MAZE_ARRAY-1)
                {
                    maze_cells[i][j].can_see |= MAZE_WALL_LEFT;
                    maze_cells[i][j].walls[WIDX_LEFT] = &maze[nwalls];
                }

                nwalls++;
            }
        }
    }

    // Always place the start on the left and
    // the end on the right.  This guarantees that there'll be
    // some traversing of the maze for the solution
    // Since the maze generator guarantees that the entire maze is
    // fully connected, the solution can always be found

    start_cell->x = 0;
    start_cell->y = rand() % MAZE_GRID;

    *ngoals = 1;
    goals[0].clx = MAZE_GRID-1;
    goals[0].cly = rand() % MAZE_GRID;

    start_obj.w = FMAZE_CELL_SIZE/6;
    start_obj.h = FxFltVal(.166);
    start_obj.z = FxFltVal(.5);
    start_obj.col = 12;
    start_obj.draw_style = DRAW_POLYGON;
    start_obj.pTexEnv = &gTexEnv[ TEX_START ];
    start_obj.ang = FaDeg(0);
    PlaceObject(&start_obj,
                CellToMfx(start_cell->x)+FMAZE_CELL_SIZE/2,
                CellToMfx(start_cell->y)+FMAZE_CELL_SIZE/2);

    end_obj.w = FMAZE_CELL_SIZE/6;
    end_obj.h = FxFltVal(.166);
    end_obj.z = FxFltVal(.5);
    end_obj.col = 10;
    end_obj.draw_style = DRAW_POLYGON;
    end_obj.pTexEnv = &gTexEnv[ TEX_END ];
    end_obj.ang = FaDeg(0);
    PlaceObject(&end_obj,
                CellToMfx(goals[0].clx)+FMAZE_CELL_SIZE/2,
                CellToMfx(goals[0].cly)+FMAZE_CELL_SIZE/2);

    // Reset some of the walls' textures to the OpenGL cover
    // for some variety
    i = (rand() % 5)+1;
    while (i-- > 0)
    {
        j = rand() % nwalls;
        maze[j].pTexEnv = &gTexEnv[TEX_COVER];
    }

    return TRUE;
}

#define PO_WALL 0
#define PO_PARTIAL 1
#define PO_COUNT 2

typedef struct _PaintWall
{
    Wall *wall;
} PaintWall;

typedef struct _PaintPartial
{
    Object *obj;
} PaintPartial;

typedef struct _PaintObject
{
    int type;
    union
    {
        PaintWall wall;
        PaintPartial partial;
    } u;
    FxValue depth;
    struct _PaintObject *closer;
} PaintObject;

#define N_PAINT_OBJECTS (4*MAZE_CELLS)
PaintObject paint[N_PAINT_OBJECTS];
int npaint;

void WallCoords(int x, int y, WallFlags flag, FxPt2 *f, FxPt2 *t)
{
    t->x = f->x = CellToMfx(x);
    t->y = f->y = CellToMfx(y);
    if (flag & MAZE_WALL_LEFT)
    {
        t->y += FMAZE_CELL_SIZE;
    }
    else if (flag & MAZE_WALL_UP)
    {
        t->x += FMAZE_CELL_SIZE;
    }
    else if (flag & MAZE_WALL_RIGHT)
    {
        f->x += FMAZE_CELL_SIZE;
        t->x = f->x;
        t->y += FMAZE_CELL_SIZE;
    }
    else if (flag & MAZE_WALL_DOWN)
    {
        f->y += FMAZE_CELL_SIZE;
        t->y = f->y;
        t->x += FMAZE_CELL_SIZE;
    }
}

void AddPaintWall(Cell *cell, int widx)
{
    PaintWall *pw;

    if (npaint == N_PAINT_OBJECTS)
    {
        printf("Paint list full\n");
        return;
    }

    pw = &paint[npaint].u.wall;
    paint[npaint].type = PO_WALL;
    npaint++;

    pw->wall = cell->walls[widx];
}

void AddPaintWalls(Cell *cell, WallFlags wf)
{
    if (wf & MAZE_WALL_LEFT)
    {
        AddPaintWall(cell, WIDX_LEFT);
    }
    if (wf & MAZE_WALL_RIGHT)
    {
        AddPaintWall(cell, WIDX_RIGHT);
    }
    if (wf & MAZE_WALL_DOWN)
    {
        AddPaintWall(cell, WIDX_DOWN);
    }
    if (wf & MAZE_WALL_UP)
    {
        AddPaintWall(cell, WIDX_UP);
    }
}

void AddPaintPartial(Object *obj)
{
    PaintPartial *pp;

    if (npaint == N_PAINT_OBJECTS)
    {
        printf("Paint list full\n");
        return;
    }

    pp = &paint[npaint].u.partial;
    paint[npaint].type = PO_PARTIAL;
    npaint++;

    pp->obj = obj;
}

void AddCell(int x, int y, WallFlags wf)
{
    Cell *cell;
    Object *obj;

    wf |= MAZE_CONTENTS;
    cell = CellAt(x, y);
    if ((cell->unseen & wf) == 0)
    {
        return;
    }

    AddPaintWalls(cell, (WallFlags)(wf & cell->unseen));

    if (cell->unseen & MAZE_CONTENTS)
    {
        for (obj = cell->contents; obj; obj = obj->next)
        {
            AddPaintPartial(obj);
        }
    }

    cell->unseen &= ~wf;
}

void TraceCells(FxPt2 *ip, FxVec2 *dp, WallHit *hit)
{
    int cx, cy;
    int sgnx, sgny;
    FxVec2 dg, dst;
    FxPt2 fp, g;
    WallFlags xwf, ywf, iwf, xpf, ypf;
    FxValue sx, sy;

    cx = MfxToCell(ip->x);
    cy = MfxToCell(ip->y);

    fp = *ip;

#ifdef TRACEDEB
    printf("pt %ld,%ld dp %ld,%ld\n", fp.x, fp.y, dp.x, dp.y);
#endif

    if (dp->x < 0)
    {
        g.x = CellToMfx(cx)-FX_MIN_VALUE;
        dg.x = -FMAZE_CELL_SIZE;
        sgnx = -1;
        xwf = MAZE_WALL_LEFT;
        xpf = MAZE_WALL_LEFT_PARTIAL;
    }
    else
    {
        g.x = CellToMfx(cx+1);
        dg.x = FMAZE_CELL_SIZE;
        sgnx = 1;
        xwf = MAZE_WALL_RIGHT;
        xpf = MAZE_WALL_RIGHT_PARTIAL;
        if (dp->x == 0)
        {
            xwf |= MAZE_WALL_LEFT;
            xpf |= MAZE_WALL_LEFT_PARTIAL;
        }
    }
    if (dp->y < 0)
    {
        g.y = CellToMfx(cy)-FX_MIN_VALUE;
        dg.y = -FMAZE_CELL_SIZE;
        sgny = -1;
        ywf = MAZE_WALL_UP;
        ypf = MAZE_WALL_UP_PARTIAL;
    }
    else
    {
        g.y = CellToMfx(cy+1);
        dg.y = FMAZE_CELL_SIZE;
        sgny = 1;
        ywf = MAZE_WALL_DOWN;
        ypf = MAZE_WALL_DOWN_PARTIAL;
        if (dp->y == 0)
        {
            ywf |= MAZE_WALL_UP;
            ypf |= MAZE_WALL_UP_PARTIAL;
        }
    }

    for (;;)
    {
        AddCell(cx, cy, (WallFlags)(xwf | ywf));

        dst.x = (g.x-fp.x)*sgnx;
        dst.y = (g.y-fp.y)*sgny;
        sx = FxMul(dst.x, dp->y);
        if (sx < 0)
        {
            sx = -sx;
        }
        sy = FxMul(dst.y, dp->x);
        if (sy < 0)
        {
            sy = -sy;
        }

#ifdef TRACEDEB
        printf("dx %ld, sx %ld, dy %ld, sy %ld\n", dst.x, sx, dst.y, sy);
#endif

        if (sx <= sy)
        {
            if ((maze_cells[cy][cx].can_see & xwf) &&
                (maze_cells[cy][cx].can_see & xpf) == 0)
            {
                iwf = xwf;
                break;
            }

            fp.x = g.x;
            fp.y += FxDiv(sx, dp->x)*sgnx*sgny;
            if (fp.y == g.y)
            {
                if ((maze_cells[cy][cx].can_see & ywf) &&
                    (maze_cells[cy][cx].can_see & ypf) == 0)
                {
                    iwf = ywf;
                    break;
                }
                cy += sgny;
                g.y += dg.y;
            }
            cx += sgnx;
            g.x += dg.x;
        }
        else
        {
            if ((maze_cells[cy][cx].can_see & ywf) &&
                (maze_cells[cy][cx].can_see & ypf) == 0)
            {
                iwf = ywf;
                break;
            }

            fp.y = g.y;
            fp.x += FxDiv(sy, dp->y)*sgnx*sgny;
            if (fp.x == g.x)
            {
                if ((maze_cells[cy][cx].can_see & xwf) &&
                    (maze_cells[cy][cx].can_see & xpf) == 0)
                {
                    iwf = xwf;
                    break;
                }
                cx += sgnx;
                g.x += dg.x;
            }
            cy += sgny;
            g.y += dg.y;
        }
    }
    hit->cell = CellAt(cx, cy);
    hit->cx = cx;
    hit->cy = cy;
    hit->flag = iwf;
}

void TraceView(MazeView *vw)
{
    FaAngle acc;
    FxVec2 vcc;
    WallHit hit;
    int rc;

    acc = FaAdd(vw->ang, FaDeg(VIEW_ANG)/2);

    for (rc = 0; rc < VIEW_ANG; rc++)
    {
        vcc.x = FaCos(acc);
        vcc.y = FaSin(acc);

        TraceCells(&vw->pos, &vcc, &hit);

        acc = FaAdd(acc, -FaDeg(1));
    }
}

static void WallCompute(PaintObject *po, MazeView *vw,
                        FxValue cs, FxValue sn)
{
    FxPt2 mid;
    Wall *wall;

    wall = po->u.wall.wall;

    // Compute depth at midpoint of wall
    // Eye coordinate depth increases along the X so
    // we only need to transform it

    mid.x = (wall->f.x+wall->t.x)/2-vw->pos.x;
    mid.y = (wall->f.y+wall->t.y)/2-vw->pos.y;

    po->depth = FxMul(mid.x, cs)+FxMul(mid.y, sn);
}

static void PartialCompute(PaintObject *po, MazeView *vw,
                           FxValue cs, FxValue sn)
{
    PaintPartial *pp;
    FxPt2 c;

    pp = &po->u.partial;

    // Compute depth at center of partial

    c.x = pp->obj->p.x-vw->pos.x;
    c.y = pp->obj->p.y-vw->pos.y;

    po->depth = FxMul(c.x, cs)+FxMul(c.y, sn);
}

typedef void (*PoComputeFn)(PaintObject *po, MazeView *vw,
                            FxValue cs, FxValue sn);
static PoComputeFn PoCompute[PO_COUNT] =
{
    WallCompute,
    PartialCompute
};

static float colors[17][3] =
{
    0.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 0.5f,
    0.0f, 0.5f, 0.0f,
    0.0f, 0.5f, 0.5f,
    0.5f, 0.0f, 0.0f,
    0.5f, 0.0f, 0.5f,
    0.5f, 0.5f, 0.0f,
    0.5f, 0.5f, 0.5f,
    0.75f, 0.75f, 0.75f,
    0.0f, 0.0f, 1.0f,
    0.0f, 1.0f, 0.0f,
    0.0f, 1.0f, 1.0f,
    1.0f, 0.0f, 0.0f,
    1.0f, 0.0f, 1.0f,
    1.0f, 1.0f, 0.0f,
    1.0f, 1.0f, 1.0f,
    0.75f, 0.39f, 0.0f
};

#define WALL_SET 0
#define FLOOR_SET 1
#define CEILING_SET 2

static float *smooth_sets[3][2][4] =
{
    &colors[1][0], &colors[2][0], &colors[4][0], &colors[7][0],
    &colors[2][0], &colors[1][0], &colors[7][0], &colors[4][0],
    &colors[10][0], &colors[2][0], &colors[4][0], &colors[6][0],
    &colors[10][0], &colors[2][0], &colors[4][0], &colors[6][0],
    &colors[9][0], &colors[1][0], &colors[2][0], &colors[3][0],
    &colors[9][0], &colors[1][0], &colors[2][0], &colors[3][0]
};

static float *flat_sets[3][2][4] =
{
    &colors[8][0], &colors[8][0], &colors[8][0], &colors[8][0],
    &colors[15][0], &colors[15][0], &colors[15][0], &colors[15][0],
    &colors[2][0], &colors[2][0], &colors[2][0], &colors[2][0],
    &colors[2][0], &colors[2][0], &colors[2][0], &colors[2][0],
    &colors[9][0], &colors[9][0], &colors[9][0], &colors[9][0],
    &colors[9][0], &colors[9][0], &colors[9][0], &colors[9][0]
};

void SetAlphaCol(GLfloat *fv3)
{
    if (maze_options.all_alpha)
    {
        GLfloat fv4[4];

        fv4[0] = fv3[0];
        fv4[1] = fv3[1];
        fv4[2] = fv3[2];
        fv4[3] = 0.5f;
        glColor4fv(fv4);
    }
    else
    {
        glColor3fv(fv3);
    }
}

static void WallDraw(PaintObject *po, MazeView *vw)
{
    Wall *wall;
    float fx, fy, tx, ty, cx, cy, nx, ny;
    float **col_set;
    int reps;
    int rept;
    GLenum old_env;

    wall = po->u.wall.wall;
    reps = wall->pTexEnv->texRep.x;
    rept = wall->pTexEnv->texRep.y;

    fx = (float)FxFlt(wall->f.x);
    fy = (float)FxFlt(wall->f.y);
    tx = (float)FxFlt(wall->t.x);
    ty = (float)FxFlt(wall->t.y);
    nx = -(ty-fy);
    ny = (tx-fx);
    cx = (float)FxFlt(vw->pos.x);
    cy = (float)FxFlt(vw->pos.y);

    col_set = &flat_sets[WALL_SET][wall->col][0];
    switch(maze_options.render[WALLS])
    {
    case RENDER_NONE:
        return;
    case RENDER_SMOOTH:
        col_set = &smooth_sets[WALL_SET][wall->col][0];
        break;
    case RENDER_FLAT:
    case RENDER_TEXTURED:
        break;
    }

    // Compute dot product with wall normal to determine
    // wall direction.  We need to know the wall direction
    // in order to ensure that the wall texture faces the
    // correct direction
    UseTextureEnv(wall->pTexEnv);

    if (wall->pTexEnv->bTransp)
    {
        if (!maze_options.all_alpha)
        {
            glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &old_env);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, gTexEnvMode);
            glEnable(GL_BLEND);
        }
    }

    glBegin(GL_POLYGON);
    if ((fx-cx)*nx+(fy-cy)*ny > 0)
    {
        glTexCoord2d(0, 0);
        SetAlphaCol(col_set[0]);
        glVertex3f(fx, fy, 0.0f);
        glTexCoord2d(reps, 0);
        SetAlphaCol(col_set[1]);
        glVertex3f(tx, ty, 0.0f);
        glTexCoord2d(reps, rept);
        SetAlphaCol(col_set[2]);
        glVertex3f(tx, ty, maze_height);
        glTexCoord2d(0, rept);
        SetAlphaCol(col_set[3]);
        glVertex3f(fx, fy, maze_height);
    }
    else
    {
        glTexCoord2d(reps, 0);
        SetAlphaCol(col_set[0]);
        glVertex3f(fx, fy, 0.0f);
        glTexCoord2d(0, 0);
        SetAlphaCol(col_set[1]);
        glVertex3f(tx, ty, 0.0f);
        glTexCoord2d(0, rept);
        SetAlphaCol(col_set[2]);
        glVertex3f(tx, ty, maze_height);
        glTexCoord2d(reps, rept);
        SetAlphaCol(col_set[3]);
        glVertex3f(fx, fy, maze_height);
    }
    glEnd();

    if (wall->pTexEnv->bTransp)
    {
        if (!maze_options.all_alpha)
        {
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, old_env);
            glDisable(GL_BLEND);
        }
    }
}

void (APIENTRY *convex_solids[SPECIAL_ARG_COUNT])(GLdouble radius) =
{
    auxSolidIcosahedron,
    auxSolidOctahedron,
    auxSolidDodecahedron,
    auxSolidTetrahedron
};

static void PartialDraw(PaintObject *po, MazeView *vw)
{
    PaintPartial *pp;
    float w, h, cx, cy, cz, vx, vy, fx, fy, fz, tx, ty, tz;
    float cs, sn;
    GLenum old_env;

    pp = &po->u.partial;

    w = (float)FxFlt(pp->obj->w);
    h = (float)FxFlt(pp->obj->h);

    // Partials are billboarded so we want it to always be
    // perpendicular to the view direction

    cs = (float)FxFlt(FaCos(vw->ang));
    sn = (float)FxFlt(FaSin(vw->ang));
    vx = -sn*w;
    vy = cs*w;

    cx = (float)FxFlt(pp->obj->p.x);
    cy = (float)FxFlt(pp->obj->p.y);
    cz = (float)FxFlt(pp->obj->z);

    fx = cx-vx;
    fy = cy-vy;
    fz = (cz-h)*maze_height;
    tx = cx+vx;
    ty = cy+vy;
    tz = (cz+h)*maze_height;

    if (maze_options.render[WALLS] == RENDER_TEXTURED)
    {
        glDisable(GL_TEXTURE_2D);
    }

    switch(pp->obj->draw_style)
    {
    case DRAW_POLYGON:
        glEnable(GL_TEXTURE_2D);
        if (!maze_options.all_alpha)
        {
            glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &old_env);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, gTexEnvMode);
            glEnable(GL_BLEND);
        }
        UseTextureEnv( pp->obj->pTexEnv );
        SetAlphaCol(colors[15]);
        glBegin(GL_POLYGON);
        glNormal3f(cs, sn, 0.0f);
        glTexCoord2f(1.0f, 0.0f);
        glVertex3f(fx, fy, fz);
        glTexCoord2f(0.0f, 0.0f);
        glVertex3f(tx, ty, fz);
        glTexCoord2f(0.0f, 1.0f);
        glVertex3f(tx, ty, tz);
        glTexCoord2f(1.0f, 1.0f);
        glVertex3f(fx, fy, tz);
        glEnd();
        glDisable(GL_TEXTURE_2D);
        if (!maze_options.all_alpha)
        {
            glDisable(GL_BLEND);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, old_env);
        }
        break;

    case DRAW_SPECIAL:
        SetAlphaCol(colors[pp->obj->col]);

        glEnable(GL_AUTO_NORMAL);
        glEnable(GL_NORMALIZE);
        glEnable(GL_LIGHTING);
        glEnable(GL_CULL_FACE);
        glEnable(GL_DITHER);
        glPushMatrix();

        glTranslated(cx, cy, cz*maze_height);
        glScaled(1.0, 1.0, maze_height);
        glRotated(FaFltDegVal(pp->obj->ang), 0, 0, 1);
        glRotated(pp->obj->user3, 0, 1, 0);
        // Must use convex objects since depth testing can be off
        convex_solids[pp->obj->draw_arg](w);

        glPopMatrix();
        if( !maze_options.bDither )
            glDisable(GL_DITHER);
        glDisable(GL_CULL_FACE);
        glDisable(GL_LIGHTING);
        glDisable(GL_AUTO_NORMAL);
        glDisable(GL_NORMALIZE);
        break;
    }

    if (maze_options.render[WALLS] == RENDER_TEXTURED)
    {
        glEnable(GL_TEXTURE_2D);
    }
}

typedef void (*PoDrawFn)(PaintObject *po, MazeView *vw);
static PoDrawFn PoDraw[PO_COUNT] =
{
    WallDraw,
    PartialDraw
};

void RenderZPlane(int render, TEX_ENV *pTexEnv, int set, float zval)
{
    float **col_set;
    int reps = pTexEnv->texRep.x;
    int rept = pTexEnv->texRep.y;

    switch(render)
    {
    case RENDER_NONE:
        break;
    case RENDER_TEXTURED:
        UseTextureEnv(pTexEnv);
        glEnable(GL_TEXTURE_2D);
        // Fall through
    case RENDER_FLAT:
    case RENDER_SMOOTH:
        col_set = &flat_sets[set][0][0];
        if (render == RENDER_SMOOTH)
        {
            col_set = &smooth_sets[set][0][0];
        }

        glBegin(GL_POLYGON);

        // Switch texture orientation dependent on surface type
        if( set == CEILING_SET ) {
            glTexCoord2f((float)reps*MAZE_SIZE, 0.0f);
            glColor3fv(col_set[0]);
            glVertex3f(0.0f, 0.0f, zval);
            glTexCoord2f(0.0f, 0.0f);
            glColor3fv(col_set[1]);
            glVertex3f((float)MAZE_SIZE, 0.0f, zval);
            glTexCoord2f(0.0f, (float)rept*MAZE_SIZE);
            glColor3fv(col_set[2]);
            glVertex3f((float)MAZE_SIZE, (float)MAZE_SIZE, zval);
            glTexCoord2f((float)reps*MAZE_SIZE, (float)rept*MAZE_SIZE);
            glColor3fv(col_set[3]);
            glVertex3f(0.0f, (float)MAZE_SIZE, zval);
        } else {
            glTexCoord2f(0.0f, 0.0f);
            glColor3fv(col_set[0]);
            glVertex3f(0.0f, 0.0f, zval);
            glTexCoord2f((float)reps*MAZE_SIZE, 0.0f);
            glColor3fv(col_set[1]);
            glVertex3f((float)MAZE_SIZE, 0.0f, zval);
            glTexCoord2f((float)reps*MAZE_SIZE, (float)rept*MAZE_SIZE);
            glColor3fv(col_set[2]);
            glVertex3f((float)MAZE_SIZE, (float)MAZE_SIZE, zval);
            glTexCoord2f(0.0f, (float)rept*MAZE_SIZE);
            glColor3fv(col_set[3]);
            glVertex3f(0.0f, (float)MAZE_SIZE, zval);
        }

        glEnd();

        if (render == RENDER_TEXTURED)
        {
            glDisable(GL_TEXTURE_2D);
        }
        break;
    }
}

void Render(MazeView *vw)
{
    FxValue cs, sn;
    PaintObject *sorted, *so, *pso;
    PaintObject *po;
    int i;
    FxPt2 at;
    BOOL special;
    float viewHeight;

    cs = FaCos(vw->ang);
    sn = FaSin(vw->ang);

    at.x = vw->pos.x+cs;
    at.y = vw->pos.y+sn;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glRotated(view_rot, 0, 0, 1);
    gluPerspective(VIEW_ANG, 1, .01, 100);
    viewHeight = 0.5f;
    gluLookAt(FxFlt(vw->pos.x), FxFlt(vw->pos.y), viewHeight,
              FxFlt(at.x), FxFlt(at.y), viewHeight,
              0, 0, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    RenderZPlane(maze_options.render[FLOOR], &gTexEnv[TEX_FLOOR], FLOOR_SET, 0.0f);
    RenderZPlane(maze_options.render[CEILING], &gTexEnv[TEX_CEILING], CEILING_SET, 1.0f);

    sorted = NULL;
    special = FALSE;
    for (i = 0, po = paint; i < npaint; i++, po++)
    {
        if (po->type == PO_PARTIAL &&
            po->u.partial.obj->draw_style == DRAW_SPECIAL)
        {
            special = TRUE;
        }

        PoCompute[po->type](po, vw, cs, sn);

        for (so = sorted, pso = NULL; so; pso = so, so = so->closer)
        {
            if (so->depth <= po->depth)
            {
                break;
            }
        }
        if (pso == NULL)
        {
            sorted = po;
        }
        else
        {
            pso->closer = po;
        }
        po->closer = so;
    }

    if (maze_options.render[WALLS] == RENDER_TEXTURED)
    {
        glEnable(GL_TEXTURE_2D);
    }

    for (so = sorted; so; so = so->closer)
    {
        PoDraw[so->type](so, vw);
    }

    if (maze_options.render[WALLS] == RENDER_TEXTURED)
    {
        glDisable(GL_TEXTURE_2D);
    }
}

void InitPaint(void)
{
    int i, j;

    npaint = 0;
    for (i = 0; i < MAZE_GRID; i++)
    {
        for (j = 0; j < MAZE_GRID; j++)
        {
            maze_cells[i][j].unseen = maze_cells[i][j].can_see | MAZE_CONTENTS;
        }
    }
}

void DrawMaze(MazeView *vw)
{
    InitPaint();
    TraceView(vw);
    Render(vw);
}

void DrawMazeWalls(void)
{
    int w;
    Wall *wall;

    wall = maze;

    glColor3f(1.0f, 1.0f, 1.0f);

    glBegin(GL_LINES);
    for (w = 0; w < nwalls; w++)
    {
        glVertex2f((float)FxFltVal(wall->f.x), (float)FxFltVal(wall->f.y));
        glVertex2f((float)FxFltVal(wall->t.x), (float)FxFltVal(wall->t.y));
        wall++;
    }
    glEnd();
}

#define SQRT2_2 0.707107f

void DrawTopView(MazeView *vw)
{
    int c;
    Cell *cell;
    Object *obj;
    float vx, vy, cx, cy, width, ang;
    extern float gfAspect;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D( -MAZE_SIZE/2.0, MAZE_SIZE/2.0,
            -MAZE_SIZE/2.0/gfAspect, MAZE_SIZE/2.0/gfAspect );
    glMatrixMode(GL_MODELVIEW);

    glPushMatrix();

    ang = (float)FaFltDegVal(vw->ang)+90.0f;
    glRotatef(ang, 0.0f, 0.0f, 1.0f);
    vx = (float)FxFltVal(vw->pos.x);
    vy = (float)FxFltVal(vw->pos.y);
    glTranslatef(-vx, -vy, 0.0f);

#define AA_LINES 1
#ifdef AA_LINES
    // Turn on antialiased lines
    glEnable( GL_BLEND );
    glEnable( GL_LINE_SMOOTH );
    glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
#endif

    glCallList(maze_walls_list);

#ifdef AA_LINES
    glDisable( GL_BLEND );
    glDisable( GL_LINE_SMOOTH );
#endif

    // Objects aren't put in the walls display list so that they
    // can move around

    cell = &maze_cells[0][0];
    for (c = 0; c < MAZE_CELLS; c++)
    {
        for (obj = cell->contents; obj != NULL; obj = obj->next)
        {
            cx = (float)FxFltVal(obj->p.x);
            cy = (float)FxFltVal(obj->p.y);
            width = (float)FxFltVal(obj->w);

            glColor3fv(colors[obj->col]);

            glPushMatrix();
            glTranslatef(cx, cy, 0.0f);
            glRotated(FaFltDegVal(obj->ang), 0, 0, 1);
            glBegin(GL_POLYGON);
            glVertex2f(width, 0.0f);
            glVertex2f(-width*SQRT2_2, width*0.5f);
            glVertex2f(-width*SQRT2_2, -width*0.5f);
            glEnd();
            glPopMatrix();
        }

        cell++;
    }

    glPopMatrix();

    // Draw self
    glColor3f(0.0f, 0.0f, 1.0f);
    width = MAZE_CELL_SIZE/4.0f;
    glBegin(GL_POLYGON);
    glVertex2f(0.0f, width);
    glVertex2f(width*0.5f, -width*SQRT2_2);
    glVertex2f(-width*0.5f, -width*SQRT2_2);
    glEnd();
}


DIALOG.C

/******************************Module*Header*******************************\
* Module Name: sspipes.c
*
* Message loop and dialog box for the OpenGL-based 3D Pipes screen saver.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <windows.h>
#include <commdlg.h>
#include <scrnsave.h>
#include <GL\gl.h>
#include <math.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys\timeb.h>
#include <time.h>
#include <commctrl.h>
#include "sscommon.h"
#include "sspipes.h"
#include "dlgs.h"
#include "dialog.h"

BOOL bFlexMode;
BOOL bMultiPipes;

// ulJointType controls the style of the elbows.

ULONG ulJointType = JOINT_ELBOW;

// ulSurfStyle determines whether the pipe surfaces are textured.

ULONG ulSurfStyle = SURFSTYLE_SOLID;

// ulTexQuality control the texture quality.

ULONG ulTexQuality = TEXQUAL_DEFAULT;

// fTesselFact controls the how finely the surface is tesselated.  It
// varies from very course (0.0) to very fine (2.0).

float fTesselFact = 1.0f;

// If ulSurfStyle indicates a textured surface, szTexPathname specifies
// the bitmap chosen as the texture.

// Texture file(s)
TEXFILE gTexFile[MAX_TEXTURES] = {0};
int gnTextures = 0;

static void updateDialogControls(HWND hDlg);

/******************************Public*Routine******************************\
* getIniSettings
*
* Get the screen saver configuration options from .INI file/registry.
*
\**************************************************************************/

void
getIniSettings()
{
    int    tessel;

    // Load resources

    LoadString(hMainInstance, IDS_GENNAME, szScreenSaver,
               sizeof(szScreenSaver) / sizeof(TCHAR));

    // Load resource strings for texture processing

    ss_LoadTextureResourceStrings();

    // Get registry settings

    if( ss_RegistrySetup( hMainInstance, IDS_SAVERNAME, IDS_INIFILE ) )
    {
        ulJointType = ss_GetRegistryInt( IDS_JOINTTYPE, JOINT_ELBOW );

        ulSurfStyle = ss_GetRegistryInt( IDS_SURFSTYLE, SURFSTYLE_SOLID );

        ulTexQuality = ss_GetRegistryInt( IDS_TEXQUAL, TEXQUAL_DEFAULT );

        tessel = ss_GetRegistryInt( IDS_TESSELATION, 0 );
        SS_CLAMP_TO_RANGE2( tessel, 0, 200 );
        fTesselFact  = (float)tessel / 100.0f;

        bFlexMode = ss_GetRegistryInt( IDS_FLEX, 0 );

        bMultiPipes = ss_GetRegistryInt( IDS_MULTIPIPES, 0 );

        // Get any textures

#ifndef NEW_TEXTURE
        // Just get one texture with old registry names
        ss_GetRegistryString( IDS_TEXTURE, 0, gTexFile[0].szPathName, MAX_PATH);
        gTexFile[0].nOffset = ss_GetRegistryInt( IDS_TEXTURE_FILE_OFFSET, 0 );
        gnTextures = 1;
#else
        gnTextures = ss_GetRegistryInt( IDS_TEXTURE_COUNT, 0 );
        SS_CLAMP_TO_RANGE2( gnTextures, 0, MAX_TEXTURES );

        idsTexture = IDS_TEXTURE0;
        idsTexOffset = IDS_TEXOFFSET0;
        for( i = 0; i < gnTextures; i++, idsTexture++, idsTexOffset++ ) {
            ss_GetRegistryString( idsTexture, 0, gTexFile[i].szPathName,
                                  MAX_PATH);
            gTexFile[i].nOffset = ss_GetRegistryInt( idsTexOffset, 0 );
        }
#endif
    }
}

/**************************************************************************\
* ConfigInit
*
\**************************************************************************/
BOOL
ss_ConfigInit( HWND hDlg )
{
    return TRUE;
}

/******************************Public*Routine******************************\
* saveIniSettings
*
* Save the screen saver configuration option to the .INI file/registry.
*
\**************************************************************************/

static void saveIniSettings(HWND hDlg)
{
    if( ss_RegistrySetup( hMainInstance, IDS_SAVERNAME, IDS_INIFILE ) )
    {
        ss_WriteRegistryInt( IDS_JOINTTYPE, ulJointType );
        ss_WriteRegistryInt( IDS_SURFSTYLE, ulSurfStyle );
        ss_WriteRegistryInt( IDS_TEXQUAL, ulTexQuality );
        ss_WriteRegistryInt( IDS_TESSELATION,
                    ss_GetTrackbarPos(hDlg, DLG_SETUP_TESSEL) );
        ss_WriteRegistryInt( IDS_FLEX, bFlexMode );
        ss_WriteRegistryInt( IDS_MULTIPIPES, bMultiPipes );
#ifndef NEW_TEXTURE
        ss_WriteRegistryString( IDS_TEXTURE, gTexFile[0].szPathName );
        ss_WriteRegistryInt( IDS_TEXTURE_FILE_OFFSET, gTexFile[0].nOffset );
#else
        idsTexture = IDS_TEXTURE0;
        idsTexOffset = IDS_TEXOFFSET0;
        for( i = 0; i < gnTextures; i++, idsTexture++, idsTexOffset++ ) {
            ss_WriteRegistryString( idsTexture, gTexFile[i].szPathName );
            ss_WriteRegistryInt( idsTexOffset, gTexFile[i].nOffset );
        }
#endif
    }
}

/******************************Public*Routine******************************\
* setupDialogControls
*
* Do initial setup of dialog controls.
\**************************************************************************/

static void
setupDialogControls(HWND hDlg)
{
    int pos;
    int wTmp;
    TCHAR szStr[GEN_STRING_SIZE];
    int idsJointType;

    InitCommonControls();

    pos = (int)(fTesselFact * 100.0f);
    ss_SetupTrackbar( hDlg, DLG_SETUP_TESSEL, 0, 200, 1, 10, pos );

    // setup jointType combo box
    idsJointType = IDS_JOINT_ELBOW;
    for (wTmp = 0; wTmp < NUM_JOINTTYPES; wTmp++, idsJointType++) {
        LoadString(hMainInstance, idsJointType, szStr,
                    GEN_STRING_SIZE);
        SendDlgItemMessage(hDlg, DLG_COMBO_JOINTTYPE, CB_ADDSTRING, 0,
                           (LPARAM) szStr);
    }
    SendDlgItemMessage(hDlg, DLG_COMBO_JOINTTYPE, CB_SETCURSEL,
                       ulJointType, 0);

    updateDialogControls( hDlg );
}

/******************************Public*Routine******************************\
* updateDialogControls
*
* Setup the dialog controls based on the current global state.
*
\**************************************************************************/

static void updateDialogControls(HWND hDlg)
{
    BOOL bTexture = (ulSurfStyle ==  SURFSTYLE_TEX);

    CheckDlgButton( hDlg, IDC_RADIO_SOLID, ulSurfStyle == SURFSTYLE_SOLID );
    CheckDlgButton( hDlg, IDC_RADIO_TEX  , bTexture );
#ifdef NEED_THESE_LATER
    CheckDlgButton( hDlg, IDC_RADIO_WIREFRAME, ulSurfStyle == SURFSTYLE_WIREFRAME   );

    CheckDlgButton( hDlg, IDC_RADIO_TEXQUAL_DEFAULT ,
                   bTexture && ulTexQuality == TEXQUAL_DEFAULT);
    CheckDlgButton( hDlg, IDC_RADIO_TEXQUAL_HIGH,
                   bTexture && ulTexQuality == TEXQUAL_HIGH);
#endif

    CheckDlgButton( hDlg, IDC_RADIO_NORMAL, !bFlexMode);
    CheckDlgButton( hDlg, IDC_RADIO_FLEX, bFlexMode);

    CheckDlgButton( hDlg, IDC_RADIO_SINGLE_PIPE, !bMultiPipes);
    CheckDlgButton( hDlg, IDC_RADIO_MULTIPLE_PIPES, bMultiPipes);

    EnableWindow( GetDlgItem(hDlg, DLG_COMBO_JOINTTYPE), !bFlexMode);
    EnableWindow( GetDlgItem(hDlg, IDC_STATIC_JOINTTYPE), !bFlexMode);

    EnableWindow( GetDlgItem(hDlg, DLG_SETUP_TEXTURE), bTexture );
    EnableWindow( GetDlgItem(hDlg, IDC_RADIO_TEXQUAL_DEFAULT), bTexture );
    EnableWindow( GetDlgItem(hDlg, IDC_RADIO_TEXQUAL_HIGH), bTexture );
    EnableWindow( GetDlgItem(hDlg, IDC_STATIC_TEXQUAL_GRP), bTexture );

    EnableWindow(GetDlgItem(hDlg, DLG_SETUP_TESSEL), TRUE);
    EnableWindow(GetDlgItem(hDlg, IDC_STATIC_TESS_MIN), TRUE);
    EnableWindow(GetDlgItem(hDlg, IDC_STATIC_TESS_MAX), TRUE);
    EnableWindow(GetDlgItem(hDlg, IDC_STATIC_TESS_GRP), TRUE);
}

BOOL WINAPI RegisterDialogClasses(HANDLE hinst)
{
    return TRUE;
}

/******************************Public*Routine******************************\
* ScreenSaverConfigureDialog
*
* Screen saver setup dialog box procedure.
\**************************************************************************/

BOOL ScreenSaverConfigureDialog(HWND hDlg, UINT message,
                                WPARAM wParam, LPARAM lParam)
{
    int optMask = 1;

    switch (message)
    {
        case WM_INITDIALOG:
            getIniSettings();
            setupDialogControls(hDlg);
            return TRUE;

        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case IDC_RADIO_SOLID:
                case IDC_RADIO_TEX:
                case IDC_RADIO_WIREFRAME:
                    ulSurfStyle = IDC_TO_SURFSTYLE(LOWORD(wParam));
                    break;

                case IDC_RADIO_TEXQUAL_DEFAULT:
                case IDC_RADIO_TEXQUAL_HIGH:
                    ulTexQuality = IDC_TO_TEXQUAL(LOWORD(wParam));
                    break;

                case IDC_RADIO_NORMAL:
                    bFlexMode = FALSE;
                    break;
                case IDC_RADIO_FLEX:
                    bFlexMode = TRUE;
                    break;

                case IDC_RADIO_SINGLE_PIPE:
                    bMultiPipes = FALSE;
                    break;
                case IDC_RADIO_MULTIPLE_PIPES:
                    bMultiPipes = TRUE;
                    break;

                case DLG_SETUP_TEXTURE:
                    // Run choose texture dialog
                    ss_SelectTextureFile( hDlg, &gTexFile[0] );
                    break;

                case DLG_COMBO_JOINTTYPE:
                    switch (HIWORD(wParam))
                    {
                        case CBN_EDITCHANGE:
                        case CBN_SELCHANGE:
                            ulJointType =
                                SendDlgItemMessage(hDlg, DLG_COMBO_JOINTTYPE,
                                                      CB_GETCURSEL, 0, 0);
                            break;
                        default:
                            return FALSE;
                    }
                    break;

                case IDOK:
                    saveIniSettings(hDlg);
                    EndDialog(hDlg, TRUE);
                    break;

                case IDCANCEL:
                    EndDialog(hDlg, FALSE);
                    break;

                default:
                    break;
            }
            updateDialogControls(hDlg);
            return TRUE;

        default:
            return 0;
    }
    return 0;
}


DIALOG.H

/******************************Module*Header*******************************\
* Module Name: dialog.h
*
* Externals from dialog.c
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __dialog_h__
#define __dialog_h__

#include "dlgs.h"
#include "sscommon.h"

#ifdef __cplusplus
extern "C" {
#endif

extern float fTesselFact;
extern ULONG ulJointType;
extern ULONG ulSurfStyle;
extern ULONG ulTexQuality;
extern TEXFILE gTexFile[];
extern int gnTextures;

extern BOOL  bFlexMode;
extern BOOL  bMultiPipes;

extern void getIniSettings(void);

// surface styles
enum {
    SURFSTYLE_SOLID = 0,
    SURFSTYLE_TEX,
    SURFSTYLE_WIREFRAME
};

// Resource constants

#define IDS_SAVERNAME           1002
#define IDS_JOINTTYPE           1008
#define IDS_SURFSTYLE           1009
#define IDS_TEXQUAL             1010

#define IDS_FLEX                1020
#define IDS_MULTIPIPES          1021

// should get rid of OFFSET...
#define IDS_TEXTURE_COUNT       1029
#define IDS_TEXTURE0            1030
#define IDS_TEXTURE1            1031
#define IDS_TEXTURE2            1032
#define IDS_TEXTURE3            1033
#define IDS_TEXTURE4            1034
#define IDS_TEXTURE5            1035
#define IDS_TEXTURE6            1036
#define IDS_TEXTURE7            1037
#define IDS_TEXOFFSET0          1050
#define IDS_TEXOFFSET1          1051
#define IDS_TEXOFFSET2          1052
#define IDS_TEXOFFSET3          1053
#define IDS_TEXOFFSET4          1054
#define IDS_TEXOFFSET5          1055
#define IDS_TEXOFFSET6          1056
#define IDS_TEXOFFSET7          1057

#define IDS_JOINT_ELBOW         1100
#define IDS_JOINT_BALL          1101
#define IDS_JOINT_MIXED         1102
#define IDS_JOINT_CYCLE         1103

#define DLG_SETUP_TESSEL        2001
#define DLG_SETUP_TEXTURE       2002
#define IDC_STATIC_TESS         2003
#define IDC_STATIC_TESS_MIN     2004
#define IDC_STATIC_TESS_MAX     2005
#define IDC_STATIC_TESS_GRP     2006
#define IDC_STATIC_TEXQUAL_GRP  2007

#define IDC_RADIO_SINGLE_PIPE       3000
#define IDC_RADIO_MULTIPLE_PIPES    3001

#define IDC_RADIO_NORMAL        3100
#define IDC_RADIO_FLEX          3101

#define IDC_STATIC_JOINTTYPE       2106

#define DLG_COMBO_JOINTTYPE     2200

enum {
    JOINT_ELBOW=0,
    JOINT_BALL,
    JOINT_MIXED,
    JOINT_CYCLE,
    NUM_JOINTTYPES
};

// In order for the IDC_TO_SURFSTYLE conversion macro to work, the radio buttons
// for surface styles must be kept contiguous.

#define IDC_RADIO_SOLID         2111
#define IDC_RADIO_TEX           2112
#define IDC_RADIO_WIREFRAME     2113
#define IDC_TO_SURFSTYLE(n)          ( (n) - IDC_RADIO_SOLID )

// In order for the IDC_TO_TEXQUAL conversion macro to work, the radio buttons
// for texture quality must be kept contiguous.

#define IDC_RADIO_TEXQUAL_DEFAULT   2121
#define IDC_RADIO_TEXQUAL_HIGH  2122
#define IDC_TO_TEXQUAL(n)       ( (n) - IDC_RADIO_TEXQUAL_DEFAULT )

#define TEXQUAL_DEFAULT         0
#define TEXQUAL_HIGH            1

// texture resources
#define IDB_DEFTEX              99
#define IDB_DEFTEX2            100

// multi texture dialog

#ifdef __cplusplus
}
#endif

#endif // __dialog_h__


EVAL.CXX

/******************************Module*Header*******************************\
* Module Name: eval.cxx
*
* Evaluator stuff
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

#include "sscommon.h"
#include "sspipes.h"
#include "eval.h"

//#define EVAL_DBG 1

typedef enum {
    X_PLANE = 0,
    Y_PLANE,
    Z_PLANE
};

#define EVAL_VSIZE 3  // vertex size in floats

#define TMAJOR_ORDER 2
#define TMINOR_ORDER 2

#define VDIM 3
#define TDIM 2

//forwards
#if EVAL_DBG
static void DrawPoints( int num, POINT3D *pts );
#endif
static void RotatePointSet( POINT3D *inPts, int numPts, float angle, int dir,
                      float radius, POINT3D *outPts );
static void ExtrudePointSetDir( POINT3D *inPts, int numPts, float *acPts,
                      int dir, POINT3D *outPts );

/**************************************************************************\
* EVAL
*
* Evaluator constructor
*
\**************************************************************************/

EVAL::EVAL( BOOL bTex )
{
    bTexture = bTex;

    // Allocate points buffer

// might want to use less than max in some cases
    int size = MAX_USECTIONS * MAX_UORDER * MAX_VORDER * sizeof(POINT3D);
    pts = (POINT3D *) LocalAlloc( LMEM_FIXED, size );
    SS_ASSERT( pts != NULL, "EVAL constructor\n" );

    // Alloc texture points buffer

    if( bTexture ) {
        size = MAX_USECTIONS * TEX_ORDER * TEX_ORDER * sizeof(TEX_POINT2D);
        texPts = (TEX_POINT2D *) LocalAlloc( LMEM_FIXED, size );
        SS_ASSERT( texPts != NULL, "EVAL constructor\n" );
    }

    ResetEvaluator( bTexture );
}

/**************************************************************************\
* ~EVAL
*
* Evaluator destructor
*
* Frees up memory
*
\**************************************************************************/

EVAL::~EVAL( )
{
    LocalFree( pts );
    if( bTexture )
        LocalFree( texPts );
}

/**************************************************************************\
* Reset
*
* Reset evaluator to generate 3d vertices and vertex normals
*
\**************************************************************************/

void
ResetEvaluator( BOOL bTexture )
{
    if( bTexture ) {
        glEnable( GL_MAP2_TEXTURE_COORD_2 );
    }
    glEnable( GL_MAP2_VERTEX_3 );
    glEnable( GL_AUTO_NORMAL );
    glFrontFace( GL_CW ); // cuz
// if mixing Normal and Flex, have to watch out for this, cuz normal
// needs CCW
}

/**************************************************************************\
* SetTextureControlPoints
*
* Set texture control point net
*
* This sets up 'numSections' sets of texture coordinate control points, based
* on starting and ending s and t values.
*
* s coords run along pipe direction, t coords run around circumference
*
\**************************************************************************/

void
EVAL::SetTextureControlPoints( float s_start, float s_end,
                              float t_start, float t_end )
{
    int i;
    TEX_POINT2D *ptexPts = texPts;
    GLfloat t_delta = (t_end - t_start) / numSections;
    GLfloat t = t_start;

    // calc ctrl pts for each quadrant
    for( i = 0; i < numSections; i++, ptexPts += (TDIM*TDIM) ) {
        // s, t coords
        ptexPts[0].t = ptexPts[2].t = t;
        t += t_delta;
        ptexPts[1].t = ptexPts[3].t = t;
        ptexPts[0].s = ptexPts[1].s = s_start;
        ptexPts[2].s = ptexPts[3].s = s_end;
    }
}

/**************************************************************************\
* SetVertexCtrlPtsXCTranslate
*
* Builds 3D control eval control net from 2 xcObjs displaced along the
* z-axis by 'length'.
*
* First xc used to generate points in z=0 plane.
* Second xc generates points in z=length plane.
* ! Replicates the last point around each u.
*
\**************************************************************************/

void
EVAL::SetVertexCtrlPtsXCTranslate( POINT3D *pts, float length,
                             XC *xcStart, XC *xcEnd )
{
    int i;
    POINT2D *ptsStart, *ptsEnd;
    POINT3D *pts1, *pts2;
    int     numPts = xcStart->numPts;

    numPts++;  // due to last point replication

    ptsStart = xcStart->pts;
    ptsEnd   = xcEnd->pts;
    pts1     = pts;
    pts2     = pts + numPts;

    for( i = 0; i < (numPts-1); i++, pts1++, pts2++ ) {
        // copy over x,y from each xc
        *( (POINT2D *) pts1) = *ptsStart++;
        *( (POINT2D *) pts2) = *ptsEnd++;
        // set z for each
        pts1->z = 0.0f;
        pts2->z = length;
    }

    // Replicate last point in each u-band
    *pts1 = *pts;
    *pts2 = *(pts + numPts);
}

/**************************************************************************\
* ProcessXCPrimLinear
*
* Processes a prim according to evaluator data
*
* - Only valid for colinear xc's (along z)
* - XC's may be identical (extrusion).  If not identical, may have
*   discontinuities at each end.
* - Converts 2D XC pts to 3D pts
*
\**************************************************************************/

void
EVAL::ProcessXCPrimLinear( XC *xcStart, XC *xcEnd, float length )
{
    if( length <= 0.0f )
        // nuttin' to do
        return;

    // Build a vertex control net from 2 xcObj's a distance 'length' apart
    // this will displace the end xcObj a distance 'length' down the z-axis
    SetVertexCtrlPtsXCTranslate( pts, length, xcStart, xcEnd );

    Evaluate( );
}

/**************************************************************************\
* ProcessXCPrimBendSimple
*
* Processes a prim by bending along dir from xcCur
*
* - dir is relative from xc in x-y plane
* - adds C2 continuity at ends
*
\**************************************************************************/

void
EVAL::ProcessXCPrimBendSimple( XC *xcCur, int dir, float radius )
{
    POINT3D *ptsSrc, *ptsDst;
    static float acPts[MAX_XC_PTS+1];
    int ptSetStride = xcCur->numPts + 1; // pt stride for output pts buffer

    // We will be creating 4 cross-sectional control point sets here.

    // Convert 2D pts in xcCur to 3D pts at z=0 for 1st point set
    xcCur->ConvertPtsZ( pts, 0.0f );

    // Calc 4th point set by rotating 1st set as per dir
    ptsDst = pts + 3*ptSetStride;
    RotatePointSet( pts, ptSetStride, 90.0f, dir, radius, ptsDst );

    // angles != 90, hard, cuz not easy to extrude 3rd set from 4th

    // Next, have to figure out ac values.  Need to extend each xc's points
    // into bend to generate ac net.  For circular bend (and later for general
    // case elliptical bend), need to know ac distance from xc for each point.
    // This is based on the point's turn radius - a function of its distance
    // from the 'hinge' of the turn.

    // Can take advantage of symmetry here.  Figure for one xc, good for 2nd.
    // This assumes 90 deg turn.  (also,last point replicated)
    xcCur->CalcArcACValues90( dir, radius, acPts );

    // 2) extrude each point's ac from xcCur (extrusion in +z)
    // apply values to 1st to get 2nd
    // MINUS_Z, cuz subtracts *back* from dir
    ExtrudePointSetDir( pts, ptSetStride, acPts, MINUS_Z,
                                                    pts + ptSetStride );

    // 3) extrude each point's ac from xcEnd (extrusion in -dir)
    ptsSrc = pts + 3*ptSetStride;
    ptsDst = pts + 2*ptSetStride;
    ExtrudePointSetDir( ptsSrc, ptSetStride, acPts, dir, ptsDst );

    Evaluate();
}

/**************************************************************************\
* eval_ProcessXCPrimSingularity
*
* Processes a prim by joining singularity to an xc
*
* - Used for closing or opening the pipe
* - If bOpening is true, starts with singularity, otherwise ends with one
* - the xc side is always in z=0 plane
* - singularity side is radius on either side of xc
* - adds C2 continuity at ends (perpendicular to +z at singularity end)
*
\**************************************************************************/

void
EVAL::ProcessXCPrimSingularity( XC *xcCur, float length, BOOL bOpening )
{
    POINT3D *ptsSing, *ptsXC;
    static float acPts[MAX_XC_PTS+1];
    float zSing; // z-value at singularity
    int ptSetStride = xcCur->numPts + 1; // pt stride for output pts buffer
    int i;
    XC xcSing(xcCur);

    // create singularity xc - which is an extremely scaled-down version
    //  of xcCur (this prevents any end-artifacts, unless of course we were
    //  to zoom it ultra-large).

    xcSing.Scale( .0005f );

    // We will be creating 4 cross-sectional control point sets here.

    if( bOpening ) {
        ptsSing = pts;
        ptsXC = pts + 3*ptSetStride;
    } else {
        ptsSing = pts + 3*ptSetStride;
        ptsXC = pts;
    }

    // Convert 2D pts in xcCur to 3D pts at 'xc' point set
    xcCur->ConvertPtsZ( ptsXC, 0.0f );

    // Set z-value for singularity point set
    zSing = bOpening ? -length : length;
    xcSing.ConvertPtsZ( ptsSing, zSing );

    // The arc control for each point is based on a radius value that is
    //  each xc point's distance from the xc center
    xcCur->CalcArcACValuesByDistance( acPts );

    // Calculate point set near xc
    if( bOpening )
        ExtrudePointSetDir( ptsXC, ptSetStride, acPts, PLUS_Z,
                                                    ptsXC - ptSetStride );
    else
        ExtrudePointSetDir( ptsXC, ptSetStride, acPts, MINUS_Z,
                                                    ptsXC + ptSetStride );

    // Point set near singularity is harder, as the points must generate
    // a curve between the singularity and each xc point
    // No, easier, just scale each point by universal arc controller !
    POINT3D *ptsDst = pts;
    ptsDst = bOpening ? ptsSing + ptSetStride : ptsSing - ptSetStride;
    for( i = 0; i < ptSetStride; i ++, ptsDst++ ) {
        ptsDst->x = EVAL_CIRC_ARC_CONTROL * ptsXC[i].x;
        ptsDst->y = EVAL_CIRC_ARC_CONTROL * ptsXC[i].y;
        ptsDst->z = zSing;
    }

    Evaluate();
}

/**************************************************************************\
* Evaluate
*
* Evaluates the EVAL object
*
* - There may be 1 or more lengthwise sections around an xc
* - u is minor, v major
* - u,t run around circumference, v,s lengthwise
* - Texture maps are 2x2 for each section
* - ! uDiv is per section !
*
\**************************************************************************/

void
EVAL::Evaluate( )
{
    int i;
    POINT3D *ppts = pts;
    TEX_POINT2D *ptexPts = texPts;
    // total # pts in cross-section:
    int xcPointCount = (uOrder-1)*numSections + 1;

    for( i = 0; i < numSections; i ++,
                                       ppts += (uOrder-1),
                                       ptexPts += (TEX_ORDER*TEX_ORDER) ) {

        // map texture coords

        if( bTexture ) {
            glMap2f(GL_MAP2_TEXTURE_COORD_2,
                    0.0f, 1.0f, TDIM, TEX_ORDER,
                    0.0f, 1.0f, TEX_ORDER*TDIM, TEX_ORDER,
                    (GLfloat *) ptexPts );
        }

        // map vertices

        glMap2f(GL_MAP2_VERTEX_3,
               0.0f, 1.0f, VDIM, uOrder,
               0.0f, 1.0f, xcPointCount*VDIM, vOrder,
               (GLfloat *) ppts );

        // evaluate

        glMapGrid2f(uDiv, 0.0f, 1.0f, vDiv, 0.0f, 1.0f);
        glEvalMesh2( GL_FILL, 0, uDiv, 0, vDiv);
    }
}

#if EVAL_DBG
/**************************************************************************\
* DrawPoints
*
* draw control points
*
\**************************************************************************/
static
void DrawPoints( int num, POINT3D *pts )
{
    GLint i;

    // draw green pts for now
    glColor3f(0.0f, 1.0f, 0.0f);
    glPointSize(2);

    glBegin(GL_POINTS);
        for (i = 0; i < num; i++, pts++) {
            glVertex3fv( (GLfloat *) pts );
        }
    glEnd();
}
#endif

/**************************************************************************\
* ExtrudePointSetDir
*
* Extrude a point set back from the current direction
*
* Generates C2 continuity at the supplied point set xc, by generating another
* point set back of the first, using supplied subtraction values.
*
\**************************************************************************/

static void
ExtrudePointSetDir( POINT3D *inPts, int numPts, float *acPts, int dir,
                    POINT3D *outPts )
{
    int i;
    float sign;
    int offset;

    switch( dir ) {
        case PLUS_X:
            offset = 0;
            sign = -1.0f;
            break;
        case MINUS_X:
            offset = 0;
            sign =  1.0f;
            break;
        case PLUS_Y:
            offset = 1;
            sign = -1.0f;
            break;
        case MINUS_Y:
            offset = 1;
            sign =  1.0f;
            break;
        case PLUS_Z:
            offset = 2;
            sign = -1.0f;
            break;
        case MINUS_Z:
            offset = 2;
            sign =  1.0f;
            break;
    }

    for( i = 0; i < numPts; i++, inPts++, outPts++, acPts++ ) {
        *outPts = *inPts;
        ((float *)outPts)[offset] = ((float *)inPts)[offset] + (sign * (*acPts));
    }
}

/**************************************************************************\
* RotatePointSet
*
* Rotate point set by angle, according to dir and radius
*
* - Put points in supplied outPts buffer
*
\**************************************************************************/

static void
RotatePointSet( POINT3D *inPts, int numPts, float angle, int dir,
                      float radius, POINT3D *outPts )
{
    MATRIX matrix1, matrix2, matrix3;
    int i;
    POINT3D rot = {0.0f};
    POINT3D anchor = {0.0f};

    /* dir      rot
       +x       90 y
       -x       -90 y
       +y       -90 x
       -y       90 x
    */

    // convert angle to radians
    //mf: as noted in objects.c, we have to take negative angle to make
    // it work in familiar 'CCW rotation is positive' mode.  The ss_* rotate
    // routines must work in the 'CW is +'ve' mode, as axis pointing at you.
    angle = SS_DEG_TO_RAD(-angle);

    // set axis rotation and anchor point

    switch( dir ) {
        case PLUS_X:
            rot.y = angle;
            anchor.x = radius;
            break;
        case MINUS_X:
            rot.y = -angle;
            anchor.x = -radius;
            break;
        case PLUS_Y:
            rot.x = -angle;
            anchor.y = radius;
            break;
        case MINUS_Y:
            rot.x = angle;
            anchor.y = -radius;
            break;
    }

    // translate anchor point to origin
    ss_matrixIdent( &matrix1 );
    ss_matrixTranslate( &matrix1, -anchor.x, -anchor.y, -anchor.z );

    // rotate
    ss_matrixIdent( &matrix2 );
    ss_matrixRotate( &matrix2, (double) rot.x, rot.y, rot.z );

    // concat these 2
    ss_matrixMult( &matrix3, &matrix2, &matrix1 );

    // translate back
    ss_matrixIdent( &matrix2 );
    ss_matrixTranslate( &matrix2,  anchor.x,  anchor.y,  anchor.z );

    // concat these 2
    ss_matrixMult( &matrix1, &matrix2, &matrix3 );

    for( i = 0; i < numPts; i ++, outPts++, inPts++ ) {
        ss_xformPoint( outPts, inPts, &matrix1 );
    }
}


EVAL.H

#ifndef __eval_h__
#define __eval_h__

#include "sscommon.h"
#include "xc.h"

#define MAX_UORDER      5 // this is per section
#define MAX_VORDER      5
#define MAX_USECTIONS   4
#define MAX_XC_PTS      (MAX_UORDER * MAX_USECTIONS)

#define TEX_ORDER       2
#define EVAL_ARC_ORDER  4
#define EVAL_CYLINDER_ORDER 2
#define EVAL_ELBOW_ORDER    4

// # of components (eg. arcs) to form a complete cross-section
#define EVAL_XC_CIRC_SECTION_COUNT 4

#define EVAL_XC_POINT_COUNT ( (EVAL_ARC_ORDER-1)*4 + 1 )

#define EVAL_CIRC_ARC_CONTROL 0.56f // for r=1

/**************************************************************************\
*
* EVAL class
*
* - Evaluator composed of one or more sections that are evaluated
*   separately with OpenGL evaluators
*
\**************************************************************************/

class EVAL {
public:
    EVAL( BOOL bTexture );
    ~EVAL();
    int         numSections;    // number of cross-sectional sections
    int         uOrder, vOrder;
        // assumed: all sections same order - uOrder is per
        // section; sections share vertex and texture control points
    int         uDiv, vDiv;    // figured out one level up ?
    POINT3D     *pts;          // vertex control points
    // - texture always order 2 for s and t (linear mapping)
    BOOL        bTexture;
    TEX_POINT2D *texPts;       // texture control points

    void        Evaluate(); // evaluate/render the object
    void        SetVertexCtrlPtsXCTranslate( POINT3D *pts, float length,
                                             XC *xcStart, XC *xcEnd );
    void        SetTextureControlPoints( float s_start, float s_end,
                                         float t_start, float t_end );
    void        ProcessXCPrimLinear( XC *xcStart, XC *xcEnd, float length );
    void        ProcessXCPrimBendSimple( XC *xcCur, int dir, float radius );
    void        ProcessXCPrimSingularity( XC *xcCur, float length,
                                          BOOL bOpening );
};

extern void ResetEvaluator( BOOL bTexture );

#endif // __eval_h__


FPIPE.CXX

/******************************Module*Header*******************************\
* Module Name: fpipe.cxx
*
* Flex pipes
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

/* Notes:

    - All Draw routines start with current xc at the beginning, and create
      a new one at the end.  Since it is common to just have 2 xc's for
      each prim, xcCur holds the current xc, and xcEnd is available
      for the draw routine to use as the end xc.
        They also reset xcCur when done
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include <windows.h>

#include "sspipes.h"
#include "fpipe.h"
#include "eval.h"

// defCylNotch shows the absolute notch for the default cylinder,
// given a direction (notch is always along +x axis)

static GLint defCylNotch[NUM_DIRS] =
        { MINUS_Z, PLUS_Z, PLUS_X, PLUS_X, PLUS_X, MINUS_X };

static int GetRelativeDir( int lastDir, int notchVec, int newDir );

/**************************************************************************\
* FLEX_PIPE constructor
*
*
\**************************************************************************/

FLEX_PIPE::FLEX_PIPE( STATE *pState )
: PIPE( pState )
{
    float circ;

    // Create an EVAL object

    nSlices = pState->nSlices;

    // No XC's yet, they will be allocated at pipe Start()
    xcCur = xcEnd = NULL;

    // The EVAL will be used for all pEvals in the pipe, so should be
    // set to hold max. possible # of pts for the pipe.
    pEval = new EVAL( bTexture );

    // Determine pipe tesselation
    // For now, this is based on global tesselation factor

    // Calculate evalDivSize, a reference value for the size of a UxV division.
    // This is used later for calculating texture coords.
    circ = CIRCUMFERENCE( pState->radius );
    evalDivSize = circ / (float) nSlices;
}

/**************************************************************************\
* ~FLEX_PIPE
*
\**************************************************************************/

FLEX_PIPE::~FLEX_PIPE( )
{
    delete pEval;

    // delete any XC's
    if( xcCur != NULL ) {
        if( xcEnd == xcCur )
            xcEnd = NULL; // xcCur and xcEnd can point to same xc !
        delete xcCur;
        xcCur = NULL;
    }

    if( xcEnd != NULL ) {
        delete xcEnd;
        xcEnd = NULL;
    }
}

/**************************************************************************\
* REGULAR_FLEX_PIPE constructor
*
\**************************************************************************/

REGULAR_FLEX_PIPE::REGULAR_FLEX_PIPE( STATE *state )
: FLEX_PIPE( state )
{
    static float turnFactorRange = 0.1f;
    type = TYPE_FLEX_REGULAR;

    // figure out turning factor range (0 for min bends, 1 for max bends)
    float avgTurn = ss_fRand( 0.11f, 0.81f );
    // set min and max turn factors, and clamp to 0..1
    turnFactorMin =
                SS_CLAMP_TO_RANGE( avgTurn - turnFactorRange, 0.0f, 1.0f );
    turnFactorMax =
                SS_CLAMP_TO_RANGE( avgTurn + turnFactorRange, 0.0f, 1.0f );

    // choose straight weighting
    // mf:for now, same as npipe - if stays same, put in pipe
    if( ! ss_iRand( 20 ) )
        weightStraight = ss_iRand2( MAX_WEIGHT_STRAIGHT/4, MAX_WEIGHT_STRAIGHT );
    else
        weightStraight = ss_iRand( 4 );
}

/**************************************************************************\
* TURNING_FLEX_PIPE constructor
*
\**************************************************************************/

TURNING_FLEX_PIPE::TURNING_FLEX_PIPE( STATE *state )
: FLEX_PIPE( state )
{
    type = TYPE_FLEX_TURNING;
}

/******************************Public*Routine******************************\
* SetTexIndex
*
* Set the texture index for this pipe, and calculate texture state dependent
* on texRep values
\**************************************************************************/

void
FLEX_PIPE::SetTexParams( TEXTURE *pTex, IPOINT2D *pTexRep )
{
    if( bTexture ) {
        GLfloat t_size;
        float circ;

        t_start = (GLfloat) pTexRep->y * 1.0f;
        t_end = 0.0f;

        // calc height (t_size) of one rep of texture around circumference
        circ = CIRCUMFERENCE( radius );
        t_size = circ / pTexRep->y;

        // now calc corresponding width of the texture using its x/y ratio
        s_length = t_size / pTex->origAspectRatio;
        s_start = s_end = 0.0f;
// this means we are 'standardizing' the texture size and proportions
// on pipe of radius 1.0 for entire program.  Might want to recalc this on
// a per-pipe basis ?
    }
}

/**************************************************************************\
* ChooseXCProfile
*
* Initialize extruded pipe scheme.  This uses a randomly constructed XC, but it
* remains constant throughout the pipe
*
\**************************************************************************/

void
FLEX_PIPE::ChooseXCProfile()
{
    static float turnFactorRange = 0.1f;
    float baseRadius = pState->radius;

    // initialize evaluator elements:

    pEval->numSections = EVAL_XC_CIRC_SECTION_COUNT;
    pEval->uOrder = EVAL_ARC_ORDER;
//  watch this - maybe should ROUND_UP uDiv
    // set uDiv per section (assumed uDiv multiple of numSections)
    pEval->uDiv = nSlices / pEval->numSections;

    // Setup XC's

    // The xc profile remains constant throughout in this case,
    // so we only need one xc.

    // Choose between elliptical or random cross-sections.  Since elliptical
    //  looks a little better, make it more likely
    if( ss_iRand(4) )  // 3/4 of the time
        xcCur = new ELLIPTICAL_XC( ss_fRand(1.2f, 2.0f) * baseRadius,
                                           baseRadius );
    else
        xcCur = new RANDOM4ARC_XC( ss_fRand(1.5f, 2.0f) * baseRadius );
}

/**************************************************************************\
* REGULAR_FLEX_PIPE::Start
*
* Does startup of extruded-XC pipe drawing scheme
*
\**************************************************************************/

void
REGULAR_FLEX_PIPE::Start()
{
    NODE_ARRAY *nodes = pState->nodes;
    int newDir;

    // Set start position

    if( !SetStartPos() ) {
        status = PIPE_OUT_OF_NODES;
        return;
    }

    // set material

    ChooseMaterial();

    // set XC profile

    ChooseXCProfile();

    // push matrix with zTrans and scene rotation

    glPushMatrix();

    // Translate to current position
    TranslateToCurrentPosition();

    // set random lastDir
    lastDir = ss_iRand( NUM_DIRS );

    // get a new node to draw to
    newDir = ChooseNewDirection();

    if( newDir == DIR_NONE ) {
        // draw like one of those tea-pouring thingies...
        status = PIPE_STUCK;
        DrawTeapot();
        glPopMatrix();
        return;
    } else
        status = PIPE_ACTIVE;

    align_plusz( newDir ); // get us pointed in right direction

    // draw start cap, which will end right at current node
    DrawCap( START_CAP );

    // set initial notch vector, which is just the default notch, since
    // we didn't have to spin the start cap around z
    notchVec = defCylNotch[newDir];

    zTrans = - pState->view.divSize;  // distance back from new node

    UpdateCurrentPosition( newDir );

    lastDir = newDir;
}

/**************************************************************************\
* TURNING_FLEX_PIPE::Start
*
* Does startup of turning extruded-XC pipe drawing scheme
*
\**************************************************************************/

void
TURNING_FLEX_PIPE::Start( )
{
    NODE_ARRAY *nodes = pState->nodes;

    // Set start position

    if( !SetStartPos() ) {
        status = PIPE_OUT_OF_NODES;
        return;
    }

    // Set material

    ChooseMaterial();

    // Set XC profile

    ChooseXCProfile();

    // Push matrix with zTrans and scene rotation

    glPushMatrix();

    // Translate to current position
    TranslateToCurrentPosition();

    // lastDir has to be set to something valid, in case we get stuck right
    // away, cuz Draw() will be called anyways on next iteration, whereupon
    // it finds out it really is stuck, AFTER calling ChooseNewTurnDirection,
    // which requires valid lastDir. (mf: fix this)
    lastDir = ss_iRand( NUM_DIRS );

    // Pick a starting direction by finding a neihgbouring empty node
    int newDir = nodes->FindClearestDirection( &curPos );
    // We don't 'choose' it, or mark it as taken, because ChooseNewDirection
    // will always check it anyways

    if( newDir == DIR_NONE ) {
        // we can't go anywhere
        // draw like one of those tea-pouring thingies...
        status = PIPE_STUCK;
        DrawTeapot();
        glPopMatrix();
        return;
    } else
        status = PIPE_ACTIVE;

    align_plusz( newDir ); // get us pointed in right direction

    // Draw start cap, which will end right at current node
    DrawCap( START_CAP );

    // Set initial notch vector, which is just the default notch, since
    // we didn't have to spin the start cap around z
    notchVec = defCylNotch[newDir];

    zTrans = 0.0f;  // right at current node

    lastDir = newDir;
}

/**************************************************************************\
* REGULAR_FLEX_PIPE::Draw
*
* Draws the pipe using a constant random xc that is extruded
*
* Minimum turn radius can vary, since xc is not symmetrical across any
* of its axes.  Therefore here we draw using a pipe/elbow sequence, so we
* know what direction we're going in before drawing the elbow.  The current
* node is the one we will draw thru next time.  Typically, the actual end
* of the pipe is way back of this node, almost at the previous node, due
* to the variable turn radius
*
\**************************************************************************/

void
REGULAR_FLEX_PIPE::Draw( )
{
    float turnRadius, minTurnRadius;
    float pipeLen, maxPipeLen, minPipeLen;
    int newDir, relDir;
    float maxXCExtent;
    NODE_ARRAY *nodes = pState->nodes;
    float divSize = pState->view.divSize;

    // get new direction

    newDir = ChooseNewDirection();
    if( newDir == DIR_NONE ) {
        status = PIPE_STUCK;
        DrawCap( END_CAP );
        glPopMatrix();
        return;
    }

    // draw pipe, and if turning, joint

    if( newDir != lastDir ) { // turning! - we have to draw joint

        // get relative turn, to figure turn radius

        relDir = GetRelativeDir( lastDir, notchVec, newDir );
        minTurnRadius = xcCur->MinTurnRadius( relDir );

        // now calc maximum straight section we can draw before turning
        // zTrans is current pos'n of end of pipe, from current node ??
        // zTrans is current pos'n of end of pipe, from last node

        maxPipeLen = (-zTrans) - minTurnRadius;

        // there is also a minimum requirement for the length of the straight
        // section, cuz if we turn too soon with a large turn radius, we
        // will swing up too close to the next node, and won't be able to
        // make one or more of the 4 possible turns from that point

        maxXCExtent = xcCur->MaxExtent(); // in case need it again
        minPipeLen = maxXCExtent - (divSize + zTrans);
        if( minPipeLen < 0.0f )
            minPipeLen = 0.0f;

        // Choose length of straight section
        // (we are translating from turnFactor to 'straightFactor' here)
        pipeLen = minPipeLen +
            ss_fRand( 1.0f - turnFactorMax, 1.0f - turnFactorMin ) *
                        (maxPipeLen - minPipeLen);

        // turn radius is whatever's left over:
        turnRadius = maxPipeLen - pipeLen + minTurnRadius;

        // draw straight section
        DrawExtrudedXCObject( pipeLen );
        zTrans += pipeLen; // not necessary for now, since elbow no use

        // draw elbow
        // this updates axes, notchVec to position at end of elbow
        DrawXCElbow( newDir, turnRadius );

        zTrans = -(divSize - turnRadius);  // distance back from node
    }
    else {  // no turn
        // draw a straight pipe through the current node
        // length can vary according to the turnFactors (e.g. for high turn
        // factors draw a short pipe, so next turn can be as big as possible)

        minPipeLen = -zTrans; // brings us just up to last node
        maxPipeLen = minPipeLen + divSize - xcCur->MaxExtent();
        // brings us as close as possible to new node

        pipeLen = minPipeLen +
            ss_fRand( 1.0f - turnFactorMax, 1.0f - turnFactorMin ) *
                        (maxPipeLen - minPipeLen);

        // draw pipe
        DrawExtrudedXCObject( pipeLen );
        zTrans += (-divSize + pipeLen);
    }

    UpdateCurrentPosition( newDir );

    lastDir = newDir;
}

/**************************************************************************\
* DrawTurningXCPipe
*
* Draws the pipe using only turns
*
* - Go straight if no turns available
*
\**************************************************************************/

void
TURNING_FLEX_PIPE::Draw()
{
    float turnRadius;
    int newDir;
    NODE_ARRAY *nodes = pState->nodes;
    float divSize = pState->view.divSize;

    // get new direction

    // pipe may have gotten stuck on Start...(we don't check for this)

    newDir = nodes->ChooseNewTurnDirection( &curPos, lastDir );
    if( newDir == DIR_NONE ) {
        status = PIPE_STUCK;
        DrawCap( END_CAP );
        glPopMatrix();
        return;
    }

    if( newDir == DIR_STRAIGHT ) {
        // No turns available - draw straight section and hope for turns
        //  on next iteration
        DrawExtrudedXCObject( divSize );
        UpdateCurrentPosition( lastDir );
        // ! we have to mark node as taken for this case, since
        // ChooseNewTurnDirection doesn't know whether we're taking the
        // straight option or not
        nodes->NodeVisited( &curPos );
    } else {
        // draw turning pipe

        // since xc is always located right at current node, turn radius
        // stays constant at one node division

        turnRadius = divSize;

        DrawXCElbow( newDir, turnRadius );

        // (zTrans stays at 0)

        // need to update 2 nodes
        UpdateCurrentPosition( lastDir );
        UpdateCurrentPosition( newDir );

        lastDir = newDir;
    }
}

/**************************************************************************\
* DrawXCElbow
*
* Draw elbow from current position through new direction
*
* - Extends current xc around bend
* - Radius of bend is provided - this is distance from xc center to hinge
*   point, along newDir.  e.g. for 'normal pipes', radius=vc->radius
*
\**************************************************************************/

void
FLEX_PIPE::DrawXCElbow( int newDir, float radius )
{
    int relDir;  // 'relative' direction of turn
    float length;

    length = (2.0f * PI * radius) / 4.0f; // average length of elbow

    // calc vDiv, texture params based on length
    CalcEvalLengthParams( length );

    pEval->vOrder = EVAL_ARC_ORDER;

    // convert absolute dir to relative dir
    relDir = GetRelativeDir( lastDir, notchVec, newDir );

    // draw it - call simple bend function

    pEval->ProcessXCPrimBendSimple( xcCur, relDir, radius );

    // set transf. matrix to new position by translating/rotating/translating
    // ! Based on simple elbow
    glTranslatef( 0.0f, 0.0f, radius );
    switch( relDir ) {
        case PLUS_X:
            glRotatef( 90.0f, 0.0f, 1.0f, 0.0f );
            break;
        case MINUS_X:
            glRotatef( -90.0f, 0.0f, 1.0f, 0.0f );
            break;
        case PLUS_Y:
            glRotatef( -90.0f, 1.0f, 0.0f, 0.0f );
            break;
        case MINUS_Y:
            glRotatef( 90.0f, 1.0f, 0.0f, 0.0f );
            break;
    }
    glTranslatef( 0.0f, 0.0f, radius );

    // update notch vector using old function
    notchVec = notchTurn[lastDir][newDir][notchVec];
}

/**************************************************************************\
* DrawExtrudedXCObject
*
* Draws object generated by extruding the current xc
*
* Object starts at xc at origin in z=0 plane, and grows along +z axis
*
\**************************************************************************/

void
FLEX_PIPE::DrawExtrudedXCObject( float length )
{
    // calc vDiv, and texture coord stuff based on length
    // this also calcs pEval texture ctrl pt arrray now
    CalcEvalLengthParams( length );

    // we can fill in some more stuff:
    pEval->vOrder = EVAL_CYLINDER_ORDER;

    // draw it

    pEval->ProcessXCPrimLinear( xcCur, xcCur, length );

    // update state draw axes position
    glTranslatef( 0.0f, 0.0f, length );
}

/**************************************************************************\
* DrawXCCap
*
* Cap the start of the pipe
*
* Needs newDir, so it can orient itself.
* Cap ends at current position with approppriate profile, starts a distance
* 'z' back along newDir.
* Profile is a singularity at start point.
*
\**************************************************************************/

void
FLEX_PIPE::DrawCap( int type )
{
    float radius;
    XC *xc = xcCur;
    BOOL bOpening = (type == START_CAP) ? TRUE : FALSE;
    float length;

    // set radius as average of the bounding box min/max's
    radius = ((xc->xRight - xc->xLeft) + (xc->yTop - xc->yBottom)) / 4.0f;

    length = (2.0f * PI * radius) / 4.0f; // average length of arc

    // calc vDiv, and texture coord stuff based on length
    CalcEvalLengthParams( length );

    // we can fill in some more stuff:
    pEval->vOrder = EVAL_ARC_ORDER;

    // draw it

    pEval->ProcessXCPrimSingularity( xc, radius, bOpening );
}

/**************************************************************************\
* CalcEvalLengthParams
*
* Calculate pEval values that depend on the length of the extruded object
*
* - calculate vDiv, s_start, s_end, and the texture control net array
*
\**************************************************************************/

void
FLEX_PIPE::CalcEvalLengthParams( float length )
{
    pEval->vDiv = (int ) SS_ROUND_UP( length / evalDivSize );

    // calc texture start and end coords

    if( bTexture ) {
        GLfloat s_delta;

        // Don't let s_end overflow : it should stay in range (0..1.0)
        if( s_end > 1.0f )
            s_end -= (int) s_end;

        s_start = s_end;
        s_delta = (length / s_length );
        s_end = s_start + s_delta;

        // the texture ctrl point array can be calc'd here - it is always
        // a simple 2x2 array for each section
        pEval->SetTextureControlPoints( s_start, s_end, t_start, t_end );
    }
}

/**************************************************************************\
*
* GetRelativeDir
*
* Calculates relative direction of turn from lastDir, notchVec, newDir
*
* - Use look up table for now.
* - Relative direction is from xy-plane, and can be +x,-x,+y,-y
* - In current orientation, +z is along lastDir, +x along notchVec
\**************************************************************************/

// this array tells you relative turn
// format: relDir[lastDir][notchVec][newDir]
static int relDir[NUM_DIRS][NUM_DIRS][NUM_DIRS] = {
//      +x      -x      +y      -y      +z      -z (newDir)
// lastDir = +x
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    PLUS_X, MINUS_X,PLUS_Y, MINUS_Y,
        iXX,    iXX,    MINUS_X,PLUS_X, MINUS_Y,PLUS_Y,
        iXX,    iXX,    MINUS_Y,PLUS_Y, PLUS_X, MINUS_X,
        iXX,    iXX,    PLUS_Y, MINUS_Y,MINUS_X,PLUS_X,
// lastDir = -x
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    PLUS_X, MINUS_X,MINUS_Y,PLUS_Y,
        iXX,    iXX,    MINUS_X,PLUS_X, PLUS_Y, MINUS_Y,
        iXX,    iXX,    PLUS_Y, MINUS_Y,PLUS_X, MINUS_X,
        iXX,    iXX,    MINUS_Y,PLUS_Y, MINUS_X,PLUS_X,
// lastDir = +y
        PLUS_X, MINUS_X,iXX,    iXX,    MINUS_Y,PLUS_Y,
        MINUS_X,PLUS_X, iXX,    iXX,    PLUS_Y, MINUS_Y,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        PLUS_Y, MINUS_Y,iXX,    iXX,    PLUS_X, MINUS_X,
        MINUS_Y,PLUS_Y, iXX,    iXX,    MINUS_X,PLUS_X,
// lastDir = -y
        PLUS_X, MINUS_X,iXX,    iXX,    PLUS_Y, MINUS_Y,
        MINUS_X,PLUS_X, iXX,    iXX,    MINUS_Y,PLUS_Y,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        MINUS_Y,PLUS_Y, iXX,    iXX,    PLUS_X, MINUS_X,
        PLUS_Y, MINUS_Y,iXX,    iXX,    MINUS_X,PLUS_X,

// lastDir = +z
        PLUS_X, MINUS_X,PLUS_Y, MINUS_Y,iXX,    iXX,
        MINUS_X,PLUS_X, MINUS_Y,PLUS_Y, iXX,    iXX,
        MINUS_Y,PLUS_Y, PLUS_X, MINUS_X,iXX,    iXX,
        PLUS_Y, MINUS_Y,MINUS_X,PLUS_X, iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
// lastDir = -z
        PLUS_X, MINUS_X,MINUS_Y,PLUS_Y, iXX,    iXX,
        MINUS_X,PLUS_X, PLUS_Y, MINUS_Y,iXX,    iXX,
        PLUS_Y, MINUS_Y,PLUS_X, MINUS_X,iXX,    iXX,
        MINUS_Y,PLUS_Y, MINUS_X,PLUS_X, iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX
};

static int
GetRelativeDir( int lastDir, int notchVec, int newDir )
{
    return( relDir[lastDir][notchVec][newDir] );
}


FPIPE.H

/******************************Module*Header*******************************\
* Module Name: fpipe.h
*
* Flexy pipe stuff
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __fpipe_h__
#define __fpipe_h__

#include "sscommon.h"
#include "sspipes.h"
#include "pipe.h"
#include "state.h"
#include "eval.h"
#include "xc.h"

// continuity types
enum {
    CONT_1 = 0,
    CONT_2
};

// cap types
enum {
    START_CAP = 0,
    END_CAP
};

#define CIRCUMFERENCE( r ) \
    ( 2.0f * PI * ((GLfloat) r) )

// drawing schemes
enum {
    SC_EXTRUDED_XC,
    SC_TURNOMANIA,
    SC_VARIABLE_XC,
    SC_COUNT
};

#define TURNOMANIA_PIPE_COUNT 10

/**************************************************************************\
*
* FLEX_PIPE class
*
* - Pipe has position and direction in node array
*
\**************************************************************************/

class FLEX_PIPE : public PIPE {
public:
    void        SetTexParams( TEXTURE *pTex, IPOINT2D *pTexRep );

protected:
    XC          *xcCur, *xcEnd;  // current xc, end xc
    EVAL        *pEval;
    GLfloat     zTrans;         // pos'n of pipe back along current dir,
                                // from current node
    FLEX_PIPE( STATE *state );
    ~FLEX_PIPE();
    void        ChooseXCProfile();
    void        DrawExtrudedXCObject( float length );
    void        DrawXCElbow( int newDir, float radius );
    void        DrawCap( int type );

private:
    int         nSlices;        // intended # of slices around an xc (based
                                // on tessLevel)
    int         tangent;        // current tangent at curXC (usually PLUS_Z)
    GLfloat     s_start, s_end;
    GLfloat     t_start, t_end;
    GLfloat     s_length;       // length in WC's of texture width
    GLfloat     evalDivSize;    // ~size in WC's of an eval division
    void        CalcEvalLengthParams( float length );
};

class REGULAR_FLEX_PIPE : public FLEX_PIPE {
public:
    float       turnFactorMin;  // describes degree of bend in an elbow
    float       turnFactorMax;  //  [0-1]

    REGULAR_FLEX_PIPE( STATE *state );
    void        Start();
    void        Draw();
};

class TURNING_FLEX_PIPE : public FLEX_PIPE {
public:
    TURNING_FLEX_PIPE( STATE *state );
    void        Start();
    void        Draw();
};

#endif // __fpipe_h__


FSTATE.CXX

/******************************Module*Header*******************************\
* Module Name: fstate.cxx
*
* FLEX_STATE
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include <windows.h>

#include "sspipes.h"
#include "fstate.h"
#include "nstate.h"
#include "npipe.h"
#include "fpipe.h"

/******************************Public*Routine******************************\
* FLEX_STATE constructor
*
\**************************************************************************/

FLEX_STATE::FLEX_STATE( STATE *pState )
{
    bTexture = pState->bTexture;
    Reset();
}

/**************************************************************************\
* Reset
*
* Reset a frame of normal pipes.
*
\**************************************************************************/

void
FLEX_STATE::Reset( )
{
    // Choose a random scheme for each frame

    if( ss_iRand(2) )  // 50/50
        scheme = SC_EXTRUDED_XC;
    else
        scheme = SC_TURNOMANIA;
}

/**************************************************************************\
* OKToUseChase
*
* Determines if we can use chase mode for flex pipes
*
\**************************************************************************/

BOOL
FLEX_STATE::OKToUseChase( )
{
    return scheme != SC_TURNOMANIA;
}

/**************************************************************************\
* NewPipe
*
* Create a new pipe, based on current drawing scheme
*
\**************************************************************************/

PIPE*
FLEX_STATE::NewPipe( STATE *pState )
{
    if( scheme == SC_TURNOMANIA )
        return new TURNING_FLEX_PIPE( pState );
    else
        return new REGULAR_FLEX_PIPE( pState );
}

/**************************************************************************\
* GetMaxPipesPerFrame
*
\**************************************************************************/

int
FLEX_STATE::GetMaxPipesPerFrame( )
{
    if( scheme == SC_TURNOMANIA ) {
        return TURNOMANIA_PIPE_COUNT;
        if( bTexture )
            return TURNOMANIA_PIPE_COUNT / 2;
    } else {
        return bTexture ? NORMAL_TEX_PIPE_COUNT : NORMAL_PIPE_COUNT;
    }
}


FSTATE.H

/******************************Module*Header*******************************\
* Module Name: fstate.h
*
* FLEX_STATE
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __fstate_h__
#define __fstate_h__

#include "sscommon.h"
#include "state.h"
#include "pipe.h"

class PIPE;
class STATE;

class FLEX_STATE {
public:
    int             scheme;         // current drawing scheme (right now this
                                    // is a per-frame thing)
    BOOL            bTexture;       // mf: repetition
    FLEX_STATE( STATE *pState );
    PIPE*           NewPipe( STATE *pState );
    void            Reset();
    BOOL            OKToUseChase();
    int             GetMaxPipesPerFrame();
};

#endif // __fstate_h__


NODE.CXX

/******************************Module*Header*******************************\
* Module Name: node.cxx
*
* Pipes node array
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include <windows.h>

#include "sspipes.h"
#include "node.h"

/**************************************************************************\
*
* NODE_ARRAY constructor
*
\**************************************************************************/

NODE_ARRAY::NODE_ARRAY()
{
    nodes = NULL; // allocated on Resize

    numNodes.x = 0;
    numNodes.y = 0;
    numNodes.z = 0;
}

/**************************************************************************\
*
* NODE_ARRAY destructor
*
\**************************************************************************/

NODE_ARRAY::~NODE_ARRAY( )
{
    if( nodes )
        delete nodes;
}

/**************************************************************************\
*
* Resize
*
\**************************************************************************/

void
NODE_ARRAY::Resize( IPOINT3D *pNewSize )
{
    if( (numNodes.x == pNewSize->x) &&
        (numNodes.y == pNewSize->y) &&
        (numNodes.z == pNewSize->z) )
        return;

    numNodes = *pNewSize;

    int elemCount = numNodes.x * numNodes.y * numNodes.z ;

    if( nodes )
        delete nodes;

    nodes = new Node[elemCount];

    SS_ASSERT( nodes, "NODE_ARRAY::Resize : can't alloc nodes\n" );

    // Reset the node states to empty

    int i;
    Node *pNode = nodes;
    for( i = 0; i < elemCount; i++, pNode++ )
        pNode->MarkAsEmpty();

    // precalculate direction offsets between nodes for speed
    nodeDirInc[PLUS_X] = 1;
    nodeDirInc[MINUS_X] = -1;
    nodeDirInc[PLUS_Y] = numNodes.x;
    nodeDirInc[MINUS_Y] = - nodeDirInc[PLUS_Y];
    nodeDirInc[PLUS_Z] = numNodes.x * numNodes.y;
    nodeDirInc[MINUS_Z] = - nodeDirInc[PLUS_Z];
}

/**************************************************************************\
*
* Reset
*
\**************************************************************************/

void
NODE_ARRAY::Reset( )
{
    int i;
    Node *pNode = nodes;

    // Reset the node states to empty
    for( i = 0; i < (numNodes.x)*(numNodes.y)*(numNodes.z); i++, pNode++ )
        pNode->MarkAsEmpty();
}

/**************************************************************************\
*
* GetNodeCount
*
\**************************************************************************/

void
NODE_ARRAY::GetNodeCount( IPOINT3D *count )
{
    *count = numNodes;
}

/**************************************************************************\
*
* ChooseRandomDirection
*
* Choose randomnly among the possible directions.  The likelyhood of going
* straight is controlled by weighting it.
*
\**************************************************************************/

int
NODE_ARRAY::ChooseRandomDirection( IPOINT3D *pos, int dir, int weightStraight )
{
    Node *nNode[NUM_DIRS];
    int numEmpty, newDir;
    int choice;
    Node *straightNode = NULL;
    int emptyDirs[NUM_DIRS];

    SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
            "NODE_ARRAY::ChooseRandomDirection: invalid dir\n" );

    // Get the neigbouring nodes
    GetNeighbours( pos, nNode );

    // Get node in straight direction if necessary
    if( weightStraight && nNode[dir] && nNode[dir]->IsEmpty() ) {
        straightNode = nNode[dir];
        // if maximum weight, choose and return
        if( weightStraight == MAX_WEIGHT_STRAIGHT ) {
            straightNode->MarkAsTaken();
            return dir;
        }
    } else
        weightStraight = 0;

    // Get directions of possible turns
    numEmpty = GetEmptyTurnNeighbours( nNode, emptyDirs, dir );

    // Make a random choice
    if( (choice = (weightStraight + numEmpty)) == 0 )
        return DIR_NONE;
    choice = ss_iRand( choice );

    if( choice < weightStraight ) {
        straightNode->MarkAsTaken();
        return dir;
    } else {
        // choose one of the turns
        newDir = emptyDirs[choice - weightStraight];
        nNode[newDir]->MarkAsTaken();
        return newDir;
    }
}

/**************************************************************************\
*
* ChoosePreferredDirection
*
* Choose randomnly from one of the supplied preferred directions.  If none
* of these are available, then try and choose any empty direction
*
\**************************************************************************/

int
NODE_ARRAY::ChoosePreferredDirection( IPOINT3D *pos, int dir, int *prefDirs,
                                      int nPrefDirs )
{
    Node *nNode[NUM_DIRS];
    int numEmpty, newDir;
    int emptyDirs[NUM_DIRS];
    int *pEmptyPrefDirs;
    int i, j;

    SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
            "NODE_ARRAY::ChoosePreferredDirection : invalid dir\n" );

    // Get the neigbouring nodes
    GetNeighbours( pos, nNode );

    // Create list of directions that are both preferred and empty

    pEmptyPrefDirs = emptyDirs;
    numEmpty = 0;

    for( i = 0, j = 0; (i < NUM_DIRS) && (j < nPrefDirs); i++ ) {
        if( i == *prefDirs ) {
            prefDirs++;
            j++;
            if( nNode[i] && nNode[i]->IsEmpty() ) {
                // add it to list
                *pEmptyPrefDirs++ = i;
                numEmpty++;
            }
        }
    }

    // if no empty preferred dirs, then any empty dirs become preferred

    if( !numEmpty ) {
        numEmpty = GetEmptyNeighbours( nNode, emptyDirs );
        if( numEmpty == 0 )
            return DIR_NONE;
    }

    // Pick a random dir from the empty set

    newDir = emptyDirs[ss_iRand( numEmpty )];
    nNode[newDir]->MarkAsTaken();
    return newDir;
}

/**************************************************************************\
*
* FindClearestDirection
*
* Finds the direction with the most empty nodes in a line 'searchRadius'
* long.  Does not mark any nodes as taken.
*
\**************************************************************************/

int
NODE_ARRAY::FindClearestDirection( IPOINT3D *pos )
{
    static Node *neighbNode[NUM_DIRS];
    static int emptyDirs[NUM_DIRS];
    int nEmpty, newDir;
    int maxEmpty = 0;
    int searchRadius = 3;
    int count = 0;
    int i;

    // Get ptrs to neighbour nodes

    GetNeighbours( pos, neighbNode );

    // find empty nodes in each direction

    for( i = 0; i < NUM_DIRS; i ++ ) {
        if( neighbNode[i] && neighbNode[i]->IsEmpty() )
        {
            // find number of contiguous empty nodes along this direction
            nEmpty = GetEmptyNeighboursAlongDir( pos, i, searchRadius );
            if( nEmpty > maxEmpty ) {
                // we have a new winner
                count = 0;
                maxEmpty = nEmpty;
                emptyDirs[count++] = i;
            }
            else if( nEmpty == maxEmpty ) {
                // tied with current max
                emptyDirs[count++] = i;
            }
        }
    }

    if( count == 0 )
        return DIR_NONE;

    // randomnly choose a direction
    newDir = emptyDirs[ss_iRand( count )];

    return newDir;
}
/**************************************************************************\
*
* ChooseNewTurnDirection
*
* Choose a direction to turn
*
* This requires finding a pair of nodes to turn through.  The first node
* is in the direction of the turn from the current node, and the second node
* is at right angles to this at the end position.  The prim will not draw
* through the first node, but may sweep close to it, so we have to mark it
* as taken.
*
* - if next node is free, but there are no turns available, return
*   DIR_STRAIGHT, so the caller can decide what to do in this case
* - The turn possibilities are based on the orientation of the current xc, with
*   4 relative directions to seek turns in.
\**************************************************************************/

int
NODE_ARRAY::ChooseNewTurnDirection( IPOINT3D *pos, int dir )
{
    int turns[NUM_DIRS], nTurns;
    IPOINT3D nextPos;
    int newDir;
    Node *nextNode;

    SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
            "NODE_ARRAY::ChooseNewTurnDirection : invalid dir\n" );

    // First, check if next node along current dir is empty

    if( ! GetNextNodePos( pos, &nextPos, dir ) )
        return DIR_NONE; // node out of bounds or not empty

    // Ok, the next node is free - check the 4 possible turns from here

    nTurns = GetBestPossibleTurns( &nextPos, dir, turns );
    if( nTurns == 0 )
        return DIR_STRAIGHT; // nowhere to turn, but could go straight

    // randomnly choose one of the possible turns
    newDir = turns[ ss_iRand( nTurns ) ];

    SS_ASSERT( (newDir >= 0) && (newDir < NUM_DIRS),
            "NODE_ARRAY::ChooseNewTurnDirection : invalid newDir\n" );

    // mark taken nodes

    nextNode = GetNode( &nextPos );
    nextNode->MarkAsTaken();

    nextNode = GetNextNode( &nextPos, newDir );
    nextNode->MarkAsTaken();

    return newDir;
}

/**************************************************************************\
*
* GetBestPossibleTurns
*
* From supplied direction and position, figure out which of 4 possible
* directions are best to turn in.
*
* Turns that have the greatest number of empty nodes after the turn are the
* best, since a pipe is less likely to hit a dead end in this case.
* - We only check as far as 'searchRadius' nodes along each dir.
* - Return direction indices of best possible turns in turnDirs, and return
*   count of these turns in fuction return value.
*
\**************************************************************************/

int
NODE_ARRAY::GetBestPossibleTurns( IPOINT3D *pos, int dir, int *turnDirs )
{
    Node *neighbNode[NUM_DIRS]; // ptrs to 6 neighbour nodes
    int i, count = 0;
    BOOL check[NUM_DIRS] = {TRUE, TRUE, TRUE, TRUE, TRUE, TRUE};
    int nEmpty, maxEmpty = 0;
    int searchRadius = 2;

    SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
            "NODE_ARRAY::GetBestPossibleTurns : invalid dir\n" );

    GetNeighbours( pos, neighbNode );

    switch( dir ) {
        case PLUS_X:
        case MINUS_X:
            check[PLUS_X] = FALSE;
            check[MINUS_X] = FALSE;
            break;
        case PLUS_Y:
        case MINUS_Y:
            check[PLUS_Y] = FALSE;
            check[MINUS_Y] = FALSE;
            break;
        case PLUS_Z:
        case MINUS_Z:
            check[PLUS_Z] = FALSE;
            check[MINUS_Z] = FALSE;
            break;
    }

    // check approppriate directions
    for( i = 0; i < NUM_DIRS; i ++ ) {
        if( check[i] && neighbNode[i] && neighbNode[i]->IsEmpty() )
        {
            // find number of contiguous empty nodes along this direction
            nEmpty = GetEmptyNeighboursAlongDir( pos, i, searchRadius );
            if( nEmpty > maxEmpty ) {
                // we have a new winner
                count = 0;
                maxEmpty = nEmpty;
                turnDirs[count++] = i;
            }
            else if( nEmpty == maxEmpty ) {
                // tied with current max
                turnDirs[count++] = i;
            }
        }
    }

    return count;
}

/**************************************************************************\
*
* GetNeighbours
*
* Get neigbour nodes relative to supplied position
*
*       - get addresses of the neigbour nodes,
*         and put them in supplied matrix
*       - boundary hits are returned as NULL
*
\**************************************************************************/

void
NODE_ARRAY::GetNeighbours( IPOINT3D *pos, Node **nNode )
{
    Node *centerNode = GetNode( pos );

    nNode[PLUS_X]  = pos->x == (numNodes.x - 1) ? NULL :
                                            centerNode + nodeDirInc[PLUS_X];
    nNode[PLUS_Y]  = pos->y == (numNodes.y - 1) ? NULL :
                                            centerNode + nodeDirInc[PLUS_Y];
    nNode[PLUS_Z]  = pos->z == (numNodes.z - 1) ? NULL :
                                            centerNode + nodeDirInc[PLUS_Z];

    nNode[MINUS_X] = pos->x == 0 ? NULL : centerNode + nodeDirInc[MINUS_X];
    nNode[MINUS_Y] = pos->y == 0 ? NULL : centerNode + nodeDirInc[MINUS_Y];
    nNode[MINUS_Z] = pos->z == 0 ? NULL : centerNode + nodeDirInc[MINUS_Z];
}

/**************************************************************************\
*
* NodeVisited
*
* Mark the node as non-empty
*
\**************************************************************************/

void
NODE_ARRAY::NodeVisited( IPOINT3D *pos )
{
    (GetNode( pos ))->MarkAsTaken();
}

/**************************************************************************\
*
* GetNode
*
* Get ptr to node from position
*
\**************************************************************************/

Node *
NODE_ARRAY::GetNode( IPOINT3D *pos )
{
    return nodes +
           pos->x +
           pos->y * numNodes.x +
           pos->z * numNodes.x * numNodes.y;
}

/**************************************************************************\
*
* GetNextNode
*
* Get ptr to next node from pos and dir
*
\**************************************************************************/

Node *
NODE_ARRAY::GetNextNode( IPOINT3D *pos, int dir )
{
    Node *curNode = GetNode( pos );

    SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
            "NODE_ARRAY::GetNextNode : invalid dir\n" );

    switch( dir ) {
        case PLUS_X:
            return( pos->x == (numNodes.x - 1) ? NULL :
                              curNode + nodeDirInc[PLUS_X]);
            break;
        case MINUS_X:
            return( pos->x == 0 ? NULL :
                              curNode + nodeDirInc[MINUS_X]);
            break;
        case PLUS_Y:
            return( pos->y == (numNodes.y - 1) ? NULL :
                              curNode + nodeDirInc[PLUS_Y]);
            break;
        case MINUS_Y:
            return( pos->y == 0 ? NULL :
                              curNode + nodeDirInc[MINUS_Y]);
            break;
        case PLUS_Z:
            return( pos->z == (numNodes.z - 1) ? NULL :
                              curNode + nodeDirInc[PLUS_Z]);
            break;
        case MINUS_Z:
            return( pos->z == 0 ? NULL :
                              curNode + nodeDirInc[MINUS_Z]);
            break;
        default:
            return NULL;
    }
}

/**************************************************************************\
*
* GetNextNodePos
*
* Get position of next node from curPos and lastDir
*
* Returns FALSE if boundary hit or node empty
*
\**************************************************************************/

BOOL
NODE_ARRAY::GetNextNodePos( IPOINT3D *curPos, IPOINT3D *nextPos, int dir )
{
    static Node *neighbNode[NUM_DIRS]; // ptrs to 6 neighbour nodes

    SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
            "NODE_ARRAY::GetNextNodePos : invalid dir\n" );

// don't need to get all neighbours, just one in next direction
    GetNeighbours( curPos, neighbNode );

    *nextPos = *curPos;

    // bail if boundary hit or node not empty
    if( (neighbNode[dir] == NULL) || !neighbNode[dir]->IsEmpty() )
        return FALSE;

    switch( dir ) {
        case PLUS_X:
            nextPos->x = curPos->x + 1;
            break;

        case MINUS_X:
            nextPos->x = curPos->x - 1;
            break;

        case PLUS_Y:
            nextPos->y = curPos->y + 1;
            break;

        case MINUS_Y:
            nextPos->y = curPos->y - 1;
            break;

        case PLUS_Z:
            nextPos->z = curPos->z + 1;
            break;

        case MINUS_Z:
            nextPos->z = curPos->z - 1;
            break;
    }

    return TRUE;
}

/**************************************************************************\
*
*    GetEmptyNeighbours()
*       - get list of direction indices of empty node neighbours,
*         and put them in supplied matrix
*       - return number of empty node neighbours
*
\**************************************************************************/

int
NODE_ARRAY::GetEmptyNeighbours( Node **nNode, int *nEmpty )
{
    int i, count = 0;

    for( i = 0; i < NUM_DIRS; i ++ ) {
        if( nNode[i] && nNode[i]->IsEmpty() )
            nEmpty[count++] = i;
    }
    return count;
}

/**************************************************************************\
*
*    GetEmptyTurnNeighbours()
*       - get list of direction indices of empty node neighbours,
*         and put them in supplied matrix
*       - don't include going straight
*       - return number of empty node neighbours
*
\**************************************************************************/

int
NODE_ARRAY::GetEmptyTurnNeighbours( Node **nNode, int *nEmpty, int lastDir )
{
    int i, count = 0;

    for( i = 0; i < NUM_DIRS; i ++ ) {
        if( nNode[i] && nNode[i]->IsEmpty() ) {
            if( i == lastDir )
                continue;
            nEmpty[count++] = i;
        }
    }
    return count;
}

/**************************************************************************\
* GetEmptyNeighboursAlongDir
*
* Sort of like above, but just gets one neigbour according to supplied dir
*
* Given a position and direction, find out how many contiguous empty nodes
* there are in that direction.
* - Can limit search with searchRadius parameter
* - Return contiguous empty node count
\**************************************************************************/

int
NODE_ARRAY::GetEmptyNeighboursAlongDir( IPOINT3D *pos, int dir,
                            int searchRadius )
{
    Node *curNode = GetNode( pos );
    int nodeStride;
    int maxSearch;
    int count = 0;

    SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
            "NODE_ARRAY::GetEmptyNeighboursAlongDir : invalid dir\n" );

    nodeStride = nodeDirInc[dir];

    switch( dir ) {
        case PLUS_X:
            maxSearch = numNodes.x - pos->x - 1;
            break;
        case MINUS_X:
            maxSearch = pos->x;
            break;
        case PLUS_Y:
            maxSearch = numNodes.y - pos->y - 1;
            break;
        case MINUS_Y:
            maxSearch = pos->y;
            break;
        case PLUS_Z:
            maxSearch = numNodes.z - pos->z - 1;
            break;
        case MINUS_Z:
            maxSearch = pos->z;
            break;
    }

    if( searchRadius > maxSearch )
        searchRadius = maxSearch;

    if( !searchRadius )
        return 0;

    while( searchRadius-- ) {
        curNode += nodeStride;
        if( ! curNode->IsEmpty() )
            return count;
        count++;
    }
    return count;
}

/**************************************************************************\
* FindRandomEmptyNode
*
* - Search for an empty node to start drawing
* - Return position of empty node in supplied pos ptr
* - Returns FALSE if couldn't find a node
* - Marks node as taken (mf: renam fn to ChooseEmptyNode ?
*
\**************************************************************************/

// If random search takes longer than twice the total number
// of nodes, give up the random search.  There may not be any
// empty nodes.

#define INFINITE_LOOP   (2 * NUM_NODE * NUM_NODE * NUM_NODE)

BOOL
NODE_ARRAY::FindRandomEmptyNode( IPOINT3D *pos )
{
    int infLoopDetect = 0;

    while( TRUE ) {

        // Pick a random node.

        pos->x = ss_iRand( numNodes.x );
        pos->y = ss_iRand( numNodes.y );
        pos->z = ss_iRand( numNodes.z );

        // If its empty, we're done.

        if( GetNode(pos)->IsEmpty() ) {
            NodeVisited( pos );
            return TRUE;
        } else {
            // Watch out for infinite loops!  After trying for
            // awhile, give up on the random search and look
            // for the first empty node.

            if ( infLoopDetect++ > INFINITE_LOOP ) {

                // Search for first empty node.

                for ( pos->x = 0; pos->x < numNodes.x; pos->x++ )
                    for ( pos->y = 0; pos->y < numNodes.y; pos->y++ )
                        for ( pos->z = 0; pos->z < numNodes.z; pos->z++ )
                            if( GetNode(pos)->IsEmpty() ) {
                                NodeVisited( pos );
                                return TRUE;
                            }

                // There are no more empty nodes.
                // Reset the pipes and exit.

                return FALSE;
            }
        }
    }
}

/**************************************************************************\
* FindRandomEmptyNode2D
*
* - Like FindRandomEmptyNode, but limits search to a 2d plane of the supplied
*   box.
*
\**************************************************************************/

#define INFINITE_LOOP   (2 * NUM_NODE * NUM_NODE * NUM_NODE)
#define MIN_VAL 1
#define MAX_VAL 0

BOOL
NODE_ARRAY::FindRandomEmptyNode2D( IPOINT3D *pos, int plane, int *box )
{
    int *newx, *newy;
    int *xDim, *yDim;

    switch( plane ) {
        case PLUS_X:
        case MINUS_X:
            pos->x = box[plane];
            newx = &pos->z;
            newy = &pos->y;
            xDim = &box[PLUS_Z];
            yDim = &box[PLUS_Y];
            break;
        case PLUS_Y:
        case MINUS_Y:
            pos->y = box[plane];
            newx = &pos->x;
            newy = &pos->z;
            xDim = &box[PLUS_X];
            yDim = &box[PLUS_Z];
            break;
        case PLUS_Z:
        case MINUS_Z:
            newx = &pos->x;
            newy = &pos->y;
            pos->z = box[plane];
            xDim = &box[PLUS_X];
            yDim = &box[PLUS_Y];
            break;
    }

    int infLoop = 2 * (xDim[MAX_VAL] - xDim[MIN_VAL] + 1) *
                      (yDim[MAX_VAL] - yDim[MIN_VAL] + 1);
    int infLoopDetect = 0;

    while( TRUE ) {

        // Pick a random node.

        *newx = ss_iRand2( xDim[MIN_VAL], xDim[MAX_VAL] );
        *newy = ss_iRand2( yDim[MIN_VAL], yDim[MAX_VAL] );

        // If its empty, we're done.

        if( GetNode(pos)->IsEmpty() ) {
            NodeVisited( pos );
            return TRUE;
        } else {
            // Watch out for infinite loops!  After trying for
            // awhile, give up on the random search and look
            // for the first empty node.

            if ( ++infLoopDetect > infLoop ) {

                // Do linear search for first empty node.

                for ( *newx = xDim[MIN_VAL]; *newx <= xDim[MAX_VAL]; (*newx)++ )
                    for ( *newy = yDim[MIN_VAL]; *newy <= yDim[MAX_VAL]; (*newy)++ )
                        if( GetNode(pos)->IsEmpty() ) {
                            NodeVisited( pos );
                            return TRUE;
                        }

                // There are no empty nodes in this plane.
                return FALSE;
            }
        }
    }
}

/**************************************************************************\
* TakeClosestEmptyNode
*
* - Search for an empty node closest to supplied node position
* - Returns FALSE if couldn't find a node
* - Marks node as taken
* - not completely opimized - if when dilating the box, a side gets
*   clamped against the node array, this side will continue to be searched
*
\**************************************************************************/

static void
DilateBox( int *box, IPOINT3D *bounds );

BOOL
NODE_ARRAY::TakeClosestEmptyNode( IPOINT3D *newPos, IPOINT3D *pos )
{
    static int searchRadius = SS_MAX( numNodes.x, numNodes.y ) / 3;

    // easy out
    if( GetNode(pos)->IsEmpty() ) {
        NodeVisited( pos );
        *newPos = *pos;
        return TRUE;
    }

    int box[NUM_DIRS] = {pos->x, pos->x, pos->y, pos->y, pos->z, pos->z};
    int clip[NUM_DIRS] = {0};

    // do a random search on successively larger search boxes
    for( int i = 0; i < searchRadius; i++ ) {
        // Increase box size
        DilateBox( box, &numNodes );
        // start looking in random 2D face of the box
        int dir = ss_iRand( NUM_DIRS );
        for( int j = 0; j < NUM_DIRS; j++, dir = (++dir == NUM_DIRS) ? 0 : dir ) {
            if( FindRandomEmptyNode2D( newPos, dir, box ) )
                return TRUE;
        }
    }

    // nothing nearby - grab a random one
    return FindRandomEmptyNode( newPos );
}

/**************************************************************************\
* DilateBox
*
* - Increase box radius without exceeding bounds
*
\**************************************************************************/

static void
DilateBox( int *box, IPOINT3D *bounds )
{
    int *min = (int *) &box[MINUS_X];
    int *max = (int *) &box[PLUS_X];
    int *boundMax = (int *) bounds;
    // boundMin always 0

    for( int i = 0; i < 3; i ++, min+=2, max+=2, boundMax++ ) {
        if( *min > 0 )
            (*min)--;
        if( *max < (*boundMax - 1) )
            (*max)++;
    }
}


NODE.H

/******************************Module*Header*******************************\
* Module Name: node.h
*
* Node stuff
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __node_h__
#define __node_h__

#include "sscommon.h"
#include "sspipes.h"

#define NUM_NODE (NUM_DIV - 1)  // num nodes in longest dimension

// maximum weighting of going straight for direction choosing functions
#define MAX_WEIGHT_STRAIGHT 100

// Node class

class Node {
public:
    void        MarkAsTaken() { empty = FALSE; }
    void        MarkAsEmpty() { empty = TRUE; }
    BOOL        IsEmpty() { return empty; }
private:
    GLboolean   empty;
};

/**************************************************************************\
*
* Node array class
*
* - 3d array of nodes
* - Functions to access node neighbours, query if taken or not, etc.
* - Not only is this the node array, but a set of methods that operates on it
*
\**************************************************************************/

class NODE_ARRAY {
public:
    NODE_ARRAY();
    ~NODE_ARRAY();
    void        Resize( IPOINT3D *pNewSize ); // new array size
    void        Reset();       // set all nodes to empty
    int         ChooseRandomDirection( IPOINT3D *pos, int dir, int weight );
    int         ChoosePreferredDirection( IPOINT3D *pos, int dir, int *prefDirs,
                                          int nPrefDirs );
    int         ChooseNewTurnDirection( IPOINT3D *pos, int dir );
    int         FindClearestDirection( IPOINT3D *pos );
    int         GetBestPossibleTurns( IPOINT3D *pos, int dir, int *turnDirs );
    BOOL        FindRandomEmptyNode( IPOINT3D *ip3dEmpty );
    BOOL        FindRandomEmptyNode2D( IPOINT3D *pos, int plane, int *box );
    BOOL        TakeClosestEmptyNode( IPOINT3D *newPos, IPOINT3D *pos );
    void        NodeVisited( IPOINT3D *pos );
    void        GetNodeCount( IPOINT3D *pos );
private:
    Node        *nodes;         // ptr to node array
    int         lock;          // semaphore lock for >1 drawing pipes
    IPOINT3D    numNodes;      // x,y,z dimensions of node array
    int         nodeDirInc[NUM_DIRS]; // array offset between nodes for each dir
    void        GetNeighbours( IPOINT3D *pos, Node **nNode );
    Node*       GetNode( IPOINT3D *pos );
    Node*       GetNextNode( IPOINT3D *pos, int dir );
    BOOL        GetNextNodePos( IPOINT3D *curPos, IPOINT3D *nextPos, int dir );
    int         GetEmptyNeighbours( Node **nNode, int *nEmpty );
    int         GetEmptyTurnNeighbours( Node **nNode, int *nEmpty, int lastDir );
    int         GetEmptyNeighboursAlongDir( IPOINT3D *pos, int dir,
                                    int searchRadius );
};

#endif // __node_h__


NPIPE.CXX

/******************************Module*Header*******************************\
* Module Name: npipe.cxx
*
* Normal pipes code
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include <windows.h>

#include "sspipes.h"
#include "npipe.h"
#include "state.h"

static void align_notch( int newDir, int notch );
static void align_plusy( int oldDir, int newDir );

// defCylNotch shows where the notch for the default cylinder will be,
//  in absolute coords, once we do an align_plusz

static GLint defCylNotch[NUM_DIRS] =
        { PLUS_Y, PLUS_Y, MINUS_Z, PLUS_Z, PLUS_Y, PLUS_Y };

/**************************************************************************\
* NORMAL_PIPE constructor
*
*
\**************************************************************************/

NORMAL_PIPE::NORMAL_PIPE( STATE *pState )
: PIPE( pState )
{

    type = TYPE_NORMAL;
    pNState = pState->pNState;

    // choose weighting of going straight
    if( ! ss_iRand( 20 ) )
        weightStraight = ss_iRand2( MAX_WEIGHT_STRAIGHT/4, MAX_WEIGHT_STRAIGHT );
    else
        weightStraight = 1 + ss_iRand( 4 );
}

/**************************************************************************\
* Start
*
* Start drawing a new normal pipe
*
* - Draw a start cap and short pipe in new direction
*
\**************************************************************************/

void
NORMAL_PIPE::Start( )
{
    int newDir;

    // Set start position

    if( !SetStartPos() ) {
        status = PIPE_OUT_OF_NODES;
        return;
    }

    // set a material

    ChooseMaterial();

    // push matrix that has initial zTrans and rotation
    glPushMatrix();

    // Translate to current position
    TranslateToCurrentPosition();

    // Pick a random lastDir
    lastDir = ss_iRand( NUM_DIRS );

    newDir = ChooseNewDirection();

    if( newDir == DIR_NONE ) {
        // pipe is stuck at the start node, draw something
        status = PIPE_STUCK;
        DrawTeapot();
        glPopMatrix();
        return;
    } else
        status = PIPE_ACTIVE;

    // set initial notch vector
    notchVec = defCylNotch[newDir];

    DrawStartCap( newDir );

    // move ahead 1.0*r to draw pipe
    glTranslatef( 0.0f, 0.0f, radius );

    // draw short pipe
    align_notch( newDir, notchVec );
    pNState->shortPipe->Draw();

    glPopMatrix();

    UpdateCurrentPosition( newDir );

    lastDir = newDir;
}

/**************************************************************************\
* Draw
*
* - if turning, draws a joint and a short cylinder, otherwise
*   draws a long cylinder.
* - the 'current node' is set as the one we draw thru the NEXT
*   time around.
*
\**************************************************************************/

void
NORMAL_PIPE::Draw()
{
    int newDir;

    newDir = ChooseNewDirection();

    if( newDir == DIR_NONE ) {  // no empty nodes - nowhere to go
        DrawEndCap();
        status = PIPE_STUCK;
        return;
    }

    // push matrix that has initial zTrans and rotation
    glPushMatrix();

    // Translate to current position
    TranslateToCurrentPosition();

    // draw joint if necessary, and pipe

    if( newDir != lastDir ) { // turning! - we have to draw joint
        DrawJoint( newDir );

        // draw short pipe
        align_notch( newDir, notchVec );
        pNState->shortPipe->Draw();
    }
    else {  // no turn
        // draw long pipe, from point 1.0*r back
        align_plusz( newDir );
        align_notch( newDir, notchVec );
        glTranslatef( 0.0f, 0.0f, -radius );
        pNState->longPipe->Draw();
    }

    glPopMatrix();

    UpdateCurrentPosition( newDir );

    lastDir = newDir;
}

/**************************************************************************\
* DrawStartCap
*
* Cap the start of the pipe with a ball
*
\**************************************************************************/

void
NORMAL_PIPE::DrawStartCap( int newDir )
{
    if( bTexture ) {
        align_plusz( newDir );
        pNState->ballCap->Draw();
    }
    else {
        // draw big ball in default orientation
        pNState->bigBall->Draw();
        align_plusz( newDir );
    }
}

/**************************************************************************\
* DrawEndCap():
*
* - Draws a ball, used to cap end of a pipe
*
\**************************************************************************/

void
NORMAL_PIPE::DrawEndCap( )
{
    glPushMatrix();

    // Translate to current position
    TranslateToCurrentPosition();

    if( bTexture ) {
        glPushMatrix();
        align_plusz( lastDir );
        align_notch( lastDir, notchVec );
        pNState->ballCap->Draw();
        glPopMatrix();
    }
    else
        pNState->bigBall->Draw();

    glPopMatrix();
}

/**************************************************************************\
* ChooseElbow
*
* - Decides which elbow to draw
* - The beginning of each elbow is aligned along +y, and we have
*   to choose the one with the notch in correct position
* - The 'primary' start notch (elbow[0]) is in same direction as
*   newDir, and successive elbows rotate this notch CCW around +y
*
\**************************************************************************/

// this array supplies the sequence of elbow notch vectors, given
//  oldDir and newDir  (0's are don't cares)
// it is also used to determine the ending notch of an elbow
static GLint notchElbDir[NUM_DIRS][NUM_DIRS][4] = {
// oldDir = +x
        iXX,            iXX,            iXX,            iXX,
        iXX,            iXX,            iXX,            iXX,
        PLUS_Y,         MINUS_Z,        MINUS_Y,        PLUS_Z,
        MINUS_Y,        PLUS_Z,         PLUS_Y,         MINUS_Z,
        PLUS_Z,         PLUS_Y,         MINUS_Z,        MINUS_Y,
        MINUS_Z,        MINUS_Y,        PLUS_Z,         PLUS_Y,
// oldDir = -x
        iXX,            iXX,            iXX,            iXX,
        iXX,            iXX,            iXX,            iXX,
        PLUS_Y,         PLUS_Z,         MINUS_Y,        MINUS_Z,
        MINUS_Y,        MINUS_Z,        PLUS_Y,         PLUS_Z,
        PLUS_Z,         MINUS_Y,        MINUS_Z,        PLUS_Y,
        MINUS_Z,        PLUS_Y,         PLUS_Z,         MINUS_Y,
// oldDir = +y
        PLUS_X,         PLUS_Z,         MINUS_X,        MINUS_Z,
        MINUS_X,        MINUS_Z,        PLUS_X,         PLUS_Z,
        iXX,            iXX,            iXX,            iXX,
        iXX,            iXX,            iXX,            iXX,
        PLUS_Z,         MINUS_X,        MINUS_Z,        PLUS_X,
        MINUS_Z,        PLUS_X,         PLUS_Z,         MINUS_X,
// oldDir = -y
        PLUS_X,         MINUS_Z,        MINUS_X,        PLUS_Z,
        MINUS_X,        PLUS_Z,         PLUS_X,         MINUS_Z,
        iXX,            iXX,            iXX,            iXX,
        iXX,            iXX,            iXX,            iXX,
        PLUS_Z,         PLUS_X,         MINUS_Z,        MINUS_X,
        MINUS_Z,        MINUS_X,        PLUS_Z,         PLUS_X,
// oldDir = +z
        PLUS_X,         MINUS_Y,        MINUS_X,        PLUS_Y,
        MINUS_X,        PLUS_Y,         PLUS_X,         MINUS_Y,
        PLUS_Y,         PLUS_X,         MINUS_Y,        MINUS_X,
        MINUS_Y,        MINUS_X,        PLUS_Y,         PLUS_X,
        iXX,            iXX,            iXX,            iXX,
        iXX,            iXX,            iXX,            iXX,
// oldDir = -z
        PLUS_X,         PLUS_Y,         MINUS_X,        MINUS_Y,
        MINUS_X,        MINUS_Y,        PLUS_X,         PLUS_Y,
        PLUS_Y,         MINUS_X,        MINUS_Y,        PLUS_X,
        MINUS_Y,        PLUS_X,         PLUS_Y,         MINUS_X,
        iXX,            iXX,            iXX,            iXX,
        iXX,            iXX,            iXX,            iXX
};

GLint
NORMAL_PIPE::ChooseElbow( int oldDir, int newDir )
{
    int i;

    // precomputed table supplies correct elbow orientation
    for( i = 0; i < 4; i ++ ) {
        if( notchElbDir[oldDir][newDir][i] == notchVec )
            return i;
    }
    // we shouldn't arrive here
    return -1;
}

/**************************************************************************\
* DrawJoint
*
* Draw a joint between 2 pipes
*
\**************************************************************************/

void
NORMAL_PIPE::DrawJoint( int newDir )
{
    int jointType;
    int iBend;

    jointType = pNState->ChooseJointType();
#if PIPES_DEBUG
    if( newDir == oppositeDir[lastDir] )
        printf( "Warning: opposite dir chosen!\n" );
#endif

    switch( jointType ) {
      case BALL_JOINT:
            if( bTexture ) {
                // use special texture-friendly ballJoints

                align_plusz( newDir );
                glPushMatrix();

                align_plusy( lastDir, newDir );

                // translate forward 1.0*r along +z to get set for drawing elbow
                glTranslatef( 0.0f, 0.0f, radius );
                // decide which elbow orientation to use
                iBend = ChooseElbow( lastDir, newDir );
                pNState->ballJoints[iBend]->Draw();

                glPopMatrix();
            }
            else {
                // draw big ball in default orientation
                pNState->bigBall->Draw();
                align_plusz( newDir );
            }
            // move ahead 1.0*r to draw pipe
            glTranslatef( 0.0f, 0.0f, radius );
        break;

      case ELBOW_JOINT:
            align_plusz( newDir );

            // the align_plusy() here will screw up our notch calcs, so
            //  we push-pop

            glPushMatrix();

            align_plusy( lastDir, newDir );

            // translate forward 1.0*r along +z to get set for drawing elbow
            glTranslatef( 0.0f, 0.0f, radius );
            // decide which elbow orientation to use
            iBend = ChooseElbow( lastDir, newDir );
            if( iBend == -1 ) {
#if PIPES_DEBUG
                printf( "ChooseElbow() screwed up\n" );
#endif
                iBend = 0; // recover
            }
            pNState->elbows[iBend]->Draw();

            glPopMatrix();

            glTranslatef( 0.0f, 0.0f, radius );
        break;

      default:
            // Horrors! It's the teapot!
            DrawTeapot();
            align_plusz( newDir );
            // move ahead 1.0*r to draw pipe
            glTranslatef( 0.0f, 0.0f, radius );
        }

        // update the current notch vector
        notchVec = notchTurn[lastDir][newDir][notchVec];
#if PIPES_DEBUG
        if( notchVec == iXX )
            printf( "notchTurn gave bad value\n" );
#endif
}

/**************************************************************************\
* Geometry functions
\**************************************************************************/

static float RotZ[NUM_DIRS][NUM_DIRS] = {
          0.0f,   0.0f,  90.0f,  90.0f,  90.0f, -90.0f,
          0.0f,   0.0f, -90.0f, -90.0f, -90.0f,  90.0f,
        180.0f, 180.0f,   0.0f,   0.0f, 180.0f, 180.0f,
          0.0f,   0.0f,   0.0f,   0.0f,   0.0f,   0.0f,
        -90.0f,  90.0f,   0.0f, 180.0f,   0.0f,   0.0f,
         90.0f, -90.0f, 180.0f,   0.0f,   0.0f,   0.0f };

/*-----------------------------------------------------------------------
|                                                                       |
|    align_plusy( int lastDir, int newDir )                             |
|       - Assuming +z axis is already aligned with newDir, align        |
|         +y axis BACK along lastDir                                    |
|                                                                       |
-----------------------------------------------------------------------*/

static void
align_plusy( int oldDir, int newDir )
{
    GLfloat rotz;

    rotz = RotZ[oldDir][newDir];
    glRotatef( rotz, 0.0f, 0.0f, 1.0f );
}

// given a dir, determine how much to rotate cylinder around z to match notches
// format is [newDir][notchVec]

static GLfloat alignNotchRot[NUM_DIRS][NUM_DIRS] = {
        fXX,    fXX,    0.0f,   180.0f,  90.0f, -90.0f,
        fXX,    fXX,    0.0f,   180.0f,  -90.0f, 90.0f,
        -90.0f, 90.0f,  fXX,    fXX,    180.0f, 0.0f,
        -90.0f, 90.0f,  fXX,    fXX,    0.0f,   180.0f,
        -90.0f, 90.0f,  0.0f,   180.0f, fXX,    fXX,
        90.0f,  -90.0f, 0.0f,   180.0f, fXX,    fXX
};

/*-----------------------------------------------------------------------
|                                                                       |
|    align_notch( int newDir )                                          |
|       - a cylinder is notched, and we have to line this up            |
|         with the previous primitive's notch which is maintained as    |
|         notchVec.                                                     |
|       - this adds a rotation around z to achieve this                 |
|                                                                       |
-----------------------------------------------------------------------*/

static void
align_notch( int newDir, int notch )
{
    GLfloat rotz;
    GLint curNotch;

    // figure out where notch is presently after +z alignment
    curNotch = defCylNotch[newDir];
    // (don't need this now we have lut)

    // look up rotation value in table
    rotz = alignNotchRot[newDir][notch];
#if PIPES_DEBUG
    if( rotz == fXX ) {
        printf( "align_notch(): unexpected value\n" );
        return;
    }
#endif

    if( rotz != 0.0f )
        glRotatef( rotz, 0.0f, 0.0f, 1.0f );
}


NPIPE.H

/******************************Module*Header*******************************\
* Module Name: npipe.h
*
* Node stuff
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __npipe_h__
#define __npipe_h__

#include "sscommon.h"
#include "state.h"
#include "pipe.h"

class NORMAL_STATE;

class NORMAL_PIPE: public PIPE {
private:
public:
    NORMAL_STATE      *pNState;

    NORMAL_PIPE( STATE *state );
    void        Start();
    GLint       ChooseElbow( int oldDir, int newDir);
    void        DrawJoint( int newDir );
    void        Draw( ); //mf: could take param to draw n sections
    void        DrawStartCap( int newDir );
    void        DrawEndCap();
};

#endif // __npipe_h__


NSTATE.CXX

/******************************Module*Header*******************************\
* Module Name: nstate.cxx
*
* NORMAL_STATE
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include <windows.h>

#include "sspipes.h"
#include "nstate.h"
#include "objects.h"
#include "dialog.h"

/******************************Public*Routine******************************\
* NORMAL_STATE constructor
*
\**************************************************************************/

NORMAL_STATE::NORMAL_STATE( STATE *pState )
{
    // init joint types from dialog settings

    bCycleJointStyles = 0;

    switch( ulJointType ) {
        case JOINT_ELBOW:
            jointStyle = ELBOWS;
            break;
        case JOINT_BALL:
            jointStyle = BALLS;
            break;
        case JOINT_MIXED:
            jointStyle = EITHER;
            break;
        case JOINT_CYCLE:
            bCycleJointStyles = 1;
            jointStyle = EITHER;
            break;
        default:
            break;
    }

    // Build the objects

    BuildObjects( pState->radius, pState->view.divSize, pState->nSlices,
                  pState->bTexture, &pState->texRep[0] );
}

/******************************Public*Routine******************************\
* NORMAL_STATE destructor
*
* Some of the objects are always created, so don't have to check if they
* exist. Others may be NULL.
\**************************************************************************/

NORMAL_STATE::~NORMAL_STATE( )
{
    delete shortPipe;
    delete longPipe;
    delete ballCap;

    for( int i = 0; i < 4; i ++ ) {
        delete elbows[i];
        if( ballJoints[i] )
            delete ballJoints[i];
    }

    if( bigBall )
        delete bigBall;
}

/**************************************************************************\
* BuildObjects
*
* - Build all the pipe primitives
* - Different prims are built based on bTexture flag
*
\**************************************************************************/
void
NORMAL_STATE::BuildObjects( float radius, float divSize, int nSlices,
                            BOOL bTexture, IPOINT2D *texRep )
{
    OBJECT_BUILD_INFO *pBuildInfo = new OBJECT_BUILD_INFO;
    pBuildInfo->radius = radius;
    pBuildInfo->divSize = divSize;
    pBuildInfo->nSlices = nSlices;
    pBuildInfo->bTexture = bTexture;

    if( bTexture ) {
        pBuildInfo->texRep = texRep;

        // Calc s texture intersection values
        float s_max = (float) texRep->y;
        float s_trans =  s_max * 2.0f * radius / divSize;

        // Build short and long pipes
        shortPipe = new PIPE_OBJECT( pBuildInfo, divSize - 2*radius,
                                     s_trans, s_max );
        longPipe = new PIPE_OBJECT( pBuildInfo, divSize, 0.0f, s_max );

        // Build elbow and ball joints
        for( int i = 0; i < 4; i ++ ) {
            elbows[i] = new ELBOW_OBJECT( pBuildInfo, i, 0.0f, s_trans );
            ballJoints[i] = new BALLJOINT_OBJECT( pBuildInfo, i, 0.0f, s_trans );
        }

        bigBall = NULL;

        // Build end cap

        float s_start = - texRep->x * (ROOT_TWO - 1.0f) * radius / divSize;
        float s_end = texRep->x * (2.0f + (ROOT_TWO - 1.0f)) * radius / divSize;
        // calc compensation value, to prevent negative s coords
        float comp_s = (int) ( - s_start ) + 1.0f;
        s_start += comp_s;
        s_end += comp_s;
        ballCap = new SPHERE_OBJECT( pBuildInfo, ROOT_TWO*radius, s_start, s_end );

    } else {
        // Build pipes, elbows
        shortPipe = new PIPE_OBJECT( pBuildInfo, divSize - 2*radius );
        longPipe = new PIPE_OBJECT( pBuildInfo, divSize );
        for( int i = 0; i < 4; i ++ ) {
            elbows[i] = new ELBOW_OBJECT( pBuildInfo, i );
            ballJoints[i] = NULL;
        }

        // Build just one ball joint when not texturing.  It is slightly
        // larger than standard ball joint, to prevent any pipe edges from
        // 'sticking' out of the ball.
        bigBall = new SPHERE_OBJECT( pBuildInfo,
                     ROOT_TWO*radius / ((float) cos(PI/nSlices)) );

        // build end cap
        ballCap = new SPHERE_OBJECT( pBuildInfo, ROOT_TWO*radius );
    }
}

/**************************************************************************\
* Reset
*
* Reset frame attributes for normal pipes.
*
\**************************************************************************/

void
NORMAL_STATE::Reset( )
{
    // Set the joint style
    if( bCycleJointStyles ) {
        if( ++(jointStyle) >= NUM_JOINT_STYLES )
            jointStyle = 0;
    }
}

/*-----------------------------------------------------------------------
|                                                                       |
|    ChooseJointType                                                    |
|       - Decides which type of joint to draw                           |
|                                                                       |
-----------------------------------------------------------------------*/

#define BLUE_MOON 153

int
NORMAL_STATE::ChooseJointType( )
{
    switch( jointStyle ) {
        case ELBOWS:
            return ELBOW_JOINT;
        case BALLS:
            return BALL_JOINT;
        case EITHER:
            // draw a teapot once in a blue moon
            if( ss_iRand(1000) == BLUE_MOON )
                return( TEAPOT );
        default:
            // otherwise an elbow or a ball (1/3 ball)
            if( !ss_iRand(3) )
                return BALL_JOINT;
            else
                return ELBOW_JOINT;
    }
}


NSTATE.H

/******************************Module*Header*******************************\
* Module Name: nstate.h
*
* NORMAL_STATE
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __nstate_h__
#define __nstate_h__

#include "sscommon.h"
#include "objects.h"
#include "state.h"

#define NORMAL_PIPE_COUNT       5
#define NORMAL_TEX_PIPE_COUNT   3

#define NUM_JOINT_STYLES        3

// styles for pipe joints
enum {
    ELBOWS = 0,
    BALLS,
    EITHER
};

// joint types
enum {
    ELBOW_JOINT = 0,
    BALL_JOINT
};

// shchemes for choosing directions
enum {
    NORMAL_SCHEME_CHOOSE_DIR_RANDOM,
    NORMAL_SCHEME_CHOOSE_DIR_TURN,
    NORMAL_SCHEME_CHOOSE_DIR_STRAIGHT
};

// this used for traditional pipe drawing

class PIPE_OBJECT;
class ELBOW_OBJECT;
class SPHERE_OBJECT;
class BALLJOINT_OBJECT;

class STATE;

class NORMAL_STATE {
public:
    int             jointStyle;
    int             bCycleJointStyles;

    PIPE_OBJECT     *shortPipe;
    PIPE_OBJECT     *longPipe;
    ELBOW_OBJECT    *elbows[4];
    SPHERE_OBJECT   *ballCap;
    SPHERE_OBJECT   *bigBall;
    BALLJOINT_OBJECT  *ballJoints[4];

    NORMAL_STATE( STATE *pState );
    ~NORMAL_STATE();
    void            Reset();
//    int             GetMaxPipesPerFrame();
    void            BuildObjects( float radius, float divSize, int nSlices,
                                  BOOL bTexture, IPOINT2D *pTexRep );
    int             ChooseJointType();
};

#endif // __nstate_h__


OBJECTS.CXX

/******************************Module*Header*******************************\
* Module Name: objects.cxx
*
* Creates command lists for pipe primitive objects
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <windows.h>
#include <GL/gl.h>
#include "sscommon.h"
#include "objects.h"
#include "sspipes.h"

#define ROOT_TWO 1.414213562373f

/**************************************************************************\
* OBJECT constructor
*
\**************************************************************************/
OBJECT::OBJECT( )
{
    listNum = glGenLists(1);
}

/**************************************************************************\
* OBJECT destructor
*
\**************************************************************************/
OBJECT::~OBJECT( )
{
    glDeleteLists( listNum, 1 );
}

/**************************************************************************\
* Draw
*
* - Draw the object by calling its display list
*
\**************************************************************************/
void
OBJECT::Draw( )
{
    glCallList( listNum );
}

/**************************************************************************\
* PIPE_OBJECT constructors
*
\**************************************************************************/
PIPE_OBJECT::PIPE_OBJECT( OBJECT_BUILD_INFO *pBuildInfo, float len )
{
    Build( pBuildInfo, len, 0.0f, 0.0f );
}

PIPE_OBJECT::PIPE_OBJECT( OBJECT_BUILD_INFO *pBuildInfo, float len, float s_start, float s_end )
{
    Build( pBuildInfo, len, s_start, s_end );
}

/**************************************************************************\
* ELBOW_OBJECT constructors
*
\**************************************************************************/
ELBOW_OBJECT::ELBOW_OBJECT( OBJECT_BUILD_INFO *pBuildInfo, int notch )
{
    Build( pBuildInfo, notch, 0.0f, 0.0f );
}

ELBOW_OBJECT::ELBOW_OBJECT( OBJECT_BUILD_INFO *pBuildInfo, int notch, float s_start, float s_end )
{
    Build( pBuildInfo, notch, s_start, s_end );
}

/**************************************************************************\
* BALLJOINT_OBJECT constructor
*
\**************************************************************************/
BALLJOINT_OBJECT::BALLJOINT_OBJECT( OBJECT_BUILD_INFO *pBuildInfo, int notch, float s_start, float s_end )
{
    Build( pBuildInfo, notch, s_start, s_end );
}

/**************************************************************************\
* SPHERE_OBJECT constructors
*
\**************************************************************************/
SPHERE_OBJECT::SPHERE_OBJECT( OBJECT_BUILD_INFO *pBuildInfo, float radius )
{
    Build( pBuildInfo, radius, 0.0f, 0.0f );
}

SPHERE_OBJECT::SPHERE_OBJECT( OBJECT_BUILD_INFO *pBuildInfo, float radius, float s_start, float s_end )
{
    Build( pBuildInfo, radius, s_start, s_end );
}

// rotate circle around x-axis, with edge attached to anchor

static void TransformCircle(
    float angle,
    POINT3D *inPoint,
    POINT3D *outPoint,
    GLint num,
    POINT3D *anchor )
{
    MATRIX matrix1, matrix2, matrix3;
    int i;

    // translate anchor point to origin
    ss_matrixIdent( &matrix1 );
    ss_matrixTranslate( &matrix1, -anchor->x, -anchor->y, -anchor->z );

    // rotate by angle, cw around x-axis
    ss_matrixIdent( &matrix2 );
    ss_matrixRotate( &matrix2, (double) -angle, 0.0, 0.0 );

    // concat these 2
    ss_matrixMult( &matrix3, &matrix2, &matrix1 );

    // translate back
    ss_matrixIdent( &matrix2 );
    ss_matrixTranslate( &matrix2,  anchor->x,  anchor->y,  anchor->z );

    // concat these 2
    ss_matrixMult( &matrix1, &matrix2, &matrix3 );

    // transform all the points, + center
    for( i = 0; i < num; i ++, outPoint++, inPoint++ ) {
        ss_xformPoint( outPoint, inPoint, &matrix1 );
    }
}

static void CalcNormals( POINT3D *p, POINT3D *n, POINT3D *center,
                         int num )
{
    int i;

    for( i = 0; i < num; i ++, n++, p++ ) {
        n->x = p->x - center->x;
        n->y = p->y - center->y;
        n->z = p->z - center->z;
        ss_normalizeNorm( n );
    }
}

/*----------------------------------------------------------------------\
|    MakeQuadStrip()                                                    |
|       - builds quadstrip between 2 rows of points. pA points to one   |
|         row of points, and pB to the next rotated row.  Because       |
|         the rotation has previously been defined CCW around the       |
|         x-axis, using an A-B sequence will result in CCW quads        |
|                                                                       |
\----------------------------------------------------------------------*/
static void MakeQuadStrip
(
    POINT3D *pA,
    POINT3D *pB,
    POINT3D *nA,
    POINT3D *nB,
    BOOL    bTexture,
    GLfloat *tex_s,
    GLfloat *tex_t,
    GLint slices
)
{
    GLint i;

    glBegin( GL_QUAD_STRIP );

    for( i = 0; i < slices; i ++ ) {
        glNormal3fv( (GLfloat *) nA++ );
        if( bTexture )
            glTexCoord2f( tex_s[0], *tex_t );
        glVertex3fv( (GLfloat *) pA++ );
        glNormal3fv( (GLfloat *) nB++ );
        if( bTexture )
            glTexCoord2f( tex_s[1], *tex_t++ );
        glVertex3fv( (GLfloat *) pB++ );
    }

    glEnd();
}

#define CACHE_SIZE      100

/*----------------------------------------------------------------------\
|    BuildElbow()                                                      |
|       - builds elbows, by rotating a circle in the y=r plane          |
|         centered at (0,r,-r), CW around the x-axis at anchor pt.      |
|         (r = radius of the circle)                                    |
|       - rotation is 90.0 degrees, ending at circle in z=0 plane,      |
|         centered at origin.                                           |
|       - in order to 'mate' texture coords with the cylinders          |
|         generated with glu, we generate 4 elbows, each corresponding  |
|         to the 4 possible CW 90 degree orientations of the start point|
|         for each circle.                                              |
|       - We call this start point the 'notch'.  If we characterize     |
|         each notch by the axis it points down in the starting and     |
|         ending circles of the elbow, then we get the following axis   |
|         pairs for our 4 notches:                                      |
|               - +z,+y                                                 |
|               - +x,+x                                                 |
|               - -z,-y                                                 |
|               - -x,-x                                                 |
|         Since the start of the elbow always points down +y, the 4     |
|         start notches give all possible 90.0 degree orientations      |
|         around y-axis.                                                |
|       - We can keep track of the current 'notch' vector to provide    |
|         proper mating between primitives.                             |
|       - Each circle of points is described CW from the start point,   |
|         assuming looking down the +y axis(+y direction).              |
|       - texture 's' starts at 0.0, and goes to 2.0*r/divSize at       |
|         end of the elbow.  (Then a short pipe would start with this   |
|         's', and run it to 1.0).                                      |
|                                                                       |
\----------------------------------------------------------------------*/
void
ELBOW_OBJECT::Build( OBJECT_BUILD_INFO *pBuildInfo, int notch, float s_start, float s_end )
{
    GLint   stacks, slices;
    GLfloat angle, startAng;
    GLint numPoints;
    GLfloat s_delta;
    POINT3D pi[CACHE_SIZE]; // initial row of points + center
    POINT3D p0[CACHE_SIZE]; // 2 rows of points
    POINT3D p1[CACHE_SIZE];
    POINT3D n0[CACHE_SIZE]; // 2 rows of normals
    POINT3D n1[CACHE_SIZE];
    GLfloat tex_t[CACHE_SIZE];// 't' texture coords
    GLfloat tex_s[2];  // 's' texture coords
    POINT3D center;  // center of circle
    POINT3D anchor;  // where circle is anchored
    POINT3D *pA, *pB, *nA, *nB;
    int i;
    IPOINT2D *texRep = pBuildInfo->texRep;
    GLfloat radius = pBuildInfo->radius;
    BOOL    bTexture = pBuildInfo->bTexture;

    slices = pBuildInfo->nSlices;
    stacks = slices / 2;

    if (slices >= CACHE_SIZE) slices = CACHE_SIZE-1;
    if (stacks >= CACHE_SIZE) stacks = CACHE_SIZE-1;

    s_delta = s_end - s_start;

    if( bTexture ) {
        // calculate 't' texture coords
        for( i = 0; i <= slices; i ++ ) {
            tex_t[i] = (GLfloat) i * texRep->y / slices;
        }
    }

    numPoints = slices + 1;

        // starting angle increment 90.0 degrees each time
        startAng = notch * PI / 2;

        // calc initial circle of points for circle centered at 0,r,-r
        // points start at (0,r,0), and rotate circle CCW

        for( i = 0; i <= slices; i ++ ) {
            angle = startAng + (2 * PI * i / slices);
            pi[i].x = radius * (float) sin(angle);
            pi[i].y = radius;
            // translate z by -r, cuz these cos calcs are for circle at origin
            pi[i].z = radius * (float) cos(angle) - radius;
        }

        // center point, tacked onto end of circle of points
        pi[i].x =  0.0f;
        pi[i].y =  radius;
        pi[i].z = -radius;
        center = pi[i];

        // anchor point
        anchor.x = anchor.z = 0.0f;
        anchor.y = radius;

        // calculate initial normals
        CalcNormals( pi, n0, &center, numPoints );

        // initial 's' texture coordinate
        tex_s[0] = s_start;

        // setup pointers
        pA = pi;
        pB = p0;
        nA = n0;
        nB = n1;

        // now iterate throught the stacks

        glNewList(listNum, GL_COMPILE);

        for( i = 1; i <= stacks; i ++ ) {
            // ! this angle must be negative, for correct vertex orientation !
            angle = - 0.5f * PI * i / stacks;

            // transform to get next circle of points + center
            TransformCircle( angle, pi, pB, numPoints+1, &anchor );

            // calculate normals
            center = pB[numPoints];
            CalcNormals( pB, nB, &center, numPoints );

            // calculate next 's' texture coord
            tex_s[1] = (GLfloat) s_start + s_delta * i / stacks;

            // now we've got points and normals, ready to be quadstrip'd
            MakeQuadStrip( pA, pB, nA, nB, bTexture, tex_s, tex_t, numPoints );

            // reset pointers
            pA = pB;
            nA = nB;
            pB = (pB == p0) ? p1 : p0;
            nB = (nB == n0) ? n1 : n0;
            tex_s[0] = tex_s[1];
        }

        glEndList();
}

/*----------------------------------------------------------------------\
|    BuildBallJoint()                                                  |
|       - These are very similar to the elbows, in that the starting    |
|         and ending positions are almost identical.   The difference   |
|         here is that the circles in the sweep describe a sphere as    |
|         they are rotated.                                             |
|                                                                       |
\----------------------------------------------------------------------*/
void
BALLJOINT_OBJECT::Build( OBJECT_BUILD_INFO *pBuildInfo, int notch,
                         float s_start, float s_end )
{
    GLfloat ballRadius;
    GLfloat angle, delta_a, startAng, theta;
    GLint numPoints;
    GLfloat s_delta;
    POINT3D pi0[CACHE_SIZE]; // 2 circles of untransformed points
    POINT3D pi1[CACHE_SIZE];
    POINT3D p0[CACHE_SIZE]; // 2 rows of transformed points
    POINT3D p1[CACHE_SIZE];
    POINT3D n0[CACHE_SIZE]; // 2 rows of normals
    POINT3D n1[CACHE_SIZE];
    float   r[CACHE_SIZE];  // radii of the circles
    GLfloat tex_t[CACHE_SIZE];// 't' texture coords
    GLfloat tex_s[2];  // 's' texture coords
    POINT3D center;  // center of circle
    POINT3D anchor;  // where circle is anchored
    POINT3D *pA, *pB, *nA, *nB;
    int i, k;
    GLint   stacks, slices;
    IPOINT2D *texRep = pBuildInfo->texRep;
    GLfloat radius = pBuildInfo->radius;
    BOOL    bTexture = pBuildInfo->bTexture;

    slices = pBuildInfo->nSlices;
    stacks = slices;

    if (slices >= CACHE_SIZE) slices = CACHE_SIZE-1;
    if (stacks >= CACHE_SIZE) stacks = CACHE_SIZE-1;

    // calculate the radii for each circle in the sweep, where
    // r[i] = y = sin(angle)/r

    angle = PI / 4;  // first radius always at 45.0 degrees
    delta_a = (PI / 2.0f) / stacks;

    ballRadius = ROOT_TWO * radius;
    for( i = 0; i <= stacks; i ++, angle += delta_a ) {
        r[i] = (float) sin(angle) * ballRadius;
    }

    if( bTexture ) {
        // calculate 't' texture coords
        for( i = 0; i <= slices; i ++ ) {
            tex_t[i] = (GLfloat) i * texRep->y / slices;
        }
    }

    s_delta = s_end - s_start;

    numPoints = slices + 1;

    // unlike the elbow, the center for the ball joint is constant
    center.x = center.y = 0.0f;
    center.z = -radius;

        // starting angle along circle, increment 90.0 degrees each time
        startAng = notch * PI / 2;

        // calc initial circle of points for circle centered at 0,r,-r
        // points start at (0,r,0), and rotate circle CCW

        delta_a = 2 * PI / slices;
        for( i = 0, theta = startAng; i <= slices; i ++, theta += delta_a ) {
            pi0[i].x = r[0] * (float) sin(theta);
            pi0[i].y = radius;
            // translate z by -r, cuz these cos calcs are for circle at origin
            pi0[i].z = r[0] * (float) cos(theta) - r[0];
        }

        // anchor point
        anchor.x = anchor.z = 0.0f;
        anchor.y = radius;

        // calculate initial normals
        CalcNormals( pi0, n0, &center, numPoints );

        // initial 's' texture coordinate
        tex_s[0] = s_start;

        // setup pointers
        pA = pi0; // circles of transformed points
        pB = p0;
        nA = n0; // circles of transformed normals
        nB = n1;

        // now iterate throught the stacks

        glNewList(listNum, GL_COMPILE);

        for( i = 1; i <= stacks; i ++ ) {
            // ! this angle must be negative, for correct vertex orientation !
            angle = - 0.5f * PI * i / stacks;

            // calc the next circle of untransformed points into pi1[]

            for( k = 0, theta = startAng; k <= slices; k ++, theta+=delta_a ) {
                pi1[k].x = r[i] * (float) sin(theta);
                pi1[k].y = radius;
                // translate z by -r, cuz calcs are for circle at origin
                pi1[k].z = r[i] * (float) cos(theta) - r[i];
            }

            // rotate cirle of points to next position
            TransformCircle( angle, pi1, pB, numPoints, &anchor );

            // calculate normals
            CalcNormals( pB, nB, &center, numPoints );

            // calculate next 's' texture coord
            tex_s[1] = (GLfloat) s_start + s_delta * i / stacks;

            // now we've got points and normals, ready to be quadstrip'd
            MakeQuadStrip( pA, pB, nA, nB, bTexture, tex_s, tex_t, numPoints );

            // reset pointers
            pA = pB;
            nA = nB;
            pB = (pB == p0) ? p1 : p0;
            nB = (nB == n0) ? n1 : n0;
            tex_s[0] = tex_s[1];
        }

        glEndList();
}

// 'glu' routines

#ifdef _EXTENSIONS_
#define COS cosf
#define SIN sinf
#define SQRT sqrtf
#else
#define COS cos
#define SIN sin
#define SQRT sqrt
#endif

/**************************************************************************\
* BuildCylinder
*
\**************************************************************************/
void
PIPE_OBJECT::Build( OBJECT_BUILD_INFO *pBuildInfo, float length, float s_start,
                            float s_end )
{
    GLint   stacks, slices;
    GLint   i,j;
    GLfloat sinCache[CACHE_SIZE];
    GLfloat cosCache[CACHE_SIZE];
    GLfloat sinCache2[CACHE_SIZE];
    GLfloat cosCache2[CACHE_SIZE];
    GLfloat angle;
    GLfloat zLow, zHigh;
    GLfloat zNormal;
    GLfloat s_delta;
    IPOINT2D *texRep = pBuildInfo->texRep;
    GLfloat radius = pBuildInfo->radius;
    BOOL    bTexture = pBuildInfo->bTexture;

    slices = pBuildInfo->nSlices;
    stacks = (int) SS_ROUND_UP( (length/pBuildInfo->divSize) * (float)slices) ;

    if (slices >= CACHE_SIZE) slices = CACHE_SIZE-1;
    if (stacks >= CACHE_SIZE) stacks = CACHE_SIZE-1;

    zNormal = 0.0f;

    s_delta = s_end - s_start;

    for (i = 0; i < slices; i++) {
        angle = 2 * PI * i / slices;
        sinCache2[i] = (float) SIN(angle);
        cosCache2[i] = (float) COS(angle);
        sinCache[i] = (float) SIN(angle);
        cosCache[i] = (float) COS(angle);
    }

    sinCache[slices] = sinCache[0];
    cosCache[slices] = cosCache[0];
    sinCache2[slices] = sinCache2[0];
    cosCache2[slices] = cosCache2[0];

    glNewList(listNum, GL_COMPILE);

        for (j = 0; j < stacks; j++) {
            zLow = j * length / stacks;
            zHigh = (j + 1) * length / stacks;

            glBegin(GL_QUAD_STRIP);
            for (i = 0; i <= slices; i++) {
                    glNormal3f(sinCache2[i], cosCache2[i], zNormal);
                    if (bTexture) {
                        glTexCoord2f( (float) s_start + s_delta * j / stacks,
                                      (float) i * texRep->y / slices );
                    }
                    glVertex3f(radius * sinCache[i],
                            radius * cosCache[i], zLow);
                    if (bTexture) {
                        glTexCoord2f( (float) s_start + s_delta*(j+1) / stacks,
                                      (float) i * texRep->y / slices );
                    }
                    glVertex3f(radius * sinCache[i],
                            radius * cosCache[i], zHigh);
            }
            glEnd();
        }

    glEndList();
}

/*----------------------------------------------------------------------\
|    pipeSphere()                                                       |
|                                                                       |
\----------------------------------------------------------------------*/
void
SPHERE_OBJECT::Build( OBJECT_BUILD_INFO *pBuildInfo, GLfloat radius,
                      GLfloat s_start, GLfloat s_end)
{
    GLint i,j;
    GLfloat sinCache1a[CACHE_SIZE];
    GLfloat cosCache1a[CACHE_SIZE];
    GLfloat sinCache2a[CACHE_SIZE];
    GLfloat cosCache2a[CACHE_SIZE];
    GLfloat sinCache1b[CACHE_SIZE];
    GLfloat cosCache1b[CACHE_SIZE];
    GLfloat sinCache2b[CACHE_SIZE];
    GLfloat cosCache2b[CACHE_SIZE];
    GLfloat angle;
    GLfloat zLow, zHigh;
    GLfloat sintemp1, sintemp2, sintemp3, sintemp4;
    GLfloat costemp3, costemp4;
    GLfloat s_delta;
    GLint start, finish;
    GLint   stacks, slices;
    BOOL    bTexture = pBuildInfo->bTexture;
    IPOINT2D *texRep = pBuildInfo->texRep;

    slices = pBuildInfo->nSlices;
    stacks = slices;
    if (slices >= CACHE_SIZE) slices = CACHE_SIZE-1;
    if (stacks >= CACHE_SIZE) stacks = CACHE_SIZE-1;

    // invert sense of s - it seems the glu sphere is not built similarly
    // to the glu cylinder
    // (this probably means stacks don't grow along +z - check it out)
    s_delta = s_start;
    s_start = s_end;
    s_end = s_delta;

    s_delta = s_end - s_start;

    /* Cache is the vertex locations cache */
    /* Cache2 is the various normals at the vertices themselves */

    for (i = 0; i < slices; i++) {
        angle = 2 * PI * i / slices;
        sinCache1a[i] = (float) SIN(angle);
        cosCache1a[i] = (float) COS(angle);
            sinCache2a[i] = sinCache1a[i];
            cosCache2a[i] = cosCache1a[i];
    }

    for (j = 0; j <= stacks; j++) {
        angle = PI * j / stacks;
                sinCache2b[j] = (float) SIN(angle);
                cosCache2b[j] = (float) COS(angle);
        sinCache1b[j] = radius * (float) SIN(angle);
        cosCache1b[j] = radius * (float) COS(angle);
    }
    /* Make sure it comes to a point */
    sinCache1b[0] = 0.0f;
    sinCache1b[stacks] = 0.0f;

    sinCache1a[slices] = sinCache1a[0];
    cosCache1a[slices] = cosCache1a[0];
        sinCache2a[slices] = sinCache2a[0];
        cosCache2a[slices] = cosCache2a[0];

    glNewList(listNum, GL_COMPILE);

        /* Do ends of sphere as TRIANGLE_FAN's (if not bTexture)
        ** We don't do it when bTexture because we need to respecify the
        ** texture coordinates of the apex for every adjacent vertex (because
        ** it isn't a constant for that point)
        */
        if (!bTexture) {
            start = 1;
            finish = stacks - 1;

            /* Low end first (j == 0 iteration) */
            sintemp2 = sinCache1b[1];
            zHigh = cosCache1b[1];
                sintemp3 = sinCache2b[1];
                costemp3 = cosCache2b[1];
                glNormal3f(sinCache2a[0] * sinCache2b[0],
                        cosCache2a[0] * sinCache2b[0],
                        cosCache2b[0]);

            glBegin(GL_TRIANGLE_FAN);
            glVertex3f(0.0f, 0.0f, radius);

                for (i = slices; i >= 0; i--) {
                        glNormal3f(sinCache2a[i] * sintemp3,
                                cosCache2a[i] * sintemp3,
                                costemp3);
                    glVertex3f(sintemp2 * sinCache1a[i],
                            sintemp2 * cosCache1a[i], zHigh);
                }
            glEnd();

            /* High end next (j == stacks-1 iteration) */
            sintemp2 = sinCache1b[stacks-1];
            zHigh = cosCache1b[stacks-1];
                sintemp3 = sinCache2b[stacks-1];
                costemp3 = cosCache2b[stacks-1];
                glNormal3f(sinCache2a[stacks] * sinCache2b[stacks],
                        cosCache2a[stacks] * sinCache2b[stacks],
                        cosCache2b[stacks]);
            glBegin(GL_TRIANGLE_FAN);
            glVertex3f(0.0f, 0.0f, -radius);
                for (i = 0; i <= slices; i++) {
                        glNormal3f(sinCache2a[i] * sintemp3,
                                cosCache2a[i] * sintemp3,
                                costemp3);
                    glVertex3f(sintemp2 * sinCache1a[i],
                            sintemp2 * cosCache1a[i], zHigh);
                }
            glEnd();
        } else {
            start = 0;
            finish = stacks;
        }
        for (j = start; j < finish; j++) {
            zLow = cosCache1b[j];
            zHigh = cosCache1b[j+1];
            sintemp1 = sinCache1b[j];
            sintemp2 = sinCache1b[j+1];
                    sintemp3 = sinCache2b[j+1];
                    costemp3 = cosCache2b[j+1];
                    sintemp4 = sinCache2b[j];
                    costemp4 = cosCache2b[j];

            glBegin(GL_QUAD_STRIP);
            for (i = 0; i <= slices; i++) {
                    glNormal3f(sinCache2a[i] * sintemp3,
                            cosCache2a[i] * sintemp3,
                            costemp3);
                    if (bTexture) {
                        glTexCoord2f( (float) s_start + s_delta*(j+1) / stacks,
                                      (float) i * texRep->y / slices );
                    }
                    glVertex3f(sintemp2 * sinCache1a[i],
                            sintemp2 * cosCache1a[i], zHigh);
                    glNormal3f(sinCache2a[i] * sintemp4,
                            cosCache2a[i] * sintemp4,
                            costemp4);
                    if (bTexture) {
                        glTexCoord2f( (float) s_start + s_delta * j / stacks,
                                      (float) i * texRep->y / slices );
                    }
                    glVertex3f(sintemp1 * sinCache1a[i],
                            sintemp1 * cosCache1a[i], zLow);
            }
            glEnd();
        }

    glEndList();
}


OBJECTS.H

/******************************Module*Header*******************************\
* Module Name: objects.h
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#ifndef __objects_h__
#define __objects_h__

#include "sspipes.h"
#include "state.h"

class OBJECT_BUILD_INFO {
public:
    float radius;
    float divSize;
    int   nSlices;
    BOOL  bTexture;
    IPOINT2D *texRep;
};

/**************************************************************************\
*
* OBJECT classes
*
* - Display list objects
*
\**************************************************************************/

class OBJECT {
protected:
    int         listNum;
    int         nSlices;
public:
    void        Draw();

    OBJECT();
    ~OBJECT();
};

class PIPE_OBJECT : public OBJECT {
private:
    void Build( OBJECT_BUILD_INFO *state, float length, float start_s, float s_end );
public:
    PIPE_OBJECT( OBJECT_BUILD_INFO *state, float length );
    PIPE_OBJECT( OBJECT_BUILD_INFO *state, float length, float start_s, float end_s );
};

class ELBOW_OBJECT : public OBJECT {
private:
    void Build( OBJECT_BUILD_INFO *state, int notch, float start_s, float end_s );
public:
    ELBOW_OBJECT( OBJECT_BUILD_INFO *state, int notch );
    ELBOW_OBJECT( OBJECT_BUILD_INFO *state, int notch, float start_s, float end_s );
};

class BALLJOINT_OBJECT : public OBJECT {
private:
    void Build( OBJECT_BUILD_INFO *state, int notch, float start_s, float end_s );
public:
    // texturing version only
    BALLJOINT_OBJECT( OBJECT_BUILD_INFO *state, int notch, float start_s, float end_s );
};

class SPHERE_OBJECT : public OBJECT {
private:
    void Build( OBJECT_BUILD_INFO *state, float radius, float start_s, float end_s );
public:
    SPHERE_OBJECT( OBJECT_BUILD_INFO *state, float radius, float start_s, float end_s );
    SPHERE_OBJECT( OBJECT_BUILD_INFO *state, float radius );
};

#endif // __objects_h__


PIPE.CXX

/******************************Module*Header*******************************\
* Module Name: pipe.cxx
*
* - Pipe base class stuff
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include <windows.h>

#include "sspipes.h"
#include "state.h"
#include "pipe.h"

/******************************Public*Routine******************************\
* PIPE constructor
\**************************************************************************/

PIPE::PIPE( STATE *state )
{
    pState = state;
    bTexture = pState->bTexture;
    radius = pState->radius;
    // default direction choosing is random
    chooseDirMethod = CHOOSE_DIR_RANDOM_WEIGHTED;
    chooseStartPosMethod = CHOOSE_STARTPOS_RANDOM;
    weightStraight = 1;
}

/******************************Public*Routine******************************\
* ChooseMaterial
*
\**************************************************************************/

void
PIPE::ChooseMaterial( )
{
    if( bTexture )
        ss_RandomTexMaterial( TRUE );
    else
        ss_RandomTeaMaterial( TRUE );
}

/**************************************************************************\
*
* DrawTeapot
*
\**************************************************************************/

extern void ResetEvaluator( BOOL bTexture );

void
PIPE::DrawTeapot( )
{
    glFrontFace( GL_CW );
    glEnable( GL_NORMALIZE );
    auxSolidTeapot(2.5 * radius);
    glDisable( GL_NORMALIZE );
    glFrontFace( GL_CCW );
    if( type != TYPE_NORMAL ) {
        // Re-init flex's evaluator state (teapot uses evaluators as well,
        //  and messes up the state).
        ResetEvaluator( bTexture );
    }
}

/******************************Public*Routine******************************\
* SetChooseDirectionMethod
\**************************************************************************/

void
PIPE::SetChooseDirectionMethod( int method )
{
    chooseDirMethod = method;
}

/**************************************************************************\
*
* ChooseNewDirection
*
* Call direction-finding function based on current method
* This is a generic entry point that is used by some pipe types
*
\**************************************************************************/

int
PIPE::ChooseNewDirection()
{
    NODE_ARRAY *nodes = pState->nodes;
    int bestDirs[NUM_DIRS], nBestDirs;

    // figger out which fn to call
    switch( chooseDirMethod ) {
        case CHOOSE_DIR_CHASE:
            if( nBestDirs = GetBestDirsForChase( bestDirs ) )
                return nodes->ChoosePreferredDirection( &curPos, lastDir,
                                                        bestDirs, nBestDirs );
            // else lead pipe must have died, so fall thru:
        case CHOOSE_DIR_RANDOM_WEIGHTED :
        default:
            return nodes->ChooseRandomDirection( &curPos, lastDir, weightStraight );
    }
}

/**************************************************************************\
*
* GetBestDirsForChase
*
* Find the best directions to take to close in on the lead pipe in chase mode.
*
\**************************************************************************/

int
PIPE::GetBestDirsForChase( int *bestDirs )
{
    // Figure out best dirs to close in on leadPos

    //mf: will have to 'protect' leadPos with GetLeadPos() for multi-threading
    IPOINT3D *leadPos = &pState->pLeadPipe->curPos;
    IPOINT3D delta;
    int numDirs = 0;

    delta.x = leadPos->x - curPos.x;
    delta.y = leadPos->y - curPos.y;
    delta.z = leadPos->z - curPos.z;

    if( delta.x ) {
        numDirs++;
        *bestDirs++ = delta.x > 0 ? PLUS_X : MINUS_X;
    }
    if( delta.y ) {
        numDirs++;
        *bestDirs++ = delta.y > 0 ? PLUS_Y : MINUS_Y;
    }
    if( delta.z ) {
        numDirs++;
        *bestDirs++ = delta.z > 0 ? PLUS_Z : MINUS_Z;
    }
    // It should be impossible for numDirs = 0 (all deltas = 0), as this
    // means curPos = leadPos
    return numDirs;
}

/******************************Public*Routine******************************\
* SetChooseStartPosMethod
\**************************************************************************/

void
PIPE::SetChooseStartPosMethod( int method )
{
    chooseStartPosMethod = method;
}

/******************************Public*Routine******************************\
* PIPE::SetStartPos
*
* - Find an empty node to start the pipe on
*
\**************************************************************************/

BOOL
PIPE::SetStartPos()
{
    NODE_ARRAY *nodes = pState->nodes;

    switch( chooseStartPosMethod ) {

        case CHOOSE_STARTPOS_RANDOM:
        default:
            if( !nodes->FindRandomEmptyNode( &curPos ) ) {
                return FALSE;
            }
            return TRUE;

        case CHOOSE_STARTPOS_FURTHEST:
            // find node furthest away from curPos
            IPOINT3D refPos, numNodes;
            nodes->GetNodeCount( &numNodes );
            refPos.x = (curPos.x >= (numNodes.x / 2)) ? 0 : numNodes.x - 1;
            refPos.y = (curPos.y >= (numNodes.y / 2)) ? 0 : numNodes.y - 1;
            refPos.z = (curPos.z >= (numNodes.z / 2)) ? 0 : numNodes.z - 1;

            if( !nodes->TakeClosestEmptyNode( &curPos, &refPos ) ) {
                return FALSE;
            }
            return TRUE;
    }
}

/******************************Public*Routine******************************\
* PIPE::IsStuck
\**************************************************************************/

BOOL
PIPE::IsStuck()
{
    return status == PIPE_STUCK;
}

/******************************Public*Routine******************************\
* PIPE::TranslateToCurrentPosition
*
\**************************************************************************/

void
PIPE::TranslateToCurrentPosition()
{
    IPOINT3D numNodes;

    float divSize = pState->view.divSize;
    // this requires knowing the size of the node array
    pState->nodes->GetNodeCount( &numNodes );
    glTranslatef( (curPos.x - (numNodes.x - 1)/2.0f )*divSize,
                  (curPos.y - (numNodes.y - 1)/2.0f )*divSize,
                  (curPos.z - (numNodes.z - 1)/2.0f )*divSize );
}

/**************************************************************************\
*
* UpdateCurrentPosition
*
* Increment current position according to direction taken
\**************************************************************************/

void
PIPE::UpdateCurrentPosition( int newDir )
{
    switch( newDir ) {
        case PLUS_X:
            curPos.x += 1;
            break;
        case MINUS_X:
            curPos.x -= 1;
            break;
        case PLUS_Y:
            curPos.y += 1;
            break;
        case MINUS_Y:
            curPos.y -= 1;
            break;
        case PLUS_Z:
            curPos.z += 1;
            break;
        case MINUS_Z:
            curPos.z -= 1;
            break;
    }
}

/******************************Public*Routine******************************\
* align_plusz
*
* - Aligns the z axis along specified direction
* - Used for all types of pipes
*
\**************************************************************************/

void align_plusz( int newDir )
{
    // align +z along new direction
    switch( newDir ) {
        case PLUS_X:
            glRotatef( 90.0f, 0.0f, 1.0f, 0.0f);
            break;
        case MINUS_X:
            glRotatef( -90.0f, 0.0f, 1.0f, 0.0f);
            break;
        case PLUS_Y:
            glRotatef( -90.0f, 1.0f, 0.0f, 0.0f);
            break;
        case MINUS_Y:
            glRotatef( 90.0f, 1.0f, 0.0f, 0.0f);
            break;
        case PLUS_Z:
            glRotatef( 0.0f, 0.0f, 1.0f, 0.0f);
            break;
        case MINUS_Z:
            glRotatef( 180.0f, 0.0f, 1.0f, 0.0f);
            break;
    }

}

/**************************************************************************\
* this array tells you which way the notch will be once you make
* a turn
* format: notchTurn[oldDir][newDir][notchVec]
*
\**************************************************************************/

GLint notchTurn[NUM_DIRS][NUM_DIRS][NUM_DIRS] = {
// oldDir = +x
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    MINUS_X,PLUS_X, PLUS_Z, MINUS_Z,
        iXX,    iXX,    PLUS_X, MINUS_X,PLUS_Z, MINUS_Z,
        iXX,    iXX,    PLUS_Y, MINUS_Y,MINUS_X,PLUS_X,
        iXX,    iXX,    PLUS_Y, MINUS_Y,PLUS_X, MINUS_X,
// oldDir = -x
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    PLUS_X, MINUS_X,PLUS_Z, MINUS_Z,
        iXX,    iXX,    MINUS_X,PLUS_X, PLUS_Z, MINUS_Z,
        iXX,    iXX,    PLUS_Y, MINUS_Y,PLUS_X, MINUS_X,
        iXX,    iXX,    PLUS_Y, MINUS_Y,MINUS_X,PLUS_X,
// oldDir = +y
        MINUS_Y,PLUS_Y, iXX,    iXX,    PLUS_Z, MINUS_Z,
        PLUS_Y, MINUS_Y,iXX,    iXX,    PLUS_Z, MINUS_Z,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        PLUS_X, MINUS_X,iXX,    iXX,    MINUS_Y,PLUS_Y,
        PLUS_X, MINUS_X,iXX,    iXX,    PLUS_Y, MINUS_Y,
// oldDir = -y
        PLUS_Y, MINUS_Y,iXX,    iXX,    PLUS_Z, MINUS_Z,
        MINUS_Y,PLUS_Y, iXX,    iXX,    PLUS_Z, MINUS_Z,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        PLUS_X, MINUS_X,iXX,    iXX,    PLUS_Y, MINUS_Y,
        PLUS_X, MINUS_X,iXX,    iXX,    MINUS_Y,PLUS_Y,
// oldDir = +z
        MINUS_Z,PLUS_Z, PLUS_Y, MINUS_Y,iXX,    iXX,
        PLUS_Z, MINUS_Z,PLUS_Y, MINUS_Y,iXX,    iXX,
        PLUS_X, MINUS_X,MINUS_Z,PLUS_Z, iXX,    iXX,
        PLUS_X, MINUS_X,PLUS_Z, MINUS_Z,iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
// oldDir = -z
        PLUS_Z, MINUS_Z,PLUS_Y, MINUS_Y,iXX,    iXX,
        MINUS_Z,PLUS_Z, PLUS_Y, MINUS_Y,iXX,    iXX,
        PLUS_X, MINUS_X,PLUS_Z, MINUS_Z,iXX,    iXX,
        PLUS_X, MINUS_X,MINUS_Z,PLUS_Z, iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX,
        iXX,    iXX,    iXX,    iXX,    iXX,    iXX
};


PIPE.H

/******************************Module*Header*******************************\
* Module Name: pipe.h
*
* PIPE base class
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __pipe_h__
#define __pipe_h__

#include "sscommon.h"
#include "state.h"

// pipe drawing status
enum {
    PIPE_ACTIVE,
    PIPE_STUCK,
    PIPE_OUT_OF_NODES
};

// pipe types
enum {
    TYPE_NORMAL,
    TYPE_FLEX_REGULAR,
    TYPE_FLEX_TURNING
};

// ways pipe choose directions
enum {
    CHOOSE_DIR_RANDOM_WEIGHTED,
    CHOOSE_DIR_CHASE // when chasing a lead pipe
};

// ways pipe choose start positions
enum {
    CHOOSE_STARTPOS_RANDOM,
    CHOOSE_STARTPOS_FURTHEST // furthest from last position
};
/**************************************************************************\
*
* PIPE class
*
* - Describes a pipe that draws thru the node array
* - Could have more than one pipe drawing in each array at same time
* - Pipe has position and direction in node array
*
\**************************************************************************/

class STATE;

class PIPE {
public:
    int         type;
    IPOINT3D    curPos;         // current node position of pipe

    STATE       *pState;        // for state value access

    void        SetChooseDirectionMethod( int method );
    void        SetChooseStartPosMethod( int method );
    int         ChooseNewDirection();
    BOOL        IsStuck();      // if pipe is stuck or not
    BOOL        NowhereToRun(){ return status == PIPE_OUT_OF_NODES; }
protected:
    BOOL        bTexture;
    float       radius;         // ideal radius (fluctuates for FPIPE)
    int         status;         // ACTIVE/STUCK/STOPPED, etc.
    int         lastDir;        // last direction taken by pipe
    int         notchVec;       // current notch vector
    PIPE( STATE *state );
    int         weightStraight; // current weighting of going straight
    BOOL        SetStartPos();  // starting node position
    void        ChooseMaterial();
    void        DrawTeapot();
    void        UpdateCurrentPosition( int dir );
    void        TranslateToCurrentPosition();
private:
    int         chooseDirMethod;
    int         chooseStartPosMethod;
    int         GetBestDirsForChase( int *bestDirs );
};

extern void align_plusz( int newDir );
extern GLint notchTurn[NUM_DIRS][NUM_DIRS][NUM_DIRS];

#endif // __pipe_h__


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by sspipes.rc
//
#define IDI_ICON1                       103
#define IDC_RADIO1                      1014
#define IDC_RADIO2                      1015
#define IDC_STATIC                      -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        104
#define _APS_NEXT_COMMAND_VALUE         101
#define _APS_NEXT_CONTROL_VALUE         1017
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif
#include "dialog.h"
#include "sscommon.rc"


SSPIPES.CXX

/******************************Module*Header*******************************\
* Module Name: sspipes.cxx
*
* Startup code
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include <windows.h>

#include "sspipes.h"
#include "dialog.h"
#include "state.h"

#ifdef DO_TIMING
int pipeCount;
#endif

void InitPipes( void *data );

/******************************Public*Routine******************************\
* ss_Init
*
* Initialize - called on first entry into ss.
* Called BEFORE gl is initialized!
* Just do basic stuff here, like set up callbacks, verify dialog stuff, etc.
*
* Fills global SSContext structure with required data, and returns ptr
* to it.
*
\**************************************************************************/

static SSContext ssc;

SSContext *
ss_Init( void )
{
    // validate some initial dialog settings
    getIniSettings();  // also called on dialog init

    if( ulSurfStyle == SURFSTYLE_TEX ) {
        // Texture verification has to go here, before gl is loaded, in case
        // error msgs are displayed.

        for( int i = 0; i < gnTextures; i ++ ) {
            if( !ss_VerifyTextureFile( &gTexFile[i]) ) {
                // user texture is invalid - substitute resource texture ?
                // If gnTextures > nRes
                // get rid of this one - move the others up
                gnTextures--;
                for( int j = i; j < gnTextures; j++ )
                    gTexFile[j] = gTexFile[j+1];
            }
        }
    }

    ss_InitFunc( InitPipes );

    // set configuration info to return
    ssc.bDoubleBuf = FALSE;
    ssc.depthType = SS_DEPTH16;
    ssc.bFloater = FALSE;

    return &ssc;
}

static void
Draw( void *data )
{
    ((STATE *) data)->Draw(data);
}

static void
Reshape( int width, int height, void *data )
{
    ((STATE *) data)->Reshape( width, height, data );
}

static void
Repaint( LPRECT pRect, void *data )
{
    ((STATE *) data)->Repaint( pRect, data );
}

static void
Finish( void *data )
{
    ((STATE *) data)->Finish( data );
}

/******************************Public*Routine******************************\
* InitPipes
*
* - Called when GL window has been initialized
*
\**************************************************************************/
void
InitPipes( void *data )
{
    // create world of pipes

    //mf: for now, bFlexMode used to choose between normal/flex

    STATE *pPipeWorld = new STATE( bFlexMode, bMultiPipes );

    ss_UpdateFunc( Draw );
    ss_ReshapeFunc( Reshape );
    ss_RepaintFunc( Repaint );
    ss_FinishFunc( Finish );
    ss_DataPtr( pPipeWorld );
}

#ifdef DO_TIMING
void CalcPipeRate( struct _timeb baseTime, int pipeCount ) {
    static struct _timeb thisTime;
    double elapsed, pipeRate;
    char buf[100];

    _ftime( &thisTime );
    elapsed = thisTime.time + thisTime.millitm/1000.0 -
       (baseTime.time + baseTime.millitm/1000.0);

    if( elapsed == 0.0 )
        pipeRate = 0.0;
    else
        pipeRate = pipeCount / elapsed;

    sprintf( buf, "Last frame's pipe rate = %4.1f pps", pipeRate );
#ifdef SS_DEBUG
    SendMessage(ss_GetHWND(), WM_SETTEXT, 0, (LPARAM)buf);
#endif
}

void Timer( int mode )
{
    static struct _timeb baseTime;

    switch( mode ) {
        case TIMER_START:
            pipeCount = 0;
         _ftime( &baseTime );
            break;
        case TIMER_STOP:
            CalcPipeRate( baseTime, pipeCount );
            break;
        case TIMER_TIMING:
            break;
        case TIMER_RESET:
        default:
            break;
    }
}
#endif


SSPIPES.H

/******************************Module*Header*******************************\
* Module Name: sspipes.h
*
* Global header for 3D Pipes screen saver.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#ifndef __sspipes_h__
#define __sspipes_h__

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

#include <commctrl.h>

#include "sscommon.h"

#define DO_TIMING 1
#ifdef DO_TIMING
enum {
    TIMER_START = 0,
    TIMER_STOP,
    TIMER_TIMING,
    TIMER_RESET
};
extern void Timer( int mode );
extern int pipeCount;
#endif

#define iXX -1
#define fXX -0.01f

// These are absolute directions, with origin in center of screen,
// looking down -z

enum {
    PLUS_X = 0,
    MINUS_X,
    PLUS_Y,
    MINUS_Y,
    PLUS_Z,
    MINUS_Z,
    NUM_DIRS,
    DIR_NONE,
    DIR_STRAIGHT
};

#define NUM_DIV 16              // divisions in window in longest dimension

#define MAX_TEXTURES 8

#endif // __sspipes_h__


STATE.CXX

/******************************Module*Header*******************************\
* Module Name: state.cxx
*
* STATE
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include <windows.h>

#include "sspipes.h"
#include "dialog.h"
#include "state.h"
#include "pipe.h"
#include "npipe.h"
#include "fpipe.h"
#include "eval.h"

// default texture resource(s)

#define DEF_TEX_COUNT 1
TEX_RES gTexRes[DEF_TEX_COUNT] = {
    { TEX_BMP, IDB_DEFTEX }
};

static void InitTexParams();

/******************************Public*Routine******************************\
* STATE constructor
*
* - global state init
* - translates variables set from the dialog boxes
*
\**************************************************************************/

STATE::STATE( BOOL bFlexMode, BOOL bMultiPipes )
{
    // various state values
    resetStatus = RESET_STARTUP_BIT;

    // Put initial hglrc in drawThreads[0]
    // This RC is also used for dlists and texture objects that are shared
    // by other RC's

    shareRC = wglGetCurrentContext();
    drawThreads[0].SetRCDC( shareRC, wglGetCurrentDC() );

    bTexture = FALSE;
    if( ulSurfStyle == SURFSTYLE_TEX ) {
        if( LoadTextureFiles( gTexFile, gnTextures, &gTexRes[0] ) )
            bTexture = TRUE;
    }
    else if( ulSurfStyle == SURFSTYLE_WIREFRAME ) {
        glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
    }

    // Initialize GL state for the initial RC (sets texture state, so
    // (must come after LoadTextureFiles())

    GLInit();

    // set 'reference' radius value

    radius = 1.0f;

    // convert tesselation from fTesselFact(0.0-2.0) to tessLevel(0-MAX_TESS)

    int tessLevel = (int) (fTesselFact * (MAX_TESS+1) / 2.0001f);
    nSlices = (tessLevel+2) * 4;

    // Allocate basic NODE_ARRAY
    // NODE_ARRAY size is determined in Reshape (based on window size)
    nodes = new NODE_ARRAY;

    // Set drawing mode, and initialize accordingly.  For now, either all normal
    // or all flex pipes are drawn, but they could be combined later.
    // Can assume here that if there's any possibility that normal pipes
    // will be drawn, NORMAL_STATE will be initialized so that dlists are
    // built

    // Again, since have either NORMAL or FLEX, set maxPipesPerFrame,
    // maxDrawThreads
    if( bMultiPipes )
        maxDrawThreads = MAX_DRAW_THREADS;
    else
        maxDrawThreads = 1;
    nDrawThreads = 0; // no active threads yet
    nPipesDrawn = 0;
    // maxPipesPerFrame is set in Reset()

    if( bFlexMode ) {
        drawMode = DRAW_FLEX;
        pFState = new FLEX_STATE( this );
        pNState = NULL;
    } else {
        drawMode = DRAW_NORMAL;
        pNState = new NORMAL_STATE( this );
        pFState = NULL;
    }

    // initialize materials

    if( bTexture )
        ss_InitTexMaterials();
    else
        ss_InitTeaMaterials();

    // default draw scheme
    drawScheme = FRAME_SCHEME_RANDOM;
}

/******************************Public*Routine******************************\
* STATE destructor
*
\**************************************************************************/

STATE::~STATE( )
{
    if( pNState )
        delete pNState;
    if( pFState )
        delete pFState;
    if( nodes )
        delete nodes;
    if( bTexture ) {
        for( int i = 0; i < nTextures; i ++ ) {
            ss_DeleteTexture( &texture[i] );
        }
    }

    // Delete any RC's - should be done by ~THREAD, but since common lib
    // deletes shareRC, have to do it here

    DRAW_THREAD *pdt = &drawThreads[0];
    for( int i = 0; i < MAX_DRAW_THREADS; i ++, pdt++ ) {
        if( pdt->hglrc && (pdt->hglrc != shareRC) ) {
            wglDeleteContext( pdt->hglrc );
        }
    }
}

/******************************Public*Routine******************************\
* CalcTexRepFactors
*
\**************************************************************************/

void
STATE::CalcTexRepFactors()
{
    ISIZE winSize;
    POINT2D texFact;

    ss_GetScreenSize( &winSize );

    // Figure out repetition factor of texture, based on bitmap size and
    // screen size.
    //
    // We arbitrarily decide to repeat textures that are smaller than
    // 1/8th of screen width or height.

    for( int i = 0; i < nTextures; i++ ) {
        texRep[i].x = texRep[i].y = 1;

        if( (texFact.x = winSize.width / texture[i].width / 8.0f) >= 1.0f)
            texRep[i].x = (int) (texFact.x+0.5f);

        if( (texFact.y = winSize.height / texture[i].height / 8.0f) >= 1.0f)
            texRep[i].y = (int) (texFact.y+0.5f);
    }

    // ! If display list based normal pipes, texture repetition is embedded
    // in the dlists and can't be changed. So use the smallest rep factors.

    if( pNState ) {
        //put smallest rep factors in texRep[0]; (mf:this is ok for now, as
        // flex pipes and normal pipes don't coexist)

        for( i = 1; i < nTextures; i++ ) {
            if( texRep[i].x < texRep[0].x )
                texRep[0].x = texRep[i].x;
            if( texRep[i].y < texRep[0].y )
                texRep[0].y = texRep[i].y;
        }
    }
}

/******************************Public*Routine******************************\
* LoadTextureFiles
*
* - Load user texture files.  If texturing on but no user textures, or
*   problems loading them, load default texture resource
*
\**************************************************************************/

BOOL
STATE::LoadTextureFiles( TEXFILE *pTexFile, int nTexFiles, TEX_RES *pTexRes )
{
    // Set pixel store state

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // Try to load the bmp or rgb file

    // i counts successfully loaded textures
    for( int i = 0; nTexFiles; nTexFiles-- ) {
        if( ss_LoadTextureFile( &pTexFile[i], &texture[i] ) )  {
            // If texture object extension, set tex params here for each object
            if( ss_TextureObjectsEnabled() )
                InitTexParams();
            i++; // count another valid texture
        }
    }

    // set number of valid textures in state
    nTextures = i;

    if( nTextures == 0 ) {
        // No user textures, or none loaded successfully
        // Load default resource texture(s)
        nTextures = DEF_TEX_COUNT;
        for( i = 0; i < nTextures; i++, pTexRes++ ) {
            if( !ss_LoadTextureResource( pTexRes, &texture[i] ) ) {
                // shouldn't happen
                return FALSE;
            }
        }
    }

    CalcTexRepFactors();

    return TRUE;
}

/******************************Public*Routine******************************\
* GLInit
*
* - Sets up GL state
* - Called once for every context (rc)
*
\**************************************************************************/

void
STATE::GLInit()
{
    static float ambient[] = {0.1f, 0.1f, 0.1f, 1.0f};
    static float diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static float position[] = {90.0f, 90.0f, 150.0f, 0.0f};
    static float lmodel_ambient[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static float lmodel_ambientTex[] = {0.6f, 0.6f, 0.6f, 0.0f};
    static float back_mat_diffuse[] = {0.0f, 0.0f, 1.0f};

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    glFrontFace(GL_CCW);

    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);

    glEnable( GL_AUTO_NORMAL ); // needed for GL_MAP2_VERTEX (tea)

    if( bTexture )
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambientTex);
    else
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);

    glCullFace(GL_BACK);
    glEnable(GL_CULL_FACE);

    // Set texture modes
    if( bTexture ) {
        glEnable(GL_TEXTURE_2D);
        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        InitTexParams();
    }
}

/**************************************************************************\
* InitTexParams
*
* Set texture parameters, globally, or per object if texture object extension
*
\**************************************************************************/

static void
InitTexParams()
{
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    switch( ulTexQuality ) {
       case TEXQUAL_HIGH:
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            break;
       case TEXQUAL_DEFAULT:
       default:
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            break;
    }
}

/**************************************************************************\
* Repaint
*
* This is called when a WM_PAINT msg has been sent to the window.   The paint
* will overwrite the frame buffer, screwing up the scene if pipes is in single
* buffer mode.  We set resetStatus accordingly to clear things up on next
* draw.
*
\**************************************************************************/

void
STATE::Repaint( LPRECT pRect, void *data)
{
    resetStatus |= RESET_REPAINT_BIT;
}

/**************************************************************************\
* Reshape
*   - called on resize, expose
*   - always called on app startup
*   - set new window size for VIEW object, and set resetStatus for validation
*     at draw time
*
\**************************************************************************/

void
STATE::Reshape(int width, int height, void *data)
{
    if( view.SetWinSize( width, height ) )
        resetStatus |= RESET_RESIZE_BIT;
}

/**************************************************************************\
* ResetView
*
* Called on FrameReset resulting from change in viewing paramters (e.g. from
* a Resize event).
\**************************************************************************/

void
STATE::ResetView()
{
    IPOINT3D numNodes;

    // Have VIEW calculate the node array size based on view params
    view.CalcNodeArraySize( &numNodes );

    // Resize the node array
    nodes->Resize( &numNodes );

    // Set GL viewing parameters for each active RC

    DRAW_THREAD *pThread = drawThreads;

    for( int i = 0; i < MAX_DRAW_THREADS; i ++, pThread++ ) {
        if( pThread->HasRC() ) {
            pThread->MakeRCCurrent();
            view.SetGLView();
        }
    }
}

/**************************************************************************\
* FrameReset
*
* Start a new frame of pipes
*
* The resetStatus parameter indicates what triggered the Reset.
*
\**************************************************************************/

static int PickRandomTexture( int i, int nTextures );

void
STATE::FrameReset()
{
    int i;
    float xRot, zRot;
    PIPE *pNewPipe;

#ifdef DO_TIMING
    Timer( TIMER_STOP );
#endif

    SS_DBGINFO( "Pipes STATE::FrameReset:\n" );

    // Kill off any active pipes ! (so they can shut down ok)

    DRAW_THREAD *pThread = drawThreads;
    for( i = 0; i < nDrawThreads; i ++, pThread++ ) {
        pThread->KillPipe();
    }
    nDrawThreads = 0;

    // Clear the screen
    Clear();

    // Check for window resize status
    if( resetStatus & RESET_RESIZE_BIT ) {
        ResetView();
    }

    // Reset the node states to empty
    nodes->Reset();

    // Call any pipe-specific state resets, and get any recommended
    // pipesPerFrame counts

    if( pNState ) {
        pNState->Reset();
    }
    if( pFState ) {
        pFState->Reset();
        xRot = ss_fRand(-5.0f, 5.0f);
        zRot = ss_fRand(-5.0f, 5.0f);
    }
    maxPipesPerFrame = CalcMaxPipesPerFrame();

    // Set new number of drawing threads

    if( maxDrawThreads > 1 ) {
        // Set maximum # of pipes per frame
        maxPipesPerFrame = (int) (maxPipesPerFrame * 1.5);

        // Set # of draw threads
        nDrawThreads = SS_MIN( maxPipesPerFrame, ss_iRand2( 2, maxDrawThreads ) );
        // Set chase mode if applicable, every now and then
        BOOL bUseChase = pNState || (pFState && pFState->OKToUseChase());
        if( bUseChase && (!ss_iRand(5)) ) {
            drawScheme = FRAME_SCHEME_CHASE;
        }
    } else {
        nDrawThreads = 1;
    }
    nPipesDrawn = 0;

    // for now, either all NORMAL or all FLEX for each frame

    pThread = drawThreads;

    for( i = 0; i < nDrawThreads; i ++, pThread++ ) {

        // Create hglrc if necessary, and init it

        if( !pThread->HasRC() ) {
            HDC hdc = wglGetCurrentDC();
            pThread->SetRCDC( wglCreateContext( hdc ), hdc );
            // also need to init each RC
            pThread->MakeRCCurrent();
            // Do GL Init for this new RC
            GLInit();

            // Set viewing params
            view.SetGLView();

            // Give this rc access to any dlists
            wglShareLists( shareRC, pThread->GetRC() );
        }
        else
            pThread->MakeRCCurrent();

        // Set up the modeling view

        glLoadIdentity();
        glTranslatef(0.0f, 0.0f, view.zTrans);

        // Rotate Scene
        glRotatef( view.yRot, 0.0f, 1.0f, 0.0f );

        // create approppriate pipe for this thread slot

        switch( drawMode ) {
            case DRAW_NORMAL:
                pNewPipe = (PIPE *) new NORMAL_PIPE(this);
                break;
            case DRAW_FLEX:
                // There are several kinds of FLEX pipes - have FLEX_STATE
                // decide which one to create
                pNewPipe = pFState->NewPipe( this );
                // rotate a bit around x and z as well
                // If combining NORMAL and FLEX, same rotations must be
                // applied to both
                glRotatef( xRot, 1.0f, 0.0f, 0.0f );
                glRotatef( zRot, 0.0f, 0.0f, 1.0f );
                break;
        }
        pThread->SetPipe( pNewPipe );

        if( drawScheme == FRAME_SCHEME_CHASE ) {
            if( i == 0 ) {
                // this will be the lead pipe
                pLeadPipe = pNewPipe;
                pNewPipe->SetChooseDirectionMethod( CHOOSE_DIR_RANDOM_WEIGHTED );
            } else {
                pNewPipe->SetChooseDirectionMethod( CHOOSE_DIR_CHASE );
            }
        }

        // If texturing, pick a random texture for this thread

        if( bTexture ) {
            int index = PickRandomTexture( i, nTextures );
            pThread->SetTexture( &texture[index] );

            // Flex pipes need to be informed of the texture, so they
            // can dynamically calculate various texture params
            if( pFState )
                ((FLEX_PIPE *) pNewPipe)->SetTexParams( &texture[index],
                                                        &texRep[index] );
        }

        // Launch the pipe (assumed: always more nodes than pipes starting, so
        // StartPipe cannot fail)

        // ! All pipe setup needs to be done before we call StartPipe, as this
        // is where the pipe starts drawing

        pThread->StartPipe();

        if( (i == 0) && (drawScheme == FRAME_SCHEME_CHASE) )
            pNewPipe->SetChooseStartPosMethod( CHOOSE_STARTPOS_FURTHEST );

        nPipesDrawn++;
    }

    // Increment scene rotation for normal reset case
    if( resetStatus & RESET_NORMAL_BIT )
        view.IncrementSceneRotation();

    // clear reset status
    resetStatus = 0;

#ifdef DO_TIMING
    Timer( TIMER_START );
#endif
}

/**************************************************************************\
* CalcMaxPipesPerFrame
*
\**************************************************************************/

int
STATE::CalcMaxPipesPerFrame()
{
    int nCount=0, fCount=0;

    if( pFState )
        fCount = pFState->GetMaxPipesPerFrame();
    if( pNState )
        nCount = bTexture ? NORMAL_TEX_PIPE_COUNT : NORMAL_PIPE_COUNT;
    return SS_MAX( nCount, fCount );
}

/**************************************************************************\
* PickRandomTexture
*
* Pick a random texture index from a list.  Remove entry from list as it
* is picked.  Once all have been picked, or starting a new frame, reset.
*
* ! Routine not reentrant, should only be called by the main thread
* dispatcher (FrameReset)
\**************************************************************************/

static int
PickRandomTexture( int iThread, int nTextures )
{
    if( nTextures == 0 )
        return 0;

    static int pickSet[MAX_TEXTURES] = {0};
    static int nPicked = 0;
    int i, index;

    if( iThread == 0 )
        // new frame - force reset
        nPicked = nTextures;

    // reset condition
    if( ++nPicked > nTextures ) {
        for( i = 0; i < nTextures; i ++ ) pickSet[i] = 0;
        nPicked = 1; // cuz
    }

    // Pick a random texture index
    index = ss_iRand( nTextures );
    while( pickSet[index] ) {
        // this index has alread been taken, try the next one
        if( ++index >= nTextures )
            index = 0;
    }
    // Hopefully, the above loop will exit :).  This means that we have
    // found a texIndex that is available
    pickSet[index] = 1; // mark as taken
    return index;
}

/**************************************************************************\
* Clear
*
* Clear the screen.  Depending on resetStatus, use normal clear or
* fancy transitional clear.
\**************************************************************************/

void
STATE::Clear()
{
    // clear the screen - any rc will do

    glClear(GL_DEPTH_BUFFER_BIT);

    if( resetStatus & RESET_RESIZE_BIT ) {
        // new window size - recalibrate the transitional clear

        // Calibration is set after a window resize, so window is already black
        ddClear.CalibrateClear( view.winSize.width, view.winSize.height, 2.0f );
    } else if( resetStatus & RESET_NORMAL_BIT )
        // do the normal transitional clear
        ddClear.Clear( view.winSize.width, view.winSize.height );
    else {
        // do a fast one-shot clear
        glClear( GL_COLOR_BUFFER_BIT );
    }
}

/**************************************************************************\
* DrawValidate
*
* Validation done before every Draw
*
* For now, this just involves checking resetStatus
*
\**************************************************************************/

void
STATE::DrawValidate()
{
    if( ! resetStatus )
        return;

    FrameReset();
}

/**************************************************************************\
* Draw
*
* - Top-level pipe drawing routine
* - Each pipe thread keeps drawing new pipes until we reach maximum number
*   of pipes per frame - then each thread gets killed as soon as it gets
*   stuck.  Once number of drawing threads reaches 0, we start a new
*   frame
*
\**************************************************************************/

void
STATE::Draw(void *data)
{
    int nKilledThreads = 0;
    BOOL bChooseNewLead = FALSE;

    // Validate the draw state

    DrawValidate();

    // Check each pipe's status

    DRAW_THREAD *pThread = drawThreads;

    for( int i = 0; i < nDrawThreads; i++, pThread++  ) {
        if( pThread->pPipe->IsStuck() ) {
            if( ++nPipesDrawn > maxPipesPerFrame ) {
                // Reaching pipe saturation - kill this pipe thread

                if( (drawScheme == FRAME_SCHEME_CHASE) &&
                    (pThread->pPipe == pLeadPipe) )
                    bChooseNewLead = TRUE;

                pThread->KillPipe();
                nKilledThreads++;

            } else {
                // Start up another pipe
                if( ! pThread->StartPipe() )
                    // we won't be able to draw any more pipes this frame
                    // (probably out of nodes)
                    maxPipesPerFrame = nPipesDrawn;
            }
        }
    }

    // Whenever one or more pipes are killed, compact the thread list
    if( nKilledThreads ) {
        CompactThreadList();
        nDrawThreads -= nKilledThreads;
    }

    if( nDrawThreads == 0 ) {
        // This frame is finished - mark for reset on next Draw
        resetStatus |= RESET_NORMAL_BIT;
        return;
    }

    if( bChooseNewLead ) {
        // We're in 'chase mode' and need to pick a new lead pipe
        ChooseNewLeadPipe();
    }

    // Draw each pipe

    for( i = 0, pThread = drawThreads; i < nDrawThreads; i++, pThread++ ) {
        pThread->DrawPipe();
#ifdef DO_TIMING
        pipeCount++;
#endif
    }

    glFlush();
}

/**************************************************************************\
*
* CompactThreadList
*
* - Compact the thread list according to number of pipe threads killed
* - The pipes have been killed, but the RC's in each slot are still valid
*   and reusable.  So we swap up entries with valid pipes. This means that
*   the ordering of the RC's in the thread list will change during the life
*   of the program.  This should be OK.
*
\**************************************************************************/

#define SWAP_SLOT( a, b ) \
    DRAW_THREAD pTemp; \
    pTemp = *(a); \
    *(a) = *(b); \
    *(b) = pTemp;

void
STATE::CompactThreadList()
{
    if( nDrawThreads <= 1 )
        // If only one active thread, it must be in slot 0 from previous
        // compactions - so nothing to do
        return;

    int iEmpty = 0;
    DRAW_THREAD *pThread = drawThreads;

    for( int i = 0; i < nDrawThreads; i ++, pThread++ ) {
        if( pThread->pPipe ) {
            if( iEmpty < i ) {
                // swap active pipe thread and empty slot
                SWAP_SLOT( &(drawThreads[iEmpty]), pThread );
            }
            iEmpty++;
        }
    }
}

/**************************************************************************\
*
* ChooseNewLeadPipe
*
* Choose a new lead pipe for chase mode.
*
\**************************************************************************/

void
STATE::ChooseNewLeadPipe()
{
    // Pick one of the active pipes at random to become the new lead

    int iLead = ss_iRand( nDrawThreads );
    pLeadPipe = drawThreads[iLead].pPipe;
    pLeadPipe->SetChooseStartPosMethod( CHOOSE_STARTPOS_FURTHEST );
    pLeadPipe->SetChooseDirectionMethod( CHOOSE_DIR_RANDOM_WEIGHTED );
}

/******************************Public*Routine******************************\
* Finish
*
* - Called when GL window being closed
*
\**************************************************************************/
void
STATE::Finish( void *data )
{
    delete (STATE *) data;
}

/**************************************************************************\
* DRAW_THREAD constructor
*
\**************************************************************************/

DRAW_THREAD::DRAW_THREAD()
{
    hdc = 0;
    hglrc = 0;
    pPipe = NULL;
    htex = (HTEXTURE) -1;
}

/**************************************************************************\
* DRAW_THREAD destructor
*
* Delete any GL contexts
*
* - can't Delete shareRC, as this is done by common lib, so had to move
*   this up to ~STATE
*
\**************************************************************************/

DRAW_THREAD::~DRAW_THREAD()
{
}

/**************************************************************************\
* MakeRCCurrent
*
\**************************************************************************/

void
DRAW_THREAD::MakeRCCurrent()
{
    if( hglrc != wglGetCurrentContext() )
        wglMakeCurrent( hdc, hglrc );
}

/**************************************************************************\
* SetRCDC
*
\**************************************************************************/

void
DRAW_THREAD::SetRCDC( HGLRC rc, HDC Hdc )
{
    hglrc = rc;
    hdc = Hdc;
}

/**************************************************************************\
* SetPipe
*
\**************************************************************************/

void
DRAW_THREAD::SetPipe( PIPE *pipe )
{
    pPipe = pipe;
}

/**************************************************************************\
* HasRC
*
\**************************************************************************/

BOOL
DRAW_THREAD::HasRC()
{
    return( hglrc != 0 );
}

/**************************************************************************\
* GetRC
*
\**************************************************************************/

HGLRC
DRAW_THREAD::GetRC()
{
    return hglrc;
}

/**************************************************************************\
* SetTexture
*
* - Set a texture for a thread
* - Cache the texture index for performance
\**************************************************************************/

void
DRAW_THREAD::SetTexture( HTEXTURE hnewtex )
{
    if( hnewtex != htex )
    {
        htex = hnewtex;
        ss_SetTexture( htex );
    }
}

/**************************************************************************\
* DrawPipe
*
* - Draw pipe in thread slot, according to its type
*
\**************************************************************************/

void
DRAW_THREAD::DrawPipe()
{
    MakeRCCurrent();

    switch( pPipe->type ) {
        case TYPE_NORMAL:
            ( (NORMAL_PIPE *) pPipe )->Draw();
            break;
        case TYPE_FLEX_REGULAR:
            ( (REGULAR_FLEX_PIPE *) pPipe )->Draw();
            break;
        case TYPE_FLEX_TURNING:
            ( (TURNING_FLEX_PIPE *) pPipe )->Draw();
            break;
    }
    glFlush();
}
/**************************************************************************\
* StartPipe
*
* Starts up pipe of the approppriate type.  If can't find an empty node
* for the pipe to start on, returns FALSE;
*
\**************************************************************************/

BOOL
DRAW_THREAD::StartPipe()
{
    MakeRCCurrent();

    // call pipe-type specific Start function

    switch( pPipe->type ) {
        case TYPE_NORMAL:
            ( (NORMAL_PIPE *) pPipe )->Start();
            break;
        case TYPE_FLEX_REGULAR:
            ( (REGULAR_FLEX_PIPE *) pPipe )->Start();
            break;
        case TYPE_FLEX_TURNING:
            ( (TURNING_FLEX_PIPE *) pPipe )->Start();
            break;
    }
    glFlush();

    // check status
    if( pPipe->NowhereToRun() )
        return FALSE;
    else
        return TRUE;
}

/**************************************************************************\
* KillPipe
*
\**************************************************************************/

void
DRAW_THREAD::KillPipe()
{
    switch( pPipe->type ) {
        case TYPE_NORMAL:
            delete (NORMAL_PIPE *) pPipe;
            break;
        case TYPE_FLEX_REGULAR:
            delete (REGULAR_FLEX_PIPE *) pPipe;
            break;
        case TYPE_FLEX_TURNING:
            delete (TURNING_FLEX_PIPE *) pPipe;
            break;
    }
    pPipe = NULL;
}


STATE.H

/******************************Module*Header*******************************\
* Module Name: state.h
*
* STATE
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __state_h__
#define __state_h__

#include "sscommon.hxx"
#include "pipe.h"
#include "node.h"
#include "view.h"
#include "nstate.h"
#include "fstate.h"

#define MAX_DRAW_THREADS    4

#define TEAPOT             66

#define MAX_TESS 3

// type(s) of pipes that are drawn
enum {
    DRAW_NORMAL,
    DRAW_FLEX,
    DRAW_BOTH  // not currently used
};

// Reset status

#define  RESET_STARTUP_BIT (1L << 0)
#define  RESET_NORMAL_BIT  (1L << 1)
#define  RESET_RESIZE_BIT  (1L << 2)
#define  RESET_REPAINT_BIT  (1L << 3)

// Frame draw schemes

enum {
    FRAME_SCHEME_RANDOM,  // pipes draw randomly
    FRAME_SCHEME_CHASE,   // pipes chase a lead pipe
};

class DRAW_THREAD {
private:
    HDC         hdc;
    HTEXTURE    htex;

public:
    HGLRC       hglrc;        // rc to draw with (public so STATE can delete)
    int         priority;

    DRAW_THREAD();
    ~DRAW_THREAD();
    PIPE        *pPipe;       // generic pipe ptr
    void        SetRCDC( HGLRC rc, HDC hdc );
    BOOL        HasRC();
    HGLRC       GetRC();
    void        MakeRCCurrent();
    void        SetTexture( HTEXTURE htex );
    void        SetPipe( PIPE *pipe );
    BOOL        StartPipe();
    void        DrawPipe();
    void        KillPipe();
};

// Program existence instance

class NORMAL_STATE;
class FLEX_STATE;

class STATE {
public:
    HGLRC       shareRC;        // RC that objects are shared from

    PIPE        *pLeadPipe;     // lead pipe for chase scenarios

    int         nSlices;      // reference # of slices around a pipe
    BOOL        bTexture;       // global texture enable
    int         nTextures;
    TEXTURE     texture[MAX_TEXTURES];
    IPOINT2D    texRep[MAX_TEXTURES];

    VIEW        view;           // viewing parameters
    float       radius;         // 'reference' pipe radius value
    NODE_ARRAY  *nodes;         // for keeping track of draw space
    NORMAL_STATE *pNState;
    FLEX_STATE  *pFState;

    STATE( BOOL bFlexMode, BOOL bMultiPipes );
    ~STATE();
    void        Reshape( int width, int height, void *data );
    void        Repaint( LPRECT pRect, void *data );
    void        Draw( void *data );
    void        Finish( void *data );

private:
    int         drawMode;       // drawing mode (flex or normal for now)
    int         drawScheme;     // random or chase

    int         maxPipesPerFrame; // max number of separate pipes/frame
    int         nPipesDrawn;    // number of pipes drawn or drawing in frame
    int         maxDrawThreads; // max number of concurrently drawing pipes
    int         nDrawThreads;   // number of live threads
    DRAW_THREAD drawThreads[MAX_DRAW_THREADS];

    int         resetStatus;

    SS_DIGITAL_DISSOLVE_CLEAR ddClear;
    int         bCalibrateClear;

    void        GLInit();
    void        DrawValidate();  // validation to do before each Draw
    void        ResetView();
    void        FrameReset();
    void        Clear();
    void        ChooseNewLeadPipe();
    void        CompactThreadList();
    BOOL        LoadTextureFiles();
    BOOL        LoadTextureFiles( TEXFILE *pTexFile, int nTexFiles,
                                  TEX_RES *pTexRes );
    void        CalcTexRepFactors();
    int         CalcMaxPipesPerFrame();
};

#endif // __state_h__


VIEW.CXX

/******************************Module*Header*******************************\
* Module Name: view.cxx
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include <windows.h>

#include "sspipes.h"
#include "view.h"

/******************************Public*Routine******************************\
* VIEW constructor
\**************************************************************************/

VIEW::VIEW()
{
    bProjMode = GL_TRUE;

    // set some initial viewing and size params

    zTrans = -75.0f;
    viewDist = -zTrans;

    numDiv = NUM_DIV;
    SS_ASSERT( numDiv >= 2, "VIEW constructor: not enough divisions\n" );
    // Because number of nodes in a dimension is derived from (numDiv-1), and
    // can't be 0

    divSize = 7.0f;

    persp.viewAngle = 90.0f;
    persp.zNear = 1.0f;

    yRot = 0.0f;

    winSize.width = winSize.height = 0;
}

/******************************Public*Routine******************************\
* SetProjMatrix
*
* Set GL view parameters
\**************************************************************************/

void
VIEW::SetGLView()
{
    glViewport(0, 0, winSize.width, winSize.height );
    SetProjMatrix();
}

/******************************Public*Routine******************************\
* SetProjMatrix
*
* Set Projection matrix
\**************************************************************************/

void
VIEW::SetProjMatrix()
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    persp.zFar = viewDist + world.z*2;
    if( bProjMode ) {
        gluPerspective( persp.viewAngle,
                        aspectRatio,
                        persp.zNear, persp.zFar );
    }
    else {
        glOrtho( -world.x/2, world.x/2, -world.y/2, world.y/2,
                          -world.z, world.z );
    }
    glMatrixMode(GL_MODELVIEW);
}

/******************************Public*Routine******************************\
* CalcNodeArraySize
*
* Based on the viewing width and height, and numDiv, calculate the x,y,z array
* node dimensions.
*
\**************************************************************************/

void
VIEW::CalcNodeArraySize( IPOINT3D *pNodeDim )
{
    // if aspect ratio deviates too much from 1, then nodes will get
    // clipped as view rotates

    if( winSize.width >= winSize.height ) {
        pNodeDim->x = numDiv - 1;
        pNodeDim->y = (int) (pNodeDim->x / aspectRatio) ;
        if( pNodeDim->y < 1 )
            pNodeDim->y = 1;
        pNodeDim->z = pNodeDim->x;
    }
    else {
        pNodeDim->y = numDiv - 1;
        pNodeDim->x = (int) (aspectRatio * pNodeDim->y);
        if( pNodeDim->x < 1 )
            pNodeDim->x = 1;
        pNodeDim->z = pNodeDim->y;
    }
}

/******************************Public*Routine******************************\
* SetWinSize
*
* Set the window size for the view, derive other view params.
*
* Return FALSE if new size same as old.
\**************************************************************************/

BOOL
VIEW::SetWinSize( int width, int height )
{
    if( (width == winSize.width) &&
        (height == winSize.height) )
        return FALSE;

    winSize.width = width;
    winSize.height = height;

    aspectRatio = winSize.height == 0 ? 1.0f : (float)winSize.width/winSize.height;

    if( winSize.width >= winSize.height ) {
        world.x = numDiv * divSize;
        world.y = world.x / aspectRatio;
        world.z = world.x;
    }
    else {
        world.y = numDiv * divSize;
        world.x = world.y * aspectRatio;
        world.z = world.y;
    }
    return TRUE;
}

/******************************Public*Routine******************************\
* SetSceneRotation
*
\**************************************************************************/

void
VIEW::IncrementSceneRotation()
{
    yRot += 9.73156f;
    if( yRot >= 360.0f )
        // prevent overflow
        yRot -= 360.0f;
}


VIEW.H

/******************************Module*Header*******************************\
* Module Name: view.h
*
* Node stuff
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#ifndef __view_h__
#define __view_h__

#include "sscommon.h"

typedef struct {
    float viewAngle;            // field of view angle for height
    float zNear;                // near z clip value
    float zFar;                 // far z clip value
} Perspective;  // perspective view description

class VIEW {
public:
    float       zTrans;         // z translation
    float       yRot;           // current yRotation
    float       viewDist;       // viewing distance, usually -zTrans
    int         numDiv;         // # grid divisions in x,y,z
    float       divSize;        // distance between divisions
    ISIZE       winSize;        // window size in pixels

    VIEW();
    BOOL        SetWinSize( int width, int height );
    void        SetGLView();
    void        CalcNodeArraySize( IPOINT3D *pNodeDim );
    void        SetProjMatrix();
    void        IncrementSceneRotation();
private:
    BOOL        bProjMode;      // projection mode
    Perspective persp;          // perspective view description
    float       aspectRatio;    // x/y window aspect ratio
    POINT3D     world;          // view area in world space
};

#endif // __view_h__


XC.CXX

/******************************Module*Header*******************************\
* Module Name: xc.cxx
*
* Cross-section (xc) object stuff
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include <float.h>

#include "sscommon.h"
#include "sspipes.h"
#include "eval.h"
#include "xc.h"

/**************************************************************************\
* XC::CalcArcACValues90
*
* Calculate arc control points for a 90 degree rotation of an xc
*
* Arc is a quarter-circle
* - 90 degree is much easier, so we special case it
* - radius is distance from xc-origin to hinge of turn
*
\**************************************************************************/

void
XC::CalcArcACValues90( int dir, float radius, float *acPts )
{
    int i;
    float sign;
    int offset;
    float *ppts = (float *) pts;

    // 1) calc 'r' values for each point (4 turn possibilities/point).  From
    //  this can determine ac, which is extrusion of point from xc face

    switch( dir ) {
        case PLUS_X:
            offset = 0;
            sign = -1.0f;
            break;
        case MINUS_X:
            offset = 0;
            sign =  1.0f;
            break;
        case PLUS_Y:
            offset = 1;
            sign = -1.0f;
            break;
        case MINUS_Y:
            offset = 1;
            sign =  1.0f;
            break;
    }

    for( i = 0; i < numPts; i++, ppts+=2, acPts++ ) {
        *acPts = EVAL_CIRC_ARC_CONTROL * (radius + (sign * ppts[offset]));
    }
    // replicate !
    *acPts = *(acPts - numPts);
}

/**************************************************************************\
* XC::CalcArcACValuesByDistance
*
* Use the distance of each xc point from the xc origin, as the radius for
* an arc control value.
*
\**************************************************************************/

void
XC::CalcArcACValuesByDistance( float *acPts )
{
    int i;
    float r;
    POINT2D *ppts =  pts;

    for( i = 0; i < numPts; i++, ppts++ ) {
        r = (float) sqrt( ppts->x*ppts->x + ppts->y*ppts->y );
        *acPts++ = EVAL_CIRC_ARC_CONTROL * r;
    }
    // replicate !
    *acPts = *(acPts - numPts);
}

/**************************************************************************\
* ELLIPTICAL_XC::SetControlPoints
*
* Set the 12 control points for a circle at origin in z=0 plane
*
* Points go CCW from +x
*
\**************************************************************************/

void
ELLIPTICAL_XC::SetControlPoints( GLfloat r1, GLfloat r2 )
{
    GLfloat ac1, ac2;

    ac1 = EVAL_CIRC_ARC_CONTROL * r2;
    ac2 = EVAL_CIRC_ARC_CONTROL * r1;

    // create 12-pt. set CCW from +x

    // last 2 points of right triplet
    pts[0].x = r1;
    pts[0].y = 0.0f;
    pts[1].x = r1;
    pts[1].y = ac1;

    // top triplet
    pts[2].x = ac2;
    pts[2].y = r2;
    pts[3].x = 0.0f;
    pts[3].y = r2;
    pts[4].x = -ac2;
    pts[4].y = r2;

    // left triplet
    pts[5].x = -r1;
    pts[5].y = ac1;
    pts[6].x = -r1;
    pts[6].y = 0.0f;
    pts[7].x = -r1;
    pts[7].y = -ac1;

    // bottom triplet
    pts[8].x = -ac2;
    pts[8].y = -r2;
    pts[9].x = 0.0f;
    pts[9].y = -r2;
    pts[10].x = ac2;
    pts[10].y = -r2;

    // first point of first triplet
    pts[11].x = r1;
    pts[11].y = -ac1;
}

/**************************************************************************\
* RANDOM4ARC_XC::SetControlPoints
*
* Set random control points for xc
* Points go CCW from +x
*
\**************************************************************************/

void
RANDOM4ARC_XC::SetControlPoints( float radius )
{
    int i;
    GLfloat r[4];
    float rMin = 0.5f * radius;
    float distx, disty;

    // figure the radius of each side first

    for( i = 0; i < 4; i ++ )
        r[i] = ss_fRand( rMin, radius );

    // The 4 r's now describe a box around the origin - this restricts stuff

    // Now need to select a point along each edge of the box as the joining
    // points for each arc (join points are at indices 0,3,6,9)

    pts[0].x = r[RIGHT];
    pts[3].y = r[TOP];
    pts[6].x = -r[LEFT];
    pts[9].y = -r[BOTTOM];

    // quarter of distance between edges
    disty = (r[TOP] - -r[BOTTOM]) / 4.0f;
    distx = (r[RIGHT] - -r[LEFT]) / 4.0f;

    // uh, put'em somwhere in the middle half of each side
    pts[0].y = ss_fRand( -r[BOTTOM] + disty, r[TOP] - disty );
    pts[6].y = ss_fRand( -r[BOTTOM] + disty, r[TOP] - disty );
    pts[3].x = ss_fRand( -r[LEFT] + distx, r[RIGHT] - distx );
    pts[9].x = ss_fRand( -r[LEFT] + distx, r[RIGHT] - distx );

    // now can calc ac's
    // easy part first:
    pts[1].x = pts[11].x = pts[0].x;
    pts[2].y = pts[4].y = pts[3].y;
    pts[5].x = pts[7].x = pts[6].x;
    pts[8].y = pts[10].y = pts[9].y;

    // right side ac's
    disty = (r[TOP] - pts[0].y) / 4.0f;
    pts[1].y = ss_fRand( pts[0].y + disty, r[TOP] );
    disty = (pts[0].y - -r[BOTTOM]) / 4.0f;
    pts[11].y = ss_fRand( -r[BOTTOM], pts[0].y - disty );

    // left side ac's
    disty = (r[TOP] - pts[6].y) / 4.0f;
    pts[5].y = ss_fRand( pts[6].y + disty, r[TOP]);
    disty = (pts[6].y - -r[BOTTOM]) / 4.0f;
    pts[7].y = ss_fRand( -r[BOTTOM], pts[6].y - disty );

    // top ac's
    distx = (r[RIGHT] - pts[3].x) / 4.0f;
    pts[2].x = ss_fRand( pts[3].x + distx, r[RIGHT] );
    distx = (pts[3].x - -r[LEFT]) / 4.0f;
    pts[4].x = ss_fRand( -r[LEFT],  pts[3].x - distx );

    // bottom ac's
    distx = (r[RIGHT] - pts[9].x) / 4.0f;
    pts[10].x = ss_fRand( pts[9].x + distx, r[RIGHT] );
    distx = (pts[9].x - -r[LEFT]) / 4.0f;
    pts[8].x = ss_fRand( -r[LEFT], pts[9].x - distx );
}

/**************************************************************************\
* ConvertPtsZ
*
* Convert the 2D pts in an xc, to 3D pts in point buffer, with z.
*
* Also replicate the last point.
\**************************************************************************/

void
XC::ConvertPtsZ( POINT3D *newpts, float z )
{
    int i;
    POINT2D *xcPts = pts;

    for( i = 0; i < numPts; i++, newpts++ ) {
        *( (POINT2D *) newpts ) = *xcPts++;
        newpts->z = z;
    }
    *newpts = *(newpts - numPts);
}

/**************************************************************************\
* XC::CalcBoundingBox
*
* Calculate bounding box in x/y plane for xc
\**************************************************************************/

void
XC::CalcBoundingBox( )
{
    POINT2D *ppts = pts;
    int i;
    float xMin, xMax, yMax, yMin;

    // initialize to really insane numbers
    xMax = yMax = -FLT_MAX;
    xMin = yMin = FLT_MAX;

    // compare with rest of points
    for( i = 0; i < numPts; i ++, ppts++ ) {
        if( ppts->x < xMin )
            xMin = ppts->x;
        else if( ppts->x > xMax )
            xMax = ppts->x;
        if( ppts->y < yMin )
            yMin = ppts->y;
        else if( ppts->y > yMax )
            yMax = ppts->y;
    }
    xLeft = xMin;
    xRight = xMax;
    yBottom = yMin;
    yTop = yMax;
}

/**************************************************************************\
*
* MinTurnRadius
*
* Get minimum radius for the xc to turn in given direction.
*
* If the turn radius is less than this minimum, then primitive will 'fold'
* over itself at the inside of the turn, creating ugliness.
*
\**************************************************************************/

float
XC::MinTurnRadius( int relDir )
{
// for now, assume xRight, yTop positive, xLeft, yBottom negative
// otherwise, might want to consider 'negative'radius
    switch( relDir ) {
        case PLUS_X:
            return( xRight );
        case MINUS_X:
            return( - xLeft );
        case PLUS_Y:
            return( yTop );
        case MINUS_Y:
            return( - yBottom );
        default:
            return(0.0f);
    }
}
/**************************************************************************\
*
* XC::MaxExtent
*
* Get maximum extent of the xc in x and y
*
\**************************************************************************/

float
XC::MaxExtent( )
{
    float max;

    max = xRight;

    if( yTop > max )
        max = yTop;
    if( -xLeft > max )
        max = -xLeft;
    if( -yBottom > max )
        max = -yBottom;

    return max;
}

/**************************************************************************\
*
* XC::Scale
*
* Scale an XC's points and extents by supplied scale value
*
\**************************************************************************/

void
XC::Scale( float scale )
{
    int i;
    POINT2D *ppts = pts;

    for( i = 0; i < numPts; i ++, ppts++ ) {
        ppts->x *= scale;
        ppts->y *= scale;
    }

    xLeft *= scale;
    xRight *= scale;
    yBottom *= scale;
    yTop *= scale;
}

/**************************************************************************\
* ~XC::XC
*
* Destructor
*
\**************************************************************************/

XC::~XC()
{
    if( pts )
        LocalFree( pts );
}

/**************************************************************************\
* XC::XC
*
* Constructor
*
* - Allocates point buffer for the xc
*
\**************************************************************************/

XC::XC( int nPts )
{
    numPts = nPts;
    pts = (POINT2D *)  LocalAlloc( LMEM_FIXED, numPts * sizeof(POINT2D) );
    SS_ASSERT( pts != 0, "XC constructor\n" );
}

XC::XC( XC *xc )
{
    numPts = xc->numPts;
    pts = (POINT2D *)  LocalAlloc( LMEM_FIXED, numPts * sizeof(POINT2D) );
    SS_ASSERT( pts != 0, "XC constructor\n" );
    RtlCopyMemory( pts, xc->pts, numPts * sizeof(POINT2D) );

    xLeft = xc->xLeft;
    xRight = xc->xRight;
    yBottom = xc->yBottom;
    yTop = xc->yTop;
}

/**************************************************************************\
*
* ELLIPTICAL_XC::ELLIPTICALXC
*
* Elliptical XC constructor

* These have 4 sections of 4 pts each, with pts shared between sections.
*
\**************************************************************************/

ELLIPTICAL_XC::ELLIPTICAL_XC( float r1, float r2 )
    // initialize base XC with numPts
    : XC( (int) EVAL_XC_CIRC_SECTION_COUNT * (EVAL_ARC_ORDER - 1))
{
    SetControlPoints( r1, r2 );
    CalcBoundingBox( );
}

/**************************************************************************\
*
* RANDOM4ARC_XC::RANDOM4ARC_XC
*
* Random 4-arc XC constructor

* The bounding box is 2*r each side
* These have 4 sections of 4 pts each, with pts shared between sections.
*
\**************************************************************************/
RANDOM4ARC_XC::RANDOM4ARC_XC( float r )
    // initialize base XC with numPts
    : XC( (int) EVAL_XC_CIRC_SECTION_COUNT * (EVAL_ARC_ORDER - 1))
{
    SetControlPoints( r );
    CalcBoundingBox( );
}


XC.H

#ifndef __xc_h__
#define __xc_h__

#include "sscommon.h"

// useful for xc-coords
enum {
    RIGHT = 0,
    TOP,
    LEFT,
    BOTTOM
};

// Cross_section (xc) class

class XC {
public:
    GLfloat     xLeft, xRight;  // bounding box
    GLfloat     yTop, yBottom;
    int         numPts;
    POINT2D     *pts;        // CW points around the xc, from +x

    XC( int numPts );
    XC( const XC& xc );
    XC( XC *xc );
    ~XC();
    void        Scale( float scale );
    float       MaxExtent();
    float       MinTurnRadius( int relDir );
    void        CalcArcACValues90( int dir, float r, float *acPts );
    void        CalcArcACValuesByDistance(  float *acPts );
    void        ConvertPtsZ( POINT3D *pts, float z );
protected:
    void        CalcBoundingBox();
};

// Specific xc's derived from base xc class

class ELLIPTICAL_XC : public XC {
public:
    ELLIPTICAL_XC( float r1, float r2 );
    ~ELLIPTICAL_XC();
private:
    void SetControlPoints( float r1, float r2 );
};

class RANDOM4ARC_XC : public XC {
public:
    RANDOM4ARC_XC( float r );
    ~RANDOM4ARC_XC();
private:
    void SetControlPoints( float radius );
};

#endif __xc_h__


DIALOG.C

/******************************Module*Header*******************************\
* Module Name: dialog.c
*
* Dialog box functions for the OpenGL-based 3D Text screen saver.
*
* Copyright (c) 1994 Microsoft Corporation
\**************************************************************************/

#include <windows.h>
#include <commdlg.h>
#include <scrnsave.h>
#include <GL\gl.h>
#include <math.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys\timeb.h>
#include <time.h>
#include "sscommon.h"
#include "sstext3d.h"

// Global screen saver settings.

static int idsStyles[NUM_DEMOS] =
    {IDS_DEMO_STRING, IDS_DEMO_CLOCK};

static int idsRotStyles[NUM_ROTSTYLES] =
{    IDS_ROTSTYLE_NONE,
     IDS_ROTSTYLE_SEESAW,
     IDS_ROTSTYLE_WOBBLE,
     IDS_ROTSTYLE_RANDOM
};

// local funtions

LONG WndProc(HWND, UINT, WPARAM, LPARAM);
static void InitAttrContext( AttrContext *pac );
static void getFont( HWND hDlg);
static void updateDialogControls(HWND hDlg);

/******************************Public*Routine******************************\
* getIniSettings
*
* Get the screen saver configuration options from .INI file/registry.
*
\**************************************************************************/

void
getIniSettings()
{
    int    options;
    int    optMask = 1;
    TCHAR  szDefaultBitmap[MAX_PATH];
    int    iPos;

    // Initialize the global AttrContext

    InitAttrContext( &gac );

    // Load resources

    LoadString(hMainInstance, IDS_GENNAME, szScreenSaver,
               sizeof(szScreenSaver) / sizeof(TCHAR));

    // Get registry settings

    if( ss_RegistrySetup( hMainInstance, IDS_SAVERNAME, IDS_INIFILE ) )
    {
        // get demo type

        gac.demoType = ss_GetRegistryInt( IDS_DEMOTYPE, DEMO_STRING );
        SS_CLAMP_TO_RANGE2( gac.demoType, 0, MAX_DEMO);

        // get rotation style

        gac.rotStyle = ss_GetRegistryInt( IDS_ROTSTYLE, ROTSTYLE_RANDOM );
        SS_CLAMP_TO_RANGE2( gac.rotStyle, 0, NUM_ROTSTYLES-1 );

        // get tesselation

        iPos = ss_GetRegistryInt( IDS_TESSELATION, 10 );
        SS_CLAMP_TO_RANGE2( iPos, MIN_SLIDER, MAX_SLIDER );
        gac.fTesselFact  = (float)iPos / 100.0f;

        // get size

        gac.uSize = ss_GetRegistryInt( IDS_SIZE, 50 );
        SS_CLAMP_TO_RANGE2( gac.uSize, MIN_SLIDER, MAX_SLIDER );

        // get speed

        gac.iSpeed = ss_GetRegistryInt( IDS_SPEED, 50 );
        SS_CLAMP_TO_RANGE2( gac.iSpeed, MIN_SLIDER, MAX_SLIDER );

        // get surface style

        gac.surfStyle = ss_GetRegistryInt( IDS_SURFSTYLE, SURFSTYLE_SOLID );
        SS_CLAMP_TO_RANGE2(gac.surfStyle, 0, SURFSTYLE_TEX);

        // get font, attributes, and charset

        ss_GetRegistryString( IDS_FONT, TEXT("Arial"), gac.szFontName, LF_FACESIZE);

        options = ss_GetRegistryInt( IDS_FONT_ATTRIBUTES, 0 );
        if( options >= 0 ) {
            optMask = 1;
            gac.bBold = ((options & optMask) != 0);
            optMask <<=1;
            gac.bItalic = ((options & optMask) != 0);
        }

        gac.charSet = ss_GetRegistryInt( IDS_CHARSET, 0 );

        // get display string

        ss_GetRegistryString( IDS_TEXT, TEXT("OpenGL"), gac.szText, TEXT_LIMIT+1);

        // Determine the default .bmp file

        ss_GetDefaultBmpFile( szDefaultBitmap );

        // Is there a texture specified in the registry that overrides the
        // default?

        ss_GetRegistryString( IDS_TEXTURE, szDefaultBitmap, gac.texFile.szPathName,
                              MAX_PATH);

        gac.texFile.nOffset = ss_GetRegistryInt( IDS_TEXTURE_FILE_OFFSET, 0 );
    }
}

/**************************************************************************\
* ConfigInit
*
\**************************************************************************/
BOOL
ss_ConfigInit( HWND hDlg )
{
    return TRUE;
}

/**************************************************************************\
* InitAttrContext
*
* Initialize some of the values in the attribute context
\**************************************************************************/
static void
InitAttrContext( AttrContext *pac )
{
    // set some default values

    pac->demoType = DEMO_STRING;
    pac->surfStyle = SURFSTYLE_SOLID;
    pac->fTesselFact = 1.0f;
    pac->uSize = 50;
    pac->iSpeed = 50;
    pac->texFile.szPathName[0] = '\0';
    pac->texFile.nOffset = 0;
}

/******************************Public*Routine******************************\
* saveIniSettings
*
* Save the screen saver configuration option to the .INI file/registry.
\**************************************************************************/

static void
saveIniSettings(HWND hDlg)
{
    int options;
    int optMask = 1;

    GetWindowText( GetDlgItem(hDlg, DLG_TEXT_ENTER), gac.szText, TEXT_LIMIT+1);

    if( ss_RegistrySetup( hMainInstance, IDS_SAVERNAME, IDS_INIFILE ) )
    {
        ss_WriteRegistryInt( IDS_DEMOTYPE, gac.demoType );
        ss_WriteRegistryInt( IDS_ROTSTYLE, gac.rotStyle );
        ss_WriteRegistryInt( IDS_TESSELATION,
                    ss_GetTrackbarPos(hDlg, DLG_SETUP_TESSEL) );
        ss_WriteRegistryInt( IDS_SIZE,
                    ss_GetTrackbarPos(hDlg, DLG_SETUP_SIZE) );
        ss_WriteRegistryInt( IDS_SPEED,
                    ss_GetTrackbarPos(hDlg, DLG_SETUP_SPEED) );
        ss_WriteRegistryInt( IDS_SURFSTYLE, gac.surfStyle );
        ss_WriteRegistryString( IDS_FONT, gac.szFontName );

        optMask = 1;
        options = gac.bBold ? optMask : 0;
        optMask <<= 1;
        options |= gac.bItalic ? optMask : 0;
        ss_WriteRegistryInt( IDS_FONT_ATTRIBUTES, options );

        ss_WriteRegistryInt( IDS_CHARSET, gac.charSet );
        ss_WriteRegistryString( IDS_TEXT, gac.szText );
        ss_WriteRegistryString( IDS_TEXTURE, gac.texFile.szPathName );
        ss_WriteRegistryInt( IDS_TEXTURE_FILE_OFFSET, gac.texFile.nOffset );
    }
}

/******************************Public*Routine******************************\
* setupDialogControls
*
* Do initial setup of dialog controls.
\**************************************************************************/

static void
setupDialogControls(HWND hDlg)
{
    int pos;

    InitCommonControls();

    // initialize sliders

    // tesselation slider

    pos = (int)(gac.fTesselFact * 100.0f);
    ss_SetupTrackbar( hDlg, DLG_SETUP_TESSEL, MIN_SLIDER, MAX_SLIDER, 1, 9,
                      pos );

    // size slider

    ss_SetupTrackbar( hDlg, DLG_SETUP_SIZE, MIN_SLIDER, MAX_SLIDER, 1, 9,
                      gac.uSize );

    // speed slider

    ss_SetupTrackbar( hDlg, DLG_SETUP_SPEED, MIN_SLIDER, MAX_SLIDER, 1, 9,
                      gac.iSpeed);

    // set state of other controls

    updateDialogControls(hDlg);
}

/******************************Public*Routine******************************\
* updateDialogControls
*
* Updates dialog controls according to current state
\**************************************************************************/

static void
updateDialogControls(HWND hDlg)
{
    BOOL bTexSurf;
    BOOL bText;

    bTexSurf = (gac.surfStyle == SURFSTYLE_TEX );

    CheckDlgButton(hDlg, IDC_RADIO_SOLID, !bTexSurf );
    CheckDlgButton(hDlg, IDC_RADIO_TEX  , bTexSurf );

    // set up demo-specific configure button

    bText = (gac.demoType == DEMO_STRING) ? TRUE : FALSE;
    EnableWindow(GetDlgItem(hDlg, DLG_TEXT_ENTER), bText );
    CheckDlgButton(hDlg, IDC_DEMO_STRING, bText );
    CheckDlgButton(hDlg, IDC_DEMO_CLOCK, !bText );

    // texture: only enable if surfStyle is texture

    EnableWindow(GetDlgItem(hDlg, DLG_SETUP_TEX), bTexSurf );
}

/******************************Public*Routine******************************\
* getFont
*
* Calls ChooseFont dialog
\**************************************************************************/

static void
getFont( HWND hDlg)
{
    CHOOSEFONT cf = {0};
    LOGFONT    lf = {0};
    HDC   hdc;

    hdc = GetDC( hDlg );

    // Create and select a font.

    cf.lStructSize = sizeof(CHOOSEFONT);
    cf.hwndOwner = hDlg;
    cf.lpLogFont = &lf;
    cf.hInstance = hMainInstance;
    cf.lpTemplateName = (LPTSTR) MAKEINTRESOURCE(IDD_FONT);
    cf.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_TTONLY |
               CF_ENABLETEMPLATE | CF_NOSIMULATIONS;

    // setup logfont with current settings

    lstrcpy(lf.lfFaceName, gac.szFontName);
    lf.lfWeight = (gac.bBold) ? FW_BOLD : FW_NORMAL;
    lf.lfItalic = (gac.bItalic) ? (BYTE) 1 : 0;
    lf.lfCharSet = gac.charSet;
    lf.lfHeight = -37;  // value ???

    if( ChooseFont(&cf) ) {
        // retrieve settings into gac
        lstrcpy( gac.szFontName, lf.lfFaceName );
        gac.bBold = (lf.lfWeight == FW_NORMAL) ? FALSE : TRUE;
        gac.bItalic = (lf.lfItalic) ? TRUE : FALSE;
        gac.charSet = lf.lfCharSet;
    }
}

BOOL WINAPI RegisterDialogClasses(HANDLE hinst)
{
    return TRUE;
}

/******************************Public*Routine******************************\
* ScreenSaverConfigureDialog
*
* Processes messages for the configuration dialog box.
*
\**************************************************************************/

BOOL ScreenSaverConfigureDialog(HWND hDlg, UINT message,
                                WPARAM wParam, LPARAM lParam)
{
    int wTmp;
    TCHAR szStr[GEN_STRING_SIZE];

    switch (message) {
        case WM_INITDIALOG:
            getIniSettings();
            setupDialogControls(hDlg);

            // setup rotStyle combo box
            for (wTmp = 0; wTmp < NUM_ROTSTYLES; wTmp++) {
                LoadString(hMainInstance, idsRotStyles[wTmp], szStr,
                            GEN_STRING_SIZE);
                SendDlgItemMessage(hDlg, DLG_SETUP_ROTSTYLE, CB_ADDSTRING, 0,
                                   (LPARAM) szStr);
            }
            SendDlgItemMessage(hDlg, DLG_SETUP_ROTSTYLE, CB_SETCURSEL,
                               gac.rotStyle, 0);

            // display current string in box
            SendDlgItemMessage( hDlg, DLG_TEXT_ENTER, EM_LIMITTEXT, TEXT_LIMIT,0);
            SetWindowText( GetDlgItem(hDlg, DLG_TEXT_ENTER), gac.szText );

            return TRUE;

        case WM_COMMAND:
            switch (LOWORD(wParam)) {
                case DLG_SETUP_TYPES:
                    switch (HIWORD(wParam))
                    {
                        case CBN_EDITCHANGE:
                        case CBN_SELCHANGE:
                            gac.demoType =
                                SendDlgItemMessage(hDlg, DLG_SETUP_TYPES,
                                                      CB_GETCURSEL, 0, 0);
                            updateDialogControls(hDlg);
                            break;
                        default:
                            break;
                    }
                    return FALSE;

                case DLG_SETUP_ROTSTYLE:
                    switch (HIWORD(wParam))
                    {
                        case CBN_EDITCHANGE:
                        case CBN_SELCHANGE:
                            gac.rotStyle =
                                SendDlgItemMessage(hDlg, DLG_SETUP_ROTSTYLE,
                                                      CB_GETCURSEL, 0, 0);
                            updateDialogControls(hDlg);
                            break;
                        default:
                            break;
                    }
                    return FALSE;

                case DLG_SETUP_TEX:
                    // Run choose texture dialog
                    ss_SelectTextureFile( hDlg, &gac.texFile );
                    break;

                case DLG_SETUP_FONT:
                    getFont(hDlg);
                    break;

                case IDC_RADIO_SOLID:
                case IDC_RADIO_TEX:
                    gac.surfStyle = IDC_TO_SURFSTYLE( LOWORD(wParam) );
                    updateDialogControls(hDlg);
                    break;

                case IDC_DEMO_STRING:
                case IDC_DEMO_CLOCK:
                    gac.demoType = IDC_TO_DEMOTYPE( LOWORD(wParam) );
                    updateDialogControls(hDlg);
                    if( LOWORD(wParam) == IDC_DEMO_STRING ) {
                        // set selected text focus
                        SetFocus( GetDlgItem(hDlg, DLG_TEXT_ENTER) );
                        SendDlgItemMessage( hDlg, DLG_TEXT_ENTER,
                                            EM_SETSEL, 0, -1 );
                    }
                    break;

                case IDOK:
                    saveIniSettings(hDlg);
                    EndDialog(hDlg, TRUE);
                    break;

                case IDCANCEL:
                    EndDialog(hDlg, FALSE);
                    break;

                default:
                    break;
            }
            return TRUE;
            break;

        default:
            return 0;
    }
    return 0;
}


FONT.C

/******************************Module*Header*******************************\
* Module Name: font.c
*
* Font and text handling for the OpenGL-based 3D Text screen saver.
*
* Copyright (c) 1994 Microsoft Corporation
\**************************************************************************/

#include <windows.h>
#include <commdlg.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <float.h> // for FLT_MAX
#include <GL/gl.h>
#include "sscommon.h"
#include "sstext3d.h"

#ifndef UNICODE
// Support dbcs characters (this is a patch)
#define SS_DBCS_SUPPORT 1
#endif

static LISTENTRY* FindExtendedLUTEntry( TCHAR c, WglFontContext *pwfc );
static BOOL UpdateReducedFontList( TCHAR c, LISTENTRY *ple,
                                   WglFontContext *pwfc );
#ifdef SS_DBCS_SUPPORT
static BOOL UpdateReducedFontListDBCS( DWORD c, LISTENTRY *ple,
                                       WglFontContext *pwfc );
#endif
static void DeleteExtendedLUTTableEntries( WglFontContext *pwfc );
static void DeleteDirectLUTTableEntries( WglFontContext *pwfc );
static void DeleteLUTEntry( LISTENTRY *ple );

/******************************Public*Routine******************************\
* CreateWglFontContext
*
* Create a WglFontContext.
*
* Initializes the look-up-table memory for characters.
* The first half of this table is a direct look-up, using the char value.
* The second half is used for unicode chars that are >256, and must be
* searched sequentially.
*
\**************************************************************************/

WglFontContext *
CreateWglFontContext( HDC hdc, int type, float fExtrusion,
                      float fChordalDeviation )
{
    WglFontContext *pwfc;

    pwfc = (WglFontContext *) LocalAlloc( LMEM_FIXED, sizeof(WglFontContext) );
    if( !pwfc ) {
        SS_ALLOC_FAILURE( "CreateWglFontContext" );
        return NULL;
    }

    pwfc->hdc = hdc;
    pwfc->type = type;
    pwfc->extrusion = fExtrusion;
    pwfc->chordalDeviation = fChordalDeviation;

    pwfc->listLUT = (LISTENTRY*) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
                                      SIZE_LIST_LUT * sizeof(LISTENTRY) );

    if( !pwfc->listLUT ) {
        LocalFree( pwfc );
        SS_ALLOC_FAILURE( "CreateWglFontContext" );
        return NULL;
    }

    pwfc->LUTIndex = 0;

    return pwfc;
}

/******************************Public*Routine******************************\
* DeleteWglFontContext
*
* Delete a WglFontContext.
*
\**************************************************************************/

void
DeleteWglFontContext( WglFontContext *pwfc )
{
    if( !pwfc )
        return;

    // Delete the extended lut table entries
    DeleteExtendedLUTTableEntries( pwfc );

    // Delete the direct lut table entries
    DeleteDirectLUTTableEntries( pwfc );

    if( pwfc->listLUT )
        LocalFree( pwfc->listLUT );

    LocalFree( pwfc );
}

/******************************Public*Routine******************************\
* DeleteExtendedLUTTablEntries
*
\**************************************************************************/

static void
DeleteExtendedLUTTableEntries( WglFontContext *pwfc )
{
    int i;
    LISTENTRY *ple = pwfc->listLUT + MAX_DIRECT_LUT;

    for( i = 0; i < pwfc->LUTIndex; i ++, ple++ )
        DeleteLUTEntry( ple );

    pwfc->LUTIndex = 0;
}

/******************************Public*Routine******************************\
* DeleteDirectLUTTablEntries
*
\**************************************************************************/

static void
DeleteDirectLUTTableEntries( WglFontContext *pwfc )
{
    int i;
    LISTENTRY *ple = pwfc->listLUT;

    for( i = 0; i < MAX_DIRECT_LUT; i ++, ple++ )
        DeleteLUTEntry( ple );
}

/******************************Public*Routine******************************\
* DeleteLUTEntry
*
\**************************************************************************/

static void
DeleteLUTEntry( LISTENTRY *ple )
{
    if( ple->listNum ) {
        glDeleteLists( ple->listNum, 1 );
        ple->listNum = 0;
    }
    if( ple->lpgmf ) {
        LocalFree( ple->lpgmf );
        ple->lpgmf = NULL;
    }
    ple->glyph = 0;
}

/**************************************************************************\
* ConvertStringToList
*
* - Translate the string into display list numbers
* - If no entry in the list table, update the table
*
\**************************************************************************/
void
ConvertStringToList( LPTSTR pszSrc, USHORT *usDst, WglFontContext *pwfc )
{
    int strLen;
    LISTENTRY *ple;
    TCHAR c;
    TUCHAR cindex; // use unsigned TCHAR for table indexing
#ifdef SS_DBCS_SUPPORT
    BOOL bIsLeadByte;
#endif

    if( !pwfc )
        return;

    /* Go thru the string, making sure every char has a display list
     * Copy the display list # into usDst
     */

    strLen = lstrlen( pszSrc );
    if( strLen > TEXT_BUF_SIZE )
        strLen = TEXT_BUF_SIZE;

    /* Check for possible overflow with the extended LUT section, and
     * invalidate the extended LUT if so
     */
    if( pwfc->LUTIndex ) {
        int roomLeft; // spaces left in extended lut table

        roomLeft = SIZE_LIST_LUT - (pwfc->LUTIndex + MAX_DIRECT_LUT +1);
        if( roomLeft < strLen ) {
            /* If none of the characters in this string have entries in the
             * table, we'll run out of room.  Assume the worst, invalidate
             * the whole table, and start over again.
             */
            DeleteExtendedLUTTableEntries( pwfc );
        }
    }

    for( ; strLen; strLen--, pszSrc++ ) {
        c = *pszSrc;
        cindex = (TUCHAR) c;

        // see if char already has cmd list entry - if not, create it
#ifdef SS_DBCS_SUPPORT
        bIsLeadByte = IsDBCSLeadByte(cindex);
        if( !bIsLeadByte && (cindex < MAX_DIRECT_LUT) ) {  // direct LUT
#else
        if( cindex < MAX_DIRECT_LUT ) {  // direct LUT entry
#endif
            ple = &pwfc->listLUT[cindex];
            if( !ple->listNum ) {
                // have to create new cmd list for this glyph
                if( ! UpdateReducedFontList( c, ple, pwfc ) ) {
                    DeleteLUTEntry( ple );
                    continue;
                }
            }
        } else {  // extended LUT entry
#ifdef SS_DBCS_SUPPORT
            DWORD dwChar;
            if( bIsLeadByte ) {
                // Iterate over the lead byte
                pszSrc++;
                strLen--;
                c = *pszSrc;
                // Setup the double byte word so that :
                //   High byte is the lead byte
                //   Low byte is the next byte in the byte stream (this byte
                //   will be used to access the extended lut table)
                dwChar = (DWORD) ( (cindex << 8) | ((TUCHAR) c)  );
            } else
                dwChar = (DWORD) (TUCHAR) c;

            if( !(ple = FindExtendedLUTEntry( c, pwfc )) ) {
                ple = &pwfc->listLUT[pwfc->LUTIndex+MAX_DIRECT_LUT];
                if( ! UpdateReducedFontListDBCS( dwChar, ple, pwfc ) ) {
                    DeleteLUTEntry( ple );
                    continue;
                }
                ple->glyph = c;
                pwfc->LUTIndex++;
            }
#else
            if( !(ple = FindExtendedLUTEntry( c, pwfc )) ) {
                ple = &pwfc->listLUT[pwfc->LUTIndex+MAX_DIRECT_LUT];
                if( ! UpdateReducedFontList( c, ple, pwfc ) ) {
                    DeleteLUTEntry( ple );
                    continue;
                }
                ple->glyph = c;
                pwfc->LUTIndex++;
            }
#endif
        }
        *usDst++ = ple->listNum;
    }
    // zero terminate
    *usDst = 0;
}

/******************************Public*Routine******************************\
* UpdateReducedFontList
*
* Calls wglUseFontOutlines() to create a command list for the supplied
* character.
*
* Allocates memory for the characters glyphmetrics as well
*
\**************************************************************************/
static BOOL
UpdateReducedFontList( TCHAR c, LISTENTRY *ple, WglFontContext *pwfc )
{
    ple->lpgmf = (LPGLYPHMETRICSFLOAT) LocalAlloc( LMEM_FIXED,
                                                sizeof( GLYPHMETRICSFLOAT ) );

    if( !ple->lpgmf ) {
        SS_ALLOC_FAILURE( "UpdateReducedFontList" );
        return FAILURE;
    }

    // get next list # for this glyph.
    ple->listNum = (USHORT) glGenLists( 1 );
    if( !ple->listNum ) {
        SS_WARNING( "glGenLists failed\n" );
        return FAILURE;
    }

    if( !wglUseFontOutlines(pwfc->hdc, (TUCHAR) c, 1, ple->listNum,
               pwfc->chordalDeviation,
               pwfc->extrusion,
               pwfc->type, ple->lpgmf) ) {
        SS_WARNING( "wglUseFontOutlines failed\n" );
        return FAILURE;
    }
    return SUCCESS;
}

#ifdef SS_DBCS_SUPPORT

// Modified version of UpdateReducedFontList that handles dbcs characters.
// This is a *patch* (as is all the SS_DBCS_SUPPORT stuff) to handle dbcs
// characters without jeopardizing SUR release stability.  Later, the functions
// can be consolidated.

static BOOL
UpdateReducedFontListDBCS( DWORD c, LISTENTRY *ple, WglFontContext *pwfc )
{
    ple->lpgmf = (LPGLYPHMETRICSFLOAT) LocalAlloc( LMEM_FIXED,
                                                sizeof( GLYPHMETRICSFLOAT ) );

    if( !ple->lpgmf ) {
        SS_ALLOC_FAILURE( "UpdateReducedFontList" );
        return FAILURE;
    }

    // get next list # for this glyph.
    ple->listNum = (USHORT) glGenLists( 1 );
    if( !ple->listNum ) {
        SS_WARNING( "glGenLists failed\n" );
        return FAILURE;
    }

    if( !wglUseFontOutlines(pwfc->hdc, c, 1, ple->listNum,
               pwfc->chordalDeviation,
               pwfc->extrusion,
               pwfc->type, ple->lpgmf) ) {
        SS_WARNING( "wglUseFontOutlines failed\n" );
        return FAILURE;
    }
    return SUCCESS;
}
#endif

/******************************Public*Routine******************************\
* FindExtendedLUTEntry
*
* Searches through the extended character LUT, and returns ptr to the
* char's info if found, otherwise NULL.
*
\**************************************************************************/

static LISTENTRY*
FindExtendedLUTEntry( TCHAR c, WglFontContext *pwfc )
{
    int i;
    LISTENTRY *ple = pwfc->listLUT + MAX_DIRECT_LUT;

    for( i = 0; i < pwfc->LUTIndex; i ++, ple++ ) {
        if( ple->glyph == c )
            return ple;
    }
    return NULL;
}

/******************************Public*Routine******************************\
* DrawString
*
* Draws string by calling cmd list for each char.
*
\**************************************************************************/

void
DrawString(  USHORT           *string,
             int              strLen,
             WglFontContext   *pwfc )
{
    if( !pwfc )
        return;

    glCallLists(strLen, GL_UNSIGNED_SHORT, (GLushort *) string);
}

/******************************Public*Routine******************************\
* GetStringExtent
*
* Calculate a string's origin and extent in world coordinates.
*
* The origin is determined by the first character's location, and thereafter
* each char's location is determined by adding the previous char's cellInc
* values.
*
* This will work for all string orientations.
*
\**************************************************************************/

int
GetStringExtent(  LPTSTR          szString,
                  POINTFLOAT      *extent,
                  POINTFLOAT      *origin,
                  WglFontContext  *pwfc )
{

    int len, strLen;
    TCHAR *c;
    TUCHAR cindex;
    LPGLYPHMETRICSFLOAT lpgmf;
    POINTFLOAT cellOrigin = {0.0f, 0.0f};
    POINTFLOAT extentOrigin = {FLT_MAX, -FLT_MAX};
    POINTFLOAT extentLowerRight = {-FLT_MAX, FLT_MAX};
    POINTFLOAT boxOrigin, boxLowerRight;
    LISTENTRY *listLUT, *ple;
#ifdef SS_DBCS_SUPPORT
    BOOL bIsLeadByte;
#endif

    if( !pwfc )
        return 0;

    listLUT = pwfc->listLUT;

    extent->x = extent->y = 0.0f;
    origin->x = origin->y = 0.0f;
    len = strLen = lstrlen( szString );
    if( !len )
        return 0;  // otherwise extents will be calc'd erroneously

    c = szString;

    for( ; strLen; strLen--, c++ ) {
        cindex = (TUCHAR) *c;
#ifdef SS_DBCS_SUPPORT
        if( bIsLeadByte = IsDBCSLeadByte(cindex) ) {
            // iterate over lead byte
            c++;
            strLen--;
            cindex = (TUCHAR) *c;
        }
        if( !bIsLeadByte && (cindex < MAX_DIRECT_LUT) )
#else
        if( cindex < MAX_DIRECT_LUT )
#endif
            ple = &listLUT[cindex];
        else
            ple = FindExtendedLUTEntry( *c, pwfc );

        lpgmf = ple->lpgmf;

        if( !lpgmf )
            // Memory must be running low, but keep going
            continue;

        // calc global position of this char's BlackBox (this is
        //  'upper left')
        boxOrigin.x = cellOrigin.x + lpgmf->gmfptGlyphOrigin.x;
        boxOrigin.y = cellOrigin.y + lpgmf->gmfptGlyphOrigin.y;

        // calc lower right position
        boxLowerRight.x = boxOrigin.x + lpgmf->gmfBlackBoxX;
        boxLowerRight.y = boxOrigin.y - lpgmf->gmfBlackBoxY;

        // compare against the current bounding box

        if( boxOrigin.x < extentOrigin.x )
            extentOrigin.x = boxOrigin.x;
        if( boxOrigin.y > extentOrigin.y )
            extentOrigin.y = boxOrigin.y;
        if( boxLowerRight.x > extentLowerRight.x )
            extentLowerRight.x = boxLowerRight.x;
        if( boxLowerRight.y < extentLowerRight.y )
            extentLowerRight.y = boxLowerRight.y;

        // set global position of next cell
        cellOrigin.x = cellOrigin.x + lpgmf->gmfCellIncX;
        cellOrigin.y = cellOrigin.y + lpgmf->gmfCellIncY;
    }

    // Check for possible total lack of glyphmetric info
    if( extentOrigin.x == FLT_MAX ) {
        // Can assume if this value is still maxed out that all glyphmetric
        // info was NULL
        origin->x = origin->y = 0.0f;
        extent->x = extent->y = 0.0f;
        return 0;
    }

    *origin = extentOrigin;
    extent->x = extentLowerRight.x - extentOrigin.x;
    extent->y = extentOrigin.y - extentLowerRight.y;
    return len;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by sstext3d.rc
//
#define IDI_ICON1                       103
#define IDD_DIALOG1                     104
#define IDC_BUTTON1                     1022
#define IDC_EDIT1                       1023
#define IDC_STATIC                      -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        106
#define _APS_NEXT_COMMAND_VALUE         101
#define _APS_NEXT_CONTROL_VALUE         1042
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SSTEXT3D.C

/******************************Module*Header*******************************\
* Module Name: sstext3d.c
*
* Core code for text3D screen saver
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <fcntl.h>
#include <io.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include <windows.h>
#include <scrnsave.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>

#include "sscommon.h"
#include "sstext3d.h"

#define FMAX_CHORDAL_DEVIATION   0.008f

#define FMIN_DEPTH           0.15f
#define FMAX_DEPTH           0.6f

#define FMIN_VIEW_ANGLE      90.0f
#define FMAX_VIEW_ANGLE     130.0f

#define FMIN_RANDOM_ANGLE    45.0f
#define FMAX_RANDOM_ANGLE    89.0f

#define FMIN_SEESAW_ANGLE    63.0f
#define FMAX_SEESAW_ANGLE    88.0f

#define FMIN_WOBBLE_ANGLE    30.0f
#define FMAX_WOBBLE_ANGLE    55.0f
#define FMIN_WOBBLE_ANGLE2   40.0f
#define FMAX_WOBBLE_ANGLE2   80.0f

#define MIN_ROT_STEP          1
#define MAX_ROT_STEP         20

#define FMAX_ZOOM             5.0f

// globals

static FLOAT gfMinCycleTime = 10.0f;
static POINTFLOAT gTrig[360];  // pre-calculated table of sines and cosines
static POINTFLOAT gSawTooth[360]; // sawtooth table
static POINTFLOAT gInvTrig[360];  // pseudo-inverse trig table
static POINT gTrigDif[360];   // table for converting trig->invtrig
static POINT gInvTrigDif[360];   // table for converting invtrig->trig

AttrContext gac;

// Default texture resource
TEX_RES gTexRes = { TEX_BMP, IDB_DEFTEX };

typedef struct _LIST *PLIST;
typedef struct _LIST {
    PLIST pnext;
    PLIST plistComplete;
    LPTSTR pszStr;
} LIST;

PLIST gplistComplete = NULL;
PLIST gplist = NULL;
static void DeleteNameList();

void text3d_Init( void *data  );
void text3d_Reset(void *data );
void text3d_Draw(void *data );
void text3d_Reshape(int width, int height, void *data );
void text3d_Finish( void *data );
static void CalcViewParams( AttrContext *pac );
static BOOL InitFont( AttrContext *pac );
static void InitLighting( AttrContext *pac );
static void InitTexture( AttrContext *pac );
static void InitMaterials( AttrContext *pac );
static void InitView( AttrContext *pac );
static FLOAT MapValue( FLOAT fInVal,
                       FLOAT fIn1, FLOAT fIn2,
                       FLOAT fOut1, FLOAT fOut2 );
static int  MapValueI( int inVal, int in1, int in2, int out1, int out2 );
static FLOAT CalcChordalDeviation( HDC hdc, AttrContext *pac );
static void (*BoundingBoxProc)( AttrContext *pac);
static void CalcBoundingBox( AttrContext *pac );
static void CalcBoundingBoxFromSphere( AttrContext *pac );
static void CalcBoundingBoxFromSpherePlus( AttrContext *pac, FLOAT zmax );
static void CalcBoundingBoxGeneric( AttrContext *pac );
static void CalcBoundingBoxFromExtents( AttrContext *pac, POINT3D *box );
static void CalcBoundingExtent( FLOAT rot, FLOAT x, FLOAT y,
                                POINTFLOAT *extent );
static void SortBoxes( POINT3D *box, FLOAT *boxvp, int numBox );
static void (*GetNextRotProc)( AttrContext *pac );
static void GetNextRotNone( AttrContext *pac );
static void GetNextRotRandom( AttrContext *pac );
static void GetNextRotWobble( AttrContext *pac );
static void InitTrigTable();
static void text3d_UpdateTime( AttrContext *pac, BOOL bCheckBounds );
static void text3d_UpdateString( AttrContext *pac, BOOL bCheckBounds );
static BOOL VerifyString( AttrContext *pac );
static BOOL CheckKeyStrings( LPTSTR testString, PSZ psz );
static void ConvertStringAsciiToUnicode( PSZ psz, PWSTR pwstr, int len );
static void InvertBitsA( char *s, int len );
static void ReadNameList();
static PSZ ReadStringFileA( char *file );
static void CreateRandomList();
static void ResetRotationLimits( AttrContext *pac, int *reset );
static int FrameCalibration( AttrContext *pac, struct _timeb *pBaseTime, int framesPerCycle,
                             int nCycle );
static void SetTransitionPoints( AttrContext *pac, int framesPerCycle,
                                int *trans1, int *trans2, FLOAT *zTrans );
static void
AdjustRotationStep( AttrContext *pac, int *reset, POINTFLOAT *oldTrig );

/******************************Public*Routine******************************\
* SetFloaterInfo
*
* Set the size and motion of the floating window
*
* ss_SetWindowAspectRatio may be called after this, to finely crop the
* window to the text being displayed.  But we can't call it here, since this
* function is called by common when creating the floating window, before the
* text string size has been determined.
\**************************************************************************/

static void
SetFloaterInfo( ISIZE *pParentSize, CHILD_INFO *pChild )
{
    float sizeFact;
    float sizeScale;
    int size;
    ISIZE *pChildSize = &pChild->size;
    MOTION_INFO *pMotion = &pChild->motionInfo;
    AttrContext *pac = &gac;

    sizeScale = (float)pac->uSize / 100.0f;  // range 0..1
    sizeFact = 0.25f + (0.5f * sizeScale);     // range 25-75%
    size = (int) (sizeFact *
            ( ((float)(pParentSize->width + pParentSize->height)) / 2.0f ));
    SS_CLAMP_TO_RANGE2( size, 0, pParentSize->width );
    SS_CLAMP_TO_RANGE2( size, 0, pParentSize->height );

    pChildSize->width = pChildSize->height = size;
    pMotion->posInc.x = .01f * (float) size;
    if( pMotion->posInc.x < 1.0f )
        pMotion->posInc.x = 1.0f;
    pMotion->posInc.y = pMotion->posInc.x;
    pMotion->posIncVary.x = .4f * pMotion->posInc.x;
    pMotion->posIncVary.y = pMotion->posIncVary.x;
}

/******************************Public*Routine******************************\
* Init
*
* Initialize - called on first entry into ss.
* Called BEFORE gl is initialized!
* Just do basic stuff here, like set up callbacks, verify dialog stuff, etc.
*
* Fills global SSContext structure with required data, and returns ptr
* to it.
*
\**************************************************************************/

SSContext *
ss_Init( void )
{
    // validate some initial dialog settings
    getIniSettings();  // also called on dialog init

    // must verify textures here, before GL floater windows are created
    if( gac.surfStyle == SURFSTYLE_TEX ) {
        ss_DisableTextureErrorMsgs();
        ss_VerifyTextureFile( &gac.texFile );
    }

    // set Init callback
    ss_InitFunc( text3d_Init );

    // set data ptr to be sent with callbacks
    ss_DataPtr( &gac );

    // set configuration info to return

    gac.ssc.bFloater = TRUE;
    gac.ssc.floaterInfo.bMotion = TRUE;
    gac.ssc.floaterInfo.ChildSizeFunc = SetFloaterInfo;

    gac.ssc.bDoubleBuf = TRUE;
    gac.ssc.depthType = SS_DEPTH16;

    return &gac.ssc;
}

/******************************Public*Routine******************************\
* text3d_Init
*
* Initializes OpenGL state for text3d screen saver
*
\**************************************************************************/
void
text3d_Init( void *data )
{
    AttrContext *pac = (AttrContext *) data;

    // Set any callbacks that require GL
    ss_UpdateFunc( text3d_Draw );
    ss_ReshapeFunc( text3d_Reshape );
    ss_FinishFunc( text3d_Finish );

#ifdef SS_DEBUG
    glClearColor( 0.2f, 0.2f, 0.2f, 0.0f );
#else
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
#endif

    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);

    // this sequence must be maintained

    InitLighting( pac );

    InitFont( pac );

    InitView( pac );

    InitTexture( pac );

    InitMaterials( pac );
}

/**************************************************************************\
* InitLighting
*
* Initialize lighting, and back face culling.
*
\**************************************************************************/
static void
InitLighting( AttrContext *pac )
{
    float ambient1[] = {0.2f, 0.2f, 0.2f, 1.0f};
    float ambient2[] = {0.1f, 0.1f, 0.1f, 1.0f};
    float diffuse1[] = {0.7f, 0.7f, 0.7f, 1.0f};
    float diffuse2[] = {0.7f, 0.7f, 0.7f, 1.0f};
    float position1[] = {0.0f, 50.0f, 150.0f, 0.0f};
    float position2[] = {25.0f, 150.0f, 50.0f, 0.0f};
    float lmodel_ambient[] = {1.0f, 1.0f, 1.0f, 1.0f};

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient1);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse1);
    glLightfv(GL_LIGHT0, GL_POSITION, position1);
    glEnable(GL_LIGHT0);

    glLightfv(GL_LIGHT1, GL_AMBIENT, ambient2);
    glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse2);
    glLightfv(GL_LIGHT1, GL_POSITION, position2);
    glEnable(GL_LIGHT1);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
    glCullFace( GL_BACK );
    glEnable(GL_CULL_FACE);
    glEnable(GL_LIGHTING);
}

/**************************************************************************\
* TestFont
*
* Test that GetOutlineTextMetrics works.  If not, wglUseFontOutlines will fail.
*
* If the font tests bad, delete it and select in the previous one.
*
\**************************************************************************/

static BOOL
TestFont( HFONT hfont )
{
    OUTLINETEXTMETRIC otm;
    HFONT hfontOld;
    HDC hdc = wglGetCurrentDC();

    hfontOld = SelectObject(hdc, hfont);

    if( GetOutlineTextMetrics( hdc, sizeof(otm), &otm) <= 0 ) {
        SS_DBGPRINT( "sstext3d Init: GetOutlineTextMetrics failure\n" );
        SelectObject(hdc, hfontOld);
        DeleteObject( hfont );
        return FALSE;
    }
    return TRUE;
}

/**************************************************************************\
* CreateFont
*
* Create a true type font and test it
*
\**************************************************************************/

static HFONT
text3d_CreateFont( LOGFONT *plf )
{
    HFONT hfont;

    // Create font from LOGFONT data
    hfont = CreateFontIndirect(plf);

    if( hfont ) {
        // Test the font
        if( ! TestFont( hfont ) )
            hfont = (HFONT) 0;
    }
    return hfont;
}

/**************************************************************************\
* InitFont
*
*
\**************************************************************************/
static BOOL
InitFont( AttrContext *pac )
{
    LOGFONT lf;
    HFONT hfont;
    int type;
    float fChordalDeviation;
    HDC hdc = wglGetCurrentDC();

    // Set up the LOGFONT structure

    memset(&lf, 0, sizeof(LOGFONT));
    lstrcpy( lf.lfFaceName, pac->szFontName );
    lf.lfWeight = (pac->bBold) ? FW_BOLD : FW_NORMAL;
    lf.lfItalic = (pac->bItalic) ? (BYTE) 1 : 0;

    lf.lfHeight = 0; // shouldn't matter
    lf.lfCharSet = pac->charSet;
    lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;

    // Create the font

    if( ! (hfont = text3d_CreateFont( &lf )) ) {
        // Couldn't create a true type font with supplied data
        SS_DBGPRINT( "initial text3d_CreateFont failed: \n" );
    }

    if( !hfont && ss_fOnWin95() ) {
    // the font mapper didn't give us anything useful
    // For some reason GetOutlineTextMetrics fails for some fonts (Symbol)
    // when using lfHeight = 0 (default height value).
        lf.lfHeight = -10;
        if( ! (hfont = text3d_CreateFont( &lf )) ) {
            SS_DBGPRINT( "text3d_CreateFont with lfHeight != 0 failed: \n" );
        }
    }

    if( hfont == NULL ) {
        /* The requested font cannot be loaded.  Try to get the system to
         * load any TrueType font
         */
        hfont = CreateFont( 100, 100, 0, 0, lf.lfWeight, lf.lfItalic,
                             0, 0, 0, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS,
                             DEFAULT_QUALITY, DEFAULT_PITCH, NULL );
        // If hfont is still null, nothing will be displayed.
        if( !hfont || !TestFont(hfont) ) {
            SS_DBGPRINT( "text3d_InitFont failure\n" );
            return FALSE;
        }
    }

    // We have a valid font

    SelectObject(hdc, hfont);

    // Set extrusion, chordal deviation, and font type

#ifdef _PPC_
    // !!! Work around for PPC compiler bug

    // calculate chordalDeviation from input attribute fTesselFact
    fChordalDeviation = CalcChordalDeviation( hdc, pac );

    pac->fDepth = ss_fRand( FMIN_DEPTH, FMAX_DEPTH );
#else
    pac->fDepth = ss_fRand( FMIN_DEPTH, FMAX_DEPTH );

    // calculate chordalDeviation from input attribute fTesselFact
    fChordalDeviation = CalcChordalDeviation( hdc, pac );
#endif

    type = pac->surfStyle == SURFSTYLE_WIREFRAME ? WGL_FONT_LINES :
                                                   WGL_FONT_POLYGONS;

    // Create a wgl font context

    if( !(pac->pWglFontC =
            CreateWglFontContext( hdc, type, pac->fDepth, fChordalDeviation )) )
        return FALSE;

    // intialize the text that will be displayed

    if( pac->demoType == DEMO_CLOCK ) {
        text3d_UpdateTime( pac, FALSE );  // sets pac->textXXX params as well
    } else if( pac->demoType == DEMO_STRING ) {
        if( !VerifyString( pac ) ) {
            ConvertStringToList( pac->szText, pac->usText, pac->pWglFontC );
            pac->textLen = GetStringExtent( pac->szText, &pac->pfTextExtent,
                                         &pac->pfTextOrigin,
                                         pac->pWglFontC );
        }
    }
    return SUCCESS;
}

/**************************************************************************\
* InitView
*
*
\**************************************************************************/
static void
InitView( AttrContext *pac )
{
    int numRots=0, axis;
    FLOAT *p3dRotMax = (FLOAT *) &pac->p3dRotMax;
    FLOAT *p3dRotMin = (FLOAT *) &pac->p3dRotMin;
    int *ip3dRotStep = (int *) &pac->ip3dRotStep;
    POINT3D p3d_zero = {0.0f, 0.0f, 0.0f};
    int stepRange = 2; // default step range
    int reset[NUM_AXIS] = {1, 1, 1};

    // text is either xmajor or ymajor
    pac->bXMajor = pac->pfTextExtent.x >= pac->pfTextExtent.y ? TRUE : FALSE;

    /* At this point, the initial string extents will have been
     * calculated, and we can use this to determine rotational
     * characteristics
     */

    // default proc to get next rotation
    GetNextRotProc = GetNextRotRandom;

    /* convert the slider speed values to rotation steps, with
     * a steeper slope at the beginning of the scale
     */
    pac->iRotStep = MapValueI( pac->iSpeed,
                               MIN_SLIDER, MAX_SLIDER,  // slider range
                               MIN_ROT_STEP, MAX_ROT_STEP ); // step range

    // initialize rotation min/max to 0
    *( (POINT3D*)p3dRotMin ) = p3d_zero;
    *( (POINT3D*)p3dRotMax ) = p3d_zero;
    pac->p3dRot = p3d_zero;

    /* Set the MAXIMUM rotation limits.  This is required initially, in
     * order to set the bounding box
     */
    switch( pac->rotStyle ) {
        case ROTSTYLE_NONE:
            GetNextRotProc = GetNextRotNone;
            break;

        case ROTSTYLE_SEESAW:
            // rotate minor axis
            if( pac->demoType == DEMO_VSTRING )
                // always rotate around y-axis
                axis = Y_AXIS;
            else
                axis = pac->bXMajor ? Y_AXIS : X_AXIS;
            p3dRotMin[axis] = FMIN_SEESAW_ANGLE;
            p3dRotMax[axis] = FMAX_SEESAW_ANGLE;
            break;

        case ROTSTYLE_WOBBLE:
            GetNextRotProc = GetNextRotWobble;
            if( pac->demoType == DEMO_VSTRING ) {
                axis = Y_AXIS;
            }
            else {
                stepRange = 1;
                axis = pac->bXMajor ? Y_AXIS : X_AXIS;
            }
            p3dRotMin[Z_AXIS] = FMAX_WOBBLE_ANGLE;
            p3dRotMax[Z_AXIS] = FMAX_WOBBLE_ANGLE;
            p3dRotMin[axis] = FMIN_WOBBLE_ANGLE2;
            p3dRotMax[axis] = FMAX_WOBBLE_ANGLE2;
            break;

        case ROTSTYLE_RANDOM:
            // adjust stepRange based on speed
            stepRange = MapValueI( pac->iSpeed,
                               MIN_SLIDER, (MAX_SLIDER-MIN_SLIDER)/2,
                               2, 6 ); // step range
            for( axis = X_AXIS; axis < NUM_AXIS; axis++ ) {
                p3dRotMin[axis] = FMIN_RANDOM_ANGLE;
                p3dRotMax[axis] = FMAX_RANDOM_ANGLE;
            }
            break;
    }

    // set min and max steps
    pac->iRotMinStep = pac->iRotStep >= (MIN_ROT_STEP + stepRange) ?
              pac->iRotStep - stepRange : MIN_ROT_STEP;
    pac->iRotMaxStep = pac->iRotStep + stepRange; // don't limit upper end

    for( axis = X_AXIS; axis < NUM_AXIS; axis++ ) {
        ip3dRotStep[axis] = p3dRotMax[axis] != 0.0f ?
                ss_iRand2( pac->iRotMinStep, pac->iRotMaxStep ) : 0;
    }

    // initialize the step iteration
    pac->ip3dRoti.x = pac->ip3dRoti.y = pac->ip3dRoti.z = 0;

    // initialize the trig table, for fast rotation calculations
    InitTrigTable();

    // set the current rotation limits
    pac->p3dRotLimit = *( (POINT3D *)p3dRotMax );
    ResetRotationLimits( pac, reset );

    // set view angle
    pac->fFovy = ss_fRand( FMIN_VIEW_ANGLE, FMAX_VIEW_ANGLE );

    for( axis = X_AXIS; axis < NUM_AXIS; axis++ ) {
        if( p3dRotMax[axis] != 0.0f )
            numRots++;
    }

    // set BoundingBoxProc dependent on which axis are being rotated
    if( numRots <= 1 )
        BoundingBoxProc = CalcBoundingBox;
    else
        BoundingBoxProc = CalcBoundingBoxGeneric;

    (*BoundingBoxProc)( pac );
    if( pac->p3dBoundingBox.y == 0.0f )
        pac->p3dBoundingBox.y = 1.0f;
}

/**************************************************************************\
* InitMaterials
*
*
\**************************************************************************/
static void
InitMaterials( AttrContext *pac )
{
    if( pac->bTexture ) {
        ss_InitTexMaterials();
        pac->bMaterialCycle = FALSE;
        pac->pMat = ss_RandomTexMaterial( TRUE );
    } else {
        ss_InitTeaMaterials();
        pac->bMaterialCycle = TRUE;
        pac->pMat = ss_RandomTeaMaterial( TRUE );
    }
}

/**************************************************************************\
* InitTexture
*
\**************************************************************************/
static void
InitTexture( AttrContext *pac )
{
    if( pac->surfStyle != SURFSTYLE_TEX )
        return;

    // No choice for texture quality in dialog - set to HIGH
    pac->texQual = TEXQUAL_HIGH;

    // Try to load the texture file or default texture resource

    if( ss_LoadTextureFile( &pac->texFile, &pac->texture ) ||
        ss_LoadTextureResource( &gTexRes, &pac->texture ) )
    {
        pac->bTexture = 1;

        glEnable(GL_TEXTURE_2D);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        ss_SetTexture( &pac->texture );

        // set auto texture coord generation
        ss_InitAutoTexture( NULL );
    }
    else {  // couldn't open .bmp file
        pac->bTexture = 0;
    }

}

/******************************Public*Routine******************************\
* text3d_Finish
*
* Handles any cleanup on program termination
*
\**************************************************************************/
void
text3d_Finish( void *data )
{
    AttrContext *pac = (AttrContext *) data;

    if( pac )
        DeleteWglFontContext( pac->pWglFontC );

    // delete any name list
    DeleteNameList();
}

/**************************************************************************\
* text3d_Reshape
*
*       - called on resize, expose
*       - always called on app startup
*
\**************************************************************************/

void
text3d_Reshape(int width, int height, void *data )
{
    AttrContext *pac = (AttrContext *) data;

    glViewport( 0, 0, width, height );

    // calculate new aspect ratio
    pac->fAspect = height == 0 ? 1.0f : (FLOAT) width / (FLOAT) height;

    CalcViewParams( pac );
    ss_SetWindowAspectRatio( pac->p3dBoundingBox.x / pac->p3dBoundingBox.y );
}

/**************************************************************************\
* CalcViewParams
*
*   Calculate viewing parameters, based on window size, bounding box, etc.
*
\**************************************************************************/

static void
CalcViewParams( AttrContext *pac )
{
    GLdouble zNear, zFar;
    FLOAT aspectBound, viewDist;
    FLOAT vHeight;
    FLOAT fovy;

    // calculate viewing distance so that front of bounding box within view

    aspectBound = pac->p3dBoundingBox.x / pac->p3dBoundingBox.y;

    // this is distance to FRONT of bounding box:
    viewDist = pac->p3dBoundingBox.y /
               ( (FLOAT) tan( deg_to_rad(pac->fFovy/2.0f) ) );

    // NOTE: these are half-widths and heights
    if( aspectBound <= pac->fAspect ) {
        // we are bound by the window's height
        fovy = pac->fFovy;
    } else {
        // we are bound by window's width
        // adjust fovy, so fovx remains the same
        vHeight = pac->p3dBoundingBox.x / pac->fAspect;
        fovy = rad_to_deg( 2.0f * (FLOAT) atan( vHeight / viewDist ) );
    }

    /* Could just use rotation sphere dimensions here, but for now
     * set clipping planes 10% beyond bounding box.
     */

    zNear = 0.9f * viewDist;
    zFar = 1.1f * (viewDist + 2.0f*pac->p3dBoundingBox.z);

    if( pac->demoType == DEMO_VSTRING )
        zFar *= FMAX_ZOOM;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective( fovy, pac->fAspect, zNear, zFar );

    // set viewing distance so that front of bounding box within view
    viewDist *= 1.01f; // pull back 1% further to be sure not off by a pixel..
    pac->fZtrans = -(viewDist + pac->p3dBoundingBox.z);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef( 0.0f, 0.0f, pac->fZtrans );

}

// number of calibration cycles
#define MAX_CALIBRATE 2

/**************************************************************************\
* text3d_Draw
*
* Draw a frame.
*
\**************************************************************************/
void
text3d_Draw( void *data )
{
    AttrContext *pac = (AttrContext *) data;
    POINT3D *rot = &pac->p3dRot;
    static BOOL bCalibrated = FALSE;
    static int nCycle = 0; // cycle count
    static int frameCount = 0, maxCount;
    static int matTransCount, matTransCount2 = 0;
    static MATERIAL transMat, transMatInc;
    static FLOAT zTrans, zTransInc;
    static MATERIAL *pNewMat;
    static struct _timeb baseTime;
    static BOOL bInit = FALSE;
    static int reset[NUM_AXIS] = {1,1,1};

    if( !bInit ) {

        // Do first time init stuff

        // Start the calibration timer
        _ftime( &baseTime );

        // set default transition points, until calibration done
        maxCount = 60;
        SetTransitionPoints( pac, maxCount, &matTransCount,
                             &matTransCount2, &zTrans );
        bInit = TRUE;
    }

    // take action based on frameCount

    if( frameCount >= matTransCount ) {

        // we are in the transition zone

        if( frameCount == matTransCount ) {

            // first transition point...

            // select new material
            if( pac->bTexture )
                pNewMat = ss_RandomTexMaterial( FALSE );
            else
                pNewMat = ss_RandomTeaMaterial( FALSE );

            // set material transition, zTrans transition
            if( pac->demoType == DEMO_VSTRING ) {
                // transition current material to black
                ss_CreateMaterialGradient( &transMatInc,
                                          pac->pMat,
                                          &ss_BlackMat,
                                          matTransCount2 - matTransCount );
                zTransInc = ((FMAX_ZOOM-1) * pac->fZtrans) /
                                        (matTransCount2 - matTransCount);
            } else {
                ss_CreateMaterialGradient( &transMatInc,
                                           pac->pMat,
                                           pNewMat,
                                           maxCount - matTransCount );
            }
            // initialize transition values to current settings
            zTrans = pac->fZtrans;
            transMat = *(pac->pMat);
            // begin transition on NEXT frame.

        } else {

            // past first transition...

            if( matTransCount2 && (frameCount == (matTransCount2+1)) ) {

                // optional second transition point...(only for vstrings)

                // transition from black to new material
                ss_CreateMaterialGradient( &transMatInc,
                                           &ss_BlackMat,
                                           pNewMat,
                                           maxCount - matTransCount2 );
                // init transition material to black
                transMat = ss_BlackMat;

                /* At this point, screen is black, so we can change strings
                 * and resize the floater without any problems.
                 */

                if( pac->demoType == DEMO_VSTRING )
                    text3d_UpdateString( pac, TRUE ); // can cause resize
                // set zTrans to furthest distance
                zTrans = (FMAX_ZOOM * pac->fZtrans);
                zTransInc = (pac->fZtrans - zTrans) /
                                            (maxCount - matTransCount2);
                // change this while string invisible
                ResetRotationLimits( pac, reset );
            }
            // set the transition material (updates transMat each time)

            ss_TransitionMaterial( &transMat, &transMatInc );
            zTrans += zTransInc;

            if( frameCount >= maxCount ) {

                // End of cycle
                nCycle++;  // 1-based

                // Calibrate on MAX_CALIBRATE cycles
                if( !bCalibrated && (nCycle >= MAX_CALIBRATE) ) {
                    maxCount = FrameCalibration( pac, &baseTime, maxCount, nCycle );

                    SetTransitionPoints( pac, maxCount, &matTransCount,
                                         &matTransCount2, &zTrans );
                    bCalibrated = TRUE;
                }

                // set, reset stuff
                pac->pMat = pNewMat;
                ss_SetMaterial( pNewMat );
                zTrans = pac->fZtrans;
                frameCount = 0;
            }
        }
    }

    if( pac->demoType == DEMO_CLOCK ) {
        // have to update the draw string with current time
        text3d_UpdateTime( pac, TRUE );
    }

    // ok, the string's setup - draw it

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    if( pac->demoType == DEMO_VSTRING )
        // use zooming zTrans
        glTranslatef( 0.0f, 0.0f, zTrans );
    else
        // use fixed zTrans
        glTranslatef( 0.0f, 0.0f, pac->fZtrans );

    /*
     * GetNextRotProc provides sinusoidal rotations
     */
    (*GetNextRotProc)( pac );  // sets pac->p3dRot, or rot

    if( pac->p3dRotMax.z != 0.0f ) {
        glRotatef( rot->z, 0.0f, 0.0f, 1.0f );
    }
    if( pac->p3dRotMax.y != 0.0f ) {
        glRotatef( rot->y, 0.0f, 1.0f, 0.0f );
    }
    if( pac->p3dRotMax.x != 0.0f ) {
        glRotatef( rot->x, 1.0f, 0.0f, 0.0f );
    }

    glTranslatef( -pac->pfTextOrigin.x - pac->pfTextExtent.x/2.0f,
                  -pac->pfTextOrigin.y + pac->pfTextExtent.y/2.0f,
                  pac->fDepth / 2.0f );

    DrawString( pac->usText, pac->textLen, pac->pWglFontC );

    glFlush();
    frameCount++;
}

/**************************************************************************\
* SetTransitionPoints
*
* Calculate draw transition points, as frame count values.
*
* If doing variable string (VSTRING), first transition point is where we
* start fading to black, and 2nd is from black to next material.  Also
* transition the z translation distance (zTrans).
* Note that trans2 indicates the frame number where the image should be
* black.  The actual transitioning may occur at trans2+1 (see text3d_draw
* above).
*
* For all other cases, set one transition point for fade to next material.

*
\**************************************************************************/
static void
SetTransitionPoints( AttrContext *pac, int framesPerCycle, int *trans1,
                     int *trans2, FLOAT *zTrans )
{
    *trans1 = (int) (0.5f * (FLOAT) framesPerCycle + 0.5f);

    if( pac->demoType == DEMO_VSTRING ) {
        *trans2 = *trans1 +
                  (int) (0.5f * (FLOAT) (framesPerCycle - *trans1) + 0.5f);
        *zTrans = pac->fZtrans;
    }
}

/**************************************************************************\
* text3d_UpdateTime
*
* Put new time string into the attribute context
*
\**************************************************************************/
static void
text3d_UpdateTime( AttrContext *pac, BOOL bCheckBounds )
{
    int oldLen;
    POINTFLOAT textExtent, textOrigin;
    POINTFLOAT textLowerRight, currentLowerRight;
    LPTSTR pszLastTime = pac->szText;
    static TCHAR szNewTime[TEXT_BUF_SIZE] = {0};

    GetLocalTime( &(pac->stTime) );
    GetTimeFormat( GetSystemDefaultLCID(), // locale id
                    0,                      // flags
                    &(pac->stTime),         // time struct
                    NULL,                   // format string
                    szNewTime,               // buffer
                    TEXT_BUF_SIZE );        // buffer size

    // Compare new time string with last one

    if( !lstrcmp( pszLastTime, szNewTime ) )
        // time string has not changed, return
        return;

    // translate the new time string into display lists in pac->usText

    ConvertStringToList( szNewTime, pac->usText, pac->pWglFontC );
    lstrcpy( pac->szText, szNewTime );

    // Check extents of new string

    // save current values
    oldLen = pac->textLen;
    textExtent = pac->pfTextExtent;
    textOrigin = pac->pfTextOrigin;

    pac->textLen = GetStringExtent( pac->szText,
                                     &textExtent,
                                     &textOrigin,
                                     pac->pWglFontC );

    if( !bCheckBounds ) {
        // just set new extents and return
        pac->pfTextExtent = textExtent;
        pac->pfTextOrigin = textOrigin;
        return;
    }

    /* only update bounding box if new extents are larger, or the number
     * of chars changes
     */
    bCheckBounds = FALSE;

    if( pac->textLen != oldLen ) {

        // recalculate everything

        bCheckBounds = TRUE;
        pac->pfTextExtent = textExtent;
        pac->pfTextOrigin = textOrigin;
    }
    else {

        // accumulate maximum bounding box in pac

        // calc current lower right limits
        textLowerRight.x = textOrigin.x + textExtent.x;
        textLowerRight.y = textOrigin.y - textExtent.y;
        currentLowerRight.x = pac->pfTextOrigin.x + pac->pfTextExtent.x;
        currentLowerRight.y = pac->pfTextOrigin.y - pac->pfTextExtent.y;

        // if new text extents extend beyond current, update

        if( textOrigin.x < pac->pfTextOrigin.x ) {
            pac->pfTextOrigin.x = textOrigin.x;
            bCheckBounds = TRUE;
        }
        if( textOrigin.y > pac->pfTextOrigin.y ) {
            pac->pfTextOrigin.y = textOrigin.y;
            bCheckBounds = TRUE;
        }
        if( textLowerRight.x > currentLowerRight.x ) {
            pac->pfTextExtent.x = textLowerRight.x - pac->pfTextOrigin.x;
            bCheckBounds = TRUE;
        }
        if( textLowerRight.y < currentLowerRight.y ) {
            pac->pfTextExtent.y = pac->pfTextOrigin.y - textLowerRight.y;
            bCheckBounds = TRUE;
        }
    }
    if( bCheckBounds ) {
        // string size has changed - recalc box and view params
        (*BoundingBoxProc)( pac );
        CalcViewParams( pac );
    }
}

/**************************************************************************\
* text3d_UpdateString
*
* Select new string to display.
* If bCheckBounds, calculate new bounds as well.
*
\**************************************************************************/
static void
text3d_UpdateString( AttrContext *pac, BOOL bCheckBounds )
{
    static int index = 0;

    // get next string to display

    if( gplist == NULL )
        CreateRandomList();

    lstrcpy( pac->szText, gplist->pszStr );
    ConvertStringToList( pac->szText, pac->usText, pac->pWglFontC );
    gplist = gplist->pnext;

    // get new extents
    pac->textLen = GetStringExtent( pac->szText,
                                     &pac->pfTextExtent,
                                     &pac->pfTextOrigin,
                                     pac->pWglFontC );

    if( !bCheckBounds )
        return;

    // calculate bounding box
    (*BoundingBoxProc)( pac );
    if( pac->p3dBoundingBox.y == 0.0f )
        // avoid /0
        pac->p3dBoundingBox.y = 1.0f;

    // Make window's aspect ratio dependent on bounding box

    /* mf: could clear buffer here, so don't get incorrect results on
     *  synchronous resize, but not necessary since we're fading
     */
    ss_SetWindowAspectRatio( pac->p3dBoundingBox.x / pac->p3dBoundingBox.y );
    CalcViewParams( pac );

    // move window to new random position
    ss_RandomWindowPos();
}

/**************************************************************************\
* GetNextRotWobble
*
*   Calculate next rotation.
*   - 'step' controls amount of rotation
*   - rotation values are scaled from -1 to 1 (trig values), and  inscribe
*     circle with r=1 in the zy plane for the ends of the string
*   - steps for both minor and major rotation axis remain in sync
*
\**************************************************************************/
static void
GetNextRotWobble( AttrContext *pac )
{
    int *step = (int *) &pac->ip3dRoti;  // use step->x
    int *rotStep = (int *) &pac->ip3dRotStep.z;
    FLOAT *rotMax = (FLOAT *) &pac->p3dRotMax;
    POINTFLOAT *pTrig = pac->pTrig;
    static int resetPoint[NUM_AXIS] = {90,90,0}; // 0 amplitude points
    int reset[NUM_AXIS] = {0}; // which axis to be reset
    int axis;

    pac->p3dRot.z = pac->p3dRotLimit.z * pTrig[ *step ].y;  // sin
    pac->p3dRot.y = pac->p3dRotLimit.y * pTrig[ *step ].x;  // cos
    pac->p3dRot.x = pac->p3dRotLimit.x * pTrig[ *step ].x;  // cos

    // check for 0 amplitude point for non-vstrings
    for( axis = X_AXIS; axis < NUM_AXIS; axis++ ) {
        if( rotMax[axis] != 0.0f ) {
            if( (pac->demoType != DEMO_VSTRING) &&
                (*step == resetPoint[axis]) )
            {
                reset[axis] = 1;
                ResetRotationLimits( pac, reset );
                reset[axis] = 0;
            }
        }
    }

    // increment step
    if( (*step += *rotStep) >= 360 ) {
        // make the step variable
        *rotStep = ss_iRand2( pac->iRotMinStep, pac->iRotMaxStep );
        // start step at variable index
        *step = ss_iRand( *rotStep );
    }
}

/**************************************************************************\
* GetNextRotRandom
*
* Same as above, but steps for each axis are not kept in sync
*
\**************************************************************************/
static void
GetNextRotRandom( AttrContext *pac )
{
    int *step = (int *) &pac->ip3dRoti;
    int *rotStep = (int *) &pac->ip3dRotStep;
    FLOAT *rotMax = (FLOAT *) &pac->p3dRotMax;
    POINTFLOAT *pTrig = pac->pTrig;
    static int resetPoint[NUM_AXIS] = {90,90,0}; // 0 amplitude points
    int reset[NUM_AXIS] = {0}; // which axis to be reset
    int axis;

    // set new rotation
    pac->p3dRot.z = pac->p3dRotLimit.z * pTrig[ step[Z_AXIS] ].y;  // sin
    pac->p3dRot.y = pac->p3dRotLimit.y * pTrig[ step[Y_AXIS] ].x;  // cos
    pac->p3dRot.x = pac->p3dRotLimit.x * pTrig[ step[X_AXIS] ].x;  // cos

    // for each rotation axis...

    for( axis = X_AXIS; axis < NUM_AXIS; axis++ ) {
        if( rotMax[axis] != 0.0f ) {
            // check for 0 amplitude point for non-vstrings
            if( (pac->demoType != DEMO_VSTRING) &&
                (step[axis] == resetPoint[axis]) ) {
                reset[axis] = 1;
                ResetRotationLimits( pac, reset );
                reset[axis] = 0;
            }

            // increment rotation step and check for end of cycle
            if( (step[axis] += rotStep[axis]) >= 360 ) {
                // make the step variable
                rotStep[axis] = ss_iRand2( pac->iRotMinStep, pac->iRotMaxStep );
                // start step at variable index
                step[axis] = ss_iRand( rotStep[axis] );
            }
        }
    }
}

/**************************************************************************\
* GetNextRotNone
*
* Null rot proc
*
\**************************************************************************/
static void
GetNextRotNone( AttrContext *pac )
{
}

/**************************************************************************\
* ResetRotationLimits
*
* Reset the maximum axis rotations.  So there won't be too much of a 'jump'
* when altering the rotation, this routine should only be called when the
* rotation of the specified axis is at zero amplitude.
*
* Also, change the rotation table if applicable.  This affects how the
* rotation 'steps' around the axis.
*
\**************************************************************************/

static void
ResetRotationLimits( AttrContext *pac, int *reset )
{
    FLOAT *p3dRot    =  (FLOAT *) &pac->p3dRot;       // current rotation
    FLOAT *p3dRotL   =  (FLOAT *) &pac->p3dRotLimit;  // new rot limit
    FLOAT *p3dRotMin =  (FLOAT *) &pac->p3dRotMin;    // max rotation
    FLOAT *p3dRotMax =  (FLOAT *) &pac->p3dRotMax;    // max rotation
    POINT3D p3dOldRotL = pac->p3dRotLimit; // save last rot limit
    POINTFLOAT *oldTrig;
    int i;

    // change rotation limits

    for( i = 0; i < NUM_AXIS; i++ ) {
        if( p3dRotMax[i] && reset[i] ) {
            p3dRotL[i] = ss_fRand( p3dRotMin[i], p3dRotMax[i] );
            // grossly modify amplitute sometimes for random
            if( pac->rotStyle == ROTSTYLE_RANDOM ) {
                if( ss_iRand(10) == 2 )
                    p3dRotL[i] = ss_fRand( 0.0f, 10.0f );
                else if( ss_iRand(10) == 2 )
                    p3dRotL[i] = ss_fRand( 90.0f, 135.0f );
            }
        }
    }

    // change rotation table

    // use i to set a frequency for choosing gInvTrig table
    i = 10;

    oldTrig = pac->pTrig;

    switch( pac->rotStyle ) {
        case ROTSTYLE_RANDOM:
            if( pac->demoType == DEMO_VSTRING )
                i = 7;
            // fall thru...
        case ROTSTYLE_SEESAW:
            // Use InvTrig table every now and then
            if( ss_iRand(i) == 2 ) {
                pac->pTrig = gInvTrig;
            }
            else
                pac->pTrig = gTrig;
            break;
        default:
            // Always use regular trig table
            pac->pTrig = gTrig;
    }

    // if trig table changed, need to adjust steps of non-zero axis rotations
    // (otherwise get 'twitch' in rotation)

    if( pac->pTrig != oldTrig ) {
        // only deal with axis which didn't have amplitudes modified
        for( i = 0; i < NUM_AXIS; i++ )
            reset[i] = ! reset[i];
        AdjustRotationStep( pac, reset, oldTrig );
    }
}

/**************************************************************************\
* AdjustRotationStep
*
* If trig table is changed in ResetRotationLimits, then axis with non-zero
* rotations will appear to jump.  This routine modifies the current step
* so this will not be apparent.
*
\**************************************************************************/

static void
AdjustRotationStep( AttrContext *pac, int *reset, POINTFLOAT *oldTrig )
{
    int *step = (int *) &pac->ip3dRoti;
    FLOAT *p3dRotMax =  (FLOAT *) &pac->p3dRotMax;
    int axis;
    POINT *trigDif;

    if( pac->demoType == DEMO_VSTRING )
        // for now doesn't matter, string is invisible at this point
        return;

    // choose diff table to use for modifying step
    trigDif = (oldTrig == gTrig) ? gTrigDif : gInvTrigDif;

    for( axis = 0; axis < NUM_AXIS; axis++ ) {
        if( p3dRotMax[axis] && reset[axis] ) {
            if( axis != Z_AXIS )
                step[axis] += trigDif[ step[axis] ].x;
            else
                step[axis] += trigDif[ step[axis] ].y;

            // check for wrap or out of bounds
            if( (step[axis] >= 360) || (step[axis] < 0) )
                step[axis] = 0;
        }
    }
}

/**************************************************************************\
* FindInvStep
*
* Finds step in invTrig table with same value as trig table at i
*
\**************************************************************************/

static int
FindInvStep( int i )
{
    FLOAT val, diff, minDiff;
    int invStep = i;

    val = gTrig[i].y;

    invStep = i;
    minDiff = val - gInvTrig[i].y;

    while( ++i <= 90 ) {
        diff = val - gInvTrig[i].y;
        if( (FLOAT) fabs(diff) < minDiff ) {
            minDiff = (FLOAT) fabs(diff);
            invStep = i;
        }
        if( diff < 0.0f )
            break;
    }

    return invStep;
}

/**************************************************************************\
* FindStep
*
* Finds step in trig table with same value as invTrig table at i
*
\**************************************************************************/

static int
FindStep( int i )
{
    FLOAT val, diff, minDiff;
    int step = i;

    val = gInvTrig[i].y;

    step = i;
    minDiff = gTrig[i].y - val;

    while( --i >= 0 ) {
        diff = val - gTrig[i].y;
        if( (FLOAT) fabs(diff) < minDiff ) {
            minDiff = (FLOAT) fabs(diff);
            step = i;
        }
        if( diff > 0.0f )
            break;
    }

    return step;
}

/**************************************************************************\
* InitTrigTable
*
* Initialize trig look-up tables
*
\**************************************************************************/
static void
InitTrigTable()
{
    int i;
    static int num = 360;
    FLOAT inc = (2.0f*PI)/((FLOAT)num);  // 360 degree range
    FLOAT angle = 0.0f;
    int newStep;

    // calc standard trig table

    for( i = 0; i < num; i ++ ) {
        gTrig[i].x = (FLOAT) cos(angle);
        gTrig[i].y = (FLOAT) sin(angle);
        angle += inc;
    }

    // Calc sawtooth and pseudo-inverse trig table, as well as a diff
    // table to convert between trig and invTrig.

    // do y, or sin values first

    for( i = 0; i <= 90; i ++ ) {
        gSawTooth[i].y = (int) i / 90.0f;
        gInvTrig[i].y = 2*gSawTooth[i].y - gTrig[i].y;
    }

    // Create tables to convert trig steps to invTrig steps, and vice-versa
    for( i = 0; i <= 90; i ++ ) {
        newStep = FindInvStep( i );
        gTrigDif[i].y = newStep - i;
        newStep = FindStep( i );
        gInvTrigDif[i].y = newStep - i; // -
    }

    // reflect 0-90 to get 90-180
    for( i = 1; i <= 90; i ++ ) {
        gSawTooth[90+i].y = gSawTooth[90-i].y;
        gInvTrig[90+i].y = gInvTrig[90-i].y;
        gTrigDif[90+i].y = -gTrigDif[90-i].y;
        gInvTrigDif[90+i].y = -gInvTrigDif[90-i].y;
    }
    // invert 0-180 to get 180-360
    for( i = 1; i < 180; i ++ ) {
        gSawTooth[180+i].y = -gSawTooth[i].y;
        gInvTrig[180+i].y = -gInvTrig[i].y;
        gTrigDif[180+i].y = gTrigDif[i].y;
        gInvTrigDif[180+i].y = gInvTrigDif[i].y;
    }

    // calc x, or cos, by phase-shifting y

    for( i = 0; i < 270; i ++ ) {
        gSawTooth[i].x = gSawTooth[i+90].y;
        gInvTrig[i].x = gInvTrig[i+90].y;
        gTrigDif[i].x = gTrigDif[i+90].y;
        gInvTrigDif[i].x = gInvTrigDif[i+90].y;
    }
    for( i = 0; i < 90; i ++ ) {
        gSawTooth[i+270].x = gSawTooth[i].y;
        gInvTrig[i+270].x = gInvTrig[i].y;
        gTrigDif[i+270].x = gTrigDif[i].y;
        gInvTrigDif[i+270].x = gInvTrigDif[i].y;
    }
}

/**************************************************************************\
* CalcChordalDeviation
*
*
\**************************************************************************/
static FLOAT
CalcChordalDeviation( HDC hdc, AttrContext *pac )
{
    OUTLINETEXTMETRIC otm;
    FLOAT       cd, mincd;  // chordal deviations

    // Query font metrics

    if( GetOutlineTextMetrics( hdc, sizeof(otm), &otm) <= 0 )
        // cmd failed, or buffer size=0
        return 1.0f;

    // minimum chordal deviation is limited by design space
    mincd = 1.0f / (FLOAT) otm.otmEMSquare;

    // now map fTesselFact to chordalDeviation
    cd = MapValue( pac->fTesselFact,
                   0.0f, 1.0f,  // fTesselFact range
                   FMAX_CHORDAL_DEVIATION, mincd );  // chordalDeviation range
    if( pac->fTesselFact == 0.0f )
        // make sure get lowest resolution
        cd = 1.0f;
    return cd;
}

/**************************************************************************\
* CalcBoundingBox
*
\**************************************************************************/
static void
CalcBoundingBox( AttrContext *pac )
{
    POINT3D box[3];  // for each axis rotation
    FLOAT viewAngle, critAngle, critAngleC, rectAngle;
    FLOAT r, rot, x, y, z, ymax, zCrit;
    FLOAT viewDist, viewDistO, xAngle[3];
    FLOAT boxvpo[3];  // viewpoint to origin distance along z for the boxes
    int n = 0;
    POINT3D pt;

    /* One thing to remember here is that box[n].z is constrained to be
     * the near clipping plane.  The boxe's x and y represent the frustum
     * cross-section at that point.
     */
    viewAngle = deg_to_rad( pac->fFovy )  / 2.0f;

    // x,y,z represent half-extents
    x = pac->pfTextExtent.x/2.0f;
    y = pac->pfTextExtent.y/2.0f;
    z = pac->fDepth/2.0f;
    // initialize box[0] with current extents
    box[0].x = x;
    box[0].y = y;
    box[0].z = z;
    boxvpo[0] = 0.0f;

    // handle rotation around x-axis

    if( pac->p3dRotMax.x != 0.0f ) {

        box[n].x = x;

        // need to determine y and z

        rot = deg_to_rad( pac->p3dRotMax.x );
        r = (FLOAT) sqrt( y*y + z*z );

        // calc incursion along z

        rectAngle = (z == 0.0f) ? PI_OVER_2 : (FLOAT) atan( y/z );
        if( rot >= rectAngle ) {
            // easy, use maximum possible extent
            box[n].z = r;
        } else {
            // rotate lower right corner of box by rot to get extent
            box[n].z = z * (FLOAT) cos( rot ) + y * (FLOAT) sin( rot );
        }

        /* figure out critical angle, where rotated rectangle would
         * be perpendicular to viewing frustum.  This indicates the max.
         * y-incursion into the frustum.
         */
        critAngle = PI_OVER_2 - viewAngle;
        ymax = r * (FLOAT) sin(critAngle);

        if( y > z ) {
            rectAngle = PI_OVER_2 - rectAngle;
            critAngleC = PI_OVER_2 - critAngle;
        } else
            critAngleC = critAngle;

        if( (rectAngle + rot) >= critAngleC ) {
            // no view reduction possible in y, use max view
            // need to calc y at box.z
            viewDistO = r / (FLOAT) cos( critAngle );
            boxvpo[n] = viewDistO;
            box[n].y = (viewDistO - box[n].z) *
                                        (FLOAT) tan( PI_OVER_2 - critAngle);
        } else {
            // we can sonic reduce it
            if( y > z )
                rot = -rot;
            // rotate front-top point by rot to get ymax, z
            ymax = z * (FLOAT) sin( rot ) + y * (FLOAT) cos( rot );
            zCrit = z * (FLOAT) cos( rot ) - y * (FLOAT) sin( rot );
            // not usin viewDistO properly here...
            viewDistO = ymax * (FLOAT) tan( viewAngle);
            boxvpo[n] = viewDistO + zCrit;
            viewDist = boxvpo[n] - box[n].z;
            box[n].y = viewDist * (FLOAT) tan( viewAngle );
        }
        n++;
    }

    if( pac->p3dRotMax.y != 0.0f ) {

        box[n].y = y;

        // need to determine x and z
        rot = deg_to_rad( pac->p3dRotMax.y );
        r = (FLOAT) sqrt( x*x + z*z );
        rectAngle = (z == 0.0f) ? PI_OVER_2 : (FLOAT) atan( x/z );

        // calc incursion along z

        if( rot >= rectAngle ) {
            // easy, use maximum possible extent
            box[n].z = r;
        } else {
            // rotate lower right corner of box by rot to get extent
            box[n].z = z * (FLOAT) cos( rot ) + x * (FLOAT) sin( rot );
        }

        // view distance to largest z
        viewDist = y / (FLOAT) tan(viewAngle);
        // make viewDist represent distance to origin
        viewDistO = viewDist + box[n].z;
        boxvpo[n] = viewDistO;

        // now minimize angle between viewpoint and rotated rect

        if( viewDistO > r ) {
            /* calc crit angle where view is maximized (line from viewpoint
             * tangent to rotation circle)
             * critAngle is between z-axis and radial line
             */
            critAngle = (FLOAT) acos( r / viewDistO );

            // critAngleC is for Comparing
            if( x > z ) {
                rectAngle = PI_OVER_2 - rectAngle;
                critAngleC = PI_OVER_2 - critAngle;
            } else
                critAngleC = critAngle;
        }

        if( (viewDistO > r) && // vp OUTSIDE circle
            ((rectAngle + rot) >= critAngleC) ) {

            /* no view reduction possible in x, use x along the max-view line
             */
            box[n].x = viewDist * (FLOAT) tan( PI_OVER_2 - critAngle );
        } else {
            // we can sonic reduce it
            if( x > z )
                rot = -rot;
            // rotate front-top point by rot to get x,z
            // pt.z not needed
            //pt.z = z * (FLOAT) cos( rot ) - x * (FLOAT) sin( rot );
            pt.x = z * (FLOAT) sin( rot ) + x * (FLOAT) cos( rot );
            box[n].x = pt.x;
        }
        n++;
    }

    if( pac->p3dRotMax.z != 0.0f ) {

        CalcBoundingExtent( deg_to_rad(pac->p3dRotMax.z),
                            x, y,
                            (POINTFLOAT *) &box[n] );
        box[n].z = z;
        // calc viewing distance from front of box
        viewDist = box[n].y / (FLOAT) tan(viewAngle);
        // calc view distance to origin;
        boxvpo[n] = box[n].z + viewDist;
        n++;
    }

    /* Now we've got 3 rectangles in x-y plane at various depths, and
     * need to pick the shortest viewing distance that will encompass
     * all of them.  Or, don't actually have to pick the view distance
     * yet, since might want to wait for the viewport size in Reshape before
     * we do this - but in that case need to pick the rectangle that 'sticks
     * out the most', so it can be used as the bounding box.
     */
    /* The box with the furthest viewpoint will work for y.
     * But then have to check
     * this against the x's of the others.  If any stick out of the frustum,
     * then it will have to be made larger in x.  By making x larger, we
     * do not affect fovy
     */
    SortBoxes( box, boxvpo, n ); // put largest viewpoint box in box[0]

    // figure view dist to first box
    // (could maintain these as they are calculated)
    viewDist = boxvpo[0] - box[0].z;

    // compare x angles of boxes

    switch( n ) {
        FLOAT den;

        case 3:
            den = viewDist + (box[0].z - box[2].z);
            xAngle[2] = den == 0.0f ? PI_OVER_2 : (FLOAT)atan( box[2].x / den);
        case 2:
            den = viewDist + (box[0].z - box[1].z);
            xAngle[1] = den == 0.0f ? PI_OVER_2 : (FLOAT)atan( box[1].x / den);
        case 1:
            xAngle[0] = viewDist == 0.0f ? PI_OVER_2 :
                                            (FLOAT)atan( box[0].x / viewDist );
    }

    // here, just call Sort again, with list of xAngles
    SortBoxes( box, xAngle, n ); // put largest xangle box in box[0]

    // now box[0] should contain half extents of the Bounding box

    pac->p3dBoundingBox = box[0];
}

/**************************************************************************\
* CalcBoundingBoxFromSphere
*
* Calculates the bounding box from a sphere with r = diagonal of the box
*
\**************************************************************************/
static void
CalcBoundingBoxFromSphere( AttrContext *pac )
{
    FLOAT x, y, z, r;
    FLOAT viewAngle, viewDist, viewDistO;
    POINT3D box;

    // x,y,z represent half-extents
    x = pac->pfTextExtent.x/2.0f;
    y = pac->pfTextExtent.y/2.0f;
    z = pac->fDepth/2.0f;

    r = (FLOAT) sqrt( x*x + y*y +z*z );
    box.z = r;
    viewAngle = deg_to_rad( pac->fFovy )  / 2.0f;
    viewDistO = r / (FLOAT) sin( viewAngle );
    viewDist = viewDistO - r;
    box.y = viewDist * (FLOAT) tan( viewAngle );
    box.x = box.y;

    pac->p3dBoundingBox = box;
}

/**************************************************************************\
*
* CalcBoundingBoxFromSpherePlus
*
* Same as above, but tries to optimize for case when z exent is small
*
\**************************************************************************/
static void
CalcBoundingBoxFromSpherePlus( AttrContext *pac, FLOAT zmax )
{
    FLOAT x, y, z, r;
    FLOAT viewAngle, viewDist, viewDistO;
    POINT3D box;

    // x,y,z represent half-extents
    x = pac->pfTextExtent.x/2.0f;
    y = pac->pfTextExtent.y/2.0f;
    z = pac->fDepth/2.0f;

    r = (FLOAT) sqrt( x*x + y*y +z*z );
    viewAngle = deg_to_rad( pac->fFovy )  / 2.0f;

    if( zmax < r ) {
        // we can get closer !
        box.z = zmax;
        viewDistO = r / (FLOAT) sin( viewAngle );
        viewDist = viewDistO - zmax;

        // we want to move the clipping plane closer by (r-zmax)
        if( (r-zmax) > viewDist ) {
#ifdef SS_DEBUG
            glClearColor( 1.0f, 0.0f, 0.0f, 0.0f );
#endif
            // we are moving the vp inside the sphere
            box.y = (FLOAT) sqrt( r*r - box.z*box.z );
            box.x = box.y;
        } else {
            FLOAT zt; // z-point where view frustum tangent to sphere

            // vp outside sphere: can only optimize if zmax < ztangent
            zt = r * (FLOAT) cos( PI_OVER_2 - viewAngle);
            if( zmax < zt ) {
#ifdef SS_DEBUG
                // GREEN ZONE !!!
                glClearColor( 0.0f, 1.0f, 0.0f, 0.0f );
#endif
                box.y = (FLOAT) sqrt( r*r - zmax*zmax );
            } else
                // this is the same as below, but with better clipping
                box.y = (viewDist + (r-zmax)) * (FLOAT) tan( viewAngle );
            box.x = box.y;
        }
    } else {
        box.z = r;
        viewDistO = r / (FLOAT) sin( viewAngle );
        viewDist = viewDistO - r;
        box.y = viewDist * (FLOAT) tan( viewAngle );
        box.x = box.y;
    }
    pac->p3dBoundingBox = box;
}

/**************************************************************************\
* CalcBoundingBoxFromExtents
*
* Calculate bounding box for text, assuming text centered at origin, and
* using maximum possible spin angles.
*
* Rotation around any one axis will affect bounding areas in the other
* 2 directions (e.g. z-rotation affects x and y bounding values).
*
* We need to find the maxima of the rotated 2d area, while staying within
* the max spin angles.
*
\**************************************************************************/
static void
CalcBoundingBoxFromExtents( AttrContext *pac, POINT3D *box )
{
    POINTFLOAT extent;

    box->x = pac->pfTextExtent.x / 2.0f;
    box->y = pac->pfTextExtent.y / 2.0f;
    box->z = pac->fDepth / 2.0f;

    // split the 3d problem into 3 2d problems in 'x-y' plane

    if( pac->p3dRotMax.x != 0.0f ) {

        CalcBoundingExtent( deg_to_rad(pac->p3dRotMax.x),
                            box->z, box->y, &extent );
        box->z = max( box->z, extent.x );
        box->y = max( box->y, extent.y );
    }

    if( pac->p3dRotMax.y != 0.0f ) {

        CalcBoundingExtent( deg_to_rad(pac->p3dRotMax.y),
                            box->x, box->z, &extent );
        box->x = max( box->x, extent.x );
        box->z = max( box->z, extent.y );
    }

    if( pac->p3dRotMax.z != 0.0f ) {

        CalcBoundingExtent( deg_to_rad(pac->p3dRotMax.z),
                            box->x, box->y, &extent );
        box->x = max( box->x, extent.x );
        box->y = max( box->y, extent.y );
    }
}

/**************************************************************************\

* CalcBoundingBoxGeneric
*
* Combines the bounding sphere with the bounding extents
* Each of these alone will guarantee no clipping.  But we can
* optimize by combining them.
*
\**************************************************************************/
static void
CalcBoundingBoxGeneric( AttrContext *pac )
{
    POINT3D extentBox;
    FLOAT x, y, z, r, d, zt, fovx;
    FLOAT viewAngle, viewDist, viewDistO;
    BOOL xIn, yIn;

    // x,y,z represent half-extents
    x = pac->pfTextExtent.x/2.0f;
    y = pac->pfTextExtent.y/2.0f;
    z = pac->fDepth/2.0f;

    // get the max extent box

    /*!!! wait, this alone doesn't guarantee no clipping?  It only
     * checks each axis-rotation separately, without combining them. This
     * is no better than calling old CalcBoundingBox ... ??  Well, I
     * can't prove why theoretically, but it works
     */
    CalcBoundingBoxFromExtents( pac, &extentBox );

    // determine whether x and y extents inside/outside bounding sphere

    r = (FLOAT) sqrt( x*x + y*y +z*z );
    // check y
    d = (FLOAT) sqrt( extentBox.y*extentBox.y + extentBox.z*extentBox.z );
    yIn = d <= r ? TRUE : FALSE;
    // check x
    d = (FLOAT) sqrt( extentBox.x*extentBox.x + extentBox.z*extentBox.z );
    xIn = d <= r ? TRUE : FALSE;

    // handle easy cases

    if( yIn && xIn ) {
        pac->p3dBoundingBox = extentBox;
        return;
    }
    if( !yIn && !xIn ) {
        CalcBoundingBoxFromSpherePlus( pac, extentBox.z );
        return;
    }

    // harder cases

    viewAngle = deg_to_rad( pac->fFovy )  / 2.0f;

    if( yIn ) {
        // figure out x
        viewDist = extentBox.y / (FLOAT) tan(viewAngle);
        /* viewDist can be inside or outside of the sphere
         * If inside - no optimization possible
         * If outside, can draw line from viewpoint tangent to sphere,
         * and use this point for x
         */
        viewDistO = extentBox.z + viewDist;
        if( viewDistO <= r ) {
            // vp inside sphere
            // set x to the point where z intersects sphere
            // this becomes a Pythagorous theorem problem:
            extentBox.x = (FLOAT) sqrt( r*r - extentBox.z*extentBox.z );
        } else {
            // vp outside sphere
            /* - figure out zt, where line tangent to circle for viewAngle
             */
            fovx = (FLOAT) asin( r / viewDistO );
            zt = r * (FLOAT) acos( PI_OVER_2 - viewAngle);
            if( extentBox.z < zt ) {
                // use x where extentBox.z intersects sphere
                extentBox.x = (FLOAT) sqrt( r*r - extentBox.z*extentBox.z );
            } else {
                // use x at tangent point
                extentBox.x = (FLOAT) sqrt( r*r - zt*zt );
            }
        }
    } else {// y out, x in
        // have to figure out whether vp inside/outside of sphere.
        /* !We can cheat a bit here.  It IS possible, with view angles > 90,
         * that the vp be inside sphere.  But since we always use 90 for
         * this app, it is safe to assume vp > r  (Fix later for general case)
         */
        // XXX: wait, if y out, isn't vp always outside sphere ?
        /* So we solve it this way:
         * - figure out line tangent to circle for viewAngle
         * - y will be where this line intersects the z=extentBox.z line
         */
        viewDistO = r / (FLOAT) sin( viewAngle );
        extentBox.y = (viewDistO - extentBox.z) * (FLOAT) tan( viewAngle );
    }
    pac->p3dBoundingBox = extentBox;
}

/**************************************************************************\
*
* Calculate the extents in x and y from rotating a rectangle in a 2d plane
*
\**************************************************************************/
static void
CalcBoundingExtent( FLOAT rot, FLOAT x, FLOAT y, POINTFLOAT *extent )
{
    FLOAT r, angleCrit;

    r = (FLOAT) sqrt( x*x + y*y );
    angleCrit = (x == 0.0f) ? PI_OVER_2 : (FLOAT) atan( y/x );

    // calc incursion in x

    if( rot >= angleCrit ) {
        // easy, use maximum possible extent
        extent->x = r;
    } else {
        // rotate lower right corner of box by rot to get extent
        extent->x = x * (FLOAT) cos( rot ) + y * (FLOAT) sin( rot );
    }

    // calc incursion in y

    angleCrit = PI/2.0f - angleCrit;

    if( rot >= angleCrit ) {
        // easy, use maximum possible extent
        extent->y = r;
    } else {
        // rotate upper right corner of box by rot to get extent
        extent->y = x * (FLOAT) sin( rot ) + y * (FLOAT) cos( rot );
    }

}

/**************************************************************************\
*
* Sorts in descending order, based on values in val array (bubble sort)
*
\**************************************************************************/
static void
SortBoxes( POINT3D *box, FLOAT *val, int numBox )
{
    int i, j, t;
    POINT3D temp;

    j = numBox;
    while( j ) {
        t = 0;
        for( i = 0; i < j-1; i++ ) {
            if( val[i] < val[i+1] ) {
                // swap'em
                temp = box[i];
                box[i] = box[i+1];
                box[i+1] = temp;
                t = i;
            }
        }
        j = t;
    }
}

#define FILE_BUF_SIZE 180

/**************************************************************************\
* VerifyString
*
* Validate the string
*
* A string can be checked against a list of 'key strings' in an ascii file.
* If there is a match between the user inputted string and one of these key
* strings, then the display mode is enhanced to show a random list of 'item
* strings' that follow the key string in the file.  To point the screen saver
* at such a file, put the filename in the registry key entry "magic", under
* the registry setting group for the text3d screen saver.  The file format is
* as follows:
*
* -key0
* item0
* item1
*  ...
* itemn
* -key1
*  ...
*
* For example:
* -color
* red
* green
* -animal
* dog
* cat
*
\**************************************************************************/
static BOOL
VerifyString( AttrContext *pac )
{
    HMODULE ghmodule;
    HRSRC hr;
    HGLOBAL hg;
    PSZ psz, pszFile = NULL;
    CHAR szSectName[30], szFileName[FILE_BUF_SIZE], szFname[30];
    BOOL bMatch = FALSE;

    // Check for string file in registry
    if (LoadStringA(hMainInstance, IDS_SAVERNAME, szSectName, 30) &&
        LoadStringA(hMainInstance, IDS_INIFILE, szFname, 30))
    {
        if( GetPrivateProfileStringA(szSectName, "magic", NULL,
                                     szFileName, FILE_BUF_SIZE, szFname) )
            pszFile = ReadStringFileA( szFileName );
    }

    // Check for key strings
    if( pszFile )
        bMatch = CheckKeyStrings( pac->szText, pszFile );

    if( !bMatch ) {
        if( (ghmodule = GetModuleHandle(NULL)) &&
            (hr = FindResource(ghmodule, MAKEINTRESOURCE(1),
                                                    MAKEINTRESOURCE(99))) &&
            (hg = LoadResource(ghmodule, hr)) &&
            (psz = (PSZ)LockResource(hg)) )
        bMatch = CheckKeyStrings( pac->szText, psz );
    }

    if( bMatch ) {
        // put first string in pac->szText
        pac->demoType = DEMO_VSTRING;
        // for now, initialize strings here
        text3d_UpdateString( pac, FALSE );

        // adjust cycle time based on rotStyle
        switch( pac->rotStyle ) {
            case ROTSTYLE_NONE:
                gfMinCycleTime = 4.0f;
                break;
            case ROTSTYLE_SEESAW:
                gfMinCycleTime = 8.0f;
                break;
            case ROTSTYLE_RANDOM:
                gfMinCycleTime = 10.0f;
                break;
            default:
                gfMinCycleTime = 9.0f;
        }
    }
    if( pszFile )
        free( pszFile );  // allocated by ReadStringFile
    return bMatch;
}

/**************************************************************************\
* CheckKeyStrings
*
* Test for match between string and any keystrings
* 'string' is user-inputted, and limited to TEXT_LIMIT chars.
*
\**************************************************************************/

static BOOL
CheckKeyStrings( LPTSTR string, PSZ psz )
{
    TCHAR szKey[TEXT_LIMIT+1], testString[TEXT_LIMIT+1] = {0};
    BOOL bMatch = FALSE;
    int nMatch = 0;
    int len;

    // make copy of test string and convert to upper case
    lstrcpy( testString, string );
#ifdef UNICODE
    _wcsupr( testString );
#else
    _strupr( testString );
#endif

    while( psz[0] != '\n' ) { // iterate keyword/data sets
        while( psz[0] != '\n' ) {  // iterate keywords
            len = strlen( psz ); // ! could be > TEXT_LIMIT if from file
            // invert keyword bits and convert to uppercase
#ifdef UNICODE
            // convert ascii keyword to unicode in szKey (inverts at same time)
            ConvertStringAsciiToUnicode( psz, szKey,
                                         len > TEXT_LIMIT ? TEXT_LIMIT : len );
            _wcsupr( szKey );
#else
            // just copy keyword to szKey, without going over TEXT_LIMIT
            strncpy( szKey, psz, TEXT_LIMIT );
            InvertBitsA( szKey, len > TEXT_LIMIT ? TEXT_LIMIT : len );
            szKey[TEXT_LIMIT] = '\0';  // in case len > TEXT_LIMIT
            _strupr( szKey );
#endif

            if( !lstrcmp( szKey, testString ) ) {
                // keyword match !
                bMatch = TRUE;
                nMatch++;
            }
            psz += len + 1;  // skip over NULL as well
        }
        psz++;  // skip over '\n' at end of keywords
        if( bMatch )
            ReadNameList( psz );

        // skip over data to get to next keyword group
        while( *psz != '\n' )
            psz++;
        psz++;  // skip over '\n' at end of data
        bMatch = FALSE; // keep searching for keyword matches
    }
    return nMatch;
}

/**************************************************************************\
* Various functions to process vstrings
*
\**************************************************************************/

static void
InvertBitsA( char *s, int len )
{
    while( len-- ) {
        *s++ = ~(*s);
    }
}

static PSZ
ReadStringFileA( LPSTR szFile )
{
    char lineBuf[180];
    PSZ buf, pBuf;
    int size, length, fdi;
    char *ps;
    char ctrl_n = '\n';
    FILE *fIn;
    BOOL bKey;

    // create buffer to hold entire file
    // mf: ! must be better way of getting file length!
    fdi = _open(szFile, O_RDONLY | O_BINARY);
    if( fdi < 0 )
        return NULL;
    size = _filelength( fdi );
    _close(fdi);
    buf= (char *) malloc( size );
    if( !buf)
        return NULL;

    // open file for ascii text reading
    fIn = fopen( szFile, "r" );
    if( !fIn )
        return NULL;

    // Read in keyword/data sequences

    bKey = TRUE;  // so '\n' not appended to file when hit first keyword
    pBuf = buf;
    while( fgets( lineBuf, 180, fIn) ) {
        ps = lineBuf;
        if( *ps == '-' ) {
            // keyword
            if( !bKey ) {
                // first key in group, append '\n' to data
                *pBuf++ = ctrl_n;
            }
            bKey = TRUE;
            ps++; // skip '-'
        } else {
            // data
            if( bKey ) {
                // first data in group, append '\n' to keywords
                *pBuf++ = ctrl_n;
            }
            bKey = FALSE;
        }
        length = strlen( ps );
        InvertBitsA( ps, length );
        *(ps+length-1) = '\0'; // convert '\n' to null
        lstrcpyA( pBuf, ps );
        pBuf += length;
    }
    fclose( fIn );

    // put 2 '\n' at end, for end condition
    *pBuf++ = ctrl_n;
    *pBuf++ = ctrl_n;
    return( buf );
}

static void
CreateRandomList()
{
   PLIST plist = gplistComplete;
   PLIST *pplist;
   int i = 0;
   int n;

   while (plist != NULL) {
       n = ss_iRand( i+1 );
       pplist = &gplist;

       while (n > 0) {
           pplist = &((*pplist)->pnext);
           n--;
       }

       plist->pnext = *pplist;
       *pplist = plist;

       plist = plist->plistComplete;
       i++;
   }
}

static void
AddName(
    LPTSTR pszStr)
{
    PLIST plist = (PLIST)LocalAlloc(LPTR, sizeof(LIST));
    if( !plist )
        return;
    plist->pszStr = pszStr;
    plist->pnext = NULL;
    plist->plistComplete = gplistComplete;
    gplistComplete = plist;
}

static void
ReadNameList( PSZ psz )
{
    int length;
    LPTSTR pszNew;

    while (psz[0] != '\n') {
        length = 0;
        while (psz[length] != 0) {
            length++;
        }
        length;
        pszNew = (LPTSTR)LocalAlloc( LPTR, (length + 1)*sizeof(TCHAR) );
        if( !pszNew )
            return;
#ifdef UNICODE
        ConvertStringAsciiToUnicode( psz, (PWSTR) pszNew, length );
#else
        strncpy( pszNew, psz, length );
        InvertBitsA( pszNew, length );
#endif
        AddName(pszNew);

        psz += length + 1;
    }
}

static void
DeleteNameList()
{
    PLIST plist = gplistComplete, plistLast;

    while( plist != NULL ) {
        LocalFree( plist->pszStr );
        plistLast = plist;
        plist = plist->plistComplete;
        LocalFree( plistLast );
    }
}

/**************************************************************************\
* ConvertStringAsciiToUnicorn
*
\**************************************************************************/
static void
ConvertStringAsciiToUnicode( PSZ psz, PWSTR pwstr, int len )
{
    while( len-- )
         *pwstr++ = ~(*psz++) & 0xFF;
    *pwstr = 0; // null terminate
}

/**************************************************************************\
* FrameCalibration
*
* Adjusts the number of frames in a cycle to conform to desired cycle time
*
\**************************************************************************/
static int
FrameCalibration( AttrContext *pac, struct _timeb *pBaseTime, int framesPerCycle, int nCycle )
{
    struct _timeb thisTime;
    FLOAT cycleTime;

    _ftime( &thisTime );
    cycleTime = thisTime.time - pBaseTime->time +
           (thisTime.millitm - pBaseTime->millitm)/1000.0f;
    cycleTime /= (FLOAT) nCycle;

    if( cycleTime < gfMinCycleTime ) {
        // need to add more frames to cycle
        if( cycleTime == 0.0f ) // very unlikely
            framesPerCycle = 800;
        else
            framesPerCycle = (int)( (FLOAT)framesPerCycle *
                            (gfMinCycleTime/cycleTime) );
    } else {
        // for vstrings, subtract frames from cycle
        if( pac->demoType == DEMO_VSTRING ) {
            framesPerCycle = (int)( (FLOAT)framesPerCycle *
                            (gfMinCycleTime/cycleTime) );
        }
    }
#define MIN_FRAMES 16
    // make sure it's not too small
    if( framesPerCycle < MIN_FRAMES )
        framesPerCycle = MIN_FRAMES;

    return framesPerCycle;
}

/**************************************************************************\
* MapValue
*
* Maps the value along an input range, to a proportional one along an
* output range.  Each range must be monotonically increasing or decreasing.
*
* NO boundary conditions checked - responsibility of caller.
*
\**************************************************************************/
FLOAT
MapValue( FLOAT fInVal,
          FLOAT fIn1, FLOAT fIn2,       // input range
          FLOAT fOut1, FLOAT fOut2 )    // output range
{
    FLOAT fDist, fOutVal;

    // how far along the input range is fInVal?, in %
    fDist = (fInVal - fIn1) / (fIn2 - fIn1);

    // use this distance to interpolate into output range
    fOutVal = fDist * (fOut2 - fOut1) + fOut1;

    return fOutVal;
}

/**************************************************************************\
* MapValueI
*
* Similar to above, but maps integer values
*
* Currently, only works for increasing ranges
*
\**************************************************************************/
int
MapValueI( int inVal,
          int in1, int in2,       // input range
          int out1, int out2 )    // output range
{
    int inDiv;
    int outVal;
    FLOAT fScale, fComp;

    if( inVal >= in2 )
        return out2;
    if( inVal <= in1 )
        return out1;

    inDiv = abs(in2 - in1) + 1;
    fScale = (FLOAT) (inDiv-1) / (FLOAT) inDiv;
    fComp = 1.0f + (1.0f / inDiv);

    outVal = (int) MapValue( (FLOAT) inVal * fComp,
                       (FLOAT) in1, (FLOAT) in2 + 0.999f,
                       (FLOAT) out1, (FLOAT) out2 + 0.999f );
    return outVal;
}


SSTEXT3D.H

/******************************Module*Header*******************************\
* Module Name: sstext3d.h
*
* Global header for text3D screen saver.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/

#ifndef __sstext3d_h__
#define __sstext3d_h__

#include <commctrl.h>
#include "dlgs.h"
#include "sscommon.h"

#define PI_OVER_2 (PI/2.0f)
#define PI_OVER_4 (PI/4.0f)

#define TEXT_BUF_SIZE       100     // max length of display text buffer
                                    // (including NULL termination)
#define TEXT_LIMIT          16      // max length of user-inputted display text

#define MAX_IROT            100     // max integer rotation level (from slider)

#define MIN_SLIDER          0
#define MAX_SLIDER          100

// demo types
enum {
    DEMO_STRING = 0,    // static string
    DEMO_CLOCK,         // digital clock
    DEMO_VSTRING,       // variable string (actually a subset of DEMO_STRING)
};

#define MAX_DEMO  1             // max demo index
#define NUM_DEMOS (MAX_DEMO+1)

// surface styles
enum {
    SURFSTYLE_SOLID = 0,
    SURFSTYLE_TEX,
    SURFSTYLE_WIREFRAME
};

// rotation styles
enum {
    ROTSTYLE_NONE = 0,
    ROTSTYLE_SEESAW,
    ROTSTYLE_WOBBLE,
    ROTSTYLE_RANDOM,
    NUM_ROTSTYLES
};

enum {
    X_AXIS = 0,
    Y_AXIS,
    Z_AXIS,
    NUM_AXIS
};

#if defined(max)
#undef max
#endif
#define max( a, b ) ( a >= b ? a : b )

#if defined(clamp)
#undef clamp
#endif
#define clamp( a, lo, hi ) ( (a < lo) ? lo : ((a > hi) ? hi : a) )

#define deg_to_rad( a ) ( (a*PI) / 180.0f )
#define rad_to_deg( a ) ( (a*180.0f) / PI )

typedef struct {
    USHORT listNum;             // display list number
    TCHAR     glyph;            // glyph value (for extended LUT entries)
    LPGLYPHMETRICSFLOAT lpgmf;  // ptr to glyphmetrics
} LISTENTRY;

#define SIZE_LIST_LUT  512
#define MAX_DIRECT_LUT 256

typedef struct {
    HDC     hdc;
    int     nGlyphs;            // number of glyphs
    int     firstGlyph;
    FLOAT   chordalDeviation;
    FLOAT   extrusion;
    int     type;               // WGL_FONT_LINES or WGL_FONT_POLYGONS
    LISTENTRY *listLUT;             // LUT for cmd list # from glyph
    int     LUTIndex;           // current index for new indirect look-ups
} WglFontContext;

typedef struct {
    // registry or registry-derived attributes
    int     demoType;
    BOOL    bMaterialCycle;
    int     matType;    // material type from registry
    MATERIAL *pMat;     // ptr to current material
    int     surfStyle;
    int     rotStyle;   // rotation style
    int     texQual;
    float   fTesselFact;
    float   fDepth; // extrusion
    int     iSpeed; // rotation speed
    UINT    uSize;  // window size
    TEXFILE texFile;  // texture file
    TEXTURE texture;
    TCHAR   szFontName[LF_FACESIZE];       // font face name
    BOOL    bBold;
    BOOL    bItalic;
    BYTE    charSet;
    TCHAR   szText[TEXT_BUF_SIZE+1];  // display string
    USHORT  usText[TEXT_BUF_SIZE+1];    // display string converted to cmd lists

    // internal attributes
    BOOL            bTexture;
    BOOL            bRandomMat;
    WglFontContext  *pWglFontC;
    int             textLen;
    POINTFLOAT      pfTextExtent;
    POINTFLOAT      pfTextOrigin;       // upper left corner of extents
    POINT3D         p3dBoundingBox;     // bounding box, from spin angles
    FLOAT           fFovy;              // field of view in y-dir
    FLOAT           fAspect;            // aspect ratio of GL window
    FLOAT           fViewDist;          // dist to front of bounding box
    FLOAT           fZtrans;            // translation in z
    int             iRotStep;           // rotation step from slider
    int             iRotMinStep;        // min rotation step
    int             iRotMaxStep;        // max rotation step
    IPOINT3D        ip3dRotStep;        // xyz rot step, for random rotation
    IPOINT3D        ip3dRoti;           // current xyz rot step iteration
    POINT3D         p3dRotMin;          // min rotation amplitude
    POINT3D         p3dRotMax;          // max rotation amplitude
    POINT3D         p3dRotLimit;        // current rotation amplitude
    POINT3D         p3dRot;             // current rotation
    POINTFLOAT      *pTrig;             // current trig table
    SYSTEMTIME      stTime;
    BOOL            bXMajor;            // string either x-major or y-major

    SSContext       ssc;                // screen saver configuration
} AttrContext;

// Global attribute context
extern AttrContext gac;

extern WglFontContext* CreateWglFontContext (
    HDC     hdc,
    int     type,
    float   fExtrusion,
    float   fChordalDeviation );

extern void DeleteWglFontContext(
    WglFontContext *pwfc );

extern void DrawString (
    USHORT *string,
    int     strLen,
    WglFontContext *pwfc );

extern int GetStringExtent(
    LPTSTR  pszString,
    POINTFLOAT *extent,
    POINTFLOAT *origin,
    WglFontContext *pwfc );

extern void ConvertStringToList(
    LPTSTR pszSrc,
    USHORT *usDst,
    WglFontContext *pwfc );

extern void getIniSettings(void);

// Resource constants

#define IDS_SCREENSAVERTITLE    1020
#define IDS_SAVERNAME           1002

// registry attribute strings:
#define IDS_DEMOTYPE            1100
#define IDS_SURFSTYLE           1104
#define IDS_FONT                1109
#define IDS_FONT_ATTRIBUTES     1110
#define IDS_CHARSET             1111
#define IDS_TEXT                1115
#define IDS_SPEED               1120
#define IDS_ROTSTYLE            1124

// demo type strings
#define IDS_DEMO_STRING       1200
#define IDS_DEMO_CLOCK        1201

// rotation resource strings
#define IDS_ROTSTYLE_NONE            1400
#define IDS_ROTSTYLE_SEESAW          1401
#define IDS_ROTSTYLE_WOBBLE          1402
#define IDS_ROTSTYLE_RANDOM          1403

#define DLG_SETUP_HELP          2001
#define DLG_SETUP_TYPES         2002    // object type menu
#define DLG_SETUP_BITMAP        2003
#define DLG_SETUP_ABOUT         2010
#define DLG_SETUP_TESSEL        2012    // tesselation slider
#define DLG_SETUP_SIZE          2014    // size slider
#define DLG_SETUP_TEX           2016    // texture button
#define DLG_SETUP_FONT          2022    // select font button
#define DLG_SETUP_SPEED         2023    // speed slider

// surface styles
#define IDC_RADIO_SOLID         2030
#define IDC_RADIO_TEX           2031
#define IDC_RADIO_WIREFRAME     2032    // not presently used

#define IDC_TO_SURFSTYLE(n)         ( (n) - IDC_RADIO_SOLID )
// In order for the IDC_TO_SURFSTYLE conversion macro to work, the radio buttons
// for surface styles must be kept contiguous.

// rotation styles
#define DLG_SETUP_ROTSTYLE      4100

// demo type
#define IDC_DEMO_STRING         5000
#define IDC_DEMO_CLOCK          5001
#define IDC_TO_DEMOTYPE(n)      ( (n) - IDC_DEMO_STRING )

// sliders
#define IDC_STATIC_TESS         2051    // box around slider
#define IDC_STATIC_TESS_MIN     2052    // min label
#define IDC_STATIC_TESS_MAX     2053    // max label
#define IDC_STATIC_SIZE         2054
#define IDC_STATIC_SIZE_MIN     2055
#define IDC_STATIC_SIZE_MAX     2056

// rotation sliders
#define IDC_STATIC_ROTATION_GRP 5000

// configure text dialog box stuff
#define IDS_TEXT_TITLE          3001
#define DLG_TEXT_ENTER          3020
#define DLG_TEXT_SHOW           3021

// Choose font template
#define DLG_CF_TEMPLATE         6000
#define IDD_FONT                6001

// Default texture resource
#define IDB_DEFTEX              7000

#define SHELP_CONTENTS          01
#define SHELP_SHAPES            02
#define SHELP_PASSWORD          03
#define SHELP_COLOR             04
#define SHELP_MISC              05
#define SHELP_OVERVIEW          06

//
// Neutral ANSI/UNICODE types and macros plucked from ntdef.h
//
#ifdef  UNICODE
typedef WCHAR TCHAR, *PTCHAR;
#else   /* UNICODE */
typedef unsigned char TUCHAR, *PTUCHAR;
#endif /* UNICODE */

#endif // __sstext3d_h__