| /* |
| * GStreamer |
| * Copyright (C) 2010 Pierre Pouzol<pierre.pouzol@hotmail.fr> |
| * |
| * 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-glfilterreflectedscreen |
| * @title: glfilterreflectedscreen |
| * |
| * Map Video Texture upon a screen, on a reflecting surface |
| * |
| * ## Examples |
| * |[ |
| * gst-launch-1.0 videotestsrc ! glupload ! glfilterreflectedscreen ! glimagesink |
| * ]| |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <math.h> |
| #include "gstglfilterreflectedscreen.h" |
| |
| #define GST_CAT_DEFAULT gst_gl_filter_reflected_screen_debug |
| GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); |
| |
| enum |
| { |
| PROP_0, |
| PROP_ACTIVE_GRAPHIC_MODE, |
| PROP_SEPARATED_SCREEN, |
| PROP_SHOW_FLOOR, |
| PROP_FOVY, |
| PROP_ASPECT, |
| PROP_ZNEAR, |
| PROP_ZFAR |
| }; |
| |
| #define DEBUG_INIT \ |
| GST_DEBUG_CATEGORY_INIT (gst_gl_filter_reflected_screen_debug, "glfilterreflectedscreen", 0, "glfilterreflectedscreen element"); |
| |
| G_DEFINE_TYPE_WITH_CODE (GstGLFilterReflectedScreen, |
| gst_gl_filter_reflected_screen, GST_TYPE_GL_FILTER, DEBUG_INIT); |
| |
| static void gst_gl_filter_reflected_screen_set_property (GObject * object, |
| guint prop_id, const GValue * value, GParamSpec * pspec); |
| static void gst_gl_filter_reflected_screen_get_property (GObject * object, |
| guint prop_id, GValue * value, GParamSpec * pspec); |
| |
| static gboolean gst_gl_filter_reflected_screen_filter_texture (GstGLFilter * |
| filter, guint in_tex, guint out_tex); |
| |
| static void gst_gl_filter_reflected_screen_draw_background (); |
| static void gst_gl_filter_reflected_screen_draw_floor (); |
| static void gst_gl_filter_reflected_screen_draw_screen (GstGLFilter * filter, |
| gint width, gint height, guint texture); |
| static void gst_gl_filter_reflected_screen_draw_separated_screen (GstGLFilter * |
| filter, gint width, gint height, guint texture, gfloat alphs, gfloat alphe); |
| |
| static void gst_gl_filter_reflected_screen_callback (gint width, gint height, |
| guint texture, gpointer stuff); |
| |
| static const GLfloat LightPos[] = { 4.0f, -4.0f, 6.0f, 1.0f }; // Light Position |
| static const GLfloat LightAmb[] = { 4.0f, 4.0f, 4.0f, 1.0f }; // Ambient Light |
| static const GLfloat LightDif[] = { 1.0f, 1.0f, 1.0f, 1.0f }; // Diffuse Light |
| |
| static void |
| gst_gl_filter_reflected_screen_class_init (GstGLFilterReflectedScreenClass * |
| klass) |
| { |
| GObjectClass *gobject_class; |
| GstElementClass *element_class; |
| |
| gobject_class = (GObjectClass *) klass; |
| element_class = GST_ELEMENT_CLASS (klass); |
| |
| gst_gl_filter_add_rgba_pad_templates (GST_GL_FILTER_CLASS (klass)); |
| |
| gobject_class->set_property = gst_gl_filter_reflected_screen_set_property; |
| gobject_class->get_property = gst_gl_filter_reflected_screen_get_property; |
| |
| GST_GL_FILTER_CLASS (klass)->filter_texture = |
| gst_gl_filter_reflected_screen_filter_texture; |
| |
| g_object_class_install_property (gobject_class, PROP_ACTIVE_GRAPHIC_MODE, |
| g_param_spec_boolean ("active-graphic-mode", |
| "Activate graphic mode", |
| "Allow user to activate stencil buffer and blending.", |
| TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| |
| g_object_class_install_property (gobject_class, PROP_SEPARATED_SCREEN, |
| g_param_spec_boolean ("separated-screen", |
| "Create a separation space", |
| "Allow to insert a space between the two screen. Will cancel 'show floor' if active. Value are TRUE or FALSE(default)", |
| FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| |
| g_object_class_install_property (gobject_class, PROP_SHOW_FLOOR, |
| g_param_spec_boolean ("show-floor", |
| "Show the support", |
| "Allow the user to show the supportive floor. Will cancel 'separated screen' if active. Value are TRUE(default) or FALSE", |
| TRUE, 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, 60, 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", 1.0, 100, 1.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.0000000001, 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 Reflected Screen filter", "Filter/Effect/Video", |
| "Reflected Screen Filter", "Pierre POUZOL <pierre.pouzol@hotmail.fr>"); |
| |
| GST_GL_BASE_FILTER_CLASS (klass)->supported_gl_api = GST_GL_API_OPENGL; |
| } |
| |
| static void |
| gst_gl_filter_reflected_screen_init (GstGLFilterReflectedScreen * filter) |
| { |
| filter->active_graphic_mode = TRUE; |
| filter->separated_screen = FALSE; |
| filter->show_floor = TRUE; |
| filter->fovy = 90; |
| filter->aspect = 1.0; |
| filter->znear = 0.1; |
| filter->zfar = 1000; |
| } |
| |
| static void |
| gst_gl_filter_reflected_screen_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstGLFilterReflectedScreen *filter = GST_GL_FILTER_REFLECTED_SCREEN (object); |
| |
| switch (prop_id) { |
| case PROP_ACTIVE_GRAPHIC_MODE: |
| filter->active_graphic_mode = g_value_get_boolean (value); |
| break; |
| case PROP_SEPARATED_SCREEN: |
| filter->separated_screen = g_value_get_boolean (value); |
| break; |
| case PROP_SHOW_FLOOR: |
| filter->show_floor = g_value_get_boolean (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_reflected_screen_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec) |
| { |
| GstGLFilterReflectedScreen *filter = GST_GL_FILTER_REFLECTED_SCREEN (object); |
| |
| switch (prop_id) { |
| case PROP_ACTIVE_GRAPHIC_MODE: |
| g_value_set_boolean (value, filter->active_graphic_mode); |
| break; |
| case PROP_SEPARATED_SCREEN: |
| g_value_set_boolean (value, filter->separated_screen); |
| break; |
| case PROP_SHOW_FLOOR: |
| g_value_set_boolean (value, filter->show_floor); |
| 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_reflected_screen_filter_texture (GstGLFilter * filter, |
| guint in_tex, guint out_tex) |
| { |
| GstGLFilterReflectedScreen *reflected_screen_filter = |
| GST_GL_FILTER_REFLECTED_SCREEN (filter); |
| |
| //blocking call, use a FBO |
| gst_gl_context_use_fbo (filter->context, |
| GST_VIDEO_INFO_WIDTH (&filter->out_info), |
| GST_VIDEO_INFO_HEIGHT (&filter->out_info), |
| filter->fbo, filter->depthbuffer, out_tex, |
| gst_gl_filter_reflected_screen_callback, |
| GST_VIDEO_INFO_WIDTH (&filter->in_info), |
| GST_VIDEO_INFO_HEIGHT (&filter->in_info), in_tex, |
| reflected_screen_filter->fovy, reflected_screen_filter->aspect, |
| reflected_screen_filter->znear, reflected_screen_filter->zfar, |
| GST_GL_DISPLAY_PROJECTION_PERSPECTIVE, |
| (gpointer) reflected_screen_filter); |
| |
| return TRUE; |
| } |
| |
| static void |
| gst_gl_filter_reflected_screen_draw_separated_screen (GstGLFilter * filter, |
| gint width, gint height, guint texture, gfloat alphs, gfloat alphe) |
| { |
| //enable ARB Rectangular texturing |
| //that's necessary to have the video displayed on our screen (with gstreamer) |
| glEnable (GL_TEXTURE_2D); |
| glBindTexture (GL_TEXTURE_2D, texture); |
| //configure parameters for the texturing |
| //the two first are used to specified how the texturing will be done if the screen is greater than the texture herself |
| glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| //the next two specified how the texture will comport near the limits |
| glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| //creating screen and setting the texture (depending on texture's height and width) |
| glBegin (GL_QUADS); |
| |
| // right Face |
| glColor4f (1.0f, 1.0f, 1.0f, alphs); |
| glTexCoord2f (0.5f, 1.0f); |
| glVertex3f (-0.75f, 0.0f, -1.0f); |
| glColor4f (1.0f, 1.0f, 1.0f, alphe); |
| glTexCoord2f (0.5f, 0.0f); |
| glVertex3f (-0.75f, 1.25f, -1.0f); |
| glTexCoord2f (1.0f, 0.0f); |
| glVertex3f (1.25f, 1.25f, -1.0f); |
| glColor4f (1.0f, 1.0f, 1.0f, alphs); |
| glTexCoord2f (1.0f, 1.0f); |
| glVertex3f (1.25f, 0.0f, -1.0f); |
| // Left Face |
| glColor4f (1.0f, 1.0f, 1.0f, alphs); |
| glTexCoord2f (0.5f, 1.0f); |
| glVertex3f (-1.0f, 0.0f, -0.75f); |
| glTexCoord2f (0.0f, 1.0f); |
| glVertex3f (-1.0f, 0.0f, 1.25f); |
| glColor4f (1.0f, 1.0f, 1.0f, alphe); |
| glTexCoord2f (0.0f, 0.0f); |
| glVertex3f (-1.0f, 1.25f, 1.25f); |
| glTexCoord2f (0.5f, 0.0f); |
| glVertex3f (-1.0f, 1.25f, -0.75f); |
| |
| glEnd (); |
| glDisable (GL_TEXTURE_2D); |
| } |
| |
| static void |
| gst_gl_filter_reflected_screen_draw_screen (GstGLFilter * filter, |
| gint width, gint height, guint texture) |
| { |
| //enable ARB Rectangular texturing |
| //that's necessary to have the video displayed on our screen (with gstreamer) |
| glEnable (GL_TEXTURE_2D); |
| glBindTexture (GL_TEXTURE_2D, texture); |
| //configure parameters for the texturing |
| //the two first are used to specified how the texturing will be done if the screen is greater than the texture herself |
| glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| //the next two specified how the texture will comport near the limits |
| glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| //creating screen and setting the texture (depending on texture's height and width) |
| glBegin (GL_QUADS); |
| |
| glTexCoord2f (0.5f, 1.0f); |
| glVertex3f (-1.0f, 0.0f, -1.0f); |
| glTexCoord2f (0.5f, 0.0f); |
| glVertex3f (-1.0f, 1.0f, -1.0f); |
| glTexCoord2f (1.0f, 0.0f); |
| glVertex3f (1.0f, 1.0f, -1.0f); |
| glTexCoord2f (1.0f, 1.0f); |
| glVertex3f (1.0f, 0.0f, -1.0f); |
| // Left Face |
| glTexCoord2f (0.5f, 1.0f); |
| glVertex3f (-1.0f, 0.0f, -1.0f); |
| glTexCoord2f (0.0f, 1.0f); |
| glVertex3f (-1.0f, 0.0f, 1.0f); |
| glTexCoord2f (0.0f, 0.0f); |
| glVertex3f (-1.0f, 1.0f, 1.0f); |
| glTexCoord2f (0.5f, 0.0f); |
| glVertex3f (-1.0f, 1.0f, -1.0f); |
| |
| glEnd (); |
| |
| //disable this kind of texturing (useless for the gluDisk) |
| glDisable (GL_TEXTURE_2D); |
| } |
| |
| static void |
| gst_gl_filter_reflected_screen_draw_background (void) |
| { |
| glBegin (GL_QUADS); |
| |
| // right Face |
| |
| glColor4f (0.0f, 0.0f, 0.0f, 1.0f); |
| glVertex3f (-10.0f, -10.0f, -1.0f); |
| |
| glColor4f (0.0f, 0.0f, 0.2f, 1.0f); |
| glVertex3f (-10.0f, 10.0f, -1.0f); |
| glVertex3f (10.0f, 10.0f, -1.0f); |
| glVertex3f (10.0f, -10.0f, -1.0f); |
| |
| glEnd (); |
| } |
| |
| static void |
| gst_gl_filter_reflected_screen_draw_floor (void) |
| { |
| GLUquadricObj *q; |
| //create a quadric for the floor's drawing |
| q = gluNewQuadric (); |
| //configure this quadric's parameter (for lighting and texturing) |
| gluQuadricNormals (q, GL_SMOOTH); |
| gluQuadricTexture (q, GL_FALSE); |
| |
| //drawing the disk. The texture are mapped thanks to the parameter we gave to the GLUquadric q |
| gluDisk (q, 0.0, 2.2, 50, 1); |
| } |
| |
| //opengl scene, params: input texture (not the output filter->texture) |
| static void |
| gst_gl_filter_reflected_screen_callback (gint width, gint height, guint texture, |
| gpointer stuff) |
| { |
| GstGLFilter *filter = GST_GL_FILTER (stuff); |
| GstGLFilterReflectedScreen *reflected_screen_filter = |
| GST_GL_FILTER_REFLECTED_SCREEN (stuff); |
| |
| glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| |
| //load identity befor tracing |
| glLoadIdentity (); |
| //camera translation |
| glTranslatef (0.0f, 0.1f, -1.3f); |
| //camera configuration |
| if (reflected_screen_filter->separated_screen) |
| gluLookAt (0.1, -0.25, 2.0, 0.025, 0.0, 0.0, 0.0, 1.0, 0.0); |
| else |
| gluLookAt (0.1, -0.35, 2.0, 0.025, 0.0, 0.0, 0.0, 1.0, 0.0); |
| |
| gst_gl_filter_reflected_screen_draw_background (); |
| |
| if (reflected_screen_filter->separated_screen) { |
| glEnable (GL_BLEND); |
| |
| glPushMatrix (); |
| glScalef (1.0f, -1.0f, 1.0f); |
| glTranslatef (0.0f, 0.0f, 1.2f); |
| glRotatef (-45.0f, 0.0, 1.0, 0.0); |
| gst_gl_filter_reflected_screen_draw_separated_screen (filter, width, height, |
| texture, 1.0f, 1.0f); |
| glPopMatrix (); |
| |
| if (reflected_screen_filter->active_graphic_mode) { |
| //configuration of the transparency function |
| glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
| glTranslatef (0.0f, 0.0f, 1.2f); |
| glRotatef (-45.0f, 0.0, 1.0, 0.0); |
| gst_gl_filter_reflected_screen_draw_separated_screen (filter, width, |
| height, texture, 0.5f, 0.0f); |
| glDisable (GL_BLEND); |
| } |
| } |
| if (reflected_screen_filter->show_floor) { |
| glLightfv (GL_LIGHT0, GL_AMBIENT, LightAmb); |
| glLightfv (GL_LIGHT0, GL_DIFFUSE, LightDif); |
| glLightfv (GL_LIGHT0, GL_POSITION, LightPos); |
| |
| //enable lighting |
| glEnable (GL_LIGHT0); |
| glEnable (GL_LIGHTING); |
| |
| if (reflected_screen_filter->active_graphic_mode) { |
| glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); |
| //enable stencil buffer use |
| glEnable (GL_STENCIL_TEST); |
| //setting the stencil buffer. Each time a pixel will be drawn by now, this pixel value will be set to 1 |
| glStencilFunc (GL_ALWAYS, 1, 1); |
| glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE); |
| |
| //disable the zbuffer |
| glDisable (GL_DEPTH_TEST); |
| //make a rotation of 90 degree on x axis. By default, gluDisk draw a disk on z axis |
| glRotatef (-90.0f, 1.0, 0.0, 0.0); |
| //draw the floor. Each pixel representing this floor will now have a value of 1 on stencil buffer |
| gst_gl_filter_reflected_screen_draw_floor (); |
| //make an anti-rotation of 90 degree to draw the rest of the scene on the right angle |
| glRotatef (90.0f, 1.0, 0.0, 0.0); |
| //enable zbuffer again |
| glEnable (GL_DEPTH_TEST); |
| //enable the drawing to be shown |
| glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| //say that the next object have to be drawn ONLY where the stencil buffer's pixel's value is 1 |
| glStencilFunc (GL_EQUAL, 1, 1); |
| glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); |
| |
| //save the actual matrix |
| glPushMatrix (); |
| glLightfv (GL_LIGHT0, GL_POSITION, LightPos); |
| //translate the object on z axis |
| glTranslatef (0.0f, 0.0f, 1.4f); |
| //rotate it (because the drawing method place the user behind the left part of the screen) |
| glRotatef (-45.0f, 0.0, 1.0, 0.0); |
| //draw the reflexion |
| gst_gl_filter_reflected_screen_draw_screen (filter, width, height, |
| texture); |
| //return to the saved matrix position |
| glPopMatrix (); |
| //end of the stencil buffer uses |
| glDisable (GL_STENCIL_TEST); |
| |
| //enable the blending to mix the floor and reflexion color |
| glEnable (GL_BLEND); |
| glDisable (GL_LIGHTING); |
| //specified a white color (for the floor) with 20% transparency |
| glColor4f (1.0f, 1.0f, 1.0f, 0.8f); |
| //configuration of the transparency function |
| glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
| } |
| //draw the floor (which will appear this time) |
| glRotatef (-90.0f, 1.0, 0.0, 0.0); |
| gst_gl_filter_reflected_screen_draw_floor (); |
| glRotatef (90.0f, 1.0, 0.0, 0.0); |
| glDisable (GL_BLEND); |
| glEnable (GL_LIGHTING); |
| //draw the real object |
| //scale on y axis. The object must be drawn upside down (to suggest a reflexion) |
| glScalef (1.0f, -1.0f, 1.0f); |
| glTranslatef (0.0f, 0.0f, 1.4f); |
| glRotatef (-45.0f, 0.0, 1.0, 0.0); |
| gst_gl_filter_reflected_screen_draw_screen (filter, width, height, texture); |
| glDisable (GL_LIGHTING); |
| } |
| } |