mesa: convert log/exp tests to ARB_v_p
[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 #define GL_GLEXT_PROTOTYPES
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 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
198 if (newModel)
199 recalcModelView();
200
201 glDepthMask(GL_FALSE);
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_TRUE);
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 glutInit(&argc, argv);
431 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
432 glutInitWindowPosition(0, 0);
433 glutInitWindowSize(300, 300);
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 glutDisplayFunc(redraw);
448 glutMouseFunc(mouse);
449 glutMotionFunc(mouseMotion);
450 glutVisibilityFunc(visible);
451 glutKeyboardFunc(key);
452 glutCreateMenu(menu);
453 glutAddMenuEntry("Reset time", 0);
454 glutAddMenuEntry("Constant", 1);
455 glutAddMenuEntry("Linear", 2);
456 glutAddMenuEntry("Quadratic", 3);
457 glutAddMenuEntry("Blend on", 4);
458 glutAddMenuEntry("Blend off", 5);
459 glutAddMenuEntry("Threshold 1", 6);
460 glutAddMenuEntry("Threshold 10", 7);
461 glutAddMenuEntry("Point smooth on", 8);
462 glutAddMenuEntry("Point smooth off", 9);
463 glutAddMenuEntry("Point size 2", 10);
464 glutAddMenuEntry("Point size 4", 11);
465 glutAddMenuEntry("Point size 8", 12);
466 glutAddMenuEntry("Toggle spin", 13);
467 glutAddMenuEntry("200 points ", 14);
468 glutAddMenuEntry("500 points ", 15);
469 glutAddMenuEntry("1000 points ", 16);
470 glutAddMenuEntry("2000 points ", 17);
471 glutAddMenuEntry("Quit", 666);
472 glutAttachMenu(GLUT_RIGHT_BUTTON);
473
474 if (!glutExtensionSupported("GL_ARB_point_parameters")) {
475 fprintf(stderr, "Sorry, GL_ARB_point_parameters is not supported.\n");
476 return -1;
477 }
478
479 glShadeModel(GL_FLAT);
480 glEnable(GL_DEPTH_TEST);
481 glEnable(GL_POINT_SMOOTH);
482 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
483 glPointSize(8.0);
484 #if GL_ARB_point_parameters
485 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad);
486 #endif
487 glMatrixMode(GL_PROJECTION);
488 gluPerspective( /* field of view in degree */ 40.0,
489 /* aspect ratio */ 1.0,
490 /* Z near */ 0.5, /* Z far */ 40.0);
491 glMatrixMode(GL_MODELVIEW);
492 gluLookAt(0.0, 1.0, 8.0, /* eye location */
493 0.0, 1.0, 0.0, /* center is at (0,0,0) */
494 0.0, 1.0, 0.); /* up is in postivie Y direction */
495 glPushMatrix(); /* dummy push so we can pop on model
496 recalc */
497
498 makePointList();
499 makeFloorTexture();
500
501 glutMainLoop();
502 return 0; /* ANSI C requires main to return int. */
503 }