1/*
2 * This program is under the GNU GPL.
3 * Use at your own risk.
4 *
5 * written by David Bucciarelli (tech.hmw@plus.it)
6 *            Humanware s.r.l.
7 */
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <math.h>
12#include <string.h>
13
14#if defined (WIN32)|| defined(_WIN32)
15#include <windows.h>
16#include <mmsystem.h>
17#endif
18
19#include <GL/glew.h> /* for GL_RESCALE_NORMAL_EXT */
20#include "glut_wrap.h"
21
22#include "readtex.h"
23
24#ifdef XMESA
25#include "GL/xmesa.h"
26static int fullscreen = 1;
27#endif
28
29static int WIDTH = 640;
30static int HEIGHT = 480;
31
32static GLint T0;
33static GLint Frames;
34
35#define MAX_LOD 9
36
37#define TEX_SKY_WIDTH 256
38#define TEX_SKY_HEIGHT TEX_SKY_WIDTH
39
40#ifndef M_PI
41#define M_PI 3.1415926535
42#endif
43
44#define FROM_NONE   0
45#define FROM_DOWN   1
46#define FROM_UP     2
47#define FROM_LEFT   3
48#define FROM_RIGHT  4
49#define FROM_FRONT  5
50#define FROM_BACK   6
51
52static int win = 0;
53
54static float obs[3] = { 3.8, 0.0, 0.0 };
55static float dir[3];
56static float v = 0.0;
57static float alpha = -90.0;
58static float beta = 90.0;
59
60static int fog = 1;
61static int bfcull = 1;
62static int usetex = 1;
63static int help = 1;
64static int poutline = 0;
65static int normext = 1;
66static int joyavailable = 0;
67static int joyactive = 0;
68static int LODbias = 3;
69static int maxdepth = MAX_LOD;
70
71static unsigned int totpoly = 0;
72
73static GLuint t1id, t2id;
74static GLuint skydlist, LODdlist[MAX_LOD], LODnumpoly[MAX_LOD];
75
76static void
77initlight(void)
78{
79   GLfloat lspec[4] = { 1.0, 1.0, 1.0, 1.0 };
80   static GLfloat lightpos[4] = { 30, 15.0, 30.0, 1.0 };
81
82   glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
83   glLightfv(GL_LIGHT0, GL_SPECULAR, lspec);
84
85   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 32.0);
86   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, lspec);
87}
88
89static void
90initdlists(void)
91{
92   static int slicetable[MAX_LOD][2] = {
93      {21, 10},
94      {18, 9},
95      {15, 8},
96      {12, 7},
97      {9, 6},
98      {7, 5},
99      {5, 4},
100      {4, 3},
101      {3, 2}
102   };
103   GLUquadricObj *obj;
104   int i, xslices, yslices;
105
106   obj = gluNewQuadric();
107
108   skydlist = glGenLists(1);
109   glNewList(skydlist, GL_COMPILE);
110   glBindTexture(GL_TEXTURE_2D, t2id);
111   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
112   glColor3f(1.0f, 1.0f, 1.0f);
113
114   gluQuadricDrawStyle(obj, GLU_FILL);
115   gluQuadricNormals(obj, GLU_NONE);
116   gluQuadricTexture(obj, GL_TRUE);
117   gluQuadricOrientation(obj, GLU_INSIDE);
118   gluSphere(obj, 40.0f, 18, 9);
119
120   glEndList();
121
122   for (i = 0; i < MAX_LOD; i++) {
123      LODdlist[i] = glGenLists(1);
124      glNewList(LODdlist[i], GL_COMPILE);
125
126      gluQuadricDrawStyle(obj, GLU_FILL);
127      gluQuadricNormals(obj, GLU_SMOOTH);
128      gluQuadricTexture(obj, GL_TRUE);
129      gluQuadricOrientation(obj, GLU_OUTSIDE);
130      xslices = slicetable[i][0];
131      yslices = slicetable[i][1];
132      gluSphere(obj, 1.0f, xslices, yslices);
133      LODnumpoly[i] = xslices * (yslices - 2) + 2 * (xslices - 1);
134
135      glEndList();
136   }
137
138   gluDeleteQuadric(obj);
139}
140
141static void
142inittextures(void)
143{
144   GLubyte tsky[TEX_SKY_HEIGHT][TEX_SKY_WIDTH][3];
145   GLuint x, y;
146   GLfloat fact;
147   GLenum gluerr;
148
149   /* Brick */
150
151   glGenTextures(1, &t1id);
152   glBindTexture(GL_TEXTURE_2D, t1id);
153
154   if (!LoadRGBMipmaps(DEMOS_DATA_DIR "bw.rgb", 3)) {
155      fprintf(stderr, "Error reading a texture.\n");
156      exit(-1);
157   }
158
159   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
160   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
161
162   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
163		   GL_LINEAR_MIPMAP_LINEAR);
164   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
165
166   /* Sky */
167
168   glGenTextures(1, &t2id);
169   glBindTexture(GL_TEXTURE_2D, t2id);
170
171   for (y = 0; y < TEX_SKY_HEIGHT; y++)
172      for (x = 0; x < TEX_SKY_WIDTH; x++)
173	 if (y < TEX_SKY_HEIGHT / 2) {
174	    fact = y / (GLfloat) (TEX_SKY_HEIGHT / 2);
175	    tsky[y][x][0] =
176	       (GLubyte) (255.0f * (0.1f * fact + 0.3f * (1.0f - fact)));
177	    tsky[y][x][1] =
178	       (GLubyte) (255.0f * (0.2f * fact + 1.0f * (1.0f - fact)));
179	    tsky[y][x][2] = 255;
180	 }
181	 else {
182	    tsky[y][x][0] = tsky[TEX_SKY_HEIGHT - y - 1][x][0];
183	    tsky[y][x][1] = tsky[TEX_SKY_HEIGHT - y - 1][x][1];
184	    tsky[y][x][2] = 255;
185	 }
186
187   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
188   if (
189       (gluerr =
190	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TEX_SKY_WIDTH, TEX_SKY_HEIGHT,
191			  GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *) (tsky)))) {
192      fprintf(stderr, "GLULib%s\n", (char *) gluErrorString(gluerr));
193      exit(-1);
194   }
195
196   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
197   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
198
199   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
200		   GL_LINEAR_MIPMAP_LINEAR);
201   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
202}
203
204static void
205calcposobs(void)
206{
207   dir[0] = sin(alpha * M_PI / 180.0);
208   dir[1] = cos(alpha * M_PI / 180.0) * sin(beta * M_PI / 180.0);
209   dir[2] = cos(beta * M_PI / 180.0);
210
211   if (dir[0] < 1.0e-5 && dir[0] > -1.0e-5)
212      dir[0] = 0;
213   if (dir[1] < 1.0e-5 && dir[1] > -1.0e-5)
214      dir[1] = 0;
215   if (dir[2] < 1.0e-5 && dir[2] > -1.0e-5)
216      dir[2] = 0;
217
218   obs[0] += v * dir[0];
219   obs[1] += v * dir[1];
220   obs[2] += v * dir[2];
221}
222
223static void
224special(int k, int x, int y)
225{
226   switch (k) {
227   case GLUT_KEY_LEFT:
228      alpha -= 2.0;
229      break;
230   case GLUT_KEY_RIGHT:
231      alpha += 2.0;
232      break;
233   case GLUT_KEY_DOWN:
234      beta -= 2.0;
235      break;
236   case GLUT_KEY_UP:
237      beta += 2.0;
238      break;
239   }
240}
241
242static void
243cleanup(void)
244{
245   int i;
246
247   glDeleteTextures(1, &t1id);
248   glDeleteTextures(1, &t2id);
249
250   glDeleteLists(skydlist, 1);
251   for (i = 0; i < MAX_LOD; i++) {
252      glDeleteLists(LODdlist[i], 1);
253      glDeleteLists(LODnumpoly[i], 1);
254   }
255
256   glutDestroyWindow(glutGetWindow());
257}
258
259
260static void
261key(unsigned char k, int x, int y)
262{
263   switch (k) {
264   case 27:
265      cleanup();
266      exit(0);
267      break;
268
269   case 'a':
270      v += 0.01;
271      break;
272   case 'z':
273      v -= 0.01;
274      break;
275
276#ifdef XMESA
277   case ' ':
278      fullscreen = (!fullscreen);
279      XMesaSetFXmode(fullscreen ? XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW);
280      break;
281#endif
282
283   case '+':
284      LODbias--;
285      break;
286   case '-':
287      LODbias++;
288      break;
289   case 'j':
290      joyactive = (!joyactive);
291      break;
292   case 'h':
293      help = (!help);
294      break;
295   case 'f':
296      fog = (!fog);
297      break;
298   case 't':
299      usetex = (!usetex);
300      break;
301   case 'n':
302      normext = (!normext);
303      break;
304   case 'b':
305      if (bfcull) {
306	 glDisable(GL_CULL_FACE);
307	 bfcull = 0;
308      }
309      else {
310	 glEnable(GL_CULL_FACE);
311	 bfcull = 1;
312      }
313      break;
314   case 'p':
315      if (poutline) {
316	 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
317	 poutline = 0;
318	 usetex = 1;
319      }
320      else {
321	 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
322	 poutline = 1;
323	 usetex = 0;
324      }
325      break;
326   }
327}
328
329static void
330reshape(int w, int h)
331{
332   WIDTH = w;
333   HEIGHT = h;
334   glMatrixMode(GL_PROJECTION);
335   glLoadIdentity();
336   gluPerspective(90.0, w / (float) h, 0.8, 100.0);
337   glMatrixMode(GL_MODELVIEW);
338   glLoadIdentity();
339   glViewport(0, 0, w, h);
340}
341
342static void
343printstring(void *font, char *string)
344{
345   int len, i;
346
347   len = (int) strlen(string);
348   for (i = 0; i < len; i++)
349      glutBitmapCharacter(font, string[i]);
350}
351
352static void
353printhelp(void)
354{
355   glEnable(GL_BLEND);
356   glColor4f(0.5, 0.5, 0.5, 0.5);
357   glRecti(40, 40, 600, 440);
358   glDisable(GL_BLEND);
359
360   glColor3f(1.0, 0.0, 0.0);
361   glRasterPos2i(300, 420);
362   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "Help");
363
364   glRasterPos2i(60, 390);
365   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "h - Toggle Help");
366   glRasterPos2i(60, 360);
367   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "t - Toggle Textures");
368   glRasterPos2i(60, 330);
369   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "f - Toggle Fog");
370   glRasterPos2i(60, 300);
371   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "b - Toggle Back face culling");
372   glRasterPos2i(60, 270);
373   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "Arrow Keys - Rotate");
374   glRasterPos2i(60, 240);
375   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "a - Increase velocity");
376   glRasterPos2i(60, 210);
377   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "z - Decrease velocity");
378   glRasterPos2i(60, 180);
379   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "p - Toggle Wire frame");
380   glRasterPos2i(60, 150);
381   printstring(GLUT_BITMAP_TIMES_ROMAN_24,
382	       "n - Toggle GL_EXT_rescale_normal extension");
383   glRasterPos2i(60, 120);
384   printstring(GLUT_BITMAP_TIMES_ROMAN_24,
385	       "+/- - Increase/decrease the Object maximum LOD");
386
387   glRasterPos2i(60, 90);
388   if (joyavailable)
389      printstring(GLUT_BITMAP_TIMES_ROMAN_24,
390		  "j - Toggle jostick control (Joystick control available)");
391   else
392      printstring(GLUT_BITMAP_TIMES_ROMAN_24,
393		  "(No Joystick control available)");
394}
395
396static void
397dojoy(void)
398{
399#ifdef _WIN32
400   static UINT max[2] = { 0, 0 };
401   static UINT min[2] = { 0xffffffff, 0xffffffff }, center[2];
402   MMRESULT res;
403   JOYINFO joy;
404
405   res = joyGetPos(JOYSTICKID1, &joy);
406
407   if (res == JOYERR_NOERROR) {
408      joyavailable = 1;
409
410      if (max[0] < joy.wXpos)
411	 max[0] = joy.wXpos;
412      if (min[0] > joy.wXpos)
413	 min[0] = joy.wXpos;
414      center[0] = (max[0] + min[0]) / 2;
415
416      if (max[1] < joy.wYpos)
417	 max[1] = joy.wYpos;
418      if (min[1] > joy.wYpos)
419	 min[1] = joy.wYpos;
420      center[1] = (max[1] + min[1]) / 2;
421
422      if (joyactive) {
423	 if (fabs(center[0] - (float) joy.wXpos) > 0.1 * (max[0] - min[0]))
424	    alpha -=
425	       2.0 * (center[0] - (float) joy.wXpos) / (max[0] - min[0]);
426	 if (fabs(center[1] - (float) joy.wYpos) > 0.1 * (max[1] - min[1]))
427	    beta += 2.0 * (center[1] - (float) joy.wYpos) / (max[1] - min[1]);
428
429	 if (joy.wButtons & JOY_BUTTON1)
430	    v += 0.01;
431	 if (joy.wButtons & JOY_BUTTON2)
432	    v -= 0.01;
433      }
434   }
435   else
436      joyavailable = 0;
437#endif
438}
439
440static void
441drawipers(int depth, int from)
442{
443   int lod;
444
445   if (depth == maxdepth)
446      return;
447
448   lod = depth + LODbias;
449   if (lod < 0)
450      lod = 0;
451   if (lod >= MAX_LOD)
452      return;
453
454   switch (from) {
455   case FROM_NONE:
456      glCallList(LODdlist[lod]);
457
458      depth++;
459      drawipers(depth, FROM_DOWN);
460      drawipers(depth, FROM_UP);
461      drawipers(depth, FROM_FRONT);
462      drawipers(depth, FROM_BACK);
463      drawipers(depth, FROM_LEFT);
464      drawipers(depth, FROM_RIGHT);
465      break;
466   case FROM_FRONT:
467      glPushMatrix();
468      glTranslatef(0.0f, -1.5f, 0.0f);
469      glScalef(0.5f, 0.5f, 0.5f);
470
471      glCallList(LODdlist[lod]);
472
473      depth++;
474      drawipers(depth, FROM_DOWN);
475      drawipers(depth, FROM_UP);
476      drawipers(depth, FROM_FRONT);
477      drawipers(depth, FROM_LEFT);
478      drawipers(depth, FROM_RIGHT);
479      glPopMatrix();
480      break;
481   case FROM_BACK:
482      glPushMatrix();
483      glTranslatef(0.0f, 1.5f, 0.0f);
484      glScalef(0.5f, 0.5f, 0.5f);
485
486      glCallList(LODdlist[lod]);
487
488      depth++;
489      drawipers(depth, FROM_DOWN);
490      drawipers(depth, FROM_UP);
491      drawipers(depth, FROM_BACK);
492      drawipers(depth, FROM_LEFT);
493      drawipers(depth, FROM_RIGHT);
494      glPopMatrix();
495      break;
496   case FROM_LEFT:
497      glPushMatrix();
498      glTranslatef(-1.5f, 0.0f, 0.0f);
499      glScalef(0.5f, 0.5f, 0.5f);
500
501      glCallList(LODdlist[lod]);
502
503      depth++;
504      drawipers(depth, FROM_DOWN);
505      drawipers(depth, FROM_UP);
506      drawipers(depth, FROM_FRONT);
507      drawipers(depth, FROM_BACK);
508      drawipers(depth, FROM_LEFT);
509      glPopMatrix();
510      break;
511   case FROM_RIGHT:
512      glPushMatrix();
513      glTranslatef(1.5f, 0.0f, 0.0f);
514      glScalef(0.5f, 0.5f, 0.5f);
515
516      glCallList(LODdlist[lod]);
517
518      depth++;
519      drawipers(depth, FROM_DOWN);
520      drawipers(depth, FROM_UP);
521      drawipers(depth, FROM_FRONT);
522      drawipers(depth, FROM_BACK);
523      drawipers(depth, FROM_RIGHT);
524      glPopMatrix();
525      break;
526   case FROM_DOWN:
527      glPushMatrix();
528      glTranslatef(0.0f, 0.0f, 1.5f);
529      glScalef(0.5f, 0.5f, 0.5f);
530
531      glCallList(LODdlist[lod]);
532
533      depth++;
534      drawipers(depth, FROM_DOWN);
535      drawipers(depth, FROM_FRONT);
536      drawipers(depth, FROM_BACK);
537      drawipers(depth, FROM_LEFT);
538      drawipers(depth, FROM_RIGHT);
539      glPopMatrix();
540      break;
541   case FROM_UP:
542      glPushMatrix();
543      glTranslatef(0.0f, 0.0f, -1.5f);
544      glScalef(0.5f, 0.5f, 0.5f);
545
546      glCallList(LODdlist[lod]);
547
548      depth++;
549      drawipers(depth, FROM_UP);
550      drawipers(depth, FROM_FRONT);
551      drawipers(depth, FROM_BACK);
552      drawipers(depth, FROM_LEFT);
553      drawipers(depth, FROM_RIGHT);
554      glPopMatrix();
555      break;
556   }
557
558   totpoly += LODnumpoly[lod];
559}
560
561static void
562draw(void)
563{
564   static char frbuf[80] = "";
565   static GLfloat alpha = 0.0f;
566   static GLfloat beta = 0.0f;
567   static float fr = 0.0;
568   static double t0 = -1.;
569   double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
570   if (t0 < 0.0)
571      t0 = t;
572   dt = t - t0;
573   t0 = t;
574
575   dojoy();
576
577   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
578
579   if (usetex)
580      glEnable(GL_TEXTURE_2D);
581   else
582      glDisable(GL_TEXTURE_2D);
583
584   if (fog)
585      glEnable(GL_FOG);
586   else
587      glDisable(GL_FOG);
588
589   glPushMatrix();
590   calcposobs();
591   gluLookAt(obs[0], obs[1], obs[2],
592	     obs[0] + dir[0], obs[1] + dir[1], obs[2] + dir[2],
593	     0.0, 0.0, 1.0);
594
595   /* Scene */
596   glEnable(GL_DEPTH_TEST);
597
598   glShadeModel(GL_SMOOTH);
599   glBindTexture(GL_TEXTURE_2D, t1id);
600   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
601   glColor3f(1.0f, 1.0f, 1.0f);
602   glEnable(GL_LIGHT0);
603   glEnable(GL_LIGHTING);
604
605   if (normext)
606      glEnable(GL_RESCALE_NORMAL_EXT);
607   else
608      glEnable(GL_NORMALIZE);
609
610   glPushMatrix();
611   glRotatef(alpha, 0.0f, 0.0f, 1.0f);
612   glRotatef(beta, 1.0f, 0.0f, 0.0f);
613   totpoly = 0;
614   drawipers(0, FROM_NONE);
615   glPopMatrix();
616
617   alpha += 4.f * dt;
618   beta += 2.4f * dt;
619
620   glDisable(GL_LIGHTING);
621   glDisable(GL_LIGHT0);
622   glShadeModel(GL_FLAT);
623
624   if (normext)
625      glDisable(GL_RESCALE_NORMAL_EXT);
626   else
627      glDisable(GL_NORMALIZE);
628
629   glCallList(skydlist);
630
631   glPopMatrix();
632
633   /* Help Screen */
634
635   sprintf(frbuf,
636	   "Frame rate: %0.2f   LOD: %d   Tot. poly.: %d   Poly/sec: %.1f",
637	   fr, LODbias, totpoly, totpoly * fr);
638
639   glDisable(GL_TEXTURE_2D);
640   glDisable(GL_FOG);
641   glShadeModel(GL_FLAT);
642   glDisable(GL_DEPTH_TEST);
643
644   glMatrixMode(GL_PROJECTION);
645   glPushMatrix();
646   glLoadIdentity();
647   glOrtho(-0.5, 639.5, -0.5, 479.5, -1.0, 1.0);
648
649   glMatrixMode(GL_MODELVIEW);
650   glLoadIdentity();
651
652   glColor3f(1.0, 0.0, 0.0);
653   glRasterPos2i(10, 10);
654   printstring(GLUT_BITMAP_HELVETICA_18, frbuf);
655   glRasterPos2i(350, 470);
656   printstring(GLUT_BITMAP_HELVETICA_10,
657	       "IperS V1.0 Written by David Bucciarelli (tech.hmw@plus.it)");
658
659   if (help)
660      printhelp();
661
662   glMatrixMode(GL_PROJECTION);
663   glPopMatrix();
664   glMatrixMode(GL_MODELVIEW);
665
666   glutSwapBuffers();
667
668   Frames++;
669   {
670      GLint t = glutGet(GLUT_ELAPSED_TIME);
671      if (t - T0 >= 2000) {
672         GLfloat seconds = (t - T0) / 1000.0;
673         fr = Frames / seconds;
674         printf("Frame rate: %f\n", fr);
675         fflush(stdout);
676         T0 = t;
677         Frames = 0;
678      }
679   }
680}
681
682int
683main(int ac, char **av)
684{
685   float fogcolor[4] = { 0.7, 0.7, 0.7, 1.0 };
686
687   fprintf(stderr,
688	   "IperS V1.0\nWritten by David Bucciarelli (tech.hmw@plus.it)\n");
689
690   glutInitWindowSize(WIDTH, HEIGHT);
691   glutInit(&ac, av);
692
693   glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
694
695   if (!(win = glutCreateWindow("IperS"))) {
696      fprintf(stderr, "Error, couldn't open window\n");
697      exit(-1);
698   }
699
700   reshape(WIDTH, HEIGHT);
701
702   glShadeModel(GL_SMOOTH);
703   glEnable(GL_DEPTH_TEST);
704   glEnable(GL_CULL_FACE);
705   glEnable(GL_TEXTURE_2D);
706
707   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
708
709   glEnable(GL_FOG);
710   glFogi(GL_FOG_MODE, GL_EXP2);
711   glFogfv(GL_FOG_COLOR, fogcolor);
712
713   glFogf(GL_FOG_DENSITY, 0.006);
714
715   glHint(GL_FOG_HINT, GL_NICEST);
716
717   inittextures();
718   initdlists();
719   initlight();
720
721   glClearColor(fogcolor[0], fogcolor[1], fogcolor[2], fogcolor[3]);
722   glClear(GL_COLOR_BUFFER_BIT);
723
724   calcposobs();
725
726   glutReshapeFunc(reshape);
727   glutDisplayFunc(draw);
728   glutKeyboardFunc(key);
729   glutSpecialFunc(special);
730   glutIdleFunc(draw);
731
732   glutMainLoop();
733   cleanup();
734
735   return 0;
736}
737