A Texture Manager

Started by
2 comments, last by alargeduck 21 years, 10 months ago
Im proud of this one, check it out.
    
/*******************************************************************************

                           Engine2 Image Handling

*******************************************************************************/

#include <stdio.h>
#include <string.h>

#include <SDL_opengl.h>

#include "engine2.h"
#include "console.h"

#include "r_image.h"

/*
  FIXME:

  Textures are now unloaded whenever noone is using them anymore. This works
  great when textures are being loaded when the program starts, and freed on
  exit. When doing loading and unloading on the fly, like level switches or
  whatever, textures shouldnt be unloaded unless they absolutely need to be.

  That is, only unload the texture if some other texture needs to take its place.

  Also, use strcasecmp() in place of strcmp(). Not used right now because
  aparently strcasecmp() isnt a standard library function. 
*/

IMAGE * img_root = 0;
GLuint current_texture = 0;

/* Thank you NeHe! */
int LoadTGA(char * filename, IMAGE * img)
	{
	unsigned int i;

	unsigned char TGAHeader[12] = {0,0,2,0,0,0,0,0,0,0,0,0};
	unsigned char TGACompare[12], header[6];
	unsigned int size;
	int temp;
	
	FILE * file;

	file = fopen(filename, "rb");
	if(!file) return 0;

	if(fread(TGACompare, 1, sizeof(TGACompare), file) != sizeof(TGAHeader)) { fclose(file); return 0; }
	if(memcmp(TGAHeader, TGACompare, sizeof(TGAHeader))) { fclose(file); return 0; }
	if(fread(header, 1, sizeof(header), file) != sizeof(header)) { fclose(file); return 0; }

	img->width = header[1] * 256 + header[0];
	img->height = header[3] * 256 + header[2];
	img->bpp = header[4];

	if(img->width <= 0 || img->height <= 0 || (img->bpp != 32 && img->bpp != 24))
		{ fclose(file);	return 0; }

	size = img->width * img->height * (img->bpp/8);	

	img->data = E2_Malloc(size);

	if(!img->data) { fclose(file); return 0; }
	if(fread(img->data, 1, size, file) != size)
		{
		E2_Free(img->data);
		fclose(file);
		return 0;
		}

	fclose(file);

	for(i=0; i<size; i += (img->bpp/8))
		{
		temp = img->data;
		img-&gt;data = img-&gt;data;
		img-&gt;data = temp;
		}

	return 1;
	}

void DefaultImage(IMAGE * image)
	{
	image-&gt;width = image-&gt;height = 32;
	image-&gt;bpp = 32;

	if(image-&gt;data) E2_Free(image-&gt;data);
	image-&gt;data = E2_Malloc(32 * 32 * 4);

	memset(image-&gt;data, 128, 32 * 32 * 4);
	}

void BuildImage(IMAGE * image)
	{
	if(!image-&gt;data) return; /* allready built or no data */
	
	glGenTextures(1, &image-&gt;id);
		
	R_Use_Image(image);
	
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, image-&gt;min);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, image-&gt;max);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, image-&gt;wraps);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, image-&gt;wrapt);

	if(image-&gt;bpp == 32)
		gluBuild2DMipmaps(GL_TEXTURE_2D, 4, image-&gt;width, image-&gt;height,
		                  GL_RGBA, GL_UNSIGNED_BYTE, image-&gt;data);	
	else
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image-&gt;width, image-&gt;height,
		                  GL_RGB, GL_UNSIGNED_BYTE, image-&gt;data);

	E2_Free(image-&gt;data);
	image-&gt;data = 0;
	}

/* This always returns a valid image */
IMAGE * R_CreateImage(char * name, int minf, int maxf, int wraps, int wrapt)
	{
	IMAGE * image;

	image = E2_Malloc(sizeof(IMAGE));

	image-&gt;name = E2_Malloc((strlen(name) + 1) * sizeof(char));
	strcpy(image-&gt;name, name);
	
	image-&gt;min = minf;
	image-&gt;max = maxf;
	image-&gt;wraps = wraps;
	image-&gt;wrapt = wrapt;

	if(!LoadTGA(name, image))
		{
		E2_Errorf("Couldnt find image '%s'\n", image-&gt;name);
		DefaultImage(image);
		}

	BuildImage(image);

	return image;
	}

void R_AttachImage(IMAGE * image)
	{
	/* Attach image to the list */

	image-&gt;next = img_root;
	image-&gt;prev = 0;

	/* True unless first and only texture in list */
	if(image-&gt;next) image-&gt;next-&gt;prev = image;

	img_root = image;	
	}

IMAGE * R_FindImageFull(char * name, int minf, int maxf, int wraps, int wrapt)
	{
	IMAGE * tmp = img_root;

	while(tmp)
		{
		if(!strcmp(tmp-&gt;name, name))
			{
			tmp-&gt;used++;
			return tmp;
			}
		tmp = tmp-&gt;next;
		}

	/* Image needs to be created */

	tmp = R_CreateImage(name, minf, maxf, wraps, wrapt);
	R_AttachImage(tmp);
			
	tmp-&gt;used = 1;

	return tmp;
	}

IMAGE * R_FindImage(char * name)
	{
	return R_FindImageFull(name, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT);
	}

void R_UnloadImage(IMAGE * image)
	{
	if(image-&gt;name) E2_Free(image-&gt;name);
	if(image-&gt;data) E2_Free(image-&gt;data);

	glDeleteTextures(1, &image-&gt;id);

	if(image-&gt;next) image-&gt;next-&gt;prev = image-&gt;prev;
	if(image-&gt;prev) image-&gt;prev-&gt;next = image-&gt;next;

	if(image == img_root) img_root = image-&gt;next;

	E2_Free(image);
	}

void R_DoneWithImage(IMAGE * image)
	{
	image-&gt;used–;

	/* FIXME: No need to unload unless short on texture memory */
	if(image-&gt;used &lt;= 0) R_UnloadImage(image);
	}

/* ormoli and nasu blingblinged hia */
void R_Use_Image(IMAGE * img)
	{
	if(img-&gt;id == current_texture) return;

	current_texture = img-&gt;id;
	glBindTexture(GL_TEXTURE_2D, img-&gt;id);
	}

void R_CleanupImages(void)
	{
	IMAGE * tmp = img_root, * temp;

	while(tmp)
		{
		temp = tmp-&gt;next;
		Con_Printf("Found orphan texture '%s'\n", tmp-&gt;name);
		R_UnloadImage(tmp);
		tmp = temp;
		}	
	}

/* Console command, lists loaded images */
void R_ListImages_Cmd(int argc, char ** argv)
	{
	int i = 0;
	IMAGE * img = img_root;

	Con_Print("\nLoaded images:\n");

	while(img)
		{
		i++;
		Con_Printf("%s\n", img-&gt;name);
		img = img-&gt;next;
		}

	Con_Printf("Found %i images\n\n", i);
	}
  

And the corresponding header file:

      
/*******************************************************************************

                           Engine2 Image Handling

*******************************************************************************/</font>

#ifndef R_IMAGE_H
<font color="green">#define R_IMAGE_H
</font>
<font color="blue">typedef</font> <font color="blue">struct</font> image_t
	{
	<font color="blue">char</font> * name;
	<font color="blue">int</font> width, height, bpp;
	<font color="blue">int</font> min, max, wraps, wrapt;
	<font color="blue">unsigned</font> <font color="blue">char</font> * data;
	<font color="blue">unsigned</font> <font color="blue">int</font> id;

	<font color="blue">int</font> used;
	<font color="blue">struct</font> image_t * next, * prev;
	} IMAGE;

<font color="blue">void</font> R_Use_Image(IMAGE * img);

IMAGE * R_CreateImage(<font color="blue">char</font> * name, <font color="blue">int</font> minf, <font color="blue">int</font> maxf, <font color="blue">int</font> wraps, <font color="blue">int</font> wrapt);
IMAGE * R_FindImageFull(<font color="blue">char</font> * name, <font color="blue">int</font> minf, <font color="blue">int</font> maxf, <font color="blue">int</font> wraps, <font color="blue">int</font> wrapt); 
IMAGE * R_FindImage(<font color="blue">char</font> * name);
<font color="blue">void</font> R_DoneWithImage(IMAGE * image);

<font color="blue">void</font> R_CleanupImages(void);

<font color="blue">void</font> R_ListImages_Cmd(<font color="blue">int</font> argc, <font color="blue">char</font> ** argv);

#end<font color="blue">if</font>
    </pre></DIV><!–ENDSCRIPT–>

Here it is, free for anyone to use. Let me know if you do use it, and comments too, please?

Edit: Usage…

When initializing: IMAGE * texture = R_FindImage("data/texture.tga");

Binding: R_Use_Image(texture), or if you really want, glBindTexture(GL_TEXTURE_2D, texture->id);

Freeing: R_DoneWithImage(texture);

Also call R_CleanUpImages() just before exiting, that should clean up any images you forgot to free <img src="smile.gif" width=15 height=15 align=middle>

<SPAN CLASS=editedby>[edited by - alargeduck on June 2, 2002 3:48:07 PM]</SPAN>    
Advertisement
I would love to test your code, but i have a little "problem".
could you please insert all the code into a .txt-file and post the link to the file here.
If I copy all the text from the textarea it is in now, all the code will bo on one row, so it is to large to paste into the program (vc++)...
dazen: edit alargeduck''s post and copy from there.
---visit #directxdev on afternet <- not just for directx, despite the name
try this:

http://12.234.160.105/~demosh/tmp/r_image.c
http://12.234.160.105/~demosh/tmp/r_image.h

BTW, im counting on the user to be smart enough to change some of the functions my engine framework provides, namely E2_Malloc(), E2_Free(), E2_Error(), and Con_Printf().


This topic is closed to new replies.

Advertisement