gstglfiltercube.c 16.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * GStreamer
 * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

/**
 * SECTION:element-glfiltercube
23
 * @title: glfiltercube
24 25 26
 *
 * The resize and redraw callbacks can be set from a client code.
 *
27
 * ## Examples
28
 * |[
29
 * gst-launch-1.0 -v videotestsrc ! glfiltercube ! glimagesink
30 31 32
 * ]| A pipeline to mpa textures on the 6 cube faces..
 * FBO is required.
 * |[
33
 * gst-launch-1.0 -v videotestsrc ! glfiltercube ! video/x-raw, width=640, height=480 ! glimagesink
34 35 36
 * ]| Resize scene after drawing the cube.
 * The scene size is greater than the input video size.
  |[
37
 * gst-launch-1.0 -v videotestsrc ! video/x-raw, width=640, height=480  ! glfiltercube ! glimagesink
38 39
 * ]| Resize scene before drawing the cube.
 * The scene size is greater than the input video size.
40
 *
41 42 43 44 45 46 47 48
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gl/gstglapi.h>
#include "gstglfiltercube.h"
49
#include "gstglutils.h"
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67

#define GST_CAT_DEFAULT gst_gl_filter_cube_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);

enum
{
  PROP_0,
  PROP_RED,
  PROP_GREEN,
  PROP_BLUE,
  PROP_FOVY,
  PROP_ASPECT,
  PROP_ZNEAR,
  PROP_ZFAR
};

#define DEBUG_INIT \
    GST_DEBUG_CATEGORY_INIT (gst_gl_filter_cube_debug, "glfiltercube", 0, "glfiltercube element");
68
#define gst_gl_filter_cube_parent_class parent_class
69 70 71 72 73 74 75 76 77 78
G_DEFINE_TYPE_WITH_CODE (GstGLFilterCube, gst_gl_filter_cube,
    GST_TYPE_GL_FILTER, DEBUG_INIT);

static void gst_gl_filter_cube_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_gl_filter_cube_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

static gboolean gst_gl_filter_cube_set_caps (GstGLFilter * filter,
    GstCaps * incaps, GstCaps * outcaps);
79 80
static gboolean gst_gl_filter_cube_gl_start (GstGLBaseFilter * filter);
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 86 87

/* vertex source */
static const gchar *cube_v_src =
    "attribute vec4 a_position;                                   \n"
88
    "attribute vec2 a_texcoord;                                   \n"
89 90
    "uniform mat4 u_matrix;                                       \n"
    "uniform float xrot_degree, yrot_degree, zrot_degree;         \n"
91
    "varying vec2 v_texcoord;                                     \n"
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
    "void main()                                                  \n"
    "{                                                            \n"
    "   float PI = 3.14159265;                                    \n"
    "   float xrot = xrot_degree*2.0*PI/360.0;                    \n"
    "   float yrot = yrot_degree*2.0*PI/360.0;                    \n"
    "   float zrot = zrot_degree*2.0*PI/360.0;                    \n"
    "   mat4 matX = mat4 (                                        \n"
    "            1.0,        0.0,        0.0, 0.0,                \n"
    "            0.0,  cos(xrot),  sin(xrot), 0.0,                \n"
    "            0.0, -sin(xrot),  cos(xrot), 0.0,                \n"
    "            0.0,        0.0,        0.0, 1.0 );              \n"
    "   mat4 matY = mat4 (                                        \n"
    "      cos(yrot),        0.0, -sin(yrot), 0.0,                \n"
    "            0.0,        1.0,        0.0, 0.0,                \n"
    "      sin(yrot),        0.0,  cos(yrot), 0.0,                \n"
    "            0.0,        0.0,       0.0,  1.0 );              \n"
    "   mat4 matZ = mat4 (                                        \n"
    "      cos(zrot),  sin(zrot),        0.0, 0.0,                \n"
    "     -sin(zrot),  cos(zrot),        0.0, 0.0,                \n"
    "            0.0,        0.0,        1.0, 0.0,                \n"
    "            0.0,        0.0,        0.0, 1.0 );              \n"
    "   gl_Position = u_matrix * matZ * matY * matX * a_position; \n"
114
    "   v_texcoord = a_texcoord;                                  \n"
115 116 117 118
    "}                                                            \n";

/* fragment source */
static const gchar *cube_f_src =
119 120 121 122
    "#ifdef GL_ES\n"
    "precision mediump float;\n"
    "#endif\n"
    "varying vec2 v_texcoord;                            \n"
123 124 125
    "uniform sampler2D s_texture;                        \n"
    "void main()                                         \n"
    "{                                                   \n"
126
    "  gl_FragColor = texture2D( s_texture, v_texcoord );\n"
127 128 129 130 131 132 133 134 135 136 137
    "}                                                   \n";

static void
gst_gl_filter_cube_class_init (GstGLFilterCubeClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *element_class;

  gobject_class = (GObjectClass *) klass;
  element_class = GST_ELEMENT_CLASS (klass);

138 139
  gst_gl_filter_add_rgba_pad_templates (GST_GL_FILTER_CLASS (klass));

140 141 142
  gobject_class->set_property = gst_gl_filter_cube_set_property;
  gobject_class->get_property = gst_gl_filter_cube_get_property;

143 144
  GST_GL_BASE_FILTER_CLASS (klass)->gl_start = gst_gl_filter_cube_gl_start;
  GST_GL_BASE_FILTER_CLASS (klass)->gl_stop = gst_gl_filter_cube_gl_stop;
145

146 147 148 149 150 151 152 153 154
  GST_GL_FILTER_CLASS (klass)->set_caps = gst_gl_filter_cube_set_caps;
  GST_GL_FILTER_CLASS (klass)->filter_texture =
      gst_gl_filter_cube_filter_texture;

  g_object_class_install_property (gobject_class, PROP_RED,
      g_param_spec_float ("red", "Red", "Background red color",
          0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_GREEN,
155
      g_param_spec_float ("green", "Green", "Background green color",
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
          0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_BLUE,
      g_param_spec_float ("blue", "Blue", "Background blue color",
          0.0f, 1.0f, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_FOVY,
      g_param_spec_double ("fovy", "Fovy", "Field of view angle in degrees",
          0.0, 180.0, 45.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_ASPECT,
      g_param_spec_double ("aspect", "Aspect",
          "Field of view in the x direction", 0.0, 100, 0.0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_ZNEAR,
      g_param_spec_double ("znear", "Znear",
          "Specifies the distance from the viewer to the near clipping plane",
          0.0, 100.0, 0.1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_ZFAR,
      g_param_spec_double ("zfar", "Zfar",
          "Specifies the distance from the viewer to the far clipping plane",
          0.0, 1000.0, 100.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  gst_element_class_set_metadata (element_class, "OpenGL cube filter",
      "Filter/Effect/Video", "Map input texture on the 6 cube faces",
      "Julien Isorce <julien.isorce@gmail.com>");
184

185
  GST_GL_BASE_FILTER_CLASS (klass)->supported_gl_api =
186
      GST_GL_API_OPENGL | GST_GL_API_GLES2 | GST_GL_API_OPENGL3;
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
}

static void
gst_gl_filter_cube_init (GstGLFilterCube * filter)
{
  filter->shader = NULL;
  filter->fovy = 45;
  filter->aspect = 0;
  filter->znear = 0.1;
  filter->zfar = 100;
}

static void
gst_gl_filter_cube_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstGLFilterCube *filter = GST_GL_FILTER_CUBE (object);

  switch (prop_id) {
    case PROP_RED:
      filter->red = g_value_get_float (value);
      break;
    case PROP_GREEN:
      filter->green = g_value_get_float (value);
      break;
    case PROP_BLUE:
      filter->blue = g_value_get_float (value);
      break;
    case PROP_FOVY:
      filter->fovy = g_value_get_double (value);
      break;
    case PROP_ASPECT:
      filter->aspect = g_value_get_double (value);
      break;
    case PROP_ZNEAR:
      filter->znear = g_value_get_double (value);
      break;
    case PROP_ZFAR:
      filter->zfar = g_value_get_double (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_gl_filter_cube_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstGLFilterCube *filter = GST_GL_FILTER_CUBE (object);

  switch (prop_id) {
    case PROP_RED:
      g_value_set_float (value, filter->red);
      break;
    case PROP_GREEN:
      g_value_set_float (value, filter->green);
      break;
    case PROP_BLUE:
      g_value_set_float (value, filter->blue);
      break;
    case PROP_FOVY:
      g_value_set_double (value, filter->fovy);
      break;
    case PROP_ASPECT:
      g_value_set_double (value, filter->aspect);
      break;
    case PROP_ZNEAR:
      g_value_set_double (value, filter->znear);
      break;
    case PROP_ZFAR:
      g_value_set_double (value, filter->zfar);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static gboolean
gst_gl_filter_cube_set_caps (GstGLFilter * filter, GstCaps * incaps,
    GstCaps * outcaps)
{
  GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);

  if (cube_filter->aspect == 0)
    cube_filter->aspect = (gdouble) GST_VIDEO_INFO_WIDTH (&filter->out_info) /
        (gdouble) GST_VIDEO_INFO_HEIGHT (&filter->out_info);

  return TRUE;
}

280
static void
281
gst_gl_filter_cube_gl_stop (GstGLBaseFilter * base_filter)
282
{
283 284
  GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (base_filter);
  const GstGLFuncs *gl = base_filter->context->gl_vtable;
285 286 287 288 289 290 291 292 293 294

  if (cube_filter->vao) {
    gl->DeleteVertexArrays (1, &cube_filter->vao);
    cube_filter->vao = 0;
  }

  if (cube_filter->vertex_buffer) {
    gl->DeleteBuffers (1, &cube_filter->vertex_buffer);
    cube_filter->vertex_buffer = 0;
  }
295

296 297 298 299 300
  if (cube_filter->vbo_indices) {
    gl->DeleteBuffers (1, &cube_filter->vbo_indices);
    cube_filter->vbo_indices = 0;
  }

301
  if (cube_filter->shader) {
302
    gst_object_unref (cube_filter->shader);
303 304
    cube_filter->shader = NULL;
  }
305

306
  GST_GL_BASE_FILTER_CLASS (parent_class)->gl_stop (base_filter);
307 308 309
}

static gboolean
310
gst_gl_filter_cube_gl_start (GstGLBaseFilter * filter)
311 312
{
  GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);
313

314
  /* blocking call, wait the opengl thread has compiled the shader */
315 316
  return gst_gl_context_gen_shader (GST_GL_BASE_FILTER (filter)->context,
      cube_v_src, cube_f_src, &cube_filter->shader);
317 318 319
}

static gboolean
320 321
gst_gl_filter_cube_filter_texture (GstGLFilter * filter, GstGLMemory * in_tex,
    GstGLMemory * out_tex)
322 323 324
{
  GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);

Matthew Waters's avatar
Matthew Waters committed
325
  cube_filter->in_tex = in_tex;
326

327 328
  return gst_gl_framebuffer_draw_to_texture (filter->fbo, out_tex, _callback,
      cube_filter);
329 330 331
}

/* *INDENT-OFF* */
332
static const GLfloat vertices[] = {
333 334
 /*|     Vertex     | TexCoord |*/ 
    /* front face */
335 336 337 338
     1.0,  1.0, -1.0, 1.0, 0.0,
     1.0, -1.0, -1.0, 1.0, 1.0,
    -1.0, -1.0, -1.0, 0.0, 1.0,
    -1.0,  1.0, -1.0, 0.0, 0.0,
339
    /* back face */
340
     1.0,  1.0,  1.0, 1.0, 0.0,
341
    -1.0,  1.0,  1.0, 0.0, 0.0,
342
    -1.0, -1.0,  1.0, 0.0, 1.0,
343 344
     1.0, -1.0,  1.0, 1.0, 1.0,
    /* right face */
345 346 347 348
     1.0,  1.0,  1.0, 1.0, 0.0,
     1.0, -1.0,  1.0, 0.0, 0.0,
     1.0, -1.0, -1.0, 0.0, 1.0,
     1.0,  1.0, -1.0, 1.0, 1.0,
349
    /* left face */
350 351 352 353 354
    -1.0,  1.0,  1.0, 1.0, 0.0,
    -1.0,  1.0, -1.0, 1.0, 1.0,
    -1.0, -1.0, -1.0, 0.0, 1.0,
    -1.0, -1.0,  1.0, 0.0, 0.0,
    /* top face */
355
     1.0, -1.0,  1.0, 1.0, 0.0,
356 357
    -1.0, -1.0,  1.0, 0.0, 0.0,
    -1.0, -1.0, -1.0, 0.0, 1.0,
358 359
     1.0, -1.0, -1.0, 1.0, 1.0,
    /* bottom face */
360 361 362 363 364
     1.0,  1.0,  1.0, 1.0, 0.0,
     1.0,  1.0, -1.0, 1.0, 1.0,
    -1.0,  1.0, -1.0, 0.0, 1.0,
    -1.0,  1.0,  1.0, 0.0, 0.0
};
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379

static const GLushort indices[] = {
    0, 1, 2,
    0, 2, 3,
    4, 5, 6,
    4, 6, 7,
    8, 9, 10,
    8, 10, 11,
    12, 13, 14,
    12, 14, 15,
    16, 17, 18,
    16, 18, 19,
    20, 21, 22,
    20, 22, 23
};
380 381
/* *INDENT-ON* */

382 383 384
static void
_bind_buffer (GstGLFilterCube * cube_filter)
{
385
  const GstGLFuncs *gl = GST_GL_BASE_FILTER (cube_filter)->context->gl_vtable;
386

387
  gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, cube_filter->vbo_indices);
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
  gl->BindBuffer (GL_ARRAY_BUFFER, cube_filter->vertex_buffer);

  cube_filter->attr_position =
      gst_gl_shader_get_attribute_location (cube_filter->shader, "a_position");

  cube_filter->attr_texture =
      gst_gl_shader_get_attribute_location (cube_filter->shader, "a_texcoord");

  /* Load the vertex position */
  gl->VertexAttribPointer (cube_filter->attr_position, 3, GL_FLOAT, GL_FALSE,
      5 * sizeof (GLfloat), (void *) 0);

  /* Load the texture coordinate */
  gl->VertexAttribPointer (cube_filter->attr_texture, 2, GL_FLOAT, GL_FALSE,
      5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));

  gl->EnableVertexAttribArray (cube_filter->attr_position);
  gl->EnableVertexAttribArray (cube_filter->attr_texture);
}

static void
_unbind_buffer (GstGLFilterCube * cube_filter)
{
411
  const GstGLFuncs *gl = GST_GL_BASE_FILTER (cube_filter)->context->gl_vtable;
412

413
  gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
414 415 416 417 418 419
  gl->BindBuffer (GL_ARRAY_BUFFER, 0);

  gl->DisableVertexAttribArray (cube_filter->attr_position);
  gl->DisableVertexAttribArray (cube_filter->attr_texture);
}

420
static gboolean
Matthew Waters's avatar
Matthew Waters committed
421
_callback (gpointer stuff)
422 423 424
{
  GstGLFilter *filter = GST_GL_FILTER (stuff);
  GstGLFilterCube *cube_filter = GST_GL_FILTER_CUBE (filter);
425
  GstGLFuncs *gl = GST_GL_BASE_FILTER (filter)->context->gl_vtable;
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445

  static GLfloat xrot = 0;
  static GLfloat yrot = 0;
  static GLfloat zrot = 0;

  const GLfloat matrix[] = {
    0.5f, 0.0f, 0.0f, 0.0f,
    0.0f, 0.5f, 0.0f, 0.0f,
    0.0f, 0.0f, 0.5f, 0.0f,
    0.0f, 0.0f, 0.0f, 1.0f
  };

  gl->Enable (GL_DEPTH_TEST);

  gl->ClearColor (cube_filter->red, cube_filter->green, cube_filter->blue, 0.0);
  gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  gst_gl_shader_use (cube_filter->shader);

  gl->ActiveTexture (GL_TEXTURE0);
446
  gl->BindTexture (GL_TEXTURE_2D, cube_filter->in_tex->tex_id);
447 448 449 450 451 452 453
  gst_gl_shader_set_uniform_1i (cube_filter->shader, "s_texture", 0);
  gst_gl_shader_set_uniform_1f (cube_filter->shader, "xrot_degree", xrot);
  gst_gl_shader_set_uniform_1f (cube_filter->shader, "yrot_degree", yrot);
  gst_gl_shader_set_uniform_1f (cube_filter->shader, "zrot_degree", zrot);
  gst_gl_shader_set_uniform_matrix_4fv (cube_filter->shader, "u_matrix", 1,
      GL_FALSE, matrix);

454 455 456 457 458 459 460 461 462 463 464
  if (!cube_filter->vertex_buffer) {
    if (gl->GenVertexArrays) {
      gl->GenVertexArrays (1, &cube_filter->vao);
      gl->BindVertexArray (cube_filter->vao);
    }

    gl->GenBuffers (1, &cube_filter->vertex_buffer);
    gl->BindBuffer (GL_ARRAY_BUFFER, cube_filter->vertex_buffer);
    gl->BufferData (GL_ARRAY_BUFFER, 6 * 4 * 5 * sizeof (GLfloat), vertices,
        GL_STATIC_DRAW);

465 466 467 468 469
    gl->GenBuffers (1, &cube_filter->vbo_indices);
    gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, cube_filter->vbo_indices);
    gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
        GL_STATIC_DRAW);

470 471
    if (gl->GenVertexArrays) {
      _bind_buffer (cube_filter);
472
      gl->BindVertexArray (0);
473
    }
474 475 476

    gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
    gl->BindBuffer (GL_ARRAY_BUFFER, 0);
477 478 479 480
  }

  if (gl->GenVertexArrays)
    gl->BindVertexArray (cube_filter->vao);
481
  _bind_buffer (cube_filter);
482

483
  gl->DrawElements (GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);
484

485 486
  if (gl->GenVertexArrays)
    gl->BindVertexArray (0);
487
  _unbind_buffer (cube_filter);
488 489 490 491 492 493

  gl->Disable (GL_DEPTH_TEST);

  xrot += 0.3f;
  yrot += 0.2f;
  zrot += 0.4f;
494 495

  return TRUE;
496
}