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