1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2004-2020 VLC authors and VideoLAN
5 * Copyright (C) 2009, 2011 Laurent Aimar
7 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8 * Ilkka Ollakka <ileoo@videolan.org>
10 * Adrien Maglo <magsoft at videolan dot org>
11 * Felix Paul Kühne <fkuehne at videolan dot org>
12 * Pierre d'Herbemont <pdherbemont at videolan dot org>
14 * This program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU Lesser General Public License as published by
16 * the Free Software Foundation; either version 2.1 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with this program; if not, write to the Free Software Foundation,
26 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
36 #include <vlc_common.h>
38 #include <vlc_picture.h>
43 #include "vout_helper.h"
45 #define SPHERE_RADIUS 1.f
47 static const GLfloat identity
[] = {
48 1.0f
, 0.0f
, 0.0f
, 0.0f
,
49 0.0f
, 1.0f
, 0.0f
, 0.0f
,
50 0.0f
, 0.0f
, 1.0f
, 0.0f
,
51 0.0f
, 0.0f
, 0.0f
, 1.0f
54 static void getZoomMatrix(float zoom
, GLfloat matrix
[static 16]) {
58 1.0f
, 0.0f
, 0.0f
, 0.0f
,
59 0.0f
, 1.0f
, 0.0f
, 0.0f
,
60 0.0f
, 0.0f
, 1.0f
, 0.0f
,
61 0.0f
, 0.0f
, zoom
, 1.0f
64 memcpy(matrix
, m
, sizeof(m
));
67 /* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
68 static void getProjectionMatrix(float sar
, float fovy
, GLfloat matrix
[static 16]) {
73 float f
= 1.f
/ tanf(fovy
/ 2.f
);
76 f
/ sar
, 0.f
, 0.f
, 0.f
,
78 0.f
, 0.f
, (zNear
+ zFar
) / (zNear
- zFar
), -1.f
,
79 0.f
, 0.f
, (2 * zNear
* zFar
) / (zNear
- zFar
), 0.f
};
81 memcpy(matrix
, m
, sizeof(m
));
84 static void getViewpointMatrixes(struct vlc_gl_renderer
*renderer
,
85 video_projection_mode_t projection_mode
)
87 if (projection_mode
== PROJECTION_MODE_EQUIRECTANGULAR
88 || projection_mode
== PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD
)
90 getProjectionMatrix(renderer
->f_sar
, renderer
->f_fovy
,
91 renderer
->var
.ProjectionMatrix
);
92 getZoomMatrix(renderer
->f_z
, renderer
->var
.ZoomMatrix
);
94 /* renderer->vp has been reversed and is a world transform */
95 vlc_viewpoint_to_4x4(&renderer
->vp
, renderer
->var
.ViewMatrix
);
99 memcpy(renderer
->var
.ProjectionMatrix
, identity
, sizeof(identity
));
100 memcpy(renderer
->var
.ZoomMatrix
, identity
, sizeof(identity
));
101 memcpy(renderer
->var
.ViewMatrix
, identity
, sizeof(identity
));
106 static void getOrientationTransformMatrix(video_orientation_t orientation
,
107 GLfloat matrix
[static 16])
109 memcpy(matrix
, identity
, sizeof(identity
));
111 const int k_cos_pi
= -1;
112 const int k_cos_pi_2
= 0;
113 const int k_cos_n_pi_2
= 0;
115 const int k_sin_pi
= 0;
116 const int k_sin_pi_2
= 1;
117 const int k_sin_n_pi_2
= -1;
119 switch (orientation
) {
121 case ORIENT_ROTATED_90
:
122 matrix
[0 * 4 + 0] = k_cos_pi_2
;
123 matrix
[0 * 4 + 1] = -k_sin_pi_2
;
124 matrix
[1 * 4 + 0] = k_sin_pi_2
;
125 matrix
[1 * 4 + 1] = k_cos_pi_2
;
126 matrix
[3 * 4 + 1] = 1;
128 case ORIENT_ROTATED_180
:
129 matrix
[0 * 4 + 0] = k_cos_pi
;
130 matrix
[0 * 4 + 1] = -k_sin_pi
;
131 matrix
[1 * 4 + 0] = k_sin_pi
;
132 matrix
[1 * 4 + 1] = k_cos_pi
;
133 matrix
[3 * 4 + 0] = 1;
134 matrix
[3 * 4 + 1] = 1;
136 case ORIENT_ROTATED_270
:
137 matrix
[0 * 4 + 0] = k_cos_n_pi_2
;
138 matrix
[0 * 4 + 1] = -k_sin_n_pi_2
;
139 matrix
[1 * 4 + 0] = k_sin_n_pi_2
;
140 matrix
[1 * 4 + 1] = k_cos_n_pi_2
;
141 matrix
[3 * 4 + 0] = 1;
143 case ORIENT_HFLIPPED
:
144 matrix
[0 * 4 + 0] = -1;
145 matrix
[3 * 4 + 0] = 1;
147 case ORIENT_VFLIPPED
:
148 matrix
[1 * 4 + 1] = -1;
149 matrix
[3 * 4 + 1] = 1;
151 case ORIENT_TRANSPOSED
:
152 matrix
[0 * 4 + 0] = 0;
153 matrix
[1 * 4 + 1] = 0;
154 matrix
[2 * 4 + 2] = -1;
155 matrix
[0 * 4 + 1] = 1;
156 matrix
[1 * 4 + 0] = 1;
158 case ORIENT_ANTI_TRANSPOSED
:
159 matrix
[0 * 4 + 0] = 0;
160 matrix
[1 * 4 + 1] = 0;
161 matrix
[2 * 4 + 2] = -1;
162 matrix
[0 * 4 + 1] = -1;
163 matrix
[1 * 4 + 0] = -1;
164 matrix
[3 * 4 + 0] = 1;
165 matrix
[3 * 4 + 1] = 1;
173 InitStereoMatrix(GLfloat matrix_out
[static 3*3],
174 video_multiview_mode_t multiview_mode
)
177 * The stereo matrix transforms 2D pictures coordinates to crop the
178 * content, in order to view only one eye.
180 * This 2D transformation is affine, so the matrix is 3x3 and applies to 3D
181 * vectors in the form (x, y, 1).
183 * Note that since for now, we always crop the left eye, in practice the
184 * offset is always 0, so the transform is actually linear (a 2x2 matrix
185 * would be sufficient).
191 /* Initialize to identity 3x3 */
192 memset(matrix_out
, 0, 3 * 3 * sizeof(float));
193 matrix_out
[COL(0) + ROW(0)] = 1;
194 matrix_out
[COL(1) + ROW(1)] = 1;
195 matrix_out
[COL(2) + ROW(2)] = 1;
197 switch (multiview_mode
)
199 case MULTIVIEW_STEREO_SBS
:
201 * +----------+----------+
206 * +----------+----------+
208 * To crop the coordinates to the left eye, divide the x
215 matrix_out
[COL(0) + ROW(0)] = 0.5;
217 case MULTIVIEW_STEREO_TB
:
231 * To crop the coordinates to the left eye, divide the y
235 * matrix = | 0 0.5 0 |
238 matrix_out
[COL(1) + ROW(1)] = 0.5;
248 BuildVertexShader(const struct vlc_gl_renderer
*renderer
)
250 /* Basic vertex shader */
251 static const char *template =
253 "attribute vec2 PicCoordsIn;\n"
254 "varying vec2 PicCoords;\n"
255 "attribute vec3 VertexPosition;\n"
256 "uniform mat3 StereoMatrix;\n"
257 "uniform mat4 ProjectionMatrix;\n"
258 "uniform mat4 ZoomMatrix;\n"
259 "uniform mat4 ViewMatrix;\n"
261 " PicCoords = (StereoMatrix * vec3(PicCoordsIn, 1.0)).st;\n"
262 " gl_Position = ProjectionMatrix * ZoomMatrix * ViewMatrix\n"
263 " * vec4(VertexPosition, 1.0);\n"
267 if (asprintf(&code
, template, renderer
->glsl_version
) < 0)
270 if (renderer
->b_dump_shaders
)
271 msg_Dbg(renderer
->gl
, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
272 (const char *) &renderer
->interop
->fmt
.i_chroma
, code
);
277 BuildFragmentShader(struct vlc_gl_renderer
*renderer
)
279 struct vlc_gl_interop
*interop
= renderer
->interop
;
281 opengl_fragment_shader_init(renderer
, interop
->tex_target
,
282 interop
->sw_fmt
.i_chroma
,
283 interop
->sw_fmt
.space
);
287 static const char *template =
289 "%s" /* extensions */
290 "%s" /* precision header */
291 "%s" /* vlc_texture definition */
292 "varying vec2 PicCoords;\n"
294 " gl_FragColor = vlc_texture(PicCoords);\n"
297 /* TODO move extensions back to fragment_shaders.c */
298 const char *extensions
= interop
->tex_target
== GL_TEXTURE_EXTERNAL_OES
299 ? "#extension GL_OES_EGL_image_external : require\n"
303 int ret
= asprintf(&code
, template, renderer
->glsl_version
, extensions
,
304 renderer
->glsl_precision_header
, vlc_texture
);
309 if (renderer
->b_dump_shaders
)
310 msg_Dbg(renderer
->gl
, "\n=== Fragment shader for fourcc: %4.4s, colorspace: %d ===\n%s\n",
311 (const char *) &interop
->sw_fmt
.i_chroma
,
312 interop
->sw_fmt
.space
, code
);
318 opengl_link_program(struct vlc_gl_renderer
*renderer
)
320 struct vlc_gl_interop
*interop
= renderer
->interop
;
321 const opengl_vtable_t
*vt
= renderer
->vt
;
323 char *vertex_shader
= BuildVertexShader(renderer
);
327 char *fragment_shader
= BuildFragmentShader(renderer
);
328 if (!fragment_shader
)
334 assert(interop
->tex_target
!= 0 &&
335 interop
->tex_count
> 0 &&
336 interop
->ops
->update_textures
!= NULL
&&
337 renderer
->pf_fetch_locations
!= NULL
&&
338 renderer
->pf_prepare_shader
!= NULL
);
341 vlc_gl_BuildProgram(VLC_OBJECT(renderer
->gl
), vt
,
342 1, (const char **) &vertex_shader
,
343 1, (const char **) &fragment_shader
);
345 free(fragment_shader
);
349 /* Fetch UniformLocations and AttribLocations */
350 #define GET_LOC(type, x, str) do { \
351 x = vt->Get##type##Location(program_id, str); \
354 msg_Err(renderer->gl, "Unable to Get"#type"Location(%s)", str); \
358 #define GET_ULOC(x, str) GET_LOC(Uniform, renderer->uloc.x, str)
359 #define GET_ALOC(x, str) GET_LOC(Attrib, renderer->aloc.x, str)
360 GET_ULOC(TransformMatrix
, "TransformMatrix");
361 GET_ULOC(OrientationMatrix
, "OrientationMatrix");
362 GET_ULOC(StereoMatrix
, "StereoMatrix");
363 GET_ULOC(ProjectionMatrix
, "ProjectionMatrix");
364 GET_ULOC(ViewMatrix
, "ViewMatrix");
365 GET_ULOC(ZoomMatrix
, "ZoomMatrix");
367 GET_ALOC(PicCoordsIn
, "PicCoordsIn");
368 GET_ALOC(VertexPosition
, "VertexPosition");
370 GET_ULOC(TexCoordsMap
[0], "TexCoordsMap0");
371 /* MultiTexCoord 1 and 2 can be optimized out if not used */
372 if (interop
->tex_count
> 1)
373 GET_ULOC(TexCoordsMap
[1], "TexCoordsMap1");
375 renderer
->uloc
.TexCoordsMap
[1] = -1;
376 if (interop
->tex_count
> 2)
377 GET_ULOC(TexCoordsMap
[2], "TexCoordsMap2");
379 renderer
->uloc
.TexCoordsMap
[2] = -1;
383 int ret
= renderer
->pf_fetch_locations(renderer
, program_id
);
384 assert(ret
== VLC_SUCCESS
);
385 if (ret
!= VLC_SUCCESS
)
387 msg_Err(renderer
->gl
, "Unable to get locations from tex_conv");
391 renderer
->program_id
= program_id
;
396 vt
->DeleteProgram(program_id
);
397 renderer
->program_id
= 0;
402 vlc_gl_renderer_Delete(struct vlc_gl_renderer
*renderer
)
404 struct vlc_gl_interop
*interop
= renderer
->interop
;
405 const opengl_vtable_t
*vt
= renderer
->vt
;
407 vt
->DeleteBuffers(1, &renderer
->vertex_buffer_object
);
408 vt
->DeleteBuffers(1, &renderer
->index_buffer_object
);
409 vt
->DeleteBuffers(1, &renderer
->texture_buffer_object
);
411 if (!interop
->handle_texs_gen
)
412 vt
->DeleteTextures(interop
->tex_count
, renderer
->textures
);
414 vlc_gl_interop_Delete(interop
);
415 if (renderer
->program_id
!= 0)
416 vt
->DeleteProgram(renderer
->program_id
);
418 #ifdef HAVE_LIBPLACEBO
419 FREENULL(renderer
->uloc
.pl_vars
);
420 if (renderer
->pl_ctx
)
421 pl_context_destroy(&renderer
->pl_ctx
);
427 static int SetupCoords(struct vlc_gl_renderer
*renderer
);
429 struct vlc_gl_renderer
*
430 vlc_gl_renderer_New(vlc_gl_t
*gl
, const struct vlc_gl_api
*api
,
431 vlc_video_context
*context
, const video_format_t
*fmt
,
434 const opengl_vtable_t
*vt
= &api
->vt
;
436 struct vlc_gl_renderer
*renderer
= calloc(1, sizeof(*renderer
));
440 struct vlc_gl_interop
*interop
=
441 vlc_gl_interop_New(gl
, api
, context
, fmt
, false);
448 renderer
->interop
= interop
;
453 renderer
->b_dump_shaders
= b_dump_shaders
;
454 #if defined(USE_OPENGL_ES2)
455 renderer
->glsl_version
= 100;
456 renderer
->glsl_precision_header
= "precision highp float;\n";
458 renderer
->glsl_version
= 120;
459 renderer
->glsl_precision_header
= "";
462 #ifdef HAVE_LIBPLACEBO
463 // Create the main libplacebo context
464 renderer
->pl_ctx
= vlc_placebo_Create(VLC_OBJECT(gl
));
465 if (renderer
->pl_ctx
) {
466 # if PL_API_VER >= 20
467 renderer
->pl_sh
= pl_shader_alloc(renderer
->pl_ctx
, NULL
);
468 # elif PL_API_VER >= 6
469 renderer
->pl_sh
= pl_shader_alloc(renderer
->pl_ctx
, NULL
, 0);
471 renderer
->pl_sh
= pl_shader_alloc(renderer
->pl_ctx
, NULL
, 0, 0);
476 int ret
= opengl_link_program(renderer
);
477 if (ret
!= VLC_SUCCESS
)
479 vlc_gl_renderer_Delete(renderer
);
483 InitStereoMatrix(renderer
->var
.StereoMatrix
, interop
->fmt
.multiview_mode
);
485 getOrientationTransformMatrix(interop
->fmt
.orientation
,
486 renderer
->var
.OrientationMatrix
);
487 getViewpointMatrixes(renderer
, interop
->fmt
.projection_mode
);
489 /* Update the fmt to main program one */
490 renderer
->fmt
= interop
->fmt
;
491 /* The orientation is handled by the orientation matrix */
492 renderer
->fmt
.orientation
= fmt
->orientation
;
495 for (unsigned j
= 0; j
< interop
->tex_count
; j
++) {
496 const GLsizei w
= renderer
->fmt
.i_visible_width
* interop
->texs
[j
].w
.num
497 / interop
->texs
[j
].w
.den
;
498 const GLsizei h
= renderer
->fmt
.i_visible_height
* interop
->texs
[j
].h
.num
499 / interop
->texs
[j
].h
.den
;
500 if (api
->supports_npot
) {
501 renderer
->tex_width
[j
] = w
;
502 renderer
->tex_height
[j
] = h
;
504 renderer
->tex_width
[j
] = vlc_align_pot(w
);
505 renderer
->tex_height
[j
] = vlc_align_pot(h
);
509 if (!interop
->handle_texs_gen
)
511 ret
= vlc_gl_interop_GenerateTextures(interop
, renderer
->tex_width
,
512 renderer
->tex_height
,
514 if (ret
!= VLC_SUCCESS
)
516 vlc_gl_renderer_Delete(renderer
);
522 vt
->Disable(GL_BLEND
);
523 vt
->Disable(GL_DEPTH_TEST
);
524 vt
->DepthMask(GL_FALSE
);
525 vt
->Enable(GL_CULL_FACE
);
526 vt
->ClearColor(0.0f
, 0.0f
, 0.0f
, 1.0f
);
527 vt
->Clear(GL_COLOR_BUFFER_BIT
);
529 vt
->GenBuffers(1, &renderer
->vertex_buffer_object
);
530 vt
->GenBuffers(1, &renderer
->index_buffer_object
);
531 vt
->GenBuffers(1, &renderer
->texture_buffer_object
);
533 ret
= SetupCoords(renderer
);
534 if (ret
!= VLC_SUCCESS
)
536 vlc_gl_renderer_Delete(renderer
);
543 static void UpdateZ(struct vlc_gl_renderer
*renderer
)
545 /* Do trigonometry to calculate the minimal z value
546 * that will allow us to zoom out without seeing the outside of the
547 * sphere (black borders). */
548 float tan_fovx_2
= tanf(renderer
->f_fovx
/ 2);
549 float tan_fovy_2
= tanf(renderer
->f_fovy
/ 2);
550 float z_min
= - SPHERE_RADIUS
/ sinf(atanf(sqrtf(
551 tan_fovx_2
* tan_fovx_2
+ tan_fovy_2
* tan_fovy_2
)));
553 /* The FOV value above which z is dynamically calculated. */
554 const float z_thresh
= 90.f
;
556 if (renderer
->f_fovx
<= z_thresh
* M_PI
/ 180)
560 float f
= z_min
/ ((FIELD_OF_VIEW_DEGREES_MAX
- z_thresh
) * M_PI
/ 180);
561 renderer
->f_z
= f
* renderer
->f_fovx
- f
* z_thresh
* M_PI
/ 180;
562 if (renderer
->f_z
< z_min
)
563 renderer
->f_z
= z_min
;
567 static void UpdateFOVy(struct vlc_gl_renderer
*renderer
)
569 renderer
->f_fovy
= 2 * atanf(tanf(renderer
->f_fovx
/ 2) / renderer
->f_sar
);
573 vlc_gl_renderer_SetViewpoint(struct vlc_gl_renderer
*renderer
,
574 const vlc_viewpoint_t
*p_vp
)
576 if (p_vp
->fov
> FIELD_OF_VIEW_DEGREES_MAX
577 || p_vp
->fov
< FIELD_OF_VIEW_DEGREES_MIN
)
580 // Convert degree into radian
581 float f_fovx
= p_vp
->fov
* (float)M_PI
/ 180.f
;
583 /* vgl->vp needs to be converted into world transform */
584 vlc_viewpoint_reverse(&renderer
->vp
, p_vp
);
586 if (fabsf(f_fovx
- renderer
->f_fovx
) >= 0.001f
)
588 /* FOVx has changed. */
589 renderer
->f_fovx
= f_fovx
;
590 UpdateFOVy(renderer
);
593 getViewpointMatrixes(renderer
, renderer
->fmt
.projection_mode
);
599 vlc_gl_renderer_SetWindowAspectRatio(struct vlc_gl_renderer
*renderer
,
602 /* Each time the window size changes, we must recompute the minimum zoom
603 * since the aspect ration changes.
604 * We must also set the new current zoom value. */
605 renderer
->f_sar
= f_sar
;
606 UpdateFOVy(renderer
);
608 getViewpointMatrixes(renderer
, renderer
->fmt
.projection_mode
);
611 static int BuildSphere(GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
612 GLushort
**indices
, unsigned *nbIndices
)
614 unsigned nbLatBands
= 128;
615 unsigned nbLonBands
= 128;
617 *nbVertices
= (nbLatBands
+ 1) * (nbLonBands
+ 1);
618 *nbIndices
= nbLatBands
* nbLonBands
* 3 * 2;
620 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
621 if (*vertexCoord
== NULL
)
623 *textureCoord
= vlc_alloc(*nbVertices
* 2, sizeof(GLfloat
));
624 if (*textureCoord
== NULL
)
629 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
630 if (*indices
== NULL
)
637 for (unsigned lat
= 0; lat
<= nbLatBands
; lat
++) {
638 float theta
= lat
* (float) M_PI
/ nbLatBands
;
639 float sinTheta
, cosTheta
;
641 sincosf(theta
, &sinTheta
, &cosTheta
);
643 for (unsigned lon
= 0; lon
<= nbLonBands
; lon
++) {
644 float phi
= lon
* 2 * (float) M_PI
/ nbLonBands
;
645 float sinPhi
, cosPhi
;
647 sincosf(phi
, &sinPhi
, &cosPhi
);
649 float x
= cosPhi
* sinTheta
;
651 float z
= sinPhi
* sinTheta
;
653 unsigned off1
= (lat
* (nbLonBands
+ 1) + lon
) * 3;
654 (*vertexCoord
)[off1
] = SPHERE_RADIUS
* x
;
655 (*vertexCoord
)[off1
+ 1] = SPHERE_RADIUS
* y
;
656 (*vertexCoord
)[off1
+ 2] = SPHERE_RADIUS
* z
;
658 unsigned off2
= (lat
* (nbLonBands
+ 1) + lon
) * 2;
659 float u
= (float)lon
/ nbLonBands
;
660 float v
= (float)lat
/ nbLatBands
;
661 (*textureCoord
)[off2
] = u
;
662 (*textureCoord
)[off2
+ 1] = v
;
666 for (unsigned lat
= 0; lat
< nbLatBands
; lat
++) {
667 for (unsigned lon
= 0; lon
< nbLonBands
; lon
++) {
668 unsigned first
= (lat
* (nbLonBands
+ 1)) + lon
;
669 unsigned second
= first
+ nbLonBands
+ 1;
671 unsigned off
= (lat
* nbLatBands
+ lon
) * 3 * 2;
673 (*indices
)[off
] = first
;
674 (*indices
)[off
+ 1] = second
;
675 (*indices
)[off
+ 2] = first
+ 1;
677 (*indices
)[off
+ 3] = second
;
678 (*indices
)[off
+ 4] = second
+ 1;
679 (*indices
)[off
+ 5] = first
+ 1;
687 static int BuildCube(float padW
, float padH
,
688 GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
689 GLushort
**indices
, unsigned *nbIndices
)
694 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
695 if (*vertexCoord
== NULL
)
697 *textureCoord
= vlc_alloc(*nbVertices
* 2, sizeof(GLfloat
));
698 if (*textureCoord
== NULL
)
703 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
704 if (*indices
== NULL
)
711 static const GLfloat coord
[] = {
712 -1.0, 1.0, -1.0f
, // front
717 -1.0, 1.0, 1.0f
, // back
722 -1.0, 1.0, -1.0f
, // left
727 1.0f
, 1.0, -1.0f
, // right
732 -1.0, -1.0, 1.0f
, // bottom
737 -1.0, 1.0, 1.0f
, // top
743 memcpy(*vertexCoord
, coord
, *nbVertices
* 3 * sizeof(GLfloat
));
745 float col
[] = {0.f
, 1.f
/3, 2.f
/3, 1.f
};
746 float row
[] = {0.f
, 1.f
/2, 1.0};
748 const GLfloat tex
[] = {
749 col
[1] + padW
, row
[1] + padH
, // front
750 col
[1] + padW
, row
[2] - padH
,
751 col
[2] - padW
, row
[1] + padH
,
752 col
[2] - padW
, row
[2] - padH
,
754 col
[3] - padW
, row
[1] + padH
, // back
755 col
[3] - padW
, row
[2] - padH
,
756 col
[2] + padW
, row
[1] + padH
,
757 col
[2] + padW
, row
[2] - padH
,
759 col
[2] - padW
, row
[0] + padH
, // left
760 col
[2] - padW
, row
[1] - padH
,
761 col
[1] + padW
, row
[0] + padH
,
762 col
[1] + padW
, row
[1] - padH
,
764 col
[0] + padW
, row
[0] + padH
, // right
765 col
[0] + padW
, row
[1] - padH
,
766 col
[1] - padW
, row
[0] + padH
,
767 col
[1] - padW
, row
[1] - padH
,
769 col
[0] + padW
, row
[2] - padH
, // bottom
770 col
[0] + padW
, row
[1] + padH
,
771 col
[1] - padW
, row
[2] - padH
,
772 col
[1] - padW
, row
[1] + padH
,
774 col
[2] + padW
, row
[0] + padH
, // top
775 col
[2] + padW
, row
[1] - padH
,
776 col
[3] - padW
, row
[0] + padH
,
777 col
[3] - padW
, row
[1] - padH
,
780 memcpy(*textureCoord
, tex
,
781 *nbVertices
* 2 * sizeof(GLfloat
));
783 const GLushort ind
[] = {
784 0, 1, 2, 2, 1, 3, // front
785 6, 7, 4, 4, 7, 5, // back
786 10, 11, 8, 8, 11, 9, // left
787 12, 13, 14, 14, 13, 15, // right
788 18, 19, 16, 16, 19, 17, // bottom
789 20, 21, 22, 22, 21, 23, // top
792 memcpy(*indices
, ind
, *nbIndices
* sizeof(GLushort
));
797 static int BuildRectangle(GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
798 GLushort
**indices
, unsigned *nbIndices
)
803 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
804 if (*vertexCoord
== NULL
)
806 *textureCoord
= vlc_alloc(*nbVertices
* 2, sizeof(GLfloat
));
807 if (*textureCoord
== NULL
)
812 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
813 if (*indices
== NULL
)
820 static const GLfloat coord
[] = {
827 memcpy(*vertexCoord
, coord
, *nbVertices
* 3 * sizeof(GLfloat
));
829 static const GLfloat tex
[] = {
836 memcpy(*textureCoord
, tex
, *nbVertices
* 2 * sizeof(GLfloat
));
838 const GLushort ind
[] = {
843 memcpy(*indices
, ind
, *nbIndices
* sizeof(GLushort
));
848 static int SetupCoords(struct vlc_gl_renderer
*renderer
)
850 const opengl_vtable_t
*vt
= renderer
->vt
;
852 GLfloat
*vertexCoord
, *textureCoord
;
854 unsigned nbVertices
, nbIndices
;
857 switch (renderer
->fmt
.projection_mode
)
859 case PROJECTION_MODE_RECTANGULAR
:
860 i_ret
= BuildRectangle(&vertexCoord
, &textureCoord
, &nbVertices
,
861 &indices
, &nbIndices
);
863 case PROJECTION_MODE_EQUIRECTANGULAR
:
864 i_ret
= BuildSphere(&vertexCoord
, &textureCoord
, &nbVertices
,
865 &indices
, &nbIndices
);
867 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD
:
868 i_ret
= BuildCube((float)renderer
->fmt
.i_cubemap_padding
/ renderer
->fmt
.i_width
,
869 (float)renderer
->fmt
.i_cubemap_padding
/ renderer
->fmt
.i_height
,
870 &vertexCoord
, &textureCoord
, &nbVertices
,
871 &indices
, &nbIndices
);
874 i_ret
= VLC_EGENERIC
;
878 if (i_ret
!= VLC_SUCCESS
)
881 vt
->BindBuffer(GL_ARRAY_BUFFER
, renderer
->texture_buffer_object
);
882 vt
->BufferData(GL_ARRAY_BUFFER
, nbVertices
* 2 * sizeof(GLfloat
),
883 textureCoord
, GL_STATIC_DRAW
);
885 vt
->BindBuffer(GL_ARRAY_BUFFER
, renderer
->vertex_buffer_object
);
886 vt
->BufferData(GL_ARRAY_BUFFER
, nbVertices
* 3 * sizeof(GLfloat
),
887 vertexCoord
, GL_STATIC_DRAW
);
889 vt
->BindBuffer(GL_ELEMENT_ARRAY_BUFFER
, renderer
->index_buffer_object
);
890 vt
->BufferData(GL_ELEMENT_ARRAY_BUFFER
, nbIndices
* sizeof(GLushort
),
891 indices
, GL_STATIC_DRAW
);
897 renderer
->nb_indices
= nbIndices
;
902 static void DrawWithShaders(struct vlc_gl_renderer
*renderer
)
904 const struct vlc_gl_interop
*interop
= renderer
->interop
;
905 const opengl_vtable_t
*vt
= renderer
->vt
;
906 renderer
->pf_prepare_shader(renderer
, renderer
->tex_width
,
907 renderer
->tex_height
, 1.0f
);
909 for (unsigned j
= 0; j
< interop
->tex_count
; j
++) {
910 assert(renderer
->textures
[j
] != 0);
911 vt
->ActiveTexture(GL_TEXTURE0
+j
);
912 vt
->BindTexture(interop
->tex_target
, renderer
->textures
[j
]);
914 vt
->UniformMatrix3fv(renderer
->uloc
.TexCoordsMap
[j
], 1, GL_FALSE
,
915 renderer
->var
.TexCoordsMap
[j
]);
918 vt
->BindBuffer(GL_ARRAY_BUFFER
, renderer
->texture_buffer_object
);
919 assert(renderer
->aloc
.PicCoordsIn
!= -1);
920 vt
->EnableVertexAttribArray(renderer
->aloc
.PicCoordsIn
);
921 vt
->VertexAttribPointer(renderer
->aloc
.PicCoordsIn
, 2, GL_FLOAT
, 0, 0, 0);
923 vt
->BindBuffer(GL_ARRAY_BUFFER
, renderer
->vertex_buffer_object
);
924 vt
->BindBuffer(GL_ELEMENT_ARRAY_BUFFER
, renderer
->index_buffer_object
);
925 vt
->EnableVertexAttribArray(renderer
->aloc
.VertexPosition
);
926 vt
->VertexAttribPointer(renderer
->aloc
.VertexPosition
, 3, GL_FLOAT
, 0, 0, 0);
928 const GLfloat
*tm
= NULL
;
929 if (interop
->ops
&& interop
->ops
->get_transform_matrix
)
930 tm
= interop
->ops
->get_transform_matrix(interop
);
934 vt
->UniformMatrix4fv(renderer
->uloc
.TransformMatrix
, 1, GL_FALSE
, tm
);
936 vt
->UniformMatrix4fv(renderer
->uloc
.OrientationMatrix
, 1, GL_FALSE
,
937 renderer
->var
.OrientationMatrix
);
938 vt
->UniformMatrix3fv(renderer
->uloc
.StereoMatrix
, 1, GL_FALSE
,
939 renderer
->var
.StereoMatrix
);
940 vt
->UniformMatrix4fv(renderer
->uloc
.ProjectionMatrix
, 1, GL_FALSE
,
941 renderer
->var
.ProjectionMatrix
);
942 vt
->UniformMatrix4fv(renderer
->uloc
.ViewMatrix
, 1, GL_FALSE
,
943 renderer
->var
.ViewMatrix
);
944 vt
->UniformMatrix4fv(renderer
->uloc
.ZoomMatrix
, 1, GL_FALSE
,
945 renderer
->var
.ZoomMatrix
);
947 vt
->DrawElements(GL_TRIANGLES
, renderer
->nb_indices
, GL_UNSIGNED_SHORT
, 0);
951 vlc_gl_renderer_Prepare(struct vlc_gl_renderer
*renderer
, picture_t
*picture
)
953 const struct vlc_gl_interop
*interop
= renderer
->interop
;
954 const video_format_t
*source
= &picture
->format
;
956 if (source
->i_x_offset
!= renderer
->last_source
.i_x_offset
957 || source
->i_y_offset
!= renderer
->last_source
.i_y_offset
958 || source
->i_visible_width
!= renderer
->last_source
.i_visible_width
959 || source
->i_visible_height
!= renderer
->last_source
.i_visible_height
)
961 memset(renderer
->var
.TexCoordsMap
, 0,
962 sizeof(renderer
->var
.TexCoordsMap
));
963 for (unsigned j
= 0; j
< interop
->tex_count
; j
++)
965 float scale_w
= (float)interop
->texs
[j
].w
.num
/ interop
->texs
[j
].w
.den
966 / renderer
->tex_width
[j
];
967 float scale_h
= (float)interop
->texs
[j
].h
.num
/ interop
->texs
[j
].h
.den
968 / renderer
->tex_height
[j
];
970 /* Warning: if NPOT is not supported a larger texture is
971 allocated. This will cause right and bottom coordinates to
972 land on the edge of two texels with the texels to the
973 right/bottom uninitialized by the call to
974 glTexSubImage2D. This might cause a green line to appear on
975 the right/bottom of the display.
976 There are two possible solutions:
977 - Manually mirror the edges of the texture.
978 - Add a "-1" when computing right and bottom, however the
979 last row/column might not be displayed at all.
981 float left
= (source
->i_x_offset
+ 0 ) * scale_w
;
982 float top
= (source
->i_y_offset
+ 0 ) * scale_h
;
983 float right
= (source
->i_x_offset
+ source
->i_visible_width
) * scale_w
;
984 float bottom
= (source
->i_y_offset
+ source
->i_visible_height
) * scale_h
;
987 * This matrix converts from picture coordinates (in range [0; 1])
988 * to textures coordinates where the picture is actually stored
989 * (removing paddings).
991 * texture (in texture coordinates)
992 * +----------------+--- 0.0
994 * | +---------+---|--- top
996 * | +---------+---|--- bottom
999 * +----------------+--- 1.0
1001 * 0.0 left right 1.0 (in texture coordinates)
1004 * - (0.0, 0.0) is mapped to (left, top)
1005 * - (1.0, 1.0) is mapped to (right, bottom)
1007 * This is an affine 2D transformation, so the input coordinates
1008 * are given as a 3D vector in the form (x, y, 1), and the output
1011 * The paddings are l (left), r (right), t (top) and b (bottom).
1014 * matrix = | 0 (b-t) t |
1017 * It is stored in column-major order.
1019 GLfloat
*matrix
= renderer
->var
.TexCoordsMap
[j
];
1020 #define COL(x) (x*3)
1022 matrix
[COL(0) + ROW(0)] = right
- left
;
1023 matrix
[COL(1) + ROW(1)] = bottom
- top
;
1024 matrix
[COL(2) + ROW(0)] = left
;
1025 matrix
[COL(2) + ROW(1)] = top
;
1030 renderer
->last_source
.i_x_offset
= source
->i_x_offset
;
1031 renderer
->last_source
.i_y_offset
= source
->i_y_offset
;
1032 renderer
->last_source
.i_visible_width
= source
->i_visible_width
;
1033 renderer
->last_source
.i_visible_height
= source
->i_visible_height
;
1036 /* Update the texture */
1037 return interop
->ops
->update_textures(interop
, renderer
->textures
,
1038 renderer
->tex_width
,
1039 renderer
->tex_height
, picture
,
1044 vlc_gl_renderer_Draw(struct vlc_gl_renderer
*renderer
)
1046 const opengl_vtable_t
*vt
= renderer
->vt
;
1048 vt
->Clear(GL_COLOR_BUFFER_BIT
);
1050 vt
->UseProgram(renderer
->program_id
);
1052 DrawWithShaders(renderer
);