Выбрать главу

On to the code. First we read the one byte header.

do // Start Loop

{

 GLubyte chunkheader = 0; // Variable To Store The Value Of The Id Chunk

 if (fread(&chunkheader, sizeof(GLubyte), 1, fTGA) == 0) // Attempt To Read The Chunk's Header

 {

  …Error code…

  return false; // If It Fails, Return False

 }

Next we will check to see if it a RAW header. If it is, we need to add one to the value to get the total number of pixels following the header.

if (chunkheader < 128) // If The Chunk Is A 'RAW' Chunk

{

 chunkheader++; // Add 1 To The Value To Get Total Number Of Raw Pixels

We then start another loop to read all the color information. It will loop the amout of times specified in the chunk header, and will read and store one pixel each loop.

First we read and verify the pixel data. The data for one pixel will be stored in the colorbuffer variable. Next we will check to see if it a RAW header. If it is, we need to add one to the value to get the total number of pixels following the header.

// Start Pixel Reading Loop

for (short counter = 0; counter < chunkheader; counter++) {

 // Try To Read 1 Pixel

 if (fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel) {

  …Error code…

  return false; // If It Fails, Return False

 }

The next part in our loop will take the color values stored in colorbuffer and writing them to the imageData varable to be used later. In the process it will flip the data from BGR format to RGB or from BGRA to RGBA depending on the number of bits per pixel. When we are done we increment the current byte, and current pixel counters.

texture->imageData[currentbyte] = colorbuffer[2]; // Write The 'R' Byte

texture->imageData[currentbyte + 1 ] = colorbuffer[1]; // Write The 'G' Byte

texture->imageData[currentbyte + 2 ] = colorbuffer[0]; // Write The 'B' Byte

if (tga.bytesPerPixel == 4) // If It's A 32bpp Image…

{

 texture->imageData[currentbyte + 3] = colorbuffer[3]; // Write The 'A' Byte

}

// Increment The Byte Counter By The Number Of Bytes In A Pixel

currentbyte += tga.bytesPerPixel;

currentpixel++; // Increment The Number Of Pixels By 1

The next section deals with the chunk headers that represent the RLE sections. First thing we do is subtract 127 from the chunkheader to get the amount of times the next color is repeated.

else // If It's An RLE Header

{

 chunkheader –= 127; // Subtract 127 To Get Rid Of The ID Bit

The we attempt to read the next color value.

// Read The Next Pixel

if (fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel) {

 …Error code…

 return false; // If It Fails, Return False

}

Next we begin a loop to copy the pixel we just read into memory multiple times, as dictated by the value from the RLE header.

Then we copy the the color values into the image data, preforming the R and B value switch.

Then we increment the current bytes, and current pixel, so we are in the right spot when we write the values again.

// Start The Loop

for (short counter = 0; counter < chunkheader; counter++) {

 // Copy The 'R' Byte

 texture->imageData[currentbyte] = colorbuffer[2];

 // Copy The 'G' Byte

 texture->imageData[currentbyte + 1 ] = colorbuffer[1];

 // Copy The 'B' Byte

 texture->imageData[currentbyte + 2 ] = colorbuffer[0];

 if (tga.bytesPerPixel == 4) // If It's A 32bpp Image

 {

  // Copy The 'A' Byte

  texture->imageData[currentbyte + 3] = colorbuffer[3];

 }

 currentbyte += tga.bytesPerPixel; // Increment The Byte Counter

 currentpixel++; // Increment The Pixel Counter

Then we contiune the main loop, as long as we still have pixels left to read.

Last of all we close up the file and return success.

 while (currentpixel < pixelcount); // More Pixels To Read? … Start Loop Over

 fclose(fTGA); // Close File

 return true; // Return Success

}

Now you have some image data ready for glGenTextures and glBindTexture. I suggest you check out NeHe's tutorial #6 and #24 for info on these commands. That concludes my first ever tutorial. I do not guarantee my code is error free, though i made an effort to see that it was. Special thanks to Jeff "NeHe" Molofee for his great tutorials and to Trent "ShiningKnight" Polack for helping me revise this tutorial. If you find errors, have suggestions, or comments please feel free to email me (terminate@gdnmail.net), or ICQ me at UIN# 38601160. Enjoy!

Evan Pipho (Terminate)
Jeff Molofee (NeHe)

* DOWNLOAD Visual C++ Code For This Lesson. 

Lesson 34

Welcome to another exciting tutorial! The code for this tutorial was written by Ben Humphrey, and is based on the GL framework from lesson 1. By now you should be a GL expert {grin}, and moving the code into your own base code should be a snap!

This tutorial will teach you how to create cool looking terrain from a height map. For those of you that have no idea what a height map is, I will attempt a crude explanation. A height map is simply… displacement from a surface. For those of you that are still scratching your heads asking yourself "what the heck is this guy talking about!?!"… In english, our heightmap represents low and height points for our landscape. It's completely up to you to decide which shades represent low points and which shades represent high points. It's also important to note that height maps do not have to be images… you can create a height map from just about any type of data. For instance, you could use an audio stream to create a visual height map representation. If you're still confused… keep reading… it will all start to make sense as you go through the tutorial :)

#include <windows.h> // Header File For Windows

#include <stdio.h> // Header file For Standard Input/Output ( NEW )

#include <gl\gl.h> // Header File For The OpenGL32 Library

#include <gl\glu.h> // Header File For The GLu32 Library

#include <gl\glaux.h> // Header File For The Glaux Library

#pragma comment(lib, "opengl32.lib") // Link OpenGL32.lib

#pragma comment(lib, "glu32.lib") // Link Glu32.lib

We start off by defining a few important variables. MAP_SIZE is the dimension of our map. In this tutorial, the map is 1024x1024. The STEP_SIZE is the size of each quad we use to draw the landscape. By reducing the step size, the landscape becomes smoother. It's important to note that the smaller the step size, the more of a performance hit your program will take, especially when using large height maps. The HEIGHT_RATIO is used to scale the landscape on the y-axis. A low HEIGHT_RATIO produces flatter mountains. A high HEIGHT_RATIO produces taller / more defined mountains.