blob: 14e9cf359fb3cecabc892a2150dc6ec4bd69369f [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
Brian Paul66fa33e2002-12-03 03:13:17 +000021#define GL_GLEXT_PROTOTYPES
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
82#define TIME_DELTA 0.025 /* The speed of time. */
83
84/* Modeling units of ground extent in each X and Z direction. */
85#define EDGE 12
86
Brian Paul02e8a032000-06-27 17:04:43 +000087static void
jtgafb833d1999-08-19 00:55:39 +000088makePointList(void)
89{
90 float angle, velocity, direction;
91 int i;
92
93 motion = 1;
94 for (i=0; i<numPoints; i++) {
95 pointList[i][0] = 0.0;
96 pointList[i][1] = 0.0;
97 pointList[i][2] = 0.0;
98 pointTime[i] = 0.0;
99 angle = (RANDOM_RANGE(60.0, 70.0)) * M_PI/180.0;
100 direction = RANDOM_RANGE(0.0, 360.0) * M_PI/180.0;
101 pointDirection[i][0] = cos(direction);
102 pointDirection[i][1] = sin(direction);
103 velocity = MEAN_VELOCITY + RANDOM_RANGE(-0.8, 1.0);
104 pointVelocity[i][0] = velocity * cos(angle);
105 pointVelocity[i][1] = velocity * sin(angle);
106 colorList[i] = rand() % NUM_COLORS;
107 }
108 theTime = 0.0;
109}
110
Brian Paul02e8a032000-06-27 17:04:43 +0000111static void
jtgafb833d1999-08-19 00:55:39 +0000112updatePointList(void)
113{
114 float distance;
115 int i;
116
117 motion = 0;
118 for (i=0; i<numPoints; i++) {
119 distance = pointVelocity[i][0] * theTime;
120
121 /* X and Z */
122 pointList[i][0] = pointDirection[i][0] * distance;
123 pointList[i][2] = pointDirection[i][1] * distance;
124
125 /* Z */
126 pointList[i][1] =
127 (pointVelocity[i][1] - 0.5 * GRAVITY * pointTime[i])*pointTime[i];
128
129 /* If we hit the ground, bounce the point upward again. */
130 if (pointList[i][1] <= 0.0) {
131 if (distance > EDGE) {
132 /* Particle has hit ground past the distance duration of
133 the particles. Mark particle as dead. */
134 colorList[i] = NUM_COLORS; /* Not moving. */
135 continue;
136 }
137
138 pointVelocity[i][1] *= 0.8; /* 80% of previous up velocity. */
139 pointTime[i] = 0.0; /* Reset the particles sense of up time. */
140 }
141 motion = 1;
142 pointTime[i] += TIME_DELTA;
143 }
144 theTime += TIME_DELTA;
145 if (!motion && !spin) {
146 if (repeat) {
147 makePointList();
148 } else {
149 glutIdleFunc(NULL);
150 }
151 }
152}
153
Brian Paul02e8a032000-06-27 17:04:43 +0000154static void
jtgafb833d1999-08-19 00:55:39 +0000155idle(void)
156{
157 updatePointList();
158 if (spin) {
159 angle += 0.3;
160 newModel = 1;
161 }
162 glutPostRedisplay();
163}
164
Brian Paul02e8a032000-06-27 17:04:43 +0000165static void
jtgafb833d1999-08-19 00:55:39 +0000166visible(int vis)
167{
168 if (vis == GLUT_VISIBLE) {
169 if (animate && (motion || spin)) {
170 glutIdleFunc(idle);
171 }
172 } else {
173 glutIdleFunc(NULL);
174 }
175}
176
Brian Paul02e8a032000-06-27 17:04:43 +0000177static void
jtgafb833d1999-08-19 00:55:39 +0000178recalcModelView(void)
179{
180 glPopMatrix();
181 glPushMatrix();
182 glRotatef(angle, 0.0, 1.0, 0.0);
183 newModel = 0;
184}
185
Brian Paul02e8a032000-06-27 17:04:43 +0000186static void
jtgafb833d1999-08-19 00:55:39 +0000187redraw(void)
188{
189 int i;
190
191 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
192 if (newModel)
193 recalcModelView();
194
195 glDepthMask(GL_FALSE);
196
197 /* Draw the floor. */
198/* glEnable(GL_TEXTURE_2D);*/
199 glColor3f(0.5, 1.0, 0.5);
200 glBegin(GL_QUADS);
201 glTexCoord2f(0.0, 0.0);
202 glVertex3f(-EDGE, -0.05, -EDGE);
203 glTexCoord2f(20.0, 0.0);
204 glVertex3f(EDGE, -0.05, -EDGE);
205 glTexCoord2f(20.0, 20.0);
206 glVertex3f(EDGE, -0.05, EDGE);
207 glTexCoord2f(0.0, 20.0);
208 glVertex3f(-EDGE, -0.05, EDGE);
209 glEnd();
210
211 /* Allow particles to blend with each other. */
212 glDepthMask(GL_TRUE);
213
214 if (blend)
215 glEnable(GL_BLEND);
216
217 glDisable(GL_TEXTURE_2D);
218 glBegin(GL_POINTS);
219 for (i=0; i<numPoints; i++) {
220 /* Draw alive particles. */
221 if (colorList[i] != DEAD) {
222 glColor4fv(colorSet[colorList[i]]);
223 glVertex3fv(pointList[i]);
224 }
225 }
226 glEnd();
227
228 glDisable(GL_BLEND);
229
230 glutSwapBuffers();
231}
232
233/* ARGSUSED2 */
Brian Paul02e8a032000-06-27 17:04:43 +0000234static void
jtgafb833d1999-08-19 00:55:39 +0000235mouse(int button, int state, int x, int y)
236{
237 /* Scene can be spun around Y axis using left
238 mouse button movement. */
239 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
240 moving = 1;
241 begin = x;
242 }
243 if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
244 moving = 0;
245 }
246}
247
248/* ARGSUSED1 */
Brian Paul02e8a032000-06-27 17:04:43 +0000249static void
jtgafb833d1999-08-19 00:55:39 +0000250mouseMotion(int x, int y)
251{
252 if (moving) {
253 angle = angle + (x - begin);
254 begin = x;
255 newModel = 1;
256 glutPostRedisplay();
257 }
258}
259
Brian Paul02e8a032000-06-27 17:04:43 +0000260static void
jtgafb833d1999-08-19 00:55:39 +0000261menu(int option)
262{
263 switch (option) {
264 case 0:
265 makePointList();
266 break;
Brian Paul66fa33e2002-12-03 03:13:17 +0000267#if GL_ARB_point_parameters
jtgafb833d1999-08-19 00:55:39 +0000268 case 1:
Brian Paul66fa33e2002-12-03 03:13:17 +0000269 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, constant);
jtgafb833d1999-08-19 00:55:39 +0000270 break;
271 case 2:
Brian Paul66fa33e2002-12-03 03:13:17 +0000272 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, linear);
jtgafb833d1999-08-19 00:55:39 +0000273 break;
274 case 3:
Brian Paul66fa33e2002-12-03 03:13:17 +0000275 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad);
jtgafb833d1999-08-19 00:55:39 +0000276 break;
277#endif
278 case 4:
279 blend = 1;
280 break;
281 case 5:
282 blend = 0;
283 break;
Brian Paul66fa33e2002-12-03 03:13:17 +0000284#if GL_ARB_point_parameters
jtgafb833d1999-08-19 00:55:39 +0000285 case 6:
Brian Paul66fa33e2002-12-03 03:13:17 +0000286 glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 1.0);
jtgafb833d1999-08-19 00:55:39 +0000287 break;
288 case 7:
Brian Paul66fa33e2002-12-03 03:13:17 +0000289 glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 10.0);
jtgafb833d1999-08-19 00:55:39 +0000290 break;
291#endif
292 case 8:
293 glEnable(GL_POINT_SMOOTH);
294 break;
295 case 9:
296 glDisable(GL_POINT_SMOOTH);
297 break;
298 case 10:
299 glPointSize(2.0);
300 break;
301 case 11:
302 glPointSize(4.0);
303 break;
304 case 12:
305 glPointSize(8.0);
306 break;
307 case 13:
308 spin = 1 - spin;
309 if (animate && (spin || motion)) {
310 glutIdleFunc(idle);
311 } else {
312 glutIdleFunc(NULL);
313 }
314 break;
315 case 14:
316 numPoints = 200;
317 break;
318 case 15:
319 numPoints = 500;
320 break;
321 case 16:
322 numPoints = 1000;
323 break;
324 case 17:
325 numPoints = 2000;
326 break;
327 case 666:
328 exit(0);
329 }
330 glutPostRedisplay();
331}
332
333/* ARGSUSED1 */
Brian Paul02e8a032000-06-27 17:04:43 +0000334static void
jtgafb833d1999-08-19 00:55:39 +0000335key(unsigned char c, int x, int y)
336{
337 switch (c) {
338 case 13:
339 animate = 1 - animate; /* toggle. */
340 if (animate && (motion || spin)) {
341 glutIdleFunc(idle);
342 } else {
343 glutIdleFunc(NULL);
344 }
345 break;
346 case ' ':
347 animate = 1;
348 makePointList();
349 glutIdleFunc(idle);
350 break;
351 case 27:
352 exit(0);
353 }
354}
355
356/* Nice floor texture tiling pattern. */
357static char *circles[] = {
358 "....xxxx........",
359 "..xxxxxxxx......",
360 ".xxxxxxxxxx.....",
361 ".xxx....xxx.....",
362 "xxx......xxx....",
363 "xxx......xxx....",
364 "xxx......xxx....",
365 "xxx......xxx....",
366 ".xxx....xxx.....",
367 ".xxxxxxxxxx.....",
368 "..xxxxxxxx......",
369 "....xxxx........",
370 "................",
371 "................",
372 "................",
373 "................",
374};
375
376static void
377makeFloorTexture(void)
378{
379 GLubyte floorTexture[16][16][3];
380 GLubyte *loc;
381 int s, t;
382
383 /* Setup RGB image for the texture. */
384 loc = (GLubyte*) floorTexture;
385 for (t = 0; t < 16; t++) {
386 for (s = 0; s < 16; s++) {
387 if (circles[t][s] == 'x') {
388 /* Nice blue. */
389 loc[0] = 0x1f;
390 loc[1] = 0x1f;
391 loc[2] = 0x8f;
392 } else {
393 /* Light gray. */
394 loc[0] = 0xca;
395 loc[1] = 0xca;
396 loc[2] = 0xca;
397 }
398 loc += 3;
399 }
400 }
401
402 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
403
404 if (useMipmaps) {
405 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
406 GL_LINEAR_MIPMAP_LINEAR);
407 gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 16, 16,
408 GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
409 } else {
410 if (linearFiltering) {
411 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
412 } else {
413 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
414 }
415 glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
416 GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
417 }
418}
419
420int
421main(int argc, char **argv)
422{
423 int i;
424 glutInit(&argc, argv);
425 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
Keith Whitwell7b5199e2004-02-16 14:45:56 +0000426 glutInitWindowPosition(0, 0);
427 glutInitWindowSize(300, 300);
jtgafb833d1999-08-19 00:55:39 +0000428
429 for (i=1; i<argc; i++) {
430 if(!strcmp("-noms", argv[i])) {
431 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
432 printf("forcing no multisampling\n");
433 } else if(!strcmp("-nomipmaps", argv[i])) {
434 useMipmaps = 0;
435 } else if(!strcmp("-nearest", argv[i])) {
436 linearFiltering = 0;
437 }
438 }
439
440 glutCreateWindow("point burst");
441 glutDisplayFunc(redraw);
442 glutMouseFunc(mouse);
443 glutMotionFunc(mouseMotion);
444 glutVisibilityFunc(visible);
445 glutKeyboardFunc(key);
446 glutCreateMenu(menu);
447 glutAddMenuEntry("Reset time", 0);
448 glutAddMenuEntry("Constant", 1);
449 glutAddMenuEntry("Linear", 2);
450 glutAddMenuEntry("Quadratic", 3);
451 glutAddMenuEntry("Blend on", 4);
452 glutAddMenuEntry("Blend off", 5);
453 glutAddMenuEntry("Threshold 1", 6);
454 glutAddMenuEntry("Threshold 10", 7);
455 glutAddMenuEntry("Point smooth on", 8);
456 glutAddMenuEntry("Point smooth off", 9);
457 glutAddMenuEntry("Point size 2", 10);
458 glutAddMenuEntry("Point size 4", 11);
459 glutAddMenuEntry("Point size 8", 12);
460 glutAddMenuEntry("Toggle spin", 13);
461 glutAddMenuEntry("200 points ", 14);
462 glutAddMenuEntry("500 points ", 15);
463 glutAddMenuEntry("1000 points ", 16);
464 glutAddMenuEntry("2000 points ", 17);
465 glutAddMenuEntry("Quit", 666);
466 glutAttachMenu(GLUT_RIGHT_BUTTON);
467
468 glShadeModel(GL_FLAT);
469 glEnable(GL_DEPTH_TEST);
470 glEnable(GL_POINT_SMOOTH);
471 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
472 glPointSize(8.0);
Brian Paul66fa33e2002-12-03 03:13:17 +0000473#if GL_ARB_point_parameters
474 glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad);
jtgafb833d1999-08-19 00:55:39 +0000475#endif
476 glMatrixMode(GL_PROJECTION);
477 gluPerspective( /* field of view in degree */ 40.0,
478 /* aspect ratio */ 1.0,
479 /* Z near */ 0.5, /* Z far */ 40.0);
480 glMatrixMode(GL_MODELVIEW);
481 gluLookAt(0.0, 1.0, 8.0, /* eye location */
482 0.0, 1.0, 0.0, /* center is at (0,0,0) */
483 0.0, 1.0, 0.); /* up is in postivie Y direction */
484 glPushMatrix(); /* dummy push so we can pop on model
485 recalc */
486
487 makePointList();
488 makeFloorTexture();
489
490 glutMainLoop();
491 return 0; /* ANSI C requires main to return int. */
492}