Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / guicast / vframe3d.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #define GL_GLEXT_PROTOTYPES
23
24 #include "bcpbuffer.h"
25 #include "bcresources.h"
26 #include "bcsignals.h"
27 #include "bcsynchronous.h"
28 #include "bctexture.h"
29 #include "bcwindowbase.h"
30 #include "vframe.h"
31
32 #if defined(HAVE_CONFIG_H)
33 #include "config.h"
34 #endif
35 #ifdef HAVE_GL
36 #include <GL/gl.h>
37 #include <GL/glext.h>
38 #endif
39
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 int VFrame::get_opengl_state()
46 {
47         return opengl_state;
48 }
49
50 void VFrame::set_opengl_state(int value)
51 {
52         opengl_state = value;
53 }
54
55 int VFrame::get_window_id()
56 {
57         return texture ? texture->window_id : -1;
58 }
59
60 int VFrame::get_texture_id()
61 {
62         return texture ? texture->texture_id : -1;
63 }
64
65 int VFrame::get_texture_w()
66 {
67         return texture ? texture->texture_w : 0;
68 }
69
70 int VFrame::get_texture_h()
71 {
72         return texture ? texture->texture_h : 0;
73 }
74
75
76 int VFrame::get_texture_components()
77 {
78         return texture ? texture->texture_components : 0;
79 }
80
81
82
83
84
85
86
87
88
89
90
91 void VFrame::to_texture()
92 {
93 #ifdef HAVE_GL
94
95 // Must be here so user can create textures without copying data by setting
96 // opengl_state to TEXTURE.
97         BC_Texture::new_texture(&texture, get_w(), get_h(), get_color_model());
98
99 // Determine what to do based on state
100         switch(opengl_state) {
101         case VFrame::TEXTURE:
102                 return;
103
104         case VFrame::SCREEN:
105                 if((get_w() % 4) || (get_h() % 4)) {
106                         printf("VFrame::to_texture w=%d h=%d\n", get_w(), get_h());
107                         return;
108                 }
109                 if(pbuffer) {
110                         enable_opengl();
111                         screen_to_texture();
112                 }
113                 opengl_state = VFrame::TEXTURE;
114                 return;
115         }
116
117 //printf("VFrame::to_texture %d\n", texture_id);
118         GLenum type, format;
119         switch(color_model) {
120         case BC_RGB888:
121         case BC_YUV888:
122                 type = GL_UNSIGNED_BYTE;
123                 format = GL_RGB;
124                 break;
125
126         case BC_RGBA8888:
127         case BC_YUVA8888:
128                 type = GL_UNSIGNED_BYTE;
129                 format = GL_RGBA;
130                 break;
131
132         case BC_RGB_FLOAT:
133                 type = GL_FLOAT;
134                 format = GL_RGB;
135                 break;
136
137         case BC_RGBA_FLOAT:
138                 format = GL_RGBA;
139                 type = GL_FLOAT;
140                 break;
141
142         default:
143                 fprintf(stderr,
144                         "VFrame::to_texture: unsupported color model %d.\n",
145                         color_model);
146                 return;
147         }
148
149         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, get_w(), get_h(),
150                 format, type, get_rows()[0]);
151         opengl_state = VFrame::TEXTURE;
152 #endif
153 }
154
155 void VFrame::to_ram()
156 {
157 #ifdef HAVE_GL
158         switch(opengl_state)
159         {
160 // Only pbuffer is supported since this is only called after the
161 // overlay operation onto the pbuffer.
162                 case VFrame::SCREEN:
163                         if(pbuffer)
164                         {
165                                 enable_opengl();
166 //printf("VFrame::to_ram %d %d\n", get_w(), get_h());
167                                 glReadPixels(0,
168                                         0,
169                                         get_w(),
170                                         get_h(),
171                                         GL_RGB,
172                                         GL_UNSIGNED_BYTE,
173                                         get_rows()[0]);
174                                 flip_vert();
175                         }
176                         opengl_state = VFrame::RAM;
177                         return;
178         }
179 #endif
180 }
181
182 void VFrame::create_pbuffer()
183 {
184         if(pbuffer &&
185                 pbuffer->window_id != BC_WindowBase::get_synchronous()->current_window->get_id())
186         {
187                 delete pbuffer;
188                 pbuffer = 0;
189         }
190
191         if((get_w() % 4) || (get_h() % 4))
192         {
193                 printf("VFrame::create_pbuffer w=%d h=%d\n", get_w(), get_h());
194                 return;
195         }
196
197         if(!pbuffer)
198         {
199                 pbuffer = new BC_PBuffer(get_w(), get_h());
200         }
201 }
202
203 void VFrame::enable_opengl()
204 {
205         create_pbuffer();
206         if(pbuffer)
207         {
208                 pbuffer->enable_opengl();
209         }
210 }
211
212 BC_PBuffer* VFrame::get_pbuffer()
213 {
214         return pbuffer;
215 }
216
217
218 void VFrame::screen_to_texture(int x, int y, int w, int h)
219 {
220 #ifdef HAVE_GL
221 // Create texture
222         BC_Texture::new_texture(&texture,
223                 get_w(),
224                 get_h(),
225                 get_color_model());
226
227         if(pbuffer)
228         {
229                 glEnable(GL_TEXTURE_2D);
230
231 // Read canvas into texture, use back texture for DOUBLE_BUFFER
232 #if 1
233 // According to the man page, it must be GL_BACK for the onscreen buffer
234 // and GL_FRONT for a single buffered PBuffer.  In reality it must be
235 // GL_BACK for a single buffered PBuffer if the PBuffer has alpha and using
236 // GL_FRONT captures the onscreen front buffer.
237 // 10/11/2010 is now generating "illegal operation"
238                 glReadBuffer(GL_BACK);
239 #else
240                 glReadBuffer(BC_WindowBase::get_synchronous()->is_pbuffer ?
241                         GL_FRONT : GL_BACK);
242 #endif
243                 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
244                         x >= 0 ? x : 0, y >= 0 ? y : 0,
245                         w >= 0 ? w : get_w(), h >= 0 ? h : get_h());
246         }
247 #endif
248 }
249
250 void VFrame::draw_texture(float in_x1,
251                 float in_y1,
252                 float in_x2,
253                 float in_y2,
254                 float out_x1,
255                 float out_y1,
256                 float out_x2,
257                 float out_y2,
258                 int flip_y)
259 {
260 #ifdef HAVE_GL
261         glBegin(GL_QUADS);
262         glNormal3f(0, 0, 1.0);
263
264         glTexCoord2f(in_x1 / get_texture_w(), in_y1 / get_texture_h());
265         glVertex3f(out_x1, flip_y ? -out_y1 : -out_y2, 0);
266
267         glTexCoord2f(in_x2 / get_texture_w(), in_y1 / get_texture_h());
268         glVertex3f(out_x2, flip_y ? -out_y1 : -out_y2, 0);
269
270         glTexCoord2f(in_x2 / get_texture_w(), in_y2 / get_texture_h());
271         glVertex3f(out_x2, flip_y ? -out_y2 : -out_y1, 0);
272
273         glTexCoord2f(in_x1 / get_texture_w(), in_y2 / get_texture_h());
274         glVertex3f(out_x1, flip_y ? -out_y2 : -out_y1, 0);
275
276
277         glEnd();
278
279 #endif
280 }
281
282 void VFrame::draw_texture(int flip_y)
283 {
284         draw_texture(0,
285                 0,
286                 get_w(),
287                 get_h(),
288                 0,
289                 0,
290                 get_w(),
291                 get_h(),
292                 flip_y);
293 }
294
295
296 void VFrame::bind_texture(int texture_unit)
297 {
298 // Bind the texture
299         if(texture)
300         {
301                 texture->bind(texture_unit);
302         }
303 }
304
305
306
307
308
309
310 void VFrame::init_screen(int w, int h)
311 {
312 #ifdef HAVE_GL
313         glViewport(0, 0, w, h);
314         glMatrixMode(GL_PROJECTION);
315         glLoadIdentity();
316         float near = 1;
317         float far = 100;
318         float frustum_ratio = near / ((near + far)/2);
319         float near_h = (float)h * frustum_ratio;
320         float near_w = (float)w * frustum_ratio;
321         glFrustum(-near_w/2, near_w/2, -near_h/2, near_h/2, near, far);
322         glMatrixMode(GL_MODELVIEW);
323         glLoadIdentity();
324 // Shift down and right so 0,0 is the top left corner
325         glTranslatef(-w/2, h/2, 0.0);
326         glTranslatef(0.0, 0.0, -(far + near) / 2);
327
328         glDisable(GL_DEPTH_TEST);
329         glShadeModel(GL_SMOOTH);
330 // Default for direct copy playback
331         glDisable(GL_BLEND);
332         glDisable(GL_COLOR_MATERIAL);
333         glDisable(GL_CULL_FACE);
334         glEnable(GL_NORMALIZE);
335         glAlphaFunc(GL_ALWAYS, 0);
336         glDisable(GL_ALPHA_TEST);
337         glDisable(GL_LIGHTING);
338
339         const GLfloat zero[] = { 0, 0, 0, 0 };
340 //      const GLfloat one[] = { 1, 1, 1, 1 };
341 //      const GLfloat light_position[] = { 0, 0, -1, 0 };
342 //      const GLfloat light_direction[] = { 0, 0, 1, 0 };
343
344 //      glEnable(GL_LIGHT0);
345 //      glLightfv(GL_LIGHT0, GL_AMBIENT, zero);
346 //      glLightfv(GL_LIGHT0, GL_DIFFUSE, one);
347 //      glLightfv(GL_LIGHT0, GL_SPECULAR, one);
348 //      glLighti(GL_LIGHT0, GL_SPOT_CUTOFF, 180);
349 //      glLightfv(GL_LIGHT0, GL_POSITION, light_position);
350 //      glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_direction);
351 //      glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
352 //      glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zero);
353         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, zero);
354         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, zero);
355         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, zero);
356         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, zero);
357         glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0);
358 #endif
359 }
360
361 void VFrame::init_screen()
362 {
363         init_screen(get_w(), get_h());
364 }
365
366 static int print_error(char *source, unsigned int object, int is_program)
367 {
368 #ifdef HAVE_GL
369     char string[BCTEXTLEN];
370         int len = 0;
371     if(is_program)
372                 glGetProgramInfoLog(object, BCTEXTLEN, &len, string);
373         else
374                 glGetShaderInfoLog(object, BCTEXTLEN, &len, string);
375         if(len > 0) printf("Playback3D::print_error:\n%s\n%s\n", source, string);
376         if(len > 0) return 1;
377         return 0;
378 #endif
379 }
380
381
382
383
384
385 unsigned int VFrame::make_shader(int x, ...)
386 {
387         unsigned int result = 0;
388 #ifdef HAVE_GL
389 // Construct single source file out of arguments
390         char *complete_program = 0;
391         int complete_size = 0;
392         int current_shader = 0;
393
394         va_list list;
395         va_start(list, x);
396
397         while(1)
398         {
399                 char *text = va_arg(list, char*);
400                 if(!text) break;
401
402 // Replace one occurrance in each source of main() with a unique id.
403                 char main_replacement[BCTEXTLEN];
404                 sprintf(main_replacement, "main%03d()", current_shader);
405 //printf("VFrame::make_shader %s %s\n", text, main_replacement);
406                 char *source_replacement = new char[strlen(text) + strlen(main_replacement) + 1];
407                 char *ptr = strstr(text, "main()");
408
409                 if(ptr)
410                 {
411                         memcpy(source_replacement, text, ptr - text);
412                         source_replacement[ptr - text] = 0;
413                         strcat(source_replacement, main_replacement);
414                         ptr += strlen("main()");
415                         strcat(source_replacement, ptr);
416                         current_shader++;
417                 }
418                 else
419                 {
420                         memcpy(source_replacement, text, strlen(text));
421                         source_replacement[strlen(text)] = 0;
422                 }
423
424                 if(!complete_program)
425                 {
426                         complete_size = strlen(source_replacement) + 1;
427                         complete_program = (char*)malloc(complete_size);
428                         strcpy(complete_program, source_replacement);
429                 }
430                 else
431                 {
432                         complete_size += strlen(source_replacement);
433                         complete_program = (char*)realloc(complete_program, complete_size);
434                         strcat(complete_program, source_replacement);
435                 }
436
437                 delete [] source_replacement;
438         }
439         va_end(list);
440
441 // Add main() function which calls all the unique main replacements in order
442         char main_function[BCTEXTLEN];
443         sprintf(main_function,
444                 "\n"
445                 "void main()\n"
446                 "{\n");
447
448         for(int i = 0; i < current_shader; i++)
449         {
450                 char main_replacement[BCTEXTLEN];
451                 sprintf(main_replacement, "\tmain%03d();\n", i);
452                 strcat(main_function, main_replacement);
453         }
454
455         strcat(main_function, "}\n");
456         if(!complete_program)
457         {
458                 complete_size = strlen(main_function) + 1;
459                 complete_program = (char*)malloc(complete_size);
460                 strcpy(complete_program, main_function);
461         }
462         else
463         {
464                 complete_size += strlen(main_function);
465                 complete_program = (char*)realloc(complete_program, complete_size);
466                 strcat(complete_program, main_function);
467         }
468
469
470
471
472
473         int got_it = 0;
474         result = BC_WindowBase::get_synchronous()->get_shader(complete_program,
475                 &got_it);
476
477         if(!got_it)
478         {
479                 result = glCreateProgram();
480
481                 unsigned int shader;
482                 shader = glCreateShader(GL_FRAGMENT_SHADER);
483                 const GLchar *text_ptr = complete_program;
484                 glShaderSource(shader, 1, &text_ptr, NULL);
485                 glCompileShader(shader);
486                 int error = print_error(complete_program, shader, 0);
487                 glAttachShader(result, shader);
488                 glDeleteShader(shader);
489
490                 glLinkProgram(result);
491                 if(!error) error = print_error(complete_program, result, 1);
492
493
494 // printf("BC_WindowBase::make_shader: shader=%d window_id=%d\n",
495 // result,
496 // BC_WindowBase::get_synchronous()->current_window->get_id());
497                 BC_WindowBase::get_synchronous()->put_shader(result, complete_program);
498         }
499
500 //printf("VFrame::make_shader\n%s\n", complete_program);
501         free(complete_program);
502         complete_program = NULL;
503
504 #endif
505         return result;
506 }
507
508 void VFrame::dump_shader(int shader_id)
509 {
510         BC_WindowBase::get_synchronous()->dump_shader(shader_id);
511 }
512
513
514 void VFrame::clear_pbuffer()
515 {
516 #ifdef HAVE_GL
517         if(BC_CModels::is_yuv(get_color_model()))
518                 glClearColor(0.0, 0.5, 0.5, 0.0);
519         else
520                 glClearColor(0.0, 0.0, 0.0, 0.0);
521         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
522 #endif
523 }
524