Archived

This topic is now archived and is closed to further replies.

A Texture Manager

This topic is 5677 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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[i];
		img->data[i] = img->data[i + 2];
		img->data[i + 2] = temp;
		}

	return 1;
	}

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

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

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

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

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

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

	E2_Free(image->data);
	image->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->name = E2_Malloc((strlen(name) + 1) * sizeof(char));
	strcpy(image->name, name);
	
	image->min = minf;
	image->max = maxf;
	image->wraps = wraps;
	image->wrapt = wrapt;

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

	BuildImage(image);

	return image;
	}

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

	image->next = img_root;
	image->prev = 0;

	/* True unless first and only texture in list */
	if(image->next) image->next->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->name, name))
			{
			tmp->used++;
			return tmp;
			}
		tmp = tmp->next;
		}

	/* Image needs to be created */

	tmp = R_CreateImage(name, minf, maxf, wraps, wrapt);
	R_AttachImage(tmp);
			
	tmp->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->name) E2_Free(image->name);
	if(image->data) E2_Free(image->data);

	glDeleteTextures(1, &image->id);

	if(image->next) image->next->prev = image->prev;
	if(image->prev) image->prev->next = image->next;

	if(image == img_root) img_root = image->next;

	E2_Free(image);
	}

void R_DoneWithImage(IMAGE * image)
	{
	image->used--;

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

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

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

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

	while(tmp)
		{
		temp = tmp->next;
		Con_Printf("Found orphan texture '%s'\n", tmp->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->name);
		img = img->next;
		}

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

And the corresponding header file:

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

                           Engine2 Image Handling

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

#ifndef R_IMAGE_H
#define R_IMAGE_H

typedef struct image_t
	{
	char * name;
	int width, height, bpp;
	int min, max, wraps, wrapt;
	unsigned char * data;
	unsigned int id;

	int used;
	struct image_t * next, * prev;
	} IMAGE;

void R_Use_Image(IMAGE * img);

IMAGE * R_CreateImage(char * name, int minf, int maxf, int wraps, int wrapt);
IMAGE * R_FindImageFull(char * name, int minf, int maxf, int wraps, int wrapt); 
IMAGE * R_FindImage(char * name);
void R_DoneWithImage(IMAGE * image);

void R_CleanupImages(void);

void R_ListImages_Cmd(int argc, char ** argv);

#endif
    
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 [edited by - alargeduck on June 2, 2002 3:48:07 PM]

Share this post


Link to post
Share on other sites
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++)...

Share this post


Link to post
Share on other sites
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().


Share this post


Link to post
Share on other sites