/* * Copyright (C) 2009 Chia-I Wu * * Based on eglgears by * Copyright (C) 1999-2001 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include "winsys.h" #ifndef M_PI #define M_PI 3.14159265 #endif struct gear { GLuint vbo; GLfloat *vertices; GLsizei stride; GLint num_teeth; }; static GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0; static struct gear gears[3]; static GLfloat angle = 0.0; /* * Initialize a gear wheel. * * Input: gear - gear to initialize * inner_radius - radius of hole at center * outer_radius - radius at center of teeth * width - width of gear * teeth - number of teeth * tooth_depth - depth of tooth */ static void init_gear(struct gear *gear, GLfloat inner_radius, GLfloat outer_radius, GLfloat width, GLint teeth, GLfloat tooth_depth) { GLfloat r0, r1, r2; GLfloat a0, da; GLint verts_per_tooth, total_verts, total_size; GLint count, i; GLfloat *verts; r0 = inner_radius; r1 = outer_radius - tooth_depth / 2.0; r2 = outer_radius + tooth_depth / 2.0; a0 = 2.0 * M_PI / teeth; da = a0 / 4.0; gear->vbo = 0; gear->vertices = NULL; gear->stride = sizeof(GLfloat) * 6; /* XYZ + normal */ gear->num_teeth = teeth; verts_per_tooth = 10 + 4; total_verts = teeth * verts_per_tooth; total_size = total_verts * gear->stride; verts = malloc(total_size); if (!verts) { printf("failed to allocate vertices\n"); return; } #define GEAR_VERT(r, n, sign) \ do { \ verts[count * 6 + 0] = (r) * vx[n]; \ verts[count * 6 + 1] = (r) * vy[n]; \ verts[count * 6 + 2] = (sign) * width * 0.5; \ verts[count * 6 + 3] = normal[0]; \ verts[count * 6 + 4] = normal[1]; \ verts[count * 6 + 5] = normal[2]; \ count++; \ } while (0) count = 0; for (i = 0; i < teeth; i++) { GLfloat normal[3]; GLfloat vx[5], vy[5]; GLfloat u, v; normal[0] = 0.0; normal[1] = 0.0; normal[2] = 0.0; vx[0] = cos(i * a0 + 0 * da); vy[0] = sin(i * a0 + 0 * da); vx[1] = cos(i * a0 + 1 * da); vy[1] = sin(i * a0 + 1 * da); vx[2] = cos(i * a0 + 2 * da); vy[2] = sin(i * a0 + 2 * da); vx[3] = cos(i * a0 + 3 * da); vy[3] = sin(i * a0 + 3 * da); vx[4] = cos(i * a0 + 4 * da); vy[4] = sin(i * a0 + 4 * da); /* outward faces of a tooth, 10 verts */ normal[0] = vx[0]; normal[1] = vy[0]; GEAR_VERT(r1, 0, 1); GEAR_VERT(r1, 0, -1); u = r2 * vx[1] - r1 * vx[0]; v = r2 * vy[1] - r1 * vy[0]; normal[0] = v; normal[1] = -u; GEAR_VERT(r2, 1, 1); GEAR_VERT(r2, 1, -1); normal[0] = vx[0]; normal[1] = vy[0]; GEAR_VERT(r2, 2, 1); GEAR_VERT(r2, 2, -1); u = r1 * vx[3] - r2 * vx[2]; v = r1 * vy[3] - r2 * vy[2]; normal[0] = v; normal[1] = -u; GEAR_VERT(r1, 3, 1); GEAR_VERT(r1, 3, -1); normal[0] = vx[0]; normal[1] = vy[0]; GEAR_VERT(r1, 4, 1); GEAR_VERT(r1, 4, -1); /* inside radius cylinder, 4 verts */ normal[0] = -vx[4]; normal[1] = -vy[4]; GEAR_VERT(r0, 4, 1); GEAR_VERT(r0, 4, -1); normal[0] = -vx[0]; normal[1] = -vy[0]; GEAR_VERT(r0, 0, 1); GEAR_VERT(r0, 0, -1); assert(count % verts_per_tooth == 0); } assert(count == total_verts); #undef GEAR_VERT gear->vertices = verts; /* setup VBO */ glGenBuffers(1, &gear->vbo); if (gear->vbo) { glBindBuffer(GL_ARRAY_BUFFER, gear->vbo); glBufferData(GL_ARRAY_BUFFER, total_size, verts, GL_STATIC_DRAW); } } static void draw_gear(const struct gear *gear) { GLint i; if (!gear->vbo && !gear->vertices) { printf("nothing to be drawn\n"); return; } if (gear->vbo) { glBindBuffer(GL_ARRAY_BUFFER, gear->vbo); glVertexPointer(3, GL_FLOAT, gear->stride, (const GLvoid *) 0); glNormalPointer(GL_FLOAT, gear->stride, (const GLvoid *) (sizeof(GLfloat) * 3)); } else { glBindBuffer(GL_ARRAY_BUFFER, 0); glVertexPointer(3, GL_FLOAT, gear->stride, gear->vertices); glNormalPointer(GL_FLOAT, gear->stride, gear->vertices + 3); } glEnableClientState(GL_VERTEX_ARRAY); for (i = 0; i < gear->num_teeth; i++) { const GLint base = (10 + 4) * i; GLushort indices[7]; glShadeModel(GL_FLAT); /* front face */ indices[0] = base + 12; indices[1] = base + 0; indices[2] = base + 2; indices[3] = base + 4; indices[4] = base + 6; indices[5] = base + 8; indices[6] = base + 10; glNormal3f(0.0, 0.0, 1.0); glDrawElements(GL_TRIANGLE_FAN, 7, GL_UNSIGNED_SHORT, indices); /* back face */ indices[0] = base + 13; indices[1] = base + 11; indices[2] = base + 9; indices[3] = base + 7; indices[4] = base + 5; indices[5] = base + 3; indices[6] = base + 1; glNormal3f(0.0, 0.0, -1.0); glDrawElements(GL_TRIANGLE_FAN, 7, GL_UNSIGNED_SHORT, indices); glEnableClientState(GL_NORMAL_ARRAY); /* outward face of a tooth */ glDrawArrays(GL_TRIANGLE_STRIP, base, 10); /* inside radius cylinder */ glShadeModel(GL_SMOOTH); glDrawArrays(GL_TRIANGLE_STRIP, base + 10, 4); glDisableClientState(GL_NORMAL_ARRAY); } glDisableClientState(GL_VERTEX_ARRAY); } static void gears_draw(void *data) { static const GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 }; static const GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 }; static const GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 }; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glRotatef(view_rotx, 1.0, 0.0, 0.0); glRotatef(view_roty, 0.0, 1.0, 0.0); glRotatef(view_rotz, 0.0, 0.0, 1.0); glPushMatrix(); glTranslatef(-3.0, -2.0, 0.0); glRotatef(angle, 0.0, 0.0, 1.0); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red); draw_gear(&gears[0]); glPopMatrix(); glPushMatrix(); glTranslatef(3.1, -2.0, 0.0); glRotatef(-2.0 * angle - 9.0, 0.0, 0.0, 1.0); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, green); draw_gear(&gears[1]); glPopMatrix(); glPushMatrix(); glTranslatef(-3.1, 4.2, 0.0); glRotatef(-2.0 * angle - 25.0, 0.0, 0.0, 1.0); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, blue); draw_gear(&gears[2]); glPopMatrix(); glPopMatrix(); /* advance rotation for next frame */ angle += 0.5; /* 0.5 degree per frame */ if (angle > 3600.0) angle -= 3600.0; } static void gears_fini(void) { GLint i; for (i = 0; i < 3; i++) { struct gear *gear = &gears[i]; if (gear->vbo) { glDeleteBuffers(1, &gear->vbo); gear->vbo = 0; } if (gear->vertices) { free(gear->vertices); gear->vertices = NULL; } } } static void gears_init(void) { static const GLfloat pos[4] = { 5.0, 5.0, 10.0, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, pos); glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); init_gear(&gears[0], 1.0, 4.0, 1.0, 20, 0.7); init_gear(&gears[1], 0.5, 2.0, 2.0, 10, 0.7); init_gear(&gears[2], 1.3, 2.0, 0.5, 10, 0.7); } /* new window size or exposure */ static void gears_reshape(int width, int height) { GLfloat h = (GLfloat) height / (GLfloat) width; glViewport(0, 0, (GLint) width, (GLint) height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustumf(-1.0, 1.0, -h, h, 5.0, 60.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -40.0); } static void gears_run(void) { winsysRun(5.0, gears_draw, NULL); } int main(int argc, char *argv[]) { EGLint width, height; if (!winsysInitScreen()) exit(1); winsysQueryScreenSize(&width, &height); gears_init(); gears_reshape(width, height); gears_run(); gears_fini(); winsysFiniScreen(); return 0; }