]> icculus.org git repositories - taylor/freespace2.git/blob - src/graphics/grgl2texture.cpp
just use linear fog in GL1; cleanup
[taylor/freespace2.git] / src / graphics / grgl2texture.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 #include "SDL_opengles2.h"
10
11 #include "pstypes.h"
12 #include "2d.h"
13 #include "gropengl.h"
14 #include "gropenglinternal.h"
15 #include "grgl2.h"
16 #include "bmpman.h"
17 #include "grinternal.h"
18 #include "systemvars.h"
19 #include "osregistry.h"
20
21
22 static bool vram_full = false;
23
24 struct tcache_slot_opengl2 {
25         GLuint texture_handle;
26         float u_scale;
27         float v_scale;
28         int bitmap_id;
29         int size;
30         int used_this_frame;
31         int time_created;
32         int is_mipmaped;
33         ushort w;
34         ushort h;
35
36         gr_texture_source texture_mode;
37 };
38
39 static tcache_slot_opengl2 *Textures = NULL;
40
41 static tcache_slot_opengl2 *GL_bound_texture;
42
43 static int GL_frame_count = 0;
44 static int GL_last_bitmap_id = -1;
45 static int GL_last_detail = -1;
46 static int GL_last_bitmap_type = -1;
47 static int GL_should_preload = 0;
48
49 static ubyte GL_xlat[256] = { 0 };
50
51 extern int Gr_textures_in;
52 extern int bm_get_cache_slot( int bitmap_id, int separate_ani_frames );
53
54 extern bool Use_mipmaps;
55
56
57 void opengl2_set_texture_state(gr_texture_source ts)
58 {
59         if (ts == TEXTURE_SOURCE_NONE) {
60                 GL_bound_texture = NULL;
61
62                 glBindTexture(GL_TEXTURE_2D, 0);
63                 opengl2_tcache_set(-1, -1, NULL, NULL, 0);
64         } else if (GL_bound_texture && GL_bound_texture->texture_mode != ts) {
65                 switch (ts) {
66                         case TEXTURE_SOURCE_DECAL:
67                                 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
68                                 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
69                                 break;
70
71                         case TEXTURE_SOURCE_NO_FILTERING: {
72                                 if (GL_bound_texture->is_mipmaped) {
73                                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
74                                 } else {
75                                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
76                                 }
77
78                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
79
80                                 break;
81                         }
82
83                         default:
84                                 break;
85                 }
86
87                 GL_bound_texture->texture_mode = ts;
88         }
89 }
90
91 void opengl2_tcache_init()
92 {
93         GL_should_preload = os_config_read_uint("Video", "PreloadTextures", 1);
94
95         Textures = (tcache_slot_opengl2 *) malloc(MAX_BITMAPS * sizeof(tcache_slot_opengl2));
96
97         if (Textures == NULL) {
98                 exit(1);
99         }
100
101         SDL_assert(gr_screen.use_sections == 0);
102
103         SDL_zerop(Textures);
104
105         for (int i = 0; i < MAX_BITMAPS; i++) {
106                 Textures[i].bitmap_id = -1;
107         }
108
109         GL_last_detail = Detail.hardware_textures;
110         GL_last_bitmap_id = -1;
111         GL_last_bitmap_type     = -1;
112
113         SDL_zero(GL_xlat);
114 }
115
116 static int opengl2_free_texture(tcache_slot_opengl2 *t)
117 {
118         if (t->bitmap_id < 0) {
119                 return 1;
120         }
121
122         // if I have been used this frame, bail
123         if (t->used_this_frame == GL_frame_count) {
124                 return 0;
125         }
126
127         glDeleteTextures(1, &t->texture_handle);
128
129         if (GL_last_bitmap_id == t->bitmap_id) {
130                 GL_last_bitmap_id = -1;
131         }
132
133         Gr_textures_in -= t->size;
134
135         t->texture_handle = 0;
136         t->bitmap_id = -1;
137         t->used_this_frame = 0;
138         t->size = 0;
139         t->is_mipmaped = 0;
140
141         return 1;
142 }
143
144 void opengl2_tcache_flush()
145 {
146         for (int i = 0; i < MAX_BITMAPS; i++) {
147                 opengl2_free_texture(&Textures[i]);
148         }
149
150         if (Gr_textures_in != 0) {
151                 mprintf(("WARNING: VRAM is at %d instead of zero after flushing!\n", Gr_textures_in));
152                 Gr_textures_in = 0;
153         }
154
155         GL_last_bitmap_id = -1;
156
157         vram_full = false;
158 }
159
160 void opengl2_tcache_cleanup()
161 {
162         opengl2_tcache_flush();
163
164         if (Textures) {
165                 free(Textures);
166                 Textures = NULL;
167         }
168 }
169
170 void opengl2_tcache_frame()
171 {
172         GL_last_bitmap_id = -1;
173         GL_frame_count++;
174
175         if (vram_full) {
176                 opengl2_tcache_flush();
177                 vram_full = false;
178         }
179 }
180
181 static int opengl2_create_texture_sub(int bitmap_handle, int bitmap_type, bitmap *bmp, tcache_slot_opengl2 *t, int tex_w, int tex_h, bool reload, bool resize, int fail_on_full)
182 {
183         // bogus
184         if ( (bmp == NULL) || (t == NULL) ) {
185                 return 0;
186         }
187
188         if (t->used_this_frame == GL_frame_count) {
189                 mprintf(("ARGHH!!! Texture already used this frame! Cannot free it!\n"));
190                 return 0;
191         }
192
193         if ( !reload ) {
194                 if ( !opengl2_free_texture(t) ) {
195                         return 0;
196                 }
197
198                 glGenTextures(1, &t->texture_handle);
199
200                 if ( !t->texture_handle ) {
201                         nprintf(("Error", "!!DEBUG!! t->texture_handle == 0"));
202                         return 0;
203                 }
204         }
205
206         t->u_scale = 1.0f;
207         t->v_scale = 1.0f;
208
209         t->texture_mode = TEXTURE_SOURCE_NO_FILTERING;
210
211         glBindTexture(GL_TEXTURE_2D, t->texture_handle);
212
213         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
214         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
215
216         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
217         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
218
219         ubyte *bmp_data = (ubyte*)bmp->data;
220         ubyte *texmem = NULL, *texmemp;
221         int i, j;
222         int size = 0;
223
224         switch (bitmap_type) {
225                 case TCACHE_TYPE_AABITMAP: {
226                         SDL_assert(tex_w == bmp->w);
227                         SDL_assert(tex_h == bmp->h);
228
229                         texmem = (ubyte *) malloc(tex_w * tex_h);
230                         texmemp = texmem;
231
232                         for (i = 0; i < tex_h; i++) {
233                                 for (j = 0; j < tex_w; j++) {
234                                         *texmemp++ = GL_xlat[bmp_data[i*bmp->w+j]];
235                                 }
236                         }
237
238                         size = tex_w * tex_h;
239
240                         if (reload) {
241                                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_ALPHA, GL_UNSIGNED_BYTE, texmem);
242                         } else {
243                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tex_w, tex_h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texmem);
244                         }
245
246                         free(texmem);
247
248                         break;
249                 }
250
251                 case TCACHE_TYPE_BITMAP_INTERFACE:
252                 case TCACHE_TYPE_BITMAP_SECTION: {
253                         size = tex_w * tex_h * 2;
254
255                         if (reload) {
256                                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, bmp_data);
257                         } else {
258                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, bmp_data);
259                         }
260
261                         break;
262                 }
263
264                 default: {
265                         if (resize) {
266                                 texmem = (ubyte *) malloc(tex_w * tex_h * 2);
267                                 texmemp = texmem;
268
269                                 SDL_assert(texmem);
270
271                                 fix u = 0, utmp, v = 0, du, dv;
272
273                                 du = ((bmp->w - 1) * F1_0) / tex_w;
274                                 dv = ((bmp->h - 1) * F1_0) / tex_h;
275
276                                 for (j = 0; j < tex_h; j++) {
277                                         utmp = u;
278
279                                         for (i = 0; i < tex_w; i++) {
280                                                 *texmemp++ = bmp_data[(f2i(v)*bmp->w+f2i(utmp))*2+0];
281                                                 *texmemp++ = bmp_data[(f2i(v)*bmp->w+f2i(utmp))*2+1];
282
283                                                 utmp += du;
284                                         }
285
286                                         v += dv;
287                                 }
288                         }
289
290                         if (reload) {
291                                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (resize) ? texmem : bmp_data);
292                         } else {
293                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (resize) ? texmem : bmp_data);
294                         }
295
296                         if (texmem) {
297                                 free(texmem);
298                         }
299
300                         if (Use_mipmaps) {
301                                 glGenerateMipmap(GL_TEXTURE_2D);
302
303                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
304                                 t->is_mipmaped = 1;
305
306                                 size = fl2i(tex_w * tex_h * 2.0f * 1.3333333f);
307                         } else {
308                                 size = tex_w * tex_h * 2;
309                         }
310
311                         break;
312                 }
313         }
314
315         t->bitmap_id = bitmap_handle;
316         t->time_created = GL_frame_count;
317         t->used_this_frame = 0;
318         t->size = size;
319         t->w = (ushort)tex_w;
320         t->h = (ushort)tex_h;
321
322         if ( !reload ) {
323                 Gr_textures_in += t->size;
324         }
325
326         return 1;
327 }
328
329 static int opengl2_create_texture(int bitmap_handle, int bitmap_type, tcache_slot_opengl2 *tslot, int fail_on_full)
330 {
331         ubyte flags = 0;
332         bool cull_size = false;
333         bool resize = false;
334         ubyte bpp = 16;
335
336         switch (bitmap_type) {
337                 case TCACHE_TYPE_AABITMAP: {
338                         flags |= BMP_AABITMAP;
339                         bpp = 8;
340
341                         break;
342                 }
343
344                 case TCACHE_TYPE_NORMAL: {
345                         flags |= BMP_TEX_OTHER;
346                         cull_size = 1;
347
348                         break;
349                 }
350
351                 case TCACHE_TYPE_BITMAP_INTERFACE:
352                 case TCACHE_TYPE_XPARENT: {
353                         flags |= BMP_TEX_XPARENT;
354
355                         break;
356                 }
357
358                 default:
359                         Int3();
360                         return 0;
361         }
362
363         bitmap *bmp = bm_lock(bitmap_handle, bpp, flags);
364
365         if (bmp == NULL) {
366                 mprintf(("Couldn't lock bitmap %d\n", bitmap_handle));
367                 return 0;
368         }
369
370         int max_w = bmp->w;
371         int max_h = bmp->h;
372
373         if ( cull_size && (Detail.hardware_textures < 4) ) {
374                 // if we are going to cull the size then we need to force a resize
375                 resize = true;
376
377                 // Detail.hardware_textures goes form 0 to 4
378                 int val = 16 >> Detail.hardware_textures;
379
380                 max_w /= val;
381                 max_h /= val;
382         }
383
384         if ( (max_w < 1) || (max_h < 1) ) {
385                 mprintf(("Bitmap %d is too small at %dx%d\n", bitmap_handle, max_w, max_h));
386                 return 0;
387         }
388
389         bool reload = false;
390
391         // see if we can reuse this slot for a new bitmap
392         if ( tslot->texture_handle && (tslot->bitmap_id != bitmap_handle) ) {
393                 if ( (max_w == tslot->w) && (max_h == tslot->h) ) {
394                         reload = true;
395                 }
396         }
397
398         // call the helper
399         int ret_val = opengl2_create_texture_sub(bitmap_handle, bitmap_type, bmp, tslot, max_w, max_h, reload, resize, fail_on_full);
400
401         // unlock the bitmap
402         bm_unlock(bitmap_handle);
403
404         return ret_val;
405 }
406
407 int opengl2_tcache_set(int bitmap_id, int bitmap_type, float *u_scale, float *v_scale, int fail_on_full)
408 {
409         if (bitmap_id < 0) {
410                 GL_last_bitmap_id = -1;
411
412                 return 0;
413         }
414
415         if (GL_last_detail != Detail.hardware_textures) {
416                 opengl2_tcache_flush();
417                 GL_last_detail = Detail.hardware_textures;
418         }
419
420         if (vram_full) {
421                 return 0;
422         }
423
424         int n = bm_get_cache_slot(bitmap_id, 1);
425         tcache_slot_opengl2 *t = &Textures[n];
426
427         if ( (GL_last_bitmap_id == bitmap_id) && (GL_last_bitmap_type == bitmap_type) && (t->bitmap_id == bitmap_id) ) {
428                 t->used_this_frame = GL_frame_count;
429
430                 *u_scale = t->u_scale;
431                 *v_scale = t->v_scale;
432
433                 return 1;
434         }
435
436         int ret_val = 1;
437
438         if (bitmap_id != t->bitmap_id) {
439                 ret_val = opengl2_create_texture(bitmap_id, bitmap_type, t, fail_on_full);
440         }
441
442         if (ret_val && t->texture_handle && !vram_full) {
443                 glBindTexture(GL_TEXTURE_2D, t->texture_handle);
444
445                 *u_scale = t->u_scale;
446                 *v_scale = t->v_scale;
447
448                 GL_bound_texture = t;
449
450                 GL_last_bitmap_id = t->bitmap_id;
451                 GL_last_bitmap_type = bitmap_type;
452
453                 t->used_this_frame = GL_frame_count;
454         } else {
455                 GL_last_bitmap_id = -1;
456                 GL_last_bitmap_type = -1;
457
458                 GL_bound_texture = NULL;
459
460                 glBindTexture(GL_TEXTURE_2D, 0);
461
462                 return 0;
463         }
464
465         return 1;
466 }
467
468 void gr_opengl2_preload_init()
469 {
470         opengl2_tcache_flush();
471 }
472
473 int gr_opengl2_preload(int bitmap_num, int is_aabitmap)
474 {
475         if ( !GL_should_preload ) {
476                 return 0;
477         }
478
479         int bitmap_type = (is_aabitmap) ? TCACHE_TYPE_AABITMAP : TCACHE_TYPE_NORMAL;
480         float u_scale, v_scale;
481
482         int retval = opengl2_tcache_set(bitmap_num, bitmap_type, &u_scale, &v_scale, 1);
483
484         if ( !retval ) {
485                 mprintf(("Texture upload failed bit bitmap %d!\n", bitmap_num));
486         }
487
488         return retval;
489 }
490
491 void gr_opengl2_set_gamma(float)
492 {
493         // set the alpha gamma settings (for fonts)
494         for (int i = 0; i < 16; i++) {
495                 GL_xlat[i] = (ubyte)Gr_gamma_lookup[(i*255)/15];
496         }
497
498         GL_xlat[15] = GL_xlat[1];
499
500         // Flush any existing textures
501         opengl2_tcache_flush();
502 }
503
504 void gr_opengl2_release_texture(int handle)
505 {
506         for (int i = 0; i < MAX_BITMAPS; i++) {
507                 tcache_slot_opengl2 *t = &Textures[i];
508
509                 if (t->bitmap_id == handle) {
510                         t->used_this_frame = 0;
511                         opengl2_free_texture(t);
512
513                         break;
514                 }
515         }
516 }