tweaked the animation rate
[mesa.git] / progs / demos / pointblast.c
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 /*
15 * $Log: pointblast.c,v $
16 * Revision 1.1 1999/08/19 00:55:40 jtg
17 * Initial revision
18 *
19 * Revision 3.3 1998/07/26 01:24:27 brianp
20 * removed include of gl.h
21 *
22 * Revision 3.2 1998/02/14 18:51:46 brianp
23 * fixed a small compiler warning
24 *
25 * Revision 3.1 1998/02/14 18:45:25 brianp
26 * optimized to use flat shading, don't blend ground polygon
27 *
28 * Revision 3.0 1998/02/14 18:42:29 brianp
29 * initial rev
30 *
31 */
32
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <math.h> /* for cos(), sin(), and sqrt() */
38 #include <GL/glut.h>
39
40 /* Some <math.h> files do not define M_PI... */
41 #ifndef M_PI
42 #define M_PI 3.14159265
43 #endif
44
45 #if 0 /* For debugging. */
46 #undef GL_EXT_point_parameters
47 #endif
48
49 static GLfloat angle = -150; /* in degrees */
50 static int spin = 0;
51 static int moving, begin;
52 static int newModel = 1;
53 static float theTime;
54 static int repeat = 1;
55 static int blend = 1;
56 int useMipmaps = 1;
57 int linearFiltering = 1;
58
59 static GLfloat constant[3] = { 1/5.0, 0.0, 0.0 };
60 static GLfloat linear[3] = { 0.0, 1/5.0, 0.0 };
61 static GLfloat theQuad[3] = { 0.25, 0.0, 1/60.0 };
62
63 #define MAX_POINTS 2000
64
65 static int numPoints = 200;
66
67 static GLfloat pointList[MAX_POINTS][3];
68 static GLfloat pointTime[MAX_POINTS];
69 static GLfloat pointVelocity[MAX_POINTS][2];
70 static GLfloat pointDirection[MAX_POINTS][2];
71 static int colorList[MAX_POINTS];
72 static int animate = 1, motion = 0;
73
74 static GLfloat colorSet[][4] = {
75 /* Shades of red. */
76 { 0.7, 0.2, 0.4, 0.5 },
77 { 0.8, 0.0, 0.7, 0.5 },
78 { 1.0, 0.0, 0.0, 0.5 },
79 { 0.9, 0.3, 0.6, 0.5 },
80 { 1.0, 0.4, 0.0, 0.5 },
81 { 1.0, 0.0, 0.5, 0.5 },
82 };
83
84 #define NUM_COLORS (sizeof(colorSet)/sizeof(colorSet[0]))
85
86 #define DEAD (NUM_COLORS+1)
87
88
89 #if 0 /* drand48 might be better on Unix machines */
90 #define RANDOM_RANGE(lo, hi) ((lo) + (hi - lo) * drand48())
91 #else
92 static float float_rand(void) { return rand() / (float) RAND_MAX; }
93 #define RANDOM_RANGE(lo, hi) ((lo) + (hi - lo) * float_rand())
94 #endif
95
96 #define MEAN_VELOCITY 3.0
97 #define GRAVITY 2.0
98 #define TIME_DELTA 0.025 /* The speed of time. */
99
100 /* Modeling units of ground extent in each X and Z direction. */
101 #define EDGE 12
102
103 void
104 makePointList(void)
105 {
106 float angle, velocity, direction;
107 int i;
108
109 motion = 1;
110 for (i=0; i<numPoints; i++) {
111 pointList[i][0] = 0.0;
112 pointList[i][1] = 0.0;
113 pointList[i][2] = 0.0;
114 pointTime[i] = 0.0;
115 angle = (RANDOM_RANGE(60.0, 70.0)) * M_PI/180.0;
116 direction = RANDOM_RANGE(0.0, 360.0) * M_PI/180.0;
117 pointDirection[i][0] = cos(direction);
118 pointDirection[i][1] = sin(direction);
119 velocity = MEAN_VELOCITY + RANDOM_RANGE(-0.8, 1.0);
120 pointVelocity[i][0] = velocity * cos(angle);
121 pointVelocity[i][1] = velocity * sin(angle);
122 colorList[i] = rand() % NUM_COLORS;
123 }
124 theTime = 0.0;
125 }
126
127 void
128 updatePointList(void)
129 {
130 float distance;
131 int i;
132
133 motion = 0;
134 for (i=0; i<numPoints; i++) {
135 distance = pointVelocity[i][0] * theTime;
136
137 /* X and Z */
138 pointList[i][0] = pointDirection[i][0] * distance;
139 pointList[i][2] = pointDirection[i][1] * distance;
140
141 /* Z */
142 pointList[i][1] =
143 (pointVelocity[i][1] - 0.5 * GRAVITY * pointTime[i])*pointTime[i];
144
145 /* If we hit the ground, bounce the point upward again. */
146 if (pointList[i][1] <= 0.0) {
147 if (distance > EDGE) {
148 /* Particle has hit ground past the distance duration of
149 the particles. Mark particle as dead. */
150 colorList[i] = NUM_COLORS; /* Not moving. */
151 continue;
152 }
153
154 pointVelocity[i][1] *= 0.8; /* 80% of previous up velocity. */
155 pointTime[i] = 0.0; /* Reset the particles sense of up time. */
156 }
157 motion = 1;
158 pointTime[i] += TIME_DELTA;
159 }
160 theTime += TIME_DELTA;
161 if (!motion && !spin) {
162 if (repeat) {
163 makePointList();
164 } else {
165 glutIdleFunc(NULL);
166 }
167 }
168 }
169
170 void
171 idle(void)
172 {
173 updatePointList();
174 if (spin) {
175 angle += 0.3;
176 newModel = 1;
177 }
178 glutPostRedisplay();
179 }
180
181 void
182 visible(int vis)
183 {
184 if (vis == GLUT_VISIBLE) {
185 if (animate && (motion || spin)) {
186 glutIdleFunc(idle);
187 }
188 } else {
189 glutIdleFunc(NULL);
190 }
191 }
192
193 void
194 recalcModelView(void)
195 {
196 glPopMatrix();
197 glPushMatrix();
198 glRotatef(angle, 0.0, 1.0, 0.0);
199 newModel = 0;
200 }
201
202 void
203 redraw(void)
204 {
205 int i;
206
207 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
208 if (newModel)
209 recalcModelView();
210
211 glDepthMask(GL_FALSE);
212
213 /* Draw the floor. */
214 /* glEnable(GL_TEXTURE_2D);*/
215 glColor3f(0.5, 1.0, 0.5);
216 glBegin(GL_QUADS);
217 glTexCoord2f(0.0, 0.0);
218 glVertex3f(-EDGE, -0.05, -EDGE);
219 glTexCoord2f(20.0, 0.0);
220 glVertex3f(EDGE, -0.05, -EDGE);
221 glTexCoord2f(20.0, 20.0);
222 glVertex3f(EDGE, -0.05, EDGE);
223 glTexCoord2f(0.0, 20.0);
224 glVertex3f(-EDGE, -0.05, EDGE);
225 glEnd();
226
227 /* Allow particles to blend with each other. */
228 glDepthMask(GL_TRUE);
229
230 if (blend)
231 glEnable(GL_BLEND);
232
233 glDisable(GL_TEXTURE_2D);
234 glBegin(GL_POINTS);
235 for (i=0; i<numPoints; i++) {
236 /* Draw alive particles. */
237 if (colorList[i] != DEAD) {
238 glColor4fv(colorSet[colorList[i]]);
239 glVertex3fv(pointList[i]);
240 }
241 }
242 glEnd();
243
244 glDisable(GL_BLEND);
245
246 glutSwapBuffers();
247 }
248
249 /* ARGSUSED2 */
250 void
251 mouse(int button, int state, int x, int y)
252 {
253 /* Scene can be spun around Y axis using left
254 mouse button movement. */
255 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
256 moving = 1;
257 begin = x;
258 }
259 if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
260 moving = 0;
261 }
262 }
263
264 /* ARGSUSED1 */
265 void
266 mouseMotion(int x, int y)
267 {
268 if (moving) {
269 angle = angle + (x - begin);
270 begin = x;
271 newModel = 1;
272 glutPostRedisplay();
273 }
274 }
275
276 void
277 menu(int option)
278 {
279 switch (option) {
280 case 0:
281 makePointList();
282 break;
283 #if GL_EXT_point_parameters
284 case 1:
285 glPointParameterfvEXT(GL_DISTANCE_ATTENUATION_EXT, constant);
286 break;
287 case 2:
288 glPointParameterfvEXT(GL_DISTANCE_ATTENUATION_EXT, linear);
289 break;
290 case 3:
291 glPointParameterfvEXT(GL_DISTANCE_ATTENUATION_EXT, theQuad);
292 break;
293 #endif
294 case 4:
295 blend = 1;
296 break;
297 case 5:
298 blend = 0;
299 break;
300 #if GL_EXT_point_parameters
301 case 6:
302 glPointParameterfEXT(GL_POINT_FADE_THRESHOLD_SIZE_EXT, 1.0);
303 break;
304 case 7:
305 glPointParameterfEXT(GL_POINT_FADE_THRESHOLD_SIZE_EXT, 10.0);
306 break;
307 #endif
308 case 8:
309 glEnable(GL_POINT_SMOOTH);
310 break;
311 case 9:
312 glDisable(GL_POINT_SMOOTH);
313 break;
314 case 10:
315 glPointSize(2.0);
316 break;
317 case 11:
318 glPointSize(4.0);
319 break;
320 case 12:
321 glPointSize(8.0);
322 break;
323 case 13:
324 spin = 1 - spin;
325 if (animate && (spin || motion)) {
326 glutIdleFunc(idle);
327 } else {
328 glutIdleFunc(NULL);
329 }
330 break;
331 case 14:
332 numPoints = 200;
333 break;
334 case 15:
335 numPoints = 500;
336 break;
337 case 16:
338 numPoints = 1000;
339 break;
340 case 17:
341 numPoints = 2000;
342 break;
343 case 666:
344 exit(0);
345 }
346 glutPostRedisplay();
347 }
348
349 /* ARGSUSED1 */
350 void
351 key(unsigned char c, int x, int y)
352 {
353 switch (c) {
354 case 13:
355 animate = 1 - animate; /* toggle. */
356 if (animate && (motion || spin)) {
357 glutIdleFunc(idle);
358 } else {
359 glutIdleFunc(NULL);
360 }
361 break;
362 case ' ':
363 animate = 1;
364 makePointList();
365 glutIdleFunc(idle);
366 break;
367 case 27:
368 exit(0);
369 }
370 }
371
372 /* Nice floor texture tiling pattern. */
373 static char *circles[] = {
374 "....xxxx........",
375 "..xxxxxxxx......",
376 ".xxxxxxxxxx.....",
377 ".xxx....xxx.....",
378 "xxx......xxx....",
379 "xxx......xxx....",
380 "xxx......xxx....",
381 "xxx......xxx....",
382 ".xxx....xxx.....",
383 ".xxxxxxxxxx.....",
384 "..xxxxxxxx......",
385 "....xxxx........",
386 "................",
387 "................",
388 "................",
389 "................",
390 };
391
392 static void
393 makeFloorTexture(void)
394 {
395 GLubyte floorTexture[16][16][3];
396 GLubyte *loc;
397 int s, t;
398
399 /* Setup RGB image for the texture. */
400 loc = (GLubyte*) floorTexture;
401 for (t = 0; t < 16; t++) {
402 for (s = 0; s < 16; s++) {
403 if (circles[t][s] == 'x') {
404 /* Nice blue. */
405 loc[0] = 0x1f;
406 loc[1] = 0x1f;
407 loc[2] = 0x8f;
408 } else {
409 /* Light gray. */
410 loc[0] = 0xca;
411 loc[1] = 0xca;
412 loc[2] = 0xca;
413 }
414 loc += 3;
415 }
416 }
417
418 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
419
420 if (useMipmaps) {
421 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
422 GL_LINEAR_MIPMAP_LINEAR);
423 gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 16, 16,
424 GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
425 } else {
426 if (linearFiltering) {
427 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
428 } else {
429 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
430 }
431 glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
432 GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
433 }
434 }
435
436 int
437 main(int argc, char **argv)
438 {
439 int i;
440 glutInit(&argc, argv);
441 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
442
443 for (i=1; i<argc; i++) {
444 if(!strcmp("-noms", argv[i])) {
445 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
446 printf("forcing no multisampling\n");
447 } else if(!strcmp("-nomipmaps", argv[i])) {
448 useMipmaps = 0;
449 } else if(!strcmp("-nearest", argv[i])) {
450 linearFiltering = 0;
451 }
452 }
453
454 glutCreateWindow("point burst");
455 glutDisplayFunc(redraw);
456 glutMouseFunc(mouse);
457 glutMotionFunc(mouseMotion);
458 glutVisibilityFunc(visible);
459 glutKeyboardFunc(key);
460 glutCreateMenu(menu);
461 glutAddMenuEntry("Reset time", 0);
462 glutAddMenuEntry("Constant", 1);
463 glutAddMenuEntry("Linear", 2);
464 glutAddMenuEntry("Quadratic", 3);
465 glutAddMenuEntry("Blend on", 4);
466 glutAddMenuEntry("Blend off", 5);
467 glutAddMenuEntry("Threshold 1", 6);
468 glutAddMenuEntry("Threshold 10", 7);
469 glutAddMenuEntry("Point smooth on", 8);
470 glutAddMenuEntry("Point smooth off", 9);
471 glutAddMenuEntry("Point size 2", 10);
472 glutAddMenuEntry("Point size 4", 11);
473 glutAddMenuEntry("Point size 8", 12);
474 glutAddMenuEntry("Toggle spin", 13);
475 glutAddMenuEntry("200 points ", 14);
476 glutAddMenuEntry("500 points ", 15);
477 glutAddMenuEntry("1000 points ", 16);
478 glutAddMenuEntry("2000 points ", 17);
479 glutAddMenuEntry("Quit", 666);
480 glutAttachMenu(GLUT_RIGHT_BUTTON);
481
482 glShadeModel(GL_FLAT);
483 glEnable(GL_DEPTH_TEST);
484 glEnable(GL_POINT_SMOOTH);
485 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
486 glPointSize(8.0);
487 #if GL_EXT_point_parameters
488 glPointParameterfvEXT(GL_DISTANCE_ATTENUATION_EXT, theQuad);
489 #endif
490 glMatrixMode(GL_PROJECTION);
491 gluPerspective( /* field of view in degree */ 40.0,
492 /* aspect ratio */ 1.0,
493 /* Z near */ 0.5, /* Z far */ 40.0);
494 glMatrixMode(GL_MODELVIEW);
495 gluLookAt(0.0, 1.0, 8.0, /* eye location */
496 0.0, 1.0, 0.0, /* center is at (0,0,0) */
497 0.0, 1.0, 0.); /* up is in postivie Y direction */
498 glPushMatrix(); /* dummy push so we can pop on model
499 recalc */
500
501 makePointList();
502 makeFloorTexture();
503
504 glutMainLoop();
505 return 0; /* ANSI C requires main to return int. */
506 }