1 2/* Copyright (c) Mark J. Kilgard, 1997. */ 3 4/* This program is freely distributable without licensing fees 5 and is provided without guarantee or warrantee expressed or 6 implied. This program is -not- in the public domain. */ 7 8/* This example demonstrates how to render particle effects 9 with OpenGL. A cloud of pinkish/orange particles explodes with the 10 particles bouncing off the ground. When the EXT_point_parameters 11 is present , the particle size is attenuated based on eye distance. */ 12 13 14/* Modified by Brian Paul to test GL_ARB_point_sprite */ 15 16 17#include <assert.h> 18#include <stdio.h> 19#include <stdlib.h> 20#include <string.h> 21#include <math.h> /* for cos(), sin(), and sqrt() */ 22#ifdef _WIN32 23#include <windows.h> 24#endif 25#include <GL/glew.h> 26#include "glut_wrap.h" 27 28/* Some <math.h> files do not define M_PI... */ 29#ifndef M_PI 30#define M_PI 3.14159265 31#endif 32 33#if 0 /* For debugging. */ 34#undef GL_EXT_point_parameters 35#endif 36 37static GLfloat angle = -150; /* in degrees */ 38static int spin = 0; 39static int moving, begin; 40static float theTime; 41static int repeat = 1; 42static int blend = 1; 43static int useMipmaps = 1; 44static int linearFiltering = 1; 45 46static GLuint Tex0, Tex1; 47 48static GLfloat constant[3] = { .2, 0.0, 0.0 }; 49static GLfloat linear[3] = { .0, .1, 0.0 }; 50static GLfloat theQuad[3] = { .005, 0.05, 1/600.0 }; 51 52#define MAX_POINTS 2000 53 54static int numPoints = 200; 55 56static GLfloat pointList[MAX_POINTS][3]; 57static GLfloat pointTime[MAX_POINTS]; 58static GLfloat pointVelocity[MAX_POINTS][2]; 59static GLfloat pointDirection[MAX_POINTS][2]; 60static int colorList[MAX_POINTS]; 61static int animate = 1, motion = 0, org = 0, sprite = 1, smooth = 1; 62 63static GLfloat colorSet[][4] = { 64 /* Shades of red. */ 65 { 0.7, 0.2, 0.4, 0.5 }, 66 { 0.8, 0.0, 0.7, 0.5 }, 67 { 1.0, 0.0, 0.0, 0.5 }, 68 { 0.9, 0.3, 0.6, 0.5 }, 69 { 1.0, 0.4, 0.0, 0.5 }, 70 { 1.0, 0.0, 0.5, 0.5 }, 71}; 72 73#define NUM_COLORS (sizeof(colorSet)/sizeof(colorSet[0])) 74 75#define DEAD (NUM_COLORS+1) 76 77 78/* GL */ 79static GLint spritePattern[16][16] = { 80 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 81 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 82 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 83 { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, 84 { 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, 85 { 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0 }, 86 { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, 87 { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, 88 { 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0 }, 89 { 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0 }, 90 { 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0 }, 91 { 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0 }, 92 { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0 }, 93 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 94 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 95 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } 96}; 97 98 99/** Fragment shader which inverts the texture's color */ 100static const char *FragShaderText = 101 "uniform sampler2D tex; \n" 102 "void main(void) \n" 103 "{ \n" 104 " gl_FragColor = vec4(1.0) - texture2D(tex, gl_TexCoord[0].xy); \n" 105 "} \n"; 106 107static GLuint FragShader, ShaderProg; 108static GLboolean UseFragShader = GL_FALSE; 109static GLboolean HaveShaders = GL_FALSE; 110 111static void 112makeFragShader(void) 113{ 114 GLint stat; 115 116 HaveShaders = GLEW_VERSION_2_0; 117 if (!HaveShaders) 118 return; 119 120 FragShader = glCreateShader(GL_FRAGMENT_SHADER); 121 glShaderSource(FragShader, 1, (const GLchar **) &FragShaderText, NULL); 122 glCompileShader(FragShader); 123 124 ShaderProg = glCreateProgram(); 125 glAttachShader(ShaderProg, FragShader); 126 glLinkProgram(ShaderProg); 127 128 glGetProgramiv(ShaderProg, GL_LINK_STATUS, &stat); 129 assert(stat); 130} 131 132 133 134 135#if 0 /* drand48 might be better on Unix machines */ 136#define RANDOM_RANGE(lo, hi) ((lo) + (hi - lo) * drand48()) 137#else 138static float float_rand(void) { return rand() / (float) RAND_MAX; } 139#define RANDOM_RANGE(lo, hi) ((lo) + (hi - lo) * float_rand()) 140#endif 141 142#define MEAN_VELOCITY 3.0 143#define GRAVITY 2.0 144 145/* Modeling units of ground extent in each X and Z direction. */ 146#define EDGE 12 147 148static void 149makePointList(void) 150{ 151 float angle, velocity, direction; 152 int i; 153 154 motion = 1; 155 for (i=0; i<numPoints; i++) { 156 pointList[i][0] = 0.0; 157 pointList[i][1] = 0.0; 158 pointList[i][2] = 0.0; 159 pointTime[i] = 0.0; 160 angle = (RANDOM_RANGE(60.0, 70.0)) * M_PI/180.0; 161 direction = RANDOM_RANGE(0.0, 360.0) * M_PI/180.0; 162 pointDirection[i][0] = cos(direction); 163 pointDirection[i][1] = sin(direction); 164 velocity = MEAN_VELOCITY + RANDOM_RANGE(-0.8, 1.0); 165 pointVelocity[i][0] = velocity * cos(angle); 166 pointVelocity[i][1] = velocity * sin(angle); 167 colorList[i] = rand() % NUM_COLORS; 168 } 169 theTime = 0.0; 170} 171 172static void 173updatePointList(void) 174{ 175 float distance; 176 int i; 177 178 static double t0 = -1.; 179 double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0; 180 if (t0 < 0.0) 181 t0 = t; 182 dt = t - t0; 183 t0 = t; 184 185 motion = 0; 186 for (i=0; i<numPoints; i++) { 187 distance = pointVelocity[i][0] * theTime; 188 189 /* X and Z */ 190 pointList[i][0] = pointDirection[i][0] * distance; 191 pointList[i][2] = pointDirection[i][1] * distance; 192 193 /* Z */ 194 pointList[i][1] = 195 (pointVelocity[i][1] - 0.5 * GRAVITY * pointTime[i])*pointTime[i]; 196 197 /* If we hit the ground, bounce the point upward again. */ 198 if (pointList[i][1] <= 0.0) { 199 if (distance > EDGE) { 200 /* Particle has hit ground past the distance duration of 201 the particles. Mark particle as dead. */ 202 colorList[i] = NUM_COLORS; /* Not moving. */ 203 continue; 204 } 205 206 pointVelocity[i][1] *= 0.8; /* 80% of previous up velocity. */ 207 pointTime[i] = 0.0; /* Reset the particles sense of up time. */ 208 } 209 motion = 1; 210 pointTime[i] += dt; 211 } 212 theTime += dt; 213 if (!motion && !spin) { 214 if (repeat) { 215 makePointList(); 216 } else { 217 glutIdleFunc(NULL); 218 } 219 } 220} 221 222static void 223idle(void) 224{ 225 updatePointList(); 226 if (spin) { 227 angle += 0.3; 228 } 229 glutPostRedisplay(); 230} 231 232static void 233visible(int vis) 234{ 235 if (vis == GLUT_VISIBLE) { 236 if (animate && (motion || spin)) { 237 glutIdleFunc(idle); 238 } 239 } else { 240 glutIdleFunc(NULL); 241 } 242} 243 244static void 245redraw(void) 246{ 247 int i; 248 249 glDepthMask(GL_TRUE); 250 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 251 252 if (HaveShaders) 253 glUseProgram(0); 254 255 glPushMatrix(); 256 glRotatef(15.0, 1.0, 0.0, 0.0); 257 glRotatef(angle, 0.0, 1.0, 0.0); 258 259 260 /* Draw the floor. */ 261 glColor3f(0.1, 0.5, 1.0); 262 glBegin(GL_QUADS); 263 glTexCoord2f(0.0, 0.0); 264 glVertex3f(-EDGE, -0.05, -EDGE); 265 glTexCoord2f(20.0, 0.0); 266 glVertex3f(EDGE, -0.05, -EDGE); 267 glTexCoord2f(20.0, 20.0); 268 glVertex3f(EDGE, -0.05, EDGE); 269 glTexCoord2f(0.0, 20.0); 270 glVertex3f(-EDGE, -0.05, EDGE); 271 glEnd(); 272 273 /* Allow particles to blend with each other. */ 274 glDepthMask(GL_FALSE); 275 276 if (blend) 277 glEnable(GL_BLEND); 278 279 if (sprite) { 280 glActiveTexture(GL_TEXTURE0); 281 glEnable(GL_TEXTURE_2D); 282 if (0) { 283 /* debug/test code */ 284 glActiveTexture(GL_TEXTURE1); 285 glEnable(GL_TEXTURE_2D); 286 } 287 288#ifdef GL_ARB_point_sprite 289 glEnable(GL_POINT_SPRITE_ARB); 290#endif 291 if (UseFragShader) { 292 glUseProgram(ShaderProg); 293 } 294 } 295 296 glColor3f(1,1,1); 297 glBegin(GL_POINTS); 298 for (i=0; i<numPoints; i++) { 299 /* Draw alive particles. */ 300 if (colorList[i] != DEAD) { 301 if (!sprite) 302 glColor4fv(colorSet[colorList[i]]); 303 glVertex3fv(pointList[i]); 304 } 305 } 306 glEnd(); 307 308 glActiveTexture(GL_TEXTURE0); 309 glDisable(GL_TEXTURE_2D); 310 glActiveTexture(GL_TEXTURE1); 311 glDisable(GL_TEXTURE_2D); 312 313#ifdef GL_ARB_point_sprite 314 glDisable(GL_POINT_SPRITE_ARB); 315#endif 316 glDisable(GL_BLEND); 317 318 glPopMatrix(); 319 320 glutSwapBuffers(); 321} 322 323/* ARGSUSED2 */ 324static void 325mouse(int button, int state, int x, int y) 326{ 327 /* Scene can be spun around Y axis using left 328 mouse button movement. */ 329 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { 330 moving = 1; 331 begin = x; 332 } 333 if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { 334 moving = 0; 335 } 336} 337 338/* ARGSUSED1 */ 339static void 340mouseMotion(int x, int y) 341{ 342 if (moving) { 343 angle = angle + (x - begin); 344 begin = x; 345 glutPostRedisplay(); 346 } 347} 348 349static void 350menu(int option) 351{ 352 switch (option) { 353 case 0: 354 makePointList(); 355 break; 356#ifdef GL_ARB_point_parameters 357 case 1: 358 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, constant); 359 break; 360 case 2: 361 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, linear); 362 break; 363 case 3: 364 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad); 365 break; 366#endif 367 case 4: 368 blend = 1; 369 break; 370 case 5: 371 blend = 0; 372 break; 373#ifdef GL_ARB_point_parameters 374 case 6: 375 glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 1.0); 376 break; 377 case 7: 378 glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 10.0); 379 break; 380#endif 381 case 8: 382 glEnable(GL_POINT_SMOOTH); 383 smooth = 1; 384 break; 385 case 9: 386 glDisable(GL_POINT_SMOOTH); 387 smooth = 0; 388 break; 389 case 10: 390 glPointSize(16.0); 391 break; 392 case 11: 393 glPointSize(32.0); 394 break; 395 case 12: 396 glPointSize(64.0); 397 break; 398 case 13: 399 spin = 1 - spin; 400 if (animate && (spin || motion)) { 401 glutIdleFunc(idle); 402 } else { 403 glutIdleFunc(NULL); 404 } 405 break; 406 case 14: 407 numPoints = 200; 408 break; 409 case 15: 410 numPoints = 500; 411 break; 412 case 16: 413 numPoints = 1000; 414 break; 415 case 17: 416 numPoints = 2000; 417 break; 418 case 666: 419 exit(0); 420 } 421 glutPostRedisplay(); 422} 423 424/* ARGSUSED1 */ 425static void 426key(unsigned char c, int x, int y) 427{ 428 switch (c) { 429 case 13: 430 animate = 1 - animate; /* toggle. */ 431 if (animate && (motion || spin)) { 432 glutIdleFunc(idle); 433 } else { 434 glutIdleFunc(NULL); 435 } 436 break; 437 case ' ': 438 animate = 1; 439 makePointList(); 440 glutIdleFunc(idle); 441 break; 442 case 'o': 443 case 'O': 444 org ^= 1; 445#ifdef GL_VERSION_2_0 446#ifdef GL_ARB_point_parameters 447 glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, 448 org ? GL_LOWER_LEFT : GL_UPPER_LEFT); 449#endif 450#endif 451 glutPostRedisplay(); 452 break; 453 case 't': 454 case 'T': 455 sprite ^= 1; 456 glutPostRedisplay(); 457 break; 458 case 's': 459 case 'S': 460 (smooth ^= 1) ? glEnable(GL_POINT_SMOOTH) : glDisable(GL_POINT_SMOOTH); 461 glutPostRedisplay(); 462 break; 463 case 'f': 464 case 'F': 465 if (HaveShaders) { 466 UseFragShader = !UseFragShader; 467 glutPostRedisplay(); 468 } 469 break; 470 case '0': 471 glPointSize(1.0); 472 glutPostRedisplay(); 473 break; 474 case '1': 475 glPointSize(16.0); 476 glutPostRedisplay(); 477 break; 478 case '2': 479 glPointSize(32.0); 480 glutPostRedisplay(); 481 break; 482 case '3': 483 glPointSize(64.0); 484 glutPostRedisplay(); 485 break; 486 case '4': 487 glPointSize(128.0); 488 glutPostRedisplay(); 489 break; 490 case 27: 491 exit(0); 492 } 493} 494 495 496 497static void 498makeSpriteTextures(void) 499{ 500 GLubyte texture[16][16][4]; 501 int i, j; 502 503 if (!glutExtensionSupported("GL_ARB_point_sprite")) { 504 printf("Sorry, this demo requires GL_ARB_point_sprite.\n"); 505 exit(0); 506 } 507 if (!glutExtensionSupported("GL_ARB_point_parameters")) { 508 printf("Sorry, this demo requires GL_ARB_point_parameters.\n"); 509 exit(0); 510 } 511 512 /* "GL" */ 513 for (i = 0; i < 16; i++) { 514 for (j = 0; j < 16; j++) { 515 if (spritePattern[i][j]) { 516 texture[i][j][0] = 255; 517 texture[i][j][1] = 255; 518 texture[i][j][2] = 255; 519 texture[i][j][3] = 255; 520 } 521 else { 522 texture[i][j][0] = 255; 523 texture[i][j][1] = 0; 524 texture[i][j][2] = 0; 525 texture[i][j][3] = 0; 526 } 527 } 528 } 529 530 /* texture 0 */ 531 glActiveTexture(GL_TEXTURE0); 532 glGenTextures(1, &Tex0); 533 glBindTexture(GL_TEXTURE_2D, Tex0); 534 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, 535 texture); 536 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 537 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 538 539 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 540#ifdef GL_ARB_point_sprite 541 glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE); 542#endif 543 544 /* left=yellow, right=green */ 545 memset(texture, 0, sizeof(texture)); 546 for (i = 0; i < 16; i++) { 547 for (j = 0; j < 16; j++) { 548 if (j < 8) { 549 texture[i][j][0] = 255; 550 texture[i][j][1] = 255; 551 texture[i][j][2] = 0; 552 texture[i][j][3] = 255; 553 } 554 else { 555 texture[i][j][0] = 0; 556 texture[i][j][1] = 255; 557 texture[i][j][2] = 0; 558 texture[i][j][3] = 255; 559 } 560 } 561 } 562 563 /* texture 1 */ 564 glActiveTexture(GL_TEXTURE1); 565 glGenTextures(1, &Tex1); 566 glBindTexture(GL_TEXTURE_2D, Tex1); 567 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, 568 texture); 569 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 570 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 571 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 572#ifdef GL_ARB_point_sprite 573 glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE); 574#endif 575 576 glActiveTexture(GL_TEXTURE0); 577} 578 579 580static void 581reshape(int width, int height) 582{ 583 GLfloat h = (GLfloat) height / (GLfloat) width; 584 585 glViewport(0, 0, (GLint) width, (GLint) height); 586 587#if 0 /* debug/test code */ 588 glMatrixMode(GL_TEXTURE); 589 glLoadIdentity(); 590 glRotatef(45, 0, 0, 1); 591#endif 592 593 glMatrixMode(GL_PROJECTION); 594 glLoadIdentity(); 595 glFrustum(-1.0, 1.0, -h, h, 2.0, 30.0); 596 glMatrixMode(GL_MODELVIEW); 597 glLoadIdentity(); 598 glTranslatef(0.0, 0.0, -10.0); 599} 600 601int 602main(int argc, char **argv) 603{ 604 int i; 605 606 glutInitWindowSize(600,300); 607 glutInit(&argc, argv); 608 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE); 609 610 for (i=1; i<argc; i++) { 611 if(!strcmp("-noms", argv[i])) { 612 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); 613 printf("forcing no multisampling\n"); 614 } else if(!strcmp("-nomipmaps", argv[i])) { 615 useMipmaps = 0; 616 } else if(!strcmp("-nearest", argv[i])) { 617 linearFiltering = 0; 618 } 619 } 620 glutCreateWindow("sprite blast"); 621 glewInit(); 622 glutReshapeFunc(reshape); 623 glutDisplayFunc(redraw); 624 glutMouseFunc(mouse); 625 glutMotionFunc(mouseMotion); 626 glutVisibilityFunc(visible); 627 glutKeyboardFunc(key); 628 glutCreateMenu(menu); 629 glutAddMenuEntry("Reset time", 0); 630 glutAddMenuEntry("Constant", 1); 631 glutAddMenuEntry("Linear", 2); 632 glutAddMenuEntry("Quadratic", 3); 633 glutAddMenuEntry("Blend on", 4); 634 glutAddMenuEntry("Blend off", 5); 635 glutAddMenuEntry("Threshold 1", 6); 636 glutAddMenuEntry("Threshold 10", 7); 637 glutAddMenuEntry("Point smooth on", 8); 638 glutAddMenuEntry("Point smooth off", 9); 639 glutAddMenuEntry("Point size 16", 10); 640 glutAddMenuEntry("Point size 32", 11); 641 glutAddMenuEntry("Point size 64", 12); 642 glutAddMenuEntry("Toggle spin", 13); 643 glutAddMenuEntry("200 points ", 14); 644 glutAddMenuEntry("500 points ", 15); 645 glutAddMenuEntry("1000 points ", 16); 646 glutAddMenuEntry("2000 points ", 17); 647 glutAddMenuEntry("Quit", 666); 648 glutAttachMenu(GLUT_RIGHT_BUTTON); 649 650 makePointList(); 651 makeSpriteTextures(); 652 makeFragShader(); 653 654 glShadeModel(GL_FLAT); 655 glEnable(GL_DEPTH_TEST); 656 glEnable(GL_POINT_SMOOTH); 657 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 658 glPointSize(32.0); 659#ifdef GL_ARB_point_parameters 660 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad); 661#endif 662 663 glutMainLoop(); 664 return 0; /* ANSI C requires main to return int. */ 665} 666