blob: b4d0a67f9930d075327603d76a52eb895b4f9ec5 [file] [log] [blame]
jtgafb833d1999-08-19 00:55:39 +00001
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
jtgafb833d1999-08-19 00:55:39 +000014#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <math.h> /* for cos(), sin(), and sqrt() */
Karl Schultz164ce122002-01-16 00:48:43 +000018#ifdef _WIN32
19#include <windows.h>
20#endif
José Fonseca2e61d132009-01-24 16:39:49 +000021#include <GL/glew.h>
jtgafb833d1999-08-19 00:55:39 +000022#include <GL/glut.h>
23
24/* Some <math.h> files do not define M_PI... */
25#ifndef M_PI
26#define M_PI 3.14159265
27#endif
28
29#if 0 /* For debugging. */
30#undef GL_EXT_point_parameters
31#endif
32
33static GLfloat angle = -150; /* in degrees */
34static int spin = 0;
35static int moving, begin;
36static int newModel = 1;
37static float theTime;
38static int repeat = 1;
39static int blend = 1;
40int useMipmaps = 1;
41int linearFiltering = 1;
42
43static GLfloat constant[3] = { 1/5.0, 0.0, 0.0 };
44static GLfloat linear[3] = { 0.0, 1/5.0, 0.0 };
45static GLfloat theQuad[3] = { 0.25, 0.0, 1/60.0 };
46
47#define MAX_POINTS 2000
48
49static int numPoints = 200;
50
51static GLfloat pointList[MAX_POINTS][3];
52static GLfloat pointTime[MAX_POINTS];
53static GLfloat pointVelocity[MAX_POINTS][2];
54static GLfloat pointDirection[MAX_POINTS][2];
55static int colorList[MAX_POINTS];
56static int animate = 1, motion = 0;
57
58static GLfloat colorSet[][4] = {
59 /* Shades of red. */
60 { 0.7, 0.2, 0.4, 0.5 },
61 { 0.8, 0.0, 0.7, 0.5 },
62 { 1.0, 0.0, 0.0, 0.5 },
63 { 0.9, 0.3, 0.6, 0.5 },
64 { 1.0, 0.4, 0.0, 0.5 },
65 { 1.0, 0.0, 0.5, 0.5 },
66};
67
68#define NUM_COLORS (sizeof(colorSet)/sizeof(colorSet[0]))
69
70#define DEAD (NUM_COLORS+1)
71
72
73#if 0 /* drand48 might be better on Unix machines */
74#define RANDOM_RANGE(lo, hi) ((lo) + (hi - lo) * drand48())
75#else
76static float float_rand(void) { return rand() / (float) RAND_MAX; }
77#define RANDOM_RANGE(lo, hi) ((lo) + (hi - lo) * float_rand())
78#endif
79
80#define MEAN_VELOCITY 3.0
81#define GRAVITY 2.0
jtgafb833d1999-08-19 00:55:39 +000082
83/* Modeling units of ground extent in each X and Z direction. */
84#define EDGE 12
85
Brian Paul02e8a032000-06-27 17:04:43 +000086static void
jtgafb833d1999-08-19 00:55:39 +000087makePointList(void)
88{
89 float angle, velocity, direction;
90 int i;
91
92 motion = 1;
93 for (i=0; i<numPoints; i++) {
94 pointList[i][0] = 0.0;
95 pointList[i][1] = 0.0;
96 pointList[i][2] = 0.0;
97 pointTime[i] = 0.0;
98 angle = (RANDOM_RANGE(60.0, 70.0)) * M_PI/180.0;
99 direction = RANDOM_RANGE(0.0, 360.0) * M_PI/180.0;
100 pointDirection[i][0] = cos(direction);
101 pointDirection[i][1] = sin(direction);
102 velocity = MEAN_VELOCITY + RANDOM_RANGE(-0.8, 1.0);
103 pointVelocity[i][0] = velocity * cos(angle);
104 pointVelocity[i][1] = velocity * sin(angle);
105 colorList[i] = rand() % NUM_COLORS;
106 }
107 theTime = 0.0;
108}
109
Brian Paul02e8a032000-06-27 17:04:43 +0000110static void
jtgafb833d1999-08-19 00:55:39 +0000111updatePointList(void)
112{
113 float distance;
114 int i;
115
Brian Paul92eddb02005-01-09 17:37:50 +0000116 static double t0 = -1.;
117 double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
118 if (t0 < 0.0)
119 t0 = t;
120 dt = t - t0;
121 t0 = t;
122
jtgafb833d1999-08-19 00:55:39 +0000123 motion = 0;
124 for (i=0; i<numPoints; i++) {
125 distance = pointVelocity[i][0] * theTime;
126
127 /* X and Z */
128 pointList[i][0] = pointDirection[i][0] * distance;
129 pointList[i][2] = pointDirection[i][1] * distance;
130
131 /* Z */
132 pointList[i][1] =
133 (pointVelocity[i][1] - 0.5 * GRAVITY * pointTime[i])*pointTime[i];
134
135 /* If we hit the ground, bounce the point upward again. */
136 if (pointList[i][1] <= 0.0) {
137 if (distance > EDGE) {
138 /* Particle has hit ground past the distance duration of
139 the particles. Mark particle as dead. */
140 colorList[i] = NUM_COLORS; /* Not moving. */
141 continue;
142 }
143
144 pointVelocity[i][1] *= 0.8; /* 80% of previous up velocity. */
145 pointTime[i] = 0.0; /* Reset the particles sense of up time. */
146 }
147 motion = 1;
Brian Paul92eddb02005-01-09 17:37:50 +0000148 pointTime[i] += dt;
jtgafb833d1999-08-19 00:55:39 +0000149 }
Brian Paul92eddb02005-01-09 17:37:50 +0000150 theTime += dt;
jtgafb833d1999-08-19 00:55:39 +0000151 if (!motion && !spin) {
152 if (repeat) {
153 makePointList();
154 } else {
155 glutIdleFunc(NULL);
156 }
157 }
158}
159
Brian Paul02e8a032000-06-27 17:04:43 +0000160static void
jtgafb833d1999-08-19 00:55:39 +0000161idle(void)
162{
163 updatePointList();
164 if (spin) {
165 angle += 0.3;
166 newModel = 1;
167 }
168 glutPostRedisplay();
169}
170
Brian Paul02e8a032000-06-27 17:04:43 +0000171static void
jtgafb833d1999-08-19 00:55:39 +0000172visible(int vis)
173{
174 if (vis == GLUT_VISIBLE) {
175 if (animate && (motion || spin)) {
176 glutIdleFunc(idle);
177 }
178 } else {
179 glutIdleFunc(NULL);
180 }
181}
182
Brian Paul02e8a032000-06-27 17:04:43 +0000183static void
jtgafb833d1999-08-19 00:55:39 +0000184recalcModelView(void)
185{
186 glPopMatrix();
187 glPushMatrix();
188 glRotatef(angle, 0.0, 1.0, 0.0);
189 newModel = 0;
190}
191
Brian Paul02e8a032000-06-27 17:04:43 +0000192static void
jtgafb833d1999-08-19 00:55:39 +0000193redraw(void)
194{
195 int i;
196
Ian Romanickf3591652009-06-30 17:29:14 -0700197 glDepthMask(GL_TRUE);
jtgafb833d1999-08-19 00:55:39 +0000198 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
199 if (newModel)
200 recalcModelView();
201
jtgafb833d1999-08-19 00:55:39 +0000202
203 /* Draw the floor. */
204/* glEnable(GL_TEXTURE_2D);*/
205 glColor3f(0.5, 1.0, 0.5);
206 glBegin(GL_QUADS);
207 glTexCoord2f(0.0, 0.0);
208 glVertex3f(-EDGE, -0.05, -EDGE);
209 glTexCoord2f(20.0, 0.0);
210 glVertex3f(EDGE, -0.05, -EDGE);
211 glTexCoord2f(20.0, 20.0);
212 glVertex3f(EDGE, -0.05, EDGE);
213 glTexCoord2f(0.0, 20.0);
214 glVertex3f(-EDGE, -0.05, EDGE);
215 glEnd();
216
217 /* Allow particles to blend with each other. */
Ian Romanickf3591652009-06-30 17:29:14 -0700218 glDepthMask(GL_FALSE);
jtgafb833d1999-08-19 00:55:39 +0000219
220 if (blend)
221 glEnable(GL_BLEND);
222
223 glDisable(GL_TEXTURE_2D);
224 glBegin(GL_POINTS);
225 for (i=0; i<numPoints; i++) {
226 /* Draw alive particles. */
227 if (colorList[i] != DEAD) {
228 glColor4fv(colorSet[colorList[i]]);
229 glVertex3fv(pointList[i]);
230 }
231 }
232 glEnd();
233
234 glDisable(GL_BLEND);
235
236 glutSwapBuffers();
237}
238
239/* ARGSUSED2 */
Brian Paul02e8a032000-06-27 17:04:43 +0000240static void
jtgafb833d1999-08-19 00:55:39 +0000241mouse(int button, int state, int x, int y)
242{
243 /* Scene can be spun around Y axis using left
244 mouse button movement. */
245 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
246 moving = 1;
247 begin = x;
248 }
249 if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
250 moving = 0;
251 }
252}
253
254/* ARGSUSED1 */
Brian Paul02e8a032000-06-27 17:04:43 +0000255static void
jtgafb833d1999-08-19 00:55:39 +0000256mouseMotion(int x, int y)
257{
258 if (moving) {
259 angle = angle + (x - begin);
260 begin = x;
261 newModel = 1;
262 glutPostRedisplay();
263 }
264}
265
Brian Paul02e8a032000-06-27 17:04:43 +0000266static void
jtgafb833d1999-08-19 00:55:39 +0000267menu(int option)
268{
269 switch (option) {
270 case 0:
271 makePointList();
272 break;
Brian Paul92eddb02005-01-09 17:37:50 +0000273#ifdef GL_ARB_point_parameters
jtgafb833d1999-08-19 00:55:39 +0000274 case 1:
Brian Paul66fa33e2002-12-03 03:13:17 +0000275 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, constant);
jtgafb833d1999-08-19 00:55:39 +0000276 break;
277 case 2:
Brian Paul66fa33e2002-12-03 03:13:17 +0000278 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, linear);
jtgafb833d1999-08-19 00:55:39 +0000279 break;
280 case 3:
Brian Paul66fa33e2002-12-03 03:13:17 +0000281 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad);
jtgafb833d1999-08-19 00:55:39 +0000282 break;
283#endif
284 case 4:
285 blend = 1;
286 break;
287 case 5:
288 blend = 0;
289 break;
Brian Paul92eddb02005-01-09 17:37:50 +0000290#ifdef GL_ARB_point_parameters
jtgafb833d1999-08-19 00:55:39 +0000291 case 6:
Brian Paul66fa33e2002-12-03 03:13:17 +0000292 glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 1.0);
jtgafb833d1999-08-19 00:55:39 +0000293 break;
294 case 7:
Brian Paul66fa33e2002-12-03 03:13:17 +0000295 glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 10.0);
jtgafb833d1999-08-19 00:55:39 +0000296 break;
297#endif
298 case 8:
299 glEnable(GL_POINT_SMOOTH);
300 break;
301 case 9:
302 glDisable(GL_POINT_SMOOTH);
303 break;
304 case 10:
305 glPointSize(2.0);
306 break;
307 case 11:
308 glPointSize(4.0);
309 break;
310 case 12:
311 glPointSize(8.0);
312 break;
313 case 13:
314 spin = 1 - spin;
315 if (animate && (spin || motion)) {
316 glutIdleFunc(idle);
317 } else {
318 glutIdleFunc(NULL);
319 }
320 break;
321 case 14:
322 numPoints = 200;
323 break;
324 case 15:
325 numPoints = 500;
326 break;
327 case 16:
328 numPoints = 1000;
329 break;
330 case 17:
331 numPoints = 2000;
332 break;
333 case 666:
334 exit(0);
335 }
336 glutPostRedisplay();
337}
338
339/* ARGSUSED1 */
Brian Paul02e8a032000-06-27 17:04:43 +0000340static void
jtgafb833d1999-08-19 00:55:39 +0000341key(unsigned char c, int x, int y)
342{
343 switch (c) {
344 case 13:
345 animate = 1 - animate; /* toggle. */
346 if (animate && (motion || spin)) {
347 glutIdleFunc(idle);
348 } else {
349 glutIdleFunc(NULL);
350 }
351 break;
352 case ' ':
353 animate = 1;
354 makePointList();
355 glutIdleFunc(idle);
356 break;
357 case 27:
358 exit(0);
359 }
360}
361
362/* Nice floor texture tiling pattern. */
363static char *circles[] = {
364 "....xxxx........",
365 "..xxxxxxxx......",
366 ".xxxxxxxxxx.....",
367 ".xxx....xxx.....",
368 "xxx......xxx....",
369 "xxx......xxx....",
370 "xxx......xxx....",
371 "xxx......xxx....",
372 ".xxx....xxx.....",
373 ".xxxxxxxxxx.....",
374 "..xxxxxxxx......",
375 "....xxxx........",
376 "................",
377 "................",
378 "................",
379 "................",
380};
381
382static void
383makeFloorTexture(void)
384{
385 GLubyte floorTexture[16][16][3];
386 GLubyte *loc;
387 int s, t;
388
389 /* Setup RGB image for the texture. */
390 loc = (GLubyte*) floorTexture;
391 for (t = 0; t < 16; t++) {
392 for (s = 0; s < 16; s++) {
393 if (circles[t][s] == 'x') {
394 /* Nice blue. */
395 loc[0] = 0x1f;
396 loc[1] = 0x1f;
397 loc[2] = 0x8f;
398 } else {
399 /* Light gray. */
400 loc[0] = 0xca;
401 loc[1] = 0xca;
402 loc[2] = 0xca;
403 }
404 loc += 3;
405 }
406 }
407
408 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
409
410 if (useMipmaps) {
411 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
412 GL_LINEAR_MIPMAP_LINEAR);
413 gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 16, 16,
414 GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
415 } else {
416 if (linearFiltering) {
417 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
418 } else {
419 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
420 }
421 glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
422 GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
423 }
424}
425
426int
427main(int argc, char **argv)
428{
429 int i;
Brian Paul263f4322009-12-18 08:12:55 -0700430
431 glutInitWindowSize(300, 300);
jtgafb833d1999-08-19 00:55:39 +0000432 glutInit(&argc, argv);
433 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
434
435 for (i=1; i<argc; i++) {
436 if(!strcmp("-noms", argv[i])) {
437 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
438 printf("forcing no multisampling\n");
439 } else if(!strcmp("-nomipmaps", argv[i])) {
440 useMipmaps = 0;
441 } else if(!strcmp("-nearest", argv[i])) {
442 linearFiltering = 0;
443 }
444 }
445
446 glutCreateWindow("point burst");
José Fonseca2e61d132009-01-24 16:39:49 +0000447 glewInit();
jtgafb833d1999-08-19 00:55:39 +0000448 glutDisplayFunc(redraw);
449 glutMouseFunc(mouse);
450 glutMotionFunc(mouseMotion);
451 glutVisibilityFunc(visible);
452 glutKeyboardFunc(key);
453 glutCreateMenu(menu);
454 glutAddMenuEntry("Reset time", 0);
455 glutAddMenuEntry("Constant", 1);
456 glutAddMenuEntry("Linear", 2);
457 glutAddMenuEntry("Quadratic", 3);
458 glutAddMenuEntry("Blend on", 4);
459 glutAddMenuEntry("Blend off", 5);
460 glutAddMenuEntry("Threshold 1", 6);
461 glutAddMenuEntry("Threshold 10", 7);
462 glutAddMenuEntry("Point smooth on", 8);
463 glutAddMenuEntry("Point smooth off", 9);
464 glutAddMenuEntry("Point size 2", 10);
465 glutAddMenuEntry("Point size 4", 11);
466 glutAddMenuEntry("Point size 8", 12);
467 glutAddMenuEntry("Toggle spin", 13);
468 glutAddMenuEntry("200 points ", 14);
469 glutAddMenuEntry("500 points ", 15);
470 glutAddMenuEntry("1000 points ", 16);
471 glutAddMenuEntry("2000 points ", 17);
472 glutAddMenuEntry("Quit", 666);
473 glutAttachMenu(GLUT_RIGHT_BUTTON);
474
Brian Paula394f542006-08-28 14:20:19 +0000475 if (!glutExtensionSupported("GL_ARB_point_parameters")) {
476 fprintf(stderr, "Sorry, GL_ARB_point_parameters is not supported.\n");
477 return -1;
478 }
479
jtgafb833d1999-08-19 00:55:39 +0000480 glShadeModel(GL_FLAT);
481 glEnable(GL_DEPTH_TEST);
482 glEnable(GL_POINT_SMOOTH);
483 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
484 glPointSize(8.0);
Brian Paul66fa33e2002-12-03 03:13:17 +0000485#if GL_ARB_point_parameters
486 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad);
jtgafb833d1999-08-19 00:55:39 +0000487#endif
488 glMatrixMode(GL_PROJECTION);
489 gluPerspective( /* field of view in degree */ 40.0,
490 /* aspect ratio */ 1.0,
491 /* Z near */ 0.5, /* Z far */ 40.0);
492 glMatrixMode(GL_MODELVIEW);
493 gluLookAt(0.0, 1.0, 8.0, /* eye location */
494 0.0, 1.0, 0.0, /* center is at (0,0,0) */
495 0.0, 1.0, 0.); /* up is in postivie Y direction */
496 glPushMatrix(); /* dummy push so we can pop on model
497 recalc */
498
499 makePointList();
500 makeFloorTexture();
501
502 glutMainLoop();
503 return 0; /* ANSI C requires main to return int. */
504}