Merge branch 'mesa_7_7_branch'
[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 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <math.h> /* for cos(), sin(), and sqrt() */
18 #ifdef _WIN32
19 #include <windows.h>
20 #endif
21 #include <GL/glew.h>
22 #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
33 static GLfloat angle = -150; /* in degrees */
34 static int spin = 0;
35 static int moving, begin;
36 static int newModel = 1;
37 static float theTime;
38 static int repeat = 1;
39 static int blend = 1;
40 int useMipmaps = 1;
41 int linearFiltering = 1;
42
43 static GLfloat constant[3] = { 1/5.0, 0.0, 0.0 };
44 static GLfloat linear[3] = { 0.0, 1/5.0, 0.0 };
45 static GLfloat theQuad[3] = { 0.25, 0.0, 1/60.0 };
46
47 #define MAX_POINTS 2000
48
49 static int numPoints = 200;
50
51 static GLfloat pointList[MAX_POINTS][3];
52 static GLfloat pointTime[MAX_POINTS];
53 static GLfloat pointVelocity[MAX_POINTS][2];
54 static GLfloat pointDirection[MAX_POINTS][2];
55 static int colorList[MAX_POINTS];
56 static int animate = 1, motion = 0;
57
58 static 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
76 static 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
82
83 /* Modeling units of ground extent in each X and Z direction. */
84 #define EDGE 12
85
86 static void
87 makePointList(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
110 static void
111 updatePointList(void)
112 {
113 float distance;
114 int i;
115
116 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
123 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;
148 pointTime[i] += dt;
149 }
150 theTime += dt;
151 if (!motion && !spin) {
152 if (repeat) {
153 makePointList();
154 } else {
155 glutIdleFunc(NULL);
156 }
157 }
158 }
159
160 static void
161 idle(void)
162 {
163 updatePointList();
164 if (spin) {
165 angle += 0.3;
166 newModel = 1;
167 }
168 glutPostRedisplay();
169 }
170
171 static void
172 visible(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
183 static void
184 recalcModelView(void)
185 {
186 glPopMatrix();
187 glPushMatrix();
188 glRotatef(angle, 0.0, 1.0, 0.0);
189 newModel = 0;
190 }
191
192 static void
193 redraw(void)
194 {
195 int i;
196
197 glDepthMask(GL_TRUE);
198 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
199 if (newModel)
200 recalcModelView();
201
202
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. */
218 glDepthMask(GL_FALSE);
219
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 */
240 static void
241 mouse(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 */
255 static void
256 mouseMotion(int x, int y)
257 {
258 if (moving) {
259 angle = angle + (x - begin);
260 begin = x;
261 newModel = 1;
262 glutPostRedisplay();
263 }
264 }
265
266 static void
267 menu(int option)
268 {
269 switch (option) {
270 case 0:
271 makePointList();
272 break;
273 #ifdef GL_ARB_point_parameters
274 case 1:
275 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, constant);
276 break;
277 case 2:
278 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, linear);
279 break;
280 case 3:
281 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad);
282 break;
283 #endif
284 case 4:
285 blend = 1;
286 break;
287 case 5:
288 blend = 0;
289 break;
290 #ifdef GL_ARB_point_parameters
291 case 6:
292 glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 1.0);
293 break;
294 case 7:
295 glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 10.0);
296 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 */
340 static void
341 key(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. */
363 static 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
382 static void
383 makeFloorTexture(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
426 int
427 main(int argc, char **argv)
428 {
429 int i;
430
431 glutInitWindowSize(300, 300);
432 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");
447 glewInit();
448 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
475 if (!glutExtensionSupported("GL_ARB_point_parameters")) {
476 fprintf(stderr, "Sorry, GL_ARB_point_parameters is not supported.\n");
477 return -1;
478 }
479
480 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);
485 #if GL_ARB_point_parameters
486 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad);
487 #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 }