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/* Modified by Brian Paul to test GL_ARB_point_sprite */
15
16
17#include <assert.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <math.h>       /* for cos(), sin(), and sqrt() */
22#ifdef _WIN32
23#include <windows.h>
24#endif
25#include <GL/glew.h>
26#include "glut_wrap.h"
27
28/* Some <math.h> files do not define M_PI... */
29#ifndef M_PI
30#define M_PI 3.14159265
31#endif
32
33#if 0  /* For debugging. */
34#undef GL_EXT_point_parameters
35#endif
36
37static GLfloat angle = -150;   /* in degrees */
38static int spin = 0;
39static int moving, begin;
40static float theTime;
41static int repeat = 1;
42static int blend = 1;
43static int useMipmaps = 1;
44static int linearFiltering = 1;
45
46static GLuint Tex0, Tex1;
47
48static GLfloat constant[3] = { .2,  0.0,     0.0 };
49static GLfloat linear[3]   = { .0,   .1,     0.0 };
50static GLfloat theQuad[3]  = { .005, 0.05, 1/600.0 };
51
52#define MAX_POINTS 2000
53
54static int numPoints = 200;
55
56static GLfloat pointList[MAX_POINTS][3];
57static GLfloat pointTime[MAX_POINTS];
58static GLfloat pointVelocity[MAX_POINTS][2];
59static GLfloat pointDirection[MAX_POINTS][2];
60static int colorList[MAX_POINTS];
61static int animate = 1, motion = 0, org = 0, sprite = 1, smooth = 1;
62
63static GLfloat colorSet[][4] = {
64  /* Shades of red. */
65  { 0.7, 0.2, 0.4, 0.5 },
66  { 0.8, 0.0, 0.7, 0.5 },
67  { 1.0, 0.0, 0.0, 0.5 },
68  { 0.9, 0.3, 0.6, 0.5 },
69  { 1.0, 0.4, 0.0, 0.5 },
70  { 1.0, 0.0, 0.5, 0.5 },
71};
72
73#define NUM_COLORS (sizeof(colorSet)/sizeof(colorSet[0]))
74
75#define DEAD (NUM_COLORS+1)
76
77
78/* GL */
79static GLint spritePattern[16][16] = {
80   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
81   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
82   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
83   { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
84   { 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
85   { 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0 },
86   { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
87   { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
88   { 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0 },
89   { 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0 },
90   { 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0 },
91   { 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0 },
92   { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0 },
93   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
94   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
95   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
96};
97
98
99/** Fragment shader which inverts the texture's color */
100static const char *FragShaderText =
101   "uniform sampler2D tex; \n"
102   "void main(void) \n"
103   "{ \n"
104   "   gl_FragColor = vec4(1.0) - texture2D(tex, gl_TexCoord[0].xy); \n"
105   "} \n";
106
107static GLuint FragShader, ShaderProg;
108static GLboolean UseFragShader = GL_FALSE;
109static GLboolean HaveShaders = GL_FALSE;
110
111static void
112makeFragShader(void)
113{
114   GLint stat;
115
116   HaveShaders = GLEW_VERSION_2_0;
117   if (!HaveShaders)
118      return;
119
120   FragShader = glCreateShader(GL_FRAGMENT_SHADER);
121   glShaderSource(FragShader, 1, (const GLchar **) &FragShaderText, NULL);
122   glCompileShader(FragShader);
123
124   ShaderProg = glCreateProgram();
125   glAttachShader(ShaderProg, FragShader);
126   glLinkProgram(ShaderProg);
127
128   glGetProgramiv(ShaderProg, GL_LINK_STATUS, &stat);
129   assert(stat);
130}
131
132
133
134
135#if 0  /* drand48 might be better on Unix machines */
136#define RANDOM_RANGE(lo, hi) ((lo) + (hi - lo) * drand48())
137#else
138static float float_rand(void) { return rand() / (float) RAND_MAX; }
139#define RANDOM_RANGE(lo, hi) ((lo) + (hi - lo) * float_rand())
140#endif
141
142#define MEAN_VELOCITY 3.0
143#define GRAVITY 2.0
144
145/* Modeling units of ground extent in each X and Z direction. */
146#define EDGE 12
147
148static void
149makePointList(void)
150{
151  float angle, velocity, direction;
152  int i;
153
154  motion = 1;
155  for (i=0; i<numPoints; i++) {
156    pointList[i][0] = 0.0;
157    pointList[i][1] = 0.0;
158    pointList[i][2] = 0.0;
159    pointTime[i] = 0.0;
160    angle = (RANDOM_RANGE(60.0, 70.0)) * M_PI/180.0;
161    direction = RANDOM_RANGE(0.0, 360.0) * M_PI/180.0;
162    pointDirection[i][0] = cos(direction);
163    pointDirection[i][1] = sin(direction);
164    velocity = MEAN_VELOCITY + RANDOM_RANGE(-0.8, 1.0);
165    pointVelocity[i][0] = velocity * cos(angle);
166    pointVelocity[i][1] = velocity * sin(angle);
167    colorList[i] = rand() % NUM_COLORS;
168  }
169  theTime = 0.0;
170}
171
172static void
173updatePointList(void)
174{
175  float distance;
176  int i;
177
178  static double t0 = -1.;
179  double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
180  if (t0 < 0.0)
181    t0 = t;
182  dt = t - t0;
183  t0 = t;
184
185  motion = 0;
186  for (i=0; i<numPoints; i++) {
187    distance = pointVelocity[i][0] * theTime;
188
189    /* X and Z */
190    pointList[i][0] = pointDirection[i][0] * distance;
191    pointList[i][2] = pointDirection[i][1] * distance;
192
193    /* Z */
194    pointList[i][1] =
195      (pointVelocity[i][1] - 0.5 * GRAVITY * pointTime[i])*pointTime[i];
196
197    /* If we hit the ground, bounce the point upward again. */
198    if (pointList[i][1] <= 0.0) {
199      if (distance > EDGE) {
200        /* Particle has hit ground past the distance duration of
201          the particles.  Mark particle as dead. */
202       colorList[i] = NUM_COLORS;  /* Not moving. */
203       continue;
204      }
205
206      pointVelocity[i][1] *= 0.8;  /* 80% of previous up velocity. */
207      pointTime[i] = 0.0;  /* Reset the particles sense of up time. */
208    }
209    motion = 1;
210    pointTime[i] += dt;
211  }
212  theTime += dt;
213  if (!motion && !spin) {
214    if (repeat) {
215      makePointList();
216    } else {
217      glutIdleFunc(NULL);
218    }
219  }
220}
221
222static void
223idle(void)
224{
225  updatePointList();
226  if (spin) {
227    angle += 0.3;
228  }
229  glutPostRedisplay();
230}
231
232static void
233visible(int vis)
234{
235  if (vis == GLUT_VISIBLE) {
236    if (animate && (motion || spin)) {
237      glutIdleFunc(idle);
238    }
239  } else {
240    glutIdleFunc(NULL);
241  }
242}
243
244static void
245redraw(void)
246{
247  int i;
248
249  glDepthMask(GL_TRUE);
250  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
251
252  if (HaveShaders)
253     glUseProgram(0);
254
255  glPushMatrix();
256  glRotatef(15.0, 1.0, 0.0, 0.0);
257  glRotatef(angle, 0.0, 1.0, 0.0);
258
259
260  /* Draw the floor. */
261  glColor3f(0.1, 0.5, 1.0);
262  glBegin(GL_QUADS);
263    glTexCoord2f(0.0, 0.0);
264    glVertex3f(-EDGE, -0.05, -EDGE);
265    glTexCoord2f(20.0, 0.0);
266    glVertex3f(EDGE, -0.05, -EDGE);
267    glTexCoord2f(20.0, 20.0);
268    glVertex3f(EDGE, -0.05, EDGE);
269    glTexCoord2f(0.0, 20.0);
270    glVertex3f(-EDGE, -0.05, EDGE);
271  glEnd();
272
273  /* Allow particles to blend with each other. */
274  glDepthMask(GL_FALSE);
275
276  if (blend)
277     glEnable(GL_BLEND);
278
279  if (sprite) {
280     glActiveTexture(GL_TEXTURE0);
281     glEnable(GL_TEXTURE_2D);
282     if (0) {
283        /* debug/test code */
284        glActiveTexture(GL_TEXTURE1);
285        glEnable(GL_TEXTURE_2D);
286     }
287
288#ifdef GL_ARB_point_sprite
289     glEnable(GL_POINT_SPRITE_ARB);
290#endif
291     if (UseFragShader) {
292        glUseProgram(ShaderProg);
293     }
294  }
295
296  glColor3f(1,1,1);
297  glBegin(GL_POINTS);
298    for (i=0; i<numPoints; i++) {
299      /* Draw alive particles. */
300      if (colorList[i] != DEAD) {
301        if (!sprite)
302           glColor4fv(colorSet[colorList[i]]);
303        glVertex3fv(pointList[i]);
304      }
305    }
306  glEnd();
307
308  glActiveTexture(GL_TEXTURE0);
309  glDisable(GL_TEXTURE_2D);
310  glActiveTexture(GL_TEXTURE1);
311  glDisable(GL_TEXTURE_2D);
312
313#ifdef GL_ARB_point_sprite
314  glDisable(GL_POINT_SPRITE_ARB);
315#endif
316  glDisable(GL_BLEND);
317
318  glPopMatrix();
319
320  glutSwapBuffers();
321}
322
323/* ARGSUSED2 */
324static void
325mouse(int button, int state, int x, int y)
326{
327  /* Scene can be spun around Y axis using left
328     mouse button movement. */
329  if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
330    moving = 1;
331    begin = x;
332  }
333  if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
334    moving = 0;
335  }
336}
337
338/* ARGSUSED1 */
339static void
340mouseMotion(int x, int y)
341{
342  if (moving) {
343    angle = angle + (x - begin);
344    begin = x;
345    glutPostRedisplay();
346  }
347}
348
349static void
350menu(int option)
351{
352  switch (option) {
353  case 0:
354    makePointList();
355    break;
356#ifdef GL_ARB_point_parameters
357  case 1:
358    glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, constant);
359    break;
360  case 2:
361    glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, linear);
362    break;
363  case 3:
364    glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad);
365    break;
366#endif
367  case 4:
368    blend = 1;
369    break;
370  case 5:
371    blend = 0;
372    break;
373#ifdef GL_ARB_point_parameters
374  case 6:
375    glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 1.0);
376    break;
377  case 7:
378    glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 10.0);
379    break;
380#endif
381  case 8:
382    glEnable(GL_POINT_SMOOTH);
383    smooth = 1;
384    break;
385  case 9:
386    glDisable(GL_POINT_SMOOTH);
387    smooth = 0;
388    break;
389  case 10:
390    glPointSize(16.0);
391    break;
392  case 11:
393    glPointSize(32.0);
394    break;
395  case 12:
396    glPointSize(64.0);
397    break;
398  case 13:
399    spin = 1 - spin;
400    if (animate && (spin || motion)) {
401      glutIdleFunc(idle);
402    } else {
403      glutIdleFunc(NULL);
404    }
405    break;
406  case 14:
407    numPoints = 200;
408    break;
409  case 15:
410    numPoints = 500;
411    break;
412  case 16:
413    numPoints = 1000;
414    break;
415  case 17:
416    numPoints = 2000;
417    break;
418  case 666:
419    exit(0);
420  }
421  glutPostRedisplay();
422}
423
424/* ARGSUSED1 */
425static void
426key(unsigned char c, int x, int y)
427{
428  switch (c) {
429  case 13:
430    animate = 1 - animate;  /* toggle. */
431    if (animate && (motion || spin)) {
432      glutIdleFunc(idle);
433    } else {
434      glutIdleFunc(NULL);
435    }
436    break;
437  case ' ':
438    animate = 1;
439    makePointList();
440    glutIdleFunc(idle);
441    break;
442  case 'o':
443  case 'O':
444    org ^= 1;
445#ifdef GL_VERSION_2_0
446#ifdef GL_ARB_point_parameters
447    glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN,
448                      org ? GL_LOWER_LEFT : GL_UPPER_LEFT);
449#endif
450#endif
451    glutPostRedisplay();
452    break;
453  case 't':
454  case 'T':
455    sprite ^= 1;
456    glutPostRedisplay();
457    break;
458  case 's':
459  case 'S':
460    (smooth ^= 1) ? glEnable(GL_POINT_SMOOTH) : glDisable(GL_POINT_SMOOTH);
461    glutPostRedisplay();
462    break;
463  case 'f':
464  case 'F':
465    if (HaveShaders) {
466       UseFragShader = !UseFragShader;
467       glutPostRedisplay();
468    }
469    break;
470  case '0':
471    glPointSize(1.0);
472    glutPostRedisplay();
473    break;
474  case '1':
475    glPointSize(16.0);
476    glutPostRedisplay();
477    break;
478  case '2':
479    glPointSize(32.0);
480    glutPostRedisplay();
481    break;
482  case '3':
483    glPointSize(64.0);
484    glutPostRedisplay();
485    break;
486  case '4':
487    glPointSize(128.0);
488    glutPostRedisplay();
489    break;
490  case 27:
491    exit(0);
492  }
493}
494
495
496
497static void
498makeSpriteTextures(void)
499{
500   GLubyte texture[16][16][4];
501   int i, j;
502
503   if (!glutExtensionSupported("GL_ARB_point_sprite")) {
504      printf("Sorry, this demo requires GL_ARB_point_sprite.\n");
505      exit(0);
506   }
507   if (!glutExtensionSupported("GL_ARB_point_parameters")) {
508      printf("Sorry, this demo requires GL_ARB_point_parameters.\n");
509      exit(0);
510   }
511
512   /* "GL" */
513   for (i = 0; i < 16; i++) {
514      for (j = 0; j < 16; j++) {
515         if (spritePattern[i][j]) {
516            texture[i][j][0] = 255;
517            texture[i][j][1] = 255;
518            texture[i][j][2] = 255;
519            texture[i][j][3] = 255;
520         }
521         else {
522            texture[i][j][0] = 255;
523            texture[i][j][1] = 0;
524            texture[i][j][2] = 0;
525            texture[i][j][3] = 0;
526         }
527      }
528   }
529
530   /* texture 0 */
531   glActiveTexture(GL_TEXTURE0);
532   glGenTextures(1, &Tex0);
533   glBindTexture(GL_TEXTURE_2D, Tex0);
534   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
535                texture);
536   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
537   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
538
539   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
540#ifdef GL_ARB_point_sprite
541   glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
542#endif
543
544   /* left=yellow, right=green */
545   memset(texture, 0, sizeof(texture));
546   for (i = 0; i < 16; i++) {
547      for (j = 0; j < 16; j++) {
548         if (j < 8) {
549            texture[i][j][0] = 255;
550            texture[i][j][1] = 255;
551            texture[i][j][2] = 0;
552            texture[i][j][3] = 255;
553         }
554         else {
555            texture[i][j][0] = 0;
556            texture[i][j][1] = 255;
557            texture[i][j][2] = 0;
558            texture[i][j][3] = 255;
559         }
560      }
561   }
562
563   /* texture 1 */
564   glActiveTexture(GL_TEXTURE1);
565   glGenTextures(1, &Tex1);
566   glBindTexture(GL_TEXTURE_2D, Tex1);
567   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
568                texture);
569   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
570   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
571   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
572#ifdef GL_ARB_point_sprite
573   glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
574#endif
575
576   glActiveTexture(GL_TEXTURE0);
577}
578
579
580static void
581reshape(int width, int height)
582{
583  GLfloat h = (GLfloat) height / (GLfloat) width;
584
585  glViewport(0, 0, (GLint) width, (GLint) height);
586
587#if 0 /* debug/test code */
588  glMatrixMode(GL_TEXTURE);
589  glLoadIdentity();
590  glRotatef(45, 0, 0, 1);
591#endif
592
593  glMatrixMode(GL_PROJECTION);
594  glLoadIdentity();
595  glFrustum(-1.0, 1.0, -h, h, 2.0, 30.0);
596  glMatrixMode(GL_MODELVIEW);
597  glLoadIdentity();
598  glTranslatef(0.0, 0.0, -10.0);
599}
600
601int
602main(int argc, char **argv)
603{
604  int i;
605
606  glutInitWindowSize(600,300);
607  glutInit(&argc, argv);
608  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
609
610  for (i=1; i<argc; i++) {
611    if(!strcmp("-noms", argv[i])) {
612      glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
613      printf("forcing no multisampling\n");
614    } else if(!strcmp("-nomipmaps", argv[i])) {
615      useMipmaps = 0;
616    } else if(!strcmp("-nearest", argv[i])) {
617      linearFiltering = 0;
618    }
619  }
620  glutCreateWindow("sprite blast");
621  glewInit();
622  glutReshapeFunc(reshape);
623  glutDisplayFunc(redraw);
624  glutMouseFunc(mouse);
625  glutMotionFunc(mouseMotion);
626  glutVisibilityFunc(visible);
627  glutKeyboardFunc(key);
628  glutCreateMenu(menu);
629  glutAddMenuEntry("Reset time", 0);
630  glutAddMenuEntry("Constant", 1);
631  glutAddMenuEntry("Linear", 2);
632  glutAddMenuEntry("Quadratic", 3);
633  glutAddMenuEntry("Blend on", 4);
634  glutAddMenuEntry("Blend off", 5);
635  glutAddMenuEntry("Threshold 1", 6);
636  glutAddMenuEntry("Threshold 10", 7);
637  glutAddMenuEntry("Point smooth on", 8);
638  glutAddMenuEntry("Point smooth off", 9);
639  glutAddMenuEntry("Point size 16", 10);
640  glutAddMenuEntry("Point size 32", 11);
641  glutAddMenuEntry("Point size 64", 12);
642  glutAddMenuEntry("Toggle spin", 13);
643  glutAddMenuEntry("200 points ", 14);
644  glutAddMenuEntry("500 points ", 15);
645  glutAddMenuEntry("1000 points ", 16);
646  glutAddMenuEntry("2000 points ", 17);
647  glutAddMenuEntry("Quit", 666);
648  glutAttachMenu(GLUT_RIGHT_BUTTON);
649
650  makePointList();
651  makeSpriteTextures();
652  makeFragShader();
653
654  glShadeModel(GL_FLAT);
655  glEnable(GL_DEPTH_TEST);
656  glEnable(GL_POINT_SMOOTH);
657  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
658  glPointSize(32.0);
659#ifdef GL_ARB_point_parameters
660  glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad);
661#endif
662
663  glutMainLoop();
664  return 0;             /* ANSI C requires main to return int. */
665}
666