132001f49Smrg/*
232001f49Smrg * Copyright (C) 2008  Tunsgten Graphics,Inc.   All Rights Reserved.
332001f49Smrg */
432001f49Smrg
532001f49Smrg/*
632001f49Smrg * Draw a lit, textured torus with X/EGL and OpenGL ES 1.x
732001f49Smrg * Brian Paul
832001f49Smrg * July 2008
932001f49Smrg */
1032001f49Smrg
1132001f49Smrg
1232001f49Smrg#include <assert.h>
1332001f49Smrg#include <math.h>
1432001f49Smrg#include <stdlib.h>
1532001f49Smrg#include <stdio.h>
1632001f49Smrg#include <GLES/gl.h>
1732001f49Smrg
1832001f49Smrg#include "eglut.h"
1932001f49Smrg
2032001f49Smrgstatic const struct {
2132001f49Smrg   GLenum internalFormat;
2232001f49Smrg   const char *name;
2332001f49Smrg   GLuint num_entries;
2432001f49Smrg   GLuint size;
2532001f49Smrg} cpal_formats[] = {
2632001f49Smrg   { GL_PALETTE4_RGB8_OES,     "GL_PALETTE4_RGB8_OES",      16, 3 },
2732001f49Smrg   { GL_PALETTE4_RGBA8_OES,    "GL_PALETTE4_RGBA8_OES",     16, 4 },
2832001f49Smrg   { GL_PALETTE4_R5_G6_B5_OES, "GL_PALETTE4_R5_G6_B5_OES",  16, 2 },
2932001f49Smrg   { GL_PALETTE4_RGBA4_OES,    "GL_PALETTE4_RGBA4_OES",     16, 2 },
3032001f49Smrg   { GL_PALETTE4_RGB5_A1_OES,  "GL_PALETTE4_RGB5_A1_OES",   16, 2 },
3132001f49Smrg   { GL_PALETTE8_RGB8_OES,     "GL_PALETTE8_RGB8_OES",     256, 3 },
3232001f49Smrg   { GL_PALETTE8_RGBA8_OES,    "GL_PALETTE8_RGBA8_OES",    256, 4 },
3332001f49Smrg   { GL_PALETTE8_R5_G6_B5_OES, "GL_PALETTE8_R5_G6_B5_OES", 256, 2 },
3432001f49Smrg   { GL_PALETTE8_RGBA4_OES,    "GL_PALETTE8_RGBA4_OES",    256, 2 },
3532001f49Smrg   { GL_PALETTE8_RGB5_A1_OES,  "GL_PALETTE8_RGB5_A1_OES",  256, 2 }
3632001f49Smrg};
3732001f49Smrg#define NUM_CPAL_FORMATS (sizeof(cpal_formats) / sizeof(cpal_formats[0]))
3832001f49Smrg
3932001f49Smrgstatic GLfloat view_rotx = 0.0, view_roty = 0.0, view_rotz = 0.0;
4032001f49Smrgstatic GLint tex_format = NUM_CPAL_FORMATS;
4132001f49Smrgstatic GLboolean animate = GL_TRUE;
4232001f49Smrgstatic int win;
4332001f49Smrg
4432001f49Smrg
4532001f49Smrgstatic void
4632001f49SmrgNormal(GLfloat *n, GLfloat nx, GLfloat ny, GLfloat nz)
4732001f49Smrg{
4832001f49Smrg   n[0] = nx;
4932001f49Smrg   n[1] = ny;
5032001f49Smrg   n[2] = nz;
5132001f49Smrg}
5232001f49Smrg
5332001f49Smrgstatic void
5432001f49SmrgVertex(GLfloat *v, GLfloat vx, GLfloat vy, GLfloat vz)
5532001f49Smrg{
5632001f49Smrg   v[0] = vx;
5732001f49Smrg   v[1] = vy;
5832001f49Smrg   v[2] = vz;
5932001f49Smrg}
6032001f49Smrg
6132001f49Smrgstatic void
6232001f49SmrgTexcoord(GLfloat *v, GLfloat s, GLfloat t)
6332001f49Smrg{
6432001f49Smrg   v[0] = s;
6532001f49Smrg   v[1] = t;
6632001f49Smrg}
6732001f49Smrg
6832001f49Smrg
6932001f49Smrg/* Borrowed from glut, adapted */
7032001f49Smrgstatic void
7132001f49Smrgdraw_torus(GLfloat r, GLfloat R, GLint nsides, GLint rings)
7232001f49Smrg{
7332001f49Smrg   int i, j;
7432001f49Smrg   GLfloat theta, phi, theta1;
7532001f49Smrg   GLfloat cosTheta, sinTheta;
7632001f49Smrg   GLfloat cosTheta1, sinTheta1;
7732001f49Smrg   GLfloat ringDelta, sideDelta;
7832001f49Smrg   GLfloat varray[100][3], narray[100][3], tarray[100][2];
7932001f49Smrg   int vcount;
8032001f49Smrg
8132001f49Smrg   glVertexPointer(3, GL_FLOAT, 0, varray);
8232001f49Smrg   glNormalPointer(GL_FLOAT, 0, narray);
8332001f49Smrg   glTexCoordPointer(2, GL_FLOAT, 0, tarray);
8432001f49Smrg   glEnableClientState(GL_VERTEX_ARRAY);
8532001f49Smrg   glEnableClientState(GL_NORMAL_ARRAY);
8632001f49Smrg   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
8732001f49Smrg
8832001f49Smrg   ringDelta = 2.0 * M_PI / rings;
8932001f49Smrg   sideDelta = 2.0 * M_PI / nsides;
9032001f49Smrg
9132001f49Smrg   theta = 0.0;
9232001f49Smrg   cosTheta = 1.0;
9332001f49Smrg   sinTheta = 0.0;
9432001f49Smrg   for (i = rings - 1; i >= 0; i--) {
9532001f49Smrg      theta1 = theta + ringDelta;
9632001f49Smrg      cosTheta1 = cos(theta1);
9732001f49Smrg      sinTheta1 = sin(theta1);
9832001f49Smrg
9932001f49Smrg      vcount = 0; /* glBegin(GL_QUAD_STRIP); */
10032001f49Smrg
10132001f49Smrg      phi = 0.0;
10232001f49Smrg      for (j = nsides; j >= 0; j--) {
10332001f49Smrg         GLfloat s0, s1, t;
10432001f49Smrg         GLfloat cosPhi, sinPhi, dist;
10532001f49Smrg
10632001f49Smrg         phi += sideDelta;
10732001f49Smrg         cosPhi = cos(phi);
10832001f49Smrg         sinPhi = sin(phi);
10932001f49Smrg         dist = R + r * cosPhi;
11032001f49Smrg
11132001f49Smrg         s0 = 20.0 * theta / (2.0 * M_PI);
11232001f49Smrg         s1 = 20.0 * theta1 / (2.0 * M_PI);
11332001f49Smrg         t = 8.0 * phi / (2.0 * M_PI);
11432001f49Smrg
11532001f49Smrg         Normal(narray[vcount], cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi);
11632001f49Smrg         Texcoord(tarray[vcount], s1, t);
11732001f49Smrg         Vertex(varray[vcount], cosTheta1 * dist, -sinTheta1 * dist, r * sinPhi);
11832001f49Smrg         vcount++;
11932001f49Smrg
12032001f49Smrg         Normal(narray[vcount], cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi);
12132001f49Smrg         Texcoord(tarray[vcount], s0, t);
12232001f49Smrg         Vertex(varray[vcount], cosTheta * dist, -sinTheta * dist,  r * sinPhi);
12332001f49Smrg         vcount++;
12432001f49Smrg      }
12532001f49Smrg
12632001f49Smrg      /*glEnd();*/
12732001f49Smrg      assert(vcount <= 100);
12832001f49Smrg      glDrawArrays(GL_TRIANGLE_STRIP, 0, vcount);
12932001f49Smrg
13032001f49Smrg      theta = theta1;
13132001f49Smrg      cosTheta = cosTheta1;
13232001f49Smrg      sinTheta = sinTheta1;
13332001f49Smrg   }
13432001f49Smrg
13532001f49Smrg   glDisableClientState(GL_VERTEX_ARRAY);
13632001f49Smrg   glDisableClientState(GL_NORMAL_ARRAY);
13732001f49Smrg   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
13832001f49Smrg}
13932001f49Smrg
14032001f49Smrg
14132001f49Smrgstatic void
14232001f49Smrgdraw(void)
14332001f49Smrg{
14432001f49Smrg   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
14532001f49Smrg
14632001f49Smrg   glPushMatrix();
14732001f49Smrg   glRotatef(view_rotx, 1, 0, 0);
14832001f49Smrg   glRotatef(view_roty, 0, 1, 0);
14932001f49Smrg   glRotatef(view_rotz, 0, 0, 1);
15032001f49Smrg   glScalef(0.5, 0.5, 0.5);
15132001f49Smrg
15232001f49Smrg   draw_torus(1.0, 3.0, 30, 60);
15332001f49Smrg
15432001f49Smrg   glPopMatrix();
15532001f49Smrg}
15632001f49Smrg
15732001f49Smrg
15832001f49Smrg/* new window size or exposure */
15932001f49Smrgstatic void
16032001f49Smrgreshape(int width, int height)
16132001f49Smrg{
16232001f49Smrg   GLfloat ar = (GLfloat) width / (GLfloat) height;
16332001f49Smrg
16432001f49Smrg   glViewport(0, 0, (GLint) width, (GLint) height);
16532001f49Smrg
16632001f49Smrg   glMatrixMode(GL_PROJECTION);
16732001f49Smrg   glLoadIdentity();
16832001f49Smrg
16932001f49Smrg#ifdef GL_VERSION_ES_CM_1_0
17032001f49Smrg   glFrustumf(-ar, ar, -1, 1, 5.0, 60.0);
17132001f49Smrg#else
17232001f49Smrg   glFrustum(-ar, ar, -1, 1, 5.0, 60.0);
17332001f49Smrg#endif
17432001f49Smrg
17532001f49Smrg   glMatrixMode(GL_MODELVIEW);
17632001f49Smrg   glLoadIdentity();
17732001f49Smrg   glTranslatef(0.0, 0.0, -15.0);
17832001f49Smrg}
17932001f49Smrg
18032001f49Smrg
18132001f49Smrgstatic GLint
18232001f49Smrgmake_cpal_texture(GLint idx)
18332001f49Smrg{
18432001f49Smrg#define SZ 64
18532001f49Smrg   GLenum internalFormat = GL_PALETTE4_RGB8_OES + idx;
18632001f49Smrg   GLenum Filter = GL_LINEAR;
18732001f49Smrg   GLubyte palette[256 * 4 + SZ * SZ];
18832001f49Smrg   GLubyte *indices;
18932001f49Smrg   GLsizei image_size;
19032001f49Smrg   GLuint i, j;
19132001f49Smrg   GLuint packed_indices = 0;
19232001f49Smrg
19332001f49Smrg   assert(cpal_formats[idx].internalFormat == internalFormat);
19432001f49Smrg
19532001f49Smrg   /* init palette */
19632001f49Smrg   switch (internalFormat) {
19732001f49Smrg   case GL_PALETTE4_RGB8_OES:
19832001f49Smrg   case GL_PALETTE8_RGB8_OES:
19932001f49Smrg      /* first entry */
20032001f49Smrg      palette[0] = 255;
20132001f49Smrg      palette[1] = 255;
20232001f49Smrg      palette[2] = 255;
20332001f49Smrg      /* second entry */
20432001f49Smrg      palette[3] = 127;
20532001f49Smrg      palette[4] = 127;
20632001f49Smrg      palette[5] = 127;
20732001f49Smrg      break;
20832001f49Smrg   case GL_PALETTE4_RGBA8_OES:
20932001f49Smrg   case GL_PALETTE8_RGBA8_OES:
21032001f49Smrg      /* first entry */
21132001f49Smrg      palette[0] = 255;
21232001f49Smrg      palette[1] = 255;
21332001f49Smrg      palette[2] = 255;
21432001f49Smrg      palette[3] = 255;
21532001f49Smrg      /* second entry */
21632001f49Smrg      palette[4] = 127;
21732001f49Smrg      palette[5] = 127;
21832001f49Smrg      palette[6] = 127;
21932001f49Smrg      palette[7] = 255;
22032001f49Smrg      break;
22132001f49Smrg   case GL_PALETTE4_R5_G6_B5_OES:
22232001f49Smrg   case GL_PALETTE8_R5_G6_B5_OES:
22332001f49Smrg      {
22432001f49Smrg         GLushort *pal = (GLushort *) palette;
22532001f49Smrg         /* first entry */
22632001f49Smrg         pal[0] = (31 << 11 | 63 << 5 | 31);
22732001f49Smrg         /* second entry */
22832001f49Smrg         pal[1] = (15 << 11 | 31 << 5 | 15);
22932001f49Smrg      }
23032001f49Smrg      break;
23132001f49Smrg   case GL_PALETTE4_RGBA4_OES:
23232001f49Smrg   case GL_PALETTE8_RGBA4_OES:
23332001f49Smrg      {
23432001f49Smrg         GLushort *pal = (GLushort *) palette;
23532001f49Smrg         /* first entry */
23632001f49Smrg         pal[0] = (15 << 12 | 15 << 8 | 15 << 4 | 15);
23732001f49Smrg         /* second entry */
23832001f49Smrg         pal[1] = (7 << 12 | 7 << 8 | 7 << 4 | 15);
23932001f49Smrg      }
24032001f49Smrg      break;
24132001f49Smrg   case GL_PALETTE4_RGB5_A1_OES:
24232001f49Smrg   case GL_PALETTE8_RGB5_A1_OES:
24332001f49Smrg      {
24432001f49Smrg         GLushort *pal = (GLushort *) palette;
24532001f49Smrg         /* first entry */
24632001f49Smrg         pal[0] = (31 << 11 | 31 << 6 | 31 << 1 | 1);
24732001f49Smrg         /* second entry */
24832001f49Smrg         pal[1] = (15 << 11 | 15 << 6 | 15 << 1 | 1);
24932001f49Smrg      }
25032001f49Smrg      break;
25132001f49Smrg   }
25232001f49Smrg
25332001f49Smrg   image_size = cpal_formats[idx].size * cpal_formats[idx].num_entries;
25432001f49Smrg   indices = palette + image_size;
25532001f49Smrg   for (i = 0; i < SZ; i++) {
25632001f49Smrg      for (j = 0; j < SZ; j++) {
25732001f49Smrg         GLfloat d;
25832001f49Smrg         GLint index;
25932001f49Smrg         d = (i - SZ/2) * (i - SZ/2) + (j - SZ/2) * (j - SZ/2);
26032001f49Smrg         d = sqrt(d);
26132001f49Smrg         index = (d < SZ / 3) ? 0 : 1;
26232001f49Smrg
26332001f49Smrg         if (cpal_formats[idx].num_entries == 16) {
26432001f49Smrg            /* 4-bit indices packed in GLubyte */
26532001f49Smrg            packed_indices |= index << (4 * (1 - (j % 2)));
26632001f49Smrg            if (j % 2) {
26732001f49Smrg               *(indices + (i * SZ + j - 1) / 2) = packed_indices & 0xff;
26832001f49Smrg               packed_indices = 0;
26932001f49Smrg               image_size += 1;
27032001f49Smrg            }
27132001f49Smrg         }
27232001f49Smrg         else {
27332001f49Smrg            /* 8-bit indices */
27432001f49Smrg            *(indices + i * SZ + j) = index;
27532001f49Smrg            image_size += 1;
27632001f49Smrg         }
27732001f49Smrg      }
27832001f49Smrg   }
27932001f49Smrg
28032001f49Smrg   glActiveTexture(GL_TEXTURE0); /* unit 0 */
28132001f49Smrg   glBindTexture(GL_TEXTURE_2D, 42);
28232001f49Smrg   glCompressedTexImage2D(GL_TEXTURE_2D, 0, internalFormat, SZ, SZ, 0,
28332001f49Smrg                          image_size, palette);
28432001f49Smrg
28532001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Filter);
28632001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Filter);
28732001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
28832001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
28932001f49Smrg#undef SZ
29032001f49Smrg
29132001f49Smrg   return image_size;
29232001f49Smrg}
29332001f49Smrg
29432001f49Smrg
29532001f49Smrgstatic GLint
29632001f49Smrgmake_texture(void)
29732001f49Smrg{
29832001f49Smrg#define SZ 64
29932001f49Smrg   GLenum Filter = GL_LINEAR;
30032001f49Smrg   GLubyte image[SZ][SZ][4];
30132001f49Smrg   GLuint i, j;
30232001f49Smrg
30332001f49Smrg   for (i = 0; i < SZ; i++) {
30432001f49Smrg      for (j = 0; j < SZ; j++) {
30532001f49Smrg         GLfloat d = (i - SZ/2) * (i - SZ/2) + (j - SZ/2) * (j - SZ/2);
30632001f49Smrg         d = sqrt(d);
30732001f49Smrg         if (d < SZ/3) {
30832001f49Smrg            image[i][j][0] = 255;
30932001f49Smrg            image[i][j][1] = 255;
31032001f49Smrg            image[i][j][2] = 255;
31132001f49Smrg            image[i][j][3] = 255;
31232001f49Smrg         }
31332001f49Smrg         else {
31432001f49Smrg            image[i][j][0] = 127;
31532001f49Smrg            image[i][j][1] = 127;
31632001f49Smrg            image[i][j][2] = 127;
31732001f49Smrg            image[i][j][3] = 255;
31832001f49Smrg         }
31932001f49Smrg      }
32032001f49Smrg   }
32132001f49Smrg
32232001f49Smrg   glActiveTexture(GL_TEXTURE0); /* unit 0 */
32332001f49Smrg   glBindTexture(GL_TEXTURE_2D, 42);
32432001f49Smrg   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SZ, SZ, 0,
32532001f49Smrg                GL_RGBA, GL_UNSIGNED_BYTE, image);
32632001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Filter);
32732001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Filter);
32832001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
32932001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
33032001f49Smrg#undef SZ
33132001f49Smrg
33232001f49Smrg   return sizeof(image);
33332001f49Smrg}
33432001f49Smrg
33532001f49Smrg
33632001f49Smrg
33732001f49Smrgstatic void
33832001f49Smrginit(void)
33932001f49Smrg{
34032001f49Smrg   static const GLfloat red[4] = {1, 0, 0, 0};
34132001f49Smrg   static const GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
34232001f49Smrg   static const GLfloat diffuse[4] = {0.7, 0.7, 0.7, 1.0};
34332001f49Smrg   static const GLfloat specular[4] = {0.001, 0.001, 0.001, 1.0};
34432001f49Smrg   static const GLfloat pos[4] = {20, 20, 50, 1};
34532001f49Smrg
34632001f49Smrg   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red);
34732001f49Smrg   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, white);
34832001f49Smrg   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 9.0);
34932001f49Smrg
35032001f49Smrg   glEnable(GL_LIGHTING);
35132001f49Smrg   glEnable(GL_LIGHT0);
35232001f49Smrg   glLightfv(GL_LIGHT0, GL_POSITION, pos);
35332001f49Smrg   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
35432001f49Smrg   glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
35532001f49Smrg
35632001f49Smrg   glClearColor(0.4, 0.4, 0.4, 0.0);
35732001f49Smrg   glEnable(GL_DEPTH_TEST);
35832001f49Smrg
35932001f49Smrg   make_texture();
36032001f49Smrg   glEnable(GL_TEXTURE_2D);
3617ec3b29aSmrg
3627ec3b29aSmrg   /* Enable automatic normalizing to get proper lighting when torus is
3637ec3b29aSmrg    * scaled down via glScalef
3647ec3b29aSmrg    */
3657ec3b29aSmrg   glEnable(GL_NORMALIZE);
36632001f49Smrg}
36732001f49Smrg
36832001f49Smrg
36932001f49Smrgstatic void
37032001f49Smrgidle(void)
37132001f49Smrg{
37232001f49Smrg   if (animate) {
37332001f49Smrg      view_rotx += 1.0;
37432001f49Smrg      view_roty += 2.0;
37532001f49Smrg      eglutPostRedisplay();
37632001f49Smrg   }
37732001f49Smrg}
37832001f49Smrg
37932001f49Smrgstatic void
38032001f49Smrgkey(unsigned char key)
38132001f49Smrg{
38232001f49Smrg   switch (key) {
38332001f49Smrg   case ' ':
38432001f49Smrg      animate = !animate;
38532001f49Smrg      break;
38632001f49Smrg   case 't':
38732001f49Smrg      {
38832001f49Smrg         GLint size;
38932001f49Smrg         tex_format = (tex_format + 1) % (NUM_CPAL_FORMATS + 1);
39032001f49Smrg         if (tex_format < NUM_CPAL_FORMATS) {
39132001f49Smrg            size = make_cpal_texture(tex_format);
39232001f49Smrg            printf("Using %s (%d bytes)\n",
39332001f49Smrg                  cpal_formats[tex_format].name, size);
39432001f49Smrg         }
39532001f49Smrg         else {
39632001f49Smrg            size = make_texture();
39732001f49Smrg            printf("Using uncompressed texture (%d bytes)\n", size);
39832001f49Smrg         }
39932001f49Smrg
40032001f49Smrg         eglutPostRedisplay();
40132001f49Smrg      }
40232001f49Smrg      break;
40332001f49Smrg   case 27:
40432001f49Smrg      eglutDestroyWindow(win);
40532001f49Smrg      exit(0);
40632001f49Smrg      break;
40732001f49Smrg   default:
40832001f49Smrg      break;
40932001f49Smrg   }
41032001f49Smrg}
41132001f49Smrg
41232001f49Smrgstatic void
41332001f49Smrgspecial_key(int key)
41432001f49Smrg{
41532001f49Smrg   switch (key) {
41632001f49Smrg   case EGLUT_KEY_LEFT:
41732001f49Smrg      view_roty += 5.0;
41832001f49Smrg      break;
41932001f49Smrg   case EGLUT_KEY_RIGHT:
42032001f49Smrg      view_roty -= 5.0;
42132001f49Smrg      break;
42232001f49Smrg   case EGLUT_KEY_UP:
42332001f49Smrg      view_rotx += 5.0;
42432001f49Smrg      break;
42532001f49Smrg   case EGLUT_KEY_DOWN:
42632001f49Smrg      view_rotx -= 5.0;
42732001f49Smrg      break;
42832001f49Smrg   default:
42932001f49Smrg      break;
43032001f49Smrg   }
43132001f49Smrg}
43232001f49Smrg
43332001f49Smrgint
43432001f49Smrgmain(int argc, char *argv[])
43532001f49Smrg{
43632001f49Smrg   eglutInitWindowSize(300, 300);
43732001f49Smrg   eglutInitAPIMask(EGLUT_OPENGL_ES1_BIT);
43832001f49Smrg   eglutInit(argc, argv);
43932001f49Smrg
44032001f49Smrg   win = eglutCreateWindow("torus");
44132001f49Smrg
44232001f49Smrg   eglutIdleFunc(idle);
44332001f49Smrg   eglutReshapeFunc(reshape);
44432001f49Smrg   eglutDisplayFunc(draw);
44532001f49Smrg   eglutKeyboardFunc(key);
44632001f49Smrg   eglutSpecialFunc(special_key);
44732001f49Smrg
44832001f49Smrg   init();
44932001f49Smrg
45032001f49Smrg   eglutMainLoop();
45132001f49Smrg
45232001f49Smrg   return 0;
45332001f49Smrg}
454