2 * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 * Kristian Høgsberg <krh@bitplanet.net>
28 * * Refactor gear drawing.
29 * * Use correct normals for surfaces.
31 * * Use perspective projection transformation.
34 * Alexandros Frantzis <alexandros.frantzis@linaro.org>
38 #define GL_GLEXT_PROTOTYPES
39 #define EGL_EGLEXT_PROTOTYPES
49 #include <GLES2/gl2.h>
51 #include <EGL/eglext.h>
54 #define STRIPS_PER_TOOTH 7
55 #define VERTICES_PER_TOOTH 34
56 #define GEAR_VERTEX_STRIDE 6
59 * Struct describing the vertices in triangle strip
62 /** The first vertex in the strip */
64 /** The number of consecutive vertices in the strip after the first */
68 /* Each vertex consist of GEAR_VERTEX_STRIDE GLfloat attributes */
69 typedef GLfloat GearVertex
[GEAR_VERTEX_STRIDE
];
72 * Struct representing a gear.
75 /** The array of vertices comprising the gear */
77 /** The number of vertices comprising the gear */
79 /** The array of triangle strips comprising the gear */
80 struct vertex_strip
*strips
;
81 /** The number of triangle strips comprising the gear */
83 /** The Vertex Buffer Object holding the vertices in the graphics card */
87 /** The view rotation [x, y, z] */
88 static GLfloat view_rot
[3] = { 20.0, 30.0, 0.0 };
90 static struct gear
*gear1
, *gear2
, *gear3
;
91 /** The current gear rotation angle */
92 static GLfloat angle
= 0.0;
93 /** The location of the shader uniforms */
94 static GLuint ModelViewProjectionMatrix_location
,
95 NormalMatrix_location
,
96 LightSourcePosition_location
,
97 MaterialColor_location
;
98 /** The projection matrix */
99 static GLfloat ProjectionMatrix
[16];
100 /** The direction of the directional light for the scene */
101 static const GLfloat LightSourcePosition
[4] = { 5.0, 5.0, 10.0, 1.0};
104 * Fills a gear vertex.
106 * @param v the vertex to fill
107 * @param x the x coordinate
108 * @param y the y coordinate
109 * @param z the z coortinate
110 * @param n pointer to the normal table
112 * @return the operation error code
115 vert(GearVertex
*v
, GLfloat x
, GLfloat y
, GLfloat z
, GLfloat n
[3])
128 * Create a gear wheel.
130 * @param inner_radius radius of hole at center
131 * @param outer_radius radius at center of teeth
132 * @param width width of gear
133 * @param teeth number of teeth
134 * @param tooth_depth depth of tooth
136 * @return pointer to the constructed struct gear
139 create_gear(GLfloat inner_radius
, GLfloat outer_radius
, GLfloat width
,
140 GLint teeth
, GLfloat tooth_depth
)
151 /* Allocate memory for the gear */
152 gear
= malloc(sizeof *gear
);
156 /* Calculate the radii used in the gear */
158 r1
= outer_radius
- tooth_depth
/ 2.0;
159 r2
= outer_radius
+ tooth_depth
/ 2.0;
161 da
= 2.0 * M_PI
/ teeth
/ 4.0;
163 /* Allocate memory for the triangle strip information */
164 gear
->nstrips
= STRIPS_PER_TOOTH
* teeth
;
165 gear
->strips
= calloc(gear
->nstrips
, sizeof (*gear
->strips
));
167 /* Allocate memory for the vertices */
168 gear
->vertices
= calloc(VERTICES_PER_TOOTH
* teeth
, sizeof(*gear
->vertices
));
171 for (i
= 0; i
< teeth
; i
++) {
172 /* Calculate needed sin/cos for varius angles */
173 sincos(i
* 2.0 * M_PI
/ teeth
, &s
[0], &c
[0]);
174 sincos(i
* 2.0 * M_PI
/ teeth
+ da
, &s
[1], &c
[1]);
175 sincos(i
* 2.0 * M_PI
/ teeth
+ da
* 2, &s
[2], &c
[2]);
176 sincos(i
* 2.0 * M_PI
/ teeth
+ da
* 3, &s
[3], &c
[3]);
177 sincos(i
* 2.0 * M_PI
/ teeth
+ da
* 4, &s
[4], &c
[4]);
179 /* A set of macros for making the creation of the gears easier */
180 #define GEAR_POINT(r, da) { (r) * c[(da)], (r) * s[(da)] }
181 #define SET_NORMAL(x, y, z) do { \
182 normal[0] = (x); normal[1] = (y); normal[2] = (z); \
185 #define GEAR_VERT(v, point, sign) vert((v), p[(point)].x, p[(point)].y, (sign) * width * 0.5, normal)
187 #define START_STRIP do { \
188 gear->strips[cur_strip].first = v - gear->vertices; \
191 #define END_STRIP do { \
192 int _tmp = (v - gear->vertices); \
193 gear->strips[cur_strip].count = _tmp - gear->strips[cur_strip].first; \
197 #define QUAD_WITH_NORMAL(p1, p2) do { \
198 SET_NORMAL((p[(p1)].y - p[(p2)].y), -(p[(p1)].x - p[(p2)].x), 0); \
199 v = GEAR_VERT(v, (p1), -1); \
200 v = GEAR_VERT(v, (p1), 1); \
201 v = GEAR_VERT(v, (p2), -1); \
202 v = GEAR_VERT(v, (p2), 1); \
210 /* Create the 7 points (only x,y coords) used to draw a tooth */
211 struct point p
[7] = {
212 GEAR_POINT(r2
, 1), // 0
213 GEAR_POINT(r2
, 2), // 1
214 GEAR_POINT(r1
, 0), // 2
215 GEAR_POINT(r1
, 3), // 3
216 GEAR_POINT(r0
, 0), // 4
217 GEAR_POINT(r1
, 4), // 5
218 GEAR_POINT(r0
, 4), // 6
223 SET_NORMAL(0, 0, 1.0);
224 v
= GEAR_VERT(v
, 0, +1);
225 v
= GEAR_VERT(v
, 1, +1);
226 v
= GEAR_VERT(v
, 2, +1);
227 v
= GEAR_VERT(v
, 3, +1);
228 v
= GEAR_VERT(v
, 4, +1);
229 v
= GEAR_VERT(v
, 5, +1);
230 v
= GEAR_VERT(v
, 6, +1);
235 QUAD_WITH_NORMAL(4, 6);
240 SET_NORMAL(0, 0, -1.0);
241 v
= GEAR_VERT(v
, 6, -1);
242 v
= GEAR_VERT(v
, 5, -1);
243 v
= GEAR_VERT(v
, 4, -1);
244 v
= GEAR_VERT(v
, 3, -1);
245 v
= GEAR_VERT(v
, 2, -1);
246 v
= GEAR_VERT(v
, 1, -1);
247 v
= GEAR_VERT(v
, 0, -1);
252 QUAD_WITH_NORMAL(0, 2);
256 QUAD_WITH_NORMAL(1, 0);
260 QUAD_WITH_NORMAL(3, 1);
264 QUAD_WITH_NORMAL(5, 3);
268 gear
->nvertices
= (v
- gear
->vertices
);
270 /* Store the vertices in a vertex buffer object (VBO) */
271 glGenBuffers(1, &gear
->vbo
);
272 glBindBuffer(GL_ARRAY_BUFFER
, gear
->vbo
);
273 glBufferData(GL_ARRAY_BUFFER
, gear
->nvertices
* sizeof(GearVertex
),
274 gear
->vertices
, GL_STATIC_DRAW
);
280 * Multiplies two 4x4 matrices.
282 * The result is stored in matrix m.
284 * @param m the first matrix to multiply
285 * @param n the second matrix to multiply
288 multiply(GLfloat
*m
, const GLfloat
*n
)
291 const GLfloat
*row
, *column
;
295 for (i
= 0; i
< 16; i
++) {
298 row
= n
+ d
.quot
* 4;
300 for (j
= 0; j
< 4; j
++)
301 tmp
[i
] += row
[j
] * column
[j
* 4];
303 memcpy(m
, &tmp
, sizeof tmp
);
307 * Rotates a 4x4 matrix.
309 * @param[in,out] m the matrix to rotate
310 * @param angle the angle to rotate
311 * @param x the x component of the direction to rotate to
312 * @param y the y component of the direction to rotate to
313 * @param z the z component of the direction to rotate to
316 rotate(GLfloat
*m
, GLfloat angle
, GLfloat x
, GLfloat y
, GLfloat z
)
320 sincos(angle
, &s
, &c
);
322 x
* x
* (1 - c
) + c
, y
* x
* (1 - c
) + z
* s
, x
* z
* (1 - c
) - y
* s
, 0,
323 x
* y
* (1 - c
) - z
* s
, y
* y
* (1 - c
) + c
, y
* z
* (1 - c
) + x
* s
, 0,
324 x
* z
* (1 - c
) + y
* s
, y
* z
* (1 - c
) - x
* s
, z
* z
* (1 - c
) + c
, 0,
333 * Translates a 4x4 matrix.
335 * @param[in,out] m the matrix to translate
336 * @param x the x component of the direction to translate to
337 * @param y the y component of the direction to translate to
338 * @param z the z component of the direction to translate to
341 translate(GLfloat
*m
, GLfloat x
, GLfloat y
, GLfloat z
)
343 GLfloat t
[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x
, y
, z
, 1 };
349 * Creates an identity 4x4 matrix.
351 * @param m the matrix make an identity matrix
363 memcpy(m
, t
, sizeof(t
));
367 * Transposes a 4x4 matrix.
369 * @param m the matrix to transpose
372 transpose(GLfloat
*m
)
375 m
[0], m
[4], m
[8], m
[12],
376 m
[1], m
[5], m
[9], m
[13],
377 m
[2], m
[6], m
[10], m
[14],
378 m
[3], m
[7], m
[11], m
[15]};
380 memcpy(m
, t
, sizeof(t
));
384 * Inverts a 4x4 matrix.
386 * This function can currently handle only pure translation-rotation matrices.
387 * Read http://www.gamedev.net/community/forums/topic.asp?topic_id=425118
388 * for an explanation.
396 // Extract and invert the translation part 't'. The inverse of a
397 // translation matrix can be calculated by negating the translation
399 t
[12] = -m
[12]; t
[13] = -m
[13]; t
[14] = -m
[14];
401 // Invert the rotation part 'r'. The inverse of a rotation matrix is
402 // equal to its transpose.
403 m
[12] = m
[13] = m
[14] = 0;
406 // inv(m) = inv(r) * inv(t)
411 * Calculate a frustum projection transformation.
413 * @param m the matrix to save the transformation in
414 * @param l the left plane distance
415 * @param r the right plane distance
416 * @param b the bottom plane distance
417 * @param t the top plane distance
418 * @param n the near plane distance
419 * @param f the far plane distance
422 frustum(GLfloat
*m
, GLfloat l
, GLfloat r
, GLfloat b
, GLfloat t
, GLfloat n
, GLfloat f
)
427 GLfloat deltaX
= r
- l
;
428 GLfloat deltaY
= t
- b
;
429 GLfloat deltaZ
= f
- n
;
431 tmp
[0] = (2 * n
) / deltaX
;
432 tmp
[5] = (2 * n
) / deltaY
;
433 tmp
[8] = (r
+ l
) / deltaX
;
434 tmp
[9] = (t
+ b
) / deltaY
;
435 tmp
[10] = -(f
+ n
) / deltaZ
;
437 tmp
[14] = -(2 * f
* n
) / deltaZ
;
440 memcpy(m
, tmp
, sizeof(tmp
));
446 * @param gear the gear to draw
447 * @param transform the current transformation matrix
448 * @param x the x position to draw the gear at
449 * @param y the y position to draw the gear at
450 * @param angle the rotation angle of the gear
451 * @param color the color of the gear
454 draw_gear(struct gear
*gear
, GLfloat
*transform
,
455 GLfloat x
, GLfloat y
, GLfloat angle
, const GLfloat color
[4])
457 GLfloat model_view
[16];
458 GLfloat normal_matrix
[16];
459 GLfloat model_view_projection
[16];
461 /* Translate and rotate the gear */
462 memcpy(model_view
, transform
, sizeof (model_view
));
463 translate(model_view
, x
, y
, 0);
464 rotate(model_view
, 2 * M_PI
* angle
/ 360.0, 0, 0, 1);
466 /* Create and set the ModelViewProjectionMatrix */
467 memcpy(model_view_projection
, ProjectionMatrix
, sizeof(model_view_projection
));
468 multiply(model_view_projection
, model_view
);
470 glUniformMatrix4fv(ModelViewProjectionMatrix_location
, 1, GL_FALSE
,
471 model_view_projection
);
474 * Create and set the NormalMatrix. It's the inverse transpose of the
477 memcpy(normal_matrix
, model_view
, sizeof (normal_matrix
));
478 invert(normal_matrix
);
479 transpose(normal_matrix
);
480 glUniformMatrix4fv(NormalMatrix_location
, 1, GL_FALSE
, normal_matrix
);
482 /* Set the gear color */
483 glUniform4fv(MaterialColor_location
, 1, color
);
485 /* Set the vertex buffer object to use */
486 glBindBuffer(GL_ARRAY_BUFFER
, gear
->vbo
);
488 /* Set up the position of the attributes in the vertex buffer object */
489 glVertexAttribPointer(0, 3, GL_FLOAT
, GL_FALSE
,
490 6 * sizeof(GLfloat
), NULL
);
491 glVertexAttribPointer(1, 3, GL_FLOAT
, GL_FALSE
,
492 6 * sizeof(GLfloat
), (GLfloat
*) 0 + 3);
494 /* Enable the attributes */
495 glEnableVertexAttribArray(0);
496 glEnableVertexAttribArray(1);
498 /* Draw the triangle strips that comprise the gear */
500 for (n
= 0; n
< gear
->nstrips
; n
++)
501 glDrawArrays(GL_TRIANGLE_STRIP
, gear
->strips
[n
].first
, gear
->strips
[n
].count
);
503 /* Disable the attributes */
504 glDisableVertexAttribArray(1);
505 glDisableVertexAttribArray(0);
514 const static GLfloat red
[4] = { 0.8, 0.1, 0.0, 1.0 };
515 const static GLfloat green
[4] = { 0.0, 0.8, 0.2, 1.0 };
516 const static GLfloat blue
[4] = { 0.2, 0.2, 1.0, 1.0 };
517 GLfloat transform
[16];
520 glClearColor(0.0, 0.0, 0.0, 0.0);
521 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
523 /* Translate and rotate the view */
524 translate(transform
, 0, 0, -40);
525 rotate(transform
, 2 * M_PI
* view_rot
[0] / 360.0, 1, 0, 0);
526 rotate(transform
, 2 * M_PI
* view_rot
[1] / 360.0, 0, 1, 0);
527 rotate(transform
, 2 * M_PI
* view_rot
[2] / 360.0, 0, 0, 1);
530 draw_gear(gear1
, transform
, -3.0, -2.0, angle
, red
);
531 draw_gear(gear2
, transform
, 3.1, -2.0, -2 * angle
- 9.0, green
);
532 draw_gear(gear3
, transform
, -3.1, 4.2, -2 * angle
- 25.0, blue
);
536 * Handles a new window size or exposure.
538 * @param width the window width
539 * @param height the window height
542 gears_reshape(int width
, int height
)
544 /* Update the projection matrix */
545 GLfloat h
= (GLfloat
)height
/ (GLfloat
)width
;
546 frustum(ProjectionMatrix
, -1.0, 1.0, -h
, h
, 5.0, 60.0);
548 /* Set the viewport */
549 glViewport(0, 0, (GLint
) width
, (GLint
) height
);
553 * Handles special eglut events.
555 * @param special the event to handle.
558 gears_special(int special
)
564 case EGLUT_KEY_RIGHT
:
579 static int frames
= 0;
580 static double tRot0
= -1.0, tRate0
= -1.0;
581 double dt
, t
= eglutGet(EGLUT_ELAPSED_TIME
) / 1000.0;
588 /* advance rotation for next frame */
589 angle
+= 70.0 * dt
; /* 70 degrees per second */
593 eglutPostRedisplay();
598 if (t
- tRate0
>= 5.0) {
599 GLfloat seconds
= t
- tRate0
;
600 GLfloat fps
= frames
/ seconds
;
601 printf("%d frames in %3.1f seconds = %6.3f FPS\n", frames
, seconds
,
608 static const char vertex_shader
[] =
609 "attribute vec3 position;\n"
610 "attribute vec3 normal;\n"
612 "uniform mat4 ModelViewProjectionMatrix;\n"
613 "uniform mat4 NormalMatrix;\n"
614 "uniform vec4 LightSourcePosition;\n"
615 "uniform vec4 MaterialColor;\n"
617 "varying vec4 Color;\n"
621 " // Transform the normal to eye coordinates\n"
622 " vec3 N = normalize(vec3(NormalMatrix * vec4(normal, 1.0)));\n"
624 " // The LightSourcePosition is actually its direction for directional light\n"
625 " vec3 L = normalize(LightSourcePosition.xyz);\n"
627 " float diffuse = max(dot(N, L), 0.0);\n"
628 " float ambient = 0.2;\n"
630 " // Multiply the diffuse value by the vertex color (which is fixed in this case)\n"
631 " // to get the actual color that we will use to draw this vertex with\n"
632 " Color = (ambient + diffuse) * MaterialColor;\n"
634 " // Transform the position to clip coordinates\n"
635 " gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0);\n"
638 static const char fragment_shader
[] =
639 "precision mediump float;\n"
640 "varying vec4 Color;\n"
644 " gl_FragColor = Color;\n"
650 GLuint v
, f
, program
;
654 glEnable(GL_CULL_FACE
);
655 glEnable(GL_DEPTH_TEST
);
657 /* Compile the vertex shader */
659 v
= glCreateShader(GL_VERTEX_SHADER
);
660 glShaderSource(v
, 1, &p
, NULL
);
662 glGetShaderInfoLog(v
, sizeof msg
, NULL
, msg
);
663 printf("vertex shader info: %s\n", msg
);
665 /* Compile the fragment shader */
667 f
= glCreateShader(GL_FRAGMENT_SHADER
);
668 glShaderSource(f
, 1, &p
, NULL
);
670 glGetShaderInfoLog(f
, sizeof msg
, NULL
, msg
);
671 printf("fragment shader info: %s\n", msg
);
673 /* Create and link the shader program */
674 program
= glCreateProgram();
675 glAttachShader(program
, v
);
676 glAttachShader(program
, f
);
677 glBindAttribLocation(program
, 0, "position");
678 glBindAttribLocation(program
, 1, "normal");
680 glLinkProgram(program
);
681 glGetProgramInfoLog(program
, sizeof msg
, NULL
, msg
);
682 printf("info: %s\n", msg
);
684 /* Enable the shaders */
685 glUseProgram(program
);
687 /* Get the locations of the uniforms so we can access them */
688 ModelViewProjectionMatrix_location
= glGetUniformLocation(program
, "ModelViewProjectionMatrix");
689 NormalMatrix_location
= glGetUniformLocation(program
, "NormalMatrix");
690 LightSourcePosition_location
= glGetUniformLocation(program
, "LightSourcePosition");
691 MaterialColor_location
= glGetUniformLocation(program
, "MaterialColor");
693 /* Set the LightSourcePosition uniform which is constant throught the program */
694 glUniform4fv(LightSourcePosition_location
, 1, LightSourcePosition
);
697 gear1
= create_gear(1.0, 4.0, 1.0, 20, 0.7);
698 gear2
= create_gear(0.5, 2.0, 2.0, 10, 0.7);
699 gear3
= create_gear(1.3, 2.0, 0.5, 10, 0.7);
703 main(int argc
, char *argv
[])
705 /* Initialize the window */
706 eglutInitWindowSize(300, 300);
707 eglutInitAPIMask(EGLUT_OPENGL_ES2_BIT
);
708 eglutInit(argc
, argv
);
710 eglutCreateWindow("es2gears");
712 /* Set up eglut callback functions */
713 eglutIdleFunc(gears_idle
);
714 eglutReshapeFunc(gears_reshape
);
715 eglutDisplayFunc(gears_draw
);
716 eglutSpecialFunc(gears_special
);
718 /* Initialize the gears */