5. Terrain Textures

Jump To:

heightfield.h Source
heightfield.cpp Source

main.cpp Source
Download

Heightfield.H File:

Another nice little adjustment to our heightfield.h file. We just need to assign a variable to hold our textures 🙂 I am calling this tID and at this point, is an array with just 2 positions, for now we will just be working with tID[0], but later on when we add normal mapping, we will be working with tID[1]:

// unsigned int tID[2];

Now lets move onto our heightfield.cpp file 🙂

1.
2.
3.
4.

5.
6.
7.
8.
9.
10.
11.
12.
13.

14.
15.
16.

    #include <windows.h>

class SwiftHeightField {
    
private:
    

int hmHeight;
    
int hmWidth;
    

    
unsigned int tID[2];
    

public:
    
bool Create(char *hFileName, const int hWidth, const int hHeight);
    

    
void Render(void);
    

    
BYTE hHeightField[1024][1024];

};

Heightfield.CPP File:

The first change here that you will need to pay attention to, is the incorporation of jpeg.h. Just like in the ‘Loading a JPEG texture’ tutorial (coming soon), we will be using the IJG jpeg library to load our textures. So after we add the line:

// include "jpeg.h"

We need to load in our texture. I am doing this after we load in our heightfield, in our Create function. So I am adding the line:

// SwiftTextureJpeg(tID, "texture.jpg", 0);

Where texture.jpg is the texture that will stretch over our terrain. Next is where our texturing comes into play. Just like texturing any other primitive shape in OpenGL, we need to enable texturing and bind our texture. So in our Render function, right before anything else, I am adding the lines:

// glEnable(GL_TEXTURE_2D);

// glBindTexture(GL_TEXTURE_2D, tID[0]);

Which will enable texturing and then bind are texture respectively. To finish off this, we need to disable texturing when we are done with our terrain, so we call:

// glDisable(GL_TEXTURE_2D);

At the end of our Render function. Now we need to take a look at our texture coordinates. At the moment, we have no texture coordinates, so in this tutorial we are going to dynamically compute them on the fly. This sounds really technical but it isn’t really. At the X = 0 coordinate on the map, our U coordinate for our texture is 0, and at the X = Width coordinate, our U coordinate for our texture is X / Width.

So at:
X = 0, U = 0;

Z = 0, V = 0;

X = 1024, U = 1; (X / Width = 1024 / 1024 = 1)
Z = 1024, V = 1; (Z / Height = 1024 / 1024 = 1)

Taking this into practice, our rendering code will now look like this:

// glTexCoord2f((float)hMapX / hmWidth, (float)hMapZ / hmHeight);
// glVertex3f(hMapX, hHeightField[hMapX][hMapZ], hMapZ);

// glTexCoord2f((float)hMapX / hmWidth, (float)(hMapZ + 1) / hmHeight);
// glVertex3f(hMapX, hHeightField[hMapX][hMapZ + 1], hMapZ + 1);

// glTexCoord2f((float)(hMapX + 1) / hmWidth, (float)hMapZ / hmHeight);
// glVertex3f(hMapX + 1, hHeightField[hMapX + 1][hMapZ], hMapZ);

// glTexCoord2f((float)(hMapX + 1) / hmWidth, (float)(hMapZ + 1) / hmHeight);

// glVertex3f(hMapX + 1, hHeightField[hMapX + 1][hMapZ + 1], hMapZ + 1);

And that is all we need to change in our heightfield.cpp file. 🙂

1.
2.
3.
4.

5.
6.
7.
8.
9.
10.
11.
12.
13.

14.
15.
16.
17.
18.
19.
20.
21.
22.

23.
24.
25.
26.
27.
28.
29.
30.
31.

32.
33.
34.
35.
36.
37.
38.
39.
40.

41.
42.
43.
44.
45.
46.
47.
48.
49.

50.
51.
52.
53.

    #include <stdio.h>
#include <gl\gl.h>

#include “jpeg.h”
#include “heightfield.h”

bool SwiftHeightField::Create(char *hFileName, const int hWidth, 
const int hHeight){
    

hmHeight = hHeight;
    
hmWidth = hWidth;
    

    
FILE *fp;
    

    
fp = fopen(hFileName, “rb”);
    

    
fread(hHeightField, 1, hWidth * hHeight, fp);
    

    
fclose(fp);
    

    
SwiftTextureJpeg(tID, “texture.jpg”, 0);
    

    
return true;
}

void SwiftHeightField::Render(void){
    
glEnable(GL_TEXTURE_2D);
    

glBindTexture(GL_TEXTURE_2D, tID[0]);
    

    
for (int hMapX = 0; hMapX < hmWidth; hMapX++){
        

for (int hMapZ = 0; hMapZ < hmHeight; hMapZ++){
            
glBegin(GL_TRIANGLE_STRIP);
            

glTexCoord2f((float)hMapX / hmWidth, (float)hMapZ / hmHeight);
            

glVertex3f(hMapX, hHeightField[hMapX][hMapZ], hMapZ);
            

            
glTexCoord2f((float)hMapX / hmWidth, (float)(hMapZ + 1) / hmHeight) ;

            
glVertex3f(hMapX, hHeightField[hMapX][hMapZ + 1], hMapZ + 1);
            

            
glTexCoord2f((float)(hMapX + 1) / hmWidth, (float)hMapZ / hmHeight);

            
glVertex3f(hMapX + 1, hHeightField[hMapX + 1][hMapZ], hMapZ);
            

            
glTexCoord2f((float)(hMapX + 1) / hmWidth, (float)(hMapZ + 1) / hmHeight);

            
glVertex3f(hMapX + 1, hHeightField[hMapX + 1][hMapZ + 1], hMapZ + 1);
            

glEnd();
        
}
    
}
    

    
glDisable(GL_TEXTURE_2D);
}

Main.CPP File:

And guess what?! No changes need to be made to our main.cpp file 🙂

Check out the next tutorial on rendering the heightfield using VBO’s (Vertex Buffer Objects) for speed optimizations here 🙂

If you have any questions, just email me at swiftless@gmail.com

1.
2.
3.
4.
5.

6.
7.
8.
9.
10.
11.
12.
13.
14.

15.
16.
17.
18.
19.
20.
21.
22.
23.

24.
25.
26.
27.
28.
29.
30.
31.
32.

33.
34.
35.
36.
37.
38.
39.
40.
41.

42.
43.
44.
45.
46.
47.
48.
49.
50.

51.
52.
53.
54.
55.
56.
57.
58.
59.

60.
61.
62.
63.
64.
65.
66.
67.
68.

69.
70.
71.
72.
73.
74.
75.
76.
77.

78.
79.
80.
81.
82.
83.
84.
85.
86.

87.
88.
89.
90.
91.
92.
93.
94.
95.

96.
97.
98.
99.
100.
101.
102.
103.
104.

105.
106.
107.
108.
109.
110.
111.
112.
113.

114.
115.
116.
117.
118.
119.
120.
121.
122.

123.
124.
125.
126.
127.
128.
129.

    #include <GL/glew.h>

#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/GLUT.h>

#include <math.h>

#include <windows.h>
#include <stdio.h>

#include <string.h>
#include <fstream>
#include <assert.h>

#include “heightfield.h”

#pragma comment(lib,“glew32.lib”)

float xpos = 851.078, ypos = 351.594, zpos = 281.033, xrot = 758, yrot = 238,
 angle=0.0;

float lastx, lasty;

float bounce;
float cScale = 1.0;

SwiftHeightField hField;

void camera (void) {
    
int posX = (int)xpos;
    

int posZ = (int)zpos;
    

    
glRotatef(xrot,1.0,0.0,0.0);
    
glRotatef(yrot,0.0,1.0,0.0);
    

glTranslated(xpos,ypos,zpos);
}

void display (void) {
    

glClearColor (0.0,0.0,0.0,1.0);
    
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    

glLoadIdentity();
    
camera();
    

    
hField.Render();
    

    

glutSwapBuffers();
}

void Init (void) {
    
glEnable(GL_DEPTH_TEST);
    

glDepthFunc(GL_LEQUAL);
    

    
hField.Create(“heightfield.raw”, 1024, 1024);
}

void mouseMovement(int x, int y) {
    
int diffx=xlastx;
    

int diffy=ylasty;
    
lastx=x;
    
lasty=y;
    
xrot += (float) diffy;
    

yrot += (float) diffx;
}

void keyboard (unsigned char key, int x, int y) {
    

    
if (key == ‘w’)
    
{
        
float xrotrad, yrotrad;
        

yrotrad = (yrot / 180 * 3.141592654f);
        
xrotrad = (xrot / 180 * 3.141592654f);
        

xpos += float(sin(yrotrad)) * cScale;
        
zpos = float(cos(yrotrad)) * cScale;
        

ypos = float(sin(xrotrad)) ;
        
bounce += 0.04;
    

}
    

    
if (key == ‘s’)
    
{
        
float xrotrad, yrotrad;
        

yrotrad = (yrot / 180 * 3.141592654f);
        
xrotrad = (xrot / 180 * 3.141592654f);
        

xpos = float(sin(yrotrad)) * cScale;
        
zpos += float(cos(yrotrad)) * cScale;
        

ypos += float(sin(xrotrad));
        
bounce += 0.04;
    

}
    

    
if (key == ‘d’)
    
{
        
float yrotrad;
        

yrotrad = (yrot / 180 * 3.141592654f);
        
xpos += float(cos(yrotrad)) * cScale;
        

zpos += float(sin(yrotrad)) * cScale;
    
}
    

    
if (key == ‘a’)
    
{
        
float yrotrad;
        

yrotrad = (yrot / 180 * 3.141592654f);
        
xpos = float(cos(yrotrad)) * cScale;
        

zpos = float(sin(yrotrad)) * cScale;
    
}
    

}

void reshape (int w, int h) {
    
glViewport (0, 0, (GLsizei)w, (GLsizei)h);
    

glMatrixMode (GL_PROJECTION);
    
glLoadIdentity ();
    
gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 1000.0);
    

glMatrixMode (GL_MODELVIEW);
}

int main (int argc, char **argv) {
    

glutInit(&argc, argv);
    
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA);
    

glutInitWindowSize(500, 500);
    
glutInitWindowPosition(100, 100);
    
glutCreateWindow(“A basic OpenGL Window);
    

Init();
    
glutDisplayFunc(display);
    
glutIdleFunc(display);
    

glutReshapeFunc(reshape);
    
glutKeyboardFunc(keyboard);
    
glutPassiveMotionFunc(mouseMovement);
    

glutMainLoop ();
    
return 0;
} 

Download:

Download heightfield.h Source Code for this Tutorial

Download heightfield.cpp Source Code for this Tutorial

Download main.cpp Source Code for this Tutorial

Download heightfield.raw for this Tutorial

Download texture.jpg for this Tutorial

Download jpeg.h Source Code for this Tutorial

Download jpeg.cpp Source Code for this Tutorial

Downlaod jpeglib.zip for this Tutorial

  • March 25, 2010
  • 8