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->vertices); |
227 | g_free (mem: g); |
228 | } |
229 | |
230 | /** |
231 | * Create a gear wheel. |
232 | * |
233 | * @param inner_radius radius of hole at center |
234 | * @param outer_radius radius at center of teeth |
235 | * @param width width of gear |
236 | * @param teeth number of teeth |
237 | * @param tooth_depth depth of tooth |
238 | * |
239 | * @return pointer to the constructed struct gear |
240 | */ |
241 | static struct gear * |
242 | create_gear (GLfloat inner_radius, |
243 | GLfloat outer_radius, |
244 | GLfloat width, |
245 | GLint teeth, |
246 | GLfloat tooth_depth) |
247 | { |
248 | GLfloat r0, r1, r2; |
249 | GLfloat da; |
250 | GearVertex *v; |
251 | struct gear *gear; |
252 | double s[5], c[5]; |
253 | GLfloat normal[3]; |
254 | int cur_strip = 0; |
255 | int i; |
256 | |
257 | /* Allocate memory for the gear */ |
258 | gear = g_malloc (n_bytes: sizeof *gear); |
259 | |
260 | /* Calculate the radii used in the gear */ |
261 | r0 = inner_radius; |
262 | r1 = outer_radius - tooth_depth / 2.0; |
263 | r2 = outer_radius + tooth_depth / 2.0; |
264 | |
265 | da = 2.0 * M_PI / teeth / 4.0; |
266 | |
267 | /* Allocate memory for the triangle strip information */ |
268 | gear->nstrips = STRIPS_PER_TOOTH * teeth; |
269 | gear->strips = g_malloc0_n (n_blocks: gear->nstrips, n_block_bytes: sizeof (*gear->strips)); |
270 | |
271 | /* Allocate memory for the vertices */ |
272 | gear->vertices = g_malloc0_n (VERTICES_PER_TOOTH * teeth, n_block_bytes: sizeof(*gear->vertices)); |
273 | v = gear->vertices; |
274 | |
275 | for (i = 0; i < teeth; i++) { |
276 | /* A set of macros for making the creation of the gears easier */ |
277 | #define GEAR_POINT(p, r, da) do { p.x = (r) * c[(da)]; p.y = (r) * s[(da)]; } while(0) |
278 | #define SET_NORMAL(x, y, z) do { \ |
279 | normal[0] = (x); normal[1] = (y); normal[2] = (z); \ |
280 | } while(0) |
281 | |
282 | #define GEAR_VERT(v, point, sign) vert((v), p[(point)].x, p[(point)].y, (sign) * width * 0.5, normal) |
283 | |
284 | #define START_STRIP do { \ |
285 | gear->strips[cur_strip].first = v - gear->vertices; \ |
286 | } while(0); |
287 | |
288 | #define END_STRIP do { \ |
289 | int _tmp = (v - gear->vertices); \ |
290 | gear->strips[cur_strip].count = _tmp - gear->strips[cur_strip].first; \ |
291 | cur_strip++; \ |
292 | } while (0) |
293 | |
294 | #define QUAD_WITH_NORMAL(p1, p2) do { \ |
295 | SET_NORMAL((p[(p1)].y - p[(p2)].y), -(p[(p1)].x - p[(p2)].x), 0); \ |
296 | v = GEAR_VERT(v, (p1), -1); \ |
297 | v = GEAR_VERT(v, (p1), 1); \ |
298 | v = GEAR_VERT(v, (p2), -1); \ |
299 | v = GEAR_VERT(v, (p2), 1); \ |
300 | } while(0) |
301 | struct point { |
302 | GLfloat x; |
303 | GLfloat y; |
304 | }; |
305 | |
306 | /* Create the 7 points (only x,y coords) used to draw a tooth */ |
307 | struct point p[7]; |
308 | |
309 | /* Calculate needed sin/cos for various angles */ |
310 | sincos(x: i * 2.0 * G_PI / teeth + da * 0, sinx: &s[0], cosx: &c[0]); |
311 | sincos(x: i * 2.0 * M_PI / teeth + da * 1, sinx: &s[1], cosx: &c[1]); |
312 | sincos(x: i * 2.0 * M_PI / teeth + da * 2, sinx: &s[2], cosx: &c[2]); |
313 | sincos(x: i * 2.0 * M_PI / teeth + da * 3, sinx: &s[3], cosx: &c[3]); |
314 | sincos(x: i * 2.0 * M_PI / teeth + da * 4, sinx: &s[4], cosx: &c[4]); |
315 | |
316 | GEAR_POINT(p[0], r2, 1); |
317 | GEAR_POINT(p[1], r2, 2); |
318 | GEAR_POINT(p[2], r1, 0); |
319 | GEAR_POINT(p[3], r1, 3); |
320 | GEAR_POINT(p[4], r0, 0); |
321 | GEAR_POINT(p[5], r1, 4); |
322 | GEAR_POINT(p[6], r0, 4); |
323 | |
324 | /* Front face */ |
325 | START_STRIP; |
326 | SET_NORMAL(0, 0, 1.0); |
327 | v = GEAR_VERT(v, 0, +1); |
328 | v = GEAR_VERT(v, 1, +1); |
329 | v = GEAR_VERT(v, 2, +1); |
330 | v = GEAR_VERT(v, 3, +1); |
331 | v = GEAR_VERT(v, 4, +1); |
332 | v = GEAR_VERT(v, 5, +1); |
333 | v = GEAR_VERT(v, 6, +1); |
334 | END_STRIP; |
335 | |
336 | /* Inner face */ |
337 | START_STRIP; |
338 | QUAD_WITH_NORMAL(4, 6); |
339 | END_STRIP; |
340 | |
341 | /* Back face */ |
342 | START_STRIP; |
343 | SET_NORMAL(0, 0, -1.0); |
344 | v = GEAR_VERT(v, 6, -1); |
345 | v = GEAR_VERT(v, 5, -1); |
346 | v = GEAR_VERT(v, 4, -1); |
347 | v = GEAR_VERT(v, 3, -1); |
348 | v = GEAR_VERT(v, 2, -1); |
349 | v = GEAR_VERT(v, 1, -1); |
350 | v = GEAR_VERT(v, 0, -1); |
351 | END_STRIP; |
352 | |
353 | /* Outer face */ |
354 | START_STRIP; |
355 | QUAD_WITH_NORMAL(0, 2); |
356 | END_STRIP; |
357 | |
358 | START_STRIP; |
359 | QUAD_WITH_NORMAL(1, 0); |
360 | END_STRIP; |
361 | |
362 | START_STRIP; |
363 | QUAD_WITH_NORMAL(3, 1); |
364 | END_STRIP; |
365 | |
366 | START_STRIP; |
367 | QUAD_WITH_NORMAL(5, 3); |
368 | END_STRIP; |
369 | } |
370 | |
371 | gear->nvertices = (v - gear->vertices); |
372 | |
373 | return gear; |
374 | } |
375 | |
376 | /** |
377 | * Multiplies two 4x4 matrices. |
378 | * |
379 | * The result is stored in matrix m. |
380 | * |
381 | * @param m the first matrix to multiply |
382 | * @param n the second matrix to multiply |
383 | */ |
384 | static void |
385 | multiply (GLfloat *m, const GLfloat *n) |
386 | { |
387 | GLfloat tmp[16]; |
388 | const GLfloat *row, *column; |
389 | div_t d; |
390 | int i, j; |
391 | |
392 | for (i = 0; i < 16; i++) { |
393 | tmp[i] = 0; |
394 | d = div(numer: i, denom: 4); |
395 | row = n + d.quot * 4; |
396 | column = m + d.rem; |
397 | for (j = 0; j < 4; j++) |
398 | tmp[i] += row[j] * column[j * 4]; |
399 | } |
400 | memcpy(dest: m, src: &tmp, n: sizeof tmp); |
401 | } |
402 | |
403 | /** |
404 | * Rotates a 4x4 matrix. |
405 | * |
406 | * @param[in,out] m the matrix to rotate |
407 | * @param angle the angle to rotate |
408 | * @param x the x component of the direction to rotate to |
409 | * @param y the y component of the direction to rotate to |
410 | * @param z the z component of the direction to rotate to |
411 | */ |
412 | static void |
413 | rotate(GLfloat *m, GLfloat angle, GLfloat x, GLfloat y, GLfloat z) |
414 | { |
415 | double s = sin (x: angle); |
416 | double c = cos (x: angle); |
417 | |
418 | GLfloat r[16] = { |
419 | x * x * (1 - c) + c, y * x * (1 - c) + z * s, x * z * (1 - c) - y * s, 0, |
420 | x * y * (1 - c) - z * s, y * y * (1 - c) + c, y * z * (1 - c) + x * s, 0, |
421 | x * z * (1 - c) + y * s, y * z * (1 - c) - x * s, z * z * (1 - c) + c, 0, |
422 | 0, 0, 0, 1 |
423 | }; |
424 | |
425 | multiply(m, n: r); |
426 | } |
427 | |
428 | /** |
429 | * Translates a 4x4 matrix. |
430 | * |
431 | * @param[in,out] m the matrix to translate |
432 | * @param x the x component of the direction to translate to |
433 | * @param y the y component of the direction to translate to |
434 | * @param z the z component of the direction to translate to |
435 | */ |
436 | static void |
437 | translate(GLfloat *m, GLfloat x, GLfloat y, GLfloat z) |
438 | { |
439 | GLfloat t[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 }; |
440 | |
441 | multiply(m, n: t); |
442 | } |
443 | |
444 | /** |
445 | * Creates an identity 4x4 matrix. |
446 | * |
447 | * @param m the matrix make an identity matrix |
448 | */ |
449 | static void |
450 | identity(GLfloat *m) |
451 | { |
452 | GLfloat t[16] = { |
453 | 1.0, 0.0, 0.0, 0.0, |
454 | 0.0, 1.0, 0.0, 0.0, |
455 | 0.0, 0.0, 1.0, 0.0, |
456 | 0.0, 0.0, 0.0, 1.0, |
457 | }; |
458 | |
459 | memcpy(dest: m, src: t, n: sizeof(t)); |
460 | } |
461 | |
462 | /** |
463 | * Transposes a 4x4 matrix. |
464 | * |
465 | * @param m the matrix to transpose |
466 | */ |
467 | static void |
468 | transpose(GLfloat *m) |
469 | { |
470 | GLfloat t[16] = { |
471 | m[0], m[4], m[8], m[12], |
472 | m[1], m[5], m[9], m[13], |
473 | m[2], m[6], m[10], m[14], |
474 | m[3], m[7], m[11], m[15]}; |
475 | |
476 | memcpy(dest: m, src: t, n: sizeof(t)); |
477 | } |
478 | |
479 | /** |
480 | * Inverts a 4x4 matrix. |
481 | * |
482 | * This function can currently handle only pure translation-rotation matrices. |
483 | * Read http://www.gamedev.net/community/forums/topic.asp?topic_id=425118 |
484 | * for an explanation. |
485 | */ |
486 | static void |
487 | invert(GLfloat *m) |
488 | { |
489 | GLfloat t[16]; |
490 | identity(m: t); |
491 | |
492 | // Extract and invert the translation part 't'. The inverse of a |
493 | // translation matrix can be calculated by negating the translation |
494 | // coordinates. |
495 | t[12] = -m[12]; t[13] = -m[13]; t[14] = -m[14]; |
496 | |
497 | // Invert the rotation part 'r'. The inverse of a rotation matrix is |
498 | // equal to its transpose. |
499 | m[12] = m[13] = m[14] = 0; |
500 | transpose(m); |
501 | |
502 | // inv(m) = inv(r) * inv(t) |
503 | multiply(m, n: t); |
504 | } |
505 | |
506 | /** |
507 | * Calculate a perspective projection transformation. |
508 | * |
509 | * @param m the matrix to save the transformation in |
510 | * @param fovy the field of view in the y direction |
511 | * @param aspect the view aspect ratio |
512 | * @param zNear the near clipping plane |
513 | * @param zFar the far clipping plane |
514 | */ |
515 | void perspective(GLfloat *m, GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar) |
516 | { |
517 | GLfloat tmp[16]; |
518 | double sine, cosine, cotangent, deltaZ; |
519 | GLfloat radians = fovy / 2 * M_PI / 180; |
520 | identity(m: tmp); |
521 | |
522 | deltaZ = zFar - zNear; |
523 | sincos(x: radians, sinx: &sine, cosx: &cosine); |
524 | |
525 | if ((deltaZ == 0) || (sine == 0) || (aspect == 0)) |
526 | return; |
527 | |
528 | cotangent = cosine / sine; |
529 | |
530 | tmp[0] = cotangent / aspect; |
531 | tmp[5] = cotangent; |
532 | tmp[10] = -(zFar + zNear) / deltaZ; |
533 | tmp[11] = -1; |
534 | tmp[14] = -2 * zNear * zFar / deltaZ; |
535 | tmp[15] = 0; |
536 | |
537 | memcpy(dest: m, src: tmp, n: sizeof(tmp)); |
538 | } |
539 | |
540 | /** |
541 | * Draws a gear. |
542 | * |
543 | * @param gear the gear to draw |
544 | * @param transform the current transformation matrix |
545 | * @param x the x position to draw the gear at |
546 | * @param y the y position to draw the gear at |
547 | * @param angle the rotation angle of the gear |
548 | * @param color the color of the gear |
549 | */ |
550 | static void |
551 | draw_gear(GtkGears *self, |
552 | struct gear *gear, |
553 | GLuint gear_vbo, |
554 | GLfloat *transform, |
555 | GLfloat x, |
556 | GLfloat y, |
557 | GLfloat angle, |
558 | const GLfloat color[4]) |
559 | { |
560 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self); |
561 | GLfloat model_view[16]; |
562 | GLfloat normal_matrix[16]; |
563 | GLfloat model_view_projection[16]; |
564 | int n; |
565 | |
566 | /* Translate and rotate the gear */ |
567 | memcpy(dest: model_view, src: transform, n: sizeof (model_view)); |
568 | translate(m: model_view, x, y, z: 0); |
569 | rotate(m: model_view, angle: 2 * G_PI * angle / 360.0, x: 0, y: 0, z: 1); |
570 | |
571 | /* Create and set the ModelViewProjectionMatrix */ |
572 | memcpy(dest: model_view_projection, src: priv->ProjectionMatrix, n: sizeof(model_view_projection)); |
573 | multiply(m: model_view_projection, n: model_view); |
574 | |
575 | glUniformMatrix4fv(priv->ModelViewProjectionMatrix_location, 1, GL_FALSE, |
576 | model_view_projection); |
577 | |
578 | /* |
579 | * Create and set the NormalMatrix. It's the inverse transpose of the |
580 | * ModelView matrix. |
581 | */ |
582 | memcpy(dest: normal_matrix, src: model_view, n: sizeof (normal_matrix)); |
583 | invert(m: normal_matrix); |
584 | transpose(m: normal_matrix); |
585 | glUniformMatrix4fv(priv->NormalMatrix_location, 1, GL_FALSE, normal_matrix); |
586 | |
587 | /* Set the gear color */ |
588 | glUniform4fv(priv->MaterialColor_location, 1, color); |
589 | |
590 | /* Set the vertex buffer object to use */ |
591 | glBindBuffer(GL_ARRAY_BUFFER, gear_vbo); |
592 | |
593 | /* Set up the position of the attributes in the vertex buffer object */ |
594 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), NULL); |
595 | glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLfloat *) 0 + 3); |
596 | |
597 | /* Enable the attributes */ |
598 | glEnableVertexAttribArray(0); |
599 | glEnableVertexAttribArray(1); |
600 | |
601 | /* Draw the triangle strips that comprise the gear */ |
602 | for (n = 0; n < gear->nstrips; n++) { |
603 | glDrawArrays(GL_TRIANGLE_STRIP, gear->strips[n].first, gear->strips[n].count); |
604 | } |
605 | |
606 | /* Disable the attributes */ |
607 | glDisableVertexAttribArray(1); |
608 | glDisableVertexAttribArray(0); |
609 | } |
610 | |
611 | /* new window size or exposure */ |
612 | static void |
613 | gtk_gears_reshape (GtkGLArea *area, int width, int height) |
614 | { |
615 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: (GtkGears *) area); |
616 | |
617 | /* Update the projection matrix */ |
618 | perspective (m: priv->ProjectionMatrix, fovy: 60.0, aspect: width / (float)height, zNear: 1.0, zFar: 1024.0); |
619 | |
620 | /* Set the viewport */ |
621 | glViewport (0, 0, (GLint) width, (GLint) height); |
622 | } |
623 | |
624 | static gboolean |
625 | gtk_gears_render (GtkGLArea *area, |
626 | GdkGLContext *context) |
627 | { |
628 | static const GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 }; |
629 | static const GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 }; |
630 | static const GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 }; |
631 | |
632 | GtkGears *self = GTK_GEARS (area); |
633 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self); |
634 | GLfloat transform[16]; |
635 | |
636 | identity (m: transform); |
637 | |
638 | glClearColor (0.0, 0.0, 0.0, 0.0); |
639 | glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
640 | |
641 | /* Translate and rotate the view */ |
642 | translate (m: transform, x: 0, y: 0, z: -20); |
643 | rotate (m: transform, angle: 2 * G_PI * priv->view_rot[0] / 360.0, x: 1, y: 0, z: 0); |
644 | rotate (m: transform, angle: 2 * G_PI * priv->view_rot[1] / 360.0, x: 0, y: 1, z: 0); |
645 | rotate (m: transform, angle: 2 * G_PI * priv->view_rot[2] / 360.0, x: 0, y: 0, z: 1); |
646 | |
647 | /* Draw the gears */ |
648 | draw_gear (self, gear: priv->gear1, gear_vbo: priv->gear_vbo[0], transform, x: -3.0, y: -2.0, angle: priv->angle, color: red); |
649 | 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); |
650 | 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); |
651 | |
652 | return TRUE; |
653 | } |
654 | |
655 | static const char vertex_shader_gl[] = |
656 | "#version 150\n" |
657 | "\n" |
658 | "in vec3 position;\n" |
659 | "in vec3 normal;\n" |
660 | "\n" |
661 | "uniform mat4 ModelViewProjectionMatrix;\n" |
662 | "uniform mat4 NormalMatrix;\n" |
663 | "uniform vec4 LightSourcePosition;\n" |
664 | "uniform vec4 MaterialColor;\n" |
665 | "\n" |
666 | "smooth out vec4 Color;\n" |
667 | "\n" |
668 | "void main(void)\n" |
669 | "{\n" |
670 | " // Transform the normal to eye coordinates\n" |
671 | " vec3 N = normalize(vec3(NormalMatrix * vec4(normal, 1.0)));\n" |
672 | "\n" |
673 | " // The LightSourcePosition is actually its direction for directional light\n" |
674 | " vec3 L = normalize(LightSourcePosition.xyz);\n" |
675 | "\n" |
676 | " // Multiply the diffuse value by the vertex color (which is fixed in this case)\n" |
677 | " // to get the actual color that we will use to draw this vertex with\n" |
678 | " float diffuse = max(dot(N, L), 0.0);\n" |
679 | " Color = diffuse * MaterialColor;\n" |
680 | "\n" |
681 | " // Transform the position to clip coordinates\n" |
682 | " gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0);\n" |
683 | "}" ; |
684 | |
685 | static const char fragment_shader_gl[] = |
686 | "#version 150\n" |
687 | "\n" |
688 | "smooth in vec4 Color;\n" |
689 | "\n" |
690 | "void main(void)\n" |
691 | "{\n" |
692 | " gl_FragColor = Color;\n" |
693 | "}" ; |
694 | |
695 | static const char vertex_shader_gles[] = |
696 | "attribute vec3 position;\n" |
697 | "attribute vec3 normal;\n" |
698 | "\n" |
699 | "uniform mat4 ModelViewProjectionMatrix;\n" |
700 | "uniform mat4 NormalMatrix;\n" |
701 | "uniform vec4 LightSourcePosition;\n" |
702 | "uniform vec4 MaterialColor;\n" |
703 | "\n" |
704 | "varying vec4 Color;\n" |
705 | "\n" |
706 | "void main(void)\n" |
707 | "{\n" |
708 | " // Transform the normal to eye coordinates\n" |
709 | " vec3 N = normalize(vec3(NormalMatrix * vec4(normal, 1.0)));\n" |
710 | "\n" |
711 | " // The LightSourcePosition is actually its direction for directional light\n" |
712 | " vec3 L = normalize(LightSourcePosition.xyz);\n" |
713 | "\n" |
714 | " // Multiply the diffuse value by the vertex color (which is fixed in this case)\n" |
715 | " // to get the actual color that we will use to draw this vertex with\n" |
716 | " float diffuse = max(dot(N, L), 0.0);\n" |
717 | " Color = diffuse * MaterialColor;\n" |
718 | "\n" |
719 | " // Transform the position to clip coordinates\n" |
720 | " gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0);\n" |
721 | "}" ; |
722 | |
723 | static const char fragment_shader_gles[] = |
724 | "precision mediump float;\n" |
725 | "varying vec4 Color;\n" |
726 | "\n" |
727 | "void main(void)\n" |
728 | "{\n" |
729 | " gl_FragColor = Color;\n" |
730 | "}" ; |
731 | |
732 | static void |
733 | gtk_gears_realize (GtkWidget *widget) |
734 | { |
735 | GtkGLArea *glarea = GTK_GL_AREA (widget); |
736 | GtkGears *gears = GTK_GEARS (widget); |
737 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
738 | GdkGLContext *context; |
739 | GLuint vao, v, f, program; |
740 | const char *p; |
741 | char msg[512]; |
742 | |
743 | GTK_WIDGET_CLASS (gtk_gears_parent_class)->realize (widget); |
744 | |
745 | gtk_gl_area_make_current (area: glarea); |
746 | if (gtk_gl_area_get_error (area: glarea) != NULL) |
747 | return; |
748 | |
749 | context = gtk_gl_area_get_context (area: glarea); |
750 | |
751 | glEnable (GL_CULL_FACE); |
752 | glEnable (GL_DEPTH_TEST); |
753 | |
754 | /* Create the VAO */ |
755 | glGenVertexArrays (1, &vao); |
756 | glBindVertexArray (vao); |
757 | priv->vao = vao; |
758 | |
759 | /* Compile the vertex shader */ |
760 | if (gdk_gl_context_get_use_es (context)) |
761 | p = vertex_shader_gles; |
762 | else |
763 | p = vertex_shader_gl; |
764 | v = glCreateShader(GL_VERTEX_SHADER); |
765 | glShaderSource(v, 1, &p, NULL); |
766 | glCompileShader(v); |
767 | glGetShaderInfoLog(v, sizeof msg, NULL, msg); |
768 | g_debug ("vertex shader info: %s\n" , msg); |
769 | |
770 | /* Compile the fragment shader */ |
771 | if (gdk_gl_context_get_use_es (context)) |
772 | p = fragment_shader_gles; |
773 | else |
774 | p = fragment_shader_gl; |
775 | f = glCreateShader(GL_FRAGMENT_SHADER); |
776 | glShaderSource(f, 1, &p, NULL); |
777 | glCompileShader(f); |
778 | glGetShaderInfoLog(f, sizeof msg, NULL, msg); |
779 | g_debug ("fragment shader info: %s\n" , msg); |
780 | |
781 | /* Create and link the shader program */ |
782 | program = glCreateProgram(); |
783 | glAttachShader(program, v); |
784 | glAttachShader(program, f); |
785 | glBindAttribLocation(program, 0, "position" ); |
786 | glBindAttribLocation(program, 1, "normal" ); |
787 | |
788 | glLinkProgram(program); |
789 | glGetProgramInfoLog(program, sizeof msg, NULL, msg); |
790 | g_debug ("program info: %s\n" , msg); |
791 | glDetachShader (program, v); |
792 | glDetachShader (program, f); |
793 | glDeleteShader (v); |
794 | glDeleteShader (f); |
795 | |
796 | /* Enable the shaders */ |
797 | glUseProgram(program); |
798 | priv->program = program; |
799 | |
800 | /* Get the locations of the uniforms so we can access them */ |
801 | priv->ModelViewProjectionMatrix_location = glGetUniformLocation(program, "ModelViewProjectionMatrix" ); |
802 | priv->NormalMatrix_location = glGetUniformLocation(program, "NormalMatrix" ); |
803 | priv->LightSourcePosition_location = glGetUniformLocation(program, "LightSourcePosition" ); |
804 | priv->MaterialColor_location = glGetUniformLocation(program, "MaterialColor" ); |
805 | |
806 | /* Set the LightSourcePosition uniform which is constant throughout the program */ |
807 | glUniform4fv(priv->LightSourcePosition_location, 1, priv->LightSourcePosition); |
808 | |
809 | /* make the gears */ |
810 | priv->gear1 = create_gear(inner_radius: 1.0, outer_radius: 4.0, width: 1.0, teeth: 20, tooth_depth: 0.7); |
811 | |
812 | /* Store the vertices in a vertex buffer object (VBO) */ |
813 | glGenBuffers (1, &(priv->gear_vbo[0])); |
814 | glBindBuffer (GL_ARRAY_BUFFER, priv->gear_vbo[0]); |
815 | glBufferData (GL_ARRAY_BUFFER, |
816 | priv->gear1->nvertices * sizeof(GearVertex), |
817 | priv->gear1->vertices, |
818 | GL_STATIC_DRAW); |
819 | |
820 | priv->gear2 = create_gear(inner_radius: 0.5, outer_radius: 2.0, width: 2.0, teeth: 10, tooth_depth: 0.7); |
821 | glGenBuffers (1, &(priv->gear_vbo[1])); |
822 | glBindBuffer (GL_ARRAY_BUFFER, priv->gear_vbo[1]); |
823 | glBufferData (GL_ARRAY_BUFFER, |
824 | priv->gear2->nvertices * sizeof(GearVertex), |
825 | priv->gear2->vertices, |
826 | GL_STATIC_DRAW); |
827 | |
828 | priv->gear3 = create_gear(inner_radius: 1.3, outer_radius: 2.0, width: 0.5, teeth: 10, tooth_depth: 0.7); |
829 | glGenBuffers (1, &(priv->gear_vbo[2])); |
830 | glBindBuffer (GL_ARRAY_BUFFER, priv->gear_vbo[2]); |
831 | glBufferData (GL_ARRAY_BUFFER, |
832 | priv->gear3->nvertices * sizeof(GearVertex), |
833 | priv->gear3->vertices, |
834 | GL_STATIC_DRAW); |
835 | } |
836 | |
837 | static void |
838 | gtk_gears_unrealize (GtkWidget *widget) |
839 | { |
840 | GtkGLArea *glarea = GTK_GL_AREA (widget); |
841 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: (GtkGears *) widget); |
842 | |
843 | gtk_gl_area_make_current (area: glarea); |
844 | if (gtk_gl_area_get_error (area: glarea) != NULL) |
845 | return; |
846 | |
847 | /* Release the resources associated with OpenGL */ |
848 | if (priv->gear_vbo[0] != 0) |
849 | glDeleteBuffers (1, &(priv->gear_vbo[0])); |
850 | |
851 | if (priv->gear_vbo[1] != 0) |
852 | glDeleteBuffers (1, &(priv->gear_vbo[1])); |
853 | |
854 | if (priv->gear_vbo[2] != 0) |
855 | glDeleteBuffers (1, &(priv->gear_vbo[2])); |
856 | |
857 | if (priv->vao != 0) |
858 | glDeleteVertexArrays (1, &priv->vao); |
859 | |
860 | if (priv->program != 0) |
861 | glDeleteProgram (priv->program); |
862 | |
863 | priv->ModelViewProjectionMatrix_location = 0; |
864 | priv->NormalMatrix_location = 0; |
865 | priv->LightSourcePosition_location = 0; |
866 | priv->MaterialColor_location = 0; |
867 | |
868 | GTK_WIDGET_CLASS (gtk_gears_parent_class)->unrealize (widget); |
869 | } |
870 | |
871 | static gboolean |
872 | gtk_gears_tick (GtkWidget *widget, |
873 | GdkFrameClock *frame_clock, |
874 | gpointer user_data) |
875 | { |
876 | GtkGears *gears = GTK_GEARS (widget); |
877 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
878 | GdkFrameTimings *previous_timings; |
879 | gint64 previous_frame_time; |
880 | gint64 frame_time; |
881 | gint64 history_start, history_len; |
882 | gint64 frame; |
883 | char *s; |
884 | |
885 | frame = gdk_frame_clock_get_frame_counter (frame_clock); |
886 | frame_time = gdk_frame_clock_get_frame_time (frame_clock); |
887 | |
888 | if (priv->first_frame_time == 0) |
889 | { |
890 | /* No need for changes on first frame */ |
891 | priv->first_frame_time = frame_time; |
892 | if (priv->fps_label) |
893 | gtk_label_set_label (self: priv->fps_label, str: "FPS: ---" ); |
894 | return G_SOURCE_CONTINUE; |
895 | } |
896 | |
897 | /* glxgears advances 70 degrees per second, so do the same */ |
898 | |
899 | priv->angle = fmod (x: (frame_time - priv->first_frame_time) / (double)G_USEC_PER_SEC * 70.0, y: 360.0); |
900 | |
901 | gtk_widget_queue_draw (widget); |
902 | |
903 | history_start = gdk_frame_clock_get_history_start (frame_clock); |
904 | |
905 | if (priv->fps_label && frame % 60 == 0) |
906 | { |
907 | history_len = frame - history_start; |
908 | if (history_len > 0) |
909 | { |
910 | previous_timings = gdk_frame_clock_get_timings (frame_clock, frame_counter: frame - history_len); |
911 | previous_frame_time = gdk_frame_timings_get_frame_time (timings: previous_timings); |
912 | |
913 | s = g_strdup_printf (format: "FPS: %-4.1f" , (G_USEC_PER_SEC * history_len) / (double)(frame_time - previous_frame_time)); |
914 | gtk_label_set_label (self: priv->fps_label, str: s); |
915 | g_free (mem: s); |
916 | } |
917 | } |
918 | |
919 | return G_SOURCE_CONTINUE; |
920 | } |
921 | |
922 | void |
923 | gtk_gears_set_axis (GtkGears *gears, int axis, double value) |
924 | { |
925 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
926 | |
927 | if (axis < 0 || axis >= GTK_GEARS_N_AXIS) |
928 | return; |
929 | |
930 | priv->view_rot[axis] = value; |
931 | |
932 | gtk_widget_queue_draw (GTK_WIDGET (gears)); |
933 | } |
934 | |
935 | double |
936 | gtk_gears_get_axis (GtkGears *gears, int axis) |
937 | { |
938 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
939 | |
940 | if (axis < 0 || axis >= GTK_GEARS_N_AXIS) |
941 | return 0.0; |
942 | |
943 | return priv->view_rot[axis]; |
944 | } |
945 | |
946 | void |
947 | gtk_gears_set_fps_label (GtkGears *gears, GtkLabel *label) |
948 | { |
949 | GtkGearsPrivate *priv = gtk_gears_get_instance_private (self: gears); |
950 | |
951 | if (label) |
952 | g_object_ref (label); |
953 | |
954 | g_clear_object (&priv->fps_label); |
955 | |
956 | priv->fps_label = label; |
957 | } |
958 | |