3 * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
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 3 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, see <http://www.gnu.org/licenses/>.
26 #include "../libsdl/print.h"
32 static uint8_t *image_rgb = NULL;
33 static uint8_t *osd_rgba[MAX_OSD] = { NULL, NULL };
34 static GLuint image_name;
35 static GLuint osd_name[MAX_OSD];
36 static int image_width, image_height;
37 static int osd_width[MAX_OSD], osd_height[MAX_OSD];
38 static int texture_size;
39 static int osd_size[MAX_OSD];
41 static GLuint shadow_framebuffer[2] = { 0, 0 };
42 static int current_view_width = 0, current_view_height = 0;
43 static GLuint shadow_texture[2];
44 static int shadow_texture_width = 0, shadow_texture_height = 0;
45 static int shadow_multisampling;
47 GLuint current_framebuffer = 0;
50 /* alloc and init an image texture with size that is greater than the power of two */
51 int init_opengl_image(int _image_width, int _image_height)
55 image_width = _image_width;
56 image_height = _image_height;
58 /* generate texture */
59 for (texture_size = 1; texture_size <= image_width || texture_size <= image_height; texture_size *= 2)
62 image_rgb = calloc(texture_size * texture_size, 3);
64 print_error("Failed to allocate texture\n");
68 glShadeModel(GL_FLAT);
69 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* bytes */
70 glGenTextures(1, &image_name);
71 glBindTexture(GL_TEXTURE_2D, image_name);
72 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
73 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
74 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_size, texture_size, 0, GL_RGB, GL_UNSIGNED_BYTE, image_rgb);
84 /* alloc and init an osd texture with size that is greater than the power of two */
85 int init_opengl_osd(int num, int _osd_width, int _osd_height)
89 if (num < 0 || num >= MAX_OSD) {
90 print_error("given OSD number out of range");
95 osd_width[num] = _osd_width;
96 osd_height[num] = _osd_height;
98 /* generate texture */
99 for (osd_size[num] = 1; osd_size[num] <= osd_width[num] || osd_size[num] <= osd_height[num]; osd_size[num] *= 2)
101 osd_rgba[num] = calloc(osd_size[num] * osd_size[num], 4);
102 if (!osd_rgba[num]) {
103 print_error("Failed to allocate texture\n");
107 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* bytes */
108 glGenTextures(1, &osd_name[num]);
109 glBindTexture(GL_TEXTURE_2D, osd_name[num]);
110 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
111 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
112 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, osd_size[num], osd_size[num], 0, GL_RGBA, GL_UNSIGNED_BYTE, osd_rgba[num]);
121 void opengl_clear(int _flip_y)
126 glClearColor(0.05, 0.05, 0.05, 1.0);
127 glClear(GL_COLOR_BUFFER_BIT);
130 /* only set view width and heigt. (used by ovr to tell frame buffer size) */
131 void opengl_set_size(int view_width, int view_height)
133 current_view_width = view_width;
134 current_view_height = view_height;
137 /* set viewport for improved rendering */
138 void opengl_viewport(int view_width, int view_height, int split, int benson_at_line, double fov, double benson_size)
140 int view_x = 0, view_y = 0;
141 double factor_height = 1.0;
142 double factor_width = 1.0;
144 /* center view, or put it in the top half of the window */
146 view_y = view_height / 2;
147 view_height = view_height / 2;
148 } else if (split == 2) {
149 view_height = view_height / 2;
152 /* avoid division by zero, if one dimension is too small */
153 if (view_width < 1 || view_height < 1)
156 /* expand width or height, if apect of image_width:image_height does not match the view_width:view_height */
157 if (view_height * image_width > view_width * image_height)
158 factor_height = (double)(view_height * image_width) / (double)(view_width * image_height);
159 else if (view_height * image_width < view_width * image_height)
160 factor_width = (double)(view_width * image_height) / (double)(view_height * image_width);
162 current_view_width = view_width;
163 current_view_height = view_height;
165 /* avoid views that are too small */
166 if (view_width < 1 || view_height < 1)
169 /* projection matrix */
170 glViewport((GLsizei)view_x, (GLsizei)view_y, (GLsizei)view_width, (GLsizei)view_height);
171 glMatrixMode(GL_PROJECTION);
174 /* calculate field-of-view */
175 double slope = tan(fov / 360 * M_PI);
176 /* make frustum to center the view in the game view above benson */
177 double left = -slope;
178 double right = slope;
179 double benson_start_at_position = ((double)image_height - (double)benson_at_line) * benson_size;
180 double top = slope * ((double)image_height - benson_start_at_position) / (double)image_width;
181 double bottom = -slope * ((double)image_height * 2.0 - ((double)image_height - benson_start_at_position)) / (double)image_width;
182 glFrustum(left * factor_width, right * factor_width, bottom * factor_height, top * factor_height, 1.0, 5000000000.0);
184 glMatrixMode(GL_MODELVIEW);
187 /* render image or only benson */
188 void opengl_blit_image(uint8_t *rgb, int filter, int benson_at_line, int render_benson_only, double fov, double monitor_distance, double benson_size, int benson_case)
190 double texture_left = 0;
191 double texture_right = ((double)image_width) / (double)texture_size;
192 double texture_top = ((double)benson_at_line) / (double)texture_size;
193 double texture_bottom = ((double)image_height) / (double)texture_size;
194 double image_start_at_position = ((double)image_height - (double)benson_at_line) * benson_size;
195 double image_top = -((double)image_height - image_start_at_position) / (double)image_width;
196 double image_bottom = -((double)image_height * 2.0 - ((double)image_height - image_start_at_position)) / (double)image_width;
198 /* calculate field-of-view */
199 double slope = tan(fov / 360 * M_PI) * monitor_distance;
204 glEnable(GL_CULL_FACE);
209 glEnable(GL_TEXTURE_2D);
210 glBindTexture(GL_TEXTURE_2D, image_name);
211 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); /* no modulation with color */
212 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
213 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
214 if (render_benson_only) {
215 /* render benson only, and copy top line of benson to one line above, so the texture filter will not take false data above benson */
216 rgb += image_width * (benson_at_line - 1) * 3;
217 memcpy(rgb, rgb + image_width * 3, image_width * 3);
218 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, benson_at_line - 1, image_width, image_height - benson_at_line - 1, GL_RGB, GL_UNSIGNED_BYTE, rgb);
220 image_top = -image_top;
222 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_width, image_height, GL_RGB, GL_UNSIGNED_BYTE, rgb);
225 glTexCoord2f(texture_left, texture_bottom);
226 glVertex3f(-benson_size * slope, image_bottom * slope, -monitor_distance);
227 glTexCoord2f(texture_right, texture_bottom);
228 glVertex3f(benson_size * slope, image_bottom * slope, -monitor_distance);
229 glTexCoord2f(texture_right, texture_top);
230 glVertex3f(benson_size * slope, image_top * slope, -monitor_distance);
231 glTexCoord2f(texture_left, texture_top);
232 glVertex3f(-benson_size * slope, image_top * slope, -monitor_distance);
234 glDisable(GL_TEXTURE_2D);
235 /* render benson case */
237 double left, right, bottom, top, near, far;
238 left = -benson_size * slope;
239 right = benson_size * slope;
240 bottom = image_bottom * slope;
241 top = image_top * slope;
242 near = -monitor_distance;
243 far = near - benson_size * slope / 3.0;
246 glColor4f(0.3, 0.3, 0.3, 1.0);
247 glVertex3f(left, top, far);
248 glVertex3f(right, top, far);
249 glVertex3f(right, bottom, far);
250 glVertex3f(left, bottom, far);
252 glColor4f(0.2, 0.2, 0.2, 1.0);
253 glVertex3f(left, top, near);
254 glVertex3f(right, top, near);
255 glVertex3f(right, top, far);
256 glVertex3f(left, top, far);
258 glColor4f(0.05, 0.05, 0.05, 1.0);
259 glVertex3f(left, bottom, far);
260 glVertex3f(right, bottom, far);
261 glVertex3f(right, bottom, near);
262 glVertex3f(left, bottom, near);
264 glColor4f(0.1, 0.1, 0.1, 1.0);
265 glVertex3f(left, bottom, far);
266 glVertex3f(left, bottom, near);
267 glVertex3f(left, top, near);
268 glVertex3f(left, top, far);
270 glVertex3f(right, top, far);
271 glVertex3f(right, top, near);
272 glVertex3f(right, bottom, near);
273 glVertex3f(right, bottom, far);
278 glDisable(GL_CULL_FACE);
282 /* render osd texture */
283 void opengl_blit_osd(int num, uint8_t *rgba, int filter, int benson_at_line, double fov, double monitor_distance, double benson_size, double scale_x, double scale_y, double offset_x, double offset_y)
285 double texture_left = 0.0;
286 double texture_right = (double)osd_width[num] / (double)osd_size[num];
287 double texture_top = 0.0;
288 double texture_bottom = (double)osd_height[num] / (double)osd_size[num];
289 double benson_start_at_position;
290 double osd_left = 0.0;
291 double osd_right = 1.0;
292 double osd_top = 0.0;
293 double osd_bottom = 1.0;
294 double slope, range, center;
298 benson_start_at_position = ((double)image_height - (double)benson_at_line) * benson_size;
299 osd_top = ((double)image_height - benson_start_at_position) / (double)image_width;
300 osd_bottom = -((double)image_height * 2.0 - ((double)image_height - benson_start_at_position)) / (double)image_width;
301 /* calculate field-of-view */
302 slope = tan(fov / 360 * M_PI) * monitor_distance;
304 range = (osd_right - osd_left) / 2.0;
305 center = (osd_right + osd_left) / 2.0;
306 osd_left = (osd_left - center) * scale_x + center + range * offset_x;
307 osd_right = (osd_right - center) * scale_x + center + range * offset_x;
308 range = (osd_bottom - osd_top) / 2.0;
309 center = (osd_bottom + osd_top) / 2.0;
310 osd_top = (osd_top - center) * scale_y + center + range * offset_y;
311 osd_bottom = (osd_bottom - center) * scale_y + center + range * offset_y;
314 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
315 glEnable(GL_TEXTURE_2D);
316 glBindTexture(GL_TEXTURE_2D, osd_name[num]);
317 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); /* no modulation with color */
318 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
319 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
320 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, osd_width[num], osd_height[num], GL_RGBA, GL_UNSIGNED_BYTE, rgba);
323 /* perspective viewport */
324 glTexCoord2f(texture_left, texture_bottom);
325 glVertex3f(osd_left * slope, osd_bottom * slope, -monitor_distance);
326 glTexCoord2f(texture_right, texture_bottom);
327 glVertex3f(osd_right * slope, osd_bottom * slope, -monitor_distance);
328 glTexCoord2f(texture_right, texture_top);
329 glVertex3f(osd_right * slope, osd_top * slope, -monitor_distance);
330 glTexCoord2f(texture_left, texture_top);
331 glVertex3f(osd_left * slope, osd_top * slope, -monitor_distance);
333 /* orthogonal viewport */
334 glTexCoord2f(texture_left, texture_top);
335 glVertex3f(osd_left, osd_top, 0.0);
336 glTexCoord2f(texture_right, texture_top);
337 glVertex3f(osd_right, osd_top, 0.0);
338 glTexCoord2f(texture_right, texture_bottom);
339 glVertex3f(osd_right, osd_bottom, 0.0);
340 glTexCoord2f(texture_left, texture_bottom);
341 glVertex3f(osd_left, osd_bottom, 0.0);
344 glDisable(GL_TEXTURE_2D);
348 /* set color and opacity */
349 void opengl_render_color(double r, double g, double b, double a)
353 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
354 glColor4d(r, g, b, a);
361 /* set color and alpha */
362 void opengl_render_color_alpha(double r, double g, double b, double a)
365 glColor4d(r, g, b, a);
369 void opengl_render_polygon(double *x, double *y, double *z, int count, int cull_face)
374 glEnable(GL_CULL_FACE);
375 glFrontFace((flip_y) ? GL_CCW : GL_CW);
379 for (i = 0; i < count; i++)
380 glVertex3d(x[i], y[i], -z[i]);
383 glDisable(GL_CULL_FACE);
386 /* render polygon, but make sure any size of it is visible */
387 void opengl_render_polygon_and_line(double *x, double *y, double *z, int count)
392 for (i = 0; i < count; i++)
393 glVertex3d(x[i], y[i], -z[i]);
395 glBegin(GL_LINE_LOOP);
396 for (i = 0; i < count; i++)
397 glVertex3d(x[i], y[i], -z[i]);
402 void opengl_render_line(double x1, double y1, double z1, double x2, double y2, double z2, double size)
406 glVertex3f(x1, y1, -z1);
407 glVertex3f(x2, y2, -z2);
414 void opengl_render_point(double x, double y, double z, double size)
418 glVertex3f(x, y, -z);
424 int init_shadow_buffer(int multisampling)
426 shadow_multisampling = multisampling;
428 glGenFramebuffers(2, shadow_framebuffer);
430 glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
435 /* change to shadow buffer and clear it */
436 void begin_shadow_render(void)
438 /* free texture, if size changes */
439 if (shadow_texture[0] && (current_view_width != shadow_texture_width || current_view_height != shadow_texture_height)) {
440 glDeleteTextures(2, shadow_texture);
441 shadow_texture[0] = 0;
443 /* generate texture for shadow rendering (two, if MSAA is used) */
444 if (!shadow_texture[0]) {
445 glGenTextures(2, shadow_texture);
446 shadow_texture_width = current_view_width;
447 shadow_texture_height = current_view_height;
448 if (shadow_multisampling > 1) {
449 glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, shadow_texture[0]);
450 glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, shadow_multisampling, GL_RGBA, shadow_texture_width, shadow_texture_height, 0);
451 glBindTexture(GL_TEXTURE_2D, shadow_texture[1]);
452 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shadow_texture_width, shadow_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
454 glBindTexture(GL_TEXTURE_2D, shadow_texture[0]);
455 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shadow_texture_width, shadow_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
459 /* switch to shadow_framebuffer now and attach the first texture */
460 glBindFramebuffer(GL_FRAMEBUFFER, shadow_framebuffer[0]);
461 if (shadow_multisampling > 1)
462 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, shadow_texture[0], 0);
464 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shadow_texture[0], 0);
465 GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
466 glDrawBuffers(1, DrawBuffers);
467 if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
468 fprintf(stderr, "failed to create shadow texture\n");
471 /* clear to black + transparent */
472 glClearColor(0.0, 0.0, 0.0, 0.0);
473 glClear(GL_COLOR_BUFFER_BIT);
476 /* apply shadow from shadow_framebuffer to current_framebuffer and switch to it */
477 void end_shadow_render(void)
479 if (shadow_multisampling > 1) {
480 /* attach second texture to second frame buffer */
481 glBindFramebuffer(GL_FRAMEBUFFER, shadow_framebuffer[1]);
482 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shadow_texture[1], 0);
483 glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, shadow_texture[1]);
484 /* blit from first texture to second texture (MSAA to normal texture) */
485 glBindFramebuffer(GL_READ_FRAMEBUFFER, shadow_framebuffer[0]);
486 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shadow_framebuffer[1]);
487 glBlitFramebufferEXT( 0, 0, shadow_texture_width, shadow_texture_height, 0, 0, shadow_texture_width, shadow_texture_height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
490 /* switch to color buffer */
491 glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
493 /* apply shadow by drawing texture to screen */
495 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
496 glColor4d(1, 1, 1, 1);
497 glMatrixMode(GL_PROJECTION);
500 glOrtho(-1, 1, -1, 1, -1, 1);
502 glEnable(GL_TEXTURE_2D);
503 if (shadow_multisampling > 1)
504 glBindTexture(GL_TEXTURE_2D, shadow_texture[1]);
506 glBindTexture(GL_TEXTURE_2D, shadow_texture[0]);
507 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
508 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
509 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
512 glVertex3f(-1, 1, 0);
516 glVertex3f(1, -1, 0);
518 glVertex3f(-1, -1, 0);
520 glDisable(GL_TEXTURE_2D);
523 glMatrixMode(GL_MODELVIEW);
527 /* free image texture */
528 void exit_opengl(void)
532 if (shadow_texture[0]) {
533 glDeleteTextures(2, shadow_texture);
534 shadow_texture[0] = 0;
536 if (shadow_framebuffer[0]) {
537 glDeleteFramebuffers(2, shadow_framebuffer);
538 shadow_framebuffer[0] = 0;
544 for (i = 0; i < MAX_OSD; i++) {