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 "glut_wrap.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
83/* Modeling units of ground extent in each X and Z direction. */
84#define EDGE 12
85
86static void
87makePointList(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
110static void
111updatePointList(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
160static void
161idle(void)
162{
163  updatePointList();
164  if (spin) {
165    angle += 0.3;
166    newModel = 1;
167  }
168  glutPostRedisplay();
169}
170
171static void
172visible(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
183static void
184recalcModelView(void)
185{
186  glPopMatrix();
187  glPushMatrix();
188  glRotatef(angle, 0.0, 1.0, 0.0);
189  newModel = 0;
190}
191
192static void
193redraw(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 */
240static void
241mouse(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 */
255static void
256mouseMotion(int x, int y)
257{
258  if (moving) {
259    angle = angle + (x - begin);
260    begin = x;
261    newModel = 1;
262    glutPostRedisplay();
263  }
264}
265
266static void
267menu(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 */
340static void
341key(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. */
363static 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
382static void
383makeFloorTexture(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
426int
427main(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}
505