diff options
Diffstat (limited to 'examples/rave.c')
| -rw-r--r-- | examples/rave.c | 1153 |
1 files changed, 1153 insertions, 0 deletions
diff --git a/examples/rave.c b/examples/rave.c new file mode 100644 index 0000000..98f30e5 --- /dev/null +++ b/examples/rave.c @@ -0,0 +1,1153 @@ +/* + * rave - a verite-gl megademo + * + * Scenes: + * 1 starfield + flying cubes + * 2 wireframe objects + * 3 vectorballs + * 4 glenz + * 5 rubber twister + * 6 plasma + * 7 shadebobs + * + * Build: + * cc -O2 -I$HOME/mesa/include -I$HOME -o rave rave.c \ + * $HOME/mesa/lib/libGL.a -lm + * Run: VERITE_UCODE=$HOME/v2000gl.uc \ + * ./rave [seconds] (0/absent = loop until ^C) + */ +#include <sys/time.h> +#include <math.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +#include <GL/gl.h> +#include <GL/vrmesa.h> + +#define SW 640 +#define SH 480 + +/* texture object ids */ +#define TEX_CHECK 1 /* orange/white checker (cubes) */ +#define TEX_SPHERE 2 /* radial shaded sphere, binary alpha */ +#define TEX_BLOB 3 /* soft additive blob (shadebobs) */ +#define TEX_ACCUM 5 /* 256^2 shadebobs accumulation buffer */ + +#define BPM 132.0f +#define T_FADE 0.7f /* cross-fade ramp length, seconds */ +#define PI 3.14159265358979f +#define TWO_PI 6.28318530717959f + +static volatile sig_atomic_t stop; + +static void +onint(int sig) +{ + + (void)sig; + stop = 1; +} + +/* shared helpers */ + +static struct timeval tstart; + +static double +now(void) +{ + struct timeval t; + + gettimeofday(&t, NULL); + return (t.tv_sec - tstart.tv_sec) + (t.tv_usec - tstart.tv_usec) / 1e6; +} + +static float +frand(float a, float b) +{ + + return a + (b - a) * (rand() / (float)RAND_MAX); +} + +/* + * Sine lookup table. + */ +#define SIN_N 1024 +static float sintab[SIN_N + 1]; /* +1 guard for interp */ + +static void +sin_init(void) +{ + int i; + + for (i = 0; i <= SIN_N; i++) + sintab[i] = sinf(TWO_PI * i / SIN_N); +} + +static float +fast_sin(float x) +{ + float idx = x * (SIN_N / TWO_PI); + long i = (long)idx; + float frac = idx - (float)i; + unsigned k; + + if (frac < 0.0f) { /* floor for negative x */ + frac += 1.0f; + i--; + } + k = (unsigned)i & (SIN_N - 1); + return sintab[k] + (sintab[k + 1] - sintab[k]) * frac; +} + +static float +fast_cos(float x) +{ + + return fast_sin(x + TWO_PI * 0.25f); +} + +/* + * Cheap atan2 + */ +static float +fast_atan2(float y, float x) +{ + float ax = fabsf(x), ay = fabsf(y); + float a, s, r; + + if (ax < 1e-6f && ay < 1e-6f) + return 0.0f; + a = (ax > ay) ? ay / ax : ax / ay; /* |slope| in [0,1] */ + s = a * a; + r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a; + if (ay > ax) + r = 1.57079637f - r; /* pi/2 - r */ + if (x < 0.0f) + r = 3.14159274f - r; /* pi - r */ + if (y < 0.0f) + r = -r; + return r; +} + +/* rainbow */ +static void +color_from_angle(float h, GLfloat *rgb) +{ + + rgb[0] = 0.5f + 0.5f * fast_sin(h); + rgb[1] = 0.5f + 0.5f * fast_sin(h + 2.0944f); /* +120 deg */ + rgb[2] = 0.5f + 0.5f * fast_sin(h + 4.1888f); /* +240 deg */ +} + +static float +beat_pulse(float t) +{ + float p = fmodf(t * (BPM / 60.0f), 1.0f); + + return 0.5f + 0.5f * fast_cos(TWO_PI * p); +} + +/* procedural textures */ + +static void +make_checker_tex(GLuint id, int n) +{ + static GLubyte tex[64 * 64 * 4]; + int x, y; + + for (y = 0; y < n; y++) + for (x = 0; x < n; x++) { + GLubyte *p = &tex[(y * n + x) * 4]; + int border = x < 2 || y < 2 || x >= n - 2 || y >= n - 2; + int check = ((x / 8 + y / 8) & 1); + + p[0] = border ? 255 : check ? 200 : 255; + p[1] = border ? 255 : check ? 60 : 160; + p[2] = border ? 255 : check ? 40 : 30; + p[3] = 255; + } + glBindTexture(GL_TEXTURE_2D, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, 3, n, n, 0, GL_RGBA, + GL_UNSIGNED_BYTE, tex); +} + +static void +make_sphere_tex(GLuint id, int n) +{ + static GLubyte tex[64 * 64 * 4]; + const float lx = -0.406f, ly = 0.507f, lz = 0.761f; /* light dir */ + int x, y; + + for (y = 0; y < n; y++) + for (x = 0; x < n; x++) { + GLubyte *p = &tex[(y * n + x) * 4]; + float dx = (x + 0.5f) / n * 2.0f - 1.0f; + float dy = (y + 0.5f) / n * 2.0f - 1.0f; + float r2 = dx * dx + dy * dy; + + if (r2 <= 1.0f) { + float nz = sqrtf(1.0f - r2); + float d = dx * lx + dy * ly + nz * lz; + float s; + + if (d < 0.0f) + d = 0.0f; + s = 0.25f + 0.75f * d; /* ambient+diff */ + p[0] = p[1] = p[2] = (GLubyte)(255.0f * s); + p[3] = 255; + } else { + p[0] = p[1] = p[2] = 0; + p[3] = 0; + } + } + glBindTexture(GL_TEXTURE_2D, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, 4, n, n, 0, GL_RGBA, + GL_UNSIGNED_BYTE, tex); +} + +/* soft radial blob, intensity in RGB and A, for GL_ONE/GL_ONE additive */ +static void +make_blob_tex(GLuint id, int n) +{ + static GLubyte tex[64 * 64 * 4]; + int x, y; + + for (y = 0; y < n; y++) + for (x = 0; x < n; x++) { + GLubyte *p = &tex[(y * n + x) * 4]; + float dx = (x + 0.5f) / n * 2.0f - 1.0f; + float dy = (y + 0.5f) / n * 2.0f - 1.0f; + float r2 = dx * dx + dy * dy; + float in = expf(-3.2f * r2); + GLubyte v = (GLubyte)(255.0f * in); + + p[0] = p[1] = p[2] = v; + p[3] = v; + } + glBindTexture(GL_TEXTURE_2D, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, 4, n, n, 0, GL_RGBA, + GL_UNSIGNED_BYTE, tex); +} + +/* shared geometry */ + +static const GLfloat faces[6][4][3] = { + { {-1,-1,-1},{-1, 1,-1},{ 1, 1,-1},{ 1,-1,-1} }, /* -Z */ + { {-1,-1, 1},{ 1,-1, 1},{ 1, 1, 1},{-1, 1, 1} }, /* +Z */ + { {-1,-1,-1},{-1,-1, 1},{-1, 1, 1},{-1, 1,-1} }, /* -X */ + { { 1,-1,-1},{ 1, 1,-1},{ 1, 1, 1},{ 1,-1, 1} }, /* +X */ + { {-1,-1,-1},{ 1,-1,-1},{ 1,-1, 1},{-1,-1, 1} }, /* -Y */ + { {-1, 1,-1},{-1, 1, 1},{ 1, 1, 1},{ 1, 1,-1} }, /* +Y */ +}; +static const GLfloat uv[4][2] = { {0,0},{1,0},{1,1},{0,1} }; + +static const GLfloat octa_v[6][3] = { + { 1, 0, 0 }, {-1, 0, 0 }, { 0, 1, 0 }, + { 0,-1, 0 }, { 0, 0, 1 }, { 0, 0,-1 }, +}; +static const int octa_f[8][3] = { + { 0, 2, 4 }, { 2, 1, 4 }, { 1, 3, 4 }, { 3, 0, 4 }, + { 2, 0, 5 }, { 1, 2, 5 }, { 3, 1, 5 }, { 0, 3, 5 }, +}; + +/* torus (scene 2): verts + an edge (index-pair) list for AA GL_LINES */ +#define TOR_RINGS 12 +#define TOR_SIDES 8 +#define TOR_NV (TOR_RINGS * TOR_SIDES) +#define TOR_NE (TOR_NV * 2) +static GLfloat tor_v[TOR_NV][3]; +static int tor_e[TOR_NE][2]; + +static void +gen_torus(float R, float r) +{ + int i, j, e = 0; + + for (i = 0; i < TOR_RINGS; i++) { + float phi = TWO_PI * i / TOR_RINGS; + + for (j = 0; j < TOR_SIDES; j++) { + float th = TWO_PI * j / TOR_SIDES; + float rr = R + r * cosf(th); + int idx = i * TOR_SIDES + j; + int jn = (j + 1) % TOR_SIDES; + int in = (i + 1) % TOR_RINGS; + + tor_v[idx][0] = rr * cosf(phi); + tor_v[idx][1] = r * sinf(th); + tor_v[idx][2] = rr * sinf(phi); + + tor_e[e][0] = idx; /* around tube */ + tor_e[e][1] = i * TOR_SIDES + jn; + e++; + tor_e[e][0] = idx; /* along tube */ + tor_e[e][1] = in * TOR_SIDES + j; + e++; + } + } +} + +/* baseline GL state + fullscreen fade overlay */ + +static void +scene_reset_gl(void) +{ + + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glDisable(GL_ALPHA_TEST); + glDisable(GL_FOG); + glDisable(GL_POINT_SMOOTH); + glDisable(GL_LINE_SMOOTH); + glDisable(GL_SCISSOR_TEST); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glColorMask(1, 1, 1, 1); + glShadeModel(GL_SMOOTH); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1, 1, 1, 1); + glLineWidth(1); + glPointSize(1); + glViewport(0, 0, SW, SH); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-1.0, 1.0, -0.75, 0.75, 1.0, 50.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +static void +overlay_black(float a) +{ + + if (a <= 0.004f) + return; + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, 1, 0, 1, -1, 1); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_TEXTURE_2D); + glDisable(GL_FOG); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(0, 0, 0, a); + glBegin(GL_QUADS); + glVertex2f(0, 0); + glVertex2f(1, 0); + glVertex2f(1, 1); + glVertex2f(0, 1); + glEnd(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +/* scene 1: starfield + flying spinning cubes */ + +#define NSTAR 220 +#define NCUBE 6 + +static struct { float x, y, z; } star[NSTAR]; +static struct { float dir, z, a, b, av, bv, zv; } cube[NCUBE]; + +static void +init_stars(void) +{ + int i; + + make_checker_tex(TEX_CHECK, 64); + for (i = 0; i < NSTAR; i++) { + star[i].x = frand(-6, 6); + star[i].y = frand(-4.5f, 4.5f); + star[i].z = frand(-40, -1); + } + for (i = 0; i < NCUBE; i++) { + cube[i].dir = frand(0, TWO_PI); + cube[i].z = frand(-42, -6); + cube[i].a = frand(0, 360); + cube[i].b = frand(0, 360); + cube[i].av = frand(30, 70); + cube[i].bv = frand(30, 70); + cube[i].zv = frand(5, 8); + } +} + +static void +draw_stars(float t, float dt) +{ + int i, f, k; + + (void)t; + glClearColor(0.02f, 0.02f, 0.06f, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + /* stars: native AA points, additive-free coverage blend, no depth */ + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_TEXTURE_2D); + glDisable(GL_FOG); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_POINT_SMOOTH); + glPointSize(2); + glBegin(GL_POINTS); + for (i = 0; i < NSTAR; i++) { + float b = 1.0f - (-star[i].z) / 44.0f; + + if (b < 0.05f) + b = 0.05f; + glColor4f(b, b, b, b); + glVertex3f(star[i].x, star[i].y, star[i].z); + } + glEnd(); + for (i = 0; i < NSTAR; i++) { + star[i].z += 13.0f * dt; + if (star[i].z > -0.8f) { + star[i].z = -40; + star[i].x = frand(-6, 6); + star[i].y = frand(-4.5f, 4.5f); + } + } + + /* cubes: textured Gouraud, depth + cull, linear fog for depth cue */ + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDisable(GL_POINT_SMOOTH); + glEnable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, TEX_CHECK); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glEnable(GL_FOG); + glFogi(GL_FOG_MODE, GL_LINEAR); + glFogf(GL_FOG_START, 10); + glFogf(GL_FOG_END, 42); + { + GLfloat fc[4] = { 0.02f, 0.02f, 0.06f, 1 }; + + glFogfv(GL_FOG_COLOR, fc); + } + for (k = 0; k < NCUBE; k++) { + float approach = (cube[k].z + 42.0f) / 41.0f; /* 0 far .. 1 near */ + float rad, ox, oy; + + if (approach < 0.0f) + approach = 0.0f; + /* offset grows ~quadratically as the cube nears, so perspective + * sweeps it out to a screen edge well before it reaches the eye */ + rad = 0.35f + approach * approach * 7.0f; + ox = cosf(cube[k].dir) * rad; + oy = sinf(cube[k].dir) * rad * 0.72f; /* 4:3 vertical squash */ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(ox, oy, cube[k].z); + glRotatef(cube[k].a, 1, 0, 0); + glRotatef(cube[k].b, 0, 1, 0); + glScalef(0.7f, 0.7f, 0.7f); + glBegin(GL_QUADS); + for (f = 0; f < 6; f++) { + GLfloat rgb[3]; + int j; + + color_from_angle(f * 1.0f + k * 1.3f, rgb); + glColor3f(0.5f + 0.5f * rgb[0], 0.5f + 0.5f * rgb[1], + 0.5f + 0.5f * rgb[2]); + for (j = 0; j < 4; j++) { + glTexCoord2f(uv[j][0], uv[j][1]); + glVertex3fv(faces[f][j]); + } + } + glEnd(); + + cube[k].z += cube[k].zv * dt; + cube[k].a += cube[k].av * dt; + cube[k].b += cube[k].bv * dt; + if (cube[k].z > -3.0f) { /* recycle before it looms */ + cube[k].z = -42; + cube[k].dir = frand(0, TWO_PI); + cube[k].a = frand(0, 360); + cube[k].b = frand(0, 360); + } + } + glDisable(GL_TEXTURE_2D); + glDisable(GL_FOG); +} + +/* scene 2: wireframe objects */ + +static void +draw_wire(float t, float dt) +{ + int i; + + (void)dt; + glClearColor(0.03f, 0.02f, 0.05f, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -5.0f); + glRotatef(t * 42.0f, 1, 0.35f, 0); + glRotatef(t * 27.0f, 0, 1, 0.2f); + + if (t < 10.0f) { + /* + * native hardware AA lines. + */ + gen_torus(1.1f, 0.42f + 0.12f * sinf(t * 1.6f)); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_LINE_SMOOTH); + glLineWidth(1); + glBegin(GL_LINES); + for (i = 0; i < TOR_NE; i++) { + int a = tor_e[i][0], b = tor_e[i][1]; + GLfloat rgb[3]; + + color_from_angle(tor_v[a][1] * 3.0f + t, rgb); + glColor3fv(rgb); + glVertex3fv(tor_v[a]); + color_from_angle(tor_v[b][1] * 3.0f + t, rgb); + glColor3fv(rgb); + glVertex3fv(tor_v[b]); + } + glEnd(); + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); + } else { + /* + * the same idea the OTHER hardware way + */ + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glScalef(1.6f, 1.6f, 1.6f); + glBegin(GL_TRIANGLES); + for (i = 0; i < 8; i++) { + GLfloat rgb[3]; + int j; + + color_from_angle(i * 0.8f + t, rgb); + glColor3fv(rgb); + for (j = 0; j < 3; j++) + glVertex3fv(octa_v[octa_f[i][j]]); + } + glEnd(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } +} + +/* scene 3: vectorballs */ + +#define VB_X 16 +#define VB_Y 8 +#define VB_CAMZ 15.0f + +static void +init_vballs(void) +{ + + make_sphere_tex(TEX_SPHERE, 64); +} + +static void +draw_vballs(float t, float dt) +{ + float ry = t * 0.4f, rx = 0.3f * sinf(t * 0.5f); + float cy = cosf(ry), sy = sinf(ry); + float cx = cosf(rx), sx = sinf(rx); + float s = 0.42f * (0.85f + 0.3f * beat_pulse(t)); + int gx, gz; + + (void)dt; + glClearColor(0.03f, 0.04f, 0.08f, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* crisp round balls: alpha-test GREATER + depth, NO blend, NO sort */ + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, TEX_SPHERE); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); /* balls are pre-transformed in eye space */ + glBegin(GL_QUADS); + for (gz = 0; gz < VB_Y; gz++) + for (gx = 0; gx < VB_X; gx++) { + float px = (gx - (VB_X - 1) * 0.5f) * 0.85f; + float pz = (gz - (VB_Y - 1) * 0.5f) * 0.85f; + float py = 1.8f * sinf(px * 0.5f + pz * 0.6f + t * 1.5f); + float x1, y1, z1, x2, y2, z2, cxx, cyy, czz; + GLfloat rgb[3]; + + /* rotate lattice about Y then X on the CPU */ + x1 = px * cy + pz * sy; + z1 = -px * sy + pz * cy; + y1 = py; + x2 = x1; + y2 = y1 * cx - z1 * sx; + z2 = y1 * sx + z1 * cx; + + cxx = x2; + cyy = y2; + czz = z2 - VB_CAMZ; + + color_from_angle(gx * 0.3f + gz * 0.25f, rgb); + glColor3f(0.4f + 0.6f * rgb[0], 0.4f + 0.6f * rgb[1], + 0.4f + 0.6f * rgb[2]); + glTexCoord2f(0, 0); glVertex3f(cxx - s, cyy - s, czz); + glTexCoord2f(1, 0); glVertex3f(cxx + s, cyy - s, czz); + glTexCoord2f(1, 1); glVertex3f(cxx + s, cyy + s, czz); + glTexCoord2f(0, 1); glVertex3f(cxx - s, cyy + s, czz); + } + glEnd(); + glDisable(GL_ALPHA_TEST); + glDisable(GL_TEXTURE_2D); +} + +/* scene 4: glenz */ + +static void +draw_glenz(float t, float dt) +{ + float A = 0.42f + 0.14f * beat_pulse(t); + int f, j; + + (void)dt; + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_TEXTURE_2D); + glShadeModel(GL_FLAT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + /* glenz cube */ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -4.5f); + glRotatef(t * 33.0f, 1, 0, 0); + glRotatef(t * 44.0f, 0, 1, 0); + glBegin(GL_QUADS); + for (f = 0; f < 6; f++) { + GLfloat rgb[3]; + + color_from_angle(f * 1.05f + t * 0.7f, rgb); + glColor4f(rgb[0], rgb[1], rgb[2], A); + for (j = 0; j < 4; j++) + glVertex3fv(faces[f][j]); + } + glEnd(); + + /* counter-rotating glenz octahedron */ + glLoadIdentity(); + glTranslatef(0, 0, -4.5f); + glRotatef(-t * 38.0f, 0, 1, 0); + glRotatef(t * 26.0f, 1, 0, 0); + glScalef(1.35f, 1.35f, 1.35f); + glBegin(GL_TRIANGLES); + for (f = 0; f < 8; f++) { + GLfloat rgb[3]; + + color_from_angle(f * 0.8f + 3.0f + t * 0.9f, rgb); + glColor4f(rgb[0], rgb[1], rgb[2], A); + for (j = 0; j < 3; j++) + glVertex3fv(octa_v[octa_f[f][j]]); + } + glEnd(); + + glDisable(GL_BLEND); + glShadeModel(GL_SMOOTH); +} + +/* scene 5: rubber twister */ + +#define TW_BARS 32 +#define TUN_GX 24 /* tunnel grid columns */ +#define TUN_GY 18 /* tunnel grid rows */ +#define TUN_RMAX 400.0f /* screen half-diagonal (radius norm) */ + +static void +tunnel_color(float rn, float ang, float t, GLfloat *rgb) +{ + float dist = 1.0f / (rn + 0.13f); /* centre reads as far */ + float band = 0.5f + 0.5f * fast_sin(dist * 2.5f - t * 2.5f); + float rip = 0.6f + 0.4f * fast_sin(ang * 6.0f + t * 1.5f); + float v = (0.35f + 0.65f * band) * rip; + + color_from_angle(dist * 0.8f + ang + t * 0.3f, rgb); + rgb[0] *= v; + rgb[1] *= v; + rgb[2] *= v; +} + +static float tun_x[TUN_GX + 1]; +static float tun_y[TUN_GY + 1]; +static float tun_rn[TUN_GY + 1][TUN_GX + 1]; +static float tun_ang[TUN_GY + 1][TUN_GX + 1]; + +static void +tunnel_init(void) +{ + int ix, iy; + + for (ix = 0; ix <= TUN_GX; ix++) + tun_x[ix] = -320.0f + 640.0f * ix / TUN_GX; + for (iy = 0; iy <= TUN_GY; iy++) + tun_y[iy] = -240.0f + 480.0f * iy / TUN_GY; + for (iy = 0; iy <= TUN_GY; iy++) + for (ix = 0; ix <= TUN_GX; ix++) { + float x = tun_x[ix], y = tun_y[iy]; + + tun_rn[iy][ix] = sqrtf(x * x + y * y) / TUN_RMAX; + tun_ang[iy][ix] = fast_atan2(y, x); + } +} + +static void +draw_tunnel(float t) +{ + float rot = t * 0.25f; + int ix, iy; + + for (iy = 0; iy < TUN_GY; iy++) { + glBegin(GL_QUAD_STRIP); + for (ix = 0; ix <= TUN_GX; ix++) { + GLfloat rgb[3]; + + tunnel_color(tun_rn[iy][ix], tun_ang[iy][ix] + rot, + t, rgb); + glColor3fv(rgb); + glVertex2f(tun_x[ix], tun_y[iy]); + tunnel_color(tun_rn[iy + 1][ix], + tun_ang[iy + 1][ix] + rot, t, rgb); + glColor3fv(rgb); + glVertex2f(tun_x[ix], tun_y[iy + 1]); + } + glEnd(); + } +} + +static void +draw_twist(float t, float dt) +{ + int i; + + (void)dt; + glClearColor(0, 0, 0, 1); + glDepthMask(GL_TRUE); /* so the depth buffer clears */ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* --- background: whole-screen Gouraud tunnel (ortho) --- */ + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); /* tunnel must not write depth */ + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glShadeModel(GL_SMOOTH); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-320, 320, -240, 240, -1, 1); /* screen-centred pixels */ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + draw_tunnel(t); + + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); /* facing flips as it twists */ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-1.0, 1.0, -0.75, 0.75, 1.0, 50.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -4.2f); + glRotatef(12.0f * fast_sin(t * 0.3f), 1, 0, 0); + + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= TW_BARS; i++) { + int d = TW_BARS - i; /* 0 at top, grows downward */ + float th = t * 1.4f + d * 0.30f; + float R = 0.5f + 0.25f * fast_sin(t * 1.7f + d * 0.4f); + float y = 1.4f - 2.8f * i / TW_BARS; /* top -> bottom */ + float c = fast_cos(th), s = fast_sin(th); + GLfloat rgb[3]; + + color_from_angle(th, rgb); + glColor3fv(rgb); + glVertex3f(-R * c, y, -R * s); + glVertex3f(R * c, y, R * s); + } + glEnd(); +} + +/* scene 6: plasma (vertex-colour Gouraud mesh) */ + +#define PL_GX 24 +#define PL_GY 18 + +static void +plasma_color(float u, float v, float t, GLfloat *rgb) +{ + float s; + + float dx = u - 0.5f, dy = v - 0.5f; + + s = fast_sin(u * 6.0f + t * 1.3f) + + fast_sin(v * 5.0f - t * 1.1f) + + fast_sin((u + v) * 4.0f + t * 0.7f) + + fast_sin(sqrtf(dx * dx + dy * dy) * 9.0f - t * 1.7f); + rgb[0] = 0.5f + 0.5f * fast_sin(s * 1.05f); + rgb[1] = 0.5f + 0.5f * fast_sin(s * 1.05f + 2.0944f); + rgb[2] = 0.5f + 0.5f * fast_sin(s * 1.05f + 4.1888f); +} + +static void +draw_plasma(float t, float dt) +{ + int x, y; + + (void)dt; + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glShadeModel(GL_SMOOTH); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 1, 0, 1, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + /* one GL_QUAD_STRIP per row halves the glVertex count (900 verts) */ + for (y = 0; y < PL_GY; y++) { + glBegin(GL_QUAD_STRIP); + for (x = 0; x <= PL_GX; x++) { + float u = (float)x / PL_GX; + float v0 = (float)y / PL_GY; + float v1 = (float)(y + 1) / PL_GY; + GLfloat rgb[3]; + + plasma_color(u, v0, t, rgb); + glColor3fv(rgb); + glVertex2f(u, v0); + plasma_color(u, v1, t, rgb); + glColor3fv(rgb); + glVertex2f(u, v1); + } + glEnd(); + } +} + +/* scene 7: shadebobs (persistent accum texture via bitblt) */ + +#define NBOB 6 +#define ACC_N 128 + +static struct { float fx, fy, phx, phy, r, g, b; } bob[NBOB]; + +static void +init_bobs(void) +{ + static GLubyte zero[ACC_N * ACC_N * 3]; /* BSS: already zero */ + int i; + + make_blob_tex(TEX_BLOB, 64); + + /* accum texture: specify + draw once so the copy hits the HW bitblt + * path, not the re-upload fallback (mesaconf.c residency idiom) */ + glBindTexture(GL_TEXTURE_2D, TEX_ACCUM); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, 3, ACC_N, ACC_N, 0, GL_RGB, + GL_UNSIGNED_BYTE, zero); + + glViewport(0, 0, ACC_N, ACC_N); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 1, 0, 1, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glColor3f(1, 1, 1); + glEnable(GL_TEXTURE_2D); + glBegin(GL_QUADS); /* prime residency */ + glTexCoord2f(0, 0); glVertex2f(0, 0); + glTexCoord2f(1, 0); glVertex2f(1, 0); + glTexCoord2f(1, 1); glVertex2f(1, 1); + glTexCoord2f(0, 1); glVertex2f(0, 1); + glEnd(); + glDisable(GL_TEXTURE_2D); + + for (i = 0; i < NBOB; i++) { + GLfloat rgb[3]; + + bob[i].fx = frand(0.5f, 1.7f); + bob[i].fy = frand(0.5f, 1.7f); + bob[i].phx = frand(0, TWO_PI); + bob[i].phy = frand(0, TWO_PI); + color_from_angle(i * 1.05f, rgb); + bob[i].r = rgb[0]; + bob[i].g = rgb[1]; + bob[i].b = rgb[2]; + } +} + +static void +bob_quad(float cx, float cy, float s) +{ + + glTexCoord2f(0, 0); glVertex2f(cx - s, cy - s); + glTexCoord2f(1, 0); glVertex2f(cx + s, cy - s); + glTexCoord2f(1, 1); glVertex2f(cx + s, cy + s); + glTexCoord2f(0, 1); glVertex2f(cx - s, cy + s); +} + +static void +draw_bobs(float t, float dt) +{ + float bp = 0.6f + 0.4f * beat_pulse(t); + int i; + + (void)dt; + + /* ---- pass A: 256^2 scratch -- fade accum, add bobs, copy back ---- */ + glViewport(0, 0, ACC_N, ACC_N); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, ACC_N, 0, ACC_N, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + /* decay: draw accum * 0.92 (no blend) */ + glBindTexture(GL_TEXTURE_2D, TEX_ACCUM); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glColor3f(0.92f, 0.92f, 0.92f); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(0, 0); + glTexCoord2f(1, 0); glVertex2f(ACC_N, 0); + glTexCoord2f(1, 1); glVertex2f(ACC_N, ACC_N); + glTexCoord2f(0, 1); glVertex2f(0, ACC_N); + glEnd(); + + /* new bobs: additive FUNC_ADD ONE/ONE (no alpha-uniformity concern) */ + glBindTexture(GL_TEXTURE_2D, TEX_BLOB); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + glBegin(GL_QUADS); + for (i = 0; i < NBOB; i++) { + float cx = ACC_N * (0.5f + 0.36f * sinf(t * bob[i].fx + bob[i].phx)); + float cy = ACC_N * (0.5f + 0.36f * sinf(t * bob[i].fy + bob[i].phy)); + + glColor3f(bob[i].r * bp * 0.55f, bob[i].g * bp * 0.55f, + bob[i].b * bp * 0.55f); + bob_quad(cx, cy, ACC_N * 0.16f); + } + glEnd(); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + + /* feed the freshly-drawn 256^2 region back into the accum texture */ + glBindTexture(GL_TEXTURE_2D, TEX_ACCUM); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, ACC_N, ACC_N); + + /* ---- pass B: present the accumulation scaled to fullscreen ---- */ + glViewport(0, 0, SW, SH); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 1, 0, 1, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + glBindTexture(GL_TEXTURE_2D, TEX_ACCUM); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDisable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glColor3f(1, 1, 1); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(0, 0); + glTexCoord2f(1, 0); glVertex2f(1, 0); + glTexCoord2f(1, 1); glVertex2f(1, 1); + glTexCoord2f(0, 1); glVertex2f(0, 1); + glEnd(); + glDisable(GL_TEXTURE_2D); +} + +/* scene table + main */ + +struct scene { + const char *name; + void (*init)(void); + void (*render)(float t, float dt); + float dur; +}; + +static const struct scene scenes[] = { + { "starfield", init_stars, draw_stars, 18.0f }, + { "wire", NULL, draw_wire, 16.0f }, + { "vballs", init_vballs, draw_vballs, 18.0f }, + { "glenz", NULL, draw_glenz, 16.0f }, + { "twister", NULL, draw_twist, 16.0f }, + { "plasma", NULL, draw_plasma, 18.0f }, + { "shadebobs", init_bobs, draw_bobs, 20.0f }, +}; +#define NSCENE (int)(sizeof scenes / sizeof scenes[0]) + +static int inited[NSCENE]; + +static void +write_shot(const char *name) +{ + static GLubyte buf[SW * SH * 3]; + char path[128]; + FILE *f; + int y; + + glReadPixels(0, 0, SW, SH, GL_RGB, GL_UNSIGNED_BYTE, buf); + snprintf(path, sizeof path, "/tmp/mesarave_%s.ppm", name); + if ((f = fopen(path, "wb")) == NULL) + return; + fprintf(f, "P6\n%d %d\n255\n", SW, SH); + for (y = SH - 1; y >= 0; y--) /* GL bottom-up -> PPM top-down */ + fwrite(buf + (size_t)y * SW * 3, 1, SW * 3, f); + fclose(f); + printf("shot: %s\n", path); +} + +int +main(int argc, char **argv) +{ + vrMesaContext ctx; + const char *lockenv = getenv("MESARAVE_SCENE"); /* lock 1 scene */ + const char *shotenv = getenv("MESARAVE_SHOT"); /* dump PPM at frame N */ + double cap = (argc > 1) ? atof(argv[1]) : 0.0; + double cycle = 0.0, tlast = 0.0; + int cur = -1, frame = 0, i, lock = -1; + int shot_at = shotenv ? atoi(shotenv) : 0, shotcnt = 0; + + for (i = 0; i < NSCENE; i++) + cycle += scenes[i].dur; + if (lockenv != NULL) { + lock = atoi(lockenv); + if (lock < 0 || lock >= NSCENE) + lock = -1; + } + srand(20260715); + sin_init(); + tunnel_init(); + signal(SIGINT, onint); + + ctx = vrMesaCreateContext(NULL); /* NULL => $VERITE_UCODE */ + if (ctx == NULL) + return 1; + vrMesaMakeCurrent(ctx); + printf("GL_RENDERER: %s (%s)\n", glGetString(GL_RENDERER), + glGetString(GL_VERSION)); + + gettimeofday(&tstart, NULL); + while (!stop) { + double T = now(); + double dt = T - tlast; + int idx; + float st, a = 0.0f, d; + + tlast = T; + if (lock >= 0) { + idx = lock; /* single scene, no cycle/fade */ + st = (float)T; + } else { + double c = fmod(T, cycle), acc = 0.0; + + for (idx = 0; idx < NSCENE; idx++) { + if (c < acc + scenes[idx].dur) + break; + acc += scenes[idx].dur; + } + if (idx >= NSCENE) + idx = NSCENE - 1; + st = (float)(c - acc); + } + + if (idx != cur) { /* scene boundary */ + scene_reset_gl(); + if (!inited[idx] && scenes[idx].init != NULL) { + scenes[idx].init(); + inited[idx] = 1; + } + cur = idx; + } + + if (dt < 0.0 || dt > 0.25) + dt = 1.0 / 60.0; /* clamp wrap/first frame */ + scenes[idx].render(st, (float)dt); + + if (shot_at > 0 && ++shotcnt >= shot_at) { + write_shot(scenes[idx].name); /* grab back buffer, exit */ + break; + } + + /* cross-fade black at each scene's head and tail */ + if (lock < 0) { + d = scenes[idx].dur; + if (st < T_FADE) + a = 1.0f - st / T_FADE; + else if (st > d - T_FADE) + a = (st - (d - T_FADE)) / T_FADE; + } + overlay_black(a); + + vrMesaSwapBuffers(); + if (++frame % 100 == 0) + printf("%s %d frames\n", scenes[cur].name, frame); + if (cap > 0.0 && T >= cap) + break; + } + + printf("mesarave done after %d frames\n", frame); + vrMesaDestroyContext(ctx); + return 0; +} |
