1 /*
2  * GStreamer
3  * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:element-glfiltercube
23  * @title: glfiltercube
24  *
25  * The resize and redraw callbacks can be set from a client code.
26  *
27  * ## Examples
28  * |[
29  * gst-launch-1.0 -v videotestsrc ! glfiltercube ! glimagesink
30  * ]| A pipeline to mpa textures on the 6 cube faces..
31  * FBO is required.
32  * |[
33  * gst-launch-1.0 -v videotestsrc ! glfiltercube ! video/x-raw, width=640, height=480 ! glimagesink
34  * ]| Resize scene after drawing the cube.
35  * The scene size is greater than the input video size.
36   |[
37  * gst-launch-1.0 -v videotestsrc ! video/x-raw, width=640, height=480  ! glfiltercube ! glimagesink
38  * ]| Resize scene before drawing the cube.
39  * The scene size is greater than the input video size.
40  *
41  */
42 
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46 
47 #include <gst/gl/gstglapi.h>
48 #include "gstglfiltercube.h"
49 #include "gstglutils.h"
50 
51 #define GST_CAT_DEFAULT gst_gl_filter_cube_debug
52 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
53 
54 enum
55 {
56   PROP_0,
57   PROP_RED,
58   PROP_GREEN,
59   PROP_BLUE,
60   PROP_FOVY,
61   PROP_ASPECT,
62   PROP_ZNEAR,
63   PROP_ZFAR
64 };
65 
66 #define DEBUG_INIT \
67     GST_DEBUG_CATEGORY_INIT (gst_gl_filter_cube_debug, "glfiltercube", 0, "glfiltercube element");
68 #define gst_gl_filter_cube_parent_class parent_class
69 G_DEFINE_TYPE_WITH_CODE (GstGLFilterCube, gst_gl_filter_cube,
70     GST_TYPE_GL_FILTER, DEBUG_INIT);
71 
72 static void gst_gl_filter_cube_set_property (GObject * object, guint prop_id,
73     const GValue * value, GParamSpec * pspec);
74 static void gst_gl_filter_cube_get_property (GObject * object, guint prop_id,
75     GValue * value, GParamSpec * pspec);
76 
77 static gboolean gst_gl_filter_cube_set_caps (GstGLFilter * filter,
78     GstCaps * incaps, GstCaps * outcaps);
79 static gboolean gst_gl_filter_cube_gl_start (GstGLBaseFilter * filter);
80 static void gst_gl_filter_cube_gl_stop (GstGLBaseFilter * filter);
81 static gboolean _callback (gpointer stuff);
82 static gboolean gst_gl_filter_cube_filter_texture (GstGLFilter * filter,
83     GstGLMemory * in_tex, GstGLMemory * out_tex);
84 
85 /* vertex source */
86 static const gchar *cube_v_src =
87     "attribute vec4 a_position;                                   \n"
88     "attribute vec2 a_texcoord;                                   \n"
89     "uniform mat4 u_matrix;                                       \n"
90     "uniform float xrot_degree, yrot_degree, zrot_degree;         \n"
91     "varying vec2 v_texcoord;                                     \n"
92     "void main()                                                  \n"
93     "{                                                            \n"
94     "   float PI = 3.14159265;                                    \n"
95     "   float xrot = xrot_degree*2.0*PI/360.0;                    \n"
96     "   float yrot = yrot_degree*2.0*PI/360.0;                    \n"
97     "   float zrot = zrot_degree*2.0*PI/360.0;                    \n"
98     "   mat4 matX = mat4 (                                        \n"
99     "            1.0,        0.0,        0.0, 0.0,                \n"
100     "            0.0,  cos(xrot),  sin(xrot), 0.0,                \n"
101     "            0.0, -sin(xrot),  cos(xrot), 0.0,                \n"
102     "            0.0,        0.0,        0.0, 1.0 );              \n"
103     "   mat4 matY = mat4 (                                        \n"
104     "      cos(yrot),        0.0, -sin(yrot), 0.0,                \n"
105     "            0.0,        1.0,        0.0, 0.0,                \n"
106     "      sin(yrot),        0.0,  cos(yrot), 0.0,                \n"
107     "            0.0,        0.0,       0.0,  1.0 );              \n"
108     "   mat4 matZ = mat4 (                                        \n"
109     "      cos(zrot),  sin(zrot),        0.0, 0.0,                \n"
110     "     -sin(zrot),  cos(zrot),        0.0, 0.0,                \n"
111     "            0.0,        0.0,        1.0, 0.0,                \n"
112     "            0.0,        0.0,        0.0, 1.0 );              \n"
113     "   gl_Position = u_matrix * matZ * matY * matX * a_position; \n"
114     "   v_texcoord = a_texcoord;                                  \n"
115     "}                                                            \n";
116 
117 /* fragment source */
118 static const gchar *cube_f_src =
119     "varying vec2 v_texcoord;                            \n"
120     "uniform sampler2D s_texture;                        \n"
121     "void main()                                         \n"
122     "{                                                   \n"
123     "  gl_FragColor = texture2D( s_texture, v_texcoord );\n"
124     "}                                                   \n";
125 
126 static void
gst_gl_filter_cube_class_init(GstGLFilterCubeClass * klass)127 gst_gl_filter_cube_class_init (GstGLFilterCubeClass * klass)
128 {
129   GObjectClass *gobject_class;
130   GstElementClass *element_class;
131 
132   gobject_class = (GObjectClass *) klass;
133   element_class = GST_ELEMENT_CLASS (klass);
134 
135   gst_gl_filter_add_rgba_pad_templates (GST_GL_FILTER_CLASS (klass));
136 
137   gobject_class->set_property = gst_gl_filter_cube_set_property;
138   gobject_class->get_property = gst_gl_filter_cube_get_property;
139 
140   GST_GL_BASE_FILTER_CLASS (klass)->gl_start = gst_gl_filter_cube_gl_start;
141   GST_GL_BASE_FILTER_CLASS (klass)->gl_stop = gst_gl_filter_cube_gl_stop;
142 
143   GST_GL_FILTER_CLASS (klass)->set_caps = gst_gl_filter_cube_set_caps;
144   GST_GL_FILTER_CLASS (klass)->filter_texture =
145       gst_gl_filter_cube_filter_texture;
146 
147   g_object_class_install_property (gobject_class, PROP_RED,
148       g_param_spec_float ("red", "Red", "Background red color",
149           0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
150 
151   g_object_class_install_property (gobject_class, PROP_GREEN,
152       g_param_spec_float ("green", "Green", "Background green color",
153           0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
154 
155   g_object_class_install_property (gobject_class, PROP_BLUE,
156       g_param_spec_float ("blue", "Blue", "Background blue color",
157           0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
158 
159   g_object_class_install_property (gobject_class, PROP_FOVY,
160       g_param_spec_double ("fovy", "Fovy", "Field of view angle in degrees",
161           0.0, 180.0, 45.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
162 
163   g_object_class_install_property (gobject_class, PROP_ASPECT,
164       g_param_spec_double ("aspect", "Aspect",
165           "Field of view in the x direction", 0.0, 100, 0.0,
166           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
167 
168   g_object_class_install_property (gobject_class, PROP_ZNEAR,
169       g_param_spec_double ("znear", "Znear",
170           "Specifies the distance from the viewer to the near clipping plane",
171           0.0, 100.0, 0.1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
172 
173   g_object_class_install_property (gobject_class, PROP_ZFAR,
174       g_param_spec_double ("zfar", "Zfar",
175           "Specifies the distance from the viewer to the far clipping plane",
176           0.0, 1000.0, 100.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
177 
178   gst_element_class_set_metadata (element_class, "OpenGL cube filter",
179       "Filter/Effect/Video", "Map input texture on the 6 cube faces",
180       "Julien Isorce <julien.isorce@gmail.com>");
181 
182   GST_GL_BASE_FILTER_CLASS (klass)->supported_gl_api =
183       GST_GL_API_OPENGL | GST_GL_API_GLES2 | GST_GL_API_OPENGL3;
184 }
185 
186 static void
gst_gl_filter_cube_init(GstGLFilterCube * filter)187 gst_gl_filter_cube_init (GstGLFilterCube * filter)
188 {
189   filter->shader = NULL;
190   filter->fovy = 45;
191   filter->aspect = 0;
192   filter->znear = 0.1;
193   filter->zfar = 100;
194 }
195 
196 static void
gst_gl_filter_cube_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)197 gst_gl_filter_cube_set_property (GObject * object, guint prop_id,
198     const GValue * value, GParamSpec * pspec)
199 {
200   GstGLFilterCube *filter = GST_GL_FILTER_CUBE (object);
201 
202   switch (prop_id) {
203     case PROP_RED:
204       filter->red = g_value_get_float (value);
205       break;
206     case PROP_GREEN:
207       filter->green = g_value_get_float (value);
208       break;
209     case PROP_BLUE:
210       filter->blue = g_value_get_float (value);
211       break;
212     case PROP_FOVY:
213       filter->fovy = g_value_get_double (value);
214       break;
215     case PROP_ASPECT:
216       filter->aspect = g_value_get_double (value);
217       break;
218     case PROP_ZNEAR:
219       filter->znear = g_value_get_double (value);
220       break;
221     case PROP_ZFAR:
222       filter->zfar = g_value_get_double (value);
223       break;
224     default:
225       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
226       break;
227   }
228 }
229 
230 static void
gst_gl_filter_cube_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)231 gst_gl_filter_cube_get_property (GObject * object, guint prop_id,
232     GValue * value, GParamSpec * pspec)
233 {
234   GstGLFilterCube *filter = GST_GL_FILTER_CUBE (object);
235 
236   switch (prop_id) {
237     case PROP_RED:
238       g_value_set_float (value, filter->red);
239       break;
240     case PROP_GREEN:
241       g_value_set_float (value, filter->green);
242       break;
243     case PROP_BLUE:
244       g_value_set_float (value, filter->blue);
245       break;
246     case PROP_FOVY:
247       g_value_set_double (value, filter->fovy);
248       break;
249     case PROP_ASPECT:
250       g_value_set_double (value, filter->aspect);
251       break;
252     case PROP_ZNEAR:
253       g_value_set_double (value, filter->znear);
254       break;
255     case PROP_ZFAR:
256       g_value_set_double (value, filter->zfar);
257       break;
258     default:
259       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
260       break;
261   }
262 }
263 
264 static gboolean
gst_gl_filter_cube_set_caps(GstGLFilter * filter,GstCaps * incaps,GstCaps * outcaps)265 gst_gl_filter_cube_set_caps (GstGLFilter * filter, GstCaps * incaps,
266     GstCaps * outcaps)
267 {
268   GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);
269 
270   if (cube_filter->aspect == 0)
271     cube_filter->aspect = (gdouble) GST_VIDEO_INFO_WIDTH (&filter->out_info) /
272         (gdouble) GST_VIDEO_INFO_HEIGHT (&filter->out_info);
273 
274   return TRUE;
275 }
276 
277 static void
gst_gl_filter_cube_gl_stop(GstGLBaseFilter * base_filter)278 gst_gl_filter_cube_gl_stop (GstGLBaseFilter * base_filter)
279 {
280   GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (base_filter);
281   const GstGLFuncs *gl = base_filter->context->gl_vtable;
282 
283   if (cube_filter->vao) {
284     gl->DeleteVertexArrays (1, &cube_filter->vao);
285     cube_filter->vao = 0;
286   }
287 
288   if (cube_filter->vertex_buffer) {
289     gl->DeleteBuffers (1, &cube_filter->vertex_buffer);
290     cube_filter->vertex_buffer = 0;
291   }
292 
293   if (cube_filter->vbo_indices) {
294     gl->DeleteBuffers (1, &cube_filter->vbo_indices);
295     cube_filter->vbo_indices = 0;
296   }
297 
298   if (cube_filter->shader) {
299     gst_object_unref (cube_filter->shader);
300     cube_filter->shader = NULL;
301   }
302 
303   GST_GL_BASE_FILTER_CLASS (parent_class)->gl_stop (base_filter);
304 }
305 
306 static gboolean
gst_gl_filter_cube_gl_start(GstGLBaseFilter * filter)307 gst_gl_filter_cube_gl_start (GstGLBaseFilter * filter)
308 {
309   GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);
310   GstGLContext *context = GST_GL_BASE_FILTER (filter)->context;
311   gchar *frag_str;
312   gboolean ret;
313 
314   cube_filter->xrot = 0.0;
315   cube_filter->yrot = 0.0;
316   cube_filter->zrot = 0.0;
317 
318   frag_str =
319       g_strdup_printf ("%s%s",
320       gst_gl_shader_string_get_highest_precision (context,
321           GST_GLSL_VERSION_NONE,
322           GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY), cube_f_src);
323 
324   /* blocking call, wait the opengl thread has compiled the shader */
325   ret = gst_gl_context_gen_shader (context, cube_v_src, frag_str,
326       &cube_filter->shader);
327   g_free (frag_str);
328 
329   return ret;
330 }
331 
332 static gboolean
gst_gl_filter_cube_filter_texture(GstGLFilter * filter,GstGLMemory * in_tex,GstGLMemory * out_tex)333 gst_gl_filter_cube_filter_texture (GstGLFilter * filter, GstGLMemory * in_tex,
334     GstGLMemory * out_tex)
335 {
336   GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);
337 
338   cube_filter->in_tex = in_tex;
339 
340   return gst_gl_framebuffer_draw_to_texture (filter->fbo, out_tex, _callback,
341       cube_filter);
342 }
343 
344 /* *INDENT-OFF* */
345 static const GLfloat vertices[] = {
346  /*|     Vertex     | TexCoord |*/
347     /* front face */
348      1.0,  1.0, -1.0, 1.0, 0.0,
349      1.0, -1.0, -1.0, 1.0, 1.0,
350     -1.0, -1.0, -1.0, 0.0, 1.0,
351     -1.0,  1.0, -1.0, 0.0, 0.0,
352     /* back face */
353      1.0,  1.0,  1.0, 1.0, 0.0,
354     -1.0,  1.0,  1.0, 0.0, 0.0,
355     -1.0, -1.0,  1.0, 0.0, 1.0,
356      1.0, -1.0,  1.0, 1.0, 1.0,
357     /* right face */
358      1.0,  1.0,  1.0, 1.0, 0.0,
359      1.0, -1.0,  1.0, 0.0, 0.0,
360      1.0, -1.0, -1.0, 0.0, 1.0,
361      1.0,  1.0, -1.0, 1.0, 1.0,
362     /* left face */
363     -1.0,  1.0,  1.0, 1.0, 0.0,
364     -1.0,  1.0, -1.0, 1.0, 1.0,
365     -1.0, -1.0, -1.0, 0.0, 1.0,
366     -1.0, -1.0,  1.0, 0.0, 0.0,
367     /* top face */
368      1.0, -1.0,  1.0, 1.0, 0.0,
369     -1.0, -1.0,  1.0, 0.0, 0.0,
370     -1.0, -1.0, -1.0, 0.0, 1.0,
371      1.0, -1.0, -1.0, 1.0, 1.0,
372     /* bottom face */
373      1.0,  1.0,  1.0, 1.0, 0.0,
374      1.0,  1.0, -1.0, 1.0, 1.0,
375     -1.0,  1.0, -1.0, 0.0, 1.0,
376     -1.0,  1.0,  1.0, 0.0, 0.0
377 };
378 
379 static const GLushort indices[] = {
380     0, 1, 2,
381     0, 2, 3,
382     4, 5, 6,
383     4, 6, 7,
384     8, 9, 10,
385     8, 10, 11,
386     12, 13, 14,
387     12, 14, 15,
388     16, 17, 18,
389     16, 18, 19,
390     20, 21, 22,
391     20, 22, 23
392 };
393 /* *INDENT-ON* */
394 
395 static void
_bind_buffer(GstGLFilterCube * cube_filter)396 _bind_buffer (GstGLFilterCube * cube_filter)
397 {
398   const GstGLFuncs *gl = GST_GL_BASE_FILTER (cube_filter)->context->gl_vtable;
399 
400   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, cube_filter->vbo_indices);
401   gl->BindBuffer (GL_ARRAY_BUFFER, cube_filter->vertex_buffer);
402 
403   cube_filter->attr_position =
404       gst_gl_shader_get_attribute_location (cube_filter->shader, "a_position");
405 
406   cube_filter->attr_texture =
407       gst_gl_shader_get_attribute_location (cube_filter->shader, "a_texcoord");
408 
409   /* Load the vertex position */
410   gl->VertexAttribPointer (cube_filter->attr_position, 3, GL_FLOAT, GL_FALSE,
411       5 * sizeof (GLfloat), (void *) 0);
412 
413   /* Load the texture coordinate */
414   gl->VertexAttribPointer (cube_filter->attr_texture, 2, GL_FLOAT, GL_FALSE,
415       5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
416 
417   gl->EnableVertexAttribArray (cube_filter->attr_position);
418   gl->EnableVertexAttribArray (cube_filter->attr_texture);
419 }
420 
421 static void
_unbind_buffer(GstGLFilterCube * cube_filter)422 _unbind_buffer (GstGLFilterCube * cube_filter)
423 {
424   const GstGLFuncs *gl = GST_GL_BASE_FILTER (cube_filter)->context->gl_vtable;
425 
426   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
427   gl->BindBuffer (GL_ARRAY_BUFFER, 0);
428 
429   gl->DisableVertexAttribArray (cube_filter->attr_position);
430   gl->DisableVertexAttribArray (cube_filter->attr_texture);
431 }
432 
433 static gboolean
_callback(gpointer stuff)434 _callback (gpointer stuff)
435 {
436   GstGLFilter *filter = GST_GL_FILTER (stuff);
437   GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);
438   GstGLFuncs *gl = GST_GL_BASE_FILTER (filter)->context->gl_vtable;
439 
440   const GLfloat matrix[] = {
441     0.5f, 0.0f, 0.0f, 0.0f,
442     0.0f, 0.5f, 0.0f, 0.0f,
443     0.0f, 0.0f, 0.5f, 0.0f,
444     0.0f, 0.0f, 0.0f, 1.0f
445   };
446 
447   gl->Enable (GL_DEPTH_TEST);
448 
449   gl->ClearColor (cube_filter->red, cube_filter->green, cube_filter->blue, 0.0);
450   gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
451 
452   gst_gl_shader_use (cube_filter->shader);
453 
454   gl->ActiveTexture (GL_TEXTURE0);
455   gl->BindTexture (GL_TEXTURE_2D, cube_filter->in_tex->tex_id);
456   gst_gl_shader_set_uniform_1i (cube_filter->shader, "s_texture", 0);
457   gst_gl_shader_set_uniform_1f (cube_filter->shader, "xrot_degree",
458       cube_filter->xrot);
459   gst_gl_shader_set_uniform_1f (cube_filter->shader, "yrot_degree",
460       cube_filter->yrot);
461   gst_gl_shader_set_uniform_1f (cube_filter->shader, "zrot_degree",
462       cube_filter->zrot);
463   gst_gl_shader_set_uniform_matrix_4fv (cube_filter->shader, "u_matrix", 1,
464       GL_FALSE, matrix);
465 
466   if (!cube_filter->vertex_buffer) {
467     if (gl->GenVertexArrays) {
468       gl->GenVertexArrays (1, &cube_filter->vao);
469       gl->BindVertexArray (cube_filter->vao);
470     }
471 
472     gl->GenBuffers (1, &cube_filter->vertex_buffer);
473     gl->BindBuffer (GL_ARRAY_BUFFER, cube_filter->vertex_buffer);
474     gl->BufferData (GL_ARRAY_BUFFER, 6 * 4 * 5 * sizeof (GLfloat), vertices,
475         GL_STATIC_DRAW);
476 
477     gl->GenBuffers (1, &cube_filter->vbo_indices);
478     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, cube_filter->vbo_indices);
479     gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
480         GL_STATIC_DRAW);
481 
482     if (gl->GenVertexArrays) {
483       _bind_buffer (cube_filter);
484       gl->BindVertexArray (0);
485     }
486 
487     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
488     gl->BindBuffer (GL_ARRAY_BUFFER, 0);
489   }
490 
491   if (gl->GenVertexArrays)
492     gl->BindVertexArray (cube_filter->vao);
493   _bind_buffer (cube_filter);
494 
495   gl->DrawElements (GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);
496 
497   if (gl->GenVertexArrays)
498     gl->BindVertexArray (0);
499   else
500     _unbind_buffer (cube_filter);
501 
502   gl->Disable (GL_DEPTH_TEST);
503 
504   cube_filter->xrot += 0.3f;
505   cube_filter->yrot += 0.2f;
506   cube_filter->zrot += 0.4f;
507 
508   return TRUE;
509 }
510