649a3b1997343d9577a0b885ec95da2f153e7adf
[goodguy/cinelerra.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 #ifdef HAVE_GL
33 #include <GL/gl.h>
34 #include <GL/glext.h>
35 #endif
36
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 int VFrame::get_opengl_state()
43 {
44         return opengl_state;
45 }
46
47 void VFrame::set_opengl_state(int value)
48 {
49         opengl_state = value;
50 }
51
52 int VFrame::get_window_id()
53 {
54         return texture ? texture->window_id : -1;
55 }
56
57 int VFrame::get_texture_id()
58 {
59         return texture ? texture->texture_id : -1;
60 }
61
62 int VFrame::get_texture_w()
63 {
64         return texture ? texture->texture_w : 0;
65 }
66
67 int VFrame::get_texture_h()
68 {
69         return texture ? texture->texture_h : 0;
70 }
71
72
73 int VFrame::get_texture_components()
74 {
75         return texture ? texture->texture_components : 0;
76 }
77
78
79
80
81
82
83
84
85
86
87
88 void VFrame::to_texture()
89 {
90 #ifdef HAVE_GL
91
92 // Must be here so user can create textures without copying data by setting
93 // opengl_state to TEXTURE.
94         BC_Texture::new_texture(&texture, get_w(), get_h(), get_color_model());
95
96 // Determine what to do based on state
97         switch(opengl_state) {
98         case VFrame::TEXTURE:
99                 return;
100
101         case VFrame::SCREEN:
102                 if(pbuffer) {
103                         enable_opengl();
104                         screen_to_texture();
105                 }
106                 opengl_state = VFrame::TEXTURE;
107                 return;
108         }
109
110 //printf("VFrame::to_texture %d\n", texture_id);
111         GLenum type, format;
112         switch(color_model) {
113         case BC_RGB888:
114         case BC_YUV888:
115                 type = GL_UNSIGNED_BYTE;
116                 format = GL_RGB;
117                 break;
118
119         case BC_RGBA8888:
120         case BC_YUVA8888:
121                 type = GL_UNSIGNED_BYTE;
122                 format = GL_RGBA;
123                 break;
124
125         case BC_RGB_FLOAT:
126                 type = GL_FLOAT;
127                 format = GL_RGB;
128                 break;
129
130         case BC_RGBA_FLOAT:
131                 format = GL_RGBA;
132                 type = GL_FLOAT;
133                 break;
134
135         default:
136                 fprintf(stderr,
137                         "VFrame::to_texture: unsupported color model %d.\n",
138                         color_model);
139                 return;
140         }
141
142         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, get_w(), get_h(),
143                 format, type, get_rows()[0]);
144         opengl_state = VFrame::TEXTURE;
145 #endif
146 }
147
148 void VFrame::create_pbuffer()
149 {
150         int ww = (get_w()+3) & ~3, hh = (get_h()+3) & ~3;
151         if( pbuffer && (pbuffer->w != ww || pbuffer->h != hh ||
152             pbuffer->window_id != BC_WindowBase::get_synchronous()->current_window->get_id() ) ) {
153                 delete pbuffer;
154                 pbuffer = 0;
155         }
156
157         if( !pbuffer ) {
158                 pbuffer = new BC_PBuffer(ww, hh);
159         }
160 }
161
162 void VFrame::enable_opengl()
163 {
164         create_pbuffer();
165         if( pbuffer ) {
166                 pbuffer->enable_opengl();
167         }
168 }
169
170 BC_PBuffer* VFrame::get_pbuffer()
171 {
172         return pbuffer;
173 }
174
175
176 void VFrame::screen_to_texture(int x, int y, int w, int h)
177 {
178 #ifdef HAVE_GL
179 // Create texture
180         BC_Texture::new_texture(&texture,
181                 get_w(), get_h(), get_color_model());
182
183         if(pbuffer) {
184                 glEnable(GL_TEXTURE_2D);
185
186 // Read canvas into texture, use back texture for DOUBLE_BUFFER
187 #if 1
188 // According to the man page, it must be GL_BACK for the onscreen buffer
189 // and GL_FRONT for a single buffered PBuffer.  In reality it must be
190 // GL_BACK for a single buffered PBuffer if the PBuffer has alpha and using
191 // GL_FRONT captures the onscreen front buffer.
192 // 10/11/2010 is now generating "illegal operation"
193                 glReadBuffer(GL_BACK);
194 #else
195                 glReadBuffer(BC_WindowBase::get_synchronous()->is_pbuffer ?
196                         GL_FRONT : GL_BACK);
197 #endif
198                 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
199                         x >= 0 ? x : 0, y >= 0 ? y : 0,
200                         w >= 0 ? w : get_w(), h >= 0 ? h : get_h());
201         }
202 #endif
203 }
204
205 void VFrame::screen_to_ram()
206 {
207 #ifdef HAVE_GL
208         enable_opengl();
209         glReadBuffer(GL_BACK);
210         int type = BC_CModels::is_float(color_model) ? GL_FLOAT : GL_UNSIGNED_BYTE;
211         int format = BC_CModels::has_alpha(color_model) ? GL_RGBA : GL_RGB;
212         glReadPixels(0, 0, get_w(), get_h(), format, type, get_rows()[0]);
213         opengl_state = VFrame::RAM;
214 #endif
215 }
216
217 void VFrame::draw_texture(
218         float in_x1, float in_y1, float in_x2, float in_y2,
219         float out_x1, float out_y1, float out_x2, float out_y2,
220         int flip_y)
221 {
222 #ifdef HAVE_GL
223         in_x1 /= get_texture_w();  in_y1 /= get_texture_h();
224         in_x2 /= get_texture_w();  in_y2 /= get_texture_h();
225         float ot_y1 = flip_y ? -out_y1 : -out_y2;
226         float ot_y2 = flip_y ? -out_y2 : -out_y1;
227         texture->draw_texture(
228                 in_x1,in_y1,  in_x2,in_y2,
229                 out_x1,ot_y1, out_x2, ot_y2);
230 #endif
231 }
232
233 void VFrame::draw_texture(int flip_y)
234 {
235         draw_texture(0,0,  get_w(),get_h(),
236                 0,0, get_w(),get_h(), flip_y);
237 }
238
239
240 void VFrame::bind_texture(int texture_unit)
241 {
242 // Bind the texture
243         if(texture)
244         {
245                 texture->bind(texture_unit);
246         }
247 }
248
249
250
251
252
253
254 void VFrame::init_screen(int w, int h)
255 {
256 #ifdef HAVE_GL
257         glViewport(0, 0, w, h);
258         glMatrixMode(GL_PROJECTION);
259         glLoadIdentity();
260         float near = 1;
261         float far = 100;
262         float frustum_ratio = near / ((near + far)/2);
263         float near_h = (float)h * frustum_ratio;
264         float near_w = (float)w * frustum_ratio;
265         glFrustum(-near_w/2, near_w/2, -near_h/2, near_h/2, near, far);
266         glMatrixMode(GL_MODELVIEW);
267         glLoadIdentity();
268 // Shift down and right so 0,0 is the top left corner
269         glTranslatef(-(w-1)/2.f, (h-1)/2.f, 0.0);
270         glTranslatef(0.0, 0.0, -(far + near) / 2);
271
272         glDisable(GL_DEPTH_TEST);
273         glShadeModel(GL_SMOOTH);
274 // Default for direct copy playback
275         glDisable(GL_BLEND);
276         glDisable(GL_COLOR_MATERIAL);
277         glDisable(GL_CULL_FACE);
278         glEnable(GL_NORMALIZE);
279         glAlphaFunc(GL_ALWAYS, 0);
280         glDisable(GL_ALPHA_TEST);
281         glDisable(GL_LIGHTING);
282
283         const GLfloat zero[] = { 0, 0, 0, 0 };
284 //      const GLfloat one[] = { 1, 1, 1, 1 };
285 //      const GLfloat light_position[] = { 0, 0, -1, 0 };
286 //      const GLfloat light_direction[] = { 0, 0, 1, 0 };
287
288 //      glEnable(GL_LIGHT0);
289 //      glLightfv(GL_LIGHT0, GL_AMBIENT, zero);
290 //      glLightfv(GL_LIGHT0, GL_DIFFUSE, one);
291 //      glLightfv(GL_LIGHT0, GL_SPECULAR, one);
292 //      glLighti(GL_LIGHT0, GL_SPOT_CUTOFF, 180);
293 //      glLightfv(GL_LIGHT0, GL_POSITION, light_position);
294 //      glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_direction);
295 //      glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
296 //      glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zero);
297         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, zero);
298         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, zero);
299         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, zero);
300         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, zero);
301         glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0);
302 #endif
303 }
304
305 void VFrame::init_screen()
306 {
307         init_screen(get_w(), get_h());
308 }
309
310 static int print_error(char *source, unsigned int object, int is_program)
311 {
312 #ifdef HAVE_GL
313     char string[BCTEXTLEN];
314         int len = 0;
315     if(is_program)
316                 glGetProgramInfoLog(object, BCTEXTLEN, &len, string);
317         else
318                 glGetShaderInfoLog(object, BCTEXTLEN, &len, string);
319         if(len > 0) printf("Playback3D::print_error:\n%s\n%s\n", source, string);
320         if(len > 0) return 1;
321 #endif
322         return 0;
323 }
324
325
326
327 // call as:
328 //    make_shader(0, frag1, .., fragn, 0);
329 // or make_shader(fragments);
330
331 unsigned int VFrame::make_shader(const char **fragments, ...)
332 {
333         unsigned int result = 0;
334 #ifdef HAVE_GL
335 // Construct single source file out of arguments
336         char *program = 0;
337         int nb_mains = 0;
338
339         int nb_frags = 1;
340         if( !fragments ) {
341                 va_list list;  va_start(list, fragments);
342                 while( va_arg(list, char*) != 0 ) ++nb_frags;
343                 va_end(list);
344         }
345         const char *frags[nb_frags], *text = 0;
346         if( !fragments ) {
347                 va_list list;  va_start(list, fragments);
348                 for( int i=0; i<nb_frags; ++i ) frags[i] = va_arg(list, char*);
349                 va_end(list);
350                 fragments = frags;
351         }
352
353         while( (text = *fragments++) ) {
354                 char src[strlen(text) + BCSTRLEN + 1];
355                 const char *tp = strstr(text, "main()");
356                 if( tp ) {
357 // Replace main() with a mainxxx()
358                         char mainxxx[BCSTRLEN], *sp = src;
359                         sprintf(mainxxx, "main%03d()", nb_mains++);
360                         int n = tp - text;
361                         memcpy(sp, text, n);  sp += n;
362                         n = strlen(mainxxx);
363                         memcpy(sp, mainxxx, n);  sp += n;
364                         tp += strlen("main()");
365                         strcpy(sp, tp);
366                         text = src;
367                 }
368
369                 char *new_program = !program ? cstrdup(text) :
370                         cstrcat(2, program, text);
371                 delete [] program;  program = new_program;
372         }
373
374 // Add main() which calls mainxxx() in order
375         char main_program[BCTEXTLEN], *cp = main_program;
376         cp += sprintf(cp, "\nvoid main() {\n");
377         for( int i=0; i < nb_mains; ++i )
378                 cp += sprintf(cp, "\tmain%03d();\n", i);
379         cp += sprintf(cp, "}\n");
380         cp = !program ? cstrdup(main_program) :
381                 cstrcat(2, program, main_program);
382         delete [] program;  program = cp;
383
384         int got_it = 0;
385         result = BC_WindowBase::get_synchronous()->get_shader(program, &got_it);
386         if( !got_it ) {
387                 result = glCreateProgram();
388                 unsigned int shader = glCreateShader(GL_FRAGMENT_SHADER);
389                 const GLchar *text_ptr = program;
390                 glShaderSource(shader, 1, &text_ptr, NULL);
391                 glCompileShader(shader);
392                 int error = print_error(program, shader, 0);
393                 glAttachShader(result, shader);
394                 glDeleteShader(shader);
395                 glLinkProgram(result);
396                 if( !error )
397                         error = print_error(program, result, 1);
398 //printf("BC_WindowBase::make_shader: shader=%d window_id=%d\n", result,
399 // BC_WindowBase::get_synchronous()->current_window->get_id());
400                 BC_WindowBase::get_synchronous()->put_shader(result, program);
401         }
402
403 //printf("VFrame::make_shader\n%s\n", program);
404         delete [] program;
405 #endif
406         return result;
407 }
408
409 void VFrame::dump_shader(int shader_id)
410 {
411         BC_WindowBase::get_synchronous()->dump_shader(shader_id);
412 }
413
414
415 void VFrame::clear_pbuffer()
416 {
417 #ifdef HAVE_GL
418         float gbuv = BC_CModels::is_yuv(get_color_model()) ? 0.5 : 0;
419         glClearColor(0.0, gbuv, gbuv, 0.0);
420         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
421 #endif
422 }
423