4 * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
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.
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.
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
22 #define GL_GLEXT_PROTOTYPES
24 #include "bcpbuffer.h"
25 #include "bcresources.h"
26 #include "bcsignals.h"
27 #include "bcsynchronous.h"
28 #include "bctexture.h"
29 #include "bcwindowbase.h"
42 int VFrame::get_opengl_state()
47 void VFrame::set_opengl_state(int value)
52 int VFrame::get_window_id()
54 return texture ? texture->window_id : -1;
57 int VFrame::get_texture_id()
59 return texture ? texture->texture_id : -1;
62 int VFrame::get_texture_w()
64 return texture ? texture->texture_w : 0;
67 int VFrame::get_texture_h()
69 return texture ? texture->texture_h : 0;
73 int VFrame::get_texture_components()
75 return texture ? texture->texture_components : 0;
79 void VFrame::to_texture()
83 // Must be here so user can create textures without copying data by setting
84 // opengl_state to TEXTURE.
85 BC_Texture::new_texture(&texture, get_w(), get_h(), get_color_model());
87 // Determine what to do based on state
88 switch(opengl_state) {
97 opengl_state = VFrame::TEXTURE;
101 //printf("VFrame::to_texture %d\n", texture_id);
103 switch(color_model) {
106 type = GL_UNSIGNED_BYTE;
112 type = GL_UNSIGNED_BYTE;
128 "VFrame::to_texture: unsupported color model %d.\n",
133 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, get_w(), get_h(),
134 format, type, get_rows()[0]);
135 opengl_state = VFrame::TEXTURE;
139 void VFrame::create_pbuffer()
142 int ww = (get_w()+3) & ~3, hh = (get_h()+3) & ~3;
144 int ww = get_w(), hh = get_h();
146 if( pbuffer && (pbuffer->w != ww || pbuffer->h != hh ||
147 pbuffer->window_id != BC_WindowBase::get_synchronous()->current_window->get_id() ) ) {
153 pbuffer = new BC_PBuffer(ww, hh);
157 void VFrame::enable_opengl()
161 pbuffer->enable_opengl();
165 BC_PBuffer* VFrame::get_pbuffer()
171 void VFrame::screen_to_texture(int x, int y, int w, int h)
175 BC_Texture::new_texture(&texture,
176 get_w(), get_h(), get_color_model());
178 glEnable(GL_TEXTURE_2D);
180 // Read canvas into texture, use back texture for DOUBLE_BUFFER
182 // According to the man page, it must be GL_BACK for the onscreen buffer
183 // and GL_FRONT for a single buffered PBuffer. In reality it must be
184 // GL_BACK for a single buffered PBuffer if the PBuffer has alpha and using
185 // GL_FRONT captures the onscreen front buffer.
186 // 10/11/2010 is now generating "illegal operation"
187 glReadBuffer(GL_BACK);
189 glReadBuffer(BC_WindowBase::get_synchronous()->is_pbuffer ?
192 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
193 x >= 0 ? x : 0, y >= 0 ? y : 0,
194 w >= 0 ? w : get_w(), h >= 0 ? h : get_h());
199 void VFrame::screen_to_ram()
203 glReadBuffer(GL_BACK);
204 int type = BC_CModels::is_float(color_model) ? GL_FLOAT : GL_UNSIGNED_BYTE;
205 int format = BC_CModels::has_alpha(color_model) ? GL_RGBA : GL_RGB;
206 glReadPixels(0, 0, get_w(), get_h(), format, type, get_rows()[0]);
207 opengl_state = VFrame::RAM;
211 void VFrame::draw_texture(
212 float in_x1, float in_y1, float in_x2, float in_y2,
213 float out_x1, float out_y1, float out_x2, float out_y2,
217 in_x1 /= get_texture_w(); in_y1 /= get_texture_h();
218 in_x2 /= get_texture_w(); in_y2 /= get_texture_h();
219 float ot_y1 = flip_y ? -out_y1 : -out_y2;
220 float ot_y2 = flip_y ? -out_y2 : -out_y1;
221 texture->draw_texture(
222 in_x1,in_y1, in_x2,in_y2,
223 out_x1,ot_y1, out_x2, ot_y2);
227 void VFrame::draw_texture(int flip_y)
229 draw_texture(0,0, get_w(),get_h(),
230 0,0, get_w(),get_h(), flip_y);
234 void VFrame::bind_texture(int texture_unit, int nearest)
238 texture->bind(texture_unit, nearest);
243 void VFrame::init_screen(int w, int h)
246 glViewport(0, 0, w, h);
247 glMatrixMode(GL_PROJECTION);
251 float frustum_ratio = near / ((near + far)/2);
252 float near_h = (float)h * frustum_ratio;
253 float near_w = (float)w * frustum_ratio;
254 glFrustum(-near_w/2, near_w/2, -near_h/2, near_h/2, near, far);
255 glMatrixMode(GL_MODELVIEW);
257 // Shift down and right so 0,0 is the top left corner
258 glTranslatef(-w/2.f, h/2.f, 0.f);
259 glTranslatef(0.0, 0.0, -(far + near) / 2);
261 glDisable(GL_DEPTH_TEST);
262 glShadeModel(GL_SMOOTH);
263 // Default for direct copy playback
265 glDisable(GL_COLOR_MATERIAL);
266 glDisable(GL_CULL_FACE);
267 glEnable(GL_NORMALIZE);
268 glAlphaFunc(GL_ALWAYS, 0);
269 glDisable(GL_ALPHA_TEST);
270 glDisable(GL_LIGHTING);
272 const GLfloat zero[] = { 0, 0, 0, 0 };
273 // const GLfloat one[] = { 1, 1, 1, 1 };
274 // const GLfloat light_position[] = { 0, 0, -1, 0 };
275 // const GLfloat light_direction[] = { 0, 0, 1, 0 };
277 // glEnable(GL_LIGHT0);
278 // glLightfv(GL_LIGHT0, GL_AMBIENT, zero);
279 // glLightfv(GL_LIGHT0, GL_DIFFUSE, one);
280 // glLightfv(GL_LIGHT0, GL_SPECULAR, one);
281 // glLighti(GL_LIGHT0, GL_SPOT_CUTOFF, 180);
282 // glLightfv(GL_LIGHT0, GL_POSITION, light_position);
283 // glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_direction);
284 // glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
285 // glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zero);
286 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, zero);
287 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, zero);
288 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, zero);
289 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, zero);
290 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0);
292 glPixelStorei(GL_PACK_ALIGNMENT,1);
293 glPixelStorei(GL_UNPACK_ALIGNMENT,1);
298 void VFrame::init_screen()
300 init_screen(get_w(), get_h());
307 static int print_error(const char *text, unsigned int object, int is_program)
309 char info[BCTEXTLEN];
312 glGetProgramInfoLog(object, BCTEXTLEN, &len, info);
314 glGetShaderInfoLog(object, BCTEXTLEN, &len, info);
315 if( len > 0 ) printf("Playback3D::print_error:\n%s\n%s\n", text, info);
319 static char *shader_segs(const char **segs, int n)
321 if( !segs || !n ) return 0;
322 // concat source segs
325 for( int i=0; i<n; ++i ) {
326 const char *text = *segs++;
327 char src[strlen(text) + BCSTRLEN + 1], *sp = src;
328 const char *tp = strstr(text, "main()");
330 // Replace main() with a mainxxx()
332 memcpy(sp, text, n); sp += n;
333 sp += sprintf(sp, "main%03d()", ids++);
334 strcpy(sp, tp+strlen("main()"));
337 char *cp = !ret ? cstrdup(text) : cstrcat(2, ret, text);
338 delete [] ret; ret = cp;
341 // add main() which calls mainxxx() in order
342 char main_prog[BCTEXTLEN];
343 char *cp = main_prog;
344 cp += sprintf(cp, "\nvoid main() {\n");
345 for( int i=0; i < ids; ++i )
346 cp += sprintf(cp, "\tmain%03d();\n", i);
347 cp += sprintf(cp, "}\n");
349 cp = cstrcat(2, ret, main_prog);
350 delete [] ret; ret = cp;
353 ret = cstrdup(main_prog);
357 static int compile_shader(unsigned int &shader, int type, const GLchar *text)
359 shader = glCreateShader(type);
360 glShaderSource(shader, 1, &text, 0);
361 glCompileShader(shader);
362 return print_error(text, shader, 0);
365 static unsigned int build_shader(const char *vert, const char *frag)
368 unsigned int vertex_shader = 0;
369 unsigned int fragment_shader = 0;
370 unsigned int program = glCreateProgram();
372 error = compile_shader(vertex_shader, GL_VERTEX_SHADER, vert);
374 error = compile_shader(fragment_shader, GL_FRAGMENT_SHADER, frag);
375 if( !error && vert ) glAttachShader(program, vertex_shader);
376 if( !error && frag ) glAttachShader(program, fragment_shader);
377 if( !error ) glLinkProgram(program);
378 if( !error ) error = print_error("link", program, 1);
380 BC_WindowBase::get_synchronous()->put_shader(program, vert, frag);
382 glDeleteProgram(program);
391 // make_shader(0, seg1, .., segn, 0);
392 // or make_shader(&seg);
393 // line 1: optional comment // vertex shader
395 unsigned int VFrame::make_shader(const char **segments, ...)
397 unsigned int program = 0;
399 // Construct single source file out of arguments
401 if( !segments ) { // arg list
402 va_list list; va_start(list, segments);
403 while( va_arg(list, char*) != 0 ) ++nb_segs;
406 else { // segment list
407 while( segments[nb_segs] ) ++nb_segs;
409 const char *segs[++nb_segs];
411 va_list list; va_start(list, segments);
412 for( int i=0; i<nb_segs; ++i )
413 segs[i] = va_arg(list, const char *);
418 const char *vert_shaders[nb_segs]; int vert_segs = 0;
419 const char *frag_shaders[nb_segs]; int frag_segs = 0;
420 for( int i=0; segments[i]!=0; ++i ) {
421 const char *seg = segments[i];
422 if( strstr(seg, "// vertex shader") )
423 vert_shaders[vert_segs++] = seg;
425 frag_shaders[frag_segs++] = seg;
428 char *vert = shader_segs(vert_shaders, vert_segs);
429 char *frag = shader_segs(frag_shaders, frag_segs);
430 if( !BC_WindowBase::get_synchronous()->get_shader(&program, vert, frag) )
431 program = build_shader(vert, frag);
438 void VFrame::dump_shader(int shader_id)
440 BC_WindowBase::get_synchronous()->dump_shader(shader_id);
444 void VFrame::clear_pbuffer()
447 int rgb = clear_color>=0 ? clear_color : 0;
448 int a = clear_color>=0 ? clear_alpha : 0;
449 int r = (rgb>>16) & 0xff;
450 int g = (rgb>> 8) & 0xff;
451 int b = (rgb>> 0) & 0xff;
452 if( BC_CModels::is_yuv(get_color_model()) )
453 YUV::yuv.rgb_to_yuv_8(r, g, b);
454 glClearColor(r/255.f, g/255.f, b/255.f, a/255.f);
455 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);