/* * 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 #include #include #include #include #include #include #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; }