1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
4
5 /**
6 * @file opengl_tex.c
7 *
8 * @brief This file handles the opengl texture wrapper routines.
9 */
10
11
12 #include "opengl.h"
13
14 #include "naev.h"
15
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include "nstring.h"
20
21 #include "log.h"
22 #include "ndata.h"
23 #include "nfile.h"
24 #include "gui.h"
25 #include "conf.h"
26 #include "npng.h"
27 #include "md5.h"
28
29
30 /*
31 * graphic list
32 */
33 /**
34 * @brief Represents a node in the texture list.
35 */
36 typedef struct glTexList_ {
37 struct glTexList_ *next; /**< Next in linked list */
38 glTexture *tex; /**< associated texture */
39 int used; /**< counts how many times texture is being used */
40 } glTexList;
41 static glTexList* texture_list = NULL; /**< Texture list. */
42
43
44 /*
45 * Extensions.
46 */
47 static int gl_tex_ext_npot = 0; /**< Support for GL_ARB_texture_non_power_of_two. */
48
49
50 /*
51 * prototypes
52 */
53 /* misc */
54 /*static int SDL_VFlipSurface( SDL_Surface* surface );*/
55 static int SDL_IsTrans( SDL_Surface* s, int x, int y );
56 static uint8_t* SDL_MapTrans( SDL_Surface* s, int w, int h );
57 static size_t gl_transSize( const int w, const int h );
58 /* glTexture */
59 static GLuint gl_loadSurface( SDL_Surface* surface, int *rw, int *rh, unsigned int flags, int freesur );
60 static glTexture* gl_loadNewImage( const char* path, unsigned int flags );
61 /* List. */
62 static glTexture* gl_texExists( const char* path );
63 static int gl_texAdd( glTexture *tex );
64
65
66 /**
67 * @brief Gets the closest power of two.
68 * @param n Number to get closest power of two to.
69 * @return Closest power of two to the number.
70 */
gl_pot(int n)71 int gl_pot( int n )
72 {
73 int i = 1;
74 while (i < n)
75 i <<= 1;
76 return i;
77 }
78
79
80 #if 0
81 /**
82 * @brief Flips the surface vertically.
83 *
84 * @param surface Surface to flip.
85 * @return 0 on success.
86 */
87 static int SDL_VFlipSurface( SDL_Surface* surface )
88 {
89 /* flip the image */
90 Uint8 *rowhi, *rowlo, *tmpbuf;
91 int y;
92
93 tmpbuf = malloc(surface->pitch);
94 if ( tmpbuf == NULL ) {
95 WARN("Out of memory");
96 return -1;
97 }
98
99 rowhi = (Uint8 *)surface->pixels;
100 rowlo = rowhi + (surface->h * surface->pitch) - surface->pitch;
101 for (y = 0; y < surface->h / 2; ++y ) {
102 memcpy(tmpbuf, rowhi, surface->pitch);
103 memcpy(rowhi, rowlo, surface->pitch);
104 memcpy(rowlo, tmpbuf, surface->pitch);
105 rowhi += surface->pitch;
106 rowlo -= surface->pitch;
107 }
108 free(tmpbuf);
109 /* flipping done */
110
111 return 0;
112 }
113 #endif
114
115
116 /**
117 * @brief Checks to see if a position of the surface is transparent.
118 *
119 * @param s Surface to check for transparency.
120 * @param x X position of the pixel to check.
121 * @param y Y position of the pixel to check.
122 * @return 0 if the pixel isn't transparent, 0 if it is.
123 */
SDL_IsTrans(SDL_Surface * s,int x,int y)124 static int SDL_IsTrans( SDL_Surface* s, int x, int y )
125 {
126 int bpp;
127 Uint8 *p;
128 Uint32 pixelcolour;
129
130 bpp = s->format->BytesPerPixel;
131 /* here p is the address to the pixel we want to retrieve */
132 p = (Uint8 *)s->pixels + y*s->pitch + x*bpp;
133
134 pixelcolour = 0;
135 switch(bpp) {
136 case 1:
137 pixelcolour = *p;
138 break;
139
140 case 2:
141 pixelcolour = *(Uint16 *)p;
142 break;
143
144 case 3:
145 #if HAS_BIGENDIAN
146 pixelcolour = p[0] << 16 | p[1] << 8 | p[2];
147 #else /* HAS_BIGENDIAN */
148 pixelcolour = p[0] | p[1] << 8 | p[2] << 16;
149 #endif /* HAS_BIGENDIAN */
150 break;
151
152 case 4:
153 pixelcolour = *(Uint32 *)p;
154 break;
155 }
156
157 /* test whether pixels colour == colour of transparent pixels for that surface */
158 return ((pixelcolour & s->format->Amask) < (Uint32)(0.1*(double)s->format->Amask));
159 }
160
161
162 /**
163 * @brief Maps the surface transparency.
164 *
165 * Basically generates a map of what pixels are transparent. Good for pixel
166 * perfect collision routines.
167 *
168 * @param s Surface to map it's transparency.
169 * @param w Width to map.
170 * @param h Height to map.
171 * @return 0 on success.
172 */
SDL_MapTrans(SDL_Surface * s,int w,int h)173 static uint8_t* SDL_MapTrans( SDL_Surface* s, int w, int h )
174 {
175 int i,j;
176 size_t size;
177 uint8_t *t;
178
179 /* Get limit.s */
180 if (w < 0)
181 w = s->w;
182 if (h < 0)
183 h = s->h;
184
185 /* alloc memory for just enough bits to hold all the data we need */
186 size = gl_transSize(w, h);
187 t = malloc(size);
188 if (t==NULL) {
189 WARN("Out of Memory");
190 return NULL;
191 }
192 memset(t, 0, size); /* important, must be set to zero */
193
194 /* Check each pixel individually. */
195 for (i=0; i<h; i++)
196 for (j=0; j<w; j++) /* sets each bit to be 1 if not transparent or 0 if is */
197 t[(i*w+j)/8] |= (SDL_IsTrans(s,j,i)) ? 0 : (1<<((i*w+j)%8));
198
199 return t;
200 }
201
202
203 /*
204 * @brief Gets the size needed for a transparency map.
205 *
206 * @param w Width of the image.
207 * @param h Height of the image.
208 * @return The size in bytes.
209 */
gl_transSize(const int w,const int h)210 static size_t gl_transSize( const int w, const int h )
211 {
212 /* One bit per pixel, plus remainder. */
213 return w*h/8 + ((w*h%8)?1:0);
214 }
215
216
217 /**
218 * @brief Prepares the surface to be loaded as a texture.
219 *
220 * @param surface to load that is freed in the process.
221 * @return New surface that is prepared for texture loading.
222 */
gl_prepareSurface(SDL_Surface * surface)223 SDL_Surface* gl_prepareSurface( SDL_Surface* surface )
224 {
225 SDL_Surface* temp;
226 int potw, poth;
227 SDL_Rect rtemp;
228 #if ! SDL_VERSION_ATLEAST(1,3,0)
229 Uint32 saved_flags;
230 #endif /* ! SDL_VERSION_ATLEAST(1,3,0) */
231
232 /* Make size power of two. */
233 potw = gl_pot(surface->w);
234 poth = gl_pot(surface->h);
235 if (gl_needPOT() && ((potw != surface->w) || (poth != surface->h))) {
236
237 /* we must blit with an SDL_Rect */
238 rtemp.x = rtemp.y = 0;
239 rtemp.w = surface->w;
240 rtemp.h = surface->h;
241
242 /* saves alpha */
243 #if SDL_VERSION_ATLEAST(1,3,0)
244 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
245
246 /* create the temp POT surface */
247 temp = SDL_CreateRGBSurface( 0, potw, poth,
248 surface->format->BytesPerPixel*8, RGBAMASK );
249 #else /* SDL_VERSION_ATLEAST(1,3,0) */
250 saved_flags = surface->flags & (SDL_SRCALPHA | SDL_RLEACCELOK);
251 if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA) {
252 SDL_SetAlpha( surface, 0, SDL_ALPHA_OPAQUE );
253 SDL_SetColorKey( surface, 0, surface->format->colorkey );
254 }
255
256 /* create the temp POT surface */
257 temp = SDL_CreateRGBSurface( SDL_SRCCOLORKEY,
258 potw, poth, surface->format->BytesPerPixel*8, RGBAMASK );
259 #endif /* SDL_VERSION_ATLEAST(1,3,0) */
260
261 if (temp == NULL) {
262 WARN("Unable to create POT surface: %s", SDL_GetError());
263 return 0;
264 }
265 if (SDL_FillRect( temp, NULL,
266 SDL_MapRGBA(surface->format,0,0,0,SDL_ALPHA_TRANSPARENT))) {
267 WARN("Unable to fill rect: %s", SDL_GetError());
268 return 0;
269 }
270
271 /* change the surface to the new blitted one */
272 SDL_BlitSurface( surface, &rtemp, temp, &rtemp);
273 SDL_FreeSurface( surface );
274 surface = temp;
275
276 #if ! SDL_VERSION_ATLEAST(1,3,0)
277 /* set saved alpha */
278 if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA )
279 SDL_SetAlpha( surface, 0, 0 );
280 #endif /* ! SDL_VERSION_ATLEAST(1,3,0) */
281 }
282
283 return surface;
284 }
285
286 /**
287 * @brief Checks to see if mipmaps are supported and enabled.
288 *
289 * @return 1 if mipmaps are supported and enabled.
290 */
gl_texHasMipmaps(void)291 int gl_texHasMipmaps (void)
292 {
293 return (nglGenerateMipmap != NULL);
294 }
295
296 /**
297 * @brief Checks to see if texture compression is available and enabled.
298 *
299 * @return 1 if texture compression is available and enabled.
300 */
gl_texHasCompress(void)301 int gl_texHasCompress (void)
302 {
303 return (nglCompressedTexImage2D != NULL);
304 }
305
306 /**
307 * @brief Loads a surface into an opengl texture.
308 *
309 * @param surface Surface to load into a texture.
310 * @param flags Flags to use.
311 * @param[out] rw Real width of the texture.
312 * @param[out] rh Real height of the texture.
313 * @return The opengl texture id.
314 */
gl_loadSurface(SDL_Surface * surface,int * rw,int * rh,unsigned int flags,int freesur)315 static GLuint gl_loadSurface( SDL_Surface* surface, int *rw, int *rh, unsigned int flags, int freesur )
316 {
317 GLuint texture;
318 GLfloat param;
319
320 /* Prepare the surface. */
321 surface = gl_prepareSurface( surface );
322 if (rw != NULL)
323 (*rw) = surface->w;
324 if (rh != NULL)
325 (*rh) = surface->h;
326
327 /* opengl texture binding */
328 glGenTextures( 1, &texture ); /* Creates the texture */
329 glBindTexture( GL_TEXTURE_2D, texture ); /* Loads the texture */
330
331 /* Filtering, LINEAR is better for scaling, nearest looks nicer, LINEAR
332 * also seems to create a bit of artifacts around the edges */
333 if ((gl_screen.scale != 1.) || (flags & OPENGL_TEX_MIPMAPS)) {
334 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
335 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
336 }
337 else {
338 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
339 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
340 }
341
342 /* Always wrap just in case. */
343 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
344 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
345
346 /* now lead the texture data up */
347 SDL_LockSurface( surface );
348 if (gl_texHasCompress()) {
349 glTexImage2D( GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA,
350 surface->w, surface->h, 0, GL_RGBA,
351 GL_UNSIGNED_BYTE, surface->pixels );
352 }
353 else {
354 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA,
355 surface->w, surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels );
356 }
357 SDL_UnlockSurface( surface );
358
359 /* Create mipmaps. */
360 if ((flags & OPENGL_TEX_MIPMAPS) && gl_texHasMipmaps()) {
361 /* Do fancy stuff. */
362 if (gl_hasExt("GL_EXT_texture_filter_anisotropic")) {
363 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, ¶m);
364 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, param);
365 }
366 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
367 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 9);
368
369 /* Now generate the mipmaps. */
370 nglGenerateMipmap(GL_TEXTURE_2D);
371 }
372
373 /* cleanup */
374 if (freesur)
375 SDL_FreeSurface( surface );
376 gl_checkErr();
377
378 return texture;
379 }
380
381
382 /**
383 * @brief Wrapper for gl_loadImagePad that includes transparency mapping.
384 *
385 * @param name Name to load with.
386 * @param surface Surface to load.
387 * @param rw RWops containing data to hash.
388 * @param flags Flags to use.
389 * @param w Non-padded width.
390 * @param h Non-padded height.
391 * @param sx X sprites.
392 * @param sy Y sprites.
393 * @param freesur Whether or not to free the surface.
394 * @return The glTexture for surface.
395 */
gl_loadImagePadTrans(const char * name,SDL_Surface * surface,SDL_RWops * rw,unsigned int flags,int w,int h,int sx,int sy,int freesur)396 glTexture* gl_loadImagePadTrans( const char *name, SDL_Surface* surface, SDL_RWops *rw,
397 unsigned int flags, int w, int h, int sx, int sy, int freesur )
398 {
399 glTexture *texture;
400 int i, filesize;
401 size_t cachesize, pngsize;
402 uint8_t *trans;
403 char *cachefile, *data;
404 char digest[33];
405 md5_state_t md5;
406 md5_byte_t *md5val;
407
408 if (name != NULL) {
409 texture = gl_texExists( name );
410 if (texture != NULL)
411 return texture;
412 }
413
414 if (flags & OPENGL_TEX_MAPTRANS)
415 flags ^= OPENGL_TEX_MAPTRANS;
416
417 /* Appropriate size for the transparency map, see SDL_MapTrans */
418 cachesize = gl_transSize(w, h);
419
420 cachefile = NULL;
421 trans = NULL;
422
423 if (rw != NULL) {
424 md5val = malloc(16);
425 md5_init(&md5);
426
427 pngsize = SDL_RWseek( rw, 0, SEEK_END );
428 SDL_RWseek( rw, 0, SEEK_SET );
429
430 data = malloc(pngsize);
431 if (data == NULL)
432 WARN("Out of memory!");
433 else {
434 SDL_RWread( rw, data, pngsize, 1 );
435 md5_append( &md5, (md5_byte_t*)data, pngsize );
436 free(data);
437 }
438 md5_finish( &md5, md5val );
439
440 for (i=0; i<16; i++)
441 nsnprintf( &digest[i * 2], 3, "%02x", md5val[i] );
442 free(md5val);
443
444 cachefile = malloc( PATH_MAX );
445 nsnprintf( cachefile, PATH_MAX, "%scollisions/%s",
446 nfile_cachePath(), digest );
447
448 /* Attempt to find a cached transparency map. */
449 if (nfile_fileExists(cachefile)) {
450 trans = (uint8_t*)nfile_readFile( &filesize, cachefile );
451
452 /* Consider cached data invalid if the length doesn't match. */
453 if (trans != NULL && cachesize != (unsigned int)filesize) {
454 free(trans);
455 trans = NULL;
456 }
457 /* Cached data matches, no need to overwrite. */
458 else {
459 free(cachefile);
460 cachefile = NULL;
461 }
462 }
463 }
464 else {
465 /* We could hash raw pixel data here, but that's slower than just
466 * generating the map from scratch.
467 */
468 WARN("Texture '%s' has no RWops", name);
469 }
470
471 if (trans == NULL) {
472 SDL_LockSurface(surface);
473 trans = SDL_MapTrans( surface, w, h );
474 SDL_UnlockSurface(surface);
475
476 if (cachefile != NULL) {
477 /* Cache newly-generated transparency map. */
478 nfile_dirMakeExist( "%s/collisions/", nfile_cachePath() );
479 nfile_writeFile( (char*)trans, cachesize, cachefile );
480 free(cachefile);
481 }
482 }
483
484 texture = gl_loadImagePad( name, surface, flags, w, h, sx, sy, freesur );
485 texture->trans = trans;
486 return texture;
487 }
488
489
490 /**
491 * @brief Loads the already padded SDL_Surface to a glTexture.
492 *
493 * @param name Name to load with.
494 * @param surface Surface to load.
495 * @param flags Flags to use.
496 * @param w Non-padded width.
497 * @param h Non-padded height.
498 * @param sx X sprites.
499 * @param sy Y sprites.
500 * @param freesur Whether or not to free the surface.
501 * @return The glTexture for surface.
502 */
gl_loadImagePad(const char * name,SDL_Surface * surface,unsigned int flags,int w,int h,int sx,int sy,int freesur)503 glTexture* gl_loadImagePad( const char *name, SDL_Surface* surface,
504 unsigned int flags, int w, int h, int sx, int sy, int freesur )
505 {
506 glTexture *texture;
507 int rw, rh;
508
509 /* Make sure doesn't already exist. */
510 if (name != NULL) {
511 texture = gl_texExists( name );
512 if (texture != NULL)
513 return texture;
514 }
515
516 if (flags & OPENGL_TEX_MAPTRANS)
517 return gl_loadImagePadTrans( name, surface, NULL, flags, w, h,
518 sx, sy, freesur );
519
520 /* set up the texture defaults */
521 texture = calloc( 1, sizeof(glTexture) );
522
523 texture->w = (double) w;
524 texture->h = (double) h;
525 texture->sx = (double) sx;
526 texture->sy = (double) sy;
527
528 texture->texture = gl_loadSurface( surface, &rw, &rh, flags, freesur );
529
530 texture->rw = (double) rw;
531 texture->rh = (double) rh;
532 texture->sw = texture->w / texture->sx;
533 texture->sh = texture->h / texture->sy;
534 texture->srw = texture->sw / texture->rw;
535 texture->srh = texture->sh / texture->rh;
536
537 if (name != NULL) {
538 texture->name = strdup(name);
539 gl_texAdd( texture );
540 }
541 else
542 texture->name = NULL;
543
544 return texture;
545 }
546
547
548 /**
549 * @brief Loads the SDL_Surface to a glTexture.
550 *
551 * @param surface Surface to load.
552 * @param flags Flags to use.
553 * @return The glTexture for surface.
554 */
gl_loadImage(SDL_Surface * surface,unsigned int flags)555 glTexture* gl_loadImage( SDL_Surface* surface, unsigned int flags )
556 {
557 return gl_loadImagePad( NULL, surface, flags, surface->w, surface->h, 1, 1, 1 );
558 }
559
560
561 /**
562 * @brief Check to see if a texture matching a path already exists.
563 *
564 * @param path Path to the texture.
565 * @return The texture, or NULL if none was found.
566 */
gl_texExists(const char * path)567 static glTexture* gl_texExists( const char* path )
568 {
569 glTexList *cur;
570
571 /* check to see if it already exists */
572 if (texture_list != NULL) {
573 for (cur=texture_list; cur!=NULL; cur=cur->next) {
574 if (strcmp(path,cur->tex->name)==0) {
575 cur->used += 1;
576 return cur->tex;
577 }
578 }
579 }
580
581 return NULL;
582 }
583
584
585 /**
586 * @brief Adds a texture to the list under the name of path.
587 */
gl_texAdd(glTexture * tex)588 static int gl_texAdd( glTexture *tex )
589 {
590 glTexList *new, *cur, *last;
591
592 /* Create the new node */
593 new = malloc( sizeof(glTexList) );
594 new->next = NULL;
595 new->used = 1;
596 new->tex = tex;
597
598 if (texture_list == NULL) /* special condition - creating new list */
599 texture_list = new;
600 else {
601 for (cur=texture_list; cur!=NULL; cur=cur->next)
602 last = cur;
603
604 last->next = new;
605 }
606
607 return 0;
608 }
609
610
611 /**
612 * @brief Loads an image as a texture.
613 *
614 * May not necessarily load the image but use one if it's already open.
615 *
616 * @param path Image to load.
617 * @param flags Flags to control image parameters.
618 * @return Texture loaded from image.
619 */
gl_newImage(const char * path,const unsigned int flags)620 glTexture* gl_newImage( const char* path, const unsigned int flags )
621 {
622 glTexture *t;
623
624 /* Check if it already exists. */
625 t = gl_texExists( path );
626 if (t != NULL)
627 return t;
628
629 /* Load the image */
630 return gl_loadNewImage( path, flags );
631 }
632
633
634 /**
635 * @brief Only loads the image, does not add to stack unlike gl_newImage.
636 *
637 * @param path Image to load.
638 * @param flags Flags to control image parameters.
639 * @return Texture loaded from image.
640 */
gl_loadNewImage(const char * path,const unsigned int flags)641 static glTexture* gl_loadNewImage( const char* path, const unsigned int flags )
642 {
643 glTexture *texture;
644 SDL_Surface *surface;
645 SDL_RWops *rw;
646 npng_t *npng;
647 png_uint_32 w, h;
648 int sx, sy;
649 char *str;
650 int len;
651
652 /* load from packfile */
653 rw = ndata_rwops( path );
654 if (rw == NULL) {
655 WARN("Failed to load surface '%s' from ndata.", path);
656 return NULL;
657 }
658 npng = npng_open( rw );
659 if (npng == NULL) {
660 WARN("File '%s' is not a png.", path );
661 return NULL;
662 }
663 npng_dim( npng, &w, &h );
664
665 /* Process metadata. */
666 len = npng_metadata( npng, "sx", &str );
667 sx = (len > 0) ? atoi(str) : 1;
668 len = npng_metadata( npng, "sy", &str );
669 sy = (len > 0) ? atoi(str) : 1;
670
671 /* Load surface. */
672 surface = npng_readSurface( npng, gl_needPOT(), 1 );
673 npng_close( npng );
674
675 if (surface == NULL) {
676 WARN("'%s' could not be opened", path );
677 SDL_RWclose( rw );
678 return NULL;
679 }
680
681 if (flags & OPENGL_TEX_MAPTRANS)
682 texture = gl_loadImagePadTrans( path, surface, rw, flags, w, h, sx, sy, 1 );
683 else
684 texture = gl_loadImagePad( path, surface, flags, w, h, sx, sy, 1 );
685
686 SDL_RWclose( rw );
687 return texture;
688 }
689
690
691 /**
692 * @brief Loads the texture immediately, but also sets it as a sprite.
693 *
694 * @param path Image to load.
695 * @param sx Number of X sprites in image.
696 * @param sy Number of Y sprites in image.
697 * @param flags Flags to control image parameters.
698 * @return Texture loaded.
699 */
gl_newSprite(const char * path,const int sx,const int sy,const unsigned int flags)700 glTexture* gl_newSprite( const char* path, const int sx, const int sy,
701 const unsigned int flags )
702 {
703 glTexture* texture;
704 texture = gl_newImage( path, flags );
705 if (texture == NULL)
706 return NULL;
707
708 /* will possibly overwrite an existing textur properties
709 * so we have to load same texture always the same sprites */
710 texture->sx = (double) sx;
711 texture->sy = (double) sy;
712 texture->sw = texture->w / texture->sx;
713 texture->sh = texture->h / texture->sy;
714 texture->srw = texture->sw / texture->rw;
715 texture->srh = texture->sh / texture->rh;
716 return texture;
717 }
718
719
720 /**
721 * @brief Frees a texture.
722 *
723 * @param texture Texture to free.
724 */
gl_freeTexture(glTexture * texture)725 void gl_freeTexture( glTexture* texture )
726 {
727 glTexList *cur, *last;
728
729 /* Shouldn't be NULL (won't segfault though) */
730 if (texture == NULL) {
731 WARN("Attempting to free NULL texture!");
732 return;
733 }
734
735 /* see if we can find it in stack */
736 last = NULL;
737 for (cur=texture_list; cur!=NULL; cur=cur->next) {
738 if (cur->tex == texture) { /* found it */
739 cur->used--;
740 if (cur->used <= 0) { /* not used anymore */
741 /* free the texture */
742 glDeleteTextures( 1, &texture->texture );
743 if (texture->trans != NULL)
744 free(texture->trans);
745 if (texture->name != NULL)
746 free(texture->name);
747 free(texture);
748
749 /* free the list node */
750 if (last == NULL) { /* case there's no texture before it */
751 if (cur->next != NULL)
752 texture_list = cur->next;
753 else /* case it's the last texture */
754 texture_list = NULL;
755 }
756 else
757 last->next = cur->next;
758 free(cur);
759 }
760 return; /* we already found it so we can exit */
761 }
762 last = cur;
763 }
764
765 /* Not found */
766 if (texture->name != NULL) /* Surfaces will have NULL names */
767 WARN("Attempting to free texture '%s' not found in stack!", texture->name);
768
769 /* Free anyways */
770 glDeleteTextures( 1, &texture->texture );
771 if (texture->trans != NULL)
772 free(texture->trans);
773 if (texture->name != NULL)
774 free(texture->name);
775 free(texture);
776
777 gl_checkErr();
778 }
779
780
781 /**
782 * @brief Duplicates a texture.
783 *
784 * @param texture Texture to duplicate.
785 * @return Duplicate of texture.
786 */
gl_dupTexture(glTexture * texture)787 glTexture* gl_dupTexture( glTexture *texture )
788 {
789 glTexList *cur;
790
791 /* No segfaults kthxbye. */
792 if (texture == NULL)
793 return NULL;
794
795 /* check to see if it already exists */
796 if (texture_list != NULL) {
797 for (cur=texture_list; cur!=NULL; cur=cur->next) {
798 if (texture == cur->tex) {
799 cur->used += 1;
800 return cur->tex;
801 }
802 }
803 }
804
805 /* Invalid texture. */
806 WARN("Unable to duplicate texture '%s'.", texture->name);
807 return NULL;
808 }
809
810
811 /**
812 * @brief Checks to see if a pixel is transparent in a texture.
813 *
814 * @param t Texture to check for transparency.
815 * @param x X position of the pixel.
816 * @param y Y position of the pixel.
817 * @return 1 if the pixel is transparent or 0 if it isn't.
818 */
gl_isTrans(const glTexture * t,const int x,const int y)819 int gl_isTrans( const glTexture* t, const int x, const int y )
820 {
821 int i;
822
823 /* Get the position in the sheet. */
824 i = y*(int)(t->w) + x ;
825 /* Now we have to pull out the individual bit. */
826 return !(t->trans[ i/8 ] & (1 << (i%8)));
827 }
828
829
830 /**
831 * @brief Sets x and y to be the appropriate sprite for glTexture using dir.
832 *
833 * Very slow, try to cache if possible like the pilots do instead of using
834 * in O(n^2) or worse functions.
835 *
836 * @param[out] x X sprite to use.
837 * @param[out] y Y sprite to use.
838 * @param t Texture to get sprite from.
839 * @param dir Direction to get sprite from.
840 */
gl_getSpriteFromDir(int * x,int * y,const glTexture * t,const double dir)841 void gl_getSpriteFromDir( int* x, int* y, const glTexture* t, const double dir )
842 {
843 int s, sx, sy;
844 double shard, rdir;
845
846 #ifdef DEBUGGING
847 if ((dir > 2.*M_PI) || (dir < 0.)) {
848 WARN("Angle not between 0 and 2.*M_PI [%f].", dir);
849 return;
850 }
851 #endif /* DEBUGGING */
852
853 /* what each image represents in angle */
854 shard = 2.*M_PI / (t->sy*t->sx);
855
856 /* real dir is slightly moved downwards */
857 rdir = dir + shard/2.;
858
859 /* now calculate the sprite we need */
860 s = (int)(rdir / shard);
861 sx = t->sx;
862 sy = t->sy;
863
864 /* makes sure the sprite is "in range" */
865 if (s > (sy*sx-1))
866 s = s % (sy*sx);
867
868 (*x) = s % sx;
869 (*y) = s / sx;
870 }
871
872
873 /**
874 * @brief Checks to see if OpenGL needs POT textures.
875 *
876 * @return 0 if OpenGL doesn't needs POT textures.
877 */
gl_needPOT(void)878 int gl_needPOT (void)
879 {
880 if (gl_tex_ext_npot && conf.npot)
881 return 0;
882 else
883 return 1;
884 }
885
886
887 /**
888 * @brief Initializes the opengl texture subsystem.
889 *
890 * @return 0 on success.
891 */
gl_initTextures(void)892 int gl_initTextures (void)
893 {
894 if (gl_hasVersion(2,0) || gl_hasExt("GL_ARB_texture_non_power_of_two"))
895 gl_tex_ext_npot = 1;
896
897 return 0;
898 }
899
900
901 /**
902 * @brief Cleans up the opengl texture subsystem.
903 */
gl_exitTextures(void)904 void gl_exitTextures (void)
905 {
906 glTexList *tex;
907
908 /* Make sure there's no texture leak */
909 if (texture_list != NULL) {
910 DEBUG("Texture leak detected!");
911 for (tex=texture_list; tex!=NULL; tex=tex->next)
912 DEBUG(" '%s' opened %d times", tex->tex->name, tex->used );
913 }
914 }
915
916