• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2008 Cyril Comparon <cyril.comparon@gmail.com>
4  * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 /**
23  * SECTION:element-glbumper
24  * @title: glbumper
25  *
26  * Bump mapping using the normal method.
27  *
28  * ## Examples
29  * |[
30  * gst-launch-1.0 -v videotestsrc ! glupload ! glbumper location=normalmap.bmp ! glimagesink
31  * ]| A pipeline to test normal mapping.
32  * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required.
33  *
34  */
35 
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39 
40 #include <stdlib.h>
41 #include <png.h>
42 
43 #include "gstglelements.h"
44 #include "gstglbumper.h"
45 
46 #if PNG_LIBPNG_VER >= 10400
47 #define int_p_NULL         NULL
48 #define png_infopp_NULL    NULL
49 #endif
50 
51 #define GST_CAT_DEFAULT gst_gl_bumper_debug
52 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
53 
54 enum
55 {
56   PROP_0,
57   PROP_LOCATION
58 };
59 
60 #define DEBUG_INIT \
61   GST_DEBUG_CATEGORY_INIT (gst_gl_bumper_debug, "glbumper", 0, "glbumper element");
62 
63 G_DEFINE_TYPE_WITH_CODE (GstGLBumper, gst_gl_bumper, GST_TYPE_GL_FILTER,
64     DEBUG_INIT);
65 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (glbumper, "glbumper",
66     GST_RANK_NONE, GST_TYPE_GL_BUMPER, gl_element_init (plugin));
67 
68 static void gst_gl_bumper_set_property (GObject * object, guint prop_id,
69     const GValue * value, GParamSpec * pspec);
70 static void gst_gl_bumper_get_property (GObject * object, guint prop_id,
71     GValue * value, GParamSpec * pspec);
72 
73 static void gst_gl_bumper_reset (GstGLFilter * filter);
74 static gboolean gst_gl_bumper_init_shader (GstGLFilter * filter);
75 static gboolean gst_gl_bumper_filter_texture (GstGLFilter * filter,
76     guint in_tex, guint out_tex);
77 static void gst_gl_bumper_callback (gint width, gint height, guint texture,
78     gpointer stuff);
79 
80 //vertex source
81 static const gchar *bumper_v_src =
82     "attribute vec3 aTangent;\n"
83     "\n"
84     "varying vec3 vNormal;\n"
85     "varying vec3 vTangent;\n"
86     "varying vec3 vVertexToLight0;\n"
87     "varying vec3 vVertexToLight1;\n"
88     "\n"
89     "void main()\n"
90     "{\n"
91     "  // transform the vertex\n"
92     "  gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n"
93     "\n"
94     "  // transform the normal and the tangent to scene coords\n"
95     "  vNormal = normalize(gl_NormalMatrix * gl_Normal);\n"
96     "  vTangent = normalize(gl_NormalMatrix * aTangent);\n"
97     "\n"
98     "  // transforming the vertex position to modelview-space\n"
99     "  //const vec4 vertexInSceneCoords = gl_ModelViewMatrix * gl_Vertex;\n"
100     "\n"
101     "  // calculate the vector from the vertex position to the light position\n"
102     "  vVertexToLight0 = normalize(gl_LightSource[0].position).xyz;\n"
103     "  vVertexToLight1 = normalize(gl_LightSource[1].position).xyz;\n"
104     "\n"
105     "  // transit vertex color\n"
106     "  gl_FrontColor = gl_BackColor = gl_Color;\n"
107     "\n"
108     "  // use the two first sets of texture coordinates in the fragment shader\n"
109     "  gl_TexCoord[0] = gl_MultiTexCoord0;\n"
110     "  gl_TexCoord[1] = gl_MultiTexCoord1;\n" "}\n";
111 
112 //fragment source
113 static const gchar *bumper_f_src =
114     "uniform sampler2D texture0;\n"
115     "uniform sampler2D texture1;\n"
116     "\n"
117     "varying vec3 vNormal;\n"
118     "varying vec3 vTangent;\n"
119     "varying vec3 vVertexToLight0;\n"
120     "varying vec3 vVertexToLight1;\n"
121     "\n"
122     "void main()\n"
123     "{\n"
124     "  // get the color of the textures\n"
125     "  vec4 textureColor = texture2D(texture0, gl_TexCoord[0].st);\n"
126     "  vec3 normalmapItem = texture2D(texture1, gl_TexCoord[1].st).xyz * 2.0 - 1.0;\n"
127     "\n"
128     "  // calculate matrix that transform from tangent space to normalmap space (contrary of intuition)\n"
129     "  vec3 binormal = cross(vNormal, vTangent);\n"
130     "  mat3 tangentSpace2normalmapSpaceMat = mat3(vTangent, binormal, vNormal);\n"
131     "\n"
132     "  // disturb the normal\n"
133     "  vec3 disturbedNormal = tangentSpace2normalmapSpaceMat * normalmapItem;\n"
134     "\n"
135     "  // calculate the diffuse term and clamping it to [0;1]\n"
136     "  float diffuseTerm0 = clamp(dot(disturbedNormal, vVertexToLight0), 0.0, 1.0);\n"
137     "  float diffuseTerm1 = clamp(dot(disturbedNormal, vVertexToLight1), 0.0, 1.0);\n"
138     "\n"
139     "  vec3 irradiance = (diffuseTerm0 * gl_LightSource[0].diffuse.rgb + diffuseTerm1 * gl_LightSource[1].diffuse.rgb);\n"
140     "\n"
141     "  // calculate the final color\n"
142     "  gl_FragColor = vec4(irradiance * textureColor.rgb, textureColor.w);\n"
143     "}\n";
144 
145 #define LOAD_ERROR(context, msg) { gst_gl_context_set_error (context, "unable to load %s: %s", bumper->location, msg); return; }
146 
147 //png reading error handler
148 static void
user_warning_fn(png_structp png_ptr,png_const_charp warning_msg)149 user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
150 {
151   g_warning ("%s\n", warning_msg);
152 }
153 
154 //Called in the gl thread
155 static void
gst_gl_bumper_init_resources(GstGLFilter * filter)156 gst_gl_bumper_init_resources (GstGLFilter * filter)
157 {
158   GstGLBumper *bumper = GST_GL_BUMPER (filter);
159   GstGLContext *context = filter->context;
160   const GstGLFuncs *gl = context->gl_vtable;
161 
162   png_structp png_ptr;
163   png_infop info_ptr;
164   png_uint_32 width = 0;
165   png_uint_32 height = 0;
166   gint bit_depth = 0;
167   gint color_type = 0;
168   gint interlace_type = 0;
169   png_FILE_p fp = NULL;
170   guint y = 0;
171   guchar *raw_data = NULL;
172   guchar **rows = NULL;
173   png_byte magic[8];
174   gint n_read;
175 
176   if (!bumper->location) {
177     gst_gl_context_set_error (context, "A filename is required");
178     return;
179   }
180 
181   /* BEGIN load png image file */
182 
183   if ((fp = fopen (bumper->location, "rb")) == NULL)
184     LOAD_ERROR (context, "file not found");
185 
186   /* Read magic number */
187   n_read = fread (magic, 1, sizeof (magic), fp);
188   if (n_read != sizeof (magic)) {
189     fclose (fp);
190     LOAD_ERROR (context, "can't read PNG magic number");
191   }
192 
193   /* Check for valid magic number */
194   if (png_sig_cmp (magic, 0, sizeof (magic))) {
195     fclose (fp);
196     LOAD_ERROR (context, "not a valid PNG image");
197   }
198 
199   png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
200 
201   if (png_ptr == NULL) {
202     fclose (fp);
203     LOAD_ERROR (context, "failed to initialize the png_struct");
204   }
205 
206   png_set_error_fn (png_ptr, NULL, NULL, user_warning_fn);
207 
208   info_ptr = png_create_info_struct (png_ptr);
209   if (info_ptr == NULL) {
210     fclose (fp);
211     png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL);
212     LOAD_ERROR (context,
213         "failed to initialize the memory for image information");
214   }
215 
216   png_init_io (png_ptr, fp);
217 
218   png_set_sig_bytes (png_ptr, sizeof (magic));
219 
220   png_read_info (png_ptr, info_ptr);
221 
222   png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
223       &interlace_type, int_p_NULL, int_p_NULL);
224 
225   if (color_type != PNG_COLOR_TYPE_RGB) {
226     fclose (fp);
227     png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL);
228     LOAD_ERROR (context, "color type is not rgb");
229   }
230 
231   raw_data = (guchar *) malloc (sizeof (guchar) * width * height * 3);
232 
233   rows = (guchar **) malloc (sizeof (guchar *) * height);
234 
235   for (y = 0; y < height; ++y)
236     rows[y] = (guchar *) (raw_data + y * width * 3);
237 
238   png_read_image (png_ptr, rows);
239 
240   free (rows);
241 
242   png_read_end (png_ptr, info_ptr);
243   png_destroy_read_struct (&png_ptr, &info_ptr, png_infopp_NULL);
244   fclose (fp);
245 
246   /* END load png image file */
247 
248   bumper->bumpmap_width = width;
249   bumper->bumpmap_height = height;
250 
251   gl->GenTextures (1, &bumper->bumpmap);
252   gl->BindTexture (GL_TEXTURE_2D, bumper->bumpmap);
253   gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
254       bumper->bumpmap_width, bumper->bumpmap_height, 0,
255       GL_RGB, GL_UNSIGNED_BYTE, raw_data);
256   gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
257   gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
258   gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
259   gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
260 
261   free (raw_data);
262 }
263 
264 //Called in the gl thread
265 static void
gst_gl_bumper_reset_resources(GstGLFilter * filter)266 gst_gl_bumper_reset_resources (GstGLFilter * filter)
267 {
268   GstGLBumper *bumper = GST_GL_BUMPER (filter);
269 
270   if (bumper->bumpmap) {
271     glDeleteTextures (1, &bumper->bumpmap);
272     bumper->bumpmap = 0;
273   }
274 }
275 
276 static void
gst_gl_bumper_class_init(GstGLBumperClass * klass)277 gst_gl_bumper_class_init (GstGLBumperClass * klass)
278 {
279   GObjectClass *gobject_class;
280   GstElementClass *element_class;
281 
282   gobject_class = (GObjectClass *) klass;
283   element_class = GST_ELEMENT_CLASS (klass);
284   gobject_class->set_property = gst_gl_bumper_set_property;
285   gobject_class->get_property = gst_gl_bumper_get_property;
286 
287   gst_gl_filter_add_rgba_pad_templates (GST_GL_FILTER_CLASS (klass));
288 
289   GST_GL_FILTER_CLASS (klass)->filter_texture = gst_gl_bumper_filter_texture;
290   GST_GL_FILTER_CLASS (klass)->display_init_cb = gst_gl_bumper_init_resources;
291   GST_GL_FILTER_CLASS (klass)->display_reset_cb = gst_gl_bumper_reset_resources;
292   GST_GL_FILTER_CLASS (klass)->init_fbo = gst_gl_bumper_init_shader;
293   GST_GL_FILTER_CLASS (klass)->onReset = gst_gl_bumper_reset;
294 
295   g_object_class_install_property (gobject_class,
296       PROP_LOCATION, g_param_spec_string ("location",
297           "Normal map location",
298           "Normal map location", NULL,
299           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
300 
301   gst_element_class_set_metadata (element_class, "OpenGL bumper filter",
302       "Filter/Effect/Video", "Bump mapping filter",
303       "Cyril Comparon <cyril.comparon@gmail.com>, "
304       "Julien Isorce <julien.isorce@gmail.com>");
305 
306   GST_GL_BASE_FILTER_CLASS (klass)->supported_gl_api = GST_GL_API_OPENGL;
307 }
308 
309 static void
gst_gl_bumper_init(GstGLBumper * bumper)310 gst_gl_bumper_init (GstGLBumper * bumper)
311 {
312   bumper->shader = NULL;
313   bumper->bumpmap = 0;
314   bumper->bumpmap_width = 0;
315   bumper->bumpmap_height = 0;
316   bumper->location = NULL;
317 }
318 
319 static void
gst_gl_bumper_reset(GstGLFilter * filter)320 gst_gl_bumper_reset (GstGLFilter * filter)
321 {
322   GstGLBumper *bumper_filter = GST_GL_BUMPER (filter);
323 
324   //blocking call, wait the opengl thread has destroyed the shader
325   if (bumper_filter->shader)
326     gst_gl_context_del_shader (filter->context, bumper_filter->shader);
327   bumper_filter->shader = NULL;
328   bumper_filter->xrot = 0.0;
329   bumper_filter->yrot = 0.0;
330   bumper_filter->zrot = 0.0;
331 }
332 
333 static void
gst_gl_bumper_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)334 gst_gl_bumper_set_property (GObject * object, guint prop_id,
335     const GValue * value, GParamSpec * pspec)
336 {
337   GstGLBumper *bumper = GST_GL_BUMPER (object);
338 
339   switch (prop_id) {
340     case PROP_LOCATION:
341       g_free (bumper->location);
342       bumper->location = g_value_dup_string (value);
343       break;
344     default:
345       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346       break;
347   }
348 }
349 
350 static void
gst_gl_bumper_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)351 gst_gl_bumper_get_property (GObject * object, guint prop_id,
352     GValue * value, GParamSpec * pspec)
353 {
354   GstGLBumper *bumper = GST_GL_BUMPER (object);
355 
356   switch (prop_id) {
357     case PROP_LOCATION:
358       g_value_set_string (value, bumper->location);
359       break;
360     default:
361       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
362       break;
363   }
364 }
365 
366 static gboolean
gst_gl_bumper_init_shader(GstGLFilter * filter)367 gst_gl_bumper_init_shader (GstGLFilter * filter)
368 {
369   GstGLBumper *bumper = GST_GL_BUMPER (filter);
370 
371   //blocking call, wait the opengl thread has compiled the shader
372   return gst_gl_context_gen_shader (filter->context, bumper_v_src, bumper_f_src,
373       &bumper->shader);
374 }
375 
376 static gboolean
gst_gl_bumper_filter_texture(GstGLFilter * filter,guint in_tex,guint out_tex)377 gst_gl_bumper_filter_texture (GstGLFilter * filter, guint in_tex, guint out_tex)
378 {
379   gpointer bumper_filter = GST_GL_BUMPER (filter);
380 
381   //blocking call, use a FBO
382   gst_gl_context_use_fbo (filter->context,
383       GST_VIDEO_INFO_WIDTH (&filter->out_info),
384       GST_VIDEO_INFO_HEIGHT (&filter->out_info),
385       filter->fbo, filter->depthbuffer, out_tex, gst_gl_bumper_callback,
386       GST_VIDEO_INFO_WIDTH (&filter->in_info),
387       GST_VIDEO_INFO_HEIGHT (&filter->in_info),
388       in_tex, 45,
389       (gdouble) GST_VIDEO_INFO_WIDTH (&filter->out_info) /
390       (gdouble) GST_VIDEO_INFO_HEIGHT (&filter->out_info), 0.1, 50,
391       GST_GL_DISPLAY_PROJECTION_PERSPECTIVE, bumper_filter);
392 
393   return TRUE;
394 }
395 
396 typedef struct _MeshData
397 {
398   float x, y, z;                /* Vertex */
399   float nx, ny, nz;             /* Normal */
400   float s0, t0;                 /* TexCoord0 */
401   float s1, t1;                 /* TexCoord1 */
402   float va0, vb0, vc0;          /* VertexAttrib */
403 } MeshData;
404 
405 //opengl scene, params: input texture (not the output filter->texture)
406 static void
gst_gl_bumper_callback(gint width,gint height,guint texture,gpointer stuff)407 gst_gl_bumper_callback (gint width, gint height, guint texture, gpointer stuff)
408 {
409   GstGLFuncs *gl;
410   GstGLBumper *bumper = GST_GL_BUMPER (stuff);
411   GstGLContext *context = GST_GL_FILTER (bumper)->context;
412   GLint locTangent = 0;
413 
414   //choose the lights
415   GLfloat light_direction0[] = { 1.0, 0.0, -1.0, 0.0 }; // light goes along -x
416   GLfloat light_direction1[] = { -1.0, 0.0, -1.0, 0.0 };        // light goes along x
417   GLfloat light_diffuse0[] = { 1.0, 1.0, 1.0, 1.0 };
418   GLfloat light_diffuse1[] = { 1.0, 1.0, 1.0, 1.0 };
419   GLfloat mat_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
420 
421 /* *INDENT-OFF* */
422   MeshData mesh[] = {
423    /* |     Vertex      |     Normal      |TexCoord0|TexCoord1|  VertexAttrib  | */
424 /*F*/ { 1.0,  1.0, -1.0,  0.0,  0.0, -1.0, 0.0, 0.0, 0.0, 0.0,  0.0,  1.0,  0.0},
425 /*r*/ { 1.0, -1.0, -1.0,  0.0,  0.0, -1.0, 1.0, 0.0, 1.0, 0.0,  0.0,  1.0,  0.0},
426 /*o*/ {-1.0, -1.0, -1.0,  0.0,  0.0, -1.0, 1.0, 1.0, 1.0, 1.0,  0.0,  1.0,  0.0},
427       {-1.0,  1.0, -1.0,  0.0,  0.0, -1.0, 0.0, 1.0, 0.0, 1.0,  0.0,  1.0,  0.0},
428 /*R*/ {-1.0,  1.0, -1.0, -1.0,  0.0,  0.0, 0.0, 0.0, 0.0, 0.0,  0.0,  1.0,  0.0},
429 /*i*/ {-1.0, -1.0, -1.0, -1.0,  0.0,  0.0, 1.0, 0.0, 1.0, 0.0,  0.0,  1.0,  0.0},
430 /*g*/ {-1.0, -1.0,  1.0, -1.0,  0.0,  0.0, 1.0, 1.0, 1.0, 1.0,  0.0,  1.0,  0.0},
431       {-1.0,  1.0,  1.0, -1.0,  0.0,  0.0, 0.0, 1.0, 0.0, 1.0,  0.0,  1.0,  0.0},
432 /*B*/ {-1.0,  1.0,  1.0,  0.0,  0.0,  1.0, 0.0, 0.0, 0.0, 0.0,  0.0,  1.0,  0.0},
433 /*a*/ {-1.0, -1.0,  1.0,  0.0,  0.0,  1.0, 1.0, 0.0, 1.0, 0.0,  0.0,  1.0,  0.0},
434 /*c*/ { 1.0, -1.0,  1.0,  0.0,  0.0,  1.0, 1.0, 1.0, 1.0, 1.0,  0.0,  1.0,  0.0},
435       { 1.0,  1.0,  1.0,  0.0,  0.0,  1.0, 0.0, 1.0, 0.0, 1.0,  0.0,  1.0,  0.0},
436 /*L*/ { 1.0,  1.0,  1.0,  1.0,  0.0,  0.0, 0.0, 0.0, 0.0, 0.0,  0.0,  1.0,  0.0},
437 /*e*/ { 1.0, -1.0,  1.0,  1.0,  0.0,  0.0, 1.0, 0.0, 1.0, 0.0,  0.0,  1.0,  0.0},
438 /*f*/ { 1.0, -1.0, -1.0,  1.0,  0.0,  0.0, 1.0, 1.0, 1.0, 1.0,  0.0,  1.0,  0.0},
439       { 1.0,  1.0, -1.0,  1.0,  0.0,  0.0, 0.0, 1.0, 0.0, 1.0,  0.0,  1.0,  0.0},
440 /*T*/ { 1.0,  1.0,  1.0,  0.0,  1.0,  0.0, 0.0, 0.0, 0.0, 0.0,  0.0,  0.0,  1.0},
441 /*o*/ { 1.0,  1.0, -1.0,  0.0,  1.0,  0.0, 1.0, 0.0, 1.0, 0.0,  0.0,  0.0,  1.0},
442 /*p*/ {-1.0,  1.0, -1.0,  0.0,  1.0,  0.0, 1.0, 1.0, 1.0, 1.0,  0.0,  0.0,  1.0},
443       {-1.0,  1.0,  1.0,  0.0,  1.0,  0.0, 0.0, 1.0, 0.0, 1.0,  0.0,  0.0,  1.0},
444 /*B*/ { 1.0, -1.0, -1.0,  0.0, -1.0,  0.0, 0.0, 0.0, 0.0, 0.0,  0.0,  0.0, -1.0},
445 /*o*/ { 1.0, -1.0,  1.0,  0.0, -1.0,  0.0, 1.0, 0.0, 1.0, 0.0,  0.0,  0.0, -1.0},
446 /*t*/ {-1.0, -1.0,  1.0,  0.0, -1.0,  0.0, 1.0, 1.0, 1.0, 1.0,  0.0,  0.0, -1.0},
447       {-1.0, -1.0, -1.0,  0.0, -1.0,  0.0, 0.0, 1.0, 0.0, 1.0,  0.0,  0.0, -1.0},
448   };
449 
450   GLushort indices[] = {
451     0, 1, 2,
452     0, 2, 3,
453     4, 5, 6,
454     4, 6, 7,
455     8, 9, 10,
456     8, 10, 11,
457     12, 13, 14,
458     12, 14, 15,
459     16, 17, 18,
460     16, 18, 19,
461     20, 21, 22,
462     20, 22, 23
463   };
464 
465 /* *INDENT-ON* */
466 
467   gl = GST_GL_FILTER (bumper)->context->gl_vtable;
468 
469   //eye point
470   gl->MatrixMode (GL_PROJECTION);
471   gluLookAt (0.0, 0.0, -6.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
472   gl->MatrixMode (GL_MODELVIEW);
473 
474   //scene conf
475   gl->Enable (GL_DEPTH_TEST);
476   gl->DepthFunc (GL_LEQUAL);
477   gl->Hint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
478 
479   gl->ShadeModel (GL_SMOOTH);
480 
481   //set the lights
482   gl->Lightfv (GL_LIGHT0, GL_POSITION, light_direction0);
483   gl->Lightfv (GL_LIGHT0, GL_DIFFUSE, light_diffuse0);
484   gl->Lightfv (GL_LIGHT1, GL_POSITION, light_direction1);
485   gl->Lightfv (GL_LIGHT1, GL_DIFFUSE, light_diffuse1);
486   gl->Materialfv (GL_FRONT, GL_DIFFUSE, mat_diffuse);
487   gl->ColorMaterial (GL_FRONT_AND_BACK, GL_DIFFUSE);
488   gl->Enable (GL_COLOR_MATERIAL);
489   gl->Enable (GL_LIGHTING);
490   gl->Enable (GL_LIGHT0);
491   gl->Enable (GL_LIGHT1);
492   //configure shader
493   gst_gl_shader_use (bumper->shader);
494   locTangent =
495       gst_gl_shader_get_attribute_location (bumper->shader, "aTangent");
496 
497   //set the normal map
498   gl->ActiveTexture (GL_TEXTURE1);
499   gst_gl_shader_set_uniform_1i (bumper->shader, "texture1", 1);
500   gl->BindTexture (GL_TEXTURE_2D, bumper->bumpmap);
501 
502   //set the video texture
503   gl->ActiveTexture (GL_TEXTURE0);
504   gst_gl_shader_set_uniform_1i (bumper->shader, "texture0", 0);
505   gl->BindTexture (GL_TEXTURE_2D, texture);
506 
507   gl->Rotatef (bumper->xrot, 1.0f, 0.0f, 0.0f);
508   gl->Rotatef (bumper->yrot, 0.0f, 1.0f, 0.0f);
509   gl->Rotatef (bumper->zrot, 0.0f, 0.0f, 1.0f);
510 
511   gl->EnableVertexAttribArray (locTangent);
512 
513   gl->ClientActiveTexture (GL_TEXTURE0);
514   gl->EnableClientState (GL_TEXTURE_COORD_ARRAY);
515   gl->EnableClientState (GL_VERTEX_ARRAY);
516   gl->EnableClientState (GL_NORMAL_ARRAY);
517 
518   gl->VertexAttribPointer (locTangent, 3, GL_FLOAT, 0, sizeof (MeshData),
519       &mesh[0].va0);
520   gl->VertexPointer (3, GL_FLOAT, sizeof (MeshData), &mesh[0].x);
521   gl->NormalPointer (GL_FLOAT, sizeof (MeshData), &mesh[0].nx);
522   gl->TexCoordPointer (2, GL_FLOAT, sizeof (MeshData), &mesh[0].s0);
523 
524   gl->ClientActiveTexture (GL_TEXTURE1);
525   gl->EnableClientState (GL_TEXTURE_COORD_ARRAY);
526   gl->TexCoordPointer (2, GL_FLOAT, sizeof (MeshData), &mesh[0].s1);
527 
528   gl->DrawElements (GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, indices);
529 
530   gl->DisableClientState (GL_VERTEX_ARRAY);
531   gl->DisableClientState (GL_TEXTURE_COORD_ARRAY);
532   gl->DisableClientState (GL_NORMAL_ARRAY);
533 
534   gl->ClientActiveTexture (GL_TEXTURE0);
535   gl->DisableClientState (GL_TEXTURE_COORD_ARRAY);
536 
537   gl->DisableVertexAttribArray (locTangent);
538 
539   gst_gl_context_clear_shader (context);
540 
541   gl->Disable (GL_LIGHT0);
542   gl->Disable (GL_LIGHT1);
543   gl->Disable (GL_LIGHTING);
544   gl->Disable (GL_COLOR_MATERIAL);
545 
546   bumper->xrot += 1.0f;
547   bumper->yrot += 0.9f;
548   bumper->zrot += 0.6f;
549 }
550