/*****************************************************************************
* renderer.c
*****************************************************************************
* Copyright (C) 2004-2020 VLC authors and VideoLAN
* Copyright (C) 2009, 2011 Laurent Aimar
*
* Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
* Ilkka Ollakka <ileoo@videolan.org>
* Rémi Denis-Courmont
* Adrien Maglo <magsoft at videolan dot org>
* Felix Paul Kühne <fkuehne at videolan dot org>
* Pierre d'Herbemont <pdherbemont at videolan dot org>
* Alexandre Janniaux <ajanni@videolabs.io>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "renderer.h"
#include <assert.h>
#include <vlc_common.h>
#include <vlc_es.h>
#include <vlc_picture.h>
#include <vlc_opengl_filter.h>
#include <vlc_configuration.h>
#include "gl_util.h"
#include "sampler.h"
#include "picture.h"
#define SPHERE_RADIUS 1.f
static void getZoomMatrix( float zoom, GLfloat matrix[ static 16]) {
const GLfloat m[] = {
/* x y z w */
1. 0f, 0. 0f, 0. 0f, 0. 0f,
0. 0f, 1. 0f, 0. 0f, 0. 0f,
0. 0f, 0. 0f, 1. 0f, 0. 0f,
0. 0f, 0. 0f, zoom, 1. 0f
};
memcpy(matrix, m, sizeof(m));
}
/* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
static void getProjectionMatrix( float sar, float fovy, GLfloat matrix[ static 16]) {
float zFar = 1000;
float zNear = 0. 01;
float f = 1.f / tanf(fovy / 2.f);
const GLfloat m[] = {
f / sar, 0.f, 0.f, 0.f,
0.f, f, 0.f, 0.f,
0.f, 0.f, (zNear + zFar) / (zNear - zFar), - 1.f,
0.f, 0.f, ( 2 * zNear * zFar) / (zNear - zFar), 0.f};
memcpy(matrix, m, sizeof(m));
}
static void getViewpointMatrixes( struct vlc_gl_renderer *renderer,
video_projection_mode_t projection_mode)
{
if ((projection_mode == PROJECTION_MODE_EQUIRECTANGULAR
|| projection_mode == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD)
&& renderer->multiview_mode == renderer->sampler->fmt_in.multiview_mode)
{
getProjectionMatrix(renderer->f_sar, renderer->f_fovy,
renderer->var.ProjectionMatrix);
getZoomMatrix(renderer->f_z, renderer->var.ZoomMatrix);
/* renderer->vp has been reversed and is a world transform */
vlc_viewpoint_to_4x4(&renderer->vp, renderer->var.ViewMatrix);
}
else
{
memcpy(renderer->var.ProjectionMatrix, MATRIX4_IDENTITY,
sizeof(MATRIX4_IDENTITY));
memcpy(renderer->var.ZoomMatrix, MATRIX4_IDENTITY,
sizeof(MATRIX4_IDENTITY));
memcpy(renderer->var.ViewMatrix, MATRIX4_IDENTITY,
sizeof(MATRIX4_IDENTITY));
}
}
static void
InitStereoMatrix(GLfloat matrix_out[ static 3* 3],
vlc_stereoscopic_mode_t stereo_mode,
video_multiview_mode_t multiview_mode)
{
/*
* The stereo matrix transforms 2D pictures coordinates to crop the
* content, in order to view only one eye.
*
* This 2D transformation is affine, so the matrix is 3x3 and applies to 3D
* vectors in the form (x, y, 1).
*/
memcpy(matrix_out, MATRIX3_IDENTITY, sizeof(MATRIX3_IDENTITY));
#define COL(x) (x* 3)
#define ROW(x) (x)
if (stereo_mode == VIDEO_STEREO_OUTPUT_SIDE_BY_SIDE)
return;
assert(stereo_mode == VIDEO_STEREO_OUTPUT_LEFT_ONLY ||
stereo_mode == VIDEO_STEREO_OUTPUT_RIGHT_ONLY);
switch (multiview_mode)
{
case MULTIVIEW_STEREO_SBS:
/*
* +----------+----------+
* | . |
* | LEFT . RIGHT |
* | EYE . EYE |
* | . |
* +----------+----------+
*
* To crop the coordinates to the left eye, divide the x
* coordinates by 2:
*
* / 0.5 0 0 \
* matrix = | 0 1 0 |
* \ 0 0 1 /
*/
matrix_out[COL( 0) + ROW( 0)] = 0. 5;
if (stereo_mode == VIDEO_STEREO_OUTPUT_RIGHT_ONLY)
matrix_out[COL( 2) + ROW( 0)] = 0. 5;
break;
case MULTIVIEW_STEREO_TB:
/*
* +----------+
* | |
* | LEFT |
* | EYE |
* | |
* +..........+
* | |
* | RIGHT |
* | EYE |
* | |
* +----------+
*
* To crop the coordinates to the left eye, divide the y
* coordinates by 2:
*
* / 1 0 0 \
* matrix = | 0 0.5 0 |
* \ 0 0 1 /
*/
matrix_out[COL( 1) + ROW( 1)] = 0. 5;
if (stereo_mode == VIDEO_STEREO_OUTPUT_RIGHT_ONLY)
matrix_out[COL( 2) + ROW( 1)] = 0. 5;
break;
default:
break;
}
#undef COL
#undef ROW
}
static int
opengl_link_program( struct vlc_gl_filter *filter)
{
struct vlc_gl_renderer *renderer = filter->sys;
struct vlc_gl_sampler *sampler = renderer->sampler;
const opengl_vtable_t *vt = renderer->vt;
static const char * const VERTEX_SHADER_BODY =
"#if __VERSION__ < 300\n"
"attribute vec2 PicCoordsIn;\n"
"varying vec2 PicCoords;\n"
"attribute vec3 VertexPosition;\n"
"#else\n"
"in vec2 PicCoordsIn;\n"
"out vec2 PicCoords;\n"
"in vec3 VertexPosition;\n"
"#endif\n"
"uniform mat3 StereoMatrix;\n"
"uniform mat4 ProjectionMatrix;\n"
"uniform mat4 ZoomMatrix;\n"
"uniform mat4 ViewMatrix;\n"
"uniform mat4 OrientationMatrix;\n"
"void main() {\n"
" PicCoords = (StereoMatrix * vec3(PicCoordsIn, 1.0)).st;\n"
" gl_Position = OrientationMatrix * ProjectionMatrix * ZoomMatrix * ViewMatrix\n"
" * vec4(VertexPosition, 1.0);\n"
"}\n";
static const char * const FRAGMENT_SHADER_BODY =
"#if __VERSION__ < 300\n"
"#define FragColor gl_FragColor\n"
"varying vec2 PicCoords;\n"
"#else\n"
"in vec2 PicCoords;\n"
"out vec4 FragColor;\n"
"#endif\n"
"void main() {\n"
" FragColor = vlc_texture(PicCoords);\n"
"}\n";
const char *extensions = sampler->shader.extensions
? sampler->shader.extensions : "";
const char *vertex_shader[] = {
sampler->shader.version,
VERTEX_SHADER_BODY,
};
const char *fragment_shader[] = {
sampler->shader.version,
extensions,
sampler->shader.precision,
sampler->shader.body,
FRAGMENT_SHADER_BODY,
};
if (renderer->dump_shaders)
{
video_format_t *fmt = &sampler->fmt_in;
msg_Dbg(filter, "\n=== Vertex shader for fourcc: %4.4s ===\n",
( const char *) &fmt->i_chroma);
for ( unsigned i = 0; i < ARRAY_SIZE(vertex_shader); ++i)
msg_Dbg(filter, "[%u] %s", i, vertex_shader[i]);
msg_Dbg(filter,
"\n=== Fragment shader for fourcc: %4.4s, colorspace: %d ===\n",
( const char *) &fmt->i_chroma, fmt->space);
for ( unsigned i = 0; i < ARRAY_SIZE(fragment_shader); ++i)
msg_Dbg(filter, "[%u] %s", i, fragment_shader[i]);
}
assert(sampler->ops &&
sampler->ops->fetch_locations &&
sampler->ops->load);
GLuint program_id =
vlc_gl_BuildProgram(VLC_OBJECT(filter), vt,
ARRAY_SIZE(vertex_shader), vertex_shader,
ARRAY_SIZE(fragment_shader), fragment_shader);
if (!program_id)
return VLC_EGENERIC;
/* Fetch UniformLocations and AttribLocations */
#define GET_LOC(type, x, str) do { \
x = vt->Get ##type ##Location(program_id, str); \
assert(x != - 1); \
if (x == - 1) { \
msg_Err(filter, "Unable to Get"#type"Location(%s)", str); \
goto error; \
} \
} while ( 0)
#define GET_ULOC(x, str) GET_LOC(Uniform, renderer->uloc.x, str)
#define GET_ALOC(x, str) GET_LOC(Attrib, renderer->aloc.x, str)
GET_ULOC(StereoMatrix, "StereoMatrix");
GET_ULOC(ProjectionMatrix, "ProjectionMatrix");
GET_ULOC(ViewMatrix, "ViewMatrix");
GET_ULOC(ZoomMatrix, "ZoomMatrix");
GET_ULOC(OrientationMatrix, "OrientationMatrix");
GET_ALOC(PicCoordsIn, "PicCoordsIn");
GET_ALOC(VertexPosition, "VertexPosition");
#undef GET_LOC
#undef GET_ULOC
#undef GET_ALOC
vlc_gl_sampler_FetchLocations(sampler, program_id);
renderer->program_id = program_id;
return VLC_SUCCESS;
error:
vt->DeleteProgram(program_id);
renderer->program_id = 0;
return VLC_EGENERIC;
}
static void
Close( struct vlc_gl_filter *filter)
{
struct vlc_gl_renderer *renderer = filter->sys;
const opengl_vtable_t *vt = renderer->vt;
vt->DeleteBuffers( 1, &renderer->vertex_buffer_object);
vt->DeleteBuffers( 1, &renderer->index_buffer_object);
vt->DeleteBuffers( 1, &renderer->texture_buffer_object);
vt->DeleteVertexArrays( 1, &renderer->vertex_array_object);
if (renderer->program_id != 0)
vt->DeleteProgram(renderer->program_id);
free(renderer);
}
static void UpdateZ( struct vlc_gl_renderer *renderer)
{
/* Do trigonometry to calculate the minimal z value
* that will allow us to zoom out without seeing the outside of the
* sphere (black borders). */
float tan_fovx_2 = tanf(renderer->f_fovx / 2);
float tan_fovy_2 = tanf(renderer->f_fovy / 2);
float z_min = - SPHERE_RADIUS / sinf(atanf(sqrtf(
tan_fovx_2 * tan_fovx_2 + tan_fovy_2 * tan_fovy_2)));
/* The FOV value above which z is dynamically calculated. */
const float z_thresh = 90.f;
if (renderer->f_fovx <= z_thresh * M_PI / 180)
renderer->f_z = 0;
else
{
float f = z_min / ((FIELD_OF_VIEW_DEGREES_MAX - z_thresh) * M_PI / 180);
renderer->f_z = f * renderer->f_fovx - f * z_thresh * M_PI / 180;
if (renderer->f_z < z_min)
renderer->f_z = z_min;
}
}
static void UpdateFOVy( struct vlc_gl_renderer *renderer)
{
renderer->f_fovy = 2 * atanf(tanf(renderer->f_fovx / 2) / renderer->f_sar);
}
int
vlc_gl_renderer_SetViewpoint( struct vlc_gl_renderer *renderer,
const vlc_viewpoint_t *p_vp)
{
if (p_vp->fov > FIELD_OF_VIEW_DEGREES_MAX
|| p_vp->fov < FIELD_OF_VIEW_DEGREES_MIN)
return VLC_EINVAL;
// Convert degree into radian
float f_fovx = p_vp->fov * ( float)M_PI / 180.f;
/* Copy the viewpoint for future pictures. */
renderer->vp = *p_vp;
if (fabsf(f_fovx - renderer->f_fovx) >= 0. 001f)
{
/* FOVx has changed. */
renderer->f_fovx = f_fovx;
UpdateFOVy(renderer);
UpdateZ(renderer);
}
getViewpointMatrixes(renderer, renderer->projection_mode);
return VLC_SUCCESS;
}
static void
vlc_gl_renderer_SetOutputSize( struct vlc_gl_renderer *renderer, unsigned width,
unsigned height)
{
float f_sar = ( float) width / height;
/* Each time the window size changes, we must recompute the minimum zoom
* since the aspect ration changes.
* We must also set the new current zoom value. */
renderer->target_width = width;
renderer->target_height = height;
renderer->f_sar = f_sar;
UpdateFOVy(renderer);
UpdateZ(renderer);
getViewpointMatrixes(renderer, renderer->projection_mode);
}
static int
RequestOutputSize( struct vlc_gl_filter *filter,
struct vlc_gl_tex_size *req,
struct vlc_gl_tex_size *optimal_in)
{
struct vlc_gl_renderer *renderer = filter->sys;
vlc_gl_renderer_SetOutputSize(renderer, req->width, req->height);
/* The optimal input size is the size for which the renderer do not need to
* scale */
optimal_in->width = renderer->target_width;
optimal_in->height = renderer->target_height;
return VLC_SUCCESS;
}
static int BuildSphere(GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVerti ces,
GLushort **indices, unsigned *nbIndices)
{
unsigned nbLatBands = 128;
unsigned nbLonBands = 128;
*nbVertices = (nbLatBands + 1) * (nbLonBands + 1);
*nbIndices = nbLatBands * nbLonBands * 3 * 2;
*vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
if (*vertexCoord == NULL)
return VLC_ENOMEM;
*textureCoord = vlc_alloc(*nbVertices * 2, sizeof(GLfloat));
if (*textureCoord == NULL)
{
free(*vertexCoord);
return VLC_ENOMEM;
}
*indices = vlc_alloc(*nbIndices, sizeof(GLushort));
if (*indices == NULL)
{
free(*textureCoord);
free(*vertexCoord);
return VLC_ENOMEM;
}
for (unsigned lat = 0; lat <= nbLatBands; lat++) {
float theta = lat * (float) M_PI / nbLatBands;
float sinTheta, cosTheta;
sincosf(theta, &sinTheta, &cosTheta);
for (unsigned int lon = 0; lon <= nbLonBands; lon++) {
float phi = 2.f * (float)M_PI * (float)lon / nbLonBands;
float sinPhi, cosPhi;
sincosf(phi, &sinPhi, &cosPhi);
/* The camera is centered on the Z axis when yaw = 0, while the
* front part of the equirectangular texture is located at u=0.5.
* To have the camera at the correct location, phi is
* shifted +pi/2 to have u=0.5 fall at the correct location.
*
* Another way to interpret the shift is to interpret the shift
* as a shift in texture coordinate. Considering the initial
* orientation of the camera to Z which accounts as a pi/2
* rotation, adding pi/2 maps the first and last coordinate to the
* meridian, pi radians after the camera, so that u=0 amd u=1, ie.
* the back face, is mapped to the back of the initial orientation
* of the camera. */
float x = -sinPhi * sinTheta;
float y = cosTheta;
float z = cosPhi * sinTheta;
unsigned off1 = (lat * (nbLonBands + 1) + lon) * 3;
(*vertexCoord)[off1 + 0] = SPHERE_RADIUS * x;
(*vertexCoord)[off1 + 1] = SPHERE_RADIUS * y;
(*vertexCoord)[off1 + 2] = SPHERE_RADIUS * z;
unsigned off2 = (lat * (nbLonBands + 1) + lon) * 2;
float u = (float)lon / nbLonBands;
/* In OpenGL, the texture coordinates start at bottom left */
float v = 1.0f - (float)lat / nbLatBands;
(*textureCoord)[off2] = u;
(*textureCoord)[off2 + 1] = v;
}
}
for (unsigned lat = 0; lat < nbLatBands; lat++) {
for (unsigned lon = 0; lon < nbLonBands; lon++) {
unsigned first = (lat * (nbLonBands + 1)) + lon;
unsigned second = first + nbLonBands + 1;
unsigned off = (lat * nbLatBands + lon) * 3 * 2;
(*indices)[off] = first;
(*indices)[off + 1] = second;
(*indices)[off + 2] = first + 1;
(*indices)[off + 3] = second;
(*indices)[off + 4] = second + 1;
(*indices)[off + 5] = first + 1;
}
}
return VLC_SUCCESS;
}
static int BuildCube(float padW, float padH,
GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
GLushort **indices, unsigned *nbIndices)
{
*nbVertices = 4 * 6;
*nbIndices = 6 * 6;
*vertexCoord = NULL;
*textureCoord = NULL;
*indices = NULL;
*vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
if (*vertexCoord == NULL)
goto error;
*textureCoord = vlc_alloc(*nbVertices * 2, sizeof(GLfloat));
if (*textureCoord == NULL)
goto error;
*indices = vlc_alloc(*nbIndices, sizeof(GLushort));
if (*indices == NULL)
goto error;
#define CUBEFACE(swap, value) \
swap(value, -1.f, 1.f), \
swap(value, -1.f, -1.f), \
swap(value, 1.f, 1.f), \
swap(value, 1.f, -1.f)
#define X_FACE(v, a, b) (v), (b), (a)
#define Y_FACE(v, a, b) (a), (v), (b)
#define Z_FACE(v, a, b) (a), (b), (v)
static const GLfloat coord[] = {
CUBEFACE(Z_FACE, -1.f), // FRONT
CUBEFACE(Z_FACE, +1.f), // BACK
CUBEFACE(X_FACE, -1.f), // LEFT
CUBEFACE(X_FACE, +1.f), // RIGHT
CUBEFACE(Y_FACE, -1.f), // BOTTOM
CUBEFACE(Y_FACE, +1.f), // TOP
};
#undef X_FACE
#undef Y_FACE
#undef Z_FACE
#undef CUBEFACE
memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
float col[] = {0.f, 1.f/3, 2.f/3, 1.f};
float row[] = {0.f, 1.f/2, 1.0};
const GLfloat tex[] = {
col[1] + padW, row[1] - padH, // front
col[1] + padW, row[0] + padH,
col[2] - padW, row[1] - padH,
col[2] - padW, row[0] + padH,
col[3] - padW, row[1] - padH, // back
col[3] - padW, row[0] + padH,
col[2] + padW, row[1] - padH,
col[2] + padW, row[0] + padH,
col[2] - padW, row[2] - padH, // left
col[2] - padW, row[1] + padH,
col[1] + padW, row[2] - padH,
col[1] + padW, row[1] + padH,
col[0] + padW, row[2] - padH, // right
col[0] + padW, row[1] + padH,
col[1] - padW, row[2] - padH,
col[1] - padW, row[1] + padH,
col[0] + padW, row[0] + padH, // bottom
col[0] + padW, row[1] - padH,
col[1] - padW, row[0] + padH,
col[1] - padW, row[1] - padH,
col[2] + padW, row[2] - padH, // top
col[2] + padW, row[1] + padH,
col[3] - padW, row[2] - padH,
col[3] - padW, row[1] + padH,
};
memcpy(*textureCoord, tex,
*nbVertices * 2 * sizeof(GLfloat));
const GLushort ind[] = {
0, 1, 2, 2, 1, 3, // front
6, 7, 4, 4, 7, 5, // back
10, 11, 8, 8, 11, 9, // left
12, 13, 14, 14, 13, 15, // right
18, 19, 16, 16, 19, 17, // bottom
20, 21, 22, 22, 21, 23, // top
};
memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
return VLC_SUCCESS;
error:
free(*vertexCoord);
free(*textureCoord);
return VLC_ENOMEM;
}
static int BuildRectangle(
GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
GLushort **indices, unsigned *nbIndices, video_orientation_t orientation)
{
*nbVertices = 4;
*nbIndices = 6;
*vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
if (*vertexCoord == NULL)
return VLC_ENOMEM;
*textureCoord = vlc_alloc(*nbVertices * 2, sizeof(GLfloat));
if (*textureCoord == NULL)
{
free(*vertexCoord);
return VLC_ENOMEM;
}
*indices = vlc_alloc(*nbIndices, sizeof(GLushort));
if (*indices == NULL)
{
free(*textureCoord);
free(*vertexCoord);
return VLC_ENOMEM;
}
static const GLfloat coord[] = {
-1.0, 1.0, -1.0f,
-1.0, -1.0, -1.0f,
1.0, 1.0, -1.0f,
1.0, -1.0, -1.0f
};
memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
static const GLfloat tex_normal[] = {
0.0, 1.0,
0.0, 0.0,
1.0, 1.0,
1.0, 0.0,
};
static const GLfloat tex_hflip[] = {
1.0, 1.0,
1.0, 0.0,
0.0, 1.0,
0.0, 0.0,
};
/* Since there is a bijection between symmetry and rotation,
* applying the general orientation in each case is equivalent
* to choosing the rotation we want to apply to the frame, and
* then decide whether we apply a horizontal flip or not. */
const GLfloat *tex;
if (ORIENT_IS_MIRROR(orientation))
tex = tex_hflip;
else
tex = tex_normal;
memcpy(*textureCoord, tex, *nbVertices * 2 * sizeof(GLfloat));
const GLushort ind[] = {
0, 1, 2,
2, 1, 3
};
memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
return VLC_SUCCESS;
}
static int SetupCoords(struct vlc_gl_renderer *renderer,
const struct vlc_gl_picture *pic,
video_orientation_t orientation)
{
const opengl_vtable_t *vt = renderer->vt;
struct vlc_gl_sampler *sampler = renderer->sampler;
const video_format_t *fmt = &sampler->fmt_in;
GLfloat *vertexCoord, *textureCoord;
GLushort *indices;
unsigned nbVertices, nbIndices;
video_projection_mode_t projection_mode = renderer->projection_mode;
if (renderer->multiview_mode != renderer->sampler->fmt_in.multiview_mode)
projection_mode = PROJECTION_MODE_RECTANGULAR;
int i_ret;
switch (projection_mode)
{
case PROJECTION_MODE_RECTANGULAR:
i_ret = BuildRectangle(&vertexCoord, &textureCoord, &nbVertices,
&indices, &nbIndices, orientation);
break;
case PROJECTION_MODE_EQUIRECTANGULAR:
i_ret = BuildSphere(&vertexCoord, &textureCoord, &nbVertices,
&indices, &nbIndices);
break;
case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
i_ret = BuildCube((float)fmt->i_cubemap_padding / fmt->i_width,
(float)fmt->i_cubemap_padding / fmt->i_height,
&vertexCoord, &textureCoord, &nbVertices,
&indices, &nbIndices);
break;
default:
i_ret = VLC_EGENERIC;
break;
}
if (i_ret != VLC_SUCCESS)
return i_ret;
/* Transform picture-to-texture coordinates in place */
vlc_gl_picture_ToTexCoords(pic, nbVertices, textureCoord, textureCoord);
vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object);
vt->BufferData(GL_ARRAY_BUFFER, nbVertices * 2 * sizeof(GLfloat),
textureCoord, GL_STATIC_DRAW);
vt->BindBuffer(GL_ARRAY_BUFFER, renderer->vertex_buffer_object);
vt->BufferData(GL_ARRAY_BUFFER, nbVertices * 3 * sizeof(GLfloat),
vertexCoord, GL_STATIC_DRAW);
vt->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->index_buffer_object);
vt->BufferData(GL_ELEMENT_ARRAY_BUFFER, nbIndices * sizeof(GLushort),
indices, GL_STATIC_DRAW);
free(textureCoord);
free(vertexCoord);
free(indices);
renderer->nb_indices = nbIndices;
return VLC_SUCCESS;
}
static int
Draw(struct vlc_gl_filter *filter, const struct vlc_gl_picture *pic,
const struct vlc_gl_input_meta *meta)
{
(void) meta;
struct vlc_gl_renderer *renderer = filter->sys;
const opengl_vtable_t *vt = renderer->vt;
vt->ClearColor(0.f,0.f,0.f,1.f);
vt->Clear(GL_COLOR_BUFFER_BIT);
GL_ASSERT_NOERROR(vt);
vt->UseProgram(renderer->program_id);
struct vlc_gl_sampler *sampler = renderer->sampler;
vlc_gl_sampler_Update(sampler, pic);
vlc_gl_sampler_Load(sampler);
if (pic->mtx_has_changed)
renderer->valid_coords = false;
if (!renderer->valid_coords)
{
int ret = SetupCoords(renderer, pic, meta->orientation);
if (ret != VLC_SUCCESS)
return ret;
renderer->valid_coords = true;
}
float orientation_matrix[ARRAY_SIZE(MATRIX4_IDENTITY)];
if (meta->orientation != ORIENT_NORMAL)
{
vlc_viewpoint_t orient_vp;
vlc_viewpoint_from_orientation(&orient_vp, meta->orientation);
vlc_viewpoint_to_4x4(&orient_vp, orientation_matrix);
}
else memcpy(orientation_matrix, MATRIX4_IDENTITY, sizeof(MATRIX4_IDENTITY));
vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object);
vt->BindVertexArray(renderer->vertex_array_object);
assert(renderer->aloc.PicCoordsIn != -1);
vt->EnableVertexAttribArray(renderer->aloc.PicCoordsIn);
vt->VertexAttribPointer(renderer->aloc.PicCoordsIn, 2, GL_FLOAT, 0, 0, 0);
vt->BindBuffer(GL_ARRAY_BUFFER, renderer->vertex_buffer_object);
vt->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->index_buffer_object);
vt->EnableVertexAttribArray(renderer->aloc.VertexPosition);
vt->VertexAttribPointer(renderer->aloc.VertexPosition, 3, GL_FLOAT, 0, 0, 0);
vt->UniformMatrix3fv(renderer->uloc.StereoMatrix, 1, GL_FALSE,
renderer->var.StereoMatrix);
vt->UniformMatrix4fv(renderer->uloc.ProjectionMatrix, 1, GL_FALSE,
renderer->var.ProjectionMatrix);
vt->UniformMatrix4fv(renderer->uloc.ViewMatrix, 1, GL_FALSE,
renderer->var.ViewMatrix);
vt->UniformMatrix4fv(renderer->uloc.ZoomMatrix, 1, GL_FALSE,
renderer->var.ZoomMatrix);
vt->UniformMatrix4fv(renderer->uloc.OrientationMatrix, 1, GL_FALSE,
orientation_matrix);
vt->DrawElements(GL_TRIANGLES, renderer->nb_indices, GL_UNSIGNED_SHORT, 0);
GL_ASSERT_NOERROR(vt);
return VLC_SUCCESS;
}
int
vlc_gl_renderer_Open(struct vlc_gl_filter *filter,
const config_chain_t *config,
struct vlc_gl_sampler *sampler,
struct vlc_gl_tex_size *size_out)
{
(void) size_out;
const char * const options[] = { "projection-mode", "video-stereo-mode", NULL };
config_ChainParse(filter, "", options, config);
struct vlc_gl_renderer *renderer = calloc(1, sizeof(*renderer));
if (!renderer)
return VLC_ENOMEM;
int ret = vlc_gl_api_Init(&renderer->api, filter->gl);
if (ret != VLC_SUCCESS)
{
free(renderer);
return VLC_EGENERIC;
}
static const struct vlc_gl_filter_ops filter_ops = {
.draw = Draw,
.close = Close,
.request_output_size = RequestOutputSize,
};
filter->ops = &filter_ops;
filter->sys = renderer;
renderer->sampler = sampler;
renderer->vt = &renderer->api.vt;
renderer->dump_shaders = var_InheritInteger(filter, "verbose") >= 4;
int projection_mode = var_InheritInteger(filter, "projection-mode");
switch (projection_mode)
{
case PROJECTION_MODE_RECTANGULAR:
renderer->projection_mode = projection_mode;
break;
default:
case -1:
renderer->projection_mode = sampler->fmt_in.projection_mode;
break;
}
int stereo_mode = var_InheritInteger(filter, "video-stereo-mode");
if (stereo_mode > VIDEO_STEREO_OUTPUT_AUTO && stereo_mode <= VIDEO_STEREO_OUTPUT_MAX)
renderer->stereo_mode = stereo_mode;
if (sampler->fmt_in.multiview_mode == MULTIVIEW_2D)
{
renderer->stereo_mode = VIDEO_STEREO_OUTPUT_SIDE_BY_SIDE;
renderer->multiview_mode = MULTIVIEW_2D;
}
else switch (stereo_mode)
{
case VIDEO_STEREO_OUTPUT_LEFT_ONLY:
case VIDEO_STEREO_OUTPUT_RIGHT_ONLY:
case VIDEO_STEREO_OUTPUT_AUTO:
/* multiview_mode != MULTIVIEW_2D */
renderer->multiview_mode = sampler->fmt_in.multiview_mode;
if (stereo_mode == VIDEO_STEREO_OUTPUT_AUTO)
renderer->stereo_mode = VIDEO_STEREO_OUTPUT_LEFT_ONLY;
break;
case VIDEO_STEREO_OUTPUT_SIDE_BY_SIDE:
case VIDEO_STEREO_OUTPUT_STEREO: /* stereo mode is not supported yet */
default:
renderer->multiview_mode = MULTIVIEW_2D; /* Enforce the planar view */
renderer->stereo_mode = VIDEO_STEREO_OUTPUT_SIDE_BY_SIDE;
break;
}
ret = opengl_link_program(filter);
if (ret != VLC_SUCCESS)
{
msg_Dbg(&filter->obj, "Could not link shaders");
free(renderer);
return ret;
}
InitStereoMatrix(renderer->var.StereoMatrix, renderer->stereo_mode, renderer->multiview_mode);
getViewpointMatrixes(renderer, renderer->projection_mode);
const opengl_vtable_t *vt = renderer->vt;
vt->GenVertexArrays(1, &renderer->vertex_array_object);
vt->GenBuffers(1, &renderer->vertex_buffer_object);
vt->GenBuffers(1, &renderer->index_buffer_object);
vt->GenBuffers(1, &renderer->texture_buffer_object);
/* The coords will be initialized on first draw */
renderer->valid_coords = false;
return VLC_SUCCESS;
}
¤ Dauer der Verarbeitung: 0.16 Sekunden
¤
*© Formatika GbR, Deutschland
|
|