3e046f0c5fbbae22a46f13842f0fefe217529e56
[mercenary-reloaded.git] / src / libsdl / opengl.c
1 /* OpenGL rendering
2  *
3  * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
4  * All Rights Reserved
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 3 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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <math.h>
24 #include <errno.h>
25 #include "print.h"
26 #include "opengl.h"
27 #include <GL/glew.h>
28
29 static uint8_t *legacy_rgb = NULL;
30 static uint8_t *benson_rgb = NULL;
31 static GLuint legacy_name;
32 static GLuint benson_name;
33 static int screen_width, screen_height;
34 static int image_width, image_height;
35 static int texture_size;
36
37 /* alloc and init an image texture with size that is greater than the power of two */
38 int init_opengl(int _image_width, int _image_height)
39 {
40         int rc;
41
42         image_width = _image_width;
43         image_height = _image_height;
44
45         /* generate texture */
46         for (texture_size = 1; texture_size <= image_width || texture_size <= image_height; texture_size *= 2)
47                 ;
48
49         legacy_rgb = calloc(texture_size * texture_size, 3);
50         if (!legacy_rgb) {
51                 print_error("Failed to allocate texture\n");
52                 rc = -ENOMEM;
53                 goto error;
54         }
55         glShadeModel(GL_FLAT);
56         glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* bytes */
57         glGenTextures(1, &legacy_name);
58         glBindTexture(GL_TEXTURE_2D, legacy_name);
59         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
60         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
61         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_size, texture_size, 0, GL_RGB, GL_UNSIGNED_BYTE, legacy_rgb);
62
63         benson_rgb = calloc(texture_size * texture_size, 3);
64         if (!benson_rgb) {
65                 print_error("Failed to allocate texture\n");
66                 rc = -ENOMEM;
67                 goto error;
68         }
69
70         glShadeModel(GL_FLAT);
71         glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* bytes */
72         glGenTextures(1, &benson_name);
73         glBindTexture(GL_TEXTURE_2D, benson_name);
74         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
75         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
76         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_size, texture_size, 0, GL_RGB, GL_UNSIGNED_BYTE, benson_rgb);
77
78         return 0;
79
80 error:
81         exit_opengl();
82         return rc;
83 }
84
85 /* set clip planes so that the image portion of the image texture is centered and pixels are rectengular */
86 void resize_opengl(int _screen_width, int _screen_height)
87 {
88         screen_width = _screen_width;
89         screen_height = _screen_height;
90 }
91
92
93 void opengl_clear(void)
94 {
95         glClearColor(0.0, 0.0, 0.0, 1.0);
96         glClear(GL_COLOR_BUFFER_BIT);
97 }
98
99 /* set viewport for legacy image */
100 void opengl_viewport_legacy(int split)
101 {
102         int view_x, view_y;
103         int view_width, view_height;
104         int new_width, new_height;
105
106         if (!split) {
107                 view_x = 0;
108                 view_y = 0;
109                 view_width = screen_width;
110                 view_height = screen_height;
111         } else {
112                 view_x = 0;
113                 view_y = screen_height / 2;
114                 view_width = screen_width;
115                 view_height = screen_height / 2;
116         }
117
118         /* avoid division by zero, if one dimension is too small */
119         if (view_width < 1 || view_height < 1)
120                 return;
121
122         /* calculate a viewport that has apect of image_width:image_height */
123         if (view_height * image_width > view_width * image_height) {
124                 new_height = view_width * image_height / image_width;
125                 view_y = view_y + view_height / 2 - new_height / 2;
126                 view_height = new_height;
127         } else if (view_height * image_width < view_width * image_height) {
128                 new_width = view_height * image_width / image_height;
129                 view_x = view_x + view_width / 2 - new_width / 2;
130                 view_width = new_width;
131         }
132
133         /* avoid views that are too small */
134         if (view_width < 1 || view_height < 1)
135                 return;
136
137         /* viewport and projection matrix */
138         glViewport((GLsizei)view_x, (GLsizei)view_y, (GLsizei)view_width, (GLsizei)view_height);
139         glMatrixMode(GL_PROJECTION);
140         glLoadIdentity();
141         glOrtho(0.0, 1.0, 1.0, 0.0, -1.0, 1.0);
142         glMatrixMode(GL_MODELVIEW);
143 }
144
145 /* render legacy image texture */
146 void opengl_render_legacy(uint8_t *rgb, int filter)
147 {
148         double width = (double)image_width / (double)texture_size;
149         double height = (double)image_height / (double)texture_size;
150
151         glEnable(GL_TEXTURE_2D);
152         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);  /* no modulation with color */
153         glBindTexture(GL_TEXTURE_2D, legacy_name);
154         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
155         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
156         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_width, image_height, GL_RGB, GL_UNSIGNED_BYTE, rgb);
157         glBegin(GL_QUADS);
158         glTexCoord2f(0.0, 0.0);
159         glVertex3f(0.0, 0.0, 0.0);
160         glTexCoord2f(width, 0.0);
161         glVertex3f(1.0, 0.0, 0.0);
162         glTexCoord2f(width, height);
163         glVertex3f(1.0, 1.0, 0.0);
164         glTexCoord2f(0.0, height);
165         glVertex3f(0.0, 1.0, 0.0);
166         glEnd();
167         glDisable(GL_TEXTURE_2D);
168 }
169
170 static double fov = 64.0;
171
172 /* set viewport for improved rendering */
173 void opengl_viewport_improved(int split, int benson_at_line)
174 {
175         int view_x, view_y;
176         int view_width, view_height;
177         int new_width, new_height;
178
179         /* center view, or put it in the top half of the window */
180         if (!split) {
181                 view_x = 0;
182                 view_y = 0;
183                 view_width = screen_width;
184                 view_height = screen_height;
185         } else {
186                 view_x = 0;
187                 view_y = 0;
188                 view_width = screen_width;
189                 view_height = screen_height / 2;
190         }
191
192         /* avoid division by zero, if one dimension is too small */
193         if (view_width < 1 || view_height < 1)
194                 return;
195
196         /* calculate a viewport that has apect of image_width:image_height */
197         if (view_height * image_width > view_width * image_height) {
198                 new_height = view_width * image_height / image_width;
199                 view_y = view_y + view_height / 2 - new_height / 2;
200                 view_height = new_height;
201         } else if (view_height * image_width < view_width * image_height) {
202                 new_width = view_height * image_width / image_height;
203                 view_x = view_x + view_width / 2 - new_width / 2;
204                 view_width = new_width;
205         }
206
207         /* avoid views that are too small */
208         if (view_width < 1 || view_height < 1)
209                 return;
210
211         /* viewport and projection matrix */
212         glViewport((GLsizei)view_x, (GLsizei)view_y, (GLsizei)view_width, (GLsizei)view_height);
213         glMatrixMode(GL_PROJECTION);
214         glLoadIdentity();
215
216         /* calculate field-of-view */
217         double slope = tan(fov / 360 * M_PI);
218         /* make frustum to center the view in the game view above benson */
219         double left = -slope;
220         double right = slope;
221         double top = slope * (double)benson_at_line / (double)image_width;
222         double bottom = -slope * ((double)image_height * 2.0 - (double)benson_at_line) / (double)image_width;
223         glFrustum(left, right, bottom, top, 1.0, 5000000000.0);
224         glMatrixMode(GL_MODELVIEW);
225
226 #if 1
227         /* test rectangle */
228         glColor3d(0.5, 0.4, 0.4);
229         glBegin(GL_QUADS);
230         glVertex3f(-1.0, -1.0, -1.0);
231         glVertex3f(1.0, -1.0, -1.0);
232         glVertex3f(1.0, 1.0, -1.0);
233         glVertex3f(-1.0, 1.0, -1.0);
234         glEnd();
235 #endif
236 }
237
238 /* render only benson */
239 void opengl_render_benson(uint8_t *rgb, int filter, int benson_at_line)
240 {
241         double texture_left = 0.0;
242         double texture_right = (double)image_width / (double)texture_size;
243         double texture_top = (double)benson_at_line / (double)texture_size;
244         double texture_bottom = (double)image_height / (double)texture_size;
245         double benson_top = -(double)benson_at_line / (double)image_width;
246         double benson_bottom = -((double)image_height * 2.0 - (double)benson_at_line) / (double)image_width;
247
248         /* calculate field-of-view */
249         double slope = tan(fov / 360 * M_PI);
250
251         /* render benson */
252         rgb += image_width * benson_at_line * 3;
253         glEnable(GL_TEXTURE_2D);
254         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);  /* no modulation with color */
255         glBindTexture(GL_TEXTURE_2D, benson_name);
256         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
257         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
258         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, benson_at_line, image_width, image_height - benson_at_line, GL_RGB, GL_UNSIGNED_BYTE, rgb);
259         glBegin(GL_QUADS);
260         glTexCoord2f(texture_left, texture_bottom);
261         glVertex3f(-2.0, 2.0 * benson_bottom, -2.0 / slope);
262         glTexCoord2f(texture_right, texture_bottom);
263         glVertex3f(2.0, 2.0 * benson_bottom, -2.0 / slope);
264         glTexCoord2f(texture_right, texture_top);
265         glVertex3f(2.0, 2.0 * benson_top, -2.0 / slope);
266         glTexCoord2f(texture_left, texture_top);
267         glVertex3f(-2.0, 2.0 * benson_top, -2.0 / slope);
268         glEnd();
269         glDisable(GL_TEXTURE_2D);
270 }
271
272 /* free image texture */
273 void exit_opengl(void)
274 {
275         if (legacy_rgb) {
276                 free(legacy_rgb);
277                 legacy_rgb = NULL;
278         }
279         if (benson_rgb) {
280                 free(benson_rgb);
281                 benson_rgb = NULL;
282         }
283 }
284