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