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 * based on a Mikael SkiZoWalker's (MoDEL) / France (Skizo@Hol.Fr) demo
9 */
10
11#include <assert.h>
12#include <stdio.h>
13#include <math.h>
14#include <stdlib.h>
15#include <string.h>
16#include <time.h>
17
18#ifdef WIN32
19#include <windows.h>
20#endif
21
22#include "glut_wrap.h"
23
24#ifdef XMESA
25#include "GL/xmesa.h"
26static int fullscreen = 1;
27#endif
28
29#ifndef M_PI
30#define M_PI 3.14159265
31#endif
32
33#define heightMnt	450
34#define	lenghtXmnt	62
35#define lenghtYmnt	62
36
37#define stepXmnt     96.0
38#define stepYmnt     96.0
39
40#define WIDTH 640
41#define HEIGHT 480
42
43static GLint T0 = 0;
44static GLint Frames = 0;
45
46#define TSCALE 4
47
48#define FOV 85
49
50static GLfloat terrain[256 * 256];
51static GLfloat terraincolor[256 * 256][3];
52
53static int win = 0;
54
55static int fog = 1;
56static int bfcull = 1;
57static int usetex = 1;
58static int poutline = 0;
59static int help = 1;
60static int joyavailable = 0;
61static int joyactive = 0;
62static float ModZMnt;
63static long GlobalMnt = 0;
64
65static int scrwidth = WIDTH;
66static int scrheight = HEIGHT;
67
68#define OBSSTARTX 992.0
69#define OBSSTARTY 103.0
70
71static float obs[3] = { OBSSTARTX, heightMnt * 1.3, OBSSTARTY };
72static float dir[3], v1[2], v2[2];
73static float v = 900.0;
74static float alpha = 75.0;
75static float beta = 90.0;
76
77static void
78calcposobs(void)
79{
80   float alpha1, alpha2;
81   static double t0 = -1.;
82   double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
83   if (t0 < 0.0)
84      t0 = t;
85   dt = t - t0;
86   t0 = t;
87
88   dir[0] = sin(alpha * M_PI / 180.0);
89   dir[2] = cos(alpha * M_PI / 180.0) * sin(beta * M_PI / 180.0);
90   dir[1] = cos(beta * M_PI / 180.0);
91
92   if (dir[0] < 1.0e-5 && dir[0] > -1.0e-5)
93      dir[0] = 0;
94   if (dir[1] < 1.0e-5 && dir[1] > -1.0e-5)
95      dir[1] = 0;
96   if (dir[2] < 1.0e-5 && dir[2] > -1.0e-5)
97      dir[2] = 0;
98
99   alpha1 = alpha + FOV / 2.0;
100   v1[0] = sin(alpha1 * M_PI / 180.0);
101   v1[1] = cos(alpha1 * M_PI / 180.0);
102
103   alpha2 = alpha - FOV / 2.0;
104   v2[0] = sin(alpha2 * M_PI / 180.0);
105   v2[1] = cos(alpha2 * M_PI / 180.0);
106
107   obs[0] += v * dir[0] * dt;
108   obs[1] += v * dir[1] * dt;
109   obs[2] += v * dir[2] * dt;
110
111   if (obs[1] < 0.0)
112      obs[1] = 0.0;
113}
114
115static void
116reshape(int width, int height)
117{
118   scrwidth = width;
119   scrheight = height;
120   glViewport(0, 0, (GLint) width, (GLint) height);
121   glMatrixMode(GL_PROJECTION);
122   glLoadIdentity();
123   gluPerspective(50.0, ((GLfloat) width / (GLfloat) height),
124		  lenghtXmnt * stepYmnt * 0.01, lenghtXmnt * stepYmnt * 0.7);
125   glMatrixMode(GL_MODELVIEW);
126   glLoadIdentity();
127}
128
129static int
130clipstrip(float y, float *start, float *end)
131{
132   float x1, x2, t1, t2, tmp;
133
134   if (v1[1] == 0.0) {
135      t1 = 0.0;
136      x1 = -HUGE_VAL;
137   }
138   else {
139      t1 = y / v1[1];
140      x1 = t1 * v1[0];
141   }
142
143   if (v2[1] == 0.0) {
144      t2 = 0.0;
145      x2 = HUGE_VAL;
146   }
147   else {
148      t2 = y / v2[1];
149      x2 = t2 * v2[0];
150   }
151
152   if (((x1 < -(lenghtXmnt * stepXmnt) / 2) && (t2 <= 0.0)) ||
153       ((t1 <= 0.0) && (x2 > (lenghtXmnt * stepXmnt) / 2)) ||
154       ((t1 < 0.0) && (t2 < 0.0)))
155      return 0;
156
157   if ((t1 == 0.0) && (t2 == 0.0)) {
158      if ((v1[0] < 0.0) && (v1[1] > 0.0) && (v2[0] < 0.0) && (v2[1] < 0.0)) {
159	 *start = -(lenghtXmnt * stepXmnt) / 2;
160	 *end = stepXmnt;
161	 return 1;
162      }
163      else {
164	 if ((v1[0] > 0.0) && (v1[1] < 0.0) && (v2[0] > 0.0) && (v2[1] > 0.0)) {
165	    *start = -stepXmnt;
166	    *end = (lenghtXmnt * stepXmnt) / 2;
167	    return 1;
168	 }
169	 else
170	    return 0;
171      }
172   }
173   else {
174      if (t2 < 0.0) {
175	 if (x1 < 0.0)
176	    x2 = -(lenghtXmnt * stepXmnt) / 2;
177	 else
178	    x2 = (lenghtXmnt * stepXmnt) / 2;
179      }
180
181      if (t1 < 0.0) {
182	 if (x2 < 0.0)
183	    x1 = -(lenghtXmnt * stepXmnt) / 2;
184	 else
185	    x1 = (lenghtXmnt * stepXmnt) / 2;
186      }
187   }
188
189   if (x1 > x2) {
190      tmp = x1;
191      x1 = x2;
192      x2 = tmp;
193   }
194
195   x1 -= stepXmnt;
196   if (x1 < -(lenghtXmnt * stepXmnt) / 2)
197      x1 = -(lenghtXmnt * stepXmnt) / 2;
198
199   x2 += stepXmnt;
200   if (x2 > (lenghtXmnt * stepXmnt) / 2)
201      x2 = (lenghtXmnt * stepXmnt) / 2;
202
203   *start = ((int) (x1 / stepXmnt)) * stepXmnt;
204   *end = ((int) (x2 / stepXmnt)) * stepXmnt;
205
206   return 1;
207}
208
209static void
210printstring(void *font, char *string)
211{
212   int len, i;
213
214   len = (int) strlen(string);
215   for (i = 0; i < len; i++)
216      glutBitmapCharacter(font, string[i]);
217}
218
219static void
220printhelp(void)
221{
222   glEnable(GL_BLEND);
223   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
224   glColor4f(0.0, 0.0, 0.0, 0.5);
225   glRecti(40, 40, 600, 440);
226   glDisable(GL_BLEND);
227
228   glColor3f(1.0, 0.0, 0.0);
229   glRasterPos2i(300, 420);
230   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "Help");
231
232   glRasterPos2i(60, 390);
233   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "h - Toggle Help");
234   glRasterPos2i(60, 360);
235   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "t - Toggle Textures");
236   glRasterPos2i(60, 330);
237   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "f - Toggle Fog");
238   glRasterPos2i(60, 300);
239   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "p - Wire frame");
240   glRasterPos2i(60, 270);
241   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "b - Toggle Back face culling");
242   glRasterPos2i(60, 240);
243   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "Arrow Keys - Rotate");
244   glRasterPos2i(60, 210);
245   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "a - Increase velocity");
246   glRasterPos2i(60, 180);
247   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "z - Decrease velocity");
248
249   glRasterPos2i(60, 150);
250   if (joyavailable)
251      printstring(GLUT_BITMAP_TIMES_ROMAN_24,
252		  "j - Toggle jostick control (Joystick control available)");
253   else
254      printstring(GLUT_BITMAP_TIMES_ROMAN_24,
255		  "(No Joystick control available)");
256}
257
258static void
259drawterrain(void)
260{
261   int h, i, idx, ox, oy;
262   float j, k, start, end;
263
264   ox = (int) (obs[0] / stepXmnt);
265   oy = (int) (obs[2] / stepYmnt);
266   GlobalMnt = ((ox * TSCALE) & 255) + ((oy * TSCALE) & 255) * 256;
267
268   glPushMatrix();
269   glTranslatef((float) ox * stepXmnt, 0, (float) oy * stepYmnt);
270
271   for (h = 0, k = -(lenghtYmnt * stepYmnt) / 2; h < lenghtYmnt;
272	k += stepYmnt, h++) {
273      if (!clipstrip(k, &start, &end))
274	 continue;
275
276      glBegin(GL_TRIANGLE_STRIP);	/* I hope that the optimizer will be able to improve this code */
277      for (i = (int) (lenghtXmnt / 2 + start / stepXmnt), j = start; j <= end;
278	   j += stepXmnt, i++) {
279	 idx = (i * TSCALE + h * 256 * TSCALE + GlobalMnt) & 65535;
280	 glColor3fv(terraincolor[idx]);
281	 glTexCoord2f((ox + i) / 8.0, (oy + h) / 8.0);
282	 glVertex3f(j, terrain[idx], k);
283
284	 idx =
285	    (i * TSCALE + h * 256 * TSCALE + 256 * TSCALE +
286	     GlobalMnt) & 65535;
287	 glColor3fv(terraincolor[idx]);
288	 glTexCoord2f((ox + i) / 8.0, (oy + h + 1) / 8.0);
289	 glVertex3f(j, terrain[idx], k + stepYmnt);
290      }
291      glEnd();
292   }
293
294   glDisable(GL_CULL_FACE);
295   glDisable(GL_TEXTURE_2D);
296   glEnable(GL_BLEND);
297   glBegin(GL_QUADS);
298   glColor4f(0.1, 0.7, 1.0, 0.4);
299   glVertex3f(-(lenghtXmnt * stepXmnt) / 2.0, heightMnt * 0.6,
300	      -(lenghtYmnt * stepYmnt) / 2.0);
301   glVertex3f(-(lenghtXmnt * stepXmnt) / 2.0, heightMnt * 0.6,
302	      (lenghtYmnt * stepYmnt) / 2.0);
303   glVertex3f((lenghtXmnt * stepXmnt) / 2.0, heightMnt * 0.6,
304	      (lenghtYmnt * stepYmnt) / 2.0);
305   glVertex3f((lenghtXmnt * stepXmnt) / 2.0, heightMnt * 0.6,
306	      -(lenghtYmnt * stepYmnt) / 2.0);
307   glEnd();
308   glDisable(GL_BLEND);
309   if (bfcull)
310      glEnable(GL_CULL_FACE);
311   glEnable(GL_TEXTURE_2D);
312
313   glPopMatrix();
314
315}
316
317static void
318dojoy(void)
319{
320#ifdef WIN32
321   static UINT max[2] = { 0, 0 };
322   static UINT min[2] = { 0xffffffff, 0xffffffff }, center[2];
323   MMRESULT res;
324   JOYINFO joy;
325
326   res = joyGetPos(JOYSTICKID1, &joy);
327
328   if (res == JOYERR_NOERROR) {
329      joyavailable = 1;
330
331      if (max[0] < joy.wXpos)
332	 max[0] = joy.wXpos;
333      if (min[0] > joy.wXpos)
334	 min[0] = joy.wXpos;
335      center[0] = (max[0] + min[0]) / 2;
336
337      if (max[1] < joy.wYpos)
338	 max[1] = joy.wYpos;
339      if (min[1] > joy.wYpos)
340	 min[1] = joy.wYpos;
341      center[1] = (max[1] + min[1]) / 2;
342
343      if (joyactive) {
344	 if (fabs(center[0] - (float) joy.wXpos) > 0.1 * (max[0] - min[0]))
345	    alpha +=
346	       2.5 * (center[0] - (float) joy.wXpos) / (max[0] - min[0]);
347	 if (fabs(center[1] - (float) joy.wYpos) > 0.1 * (max[1] - min[1]))
348	    beta += 2.5 * (center[1] - (float) joy.wYpos) / (max[1] - min[1]);
349
350	 if (joy.wButtons & JOY_BUTTON1)
351	    v += 0.5;
352	 if (joy.wButtons & JOY_BUTTON2)
353	    v -= 0.5;
354      }
355   }
356   else
357      joyavailable = 0;
358#endif
359}
360
361static void
362drawscene(void)
363{
364   static char frbuf[80] = "";
365
366   dojoy();
367
368   glShadeModel(GL_SMOOTH);
369   glEnable(GL_DEPTH_TEST);
370
371   if (usetex)
372      glEnable(GL_TEXTURE_2D);
373   else
374      glDisable(GL_TEXTURE_2D);
375
376   if (fog)
377      glEnable(GL_FOG);
378   else
379      glDisable(GL_FOG);
380
381   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
382
383   glPushMatrix();
384
385   calcposobs();
386   gluLookAt(obs[0], obs[1], obs[2],
387	     obs[0] + dir[0], obs[1] + dir[1], obs[2] + dir[2],
388	     0.0, 1.0, 0.0);
389
390   drawterrain();
391   glPopMatrix();
392
393   glDisable(GL_TEXTURE_2D);
394   glDisable(GL_DEPTH_TEST);
395   glDisable(GL_FOG);
396   glShadeModel(GL_FLAT);
397
398   glMatrixMode(GL_PROJECTION);
399   glLoadIdentity();
400   glOrtho(-0.5, 639.5, -0.5, 479.5, -1.0, 1.0);
401   glMatrixMode(GL_MODELVIEW);
402   glLoadIdentity();
403
404   glColor3f(1.0, 0.0, 0.0);
405   glRasterPos2i(10, 10);
406   printstring(GLUT_BITMAP_HELVETICA_18, frbuf);
407   glRasterPos2i(350, 470);
408   printstring(GLUT_BITMAP_HELVETICA_10,
409	       "Terrain V1.2 Written by David Bucciarelli (tech.hmw@plus.it)");
410   glRasterPos2i(434, 457);
411   printstring(GLUT_BITMAP_HELVETICA_10,
412	       "Based on a Mickael's demo (Skizo@Hol.Fr)");
413
414   if (help)
415      printhelp();
416
417   reshape(scrwidth, scrheight);
418
419   glutSwapBuffers();
420
421   Frames++;
422   {
423      GLint t = glutGet(GLUT_ELAPSED_TIME);
424      if (t - T0 >= 2000) {
425         GLfloat seconds = (t - T0) / 1000.0;
426         GLfloat fps = Frames / seconds;
427         sprintf(frbuf, "Frame rate: %f", fps);
428         printf("%s\n", frbuf);
429         fflush(stdout);
430         T0 = t;
431         Frames = 0;
432      }
433   }
434}
435
436static void
437key(unsigned char k, int x, int y)
438{
439   switch (k) {
440   case 27:
441      exit(0);
442      break;
443   case 'a':
444      v += 50.;
445      break;
446   case 'z':
447      v -= 50.;
448      break;
449   case 'p':
450      if (poutline) {
451	 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
452	 poutline = 0;
453      }
454      else {
455	 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
456	 poutline = 1;
457      }
458      break;
459   case 'j':
460      joyactive = (!joyactive);
461      break;
462   case 'h':
463      help = (!help);
464      break;
465   case 'f':
466      fog = (!fog);
467      break;
468   case 't':
469      usetex = (!usetex);
470      break;
471   case 'b':
472      if (bfcull) {
473	 glDisable(GL_CULL_FACE);
474	 bfcull = 0;
475      }
476      else {
477	 glEnable(GL_CULL_FACE);
478	 bfcull = 1;
479      }
480      break;
481#ifdef XMESA
482   case ' ':
483      XMesaSetFXmode(fullscreen ? XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW);
484      fullscreen = (!fullscreen);
485      break;
486#endif
487   }
488}
489
490static void
491special(int k, int x, int y)
492{
493   switch (k) {
494   case GLUT_KEY_LEFT:
495      alpha += 2.0;
496      break;
497   case GLUT_KEY_RIGHT:
498      alpha -= 2.0;
499      break;
500   case GLUT_KEY_DOWN:
501      beta -= 2.0;
502      break;
503   case GLUT_KEY_UP:
504      beta += 2.0;
505      break;
506   }
507}
508
509static void
510calccolor(GLfloat height, GLfloat c[3])
511{
512   GLfloat color[4][3] = {
513      {1.0, 1.0, 1.0},
514      {0.0, 0.8, 0.0},
515      {1.0, 1.0, 0.3},
516      {0.0, 0.0, 0.8}
517   };
518   GLfloat fact;
519
520   height = height * (1.0 / 255.0);
521
522   if (height >= 0.9) {
523      c[0] = color[0][0];
524      c[1] = color[0][1];
525      c[2] = color[0][2];
526      return;
527   }
528
529   if ((height < 0.9) && (height >= 0.7)) {
530      fact = (height - 0.7) * 5.0;
531      c[0] = fact * color[0][0] + (1.0 - fact) * color[1][0];
532      c[1] = fact * color[0][1] + (1.0 - fact) * color[1][1];
533      c[2] = fact * color[0][2] + (1.0 - fact) * color[1][2];
534      return;
535   }
536
537   if ((height < 0.7) && (height >= 0.6)) {
538      fact = (height - 0.6) * 10.0;
539      c[0] = fact * color[1][0] + (1.0 - fact) * color[2][0];
540      c[1] = fact * color[1][1] + (1.0 - fact) * color[2][1];
541      c[2] = fact * color[1][2] + (1.0 - fact) * color[2][2];
542      return;
543   }
544
545   if ((height < 0.6) && (height >= 0.5)) {
546      fact = (height - 0.5) * 10.0;
547      c[0] = fact * color[2][0] + (1.0 - fact) * color[3][0];
548      c[1] = fact * color[2][1] + (1.0 - fact) * color[3][1];
549      c[2] = fact * color[2][2] + (1.0 - fact) * color[3][2];
550      return;
551   }
552
553   c[0] = color[3][0];
554   c[1] = color[3][1];
555   c[2] = color[3][2];
556}
557
558static void
559loadpic(void)
560{
561   GLubyte bufferter[256 * 256], terrainpic[256 * 256];
562   FILE *FilePic;
563   int i, tmp;
564   GLenum gluerr;
565   size_t result;
566
567   if ((FilePic = fopen(DEMOS_DATA_DIR "terrain.dat", "r")) == NULL) {
568      fprintf(stderr, "Error loading terrain.dat\n");
569      exit(-1);
570   }
571   result = fread(bufferter, 256 * 256, 1, FilePic);
572   assert(result == 1);
573   fclose(FilePic);
574
575   for (i = 0; i < (256 * 256); i++) {
576      terrain[i] = (bufferter[i] * (heightMnt / 255.0f));
577      calccolor((GLfloat) bufferter[i], terraincolor[i]);
578      tmp = (((int) bufferter[i]) + 96);
579      terrainpic[i] = (tmp > 255) ? 255 : tmp;
580   }
581
582   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
583   if ((gluerr = gluBuild2DMipmaps(GL_TEXTURE_2D, 1, 256, 256, GL_LUMINANCE,
584				   GL_UNSIGNED_BYTE,
585				   (GLvoid *) (&terrainpic[0])))) {
586      fprintf(stderr, "GLULib%s\n", (char *) gluErrorString(gluerr));
587      exit(-1);
588   }
589
590   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
591   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
592
593   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
594		   GL_LINEAR_MIPMAP_LINEAR);
595   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
596
597   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
598   glEnable(GL_TEXTURE_2D);
599}
600
601static void
602init(void)
603{
604   float fogcolor[4] = { 0.6, 0.7, 0.7, 1.0 };
605
606   glClearColor(fogcolor[0], fogcolor[1], fogcolor[2], fogcolor[3]);
607   glClearDepth(1.0);
608   glDepthFunc(GL_LEQUAL);
609   glShadeModel(GL_SMOOTH);
610   glEnable(GL_DEPTH_TEST);
611   glEnable(GL_CULL_FACE);
612
613   glDisable(GL_BLEND);
614   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
615
616   glEnable(GL_FOG);
617   glFogi(GL_FOG_MODE, GL_EXP2);
618   glFogfv(GL_FOG_COLOR, fogcolor);
619   glFogf(GL_FOG_DENSITY, 0.0007);
620#ifdef FX
621   glHint(GL_FOG_HINT, GL_NICEST);
622#endif
623
624   reshape(scrwidth, scrheight);
625}
626
627
628int
629main(int ac, char **av)
630{
631   glutInitWindowSize(WIDTH, HEIGHT);
632   glutInit(&ac, av);
633
634   glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
635
636   if (!(win = glutCreateWindow("Terrain"))) {
637      fprintf(stderr, "Error, couldn't open window\n");
638      return -1;
639   }
640
641   ModZMnt = 0.0f;
642   loadpic();
643
644   init();
645
646#ifndef FX
647   glDisable(GL_TEXTURE_2D);
648   usetex = 0;
649#endif
650
651   glutReshapeFunc(reshape);
652   glutDisplayFunc(drawscene);
653   glutKeyboardFunc(key);
654   glutSpecialFunc(special);
655   glutIdleFunc(drawscene);
656
657   glutMainLoop();
658
659   return 0;
660}
661