diff options
Diffstat (limited to 'examples/teapot.c')
| -rw-r--r-- | examples/teapot.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/examples/teapot.c b/examples/teapot.c new file mode 100644 index 0000000..ff67f95 --- /dev/null +++ b/examples/teapot.c @@ -0,0 +1,288 @@ +/* + * teapot - the Utah teapot, lit and spinning + * + * Build: + * cc -O2 -I../include -o teapot teapot.c ../lib/libGL.a -lm + * + * Run: VERITE_UCODE=$HOME/v2000gl.uc \ + * ./teapot [seconds] (0 = spin until ^C) + * + * Tessellation density is TEAPOT_GRID (default 8)... + */ +#include <sys/time.h> +#include <math.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +#include <GL/gl.h> +#include <GL/vrmesa.h> + +static const GLfloat teapot_patch[32][4][4][3] = { +#include "teapot_data.inc" +}; + +static volatile sig_atomic_t stop; + +static void +onint(int sig) +{ + + (void)sig; + stop = 1; +} + +/* Cubic Bernstein basis and its derivative at t. */ +static void +bernstein(float t, float b[4], float db[4]) +{ + float it = 1.0f - t; + + b[0] = it * it * it; + b[1] = 3.0f * t * it * it; + b[2] = 3.0f * t * t * it; + b[3] = t * t * t; + + db[0] = -3.0f * it * it; + db[1] = 3.0f * it * it - 6.0f * t * it; + db[2] = 6.0f * t * it - 3.0f * t * t; + db[3] = 3.0f * t * t; +} + +/* + * Evaluate patch p at (u,v) + */ +static void +eval_patch(int p, float u, float v, float pos[3], float nrm[3]) +{ + float bu[4], dbu[4], bv[4], dbv[4]; + float du[3], dv[3], len; + int i, j, k; + + if (u < 1e-4f) + u = 1e-4f; + else if (u > 1.0f - 1e-4f) + u = 1.0f - 1e-4f; + if (v < 1e-4f) + v = 1e-4f; + else if (v > 1.0f - 1e-4f) + v = 1.0f - 1e-4f; + + bernstein(u, bu, dbu); + bernstein(v, bv, dbv); + + for (k = 0; k < 3; k++) + pos[k] = du[k] = dv[k] = 0.0f; + + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) { + const GLfloat *cp = teapot_patch[p][i][j]; + float w = bu[i] * bv[j]; + float wu = dbu[i] * bv[j]; + float wv = bu[i] * dbv[j]; + + for (k = 0; k < 3; k++) { + pos[k] += w * cp[k]; + du[k] += wu * cp[k]; + dv[k] += wv * cp[k]; + } + } + + /* normal = du x dv (points outward across the whole teaset) */ + nrm[0] = du[1] * dv[2] - du[2] * dv[1]; + nrm[1] = du[2] * dv[0] - du[0] * dv[2]; + nrm[2] = du[0] * dv[1] - du[1] * dv[0]; + len = sqrtf(nrm[0] * nrm[0] + nrm[1] * nrm[1] + nrm[2] * nrm[2]); + if (len > 1e-8f) { + nrm[0] /= len; + nrm[1] /= len; + nrm[2] /= len; + } else { + nrm[0] = nrm[1] = 0.0f; + nrm[2] = 1.0f; + } +} + +struct vtx { + GLfloat p[3]; + GLfloat n[3]; +}; + +static int grid; /* steps per patch edge */ +static int row; /* vertices per patch edge = grid + 1 */ +static struct vtx *mesh; /* [32][row*row], contiguous */ + +static void +build_mesh(void) +{ + GLfloat lo[3], hi[3]; + GLfloat cx, cy, cz, dim, m, gscale; + int p, a, b, k, n, nverts; + + row = grid + 1; + nverts = 32 * row * row; + mesh = malloc((size_t)nverts * sizeof(*mesh)); + if (mesh == NULL) { + perror("malloc"); + exit(1); + } + + for (k = 0; k < 3; k++) { + lo[k] = 1e30f; + hi[k] = -1e30f; + } + + for (p = 0; p < 32; p++) + for (a = 0; a < row; a++) + for (b = 0; b < row; b++) { + struct vtx *vp = &mesh[(p * row + a) * row + b]; + + eval_patch(p, (float)a / grid, (float)b / grid, + vp->p, vp->n); + for (k = 0; k < 3; k++) { + if (vp->p[k] < lo[k]) + lo[k] = vp->p[k]; + if (vp->p[k] > hi[k]) + hi[k] = vp->p[k]; + } + } + + /* Bake centre + uniform scale into the vertices so the modelview needs + * no scale and the unit normals need no GL_NORMALIZE. */ + cx = 0.5f * (lo[0] + hi[0]); + cy = 0.5f * (lo[1] + hi[1]); + cz = 0.5f * (lo[2] + hi[2]); + dim = hi[0] - lo[0]; + m = hi[1] - lo[1]; + if (m > dim) + dim = m; + m = hi[2] - lo[2]; + if (m > dim) + dim = m; + gscale = 3.0f / dim; + for (n = 0; n < nverts; n++) { + mesh[n].p[0] = (mesh[n].p[0] - cx) * gscale; + mesh[n].p[1] = (mesh[n].p[1] - cy) * gscale; + mesh[n].p[2] = (mesh[n].p[2] - cz) * gscale; + } +} + +static void +draw_teapot(void) +{ + int p, a, b; + + for (p = 0; p < 32; p++) + for (a = 0; a < grid; a++) { + const struct vtx *r0 = &mesh[(p * row + a) * row]; + const struct vtx *r1 = &mesh[(p * row + a + 1) * row]; + + glBegin(GL_TRIANGLE_STRIP); + for (b = 0; b < row; b++) { + glNormal3fv(r0[b].n); + glVertex3fv(r0[b].p); + glNormal3fv(r1[b].n); + glVertex3fv(r1[b].p); + } + glEnd(); + } +} + +int +main(int argc, char **argv) +{ + static const GLfloat lightpos[4] = { -0.4f, 0.7f, 1.0f, 0.0f }; + static const GLfloat lightamb[4] = { 0.15f, 0.15f, 0.18f, 1.0f }; + static const GLfloat lightdif[4] = { 0.9f, 0.9f, 0.85f, 1.0f }; + static const GLfloat lightspc[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + static const GLfloat matdif[4] = { 0.75f, 0.42f, 0.16f, 1.0f }; + static const GLfloat matspc[4] = { 0.85f, 0.85f, 0.75f, 1.0f }; + static const GLfloat matamb[4] = { 0.25f, 0.14f, 0.06f, 1.0f }; + vrMesaContext ctx; + struct timeval t0, t1, tstart; + const char *genv = getenv("TEAPOT_GRID"); + double secs = 30.0; + float spin = 0.0f; + int frame = 0; + + if (argc > 1) + secs = atof(argv[1]); + grid = genv != NULL ? atoi(genv) : 8; + if (grid < 2) + grid = 2; + else if (grid > 40) + grid = 40; + signal(SIGINT, onint); + + build_mesh(); + + ctx = vrMesaCreateContext(NULL); + if (ctx == NULL) + return 1; + vrMesaMakeCurrent(ctx); + printf("GL_RENDERER: %s (%s)\n", glGetString(GL_RENDERER), + glGetString(GL_VERSION)); + printf("teapot: 32 patches, grid %d -> %d triangles\n", + grid, 32 * grid * grid * 2); + + glEnable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); + /* cross(du,dv) points outward across the whole teaset, so single-sided + * lighting shades the exterior correctly and stays on the hardware + * triangle path (two-sided lighting would force a swrast fallback). + * Back faces fall behind the depth buffer, so culling is unnecessary. */ + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT0, GL_AMBIENT, lightamb); + glLightfv(GL_LIGHT0, GL_DIFFUSE, lightdif); + glLightfv(GL_LIGHT0, GL_SPECULAR, lightspc); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, matamb); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, matdif); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, matspc); + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 32.0f); + + glClearColor(0.07f, 0.08f, 0.11f, 1.0f); + glViewport(0, 0, 640, 480); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-1.0, 1.0, -0.75, 0.75, 2.0, 80.0); + + gettimeofday(&tstart, NULL); + t0 = tstart; + while (!stop) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.0f, -0.3f, -7.0f); + /* Fixed light, pinned in eye space before any model rotation. */ + glLightfv(GL_LIGHT0, GL_POSITION, lightpos); + /* Stand the Z-up model upright (-90) and look slightly down (+15). */ + glRotatef(-75.0f, 1.0f, 0.0f, 0.0f); + glRotatef(spin, 0.0f, 0.0f, 1.0f); /* spin about the model axis */ + + draw_teapot(); + + vrMesaSwapBuffers(); + spin += 1.3f; + if (++frame % 100 == 0) { + double dt; + + gettimeofday(&t1, NULL); + dt = (t1.tv_sec - t0.tv_sec) + + (t1.tv_usec - t0.tv_usec) / 1e6; + printf("%d frames, %.1f fps\n", frame, 100.0 / dt); + t0 = t1; + } + if (secs > 0) { + gettimeofday(&t1, NULL); + if ((t1.tv_sec - tstart.tv_sec) + + (t1.tv_usec - tstart.tv_usec) / 1e6 >= secs) + break; + } + } + + printf("teapot done after %d frames\n", frame); + vrMesaDestroyContext(ctx); + free(mesh); + return 0; +} |
