2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
9 #include "SDL_opengl.h"
14 #include "gropenglinternal.h"
17 #include "grinternal.h"
18 #include "systemvars.h"
19 #include "osregistry.h"
22 static int vram_full = 0;
24 typedef struct tcache_slot_opengl {
25 GLuint texture_handle;
26 float u_scale, v_scale;
34 tcache_slot_opengl *data_sections[MAX_BMAP_SECTIONS_X][MAX_BMAP_SECTIONS_Y];
35 tcache_slot_opengl *parent;
37 gr_texture_source texture_mode;
40 static void *Texture_sections = NULL;
41 static tcache_slot_opengl *Textures = NULL;
43 static tcache_slot_opengl *GL_bound_texture;
45 static int GL_frame_count = 0;
46 static int GL_last_bitmap_id = -1;
47 static int GL_last_detail = -1;
48 static int GL_last_bitmap_type = -1;
49 static int GL_last_section_x = -1;
50 static int GL_last_section_y = -1;
51 static int GL_should_preload = 0;
53 extern int Gr_textures_in;
55 static gr_texture_source GL_current_texture_source = (gr_texture_source) -1;
57 static ubyte GL_xlat[256] = { 0 };
59 extern int bm_get_cache_slot( int bitmap_id, int separate_ani_frames );
62 void opengl1_set_texture_state(gr_texture_source ts)
64 if (ts == TEXTURE_SOURCE_NONE) {
65 GL_bound_texture = NULL;
67 glBindTexture(GL_TEXTURE_2D, 0);
68 opengl1_tcache_set(-1, -1, NULL, NULL, 0, -1, -1, 0 );
69 } else if (GL_bound_texture &&
70 GL_bound_texture->texture_mode != ts) {
72 case TEXTURE_SOURCE_DECAL:
73 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
74 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
76 case TEXTURE_SOURCE_NO_FILTERING:
77 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
78 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
84 GL_bound_texture->texture_mode = ts;
87 GL_current_texture_source = ts;
91 void opengl1_tcache_init()
95 if ( os_config_read_uint("Video", "PreloadTextures", 1) ) {
96 GL_should_preload = 1;
98 GL_should_preload = 0;
101 Textures = (tcache_slot_opengl *)malloc(MAX_BITMAPS*sizeof(tcache_slot_opengl));
106 if (gr_screen.use_sections) {
107 Texture_sections = (tcache_slot_opengl*)malloc(MAX_BITMAPS * MAX_BMAP_SECTIONS_X * MAX_BMAP_SECTIONS_Y * sizeof(tcache_slot_opengl));
108 if(!Texture_sections){
111 memset(Texture_sections, 0, MAX_BITMAPS * MAX_BMAP_SECTIONS_X * MAX_BMAP_SECTIONS_Y * sizeof(tcache_slot_opengl));
114 // Init the texture structures
115 int section_count = 0;
116 for( i=0; i<MAX_BITMAPS; i++ ) {
117 Textures[i].texture_handle = 0;
119 Textures[i].bitmap_id = -1;
120 Textures[i].size = 0;
121 Textures[i].used_this_frame = 0;
123 Textures[i].parent = NULL;
126 if (gr_screen.use_sections) {
127 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
128 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
129 Textures[i].data_sections[idx][s_idx] = &((tcache_slot_opengl*)Texture_sections)[section_count++];
130 Textures[i].data_sections[idx][s_idx]->parent = &Textures[i];
131 Textures[i].data_sections[idx][s_idx]->texture_handle = 0;
132 Textures[i].data_sections[idx][s_idx]->bitmap_id = -1;
133 Textures[i].data_sections[idx][s_idx]->size = 0;
134 Textures[i].data_sections[idx][s_idx]->used_this_frame = 0;
138 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
139 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
140 Textures[i].data_sections[idx][s_idx] = NULL;
146 GL_last_detail = Detail.hardware_textures;
147 GL_last_bitmap_id = -1;
148 GL_last_bitmap_type = -1;
150 GL_last_section_x = -1;
151 GL_last_section_y = -1;
153 memset(GL_xlat, 0, sizeof(GL_xlat));
156 static int opengl1_free_texture ( tcache_slot_opengl *t )
162 if ( t->bitmap_id > -1 ) {
163 // if I, or any of my children have been used this frame, bail
164 if(t->used_this_frame == GL_frame_count){
168 if (gr_screen.use_sections) {
169 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
170 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
171 if((t->data_sections[idx][s_idx] != NULL) && (t->data_sections[idx][s_idx]->used_this_frame == GL_frame_count)){
178 // ok, now we know its legal to free everything safely
179 t->texture_mode = (gr_texture_source) -1;
180 glDeleteTextures (1, &t->texture_handle);
181 t->texture_handle = 0;
183 if ( GL_last_bitmap_id == t->bitmap_id ) {
184 GL_last_bitmap_id = -1;
187 // if this guy has children, free them too, since the children
188 // actually make up his size
189 if (gr_screen.use_sections) {
190 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
191 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
192 if(t->data_sections[idx][s_idx] != NULL){
193 opengl1_free_texture(t->data_sections[idx][s_idx]);
200 t->used_this_frame = 0;
201 Gr_textures_in -= t->size;
208 void opengl1_tcache_flush()
212 for( i=0; i<MAX_BITMAPS; i++ ) {
213 opengl1_free_texture ( &Textures[i] );
215 if (Gr_textures_in != 0) {
216 mprintf(( "WARNING: VRAM is at %d instead of zero after flushing!\n", Gr_textures_in ));
220 GL_last_bitmap_id = -1;
221 GL_last_section_x = -1;
222 GL_last_section_y = -1;
225 void opengl1_tcache_cleanup()
227 opengl1_tcache_flush ();
234 if( Texture_sections != NULL ){
235 free(Texture_sections);
236 Texture_sections = NULL;
240 void opengl1_tcache_frame()
242 GL_last_bitmap_id = -1;
249 for( i=0; i<MAX_BITMAPS; i++ ) {
250 Textures[i].used_this_frame = 0;
253 if(Textures[i].data_sections[0][0] != NULL){
254 SDL_assert(GL_texture_sections);
255 if(GL_texture_sections){
256 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
257 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
258 if(Textures[i].data_sections[idx][s_idx] != NULL){
259 Textures[i].data_sections[idx][s_idx]->used_this_frame = 0;
269 opengl1_tcache_flush();
274 static void opengl1_tcache_get_adjusted_texture_size(int w_in, int h_in, int *w_out, int *h_out)
280 if((w_out == NULL) || (h_out == NULL)){
288 // set height and width to a power of 2
289 for (i=0; i<16; i++ ) {
290 if ( (tex_w > (1<<i)) && (tex_w <= (1<<(i+1))) ) {
296 for (i=0; i<16; i++ ) {
297 if ( (tex_h > (1<<i)) && (tex_h <= (1<<(i+1))) ) {
303 // try to keep an 8:1 size ratio
309 if ( tex_w < GL_min_texture_width ) {
310 tex_w = GL_min_texture_width;
311 } else if ( tex_w > GL_max_texture_width ) {
312 tex_w = GL_max_texture_width;
315 if ( tex_h < GL_min_texture_height ) {
316 tex_h = GL_min_texture_height;
317 } else if ( tex_h > GL_max_texture_height ) {
318 tex_h = GL_max_texture_height;
321 // store the outgoing size
326 // data == start of bitmap data
327 // sx == x offset into bitmap
328 // sy == y offset into bitmap
329 // src_w == absolute width of section on source bitmap
330 // src_h == absolute height of section on source bitmap
331 // bmap_w == width of source bitmap
332 // bmap_h == height of source bitmap
333 // tex_w == width of final texture
334 // tex_h == height of final texture
335 static int opengl1_create_texture_sub(int bitmap_type, int texture_handle, ushort *data, int sx, int sy, int src_w, int src_h, int bmap_w, int bmap_h, int tex_w, int tex_h, tcache_slot_opengl *t, int reload, int resize, int fail_on_full)
340 ubyte *bmp_data = ((ubyte*)data);
341 ubyte *texmem = NULL, *texmemp;
344 if((t == NULL) || (bmp_data == NULL)){
348 if ( t->used_this_frame == GL_frame_count ) {
349 mprintf(( "ARGHH!!! Texture already used this frame! Cannot free it!\n" ));
354 if(!opengl1_free_texture(t)){
359 switch (bitmap_type) {
360 case TCACHE_TYPE_AABITMAP:
361 t->u_scale = (float)bmap_w / (float)tex_w;
362 t->v_scale = (float)bmap_h / (float)tex_h;
365 case TCACHE_TYPE_BITMAP_INTERFACE:
366 case TCACHE_TYPE_BITMAP_SECTION:
367 t->u_scale = (float)src_w / (float)tex_w;
368 t->v_scale = (float)src_h / (float)tex_h;
378 glGenTextures (1, &t->texture_handle);
381 if (t->texture_handle == 0) {
382 nprintf(("Error", "!!DEBUG!! t->texture_handle == 0"));
386 GL_bound_texture = t;
388 GL_bound_texture->texture_mode = (gr_texture_source) -1;
390 glBindTexture (GL_TEXTURE_2D, t->texture_handle);
392 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
393 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
394 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
396 /* this should be set next anyway */
397 // if (GL_current_texture_source != TEXTURE_SOURCE_NONE) {
398 // gr_opengl_set_texture_state(GL_current_texture_source);
401 switch (bitmap_type) {
402 case TCACHE_TYPE_AABITMAP:
404 texmem = (ubyte *) malloc (tex_w*tex_h);
407 for (i=0;i<tex_h;i++)
409 for (j=0;j<tex_w;j++)
411 if (i < bmap_h && j < bmap_w) {
412 *texmemp++ = GL_xlat[bmp_data[i*bmap_w+j]];
422 glTexImage2D (GL_TEXTURE_2D, 0, GL_ALPHA, tex_w, tex_h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texmem);
424 glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_ALPHA, GL_UNSIGNED_BYTE, texmem);
432 case TCACHE_TYPE_BITMAP_INTERFACE:
433 case TCACHE_TYPE_BITMAP_SECTION:
435 // if we aren't resizing in any way then we can just use bmp_data directly
437 texmem = (ubyte *) malloc (tex_w*tex_h*2);
440 for (i=0;i<tex_h;i++) {
441 for (j=0;j<tex_w;j++) {
442 if (i < src_h && j < src_w) {
443 *texmemp++ = bmp_data[((i+sy)*bmap_w+(j+sx))*2+0];
444 *texmemp++ = bmp_data[((i+sy)*bmap_w+(j+sx))*2+1];
453 size = tex_w*tex_h*2;
456 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);
458 glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (resize) ? texmem : bmp_data);
469 // if we aren't resizing then we can just use bmp_data directly
471 texmem = (ubyte *) malloc (tex_w*tex_h*2);
474 SDL_assert( texmem != NULL );
476 fix u, utmp, v, du, dv;
480 du = ( (bmap_w-1)*F1_0 ) / tex_w;
481 dv = ( (bmap_h-1)*F1_0 ) / tex_h;
483 for (j=0;j<tex_h;j++) {
485 for (i=0;i<tex_w;i++) {
486 *texmemp++ = bmp_data[(f2i(v)*bmap_w+f2i(utmp))*2+0];
487 *texmemp++ = bmp_data[(f2i(v)*bmap_w+f2i(utmp))*2+1];
494 size = tex_w*tex_h*2;
497 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);
499 glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (resize) ? texmem : bmp_data);
509 t->bitmap_id = texture_handle;
510 t->time_created = GL_frame_count;
511 t->used_this_frame = 0;
513 t->w = (ushort)tex_w;
514 t->h = (ushort)tex_h;
517 Gr_textures_in += t->size;
523 static int opengl1_create_texture(int bitmap_handle, int bitmap_type, tcache_slot_opengl *tslot, int fail_on_full)
527 int final_w, final_h;
533 // setup texture/bitmap flags
536 case TCACHE_TYPE_AABITMAP:
537 flags |= BMP_AABITMAP;
540 case TCACHE_TYPE_NORMAL:
541 flags |= BMP_TEX_OTHER;
544 case TCACHE_TYPE_BITMAP_INTERFACE:
545 case TCACHE_TYPE_XPARENT:
546 flags |= BMP_TEX_XPARENT;
553 // lock the bitmap into the proper format
554 bmp = bm_lock(bitmap_handle, bpp, flags);
556 mprintf(("Couldn't lock bitmap %d.\n", bitmap_handle ));
564 // if we are going to cull the size then we need to force a resize
565 if (Detail.hardware_textures < 4) {
568 // Detail.debris_culling goes from 0 to 4.
569 max_w /= (16 >> Detail.hardware_textures);
570 max_h /= (16 >> Detail.hardware_textures);
575 // get final texture size as it will be allocated as a DD surface
576 opengl1_tcache_get_adjusted_texture_size(max_w, max_h, &final_w, &final_h);
578 if ( (final_w < 1) || (final_h < 1) ) {
579 mprintf(("Bitmap is too small at %dx%d.\n", final_w, final_h));
583 // if we don't have to resize the image (to get power of 2, etc.) then skip that extra work
584 if ( (max_w != final_w) || (max_h != final_h) ) {
588 // if this tcache slot has no bitmap
589 if ( tslot->bitmap_id < 0) {
592 // different bitmap altogether - determine if the new one can use the old one's slot
593 else if (tslot->bitmap_id != bitmap_handle) {
594 if((final_w == tslot->w) && (final_h == tslot->h)){
596 //ml_printf("Reloading texture %d\n", bitmap_handle);
603 int ret_val = opengl1_create_texture_sub(bitmap_type, bitmap_handle, (ushort*)bmp->data, 0, 0, bmp->w, bmp->h, bmp->w, bmp->h, final_w, final_h, tslot, reload, resize, fail_on_full);
606 bm_unlock(bitmap_handle);
611 static int opengl1_create_texture_sectioned(int bitmap_handle, int bitmap_type, tcache_slot_opengl *tslot, int sx, int sy, int fail_on_full)
615 int final_w, final_h;
616 int section_x, section_y;
620 SDL_assert( gr_screen.use_sections );
622 // setup texture/bitmap flags
623 SDL_assert(bitmap_type == TCACHE_TYPE_BITMAP_SECTION);
624 if(bitmap_type != TCACHE_TYPE_BITMAP_SECTION){
625 bitmap_type = TCACHE_TYPE_BITMAP_SECTION;
627 flags = BMP_TEX_XPARENT;
629 // lock the bitmap in the proper format
630 bmp = bm_lock(bitmap_handle, 16, flags);
632 mprintf(("Couldn't lock bitmap %d.\n", bitmap_handle ));
635 // determine the width and height of this section
636 bm_get_section_size(bitmap_handle, sx, sy, §ion_x, §ion_y);
638 // get final texture size as it will be allocated as an opengl texture
639 opengl1_tcache_get_adjusted_texture_size(section_x, section_y, &final_w, &final_h);
641 if ( (final_w < 1) || (final_h < 1) ) {
642 mprintf(("Bitmap is too small at %dx%d.\n", final_w, final_h));
646 // if we don't have to resize the image (to get power of 2, etc.) then skip that extra work
647 if ( (bmp->sections.num_x == 1) && (bmp->sections.num_y == 1) && (section_x == final_w) && (section_y == final_h) ) {
651 // if this tcache slot has no bitmap
652 if ( tslot->bitmap_id < 0) {
655 // different bitmap altogether - determine if the new one can use the old one's slot
656 else if (tslot->bitmap_id != bitmap_handle) {
657 if((final_w == tslot->w) && (final_h == tslot->h)){
665 int ret_val = opengl1_create_texture_sub(bitmap_type, bitmap_handle, (ushort*)bmp->data, bmp->sections.sx[sx], bmp->sections.sy[sy], section_x, section_y, bmp->w, bmp->h, final_w, final_h, tslot, reload, resize, fail_on_full);
668 bm_unlock(bitmap_handle);
673 int opengl1_tcache_set(int bitmap_id, int bitmap_type, float *u_scale, float *v_scale, int fail_on_full, int sx, int sy, int force)
682 GL_last_bitmap_id = -1;
686 if ( GL_last_detail != Detail.hardware_textures ) {
687 GL_last_detail = Detail.hardware_textures;
688 opengl1_tcache_flush();
695 int n = bm_get_cache_slot (bitmap_id, 1);
696 tcache_slot_opengl *t = &Textures[n];
698 if ( (GL_last_bitmap_id == bitmap_id) && (GL_last_bitmap_type==bitmap_type) && (t->bitmap_id == bitmap_id) && (GL_last_section_x == sx) && (GL_last_section_y == sy)) {
699 t->used_this_frame = GL_frame_count;
701 // mark all children as used
702 if (gr_screen.use_sections) {
703 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
704 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
705 if(t->data_sections[idx][s_idx] != NULL){
706 t->data_sections[idx][s_idx]->used_this_frame = GL_frame_count;
712 *u_scale = t->u_scale;
713 *v_scale = t->v_scale;
717 if (bitmap_type == TCACHE_TYPE_BITMAP_SECTION){
718 SDL_assert( gr_screen.use_sections );
719 SDL_assert((sx >= 0) && (sy >= 0) && (sx < MAX_BMAP_SECTIONS_X) && (sy < MAX_BMAP_SECTIONS_Y));
720 if(!((sx >= 0) && (sy >= 0) && (sx < MAX_BMAP_SECTIONS_X) && (sy < MAX_BMAP_SECTIONS_Y))){
726 // if the texture sections haven't been created yet
727 if((t->bitmap_id < 0) || (t->bitmap_id != bitmap_id)){
729 // lock the bitmap in the proper format
730 bmp = bm_lock(bitmap_id, 16, BMP_TEX_XPARENT);
731 bm_unlock(bitmap_id);
733 // now lets do something for each texture
735 for(idx=0; idx<bmp->sections.num_x; idx++){
736 for(s_idx=0; s_idx<bmp->sections.num_y; s_idx++){
737 // hmm. i'd rather we didn't have to do it this way...
738 if(!opengl1_create_texture_sectioned(bitmap_id, bitmap_type, t->data_sections[idx][s_idx], idx, s_idx, fail_on_full)){
742 // not used this frame
743 t->data_sections[idx][s_idx]->used_this_frame = 0;
747 // zero out pretty much everything in the parent struct since he's just the root
748 t->bitmap_id = bitmap_id;
749 t->texture_handle = 0;
750 t->time_created = t->data_sections[sx][sy]->time_created;
751 t->used_this_frame = 0;
754 // argh. we failed to upload. free anything we can
756 opengl1_free_texture(t);
758 // swap in the texture we want
760 t = t->data_sections[sx][sy];
763 // all other "normal" textures
764 else if((bitmap_id < 0) || (bitmap_id != t->bitmap_id)){
765 ret_val = opengl1_create_texture( bitmap_id, bitmap_type, t, fail_on_full );
768 // everything went ok
769 if(ret_val && (t->texture_handle) && !vram_full){
770 *u_scale = t->u_scale;
771 *v_scale = t->v_scale;
773 GL_bound_texture = t;
775 glBindTexture (GL_TEXTURE_2D, t->texture_handle );
777 /* this should be set next anyway */
778 // if (GL_current_texture_source != TEXTURE_SOURCE_NONE) {
779 // gr_opengl_set_texture_state(GL_current_texture_source);
782 GL_last_bitmap_id = t->bitmap_id;
783 GL_last_bitmap_type = bitmap_type;
784 GL_last_section_x = sx;
785 GL_last_section_y = sy;
787 t->used_this_frame = GL_frame_count;
791 GL_last_bitmap_id = -1;
792 GL_last_bitmap_type = -1;
794 GL_last_section_x = -1;
795 GL_last_section_y = -1;
797 GL_bound_texture = NULL;
799 glBindTexture (GL_TEXTURE_2D, 0); // test - DDOI
806 void gr_opengl1_preload_init()
808 // if (gr_screen.mode != GR_OPENGL) {
812 opengl1_tcache_flush();
815 int gr_opengl1_preload(int bitmap_num, int is_aabitmap)
817 // if ( gr_screen.mode != GR_OPENGL) {
821 if ( !GL_should_preload ) {
825 float u_scale, v_scale;
827 int bitmap_type = TCACHE_TYPE_NORMAL;
830 bitmap_type = TCACHE_TYPE_AABITMAP;
833 retval = opengl1_tcache_set(bitmap_num, bitmap_type, &u_scale, &v_scale, 1, -1, -1, 0 );
836 mprintf(("Texture upload failed!\n" ));
842 void gr_opengl1_set_gamma(float)
846 // set the alpha gamma settings (for fonts)
847 for (i = 0; i < 16; i++) {
848 GL_xlat[i] = (ubyte)Gr_gamma_lookup[(i*255)/15];
851 GL_xlat[15] = GL_xlat[1];
853 // Flush any existing textures
854 opengl1_tcache_flush();
857 void gr_opengl1_release_texture(int handle)
859 for(int i=0; i<MAX_BITMAPS; i++ ) {
860 if (Textures[i].bitmap_id == handle) {
861 Textures[i].used_this_frame = 0; // this bmp doesn't even exist any longer...
862 opengl1_free_texture( &Textures[i] );