1 | /* The rendering code in here is taken from es2gears, which has the |
2 | * following copyright notice: |
3 | * |
4 | * Copyright (C) 1999-2001 Brian Paul All Rights Reserved. |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the "Software"), |
8 | * to deal in the Software without restriction, including without limitation |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
10 | * and/or sell copies of the Software, and to permit persons to whom the |
11 | * Software is furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included |
14 | * in all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
20 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
22 | * |
23 | * Ported to GLES2. |
24 | * Kristian Høgsberg <krh@bitplanet.net> |
25 | * May 3, 2010 |
26 | * |
27 | * Improve GLES2 port: |
28 | * * Refactor gear drawing. |
29 | * * Use correct normals for surfaces. |
30 | * * Improve shader. |
31 | * * Use perspective projection transformation. |
32 | * * Add FPS count. |
33 | * * Add comments. |
34 | * Alexandros Frantzis <alexandros.frantzis@linaro.org> |
35 | * Jul 13, 2010 |
36 | */ |
37 | |
38 | #include "config.h" |
39 | |
40 | #include <math.h> |
41 | #include <stdlib.h> |
42 | #include <string.h> |
43 | #include <epoxy/gl.h> |
44 | |
45 | #include "gtkgears.h" |
46 | |
47 | #define STRIPS_PER_TOOTH 7 |
48 | #define VERTICES_PER_TOOTH 34 |
49 | #define GEAR_VERTEX_STRIDE 6 |
50 | |
51 | #ifndef HAVE_SINCOS |
52 | static void |
53 | sincos (double x, double *_sin, double *_cos) |
54 | { |
55 | *_sin = sin (x); |
56 | *_cos = cos (x); |
57 | } |
58 | #endif |
59 | |
60 | /** |
61 | * Struct describing the vertices in triangle strip |
62 | */ |
63 | struct vertex_strip { |
64 | /** The first vertex in the strip */ |
65 | GLint first; |
66 | /** The number of consecutive vertices in the strip after the first */ |
67 | GLint count; |
68 | }; |
69 | |
70 | /* Each vertex consist of GEAR_VERTEX_STRIDE GLfloat attributes */ |
71 | typedef GLfloat GearVertex[GEAR_VERTEX_STRIDE]; |
72 | |
73 | /** |
74 | * Struct representing a gear. |
75 | */ |
76 | struct gear { |
77 | /** The array of vertices comprising the gear */ |
78 | GearVertex *vertices; |
79 | /** The number of vertices comprising the gear */ |
80 | int nvertices; |
81 | /** The array of triangle strips comprising the gear */ |
82 | struct vertex_strip *strips; |
83 | /** The number of triangle strips comprising the gear */ |
84 | int nstrips; |
85 | }; |
86 | |
87 | typedef struct { |
88 | /* The view rotation [x, y, z] */ |
89 | GLfloat view_rot[GTK_GEARS_N_AXIS]; |
90 | |
91 | /* The Vertex Array Object */ |
92 | GLuint vao; |
93 | |
94 | /* The shader program */ |
95 | GLuint program; |
96 | |
97 | /* The gears */ |
98 | struct gear *gear1; |
99 | struct gear *gear2; |
100 | struct gear *gear3; |
101 | |
102 | /** The Vertex Buffer Object holding the vertices in the graphics card */ |
103 | GLuint gear_vbo[3]; |
104 | |
105 | /** The location of the shader uniforms */ |
106 | GLuint ModelViewProjectionMatrix_location; |
107 | GLuint NormalMatrix_location; |
108 | GLuint LightSourcePosition_location; |
109 | GLuint MaterialColor_location; |
110 | |
111 | /* The current gear rotation angle */ |
112 | GLfloat angle; |
113 | |
114 | /* The projection matrix */ |
115 | GLfloat ProjectionMatrix[16]; |
116 | |
117 | /* The direction of the directional light for the scene */ |
118 | GLfloat LightSourcePosition[4]; |
119 | |
120 | gint64 first_frame_time; |
121 | guint tick; |
122 | GtkLabel *fps_label; |
123 | } GtkGearsPrivate; |
124 | |
125 | G_DEFINE_TYPE_WITH_PRIVATE (GtkGears, gtk_gears, GTK_TYPE_GL_AREA) |
126 | |
127 | static gboolean gtk_gears_render (GtkGLArea *area, |
128 | GdkGLContext *context); |
129 | static void gtk_gears_reshape (GtkGLArea *area, |
130 | int width, |
131 | int height); |
132 | static void gtk_gears_realize (GtkWidget *widget); |
133 | static void gtk_gears_unrealize (GtkWidget *widget); |
134 | static gboolean gtk_gears_tick (GtkWidget *widget, |
135 | GdkFrameClock *frame_clock, |
136 | gpointer user_data); |
137 | |
138 | static void destroy_gear (struct gear *g); |
139 | |
140 | GtkWidget * |
141 | gtk_gears_new (void) |
142 | { |
143 | return g_object_new (object_type: gtk_gears_get_type (), |
144 | first_property_name: "has-depth-buffer" , TRUE, |
145 | NULL); |
146 | } |
147 | |
148 | static void |
149 | gtk_gears_init (GtkGears *gears) |
150 | { |
151 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
152 | |
153 | priv->view_rot[GTK_GEARS_X_AXIS] = 20.0; |
154 | priv->view_rot[GTK_GEARS_Y_AXIS] = 30.0; |
155 | priv->view_rot[GTK_GEARS_Z_AXIS] = 20.0; |
156 | |
157 | priv->LightSourcePosition[0] = 5.0; |
158 | priv->LightSourcePosition[1] = 5.0; |
159 | priv->LightSourcePosition[2] = 10.0; |
160 | priv->LightSourcePosition[3] = 1.0; |
161 | |
162 | priv->tick = gtk_widget_add_tick_callback (GTK_WIDGET (gears), callback: gtk_gears_tick, user_data: gears, NULL); |
163 | } |
164 | |
165 | static void |
166 | gtk_gears_finalize (GObject *obj) |
167 | { |
168 | GtkGears *gears = GTK_GEARS (obj); |
169 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
170 | |
171 | gtk_widget_remove_tick_callback (GTK_WIDGET (gears), id: priv->tick); |
172 | |
173 | g_clear_object (&priv->fps_label); |
174 | |
175 | g_clear_pointer (&priv->gear1, destroy_gear); |
176 | g_clear_pointer (&priv->gear2, destroy_gear); |
177 | g_clear_pointer (&priv->gear3, destroy_gear); |
178 | |
179 | G_OBJECT_CLASS (gtk_gears_parent_class)->finalize (obj); |
180 | } |
181 | |
182 | static void |
183 | gtk_gears_class_init (GtkGearsClass *klass) |
184 | { |
185 | GTK_GL_AREA_CLASS (klass)->render = gtk_gears_render; |
186 | GTK_GL_AREA_CLASS (klass)->resize = gtk_gears_reshape; |
187 | |
188 | GTK_WIDGET_CLASS (klass)->realize = gtk_gears_realize; |
189 | GTK_WIDGET_CLASS (klass)->unrealize = gtk_gears_unrealize; |
190 | |
191 | G_OBJECT_CLASS (klass)->finalize = gtk_gears_finalize; |
192 | } |
193 | |
194 | /* |
195 | * Fills a gear vertex. |
196 | * |
197 | * @param v the vertex to fill |
198 | * @param x the x coordinate |
199 | * @param y the y coordinate |
200 | * @param z the z coordinate |
201 | * @param n pointer to the normal table |
202 | * |
203 | * @return the operation error code |
204 | */ |
205 | static GearVertex * |
206 | vert (GearVertex *v, |
207 | GLfloat x, |
208 | GLfloat y, |
209 | GLfloat z, |
210 | GLfloat n[3]) |
211 | { |
212 | v[0][0] = x; |
213 | v[0][1] = y; |
214 | v[0][2] = z; |
215 | v[0][3] = n[0]; |
216 | v[0][4] = n[1]; |
217 | v[0][5] = n[2]; |
218 | |
219 | return v + 1; |
220 | } |
221 | |
222 | static void |
223 | destroy_gear (struct gear *g) |
224 | { |
225 | g_free (mem: g->strips); |
226 | g_free (mem: g); |
227 | } |
228 | |
229 | /** |
230 | * Create a gear wheel. |
231 | * |
232 | * @param inner_radius radius of hole at center |
233 | * @param outer_radius radius at center of teeth |
234 | * @param width width of gear |
235 | * @param teeth number of teeth |
236 | * @param tooth_depth depth of tooth |
237 | * |
238 | * @return pointer to the constructed struct gear |
239 | */ |
240 | static struct gear * |
241 | create_gear (GLfloat inner_radius, |
242 | GLfloat outer_radius, |
243 | GLfloat width, |
244 | GLint teeth, |
245 | GLfloat tooth_depth) |
246 | { |
247 | GLfloat r0, r1, r2; |
248 | GLfloat da; |
249 | GearVertex *v; |
250 | struct gear *gear; |
251 | double s[5], c[5]; |
252 | GLfloat normal[3]; |
253 | int cur_strip = 0; |
254 | int i; |
255 | |
256 | /* Allocate memory for the gear */ |
257 | gear = g_malloc (n_bytes: sizeof *gear); |
258 | |
259 | /* Calculate the radii used in the gear */ |
260 | r0 = inner_radius; |
261 | r1 = outer_radius - tooth_depth / 2.0; |
262 | r2 = outer_radius + tooth_depth / 2.0; |
263 | |
264 | da = 2.0 * M_PI / teeth / 4.0; |
265 | |
266 | /* Allocate memory for the triangle strip information */ |
267 | gear->nstrips = STRIPS_PER_TOOTH * teeth; |
268 | gear->strips = g_malloc0_n (n_blocks: gear->nstrips, n_block_bytes: sizeof (*gear->strips)); |
269 | |
270 | /* Allocate memory for the vertices */ |
271 | gear->vertices = g_malloc0_n (VERTICES_PER_TOOTH * teeth, n_block_bytes: sizeof(*gear->vertices)); |
272 | v = gear->vertices; |
273 | |
274 | for (i = 0; i < teeth; i++) { |
275 | /* A set of macros for making the creation of the gears easier */ |
276 | #define GEAR_POINT(p, r, da) do { p.x = (r) * c[(da)]; p.y = (r) * s[(da)]; } while(0) |
277 | #define SET_NORMAL(x, y, z) do { \ |
278 | normal[0] = (x); normal[1] = (y); normal[2] = (z); \ |
279 | } while(0) |
280 | |
281 | #define GEAR_VERT(v, point, sign) vert((v), p[(point)].x, p[(point)].y, (sign) * width * 0.5, normal) |
282 | |
283 | #define START_STRIP do { \ |
284 | gear->strips[cur_strip].first = v - gear->vertices; \ |
285 | } while(0); |
286 | |
287 | #define END_STRIP do { \ |
288 | int _tmp = (v - gear->vertices); \ |
289 | gear->strips[cur_strip].count = _tmp - gear->strips[cur_strip].first; \ |
290 | cur_strip++; \ |
291 | } while (0) |
292 | |
293 | #define QUAD_WITH_NORMAL(p1, p2) do { \ |
294 | SET_NORMAL((p[(p1)].y - p[(p2)].y), -(p[(p1)].x - p[(p2)].x), 0); \ |
295 | v = GEAR_VERT(v, (p1), -1); \ |
296 | v = GEAR_VERT(v, (p1), 1); \ |
297 | v = GEAR_VERT(v, (p2), -1); \ |
298 | v = GEAR_VERT(v, (p2), 1); \ |
299 | } while(0) |
300 | struct point { |
301 | GLfloat x; |
302 | GLfloat y; |
303 | }; |
304 | |
305 | /* Create the 7 points (only x,y coords) used to draw a tooth */ |
306 | struct point p[7]; |
307 | |
308 | /* Calculate needed sin/cos for various angles */ |
309 | sincos(x: i * 2.0 * G_PI / teeth + da * 0, sinx: &s[0], cosx: &c[0]); |
310 | sincos(x: i * 2.0 * M_PI / teeth + da * 1, sinx: &s[1], cosx: &c[1]); |
311 | sincos(x: i * 2.0 * M_PI / teeth + da * 2, sinx: &s[2], cosx: &c[2]); |
312 | sincos(x: i * 2.0 * M_PI / teeth + da * 3, sinx: &s[3], cosx: &c[3]); |
313 | sincos(x: i * 2.0 * M_PI / teeth + da * 4, sinx: &s[4], cosx: &c[4]); |
314 | |
315 | GEAR_POINT(p[0], r2, 1); |
316 | GEAR_POINT(p[1], r2, 2); |
317 | GEAR_POINT(p[2], r1, 0); |
318 | GEAR_POINT(p[3], r1, 3); |
319 | GEAR_POINT(p[4], r0, 0); |
320 | GEAR_POINT(p[5], r1, 4); |
321 | GEAR_POINT(p[6], r0, 4); |
322 | |
323 | /* Front face */ |
324 | START_STRIP; |
325 | SET_NORMAL(0, 0, 1.0); |
326 | v = GEAR_VERT(v, 0, +1); |
327 | v = GEAR_VERT(v, 1, +1); |
328 | v = GEAR_VERT(v, 2, +1); |
329 | v = GEAR_VERT(v, 3, +1); |
330 | v = GEAR_VERT(v, 4, +1); |
331 | v = GEAR_VERT(v, 5, +1); |
332 | v = GEAR_VERT(v, 6, +1); |
333 | END_STRIP; |
334 | |
335 | /* Inner face */ |
336 | START_STRIP; |
337 | QUAD_WITH_NORMAL(4, 6); |
338 | END_STRIP; |
339 | |
340 | /* Back face */ |
341 | START_STRIP; |
342 | SET_NORMAL(0, 0, -1.0); |
343 | v = GEAR_VERT(v, 6, -1); |
344 | v = GEAR_VERT(v, 5, -1); |
345 | v = GEAR_VERT(v, 4, -1); |
346 | v = GEAR_VERT(v, 3, -1); |
347 | v = GEAR_VERT(v, 2, -1); |
348 | v = GEAR_VERT(v, 1, -1); |
349 | v = GEAR_VERT(v, 0, -1); |
350 | END_STRIP; |
351 | |
352 | /* Outer face */ |
353 | START_STRIP; |
354 | QUAD_WITH_NORMAL(0, 2); |
355 | END_STRIP; |
356 | |
357 | START_STRIP; |
358 | QUAD_WITH_NORMAL(1, 0); |
359 | END_STRIP; |
360 | |
361 | START_STRIP; |
362 | QUAD_WITH_NORMAL(3, 1); |
363 | END_STRIP; |
364 | |
365 | START_STRIP; |
366 | QUAD_WITH_NORMAL(5, 3); |
367 | END_STRIP; |
368 | } |
369 | |
370 | gear->nvertices = (v - gear->vertices); |
371 | |
372 | return gear; |
373 | } |
374 | |
375 | /** |
376 | * Multiplies two 4x4 matrices. |
377 | * |
378 | * The result is stored in matrix m. |
379 | * |
380 | * @param m the first matrix to multiply |
381 | * @param n the second matrix to multiply |
382 | */ |
383 | static void |
384 | multiply (GLfloat *m, const GLfloat *n) |
385 | { |
386 | GLfloat tmp[16]; |
387 | const GLfloat *row, *column; |
388 | div_t d; |
389 | int i, j; |
390 | |
391 | for (i = 0; i < 16; i++) { |
392 | tmp[i] = 0; |
393 | d = div(numer: i, denom: 4); |
394 | row = n + d.quot * 4; |
395 | column = m + d.rem; |
396 | for (j = 0; j < 4; j++) |
397 | tmp[i] += row[j] * column[j * 4]; |
398 | } |
399 | memcpy(dest: m, src: &tmp, n: sizeof tmp); |
400 | } |
401 | |
402 | /** |
403 | * Rotates a 4x4 matrix. |
404 | * |
405 | * @param[in,out] m the matrix to rotate |
406 | * @param angle the angle to rotate |
407 | * @param x the x component of the direction to rotate to |
408 | * @param y the y component of the direction to rotate to |
409 | * @param z the z component of the direction to rotate to |
410 | */ |
411 | static void |
412 | rotate(GLfloat *m, GLfloat angle, GLfloat x, GLfloat y, GLfloat z) |
413 | { |
414 | double s = sin (x: angle); |
415 | double c = cos (x: angle); |
416 | |
417 | GLfloat r[16] = { |
418 | x * x * (1 - c) + c, y * x * (1 - c) + z * s, x * z * (1 - c) - y * s, 0, |
419 | x * y * (1 - c) - z * s, y * y * (1 - c) + c, y * z * (1 - c) + x * s, 0, |
420 | x * z * (1 - c) + y * s, y * z * (1 - c) - x * s, z * z * (1 - c) + c, 0, |
421 | 0, 0, 0, 1 |
422 | }; |
423 | |
424 | multiply(m, n: r); |
425 | } |
426 | |
427 | /** |
428 | * Translates a 4x4 matrix. |
429 | * |
430 | * @param[in,out] m the matrix to translate |
431 | * @param x the x component of the direction to translate to |
432 | * @param y the y component of the direction to translate to |
433 | * @param z the z component of the direction to translate to |
434 | */ |
435 | static void |
436 | translate(GLfloat *m, GLfloat x, GLfloat y, GLfloat z) |
437 | { |
438 | GLfloat t[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 }; |
439 | |
440 | multiply(m, n: t); |
441 | } |
442 | |
443 | /** |
444 | * Creates an identity 4x4 matrix. |
445 | * |
446 | * @param m the matrix make an identity matrix |
447 | */ |
448 | static void |
449 | identity(GLfloat *m) |
450 | { |
451 | GLfloat t[16] = { |
452 | 1.0, 0.0, 0.0, 0.0, |
453 | 0.0, 1.0, 0.0, 0.0, |
454 | 0.0, 0.0, 1.0, 0.0, |
455 | 0.0, 0.0, 0.0, 1.0, |
456 | }; |
457 | |
458 | memcpy(dest: m, src: t, n: sizeof(t)); |
459 | } |
460 | |
461 | /** |
462 | * Transposes a 4x4 matrix. |
463 | * |
464 | * @param m the matrix to transpose |
465 | */ |
466 | static void |
467 | transpose(GLfloat *m) |
468 | { |
469 | GLfloat t[16] = { |
470 | m[0], m[4], m[8], m[12], |
471 | m[1], m[5], m[9], m[13], |
472 | m[2], m[6], m[10], m[14], |
473 | m[3], m[7], m[11], m[15]}; |
474 | |
475 | memcpy(dest: m, src: t, n: sizeof(t)); |
476 | } |
477 | |
478 | /** |
479 | * Inverts a 4x4 matrix. |
480 | * |
481 | * This function can currently handle only pure translation-rotation matrices. |
482 | * Read http://www.gamedev.net/community/forums/topic.asp?topic_id=425118 |
483 | * for an explanation. |
484 | */ |
485 | static void |
486 | invert(GLfloat *m) |
487 | { |
488 | GLfloat t[16]; |
489 | identity(m: t); |
490 | |
491 | // Extract and invert the translation part 't'. The inverse of a |
492 | // translation matrix can be calculated by negating the translation |
493 | // coordinates. |
494 | t[12] = -m[12]; t[13] = -m[13]; t[14] = -m[14]; |
495 | |
496 | // Invert the rotation part 'r'. The inverse of a rotation matrix is |
497 | // equal to its transpose. |
498 | m[12] = m[13] = m[14] = 0; |
499 | transpose(m); |
500 | |
501 | // inv(m) = inv(r) * inv(t) |
502 | multiply(m, n: t); |
503 | } |
504 | |
505 | /** |
506 | * Calculate a perspective projection transformation. |
507 | * |
508 | * @param m the matrix to save the transformation in |
509 | * @param fovy the field of view in the y direction |
510 | * @param aspect the view aspect ratio |
511 | * @param zNear the near clipping plane |
512 | * @param zFar the far clipping plane |
513 | */ |
514 | static void perspective(GLfloat *m, GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar) |
515 | { |
516 | GLfloat tmp[16]; |
517 | double sine, cosine, cotangent, deltaZ; |
518 | GLfloat radians = fovy / 2 * M_PI / 180; |
519 | identity(m: tmp); |
520 | |
521 | deltaZ = zFar - zNear; |
522 | sincos(x: radians, sinx: &sine, cosx: &cosine); |
523 | |
524 | if ((deltaZ == 0) || (sine == 0) || (aspect == 0)) |
525 | return; |
526 | |
527 | cotangent = cosine / sine; |
528 | |
529 | tmp[0] = cotangent / aspect; |
530 | tmp[5] = cotangent; |
531 | tmp[10] = -(zFar + zNear) / deltaZ; |
532 | tmp[11] = -1; |
533 | tmp[14] = -2 * zNear * zFar / deltaZ; |
534 | tmp[15] = 0; |
535 | |
536 | memcpy(dest: m, src: tmp, n: sizeof(tmp)); |
537 | } |
538 | |
539 | /** |
540 | * Draws a gear. |
541 | * |
542 | * @param gear the gear to draw |
543 | * @param transform the current transformation matrix |
544 | * @param x the x position to draw the gear at |
545 | * @param y the y position to draw the gear at |
546 | * @param angle the rotation angle of the gear |
547 | * @param color the color of the gear |
548 | */ |
549 | static void |
550 | draw_gear(GtkGears *self, |
551 | struct gear *gear, |
552 | GLuint gear_vbo, |
553 | GLfloat *transform, |
554 | GLfloat x, |
555 | GLfloat y, |
556 | GLfloat angle, |
557 | const GLfloat color[4]) |
558 | { |
559 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self); |
560 | GLfloat model_view[16]; |
561 | GLfloat normal_matrix[16]; |
562 | GLfloat model_view_projection[16]; |
563 | int n; |
564 | |
565 | /* Translate and rotate the gear */ |
566 | memcpy(dest: model_view, src: transform, n: sizeof (model_view)); |
567 | translate(m: model_view, x, y, z: 0); |
568 | rotate(m: model_view, angle: 2 * G_PI * angle / 360.0, x: 0, y: 0, z: 1); |
569 | |
570 | /* Create and set the ModelViewProjectionMatrix */ |
571 | memcpy(dest: model_view_projection, src: priv->ProjectionMatrix, n: sizeof(model_view_projection)); |
572 | multiply(m: model_view_projection, n: model_view); |
573 | |
574 | glUniformMatrix4fv(priv->ModelViewProjectionMatrix_location, 1, GL_FALSE, |
575 | model_view_projection); |
576 | |
577 | /* |
578 | * Create and set the NormalMatrix. It's the inverse transpose of the |
579 | * ModelView matrix. |
580 | */ |
581 | memcpy(dest: normal_matrix, src: model_view, n: sizeof (normal_matrix)); |
582 | invert(m: normal_matrix); |
583 | transpose(m: normal_matrix); |
584 | glUniformMatrix4fv(priv->NormalMatrix_location, 1, GL_FALSE, normal_matrix); |
585 | |
586 | /* Set the gear color */ |
587 | glUniform4fv(priv->MaterialColor_location, 1, color); |
588 | |
589 | /* Set the vertex buffer object to use */ |
590 | glBindBuffer(GL_ARRAY_BUFFER, gear_vbo); |
591 | |
592 | /* Set up the position of the attributes in the vertex buffer object */ |
593 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), NULL); |
594 | glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLfloat *) 0 + 3); |
595 | |
596 | /* Enable the attributes */ |
597 | glEnableVertexAttribArray(0); |
598 | glEnableVertexAttribArray(1); |
599 | |
600 | /* Draw the triangle strips that comprise the gear */ |
601 | for (n = 0; n < gear->nstrips; n++) { |
602 | glDrawArrays(GL_TRIANGLE_STRIP, gear->strips[n].first, gear->strips[n].count); |
603 | } |
604 | |
605 | /* Disable the attributes */ |
606 | glDisableVertexAttribArray(1); |
607 | glDisableVertexAttribArray(0); |
608 | } |
609 | |
610 | /* new window size or exposure */ |
611 | static void |
612 | gtk_gears_reshape (GtkGLArea *area, int width, int height) |
613 | { |
614 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: (GtkGears *) area); |
615 | |
616 | /* Update the projection matrix */ |
617 | perspective (m: priv->ProjectionMatrix, fovy: 60.0, aspect: width / (float)height, zNear: 1.0, zFar: 1024.0); |
618 | |
619 | /* Set the viewport */ |
620 | glViewport (0, 0, (GLint) width, (GLint) height); |
621 | } |
622 | |
623 | static gboolean |
624 | gtk_gears_render (GtkGLArea *area, |
625 | GdkGLContext *context) |
626 | { |
627 | static const GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 }; |
628 | static const GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 }; |
629 | static const GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 }; |
630 | |
631 | GtkGears *self = GTK_GEARS (area); |
632 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self); |
633 | GLfloat transform[16]; |
634 | |
635 | identity (m: transform); |
636 | |
637 | glClearColor (0.0, 0.0, 0.0, 0.0); |
638 | glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
639 | |
640 | /* Translate and rotate the view */ |
641 | translate (m: transform, x: 0, y: 0, z: -20); |
642 | rotate (m: transform, angle: 2 * G_PI * priv->view_rot[0] / 360.0, x: 1, y: 0, z: 0); |
643 | rotate (m: transform, angle: 2 * G_PI * priv->view_rot[1] / 360.0, x: 0, y: 1, z: 0); |
644 | rotate (m: transform, angle: 2 * G_PI * priv->view_rot[2] / 360.0, x: 0, y: 0, z: 1); |
645 | |
646 | /* Draw the gears */ |
647 | draw_gear (self, gear: priv->gear1, gear_vbo: priv->gear_vbo[0], transform, x: -3.0, y: -2.0, angle: priv->angle, color: red); |
648 | draw_gear (self, gear: priv->gear2, gear_vbo: priv->gear_vbo[1], transform, x: 3.1, y: -2.0, angle: -2 * priv->angle - 9.0, color: green); |
649 | draw_gear (self, gear: priv->gear3, gear_vbo: priv->gear_vbo[2], transform, x: -3.1, y: 4.2, angle: -2 * priv->angle - 25.0, color: blue); |
650 | |
651 | return TRUE; |
652 | } |
653 | |
654 | static const char vertex_shader_gl[] = |
655 | "#version 150\n" |
656 | "\n" |
657 | "in vec3 position;\n" |
658 | "in vec3 normal;\n" |
659 | "\n" |
660 | "uniform mat4 ModelViewProjectionMatrix;\n" |
661 | "uniform mat4 NormalMatrix;\n" |
662 | "uniform vec4 LightSourcePosition;\n" |
663 | "uniform vec4 MaterialColor;\n" |
664 | "\n" |
665 | "smooth out vec4 Color;\n" |
666 | "\n" |
667 | "void main(void)\n" |
668 | "{\n" |
669 | " // Transform the normal to eye coordinates\n" |
670 | " vec3 N = normalize(vec3(NormalMatrix * vec4(normal, 1.0)));\n" |
671 | "\n" |
672 | " // The LightSourcePosition is actually its direction for directional light\n" |
673 | " vec3 L = normalize(LightSourcePosition.xyz);\n" |
674 | "\n" |
675 | " // Multiply the diffuse value by the vertex color (which is fixed in this case)\n" |
676 | " // to get the actual color that we will use to draw this vertex with\n" |
677 | " float diffuse = max(dot(N, L), 0.0);\n" |
678 | " Color = diffuse * MaterialColor;\n" |
679 | "\n" |
680 | " // Transform the position to clip coordinates\n" |
681 | " gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0);\n" |
682 | "}" ; |
683 | |
684 | static const char fragment_shader_gl[] = |
685 | "#version 150\n" |
686 | "\n" |
687 | "smooth in vec4 Color;\n" |
688 | "\n" |
689 | "void main(void)\n" |
690 | "{\n" |
691 | " gl_FragColor = Color;\n" |
692 | "}" ; |
693 | |
694 | static const char vertex_shader_gles[] = |
695 | "attribute vec3 position;\n" |
696 | "attribute vec3 normal;\n" |
697 | "\n" |
698 | "uniform mat4 ModelViewProjectionMatrix;\n" |
699 | "uniform mat4 NormalMatrix;\n" |
700 | "uniform vec4 LightSourcePosition;\n" |
701 | "uniform vec4 MaterialColor;\n" |
702 | "\n" |
703 | "varying vec4 Color;\n" |
704 | "\n" |
705 | "void main(void)\n" |
706 | "{\n" |
707 | " // Transform the normal to eye coordinates\n" |
708 | " vec3 N = normalize(vec3(NormalMatrix * vec4(normal, 1.0)));\n" |
709 | "\n" |
710 | " // The LightSourcePosition is actually its direction for directional light\n" |
711 | " vec3 L = normalize(LightSourcePosition.xyz);\n" |
712 | "\n" |
713 | " // Multiply the diffuse value by the vertex color (which is fixed in this case)\n" |
714 | " // to get the actual color that we will use to draw this vertex with\n" |
715 | " float diffuse = max(dot(N, L), 0.0);\n" |
716 | " Color = diffuse * MaterialColor;\n" |
717 | "\n" |
718 | " // Transform the position to clip coordinates\n" |
719 | " gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0);\n" |
720 | "}" ; |
721 | |
722 | static const char fragment_shader_gles[] = |
723 | "precision mediump float;\n" |
724 | "varying vec4 Color;\n" |
725 | "\n" |
726 | "void main(void)\n" |
727 | "{\n" |
728 | " gl_FragColor = Color;\n" |
729 | "}" ; |
730 | |
731 | static void |
732 | gtk_gears_realize (GtkWidget *widget) |
733 | { |
734 | GtkGLArea *glarea = GTK_GL_AREA (widget); |
735 | GtkGears *gears = GTK_GEARS (widget); |
736 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
737 | GdkGLContext *context; |
738 | GLuint vao, v, f, program; |
739 | const char *p; |
740 | char msg[512]; |
741 | |
742 | GTK_WIDGET_CLASS (gtk_gears_parent_class)->realize (widget); |
743 | |
744 | gtk_gl_area_make_current (area: glarea); |
745 | if (gtk_gl_area_get_error (area: glarea) != NULL) |
746 | return; |
747 | |
748 | context = gtk_gl_area_get_context (area: glarea); |
749 | |
750 | glEnable (GL_CULL_FACE); |
751 | glEnable (GL_DEPTH_TEST); |
752 | |
753 | /* Create the VAO */ |
754 | glGenVertexArrays (1, &vao); |
755 | glBindVertexArray (vao); |
756 | priv->vao = vao; |
757 | |
758 | /* Compile the vertex shader */ |
759 | if (gdk_gl_context_get_use_es (context)) |
760 | p = vertex_shader_gles; |
761 | else |
762 | p = vertex_shader_gl; |
763 | v = glCreateShader(GL_VERTEX_SHADER); |
764 | glShaderSource(v, 1, &p, NULL); |
765 | glCompileShader(v); |
766 | glGetShaderInfoLog(v, sizeof msg, NULL, msg); |
767 | g_print (format: "vertex shader info: %s\n" , msg); |
768 | |
769 | /* Compile the fragment shader */ |
770 | if (gdk_gl_context_get_use_es (context)) |
771 | p = fragment_shader_gles; |
772 | else |
773 | p = fragment_shader_gl; |
774 | f = glCreateShader(GL_FRAGMENT_SHADER); |
775 | glShaderSource(f, 1, &p, NULL); |
776 | glCompileShader(f); |
777 | glGetShaderInfoLog(f, sizeof msg, NULL, msg); |
778 | g_print (format: "fragment shader info: %s\n" , msg); |
779 | |
780 | /* Create and link the shader program */ |
781 | program = glCreateProgram(); |
782 | glAttachShader(program, v); |
783 | glAttachShader(program, f); |
784 | glBindAttribLocation(program, 0, "position" ); |
785 | glBindAttribLocation(program, 1, "normal" ); |
786 | |
787 | glLinkProgram(program); |
788 | glGetProgramInfoLog(program, sizeof msg, NULL, msg); |
789 | g_print (format: "program info: %s\n" , msg); |
790 | glDetachShader (program, v); |
791 | glDetachShader (program, f); |
792 | glDeleteShader (v); |
793 | glDeleteShader (f); |
794 | |
795 | /* Enable the shaders */ |
796 | glUseProgram(program); |
797 | priv->program = program; |
798 | |
799 | /* Get the locations of the uniforms so we can access them */ |
800 | priv->ModelViewProjectionMatrix_location = glGetUniformLocation(program, "ModelViewProjectionMatrix" ); |
801 | priv->NormalMatrix_location = glGetUniformLocation(program, "NormalMatrix" ); |
802 | priv->LightSourcePosition_location = glGetUniformLocation(program, "LightSourcePosition" ); |
803 | priv->MaterialColor_location = glGetUniformLocation(program, "MaterialColor" ); |
804 | |
805 | /* Set the LightSourcePosition uniform which is constant throughout the program */ |
806 | glUniform4fv(priv->LightSourcePosition_location, 1, priv->LightSourcePosition); |
807 | |
808 | /* make the gears */ |
809 | priv->gear1 = create_gear(inner_radius: 1.0, outer_radius: 4.0, width: 1.0, teeth: 20, tooth_depth: 0.7); |
810 | |
811 | /* Store the vertices in a vertex buffer object (VBO) */ |
812 | glGenBuffers (1, &(priv->gear_vbo[0])); |
813 | glBindBuffer (GL_ARRAY_BUFFER, priv->gear_vbo[0]); |
814 | glBufferData (GL_ARRAY_BUFFER, |
815 | priv->gear1->nvertices * sizeof(GearVertex), |
816 | priv->gear1->vertices, |
817 | GL_STATIC_DRAW); |
818 | |
819 | priv->gear2 = create_gear(inner_radius: 0.5, outer_radius: 2.0, width: 2.0, teeth: 10, tooth_depth: 0.7); |
820 | glGenBuffers (1, &(priv->gear_vbo[1])); |
821 | glBindBuffer (GL_ARRAY_BUFFER, priv->gear_vbo[1]); |
822 | glBufferData (GL_ARRAY_BUFFER, |
823 | priv->gear2->nvertices * sizeof(GearVertex), |
824 | priv->gear2->vertices, |
825 | GL_STATIC_DRAW); |
826 | |
827 | priv->gear3 = create_gear(inner_radius: 1.3, outer_radius: 2.0, width: 0.5, teeth: 10, tooth_depth: 0.7); |
828 | glGenBuffers (1, &(priv->gear_vbo[2])); |
829 | glBindBuffer (GL_ARRAY_BUFFER, priv->gear_vbo[2]); |
830 | glBufferData (GL_ARRAY_BUFFER, |
831 | priv->gear3->nvertices * sizeof(GearVertex), |
832 | priv->gear3->vertices, |
833 | GL_STATIC_DRAW); |
834 | } |
835 | |
836 | static void |
837 | gtk_gears_unrealize (GtkWidget *widget) |
838 | { |
839 | GtkGLArea *glarea = GTK_GL_AREA (widget); |
840 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: (GtkGears *) widget); |
841 | |
842 | gtk_gl_area_make_current (area: glarea); |
843 | if (gtk_gl_area_get_error (area: glarea) != NULL) |
844 | return; |
845 | |
846 | /* Release the resources associated with OpenGL */ |
847 | if (priv->gear_vbo[0] != 0) |
848 | glDeleteBuffers (1, &(priv->gear_vbo[0])); |
849 | |
850 | if (priv->gear_vbo[1] != 0) |
851 | glDeleteBuffers (1, &(priv->gear_vbo[1])); |
852 | |
853 | if (priv->gear_vbo[2] != 0) |
854 | glDeleteBuffers (1, &(priv->gear_vbo[2])); |
855 | |
856 | if (priv->vao != 0) |
857 | glDeleteVertexArrays (1, &priv->vao); |
858 | |
859 | if (priv->program != 0) |
860 | glDeleteProgram (priv->program); |
861 | |
862 | priv->ModelViewProjectionMatrix_location = 0; |
863 | priv->NormalMatrix_location = 0; |
864 | priv->LightSourcePosition_location = 0; |
865 | priv->MaterialColor_location = 0; |
866 | |
867 | GTK_WIDGET_CLASS (gtk_gears_parent_class)->unrealize (widget); |
868 | } |
869 | |
870 | static gboolean |
871 | gtk_gears_tick (GtkWidget *widget, |
872 | GdkFrameClock *frame_clock, |
873 | gpointer user_data) |
874 | { |
875 | GtkGears *gears = GTK_GEARS (widget); |
876 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
877 | GdkFrameTimings *timings, *previous_timings; |
878 | gint64 previous_frame_time = 0; |
879 | gint64 frame_time; |
880 | gint64 history_start, history_len; |
881 | gint64 frame; |
882 | char *s; |
883 | |
884 | frame = gdk_frame_clock_get_frame_counter (frame_clock); |
885 | frame_time = gdk_frame_clock_get_frame_time (frame_clock); |
886 | |
887 | if (priv->first_frame_time == 0) |
888 | { |
889 | /* No need for changes on first frame */ |
890 | priv->first_frame_time = frame_time; |
891 | if (priv->fps_label) |
892 | gtk_label_set_label (self: priv->fps_label, str: "FPS: ---" ); |
893 | return G_SOURCE_CONTINUE; |
894 | } |
895 | |
896 | /* glxgears advances 70 degrees per second, so do the same */ |
897 | |
898 | priv->angle = fmod (x: (frame_time - priv->first_frame_time) / (double)G_USEC_PER_SEC * 70.0, y: 360.0); |
899 | |
900 | gtk_widget_queue_draw (widget); |
901 | |
902 | history_start = gdk_frame_clock_get_history_start (frame_clock); |
903 | |
904 | if (priv->fps_label && frame % 60 == 0) |
905 | { |
906 | history_len = frame - history_start; |
907 | if (history_len > 0) |
908 | { |
909 | previous_timings = gdk_frame_clock_get_timings (frame_clock, frame_counter: frame - history_len); |
910 | previous_frame_time = gdk_frame_timings_get_frame_time (timings: previous_timings); |
911 | |
912 | s = g_strdup_printf (format: "FPS: %-4.1f" , (G_USEC_PER_SEC * history_len) / (double)(frame_time - previous_frame_time)); |
913 | gtk_label_set_label (self: priv->fps_label, str: s); |
914 | g_free (mem: s); |
915 | } |
916 | } |
917 | |
918 | timings = gdk_frame_clock_get_current_timings (frame_clock); |
919 | previous_timings = gdk_frame_clock_get_timings (frame_clock, |
920 | frame_counter: gdk_frame_timings_get_frame_counter (timings) - 1); |
921 | if (previous_timings != NULL) |
922 | previous_frame_time = gdk_frame_timings_get_frame_time (timings: previous_timings); |
923 | |
924 | return G_SOURCE_CONTINUE; |
925 | } |
926 | |
927 | void |
928 | gtk_gears_set_axis (GtkGears *gears, int axis, double value) |
929 | { |
930 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
931 | |
932 | if (axis < 0 || axis >= GTK_GEARS_N_AXIS) |
933 | return; |
934 | |
935 | priv->view_rot[axis] = value; |
936 | |
937 | gtk_widget_queue_draw (GTK_WIDGET (gears)); |
938 | } |
939 | |
940 | double |
941 | gtk_gears_get_axis (GtkGears *gears, int axis) |
942 | { |
943 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
944 | |
945 | if (axis < 0 || axis >= GTK_GEARS_N_AXIS) |
946 | return 0.0; |
947 | |
948 | return priv->view_rot[axis]; |
949 | } |
950 | |
951 | void |
952 | gtk_gears_set_fps_label (GtkGears *gears, GtkLabel *label) |
953 | { |
954 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
955 | |
956 | if (label) |
957 | g_object_ref (label); |
958 | |
959 | g_clear_object (&priv->fps_label); |
960 | |
961 | priv->fps_label = label; |
962 | } |
963 | |