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