blob: 36e226e1d07929ae7befb6700b8fb7ec6250e207 [file] [log] [blame]
/* Copyright (c) Mark J. Kilgard, 1997. */
/* This program is freely distributable without licensing fees
and is provided without guarantee or warrantee expressed or
implied. This program is -not- in the public domain. */
/* This example demonstrates how to render particle effects
with OpenGL. A cloud of pinkish/orange particles explodes with the
particles bouncing off the ground. When the EXT_point_parameters
is present , the particle size is attenuated based on eye distance. */
/* Modified by Brian Paul to test GL_ARB_point_sprite */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h> /* for cos(), sin(), and sqrt() */
#ifdef _WIN32
#include <windows.h>
#endif
#include <GL/glew.h>
#include <GL/glut.h>
/* Some <math.h> files do not define M_PI... */
#ifndef M_PI
#define M_PI 3.14159265
#endif
#if 0 /* For debugging. */
#undef GL_EXT_point_parameters
#endif
static GLfloat angle = -150; /* in degrees */
static int spin = 0;
static int moving, begin;
static float theTime;
static int repeat = 1;
static int blend = 1;
int useMipmaps = 1;
int linearFiltering = 1;
static GLfloat constant[3] = { .2, 0.0, 0.0 };
static GLfloat linear[3] = { .0, .1, 0.0 };
static GLfloat theQuad[3] = { .005, 0.1, 1/600.0 };
#define MAX_POINTS 2000
static int numPoints = 200;
static GLfloat pointList[MAX_POINTS][3];
static GLfloat pointTime[MAX_POINTS];
static GLfloat pointVelocity[MAX_POINTS][2];
static GLfloat pointDirection[MAX_POINTS][2];
static int colorList[MAX_POINTS];
static int animate = 1, motion = 0, org = 0, sprite = 1, smooth = 1;
static GLfloat colorSet[][4] = {
/* Shades of red. */
{ 0.7, 0.2, 0.4, 0.5 },
{ 0.8, 0.0, 0.7, 0.5 },
{ 1.0, 0.0, 0.0, 0.5 },
{ 0.9, 0.3, 0.6, 0.5 },
{ 1.0, 0.4, 0.0, 0.5 },
{ 1.0, 0.0, 0.5, 0.5 },
};
#define NUM_COLORS (sizeof(colorSet)/sizeof(colorSet[0]))
#define DEAD (NUM_COLORS+1)
/* GL */
static GLint spritePattern[16][16] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0 },
{ 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
#if 0 /* drand48 might be better on Unix machines */
#define RANDOM_RANGE(lo, hi) ((lo) + (hi - lo) * drand48())
#else
static float float_rand(void) { return rand() / (float) RAND_MAX; }
#define RANDOM_RANGE(lo, hi) ((lo) + (hi - lo) * float_rand())
#endif
#define MEAN_VELOCITY 3.0
#define GRAVITY 2.0
/* Modeling units of ground extent in each X and Z direction. */
#define EDGE 12
static void
makePointList(void)
{
float angle, velocity, direction;
int i;
motion = 1;
for (i=0; i<numPoints; i++) {
pointList[i][0] = 0.0;
pointList[i][1] = 0.0;
pointList[i][2] = 0.0;
pointTime[i] = 0.0;
angle = (RANDOM_RANGE(60.0, 70.0)) * M_PI/180.0;
direction = RANDOM_RANGE(0.0, 360.0) * M_PI/180.0;
pointDirection[i][0] = cos(direction);
pointDirection[i][1] = sin(direction);
velocity = MEAN_VELOCITY + RANDOM_RANGE(-0.8, 1.0);
pointVelocity[i][0] = velocity * cos(angle);
pointVelocity[i][1] = velocity * sin(angle);
colorList[i] = rand() % NUM_COLORS;
}
theTime = 0.0;
}
static void
updatePointList(void)
{
float distance;
int i;
static double t0 = -1.;
double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
if (t0 < 0.0)
t0 = t;
dt = t - t0;
t0 = t;
motion = 0;
for (i=0; i<numPoints; i++) {
distance = pointVelocity[i][0] * theTime;
/* X and Z */
pointList[i][0] = pointDirection[i][0] * distance;
pointList[i][2] = pointDirection[i][1] * distance;
/* Z */
pointList[i][1] =
(pointVelocity[i][1] - 0.5 * GRAVITY * pointTime[i])*pointTime[i];
/* If we hit the ground, bounce the point upward again. */
if (pointList[i][1] <= 0.0) {
if (distance > EDGE) {
/* Particle has hit ground past the distance duration of
the particles. Mark particle as dead. */
colorList[i] = NUM_COLORS; /* Not moving. */
continue;
}
pointVelocity[i][1] *= 0.8; /* 80% of previous up velocity. */
pointTime[i] = 0.0; /* Reset the particles sense of up time. */
}
motion = 1;
pointTime[i] += dt;
}
theTime += dt;
if (!motion && !spin) {
if (repeat) {
makePointList();
} else {
glutIdleFunc(NULL);
}
}
}
static void
idle(void)
{
updatePointList();
if (spin) {
angle += 0.3;
}
glutPostRedisplay();
}
static void
visible(int vis)
{
if (vis == GLUT_VISIBLE) {
if (animate && (motion || spin)) {
glutIdleFunc(idle);
}
} else {
glutIdleFunc(NULL);
}
}
static void
redraw(void)
{
int i;
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glRotatef(15.0, 1.0, 0.0, 0.0);
glRotatef(angle, 0.0, 1.0, 0.0);
/* Draw the floor. */
/* glEnable(GL_TEXTURE_2D);*/
glColor3f(0.1, 0.5, 1.0);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0);
glVertex3f(-EDGE, -0.05, -EDGE);
glTexCoord2f(20.0, 0.0);
glVertex3f(EDGE, -0.05, -EDGE);
glTexCoord2f(20.0, 20.0);
glVertex3f(EDGE, -0.05, EDGE);
glTexCoord2f(0.0, 20.0);
glVertex3f(-EDGE, -0.05, EDGE);
glEnd();
/* Allow particles to blend with each other. */
glDepthMask(GL_FALSE);
if (blend)
glEnable(GL_BLEND);
if (sprite) {
glEnable(GL_TEXTURE_2D);
#ifdef GL_ARB_point_sprite
glEnable(GL_POINT_SPRITE_ARB);
#endif
}
glColor3f(1,1,1);
glBegin(GL_POINTS);
for (i=0; i<numPoints; i++) {
/* Draw alive particles. */
if (colorList[i] != DEAD) {
if (!sprite) glColor4fv(colorSet[colorList[i]]);
glVertex3fv(pointList[i]);
}
}
glEnd();
glDisable(GL_TEXTURE_2D);
#ifdef GL_ARB_point_sprite
glDisable(GL_POINT_SPRITE_ARB);
#endif
glDisable(GL_BLEND);
glPopMatrix();
glutSwapBuffers();
}
/* ARGSUSED2 */
static void
mouse(int button, int state, int x, int y)
{
/* Scene can be spun around Y axis using left
mouse button movement. */
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
moving = 1;
begin = x;
}
if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
moving = 0;
}
}
/* ARGSUSED1 */
static void
mouseMotion(int x, int y)
{
if (moving) {
angle = angle + (x - begin);
begin = x;
glutPostRedisplay();
}
}
static void
menu(int option)
{
switch (option) {
case 0:
makePointList();
break;
#ifdef GL_ARB_point_parameters
case 1:
glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, constant);
break;
case 2:
glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, linear);
break;
case 3:
glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad);
break;
#endif
case 4:
blend = 1;
break;
case 5:
blend = 0;
break;
#ifdef GL_ARB_point_parameters
case 6:
glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 1.0);
break;
case 7:
glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 10.0);
break;
#endif
case 8:
glEnable(GL_POINT_SMOOTH);
smooth = 1;
break;
case 9:
glDisable(GL_POINT_SMOOTH);
smooth = 0;
break;
case 10:
glPointSize(16.0);
break;
case 11:
glPointSize(32.0);
break;
case 12:
glPointSize(64.0);
break;
case 13:
spin = 1 - spin;
if (animate && (spin || motion)) {
glutIdleFunc(idle);
} else {
glutIdleFunc(NULL);
}
break;
case 14:
numPoints = 200;
break;
case 15:
numPoints = 500;
break;
case 16:
numPoints = 1000;
break;
case 17:
numPoints = 2000;
break;
case 666:
exit(0);
}
glutPostRedisplay();
}
/* ARGSUSED1 */
static void
key(unsigned char c, int x, int y)
{
switch (c) {
case 13:
animate = 1 - animate; /* toggle. */
if (animate && (motion || spin)) {
glutIdleFunc(idle);
} else {
glutIdleFunc(NULL);
}
break;
case ' ':
animate = 1;
makePointList();
glutIdleFunc(idle);
break;
case 'o':
case 'O':
org ^= 1;
#ifdef GL_VERSION_2_0
#ifdef GL_ARB_point_parameters
glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN,
org ? GL_LOWER_LEFT : GL_UPPER_LEFT);
#endif
#endif
glutPostRedisplay();
break;
case 't':
case 'T':
sprite ^= 1;
glutPostRedisplay();
break;
case 's':
case 'S':
(smooth ^= 1) ? glEnable(GL_POINT_SMOOTH) : glDisable(GL_POINT_SMOOTH);
glutPostRedisplay();
break;
case '0':
glPointSize(1.0);
glutPostRedisplay();
break;
case '1':
glPointSize(16.0);
glutPostRedisplay();
break;
case '2':
glPointSize(32.0);
glutPostRedisplay();
break;
case '3':
glPointSize(64.0);
glutPostRedisplay();
break;
case '4':
glPointSize(128.0);
glutPostRedisplay();
break;
case 27:
exit(0);
}
}
static void
makeSprite(void)
{
GLubyte texture[16][16][4];
int i, j;
if (!glutExtensionSupported("GL_ARB_point_sprite")) {
printf("Sorry, this demo requires GL_ARB_point_sprite.\n");
exit(0);
}
if (!glutExtensionSupported("GL_ARB_point_parameters")) {
printf("Sorry, this demo requires GL_ARB_point_parameters.\n");
exit(0);
}
for (i = 0; i < 16; i++) {
for (j = 0; j < 16; j++) {
if (spritePattern[i][j]) {
texture[i][j][0] = 255;
texture[i][j][1] = 255;
texture[i][j][2] = 255;
texture[i][j][3] = 255;
}
else {
texture[i][j][0] = 255;
texture[i][j][1] = 0;
texture[i][j][2] = 0;
texture[i][j][3] = 0;
}
}
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef GL_ARB_point_sprite
glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
#endif
}
static void
reshape(int width, int height)
{
GLfloat h = (GLfloat) height / (GLfloat) width;
glViewport(0, 0, (GLint) width, (GLint) height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1.0, 1.0, -h, h, 2.0, 30.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -10.0);
}
int
main(int argc, char **argv)
{
int i;
glutInitWindowSize(600,300);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
for (i=1; i<argc; i++) {
if(!strcmp("-noms", argv[i])) {
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
printf("forcing no multisampling\n");
} else if(!strcmp("-nomipmaps", argv[i])) {
useMipmaps = 0;
} else if(!strcmp("-nearest", argv[i])) {
linearFiltering = 0;
}
}
glutCreateWindow("sprite blast");
glewInit();
glutReshapeFunc(reshape);
glutDisplayFunc(redraw);
glutMouseFunc(mouse);
glutMotionFunc(mouseMotion);
glutVisibilityFunc(visible);
glutKeyboardFunc(key);
glutCreateMenu(menu);
glutAddMenuEntry("Reset time", 0);
glutAddMenuEntry("Constant", 1);
glutAddMenuEntry("Linear", 2);
glutAddMenuEntry("Quadratic", 3);
glutAddMenuEntry("Blend on", 4);
glutAddMenuEntry("Blend off", 5);
glutAddMenuEntry("Threshold 1", 6);
glutAddMenuEntry("Threshold 10", 7);
glutAddMenuEntry("Point smooth on", 8);
glutAddMenuEntry("Point smooth off", 9);
glutAddMenuEntry("Point size 16", 10);
glutAddMenuEntry("Point size 32", 11);
glutAddMenuEntry("Point size 64", 12);
glutAddMenuEntry("Toggle spin", 13);
glutAddMenuEntry("200 points ", 14);
glutAddMenuEntry("500 points ", 15);
glutAddMenuEntry("1000 points ", 16);
glutAddMenuEntry("2000 points ", 17);
glutAddMenuEntry("Quit", 666);
glutAttachMenu(GLUT_RIGHT_BUTTON);
makePointList();
makeSprite();
glShadeModel(GL_FLAT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_POINT_SMOOTH);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPointSize(32.0);
#ifdef GL_ARB_point_parameters
glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, theQuad);
#endif
glutMainLoop();
return 0; /* ANSI C requires main to return int. */
}