#include <string.h>
#include <math.h>
+#include "config.h"
#include "glext.h"
#include "part.h"
#include "vec3.h"
/*---------------------------------------------------------------------------*/
-struct part
+struct part_vary
{
- float t;
- float a;
- float w;
- float c[3];
- float p[3];
- float v[3];
+ GLfloat v[3]; /* Velocity */
};
-static struct part part_coin[PART_MAX_COIN];
-static struct part part_goal[PART_MAX_GOAL];
-static struct part part_jump[PART_MAX_JUMP];
-static GLuint part_text_star;
-static GLuint part_text_squiggle;
-static GLuint part_list;
+struct part_draw
+{
+ GLfloat p[3]; /* Position */
+ GLfloat c[3]; /* Color */
+ GLfloat t; /* Time until death. Doubles as opacity. */
+};
+
+static struct part_vary coin_vary[PART_MAX_COIN];
+static struct part_draw coin_draw[PART_MAX_COIN];
-static float goal_height;
-static float jump_height;
+static GLuint part_text_star;
+static GLuint coin_vbo;
/*---------------------------------------------------------------------------*/
};
static struct part_lerp part_lerp_coin[PART_MAX_COIN];
-static struct part_lerp part_lerp_goal[PART_MAX_GOAL];
-static struct part_lerp part_lerp_jump[PART_MAX_JUMP];
void part_lerp_copy(void)
{
int i;
for (i = 0; i < PART_MAX_COIN; i++)
- v_cpy(part_lerp_coin[i].p[PREV], part_lerp_coin[i].p[CURR]);
-
- for (i = 0; i < PART_MAX_GOAL; i++)
- v_cpy(part_lerp_goal[i].p[PREV], part_lerp_goal[i].p[CURR]);
-
- for (i = 0; i < PART_MAX_JUMP; i++)
- v_cpy(part_lerp_jump[i].p[PREV], part_lerp_jump[i].p[CURR]);
+ v_cpy(part_lerp_coin[i].p[PREV],
+ part_lerp_coin[i].p[CURR]);
}
void part_lerp_init(void)
{
- int i;
-
- for (i = 0; i < PART_MAX_GOAL; i++)
- if (part_goal[i].t > 0.0f)
- {
- v_cpy(part_lerp_goal[i].p[PREV], part_goal[i].p);
- v_cpy(part_lerp_goal[i].p[CURR], part_goal[i].p);
- }
-
- for (i = 0; i < PART_MAX_JUMP; i++)
- if (part_jump[i].t > 0.0f)
- {
- v_cpy(part_lerp_jump[i].p[PREV], part_jump[i].p);
- v_cpy(part_lerp_jump[i].p[CURR], part_jump[i].p);
- }
}
void part_lerp_burst(int i)
{
- if (part_coin[i].t >= 1.0f)
+ if (coin_draw[i].t >= 1.0f)
{
- v_cpy(part_lerp_coin[i].p[PREV], part_coin[i].p);
- v_cpy(part_lerp_coin[i].p[CURR], part_coin[i].p);
+ v_cpy(part_lerp_coin[i].p[PREV], coin_draw[i].p);
+ v_cpy(part_lerp_coin[i].p[CURR], coin_draw[i].p);
}
}
int i;
for (i = 0; i < PART_MAX_COIN; i++)
- if (part_coin[i].t > 0.0f)
- {
- v_lerp(part_coin[i].p,
+ if (coin_draw[i].t > 0.0f)
+ v_lerp(coin_draw[i].p,
part_lerp_coin[i].p[PREV],
part_lerp_coin[i].p[CURR], a);
- }
-
- for (i = 0; i < PART_MAX_GOAL; i++)
- if (part_goal[i].t > 0.0f)
- {
- v_lerp(part_goal[i].p,
- part_lerp_goal[i].p[PREV],
- part_lerp_goal[i].p[CURR], a);
- }
-
- for (i = 0; i < PART_MAX_GOAL; i++)
- if (part_jump[i].t > 0.0f)
- {
- v_lerp(part_jump[i].p,
- part_lerp_jump[i].p[PREV],
- part_lerp_jump[i].p[CURR], a);
- }
}
/*---------------------------------------------------------------------------*/
-void part_reset(float zh, float jh)
+void part_reset(void)
{
int i;
- goal_height = zh;
- jump_height = jh;
-
for (i = 0; i < PART_MAX_COIN; i++)
- part_coin[i].t = 0.0f;
-
- for (i = 0; i < PART_MAX_GOAL; i++)
- {
- float t = rnd(+0.1f, +1.0f);
- float a = rnd(-1.0f * PI, +1.0f * PI);
- float w = rnd(-2.0f * PI, +2.0f * PI);
-
- part_goal[i].t = t;
- part_goal[i].a = V_DEG(a);
- part_goal[i].w = V_DEG(w);
-
- part_goal[i].c[0] = 1.0f;
- part_goal[i].c[1] = 1.0f;
- part_goal[i].c[2] = 0.0f;
-
- part_goal[i].p[0] = fsinf(a);
- part_goal[i].p[1] = (1.f - t) * goal_height;
- part_goal[i].p[2] = fcosf(a);
-
- part_goal[i].v[0] = 0.f;
- part_goal[i].v[1] = 0.f;
- part_goal[i].v[2] = 0.f;
- }
-
- for (i = 0; i < PART_MAX_JUMP; i++)
- {
- float t = rnd(+0.1f, +1.0f);
- float a = rnd(-1.0f * PI, +1.0f * PI);
- float w = rnd(+0.5f, +2.5f);
-
- float vy = rnd(+0.025f, +0.25f);
-
- part_jump[i].t = t;
- part_jump[i].a = V_DEG(a);
- part_jump[i].w = w;
-
- part_jump[i].c[0] = 1.0f;
- part_jump[i].c[1] = 1.0f;
- part_jump[i].c[2] = 1.0f;
-
- part_jump[i].p[0] = fsinf(a);
- part_jump[i].p[1] = (1.f - t) * jump_height;
- part_jump[i].p[2] = fcosf(a);
-
- part_jump[i].v[0] = 0.f;
- part_jump[i].v[1] = vy;
- part_jump[i].v[2] = 0.f;
- }
+ coin_draw[i].t = 0.0f;
part_lerp_init();
}
-void part_init(float zh, float jh)
+void part_init(void)
{
- memset(part_coin, 0, PART_MAX_COIN * sizeof (struct part));
- memset(part_goal, 0, PART_MAX_GOAL * sizeof (struct part));
- memset(part_jump, 0, PART_MAX_JUMP * sizeof (struct part));
+ part_text_star = make_image_from_file(IMG_PART_STAR);
- part_text_star = make_image_from_file(IMG_PART_STAR);
- part_text_squiggle = make_image_from_file(IMG_PART_SQUIGGLE);
+ memset(coin_vary, 0, PART_MAX_COIN * sizeof (struct part_vary));
+ memset(coin_draw, 0, PART_MAX_COIN * sizeof (struct part_draw));
- part_list = glGenLists(1);
+ glGenBuffers(1, &coin_vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, coin_vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof (coin_draw),
+ coin_draw, GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
- glNewList(part_list, GL_COMPILE);
- {
- glBegin(GL_QUADS);
- {
- glTexCoord2f(0.f, 0.f);
- glVertex2f(-PART_SIZE, -PART_SIZE);
-
- glTexCoord2f(1.f, 0.f);
- glVertex2f(+PART_SIZE, -PART_SIZE);
-
- glTexCoord2f(1.f, 1.f);
- glVertex2f(+PART_SIZE, +PART_SIZE);
-
- glTexCoord2f(0.f, 1.f);
- glVertex2f(-PART_SIZE, +PART_SIZE);
- }
- glEnd();
- }
- glEndList();
-
- part_reset(zh, jh);
+ part_reset();
}
void part_free(void)
{
- if (glIsList(part_list))
- glDeleteLists(part_list, 1);
+ if (glIsBuffer(coin_vbo))
+ glDeleteBuffers(1, &coin_vbo);
if (glIsTexture(part_text_star))
glDeleteTextures(1, &part_text_star);
-
- if (glIsTexture(part_text_squiggle))
- glDeleteTextures(1, &part_text_squiggle);
}
/*---------------------------------------------------------------------------*/
int i, n = 0;
for (i = 0; n < 10 && i < PART_MAX_COIN; i++)
- if (part_coin[i].t <= 0.f)
+ if (coin_draw[i].t <= 0.f)
{
float a = rnd(-1.0f * PI, +1.0f * PI);
float b = rnd(+0.3f * PI, +0.5f * PI);
- float w = rnd(-4.0f * PI, +4.0f * PI);
- part_coin[i].p[0] = p[0];
- part_coin[i].p[1] = p[1];
- part_coin[i].p[2] = p[2];
+ coin_draw[i].c[0] = c[0];
+ coin_draw[i].c[1] = c[1];
+ coin_draw[i].c[2] = c[2];
- part_coin[i].v[0] = 4.f * fcosf(a) * fcosf(b);
- part_coin[i].v[1] = 4.f * fsinf(b);
- part_coin[i].v[2] = 4.f * fsinf(a) * fcosf(b);
+ coin_draw[i].p[0] = p[0];
+ coin_draw[i].p[1] = p[1];
+ coin_draw[i].p[2] = p[2];
- part_coin[i].c[0] = c[0];
- part_coin[i].c[1] = c[1];
- part_coin[i].c[2] = c[2];
+ coin_vary[i].v[0] = 4.f * fcosf(a) * fcosf(b);
+ coin_vary[i].v[1] = 4.f * fsinf(b);
+ coin_vary[i].v[2] = 4.f * fsinf(a) * fcosf(b);
- part_coin[i].t = 1.f;
- part_coin[i].a = 0.f;
- part_coin[i].w = V_DEG(w);
+ coin_draw[i].t = 1.f;
part_lerp_burst(i);
/*---------------------------------------------------------------------------*/
static void part_fall(struct part_lerp *lerp,
- struct part *part, int n,
- const float *g, float dt)
+ struct part_vary *vary,
+ struct part_draw *draw,
+ int n, const float *g, float dt)
{
int i;
for (i = 0; i < n; i++)
- if (part[i].t > 0.f)
+ if (draw[i].t > 0.f)
{
- part[i].t -= dt;
-
- v_mad(part[i].v, part[i].v, g, dt);
-
- v_mad(lerp[i].p[CURR], lerp[i].p[CURR], part[i].v, dt);
- }
-}
-
-static void part_spin(struct part_lerp *lerp,
- struct part *part, int n,
- const float *g, float dt)
-{
- int i;
+ draw[i].t -= dt;
- for (i = 0; i < n; i++)
- if (part[i].t > 0.f)
- {
- part[i].a += 30.f * dt;
+ v_mad(vary[i].v, vary[i].v, g, dt);
- lerp[i].p[CURR][0] = fsinf(V_RAD(part[i].a));
- lerp[i].p[CURR][2] = fcosf(V_RAD(part[i].a));
+ v_mad(lerp[i].p[CURR], lerp[i].p[CURR], vary[i].v, dt);
}
+ else draw[i].t = 0.0f;
}
void part_step(const float *g, float dt)
{
- int i;
-
part_lerp_copy();
-
- part_fall(part_lerp_coin, part_coin, PART_MAX_COIN, g, dt);
-
- if (g[1] > 0.f)
- part_fall(part_lerp_goal, part_goal, PART_MAX_GOAL, g, dt);
- else
- part_spin(part_lerp_goal, part_goal, PART_MAX_GOAL, g, dt);
-
- for (i = 0; i < PART_MAX_JUMP; i++)
- {
- part_lerp_jump[i].p[CURR][1] += part_jump[i].v[1] * dt;
-
- if (part_lerp_jump[i].p[PREV][1] > jump_height)
- {
- part_lerp_jump[i].p[PREV][1] = 0.0f;
- part_lerp_jump[i].p[CURR][1] = 0.0f;
- }
- }
+ part_fall(part_lerp_coin, coin_vary, coin_draw, PART_MAX_COIN, g, dt);
}
/*---------------------------------------------------------------------------*/
-static void part_draw(const float *M,
- const float *p, float r, float rz, float s)
-{
- glPushMatrix();
- {
- glTranslatef(r * p[0], p[1], r * p[2]);
- glMultMatrixf(M);
- glRotatef(rz, 0.f, 0.f, 1.f);
- glScalef(s, s, 1.0f);
-
- glCallList(part_list);
- }
- glPopMatrix();
-}
-
void part_draw_coin(const float *M, float t)
{
- int i;
+ const GLfloat c[3] = { 0.0f, 1.0f, 0.0f };
+ GLint s = config_get_d(CONFIG_HEIGHT) / 8;
glBindTexture(GL_TEXTURE_2D, part_text_star);
- for (i = 0; i < PART_MAX_COIN; i++)
- if (part_coin[i].t > 0.f)
- {
- glColor4f(part_coin[i].c[0],
- part_coin[i].c[1],
- part_coin[i].c[2],
- part_coin[i].t);
-
- part_draw(M, part_coin[i].p, 1.0f, t * part_coin[i].w, 1.0f);
- }
-
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
-}
-
-void part_draw_goal(const float *M, float radius, float a, float t)
-{
- int i;
-
- glBindTexture(GL_TEXTURE_2D, part_text_star);
+ /* Upload the current state of the particles. It would be best to limit */
+ /* this upload to only active particles, but it's more important to do */
+ /* it all in a single call. */
- glColor4f(1.0f, 1.0f, 0.0f, a);
+ glBindBuffer (GL_ARRAY_BUFFER, coin_vbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof (coin_draw), coin_draw);
- for (i = 0; i < PART_MAX_GOAL; i++)
- if (part_goal[i].t > 0.0f)
- part_draw(M, part_goal[i].p, radius - 0.05f,
- t * part_goal[i].w, 1.0f);
+ /* Draw the entire buffer. Dead particles have zero opacity anyway. */
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
-}
-
-void part_draw_jump(const float *M, float radius, float a, float t)
-{
- int i;
-
- glBindTexture(GL_TEXTURE_2D, part_text_squiggle);
-
- for (i = 0; i < PART_MAX_JUMP; i++)
+ glEnableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
{
- glColor4f(part_jump[i].c[0],
- part_jump[i].c[1],
- part_jump[i].c[2],
- 1.0f - part_jump[i].p[1] / jump_height);
-
- /*
- * X is the current time since some Epoch, Y is the time it
- * takes for a squiggle to grow to its full size and then
- * shrink again. F is the current scale of the squiggle in
- * the interval [0.0, 1.0].
- */
-
-#define F(x, y) fabsf(fcosf(((x) / (y)) * PI))
+ glColorPointer (4, GL_FLOAT, sizeof (struct part_draw),
+ (GLvoid *) offsetof (struct part_draw, c));
+ glVertexPointer(3, GL_FLOAT, sizeof (struct part_draw),
+ (GLvoid *) offsetof (struct part_draw, p));
- part_draw(M, part_jump[i].p, radius - 0.05f,
- 0.0f, F(t, part_jump[i].w));
+ glEnable(GL_POINT_SPRITE);
+ {
+ glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
+ glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, c);
+ glPointSize(s);
-#undef F
+ glDrawArrays(GL_POINTS, 0, PART_MAX_COIN);
+ }
+ glDisable(GL_POINT_SPRITE);
}
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}