Cool, It Works! - Issue 03 - Files, Texture Effects, Coronas, & More by (09 September 1999) | Return to The Archives

Introduction

Welcome to another action packed episode of my little column. Not much to say here in the intro section, so let's just get to it shall we? Alrighty then...

File Reading/Processing

A few years back, I was reading some source that John Carmack had released .. I think it was the common file handling code included in the Quake1 tools source code. And I noticed that he was doing something I had never thought of when processing files. He would read the entire file into memory, process that glob of RAM and then free it when he was done. I had never considered that, and at the time, was very cool to discover. There's only one file read operation (albeit a large one, but still, only one), and the rest of the processing takes place in RAM so it's really fast.
And how exactly do you read the file into a glob of RAM? Like so... here is a small chunk of my Tfile base class..

    int TFile::LoadFile(CString* _pcsFilename)
    {
    	FILE* l_pfile;
    	int l_iLength;

    	l_pfile = ::fopen(*_pcsFilename, "rb");

    	if(!l_pfile) {
    		m_pbBuffer = NULL;
    		return -1;
    	}

    	// Read the file into RAM.
    	//
    	l_iLength = GetFileLength(l_pfile);
    	m_pbBuffer = new byte[l_iLength + 1];
    	::ZeroMemory(m_pbBuffer, l_iLength + 1);
    	::fread(m_pbBuffer, 1, l_iLength, l_pfile);
    	::fclose(l_pfile);

    	return l_iLength;
    }

    int TFile::GetFileLength(FILE* _pfile)
    {
    	int l_iSavPos, l_iEnd;

    	l_iSavPos = ::ftell(_pfile);
    	::fseek(_pfile, 0, SEEK_END);
    	l_iEnd = ::ftell(_pfile);
    	::fseek(_pfile, l_iSavPos, SEEK_SET);

    	return l_iEnd;
    }

FYI : m_pbBuffer is a byte* member variable.
That's really it. You open the file, allocate a chunk of RAM that matches the file size, and read it into that buffer. Now if you take a byte* and start walking through that chunk of data, it's just like you were walking through the file itself by reading byte at a time. Except much faster...
When processing the data, I use a byte* to walk through the file data in RAM ... the file is over when the value that variable points to is NULL. So I loop something like this...

    byte* l_pbuffer = m_pbBuffer;

    while(*l_pbuffer) {

    	// Do something with the data here

    	l_pbuffer++;
    }

Now finding this was cool enough in itself because of how much faster my file operations became. But it was REALLY cool because it set me up with the tools I needed to make...

Package Files

Basically this is a large file that has many smaller files inside of it. Sort of like a ZIP file, but I don't use any compression ... and similar to the PAK files that Quake engines use. I'm doing this mainly for ease of distribution when the game is done. Much easier to keep track of 1 texture package than 500 PCX files. It's not the most convenient thing in the world to have to constantly rebuild package files when a graphic or model changes, but I've written some utility programs and batch files that make the job little more than a few mouse clicks - so it's not so bad.
The internal format is VERY simple and straightforward. I have a conversion program that reads a directory structure, and stores the files it finds in there inside the package file. I have an unwritten rule that you can't go deeper than 1 subdirectory deep. I don't have hierarchies in my package files ... just a list of files, laid down end to end.
The file starts off with a simple header. Basically just a list of filenames, their offsets into the package file and their size. So to retrieve a file from the package file, I open it, scan the table of contents looking for a matching name ... when I find it, I seek to that spot in the file, and read in the number of bytes that the table of contents says to.
What does this give me? A glob of RAM that contains the data that was in the original file. Now I can just point my file handling routines at that glob, and process the file as normal. Cool huh?

Masked Textures

This took me a while to figure out how to do in OpenGL. What this effect is, is like if you have a chain link fence. You want to render the fence wires, but leave out the filler between them. This filler is usually some common color ... and in other API's I've used, I could specify the specific color to leave out and it would render correctly. But not in OpenGL. Until someone told me where to look...
OpenGL accomplishes this through something called "alpha test". Basically what happens is, you can tell OpenGL that you are rendering textures with an alpha channel (RGBA instead of RGB), and tell it what your bottom threshold for alpha values is. Any pixel that has an alpha value below that threshold will not be rendered. Effectively masking the texture.
So here's what I do ...
I read in my PCX file. I convert the RGB data I get into RGBA data - basically, copying it to a new data buffer and filling in the alpha value for each pixel with 255. I know what color I want to be masked out, so I loop through that new data buffer and for each pixel I find of that color, I change the alpha value to 0.
Then in my OpenGL setup, I add these lines ...

    glEnable(GL_ALPHA_TEST);
    glAlphaFunc(GL_GEQUAL, 0.05f);

...and when OpenGL does it's texture mapping, it throws away pixels that have an alpha value of less then 0.05 ... so textures containing pixels with less than 0.05 alpha get masked out.

Translucent Textures

There's a few ways to do translucent surfaces in OpenGL. The simplest and quickest is to just specify an alpha value when you select the polygons color. So where you would normally say ...

    glColor3f(1.0f, 1.0f, 1.0f);

... you say this instead ...

    glColor4f(1.0f, 1.0f, 1.0f, 0.5f);

... and you would get a 50% translucent polygon.
This is the easiest and cheapest way to get translucency, but this is not really very cool for most applications. We'll talk about how my engine is handling translucency next time, but the above statement is good enough to let us do a simple effect like ...

Coronas

OK, here's your eye candy for this installment ... coronas! Actually, these are so easy it's sick. Heh. Basically you know how to render a sprite (we talked about it last time). So with that knowledge, what is a corona? Basically, a corona is a texture of a sphere of light (or some other nice looking effect) that is rendered as a partially translucent sprite. The trick to the corona effect (as seen in Unreal) is to make the sprite grow larger as you move away from it and shrink as you get closer. That's it.
Now, you can achieve this pretty easily by choosing a distance at which the corona renders at 100% of it's size. Say, 350 units. So if the camera gets closer than 350 units, you start scaling the corona down. And of course, scale it up if it gets more than 350 units away. And you should probably have something where , say, if the camera gets further way then 900 units, you shut off the corona. You don't want it to grow too large or it'll cover half the world. ;)
The last thing you have to do now, is to check line of sight to the corona. If your line of sight to the corona is blocked by something, don't render it. Pretty simple. So if you see it through some bars, as you slide past the bars the corona will pop on whenever you see the light source through the bars and off again as a bar slides into your view.
One thing that makes them look really nice (which I don't do yet, but Unreal does) is to make them fade in and out when they come on or turn off. This is slick, and when I get it working, I'll talk about it. Heh.

Next Time

OK, that's it for this installment. Next time we'll talk about ... stuff! Seriously, I don't want to make commitments on what we'll talk about from column to column because that locks me into writing about those things ... and if something cool happens and I want to write about that instead, I want the freedom to do so. :) So see you next time!

Article Series:

> - Cool, It Works! - Issue 01 - Introduction
> - Cool, It Works! - Issue 02 - Linked Lists, Overloading Operators, Sprites, & More
> - Cool, It Works! - Issue 03 - Files, Texture Effects, Coronas, & More
> - Cool, It Works! - Issue 04 - Handling Textures & Effects
> - Cool, It Works! - Issue 05 - 32-Bit GL Textures & Log Files

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