renderer.c 23.4 KB
Newer Older
Romain Vimont's avatar
Romain Vimont committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*****************************************************************************
 * 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>
 *
 * 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 "gl_util.h"
#include "internal.h"
#include "vout_helper.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)
    {
        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
    {
91
92
93
94
95
96
        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));
Romain Vimont's avatar
Romain Vimont committed
97
98
99
100
    }

}

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
static void
InitStereoMatrix(GLfloat matrix_out[static 3*3],
                 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).
     *
     * Note that since for now, we always crop the left eye, in practice the
     * offset is always 0, so the transform is actually linear (a 2x2 matrix
     * would be sufficient).
     */

117
118
    memcpy(matrix_out, MATRIX3_IDENTITY, sizeof(MATRIX3_IDENTITY));

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#define COL(x) (x*3)
#define ROW(x) (x)

    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;
            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;
            break;
        default:
            break;
    }
#undef COL
#undef ROW
}

172
173
174
175
176
177
178
179
180
181
182
/* https://en.wikipedia.org/wiki/OpenGL_Shading_Language#Versions */
#ifdef USE_OPENGL_ES2
# define SHADER_VERSION "#version 100\n"
  /* In OpenGL ES, the fragment language has no default precision qualifier for
   * floating point types. */
# define FRAGMENT_SHADER_PRECISION "precision highp float;\n"
#else
# define SHADER_VERSION "#version 120\n"
# define FRAGMENT_SHADER_PRECISION
#endif

183
static char *
184
BuildVertexShader(const struct vlc_gl_renderer *renderer)
Romain Vimont's avatar
Romain Vimont committed
185
186
187
{
    /* Basic vertex shader */
    static const char *template =
188
        SHADER_VERSION
189
190
        "attribute vec2 PicCoordsIn;\n"
        "varying vec2 PicCoords;\n"
Romain Vimont's avatar
Romain Vimont committed
191
        "attribute vec3 VertexPosition;\n"
192
        "uniform mat3 StereoMatrix;\n"
Romain Vimont's avatar
Romain Vimont committed
193
194
195
196
        "uniform mat4 ProjectionMatrix;\n"
        "uniform mat4 ZoomMatrix;\n"
        "uniform mat4 ViewMatrix;\n"
        "void main() {\n"
197
        " PicCoords = (StereoMatrix * vec3(PicCoordsIn, 1.0)).st;\n"
Romain Vimont's avatar
Romain Vimont committed
198
199
200
201
        " gl_Position = ProjectionMatrix * ZoomMatrix * ViewMatrix\n"
        "               * vec4(VertexPosition, 1.0);\n"
        "}";

202
203
    char *code = strdup(template);
    if (!code)
204
        return NULL;
Romain Vimont's avatar
Romain Vimont committed
205

206
    if (renderer->dump_shaders)
Romain Vimont's avatar
Romain Vimont committed
207
        msg_Dbg(renderer->gl, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
208
                (const char *) &renderer->sampler->sw_fmt->i_chroma, code);
209
    return code;
Romain Vimont's avatar
Romain Vimont committed
210
211
}

212
213
214
static char *
BuildFragmentShader(struct vlc_gl_renderer *renderer)
{
215
    struct vlc_gl_sampler *sampler = renderer->sampler;
216
217

    static const char *template =
218
        SHADER_VERSION
219
        "%s" /* extensions */
220
        FRAGMENT_SHADER_PRECISION
221
222
223
224
225
226
        "%s" /* vlc_texture definition */
        "varying vec2 PicCoords;\n"
        "void main() {\n"
        " gl_FragColor = vlc_texture(PicCoords);\n"
        "}\n";

227
228
    const char *extensions = sampler->shader.extensions
                           ? sampler->shader.extensions : "";
229
230

    char *code;
231
    int ret = asprintf(&code, template, extensions, sampler->shader.body);
232
233
234
    if (ret < 0)
        return NULL;

235
    if (renderer->dump_shaders)
236
        msg_Dbg(renderer->gl, "\n=== Fragment shader for fourcc: %4.4s, colorspace: %d ===\n%s\n",
237
238
                              (const char *) &sampler->sw_fmt->i_chroma,
                              sampler->fmt->space, code);
239
240
241
242

    return code;
}

Romain Vimont's avatar
Romain Vimont committed
243
244
245
static int
opengl_link_program(struct vlc_gl_renderer *renderer)
{
246
    struct vlc_gl_sampler *sampler = renderer->sampler;
Romain Vimont's avatar
Romain Vimont committed
247
248
    const opengl_vtable_t *vt = renderer->vt;

249
    char *vertex_shader = BuildVertexShader(renderer);
Romain Vimont's avatar
Romain Vimont committed
250
251
252
    if (!vertex_shader)
        return VLC_EGENERIC;

253
    char *fragment_shader = BuildFragmentShader(renderer);
Romain Vimont's avatar
Romain Vimont committed
254
    if (!fragment_shader)
255
256
    {
        free(vertex_shader);
Romain Vimont's avatar
Romain Vimont committed
257
        return VLC_EGENERIC;
258
    }
Romain Vimont's avatar
Romain Vimont committed
259

260
261
    assert(sampler->ops &&
           sampler->ops->fetch_locations &&
262
           sampler->ops->load);
Romain Vimont's avatar
Romain Vimont committed
263

264
265
266
267
268
269
270
271
    GLuint program_id =
        vlc_gl_BuildProgram(VLC_OBJECT(renderer->gl), vt,
                            1, (const char **) &vertex_shader,
                            1, (const char **) &fragment_shader);
    free(vertex_shader);
    free(fragment_shader);
    if (!program_id)
        return VLC_EGENERIC;
Romain Vimont's avatar
Romain Vimont committed
272
273
274
275
276
277
278
279
280
281
282
283

    /* 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(renderer->gl, "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)
284
    GET_ULOC(StereoMatrix, "StereoMatrix");
Romain Vimont's avatar
Romain Vimont committed
285
286
287
288
    GET_ULOC(ProjectionMatrix, "ProjectionMatrix");
    GET_ULOC(ViewMatrix, "ViewMatrix");
    GET_ULOC(ZoomMatrix, "ZoomMatrix");

289
    GET_ALOC(PicCoordsIn, "PicCoordsIn");
Romain Vimont's avatar
Romain Vimont committed
290
291
292
293
    GET_ALOC(VertexPosition, "VertexPosition");
#undef GET_LOC
#undef GET_ULOC
#undef GET_ALOC
294

295
    vlc_gl_sampler_FetchLocations(sampler, program_id);
Romain Vimont's avatar
Romain Vimont committed
296

297
298
    renderer->program_id = program_id;

Romain Vimont's avatar
Romain Vimont committed
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
    return VLC_SUCCESS;

error:
    vt->DeleteProgram(program_id);
    renderer->program_id = 0;
    return VLC_EGENERIC;
}

void
vlc_gl_renderer_Delete(struct vlc_gl_renderer *renderer)
{
    const opengl_vtable_t *vt = renderer->vt;

    vt->DeleteBuffers(1, &renderer->vertex_buffer_object);
    vt->DeleteBuffers(1, &renderer->index_buffer_object);
314
    vt->DeleteBuffers(1, &renderer->texture_buffer_object);
Romain Vimont's avatar
Romain Vimont committed
315
316

    if (renderer->program_id != 0)
317
        vt->DeleteProgram(renderer->program_id);
Romain Vimont's avatar
Romain Vimont committed
318
319
320
321

    free(renderer);
}

322
323
static int SetupCoords(struct vlc_gl_renderer *renderer);

Romain Vimont's avatar
Romain Vimont committed
324
struct vlc_gl_renderer *
325
vlc_gl_renderer_New(vlc_gl_t *gl, const struct vlc_gl_api *api,
326
                    struct vlc_gl_sampler *sampler)
Romain Vimont's avatar
Romain Vimont committed
327
{
328
    const opengl_vtable_t *vt = &api->vt;
329
    const video_format_t *fmt = sampler->fmt;
330

Romain Vimont's avatar
Romain Vimont committed
331
332
333
    struct vlc_gl_renderer *renderer = calloc(1, sizeof(*renderer));
    if (!renderer)
        return NULL;
334
335

    renderer->sampler = sampler;
Romain Vimont's avatar
Romain Vimont committed
336
337

    renderer->gl = gl;
338
    renderer->api = api;
Romain Vimont's avatar
Romain Vimont committed
339
    renderer->vt = vt;
340
    renderer->dump_shaders = var_InheritInteger(gl, "verbose") >= 4;
Romain Vimont's avatar
Romain Vimont committed
341
342
343
344
345
346
347
348

    int ret = opengl_link_program(renderer);
    if (ret != VLC_SUCCESS)
    {
        vlc_gl_renderer_Delete(renderer);
        return NULL;
    }

349
    InitStereoMatrix(renderer->var.StereoMatrix, fmt->multiview_mode);
350

351
    getViewpointMatrixes(renderer, fmt->projection_mode);
Romain Vimont's avatar
Romain Vimont committed
352
353
354
355
356
357
358
359
360
361
362

    /* */
    vt->Disable(GL_BLEND);
    vt->Disable(GL_DEPTH_TEST);
    vt->DepthMask(GL_FALSE);
    vt->Enable(GL_CULL_FACE);
    vt->ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    vt->Clear(GL_COLOR_BUFFER_BIT);

    vt->GenBuffers(1, &renderer->vertex_buffer_object);
    vt->GenBuffers(1, &renderer->index_buffer_object);
363
    vt->GenBuffers(1, &renderer->texture_buffer_object);
Romain Vimont's avatar
Romain Vimont committed
364

365
366
367
368
369
370
371
    ret = SetupCoords(renderer);
    if (ret != VLC_SUCCESS)
    {
        vlc_gl_renderer_Delete(renderer);
        return NULL;
    }

Romain Vimont's avatar
Romain Vimont committed
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
    return 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_EBADVAR;

    // Convert degree into radian
    float f_fovx = p_vp->fov * (float)M_PI / 180.f;

    /* vgl->vp needs to be converted into world transform */
    vlc_viewpoint_reverse(&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);
    }
425
426
    const video_format_t *fmt = renderer->sampler->fmt;
    getViewpointMatrixes(renderer, fmt->projection_mode);
Romain Vimont's avatar
Romain Vimont committed
427
428
429
430
431
432
433
434
435
436
437
438
439
440

    return VLC_SUCCESS;
}

void
vlc_gl_renderer_SetWindowAspectRatio(struct vlc_gl_renderer *renderer,
                                     float f_sar)
{
    /* 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->f_sar = f_sar;
    UpdateFOVy(renderer);
    UpdateZ(renderer);
441
442
443

    const video_format_t *fmt = renderer->sampler->fmt;
    getViewpointMatrixes(renderer, fmt->projection_mode);
Romain Vimont's avatar
Romain Vimont committed
444
445
}

446
447
static int BuildSphere(GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
                       GLushort **indices, unsigned *nbIndices)
Romain Vimont's avatar
Romain Vimont committed
448
449
450
451
452
453
454
455
456
457
{
    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;
458
    *textureCoord = vlc_alloc(*nbVertices * 2, sizeof(GLfloat));
Romain Vimont's avatar
Romain Vimont committed
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
    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 lon = 0; lon <= nbLonBands; lon++) {
            float phi = lon * 2 * (float) M_PI / nbLonBands;
            float sinPhi, cosPhi;

            sincosf(phi, &sinPhi, &cosPhi);

            float x = cosPhi * sinTheta;
            float y = cosTheta;
            float z = sinPhi * sinTheta;

            unsigned off1 = (lat * (nbLonBands + 1) + lon) * 3;
            (*vertexCoord)[off1] = SPHERE_RADIUS * x;
            (*vertexCoord)[off1 + 1] = SPHERE_RADIUS * y;
            (*vertexCoord)[off1 + 2] = SPHERE_RADIUS * z;

493
494
            unsigned off2 = (lat * (nbLonBands + 1) + lon) * 2;
            float u = (float)lon / nbLonBands;
495
496
            /* In OpenGL, the texture coordinates start at bottom left */
            float v = 1.0f - (float)lat / nbLatBands;
497
498
            (*textureCoord)[off2] = u;
            (*textureCoord)[off2 + 1] = v;
Romain Vimont's avatar
Romain Vimont committed
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
        }
    }

    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;
}


523
static int BuildCube(float padW, float padH,
Romain Vimont's avatar
Romain Vimont committed
524
                     GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
525
                     GLushort **indices, unsigned *nbIndices)
Romain Vimont's avatar
Romain Vimont committed
526
527
528
529
530
531
532
{
    *nbVertices = 4 * 6;
    *nbIndices = 6 * 6;

    *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
    if (*vertexCoord == NULL)
        return VLC_ENOMEM;
533
    *textureCoord = vlc_alloc(*nbVertices * 2, sizeof(GLfloat));
Romain Vimont's avatar
Romain Vimont committed
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
    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, // front
        -1.0,    -1.0,   -1.0f,
        1.0,     1.0,    -1.0f,
        1.0,     -1.0,   -1.0f,

        -1.0,    1.0,    1.0f, // back
        -1.0,    -1.0,   1.0f,
        1.0,     1.0,    1.0f,
        1.0,     -1.0,   1.0f,

        -1.0,    1.0,    -1.0f, // left
        -1.0,    -1.0,   -1.0f,
        -1.0,     1.0,    1.0f,
        -1.0,     -1.0,   1.0f,

        1.0f,    1.0,    -1.0f, // right
        1.0f,   -1.0,    -1.0f,
        1.0f,   1.0,     1.0f,
        1.0f,   -1.0,    1.0f,

        -1.0,    -1.0,    1.0f, // bottom
        -1.0,    -1.0,   -1.0f,
        1.0,     -1.0,    1.0f,
        1.0,     -1.0,   -1.0f,

        -1.0,    1.0,    1.0f, // top
        -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));

581
582
583
584
    float col[] = {0.f, 1.f/3, 2.f/3, 1.f};
    float row[] = {0.f, 1.f/2, 1.0};

    const GLfloat tex[] = {
585
        col[1] + padW, row[1] - padH, // front
586
        col[1] + padW, row[0] + padH,
587
588
        col[2] - padW, row[1] - padH,
        col[2] - padW, row[0] + padH,
589

590
591
592
593
594
595
596
597
598
        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,
599

600
        col[0] + padW, row[2] - padH, // right
601
602
603
604
        col[0] + padW, row[1] + padH,
        col[1] - padW, row[2] - padH,
        col[1] - padW, row[1] + padH,

605
606
607
608
609
610
611
612
613
        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,
614
615
616
617
    };

    memcpy(*textureCoord, tex,
           *nbVertices * 2 * sizeof(GLfloat));
Romain Vimont's avatar
Romain Vimont committed
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632

    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;
}

633
634
static int BuildRectangle(GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
                          GLushort **indices, unsigned *nbIndices)
Romain Vimont's avatar
Romain Vimont committed
635
636
637
638
639
640
641
{
    *nbVertices = 4;
    *nbIndices = 6;

    *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
    if (*vertexCoord == NULL)
        return VLC_ENOMEM;
642
    *textureCoord = vlc_alloc(*nbVertices * 2, sizeof(GLfloat));
Romain Vimont's avatar
Romain Vimont committed
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
    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));

665
666
    static const GLfloat tex[] = {
        0.0, 1.0,
667
        0.0, 0.0,
668
        1.0, 1.0,
669
        1.0, 0.0,
670
671
672
    };

    memcpy(*textureCoord, tex, *nbVertices * 2 * sizeof(GLfloat));
Romain Vimont's avatar
Romain Vimont committed
673
674
675
676
677
678
679
680
681
682
683

    const GLushort ind[] = {
        0, 1, 2,
        2, 1, 3
    };

    memcpy(*indices, ind, *nbIndices * sizeof(GLushort));

    return VLC_SUCCESS;
}

684
static int SetupCoords(struct vlc_gl_renderer *renderer)
Romain Vimont's avatar
Romain Vimont committed
685
686
{
    const opengl_vtable_t *vt = renderer->vt;
687
    const video_format_t *fmt = renderer->sampler->fmt;
Romain Vimont's avatar
Romain Vimont committed
688
689
690
691
692
693

    GLfloat *vertexCoord, *textureCoord;
    GLushort *indices;
    unsigned nbVertices, nbIndices;

    int i_ret;
694
    switch (fmt->projection_mode)
Romain Vimont's avatar
Romain Vimont committed
695
696
    {
    case PROJECTION_MODE_RECTANGULAR:
697
698
        i_ret = BuildRectangle(&vertexCoord, &textureCoord, &nbVertices,
                               &indices, &nbIndices);
Romain Vimont's avatar
Romain Vimont committed
699
700
        break;
    case PROJECTION_MODE_EQUIRECTANGULAR:
701
702
        i_ret = BuildSphere(&vertexCoord, &textureCoord, &nbVertices,
                            &indices, &nbIndices);
Romain Vimont's avatar
Romain Vimont committed
703
704
        break;
    case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
705
706
        i_ret = BuildCube((float)fmt->i_cubemap_padding / fmt->i_width,
                          (float)fmt->i_cubemap_padding / fmt->i_height,
Romain Vimont's avatar
Romain Vimont committed
707
                          &vertexCoord, &textureCoord, &nbVertices,
708
                          &indices, &nbIndices);
Romain Vimont's avatar
Romain Vimont committed
709
710
711
712
713
714
715
716
717
        break;
    default:
        i_ret = VLC_EGENERIC;
        break;
    }

    if (i_ret != VLC_SUCCESS)
        return i_ret;

718
719
720
    vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object);
    vt->BufferData(GL_ARRAY_BUFFER, nbVertices * 2 * sizeof(GLfloat),
                   textureCoord, GL_STATIC_DRAW);
Romain Vimont's avatar
Romain Vimont committed
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738

    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;
}

739
740
int
vlc_gl_renderer_Draw(struct vlc_gl_renderer *renderer)
Romain Vimont's avatar
Romain Vimont committed
741
742
{
    const opengl_vtable_t *vt = renderer->vt;
743

744
745
746
747
    vt->Clear(GL_COLOR_BUFFER_BIT);

    vt->UseProgram(renderer->program_id);

748
    vlc_gl_sampler_Load(renderer->sampler);
Romain Vimont's avatar
Romain Vimont committed
749

750
751
752
753
754
    vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object);
    assert(renderer->aloc.PicCoordsIn != -1);
    vt->EnableVertexAttribArray(renderer->aloc.PicCoordsIn);
    vt->VertexAttribPointer(renderer->aloc.PicCoordsIn, 2, GL_FLOAT, 0, 0, 0);

Romain Vimont's avatar
Romain Vimont committed
755
756
757
758
759
    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);

760
761
    vt->UniformMatrix3fv(renderer->uloc.StereoMatrix, 1, GL_FALSE,
                         renderer->var.StereoMatrix);
Romain Vimont's avatar
Romain Vimont committed
762
763
764
765
766
767
768
769
770
771
772
    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->DrawElements(GL_TRIANGLES, renderer->nb_indices, GL_UNSIGNED_SHORT, 0);

    return VLC_SUCCESS;
}