-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathterrain.cpp
More file actions
574 lines (427 loc) · 14.9 KB
/
terrain.cpp
File metadata and controls
574 lines (427 loc) · 14.9 KB
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
#include "terrain.h"
// Load images and initialize variables
terrain::terrain()
{
initialized = false;
heightmap = 0;
angle = 0;
// The display list is not valid
dl_valid = false;
dl_handle = 0;
// Try to load the heightmap
if (!load_heightmap("../../data/tex_height.bmp"))
return;
// Try to load the texture
if (!load_texture("../../data/tex_topo.bmp", &texture_handle))
return;
initialized = true;
// Basic initial settings
set_show_solid(true);
set_show_wireframe(false);
set_show_levels(false);
}
// Unload images and textures
terrain::~terrain()
{
// Delete the texture
glDeleteTextures(1, &texture_handle);
// Free the memory for the height map
BMP_Free(heightmap);
// Delete the display list if neccessary
if (glIsList(dl_handle))
glDeleteLists(dl_handle, 1);
}
// Render the actual scene
void terrain::render()
{
// Enable depth testing
glEnable(GL_DEPTH_TEST);
// Enable automatic normalization of normals
glEnable(GL_NORMALIZE);
// Clear the screen
glClearColor(1,1,1,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Nothing to do if we are not properly initialized
if (!initialized)
return;
// Setup perspective and modelview matrix
setup_projection();
// Setup light
setup_light();
// Render the solid terrain with lighting and texture mapping
if (show_solid)
render_solid_terrain();
// Render the outline of the terrain
if (show_wireframe)
render_wireframe_terrain();
// Eventually render level lines
if (show_levels)
render_level_lines();
}
// Render the terrain as solid
void terrain::render_solid_terrain()
{
// Enable lighting
glEnable(GL_LIGHTING);
// This is a trick which is neccessary in order to draw lines
// later on. Otherwise there would be artifacts due to a z-fight.
glPolygonOffset(1, 1);
glEnable(GL_POLYGON_OFFSET_FILL);
/********
Task 2.2.4. Activate 2D texture mapping and bind the texture that
is identified by the handle "texture_handle". Do not remove
any of the code in this method.
Aufgabe 2.2.4. Aktivieren Sie 2D-Texturierung und binden Sie die
Textur, die ueber das Handle "texture_handle" identifiziert
ist. Entfernen Sie keinen Code in dieser Methode.
************/
glBindTexture(GL_TEXTURE_2D, texture_handle);
glEnable(GL_TEXTURE_2D);
// Set the material color to white
glColor3d(1, 1, 1);
// Render the terrain
render_terrain();
// Disable texture mapping
glDisable(GL_TEXTURE_2D);
// Disable support for depth buffer offsets
glDisable(GL_POLYGON_OFFSET_FILL);
// Disable lighting
glDisable(GL_LIGHTING);
}
// Render the terrain as wireframe
void terrain::render_wireframe_terrain()
{
// Set the line width to be 1 pixel
glLineWidth(1.0);
// Set the draw mode to draw outlines of polygons
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Set the color to black
glColor3d(0, 0, 0);
// Render the terrain
render_terrain();
// Set the draw mode to fill polygons
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
// Render the terrain
void terrain::render_terrain()
{
// Store width and height for faster access
int map_width = get_heightmap_width();
int map_height = get_heightmap_height();
// Move and scale the coordinate system so that we can work with
// whole units. That means that a vertex at height map position (x, y)
// with a height of h can be placed as glVertex3d(x, h, y). The
// terrain extents in the XZ-layer while the height is on the Y-axis.
glPushMatrix();
glTranslated(-1, 0, -1);
glScaled(2.0/(double)map_width, 1.0/256.0, 2.0/(double)map_height);
/********
Task 2.2.1. Complete the code below to create a regular grid which expands
along the X-Z-layer. Create the grid by specifying triangle
strips for each line. The coordinate system is already scaled
and translated to use the values of the variables "x" and "y"
defined below directly as coordinates. The initial height of
the grid shall be 0.
Aufgabe 2.2.1. Vervollstaendigen Sie den nachfolgenden Quelltext um die
Erstellung eines regelmaessigen Gitters das sich ueber die
X-Z-Ebene erstreckt. Erstellen Sie dieses Gitter, indem Sie
zeilenweise Dreiecksstreifen definieren. Das Koordinatensystem
ist bereits skaliert und verschoben, so dass Sie die Werte
der Variablen "x" und "y", die unten definiert werden direkt
als Koordinaten einsetzen koennen. Das Gitter soll zunaechst
eine Hoehe von 0 besitzen.
Task 2.2.2. Now elevate the height of the vertices to create the grid terrain
that corresponds to the height map. Use "get_heightmap_value(x, y)".
Aufgabe 2.2.2. Heben Sie jetzt die Vertices an um ein Drahtgitterterrain zu erstellen,
das zur Hoehenkarte korrespondiert. Nutzen Sie die Methode
"get_heightmap_value(x, y)".
Task 2.2.3. Make sure to call "set_normal" for every vertex and continue this
task in "set_normal".
Aufgabe 2.2.3. Stellen Sie sicher, dass Sie "set_normal" für jeden Vertex aufrufen
und fahren Sie in "set_normal" mit der Aufgabe fort.
Task 2.2.4. Activate texture mapping and bind the texture in the method
"render_solid_terrain". Provide 2D texture coordinates per vertex in
this method using "glTexCoord2d".
Aufgabe 2.2.4. Aktivieren Sie Texturmapping und binden Sie eine Textur in der
Methode "render_solid_terrain". Spezifizieren Sie in dieser Methode pro
Vertex eine Texturkoordinate mittels der Methode "glTexCoord2d".
*********/
// Go through all rows (-1)
for (int y = 0; y<map_height-1; y++) {
glBegin(GL_TRIANGLE_STRIP);
// Draw one strip+
for (int x = 0; x<map_width; x++) {
glTexCoord2d((double) x / map_width, (double) y / (map_height - 1));
set_normal(x, y);
glVertex3d(x, get_heightmap_value(x, y), y);
glTexCoord2d((double) x / map_width, (double) (y + 1) / (map_height - 1));
set_normal(x, y + 1);
glVertex3d(x, get_heightmap_value(x, y + 1), y + 1);
}
glEnd();
}
glPopMatrix();
}
// Calculate and set the normal for height map entry (x,y)
void terrain::set_normal(int x, int y)
{
/********
Task 2.2.3. Calculate the normal for the vertex that corresponds to
the height map value (x, y). You can either use forward differences,
backward differences or central differences. The latter will give
the best results. The calculation is done by first determining direction
vectors in X and Z and then using vector operations to get a vector that
is perpendicular to both. You find some examples for vector operations in
this project below. Do not forget to pass the normal to OpenGL using
the command glNormal3d.
Aufgabe 2.2.3. Berechnen Sie die Normalen fuer den Vertex der zum Hoehenwert (x, y)
gehoert. Sie koennen entweder Vorwaerts-, Rueckwaerts- oder Zentral-
Differenzen verwenden. Die letzte Methode erzeugt die besten Resultate.
Fuer die Berechnung werden zunaechst Richtungvektoren in X- und Z-Richtung
gebildet und anschliessend mittels Vektoroperationen ein zu beiden Vektoren
senkrechter Vektor berechnet. Nachfolgend ein paar Beispiele fuer Vektor-
rechnungen in diesem Projekt. Vergessen Sie nicht die Normale nach der
Berechnung mittels glNormal3d an OpenGL zu senden.
// Create a vector
vec3d vec1(1.0, 1.0, 1.0);
// Create another vector
vec3d vec2(0.5, -1.0, 2.0);
// Normalize the first vector
vec1.normalize();
// Add the first vector to the second
vec2 = vec1 + vec2;
// Increase the length of the first vector
vec1 *= 10.0;
// Calculate the dot product
double x = dot(vec1, vec2);
// Calculate the cross product
vec3d vec3 = cross(vec1, vec2);
// Get the length
double l = vec3.length();
// Get the components for a vector
double x = vec3.x();
double y = vec3.y();
double z = vec3.z();
*****************/
vec3d v1(x, get_heightmap_value(x, y), y);
vec3d v2(x, get_heightmap_value(x, y + 1), y + 1);
vec3d v3(x + 1, get_heightmap_value(x + 1, y), y);
v2 = v2 - v1;
v3 = v3 - v1;
v1 = cross(v2, v3);
glNormal3d(v1.x(), v1.y(), v1.z());
}
// Render height level lines
void terrain::render_level_lines()
{
// Set color to gray
glColor3d(0.5, 0.5, 0.5);
// Set the line width to 3 pixels
glLineWidth(2.0);
// Change the coordinate system so that one can work with whole units
glPushMatrix();
glTranslated(-1, 0, -1);
glScaled(2.0/(double)get_heightmap_width(), 1.0/256.0, 2.0/(double)get_heightmap_height());
// Connect with lines
glBegin(GL_LINES);
// Render lines which were created in "create_level_lines" and stored into
// the list "level_lines"
for (int i=0; i<(int)level_lines.size(); i+=2) {
glVertex3d(level_lines[i ].x(), level_lines[i ].y(), level_lines[i ].z());
glVertex3d(level_lines[i+1].x(), level_lines[i+1].y(), level_lines[i+1].z());
}
glEnd();
// Set the width back to 1.0
glLineWidth(1.0);
glPopMatrix();
}
// Create height lines for the level "level"
void terrain::create_level_line(int level)
{
/********
Additional Task: Find iso lines with the "Marching Squares" algorithm for the
height value "level". Store the start and end points of the
found lines in the list "level_lines".
Zusatzaufgabe: Finden Sie Hoehenlinien mittels des "Marching Squares"-Algorithmus
fuer den Hoehenwert "level". Legen Sie die Start- und Endpunkte
der gefundenen Linien in der Liste "level_lines" ab.
*************/
}
// Set the light parameters
void terrain::setup_light()
{
// Enable lighting and colored materials
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
// Enable light source 0
glEnable(GL_LIGHT0);
// Set the modelview matrix to be the identity to avoid
// having the light position moved with the terrain.
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// Set light parameters
float position[4]={1.0f,1.0f,1.0f,1.0f};
float ambient_color[4]={0.0f,0.0f,0.0f,1.0f};
float diffuse_color[4]={1.0f,1.0f,1.0f,1.0f};
float specular_color[4]={1.0f,1.0f,1.0f,1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, position);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color);
glLightf(GL_LIGHT0,GL_SPOT_EXPONENT, 10.0f);
glPopMatrix();
glDisable(GL_LIGHTING);
}
// Set the projection and the view
void terrain::setup_projection()
{
// For projection choose a perspective matrix with an aperture angle of
// 45deg, an aspect ratio that corresponds to the width and height of the
// window, z_near at 0.01 and z_far at 10.0
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, glutGet(GLUT_WINDOW_WIDTH) / (double)glutGet(GLUT_WINDOW_HEIGHT), 0.01, 10.0);
// For the modelview matrix choose a view from the position (2.5, 2.5, 0) to
// the position (0, 0, 0) where the up-direction is (0, 1, 0).
// Also rotate with the specified angle around the Y-axis
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(2.5,2.5,0, 0,0,0, 0,1,0);
glRotated(angle, 0,1,0);
}
// Advance one frame
void terrain::advance_frame()
{
// Increase angle and perform a modulo 360
angle = (angle+1)%360;
}
// (De)activate solid rendering
void terrain::set_show_solid(bool state)
{
show_solid = state;
// The current display list is outdated now
dl_valid = false;
}
// (De)activate wireframe rendering
void terrain::set_show_wireframe(bool state)
{
show_wireframe = state;
// The current display list is outdated now
dl_valid = false;
}
// (De)activate height level line rendering
void terrain::set_show_levels(bool state)
{
show_levels = state;
// If level lines shall be displayed then
// recreate them in equidistant ranges
if (show_levels) {
level_lines.clear();
for (int i=20; i<=255; i+=20)
create_level_line(i);
}
// The current display list is outdated now
dl_valid = false;
}
// Load the height map from the file "filename"
bool terrain::load_heightmap(const char *filename)
{
// Load the heightmap by reading a bmp file
heightmap = BMP_ReadFile(filename);
// Return false and show an error message if the file
// could not be loaded
if (BMP_GetError() != BMP_OK) {
std::cout<<BMP_GetErrorDescription()<<std::endl;
return false;
}
// All went well...
return true;
}
// Load a texture and store its handle in "handle"
bool terrain::load_texture(const char *filename, GLuint *handle)
{
BMP *bitmap;
// Load the texture by reading a bmp file
bitmap = BMP_ReadFile(filename);
// Return false and show an error message if the file
// could not be loaded
if (BMP_GetError() != BMP_OK) {
std::cout<<BMP_GetErrorDescription()<<std::endl;
return false;
}
// Get a pointer to the bitmap data
unsigned char* data = BMP_GetImageData(bitmap);
// Generate one texture and store its ID in "handle"
glGenTextures(1, handle);
// Bind the texture
glBindTexture(GL_TEXTURE_2D, *handle);
// Enable linear blending between different mipmapping levels
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
// Clamp the texture at the borders
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
// Transfer the image data to the graphics card.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, BMP_GetWidth(bitmap), BMP_GetHeight(bitmap), 0, GL_RGB, GL_UNSIGNED_BYTE, data);
// Not needed anymore
free(data);
BMP_Free(bitmap);
// Unbind texture
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
// Return the width of the height map
int terrain::get_heightmap_width() const
{
return BMP_GetWidth(heightmap);
}
// Return the height of the height map
int terrain::get_heightmap_height() const
{
return BMP_GetHeight(heightmap);
}
// Return the height value at (x,y)
int terrain::get_heightmap_value(int x, int y) const
{
// Returns one value from the heightmap. The image should
// be in grayscale so just the red channel is returned.
// The image is mirrored at the edges (by adjusting the
// coordinates) to assure correct normal estimation.
unsigned char r, g, b;
// Mirror at the left and upper edge if neccessary
x = abs(x);
y = abs(y);
// Mirror at the right and bottom edge if neccessary
if (x>=get_heightmap_width())
x = 2*get_heightmap_width()-x-1;
if (y>=get_heightmap_height())
y = 2*get_heightmap_height()-y-1;
// Read the pixel and return the red component
BMP_GetPixelRGB(heightmap, x, y, &r, &g, &b);
return r;
}
// Set debug text
void terrain::set_text(stringstream &stream)
{
if (!initialized) {
stream<<"Cannot show terrain - not all files were loaded!";
return;
}
stream<<"Showing terrain";
if (!show_solid && !show_wireframe && !show_levels) {
stream<<" (all features disabled)";
return;
}
stream<<" (";
if (show_solid)
stream<<"as solid geometry";
if (show_solid && show_wireframe)
stream<<", ";
if (show_wireframe)
stream<<"as wireframe";
if (show_wireframe && show_levels)
stream<<", ";
if (show_levels)
stream<<"with level lines";
stream<<")";
}