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 BuildVertexShader(const struct vlc_gl_renderer
*renderer
, unsigned plane_count
)
175 /* Basic vertex shader */
176 static const char *template =
178 "varying vec2 TexCoord0;\n"
179 "attribute vec4 MultiTexCoord0;\n"
181 "attribute vec3 VertexPosition;\n"
182 "uniform mat4 TransformMatrix;\n"
183 "uniform mat4 OrientationMatrix;\n"
184 "uniform mat4 ProjectionMatrix;\n"
185 "uniform mat4 ZoomMatrix;\n"
186 "uniform mat4 ViewMatrix;\n"
188 " TexCoord0 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord0).st;\n"
190 " gl_Position = ProjectionMatrix * ZoomMatrix * ViewMatrix\n"
191 " * vec4(VertexPosition, 1.0);\n"
194 const char *coord1_header
= plane_count
> 1 ?
195 "varying vec2 TexCoord1;\nattribute vec4 MultiTexCoord1;\n" : "";
196 const char *coord1_code
= plane_count
> 1 ?
197 " TexCoord1 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord1).st;\n" : "";
198 const char *coord2_header
= plane_count
> 2 ?
199 "varying vec2 TexCoord2;\nattribute vec4 MultiTexCoord2;\n" : "";
200 const char *coord2_code
= plane_count
> 2 ?
201 " TexCoord2 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord2).st;\n" : "";
204 if (asprintf(&code
, template, renderer
->glsl_version
, coord1_header
,
205 coord2_header
, coord1_code
, coord2_code
) < 0)
208 if (renderer
->b_dump_shaders
)
209 msg_Dbg(renderer
->gl
, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
210 (const char *) &renderer
->interop
->fmt
.i_chroma
, code
);
215 opengl_link_program(struct vlc_gl_renderer
*renderer
)
217 struct vlc_gl_interop
*interop
= renderer
->interop
;
218 const opengl_vtable_t
*vt
= renderer
->vt
;
220 char *vertex_shader
= BuildVertexShader(renderer
, interop
->tex_count
);
224 char *fragment_shader
=
225 opengl_fragment_shader_init(renderer
, interop
->tex_target
,
226 interop
->sw_fmt
.i_chroma
,
227 interop
->sw_fmt
.space
);
228 if (!fragment_shader
)
234 assert(interop
->tex_target
!= 0 &&
235 interop
->tex_count
> 0 &&
236 interop
->ops
->update_textures
!= NULL
&&
237 renderer
->pf_fetch_locations
!= NULL
&&
238 renderer
->pf_prepare_shader
!= NULL
);
241 vlc_gl_BuildProgram(VLC_OBJECT(renderer
->gl
), vt
,
242 1, (const char **) &vertex_shader
,
243 1, (const char **) &fragment_shader
);
245 free(fragment_shader
);
249 /* Fetch UniformLocations and AttribLocations */
250 #define GET_LOC(type, x, str) do { \
251 x = vt->Get##type##Location(program_id, str); \
254 msg_Err(renderer->gl, "Unable to Get"#type"Location(%s)", str); \
258 #define GET_ULOC(x, str) GET_LOC(Uniform, renderer->uloc.x, str)
259 #define GET_ALOC(x, str) GET_LOC(Attrib, renderer->aloc.x, str)
260 GET_ULOC(TransformMatrix
, "TransformMatrix");
261 GET_ULOC(OrientationMatrix
, "OrientationMatrix");
262 GET_ULOC(ProjectionMatrix
, "ProjectionMatrix");
263 GET_ULOC(ViewMatrix
, "ViewMatrix");
264 GET_ULOC(ZoomMatrix
, "ZoomMatrix");
266 GET_ALOC(VertexPosition
, "VertexPosition");
267 GET_ALOC(MultiTexCoord
[0], "MultiTexCoord0");
268 /* MultiTexCoord 1 and 2 can be optimized out if not used */
269 if (interop
->tex_count
> 1)
270 GET_ALOC(MultiTexCoord
[1], "MultiTexCoord1");
272 renderer
->aloc
.MultiTexCoord
[1] = -1;
273 if (interop
->tex_count
> 2)
274 GET_ALOC(MultiTexCoord
[2], "MultiTexCoord2");
276 renderer
->aloc
.MultiTexCoord
[2] = -1;
280 int ret
= renderer
->pf_fetch_locations(renderer
, program_id
);
281 assert(ret
== VLC_SUCCESS
);
282 if (ret
!= VLC_SUCCESS
)
284 msg_Err(renderer
->gl
, "Unable to get locations from tex_conv");
288 renderer
->program_id
= program_id
;
293 vt
->DeleteProgram(program_id
);
294 renderer
->program_id
= 0;
299 vlc_gl_renderer_Delete(struct vlc_gl_renderer
*renderer
)
301 struct vlc_gl_interop
*interop
= renderer
->interop
;
302 const opengl_vtable_t
*vt
= renderer
->vt
;
304 vt
->DeleteBuffers(1, &renderer
->vertex_buffer_object
);
305 vt
->DeleteBuffers(1, &renderer
->index_buffer_object
);
306 vt
->DeleteBuffers(interop
->tex_count
, renderer
->texture_buffer_object
);
308 if (!interop
->handle_texs_gen
)
309 vt
->DeleteTextures(interop
->tex_count
, renderer
->textures
);
311 vlc_gl_interop_Delete(interop
);
312 if (renderer
->program_id
!= 0)
313 renderer
->vt
->DeleteProgram(renderer
->program_id
);
315 #ifdef HAVE_LIBPLACEBO
316 FREENULL(renderer
->uloc
.pl_vars
);
317 if (renderer
->pl_ctx
)
318 pl_context_destroy(&renderer
->pl_ctx
);
324 struct vlc_gl_renderer
*
325 vlc_gl_renderer_New(vlc_gl_t
*gl
, const opengl_vtable_t
*vt
,
326 vlc_video_context
*context
, const video_format_t
*fmt
,
327 bool supports_npot
, bool b_dump_shaders
)
329 struct vlc_gl_renderer
*renderer
= calloc(1, sizeof(*renderer
));
333 struct vlc_gl_interop
*interop
=
334 vlc_gl_interop_New(gl
, vt
, context
, fmt
, false);
341 renderer
->interop
= interop
;
345 renderer
->b_dump_shaders
= b_dump_shaders
;
346 #if defined(USE_OPENGL_ES2)
347 renderer
->glsl_version
= 100;
348 renderer
->glsl_precision_header
= "precision highp float;\n";
350 renderer
->glsl_version
= 120;
351 renderer
->glsl_precision_header
= "";
354 #ifdef HAVE_LIBPLACEBO
355 // Create the main libplacebo context
356 renderer
->pl_ctx
= vlc_placebo_Create(VLC_OBJECT(gl
));
357 if (renderer
->pl_ctx
) {
358 # if PL_API_VER >= 20
359 renderer
->pl_sh
= pl_shader_alloc(renderer
->pl_ctx
, NULL
);
360 # elif PL_API_VER >= 6
361 renderer
->pl_sh
= pl_shader_alloc(renderer
->pl_ctx
, NULL
, 0);
363 renderer
->pl_sh
= pl_shader_alloc(renderer
->pl_ctx
, NULL
, 0, 0);
368 int ret
= opengl_link_program(renderer
);
369 if (ret
!= VLC_SUCCESS
)
371 vlc_gl_renderer_Delete(renderer
);
375 getOrientationTransformMatrix(interop
->fmt
.orientation
,
376 renderer
->var
.OrientationMatrix
);
377 getViewpointMatrixes(renderer
, interop
->fmt
.projection_mode
);
379 /* Update the fmt to main program one */
380 renderer
->fmt
= interop
->fmt
;
381 /* The orientation is handled by the orientation matrix */
382 renderer
->fmt
.orientation
= fmt
->orientation
;
385 for (unsigned j
= 0; j
< interop
->tex_count
; j
++) {
386 const GLsizei w
= renderer
->fmt
.i_visible_width
* interop
->texs
[j
].w
.num
387 / interop
->texs
[j
].w
.den
;
388 const GLsizei h
= renderer
->fmt
.i_visible_height
* interop
->texs
[j
].h
.num
389 / interop
->texs
[j
].h
.den
;
391 renderer
->tex_width
[j
] = w
;
392 renderer
->tex_height
[j
] = h
;
394 renderer
->tex_width
[j
] = vlc_align_pot(w
);
395 renderer
->tex_height
[j
] = vlc_align_pot(h
);
399 if (!interop
->handle_texs_gen
)
401 ret
= vlc_gl_interop_GenerateTextures(interop
, renderer
->tex_width
,
402 renderer
->tex_height
,
404 if (ret
!= VLC_SUCCESS
)
406 vlc_gl_renderer_Delete(renderer
);
412 vt
->Disable(GL_BLEND
);
413 vt
->Disable(GL_DEPTH_TEST
);
414 vt
->DepthMask(GL_FALSE
);
415 vt
->Enable(GL_CULL_FACE
);
416 vt
->ClearColor(0.0f
, 0.0f
, 0.0f
, 1.0f
);
417 vt
->Clear(GL_COLOR_BUFFER_BIT
);
419 vt
->GenBuffers(1, &renderer
->vertex_buffer_object
);
420 vt
->GenBuffers(1, &renderer
->index_buffer_object
);
421 vt
->GenBuffers(interop
->tex_count
, renderer
->texture_buffer_object
);
426 static void UpdateZ(struct vlc_gl_renderer
*renderer
)
428 /* Do trigonometry to calculate the minimal z value
429 * that will allow us to zoom out without seeing the outside of the
430 * sphere (black borders). */
431 float tan_fovx_2
= tanf(renderer
->f_fovx
/ 2);
432 float tan_fovy_2
= tanf(renderer
->f_fovy
/ 2);
433 float z_min
= - SPHERE_RADIUS
/ sinf(atanf(sqrtf(
434 tan_fovx_2
* tan_fovx_2
+ tan_fovy_2
* tan_fovy_2
)));
436 /* The FOV value above which z is dynamically calculated. */
437 const float z_thresh
= 90.f
;
439 if (renderer
->f_fovx
<= z_thresh
* M_PI
/ 180)
443 float f
= z_min
/ ((FIELD_OF_VIEW_DEGREES_MAX
- z_thresh
) * M_PI
/ 180);
444 renderer
->f_z
= f
* renderer
->f_fovx
- f
* z_thresh
* M_PI
/ 180;
445 if (renderer
->f_z
< z_min
)
446 renderer
->f_z
= z_min
;
450 static void UpdateFOVy(struct vlc_gl_renderer
*renderer
)
452 renderer
->f_fovy
= 2 * atanf(tanf(renderer
->f_fovx
/ 2) / renderer
->f_sar
);
456 vlc_gl_renderer_SetViewpoint(struct vlc_gl_renderer
*renderer
,
457 const vlc_viewpoint_t
*p_vp
)
459 if (p_vp
->fov
> FIELD_OF_VIEW_DEGREES_MAX
460 || p_vp
->fov
< FIELD_OF_VIEW_DEGREES_MIN
)
463 // Convert degree into radian
464 float f_fovx
= p_vp
->fov
* (float)M_PI
/ 180.f
;
466 /* vgl->vp needs to be converted into world transform */
467 vlc_viewpoint_reverse(&renderer
->vp
, p_vp
);
469 if (fabsf(f_fovx
- renderer
->f_fovx
) >= 0.001f
)
471 /* FOVx has changed. */
472 renderer
->f_fovx
= f_fovx
;
473 UpdateFOVy(renderer
);
476 getViewpointMatrixes(renderer
, renderer
->fmt
.projection_mode
);
482 vlc_gl_renderer_SetWindowAspectRatio(struct vlc_gl_renderer
*renderer
,
485 /* Each time the window size changes, we must recompute the minimum zoom
486 * since the aspect ration changes.
487 * We must also set the new current zoom value. */
488 renderer
->f_sar
= f_sar
;
489 UpdateFOVy(renderer
);
491 getViewpointMatrixes(renderer
, renderer
->fmt
.projection_mode
);
495 vlc_gl_renderer_Prepare(struct vlc_gl_renderer
*renderer
, picture_t
*picture
)
497 const struct vlc_gl_interop
*interop
= renderer
->interop
;
498 /* Update the texture */
499 return interop
->ops
->update_textures(interop
, renderer
->textures
,
501 renderer
->tex_height
, picture
,
505 static int BuildSphere(unsigned nbPlanes
,
506 GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
507 GLushort
**indices
, unsigned *nbIndices
,
508 const float *left
, const float *top
,
509 const float *right
, const float *bottom
)
511 unsigned nbLatBands
= 128;
512 unsigned nbLonBands
= 128;
514 *nbVertices
= (nbLatBands
+ 1) * (nbLonBands
+ 1);
515 *nbIndices
= nbLatBands
* nbLonBands
* 3 * 2;
517 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
518 if (*vertexCoord
== NULL
)
520 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
521 if (*textureCoord
== NULL
)
526 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
527 if (*indices
== NULL
)
534 for (unsigned lat
= 0; lat
<= nbLatBands
; lat
++) {
535 float theta
= lat
* (float) M_PI
/ nbLatBands
;
536 float sinTheta
, cosTheta
;
538 sincosf(theta
, &sinTheta
, &cosTheta
);
540 for (unsigned lon
= 0; lon
<= nbLonBands
; lon
++) {
541 float phi
= lon
* 2 * (float) M_PI
/ nbLonBands
;
542 float sinPhi
, cosPhi
;
544 sincosf(phi
, &sinPhi
, &cosPhi
);
546 float x
= cosPhi
* sinTheta
;
548 float z
= sinPhi
* sinTheta
;
550 unsigned off1
= (lat
* (nbLonBands
+ 1) + lon
) * 3;
551 (*vertexCoord
)[off1
] = SPHERE_RADIUS
* x
;
552 (*vertexCoord
)[off1
+ 1] = SPHERE_RADIUS
* y
;
553 (*vertexCoord
)[off1
+ 2] = SPHERE_RADIUS
* z
;
555 for (unsigned p
= 0; p
< nbPlanes
; ++p
)
557 unsigned off2
= (p
* (nbLatBands
+ 1) * (nbLonBands
+ 1)
558 + lat
* (nbLonBands
+ 1) + lon
) * 2;
559 float width
= right
[p
] - left
[p
];
560 float height
= bottom
[p
] - top
[p
];
561 float u
= (float)lon
/ nbLonBands
* width
;
562 float v
= (float)lat
/ nbLatBands
* height
;
563 (*textureCoord
)[off2
] = u
;
564 (*textureCoord
)[off2
+ 1] = v
;
569 for (unsigned lat
= 0; lat
< nbLatBands
; lat
++) {
570 for (unsigned lon
= 0; lon
< nbLonBands
; lon
++) {
571 unsigned first
= (lat
* (nbLonBands
+ 1)) + lon
;
572 unsigned second
= first
+ nbLonBands
+ 1;
574 unsigned off
= (lat
* nbLatBands
+ lon
) * 3 * 2;
576 (*indices
)[off
] = first
;
577 (*indices
)[off
+ 1] = second
;
578 (*indices
)[off
+ 2] = first
+ 1;
580 (*indices
)[off
+ 3] = second
;
581 (*indices
)[off
+ 4] = second
+ 1;
582 (*indices
)[off
+ 5] = first
+ 1;
590 static int BuildCube(unsigned nbPlanes
,
591 float padW
, float padH
,
592 GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
593 GLushort
**indices
, unsigned *nbIndices
,
594 const float *left
, const float *top
,
595 const float *right
, const float *bottom
)
600 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
601 if (*vertexCoord
== NULL
)
603 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
604 if (*textureCoord
== NULL
)
609 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
610 if (*indices
== NULL
)
617 static const GLfloat coord
[] = {
618 -1.0, 1.0, -1.0f
, // front
623 -1.0, 1.0, 1.0f
, // back
628 -1.0, 1.0, -1.0f
, // left
633 1.0f
, 1.0, -1.0f
, // right
638 -1.0, -1.0, 1.0f
, // bottom
643 -1.0, 1.0, 1.0f
, // top
649 memcpy(*vertexCoord
, coord
, *nbVertices
* 3 * sizeof(GLfloat
));
651 for (unsigned p
= 0; p
< nbPlanes
; ++p
)
653 float width
= right
[p
] - left
[p
];
654 float height
= bottom
[p
] - top
[p
];
656 float col
[] = {left
[p
],
657 left
[p
] + width
* 1.f
/3,
658 left
[p
] + width
* 2.f
/3,
661 float row
[] = {top
[p
],
662 top
[p
] + height
* 1.f
/2,
665 const GLfloat tex
[] = {
666 col
[1] + padW
, row
[1] + padH
, // front
667 col
[1] + padW
, row
[2] - padH
,
668 col
[2] - padW
, row
[1] + padH
,
669 col
[2] - padW
, row
[2] - padH
,
671 col
[3] - padW
, row
[1] + padH
, // back
672 col
[3] - padW
, row
[2] - padH
,
673 col
[2] + padW
, row
[1] + padH
,
674 col
[2] + padW
, row
[2] - padH
,
676 col
[2] - padW
, row
[0] + padH
, // left
677 col
[2] - padW
, row
[1] - padH
,
678 col
[1] + padW
, row
[0] + padH
,
679 col
[1] + padW
, row
[1] - padH
,
681 col
[0] + padW
, row
[0] + padH
, // right
682 col
[0] + padW
, row
[1] - padH
,
683 col
[1] - padW
, row
[0] + padH
,
684 col
[1] - padW
, row
[1] - padH
,
686 col
[0] + padW
, row
[2] - padH
, // bottom
687 col
[0] + padW
, row
[1] + padH
,
688 col
[1] - padW
, row
[2] - padH
,
689 col
[1] - padW
, row
[1] + padH
,
691 col
[2] + padW
, row
[0] + padH
, // top
692 col
[2] + padW
, row
[1] - padH
,
693 col
[3] - padW
, row
[0] + padH
,
694 col
[3] - padW
, row
[1] - padH
,
697 memcpy(*textureCoord
+ p
* *nbVertices
* 2, tex
,
698 *nbVertices
* 2 * sizeof(GLfloat
));
701 const GLushort ind
[] = {
702 0, 1, 2, 2, 1, 3, // front
703 6, 7, 4, 4, 7, 5, // back
704 10, 11, 8, 8, 11, 9, // left
705 12, 13, 14, 14, 13, 15, // right
706 18, 19, 16, 16, 19, 17, // bottom
707 20, 21, 22, 22, 21, 23, // top
710 memcpy(*indices
, ind
, *nbIndices
* sizeof(GLushort
));
715 static int BuildRectangle(unsigned nbPlanes
,
716 GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
717 GLushort
**indices
, unsigned *nbIndices
,
718 const float *left
, const float *top
,
719 const float *right
, const float *bottom
)
724 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
725 if (*vertexCoord
== NULL
)
727 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
728 if (*textureCoord
== NULL
)
733 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
734 if (*indices
== NULL
)
741 static const GLfloat coord
[] = {
748 memcpy(*vertexCoord
, coord
, *nbVertices
* 3 * sizeof(GLfloat
));
750 for (unsigned p
= 0; p
< nbPlanes
; ++p
)
752 const GLfloat tex
[] = {
759 memcpy(*textureCoord
+ p
* *nbVertices
* 2, tex
,
760 *nbVertices
* 2 * sizeof(GLfloat
));
763 const GLushort ind
[] = {
768 memcpy(*indices
, ind
, *nbIndices
* sizeof(GLushort
));
773 static int SetupCoords(struct vlc_gl_renderer
*renderer
,
774 const float *left
, const float *top
,
775 const float *right
, const float *bottom
)
777 const struct vlc_gl_interop
*interop
= renderer
->interop
;
778 const opengl_vtable_t
*vt
= renderer
->vt
;
780 GLfloat
*vertexCoord
, *textureCoord
;
782 unsigned nbVertices
, nbIndices
;
785 switch (renderer
->fmt
.projection_mode
)
787 case PROJECTION_MODE_RECTANGULAR
:
788 i_ret
= BuildRectangle(interop
->tex_count
,
789 &vertexCoord
, &textureCoord
, &nbVertices
,
790 &indices
, &nbIndices
,
791 left
, top
, right
, bottom
);
793 case PROJECTION_MODE_EQUIRECTANGULAR
:
794 i_ret
= BuildSphere(interop
->tex_count
,
795 &vertexCoord
, &textureCoord
, &nbVertices
,
796 &indices
, &nbIndices
,
797 left
, top
, right
, bottom
);
799 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD
:
800 i_ret
= BuildCube(interop
->tex_count
,
801 (float)renderer
->fmt
.i_cubemap_padding
/ renderer
->fmt
.i_width
,
802 (float)renderer
->fmt
.i_cubemap_padding
/ renderer
->fmt
.i_height
,
803 &vertexCoord
, &textureCoord
, &nbVertices
,
804 &indices
, &nbIndices
,
805 left
, top
, right
, bottom
);
808 i_ret
= VLC_EGENERIC
;
812 if (i_ret
!= VLC_SUCCESS
)
815 for (unsigned j
= 0; j
< interop
->tex_count
; j
++)
817 vt
->BindBuffer(GL_ARRAY_BUFFER
, renderer
->texture_buffer_object
[j
]);
818 vt
->BufferData(GL_ARRAY_BUFFER
, nbVertices
* 2 * sizeof(GLfloat
),
819 textureCoord
+ j
* nbVertices
* 2, GL_STATIC_DRAW
);
822 vt
->BindBuffer(GL_ARRAY_BUFFER
, renderer
->vertex_buffer_object
);
823 vt
->BufferData(GL_ARRAY_BUFFER
, nbVertices
* 3 * sizeof(GLfloat
),
824 vertexCoord
, GL_STATIC_DRAW
);
826 vt
->BindBuffer(GL_ELEMENT_ARRAY_BUFFER
, renderer
->index_buffer_object
);
827 vt
->BufferData(GL_ELEMENT_ARRAY_BUFFER
, nbIndices
* sizeof(GLushort
),
828 indices
, GL_STATIC_DRAW
);
834 renderer
->nb_indices
= nbIndices
;
839 static void DrawWithShaders(struct vlc_gl_renderer
*renderer
)
841 const struct vlc_gl_interop
*interop
= renderer
->interop
;
842 const opengl_vtable_t
*vt
= renderer
->vt
;
843 renderer
->pf_prepare_shader(renderer
, renderer
->tex_width
,
844 renderer
->tex_height
, 1.0f
);
846 for (unsigned j
= 0; j
< interop
->tex_count
; j
++) {
847 assert(renderer
->textures
[j
] != 0);
848 vt
->ActiveTexture(GL_TEXTURE0
+j
);
849 vt
->BindTexture(interop
->tex_target
, renderer
->textures
[j
]);
851 vt
->BindBuffer(GL_ARRAY_BUFFER
, renderer
->texture_buffer_object
[j
]);
853 assert(renderer
->aloc
.MultiTexCoord
[j
] != -1);
854 vt
->EnableVertexAttribArray(renderer
->aloc
.MultiTexCoord
[j
]);
855 vt
->VertexAttribPointer(renderer
->aloc
.MultiTexCoord
[j
], 2,
859 vt
->BindBuffer(GL_ARRAY_BUFFER
, renderer
->vertex_buffer_object
);
860 vt
->BindBuffer(GL_ELEMENT_ARRAY_BUFFER
, renderer
->index_buffer_object
);
861 vt
->EnableVertexAttribArray(renderer
->aloc
.VertexPosition
);
862 vt
->VertexAttribPointer(renderer
->aloc
.VertexPosition
, 3, GL_FLOAT
, 0, 0, 0);
864 const GLfloat
*tm
= NULL
;
865 if (interop
->ops
&& interop
->ops
->get_transform_matrix
)
866 tm
= interop
->ops
->get_transform_matrix(interop
);
870 vt
->UniformMatrix4fv(renderer
->uloc
.TransformMatrix
, 1, GL_FALSE
, tm
);
872 vt
->UniformMatrix4fv(renderer
->uloc
.OrientationMatrix
, 1, GL_FALSE
,
873 renderer
->var
.OrientationMatrix
);
874 vt
->UniformMatrix4fv(renderer
->uloc
.ProjectionMatrix
, 1, GL_FALSE
,
875 renderer
->var
.ProjectionMatrix
);
876 vt
->UniformMatrix4fv(renderer
->uloc
.ViewMatrix
, 1, GL_FALSE
,
877 renderer
->var
.ViewMatrix
);
878 vt
->UniformMatrix4fv(renderer
->uloc
.ZoomMatrix
, 1, GL_FALSE
,
879 renderer
->var
.ZoomMatrix
);
881 vt
->DrawElements(GL_TRIANGLES
, renderer
->nb_indices
, GL_UNSIGNED_SHORT
, 0);
885 static void GetTextureCropParamsForStereo(unsigned i_nbTextures
,
886 const float *stereoCoefs
,
887 const float *stereoOffsets
,
888 float *left
, float *top
,
889 float *right
, float *bottom
)
891 for (unsigned i
= 0; i
< i_nbTextures
; ++i
)
893 float f_2eyesWidth
= right
[i
] - left
[i
];
894 left
[i
] = left
[i
] + f_2eyesWidth
* stereoOffsets
[0];
895 right
[i
] = left
[i
] + f_2eyesWidth
* stereoCoefs
[0];
897 float f_2eyesHeight
= bottom
[i
] - top
[i
];
898 top
[i
] = top
[i
] + f_2eyesHeight
* stereoOffsets
[1];
899 bottom
[i
] = top
[i
] + f_2eyesHeight
* stereoCoefs
[1];
903 static void TextureCropForStereo(struct vlc_gl_renderer
*renderer
,
904 float *left
, float *top
,
905 float *right
, float *bottom
)
907 const struct vlc_gl_interop
*interop
= renderer
->interop
;
909 float stereoCoefs
[2];
910 float stereoOffsets
[2];
912 switch (renderer
->fmt
.multiview_mode
)
914 case MULTIVIEW_STEREO_TB
:
915 // Display only the left eye.
916 stereoCoefs
[0] = 1; stereoCoefs
[1] = 0.5;
917 stereoOffsets
[0] = 0; stereoOffsets
[1] = 0;
918 GetTextureCropParamsForStereo(interop
->tex_count
,
919 stereoCoefs
, stereoOffsets
,
920 left
, top
, right
, bottom
);
922 case MULTIVIEW_STEREO_SBS
:
923 // Display only the left eye.
924 stereoCoefs
[0] = 0.5; stereoCoefs
[1] = 1;
925 stereoOffsets
[0] = 0; stereoOffsets
[1] = 0;
926 GetTextureCropParamsForStereo(interop
->tex_count
,
927 stereoCoefs
, stereoOffsets
,
928 left
, top
, right
, bottom
);
936 vlc_gl_renderer_Draw(struct vlc_gl_renderer
*renderer
,
937 const video_format_t
*source
)
939 const opengl_vtable_t
*vt
= renderer
->vt
;
941 vt
->Clear(GL_COLOR_BUFFER_BIT
);
943 vt
->UseProgram(renderer
->program_id
);
945 if (source
->i_x_offset
!= renderer
->last_source
.i_x_offset
946 || source
->i_y_offset
!= renderer
->last_source
.i_y_offset
947 || source
->i_visible_width
!= renderer
->last_source
.i_visible_width
948 || source
->i_visible_height
!= renderer
->last_source
.i_visible_height
)
950 float left
[PICTURE_PLANE_MAX
];
951 float top
[PICTURE_PLANE_MAX
];
952 float right
[PICTURE_PLANE_MAX
];
953 float bottom
[PICTURE_PLANE_MAX
];
954 const struct vlc_gl_interop
*interop
= renderer
->interop
;
955 for (unsigned j
= 0; j
< interop
->tex_count
; j
++)
957 float scale_w
= (float)interop
->texs
[j
].w
.num
/ interop
->texs
[j
].w
.den
958 / renderer
->tex_width
[j
];
959 float scale_h
= (float)interop
->texs
[j
].h
.num
/ interop
->texs
[j
].h
.den
960 / renderer
->tex_height
[j
];
962 /* Warning: if NPOT is not supported a larger texture is
963 allocated. This will cause right and bottom coordinates to
964 land on the edge of two texels with the texels to the
965 right/bottom uninitialized by the call to
966 glTexSubImage2D. This might cause a green line to appear on
967 the right/bottom of the display.
968 There are two possible solutions:
969 - Manually mirror the edges of the texture.
970 - Add a "-1" when computing right and bottom, however the
971 last row/column might not be displayed at all.
973 left
[j
] = (source
->i_x_offset
+ 0 ) * scale_w
;
974 top
[j
] = (source
->i_y_offset
+ 0 ) * scale_h
;
975 right
[j
] = (source
->i_x_offset
+ source
->i_visible_width
) * scale_w
;
976 bottom
[j
] = (source
->i_y_offset
+ source
->i_visible_height
) * scale_h
;
979 TextureCropForStereo(renderer
, left
, top
, right
, bottom
);
980 int ret
= SetupCoords(renderer
, left
, top
, right
, bottom
);
981 if (ret
!= VLC_SUCCESS
)
984 renderer
->last_source
.i_x_offset
= source
->i_x_offset
;
985 renderer
->last_source
.i_y_offset
= source
->i_y_offset
;
986 renderer
->last_source
.i_visible_width
= source
->i_visible_width
;
987 renderer
->last_source
.i_visible_height
= source
->i_visible_height
;
989 DrawWithShaders(renderer
);