This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Texture Generation Utility Submitted by

I was just reading Terrain Texture Generation by Tobias Franke on flipcode today. I had written a similar utility some time back which had support for config-files and multiple texture images. The textures wrap around if the heightmap is larger than the base textures. I thought that ppl who're writing terrain engines could use it. I write and use most of my utilities in Linux. This one was hastily converted to win32 just for this post.

Currently browsing [texgen.zip] (9,084 bytes) - [main.cpp] - (4,225 bytes)

    /*
      This is the unique texture generator for the terrain

      Limitations: The base textures should be in 24bpp format.
      and the heightmap should be in 8bpp format

      Added config file support
    */

    #include <stdio.h>
    #include "tga.h"
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>

    #define CFG_FILE "texgen.cfg"

    typedef struct
    {
      int hi, lo;
    } limits;

    int num_textures;
    tga **pics, *hmap;
    limits *ltable;

    //For every texture image create a reference map and......
    void parse_file();
    void get_string(char *, int, FILE *);

    void gen_tex_table(int num)
    {
      pics=(tga **)malloc(num*sizeof(tga *));
      ltable=(limits *)malloc(num*sizeof(limits));
    }

    void add_to_tex_table(char *filename, int index, int hi, int lo)
    {
      pics[index]=new tga(filename);
      ltable[index].lo=lo;
      ltable[index].hi=hi;
    }

    void destroy_tex_table()
    {
      int i;
      for(i=0; i<num_textures; i++)
        {
          free(pics[i]);
        }
      free(pics);
      free(ltable);
    }

    void main(int argc, char **argv)
    {
      tga *hmap;

      unsigned char *data;

      if (argc != 2)
        {
          printf("Usage: %s <8bppheightmap.tga>\n",argv[0]);
          exit(0);
        }

      parse_file();

      hmap=new tga(argv[1]);

      data=(unsigned char *)malloc(3*hmap->width*hmap->height);
      memset(data, 0, 3*hmap->width*hmap->height);

      //Main processing phase
      int offset=0;
      for(int y=0; y<hmap->height; y++)
        for(int x=0; x< hmap->width; x++)
          {
    	unsigned char height=hmap->data[offset++];
    	int clean=1;
    	float alpha;

    	for(int i=0; i<num_textures; i++)
    	  {
    	    //If within limits, add to data suitably
    	    if ( (height >= ltable[i].lo) && (height <= ltable[i].hi))
    	      {
    		//Calculate alpha for the current texture
    		alpha=(float)(height-ltable[i].lo) \
    		  /(float)(ltable[i].hi-ltable[i].lo);

    		// This is a real funky way of doing the alpha calculation but
    		// who cares

    		//Get a value betn 30 & 150
    		float degrees=(alpha*120.0f)+30.0f;
    		alpha=sin(degrees*3.14159f/180.0f);
    		//Just check the bounds
    		if (alpha < 0.0f) alpha=0.0f;
    		if (alpha > 1.0f) alpha=1.0f;

    		colour24 col=pics[i]->getCol(x,y);

    		int offset2=(offset<<1)+offset;
    		if (clean) //write directly
    		  {
    		    data[offset2+1]=col.r;
    		    data[offset2+2]=col.g;
    		    data[offset2+3]=col.b;
    		  }
    		else
    		  {
    #define A1 alpha
    #define A2 (1.0f-A1)

    		    //Mix the two colours
    		    data[offset2+1]=(A1*col.r+A2*data[offset2+1]);
    		    data[offset2+2]=(A1*col.g+A2*data[offset2+2]);
    		    data[offset2+3]=(A1*col.b+A2*data[offset2+3]);
    		  }
    		clean=0;
    	      }
    	  }
    	}

      hmap->write_24bpp(hmap->width,hmap->height,data,"texture.tga");

      delete hmap;
      //This crashes in windows. I dunno why ?
      //free(data);
      destroy_tex_table();
    }

    /*
      # SAMPLE CFG FILE
      num_textures 2
      # filename1 range.....
      tex1.tga 0 127
      tex2.tga 127 255
    */
    void parse_file()
    {
      FILE *fp;
      char buffer[256];
      int num_read=0;
      char str[256];
      int hi,lo;

      if((fp=fopen(CFG_FILE,"r"))==NULL)
        {
          printf("Cannot open file %s\n", CFG_FILE);
          exit(0);
        }

      //read the first string
      get_string(buffer,256,fp);
      while(buffer[0] == '#' || buffer[0] == ' ' || buffer[0] == '\t')
        {
          get_string(buffer,256,fp);
        }
      //If not a comment
      //Get the number of textures
      sscanf(buffer,"num_textures %d",&num_textures);
      printf("TEXGEN: num_textures: %d\n", num_textures);
      gen_tex_table(num_textures);

      //Now that we got num_textures, we can proceed further
      while(num_read != num_textures)
        {
          get_string(buffer,256,fp);
          if(buffer[0] != '#')
    	{
    	  sscanf(buffer,"%s %d %d",str,&lo,&hi);
    	  printf("TEXGEN: %s %5d %5d\n",str,lo,hi);
    	  add_to_tex_table(str,num_read,hi,lo);
    	  num_read++;
    	}
        }
      fclose(fp);
    }

    /*
      Slow way to do it but I dont care
      Cut-pasted and modified from my CNC expt
    */
    void get_string(char *buff, int size, FILE *stream)
    {
      char ch;
      int i=0;
      while( (ch=(char)fgetc(stream)) != '\n')
        {
          if (i<size-1)
            {
              buff[i++]=ch;
            }
        }
      buff[i]='\0';
    }

Currently browsing [texgen.zip] (9,084 bytes) - [tga.cpp] - (4,497 bytes)

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include "tga.h"

    #define DEBUG_ON

    tga::~tga()
    {
    	if (data)
    	free(data);
    #ifdef DEBUG_ON
    	printf("\nUnloaded\n");
    #endif
    }

    tga::tga()
    {
    }

    tga::tga(char *filename)
    {
    	load(filename);
    }

    void tga::load(char *filename)
    {
    	tga_header hdr;
    	FILE *fp;
    	int size;
    	unsigned char temp;

    	if ( (fp=fopen(filename,"rb")) == NULL)
    	{
    		perror("TGA_LOADER");
    		exit(1);
    	}

    	fread(&hdr,sizeof(tga_header),1,fp);

    	if (hdr.cmap_type)
    	{
    		printf("TGA_LOADER: Can't load colour-mapped Images\n");
    		exit(1);
    	}

    	pixfmt=hdr.bpp >> 3;
    	width=hdr.width;
    	height=hdr.height;
    	size=width*height*pixfmt;
    	data=(byte *)malloc(size);
    	//Position the pointer at the raw image data !
    	fseek(fp,hdr.id_length,SEEK_CUR);

    	if(hdr.image_type > 3)
    	{
    		switch(pixfmt)
    		{
    		case 3: //24 bpp
    			decode24bit(fp);
    			break;
    		case 1: //8 bpp
    			decode8bit(fp);
    			break;
    		}
    	}
    	else ///Uncompressed data
    	{
    		fread(data,1,size,fp);
    		if (pixfmt == 3)
    		{
    			//Convert BGR to RGB
    			for(int i=0; i<size; i+=3)
    			{
    				temp=data[i];
    				data[i]=data[i+2];
    				data[i+2]=temp;
    			}
    		}
    	}
    #ifdef DEBUG_ON
    	printf("Loaded %s: %dx%d @ %dbpp\n", filename, width,height,hdr.bpp);
    #endif
    	fclose(fp);
    }

    void tga::decode8bit(FILE *fp)
    {
    	byte packet, colour;
    	byte *ptr;
    	int count, offset=0, size;
    	size=height*width;
    	ptr=data;

    	while(offset < size)
    	{
    		fread(&packet,1,1,fp);
    		count=(packet&0x7f)+1;
    		offset+=count;
    		//Runlength packet
    		if (packet&0x80)
    		{
    			//Read BGR and convert to RGB
    			fread(&colour,1,1,fp);
    			while(count)
    			{
    				*ptr=colour;
    				ptr++;
    				--count;
    			}
    		}
    		else
    		{
    			//Raw packet
    			while(count)
    			{
    				fread(&colour,1,1,fp);
    				*ptr=colour;
    				ptr++;
    				--count;
    			}
    		}
    	}
    #ifdef DEBUG_ON
    	printf("Offset=%d , Size=%d\n",offset,size);
    #endif
    }

    void tga::decode24bit(FILE *fp)
    {
    	byte packet, colour[3];
    	byte *ptr;
    	int count, offset=0, size;
    	size=height*width;
    	ptr=data;

    	//Sometimes offset exceeds size !
    	while(offset < size)
    	{
    		fread(&packet,1,1,fp);
    		count=(packet&0x7f)+1;
    		offset+=count;
    		//Runlength packet
    		if (packet&0x80)
    		{
    			//Read BGR and convert to RGB
    			fread(colour,3,1,fp);
    			while(count)
    			{
    				ptr[0]=colour[2];
    				ptr[1]=colour[1];
    				ptr[2]=colour[0];
    				ptr+=3;
    				--count;
    			}
    		}
    		else
    		{
    			//Raw packet
    			while(count)
    			{
    				fread(colour,3,1,fp);
    				ptr[0]=colour[2];
    				ptr[1]=colour[1];
    				ptr[2]=colour[0];
    				ptr+=3;
    				--count;
    			}
    		}
    	}
    #ifdef DEBUG_ON
    	printf("Offset=%d , Size=%d\n",offset,size);
    #endif
    }

    //Will be defined for future use!
    void tga::decode32bit(FILE *fp)
    {
    }

    void tga::write_24bpp(int w, int h, unsigned char *data, char *filename)
    {
    	FILE *fp;
    	char *header=
    		"\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00";
    	//Open o/p file
    	unsigned char info[6];
    	unsigned char temp;
    	unsigned char *copy;
    	int size;

    	size=w*h*3;

    	info[0]=w;             //lo
    	info[1]=(w>>8);        //hi
    	info[2]=h;             //lo
    	info[3]=(h>>8);        //hi
    	info[4]=24;
    	info[5]=0;

    	fp=fopen(filename,"wb");
    	fwrite(header,1,12,fp);
    	fwrite(info,1,6,fp);

    	//Just write the data in RGB fmt
    	copy=(unsigned char *)malloc(size);
    	memcpy(copy,data,size);
    	//Convert this buffer to BGR fmt
    	for(int i=0; i<size; i+=3)
    	{
    		temp=copy[i];
    		copy[i]=copy[i+2];
    		copy[i+2]=temp;
    	}

    	fwrite(copy,1,size,fp);
    	fclose(fp);
    	free(copy);
    }

    void tga::write_8bpp(int w, int h, unsigned char *data, char *filename)
    {
    	FILE *fp;
    	char *header=
    		"\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00";
    	//Open o/p file
    	unsigned char info[6];
    	int size;

    	size=w*h;

    	info[0]=w;             //lo
    	info[1]=(w>>8);        //hi
    	info[2]=h;             //lo
    	info[3]=(h>>8);        //hi
    	info[4]=8;
    	info[5]=0;

    	fp=fopen(filename,"wb");
    	fwrite(header,1,12,fp);
    	fwrite(info,1,6,fp);

    	fwrite(data,1,size,fp);
    	fclose(fp);
    }

    // Slightly expensive but who cares..
    // this is used in the preprocess stage
    colour24 tga::getCol(int x, int y)
    {
      colour24 a;
      int offset;
      while( y >= height)
        y=y-height;

      while( x >= width)
        x=x - width;

      offset=y*width+x;
      offset*=3;
      a.r=data[offset+1];
      a.g=data[offset+2];
      a.b=data[offset+3];
      return a;
    }

Currently browsing [texgen.zip] (9,084 bytes) - [tga.h] - (1,136 bytes)

    /*
    	TGA CLASS:
    	Author: Darshan A Patil
    	Date:   2000-2001
    	e-mail: dapatil@hotmail.com

    	Notes:
    	This TGA class,
    	Can load 8bit and 24bit TGA files.
    	Writes 8 and 24 bit TGA files w/o RLE compression
    */
    #ifndef TGA_H
    #define TGA_H
    #include <stdio.h>

    typedef unsigned char byte;

    typedef struct
    {
    	byte id_length;
    	byte cmap_type;
    	byte image_type;
    	byte cmap_specs[5];
    	unsigned short int xorg, yorg,width, height;
    	byte bpp; //bits per pixel
    	byte img_descriptor;
    }tga_header;

    typedef struct
    {
      unsigned char r,g,b;
    } colour24;

    class tga
    {
    private:
    	void decode8bit(FILE *fp);
    	void decode32bit(FILE *fp);
    	void decode24bit(FILE *fp);
    public:
    	int width, height;
    	byte pixfmt; //Bytes per pixel.
    	byte *data;  //Raw Image Data

    	tga();
    	tga(char *filename);
    	~tga();

    	void load(char *filename);
    	void write_24bpp(int width, int height, unsigned char *data, char *filename);
    	void write_8bpp(int width, int height, unsigned char *data, char *filename);
    	// To be used only for the synthesizer....
    	// Uses wrap around mode
    	colour24 getCol(int x,int y);
    };

    #endif

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.