forked from scarlettDiMa/OpenGL-HeightMap-Terrain
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHeightMapMeshimpl.cpp
429 lines (353 loc) · 13.9 KB
/
HeightMapMeshimpl.cpp
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
/*
* Geneva Smith
* Terrrain Generator
* File: Height Mesh Class Implementation
* --------------------------------------
* This program implements all functions that can be found in the Height Map Mesh class header
* file. It references private class variables from the HeightMeshpriv header file.
*/
//Graphic libraries
#include <GLUT/GLUT.h>
#include <OpenGL/OpenGL.h>
//C++ libraries
#include <iostream>
#include <cmath>
#include <vector>
//Associated header file
#include "Height Map Mesh.h"
using namespace std;
//Constant values
const double MAP_SCALE_X = 1.5;
const double MAP_SCALE_Z = 1.5;
const double DISPLACEMENT = 0.3;
const int MAX_RADIUS = 20;
//Maximum height to calculate colours
double maxHeight = 0;
/* Class Constructor */
/*
* The class constructor sets the private class variables for width and depth to the parameters given to it
* by the user. It then dynamically creates three 2D arrays - one for each heights. face ormals and vertex
* normals. It fills in the height cells with an intial value of zero in preparation for the summations
* computed by the terrain algorithm.
*/
HeightMesh::HeightMesh(int x, int z)
{
mapWidth = x;
mapLength = z;
//Creating a 2D array dynamically to match map xz plane
heights = new double *[mapLength];
for (int i = 0; i < mapLength; i++)
{
heights[i] = new double [mapWidth];
}
//Initializing the height values to 0 for summations later
for (int j = 0; j < mapLength; j++)
{
for (int k = 0; k < mapWidth; k++)
{
heights[j][k] = 0;
}
}
//Dynamic array creation for Face normals
faceIndex = (mapWidth - 1) * 2;
faceNormals = new vector <double> *[mapLength - 1];
for (int i = 0; i < mapLength - 1; i++)
faceNormals[i] = new vector <double> [faceIndex];
//Dynamic array creation for Point normals
vertexNormals = new vector <double> *[mapLength];
for (int i = 0; i < mapLength; i++)
vertexNormals[i] = new vector <double> [mapWidth];
}
/* Class Destructor */
HeightMesh::~HeightMesh()
{
if (heights != NULL)
{
for (int i = 0; i < mapWidth; i++)
delete [] heights[i];
delete [] heights;
height = nullptr;
}
if (vertexNormals != NULL)
{
for (int i = 0; i < mapLength; i++)
delete [] vertexNormals[i];
delete [] vertexNormals;
vertexNormals = nullptr;
}
if (faceNormals != NULL)
{
for (int i = 0; i < mapLength - 1; i++)
delete[] faceNormals[i];
delete[] faceNormals;
faceNormals = nullptr;
}
}
/* Get Functions */
/*
* These functions allow the user to retrieve private map parameters as they need.
*/
int HeightMesh::GetLength()
{
return mapLength;
}
int HeightMesh::GetWidth()
{
return mapWidth;
}
double HeightMesh::GetMaxHeight()
{
return maxHeight;
}
/* Drawing Functions */
/*
* These functions are used to draw the actual height mesh. It allows for toggling between shading types
* flat or Gouraud) and between wireframe modes (on or off).
* The overview function is used to draw a 2D overhead view of the terrain. It is coloured in the same fashion
* as the 3D map.
*/
void HeightMesh::DrawHeightMesh(bool wireframe, bool shadeflat)
{
for (int i = 0; i < mapLength - 1; i++)
{
//Determine if it is wireframe or filled terrain
if (wireframe == true) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//Shade modelling
if (shadeflat == true) glShadeModel(GL_FLAT);
else glShadeModel(GL_SMOOTH);
//Triangle strips
glBegin(GL_TRIANGLE_STRIP);
for (int j = 0; j < mapWidth; j++)
{
//Colouring the first vertex
if (wireframe == false)
{
if (heights[i][j] < 0) glColor3d(0, 1, 0);
else if (heights[i][j] < maxHeight * 0.1) glColor3d(0.05, 0.95, 0);
else if (heights[i][j] < maxHeight * 0.2) glColor3d(0.1, 0.9, 0);
else if (heights[i][j] < maxHeight * 0.3) glColor3d(0.15, 0.85, 0);
else if (heights[i][j] < maxHeight * 0.4) glColor3d(0.2, 0.8, 0);
else if (heights[i][j] < maxHeight * 0.5) glColor3d(0.25, 0.75, 0);
else if (heights[i][j] < maxHeight * 0.6) glColor3d(0.3, 0.7, 0);
else if (heights[i][j] < maxHeight * 0.7) glColor3d(0.35, 0.65, 0);
else if (heights[i][j] < maxHeight * 0.8) glColor3d(0.4, 0.6, 0);
else if (heights[i][j] < maxHeight * 0.9) glColor3d(0.45, 0.55, 0);
else glColor3d(0.5, 0.5, 0.5);
}
else glColor3d(1, 1, 1);
//Normals
if (shadeflat == true)
{
if (j > 0) glNormal3d(faceNormals[i][j - 1][0], faceNormals[i][j - 1][1], faceNormals[i][j - 1][2]);
}
else glNormal3d(vertexNormals[i][j][0], vertexNormals[i][j][1], vertexNormals[i][j][2]);
//Draw first vertex
if (wireframe == false) glVertex3d(double(i - mapLength / 2), heights[i][j], double(j - mapWidth / 2));
else glVertex3d(double(i - mapLength / 2), (heights[i][j] + 0.1), double(j - mapWidth / 2));
//Colouring the second vertex
if (wireframe == false)
{
if (heights[i + 1][j] < 0) glColor3d(0, 1, 0);
else if (heights[i + 1][j] < maxHeight * 0.1) glColor3d(0.05, 0.95, 0);
else if (heights[i + 1][j] < maxHeight * 0.2) glColor3d(0.1, 0.9, 0);
else if (heights[i + 1][j] < maxHeight * 0.3) glColor3d(0.15, 0.85, 0);
else if (heights[i + 1][j] < maxHeight * 0.4) glColor3d(0.2, 0.8, 0);
else if (heights[i + 1][j] < maxHeight * 0.5) glColor3d(0.25, 0.75, 0);
else if (heights[i + 1][j] < maxHeight * 0.6) glColor3d(0.3, 0.7, 0);
else if (heights[i + 1][j] < maxHeight * 0.7) glColor3d(0.35, 0.65, 0);
else if (heights[i + 1][j] < maxHeight * 0.8) glColor3d(0.4, 0.6, 0);
else if (heights[i + 1][j] < maxHeight * 0.9) glColor3d(0.45, 0.55, 0);
else glColor3d(0.5, 0.5, 0.5);
}
else glColor3d(1, 1, 1);
//Normals
if (shadeflat == true)
{
if (j > 0) glNormal3d(faceNormals[i][j][0], faceNormals[i][j][1], faceNormals[i][j][2]);
}
else glNormal3d(vertexNormals[i + 1][j][0], vertexNormals[i + 1][j][1], vertexNormals[i + 1][j][2]);
//Draw second vertex (finally -.-;;;)
if (wireframe == false) glVertex3d(double(i + 1 - mapLength / 2), heights[i + 1][j], double(j - mapWidth / 2));
else glVertex3d(double(i + 1 - mapLength / 2), (heights[i + 1][j] + 0.1), double(j - mapWidth / 2));
}
glEnd();
}
return;
}
void HeightMesh::DrawOverview()
{
glBegin(GL_POINTS);
for (int i = 0; i < mapLength; i++)
{
for (int j = 0; j < mapWidth; j++)
{
if (heights[i][j] < 0) glColor3d(0, 1, 0);
else if (heights[i][j] < maxHeight * 0.1) glColor3d(0.05, 0.95, 0);
else if (heights[i][j] < maxHeight * 0.2) glColor3d(0.1, 0.9, 0);
else if (heights[i][j] < maxHeight * 0.3) glColor3d(0.15, 0.85, 0);
else if (heights[i][j] < maxHeight * 0.4) glColor3d(0.2, 0.8, 0);
else if (heights[i][j] < maxHeight * 0.5) glColor3d(0.25, 0.75, 0);
else if (heights[i][j] < maxHeight * 0.6) glColor3d(0.3, 0.7, 0);
else if (heights[i][j] < maxHeight * 0.7) glColor3d(0.35, 0.65, 0);
else if (heights[i][j] < maxHeight * 0.8) glColor3d(0.4, 0.6, 0);
else if (heights[i][j] < maxHeight * 0.9) glColor3d(0.45, 0.55, 0);
else glColor3d(0.5, 0.5, 0.5);
glVertex2d(i, j);
}
}
glEnd();
return;
}
/* Terrain Algorithm */
/*
* These functions intialiaize the terrain. filling in height values and computing the face and vertex normals.
* These should only be called once by the user, unless they want to compound the effects of the terrain
* generator.
*/
vector <double> ComputeUnitNormal(vector <double> p1, vector <double> p2, vector <double> p3)
{
double x, y, z;
double length;
vector <double> u, v;
vector <double> normal;
//u vector
x = p1[0] - p2[0]; u.push_back(x);
y = p1[1] - p2[1]; u.push_back(y);
z = p1[2] - p2[2]; u.push_back(z);
//v vector
x = p3[0] - p2[0]; v.push_back(x);
y = p3[1] - p2[1]; v.push_back(y);
z = p3[2] - p2[2]; v.push_back(z);
//Normal
x = u[1] * v[2] - u[2] * v[1];
y = u[2] * v[0] - u[0] * v[2];
z = u[0] * v[1] - u[1] * v[0];
length = sqrt(x*x + y*y + z*z);
normal.push_back(-1 * x / length);
normal.push_back(-1 * y / length);
normal.push_back(-1 * z / length);
return normal;
}
void HeightMesh::CircleTerrain()
{
//Determine number of hills in terrain
const int area = mapWidth * mapLength;
const int num = floor(area / 1000.0);
const int numCircles = rand() % area + num * (mapWidth);
//Adjust height values
for (int a = 0; a < numCircles; a++)
{
//Circle parameters
const int x = rand() % mapWidth;
const int z = rand() % mapLength;
const double radius = rand() % MAX_RADIUS + 1;
//Progress bar (text version)
cout << "Drawing progress: " << a << "/" << numCircles - 1 << endl;
const int startx = (x - radius < 0) ? 0 : (x - radius);
const int startz = (z - radius < 0) ? 0 : (z - radius);
const int endx = (x + radius > mapWidth) ? mapWidth : (x + radius);
const int endz = (z + radius > mapLength) ? mapLength : (z + radius);
//Each terrain point
for (int i = startz; i < endz; i++)
{
const int dz = z - i;
for (int j = startx; j < endx; j++)
{
//Calculate y of points
const int dx = x - j;
//Calculate distance between point and circle centre
const double distance = sqrt(double((dz*dz) + (dx*dx)));
const double test = distance * 2 / radius;
if (fabs(test) <= 1.0)
{
const double cosVal = test * 3.14 / 180;
heights[i][j] += (DISPLACEMENT / 2.0) + (cos(cosVal) * DISPLACEMENT / 2.0);
if (heights[i][j] > maxHeight) maxHeight = heights[i][j];
}
}
}
}
cout << "Calculating normals...might want to pull up a YouTube video or something" << endl;
cout << "Calculating face normals..." << endl;
vector <double> point;
point.push_back(0); point.push_back(0); point.push_back(0);
vector <double> p1, p2, p3;
p1 = point; p2 = point; p3 = point;
vector <double> face;
for (int i = 0; i < mapLength - 1; i++)
{
for (int j = 0; j < faceIndex - 1; j++)
{
p1[0] = i + 1; p1[1] = heights[i + 1][j]; p1[2] = j;
p2[0] = i; p2[1] = heights[i][j]; p2[2] = j;
p3[0] = i; p3[1] = heights[i][j + 1]; p3[2] = j + 1;
face = ComputeUnitNormal(p1, p2, p3);
faceNormals[i][j] = face;
p1[0] = i; p1[1] = heights[i][j + 1]; p1[2] = j + 1;
p2[0] = i + 1; p2[1] = heights[i + 1][j]; p2[2] = j;
p3[0] = i + 1; p3[1] = heights[i + 1][j + 1]; p3[2] = j + 1;
face = ComputeUnitNormal(p1, p2, p3);
faceNormals[i][j + 1] = face;
}
}
cout << "Face normals complete...calculating point normals..." << endl;
int index;
//First row
vertexNormals[0][0] = faceNormals[0][0];
//Middle points first row
for (int i = 1; i < mapWidth - 1; i++)
{
index = i * 2;
point[0] = (faceNormals[0][index - 2][0] + faceNormals[0][index - 1][0] + faceNormals[0][index][0]) / 3;
point[1] = (faceNormals[0][index - 2][1] + faceNormals[0][index - 1][1] + faceNormals[0][index][1]) / 3;
point[2] = (faceNormals[0][index - 2][2] + faceNormals[0][index - 1][2] + faceNormals[0][index][2]) / 3;
vertexNormals[0][i] = point;
}
//Last point, first row
point[0] = (faceNormals[0][(mapWidth * 2) - 4][0] + faceNormals[0][(mapWidth * 2) - 3][0]) / 2;
point[1] = (faceNormals[0][(mapWidth * 2) - 4][1] + faceNormals[0][(mapWidth * 2) - 3][1]) / 2;
point[2] = (faceNormals[0][(mapWidth * 2) - 4][2] + faceNormals[0][(mapWidth * 2) - 3][2]) / 2;
vertexNormals[0][mapWidth - 1] = point;
//Middle rows
for (int i = 1; i < mapLength - 1; i++)
{
//First point in the row
point[0] = (faceNormals[i - 1][0][0] + faceNormals[i - 1][1][0] + faceNormals[i][0][0]) / 3;
point[1] = (faceNormals[i - 1][0][1] + faceNormals[i - 1][1][1] + faceNormals[i][0][1]) / 3;
point[2] = (faceNormals[i - 1][0][2] + faceNormals[i - 1][1][2] + faceNormals[i][0][2]) / 3;
vertexNormals[i][0] = point;
//Middle row points (columns)
for (int j = 1; j < mapWidth - 1; j++)
{
index = j * 2;
point[0] = (faceNormals[i - 1][index - 1][0] + faceNormals[i - 1][index][0] + faceNormals[i - 1][index + 1][0] + faceNormals[i][index - 2][0] + faceNormals[i][index - 1][0] + faceNormals[i][index][0]) / 6;
point[1] = (faceNormals[i - 1][index - 1][1] + faceNormals[i - 1][index][1] + faceNormals[i - 1][index + 1][1] + faceNormals[i][index - 2][1] + faceNormals[i][index - 1][1] + faceNormals[i][index][1]) / 6;
point[2] = (faceNormals[i - 1][index - 1][2] + faceNormals[i - 1][index][2] + faceNormals[i - 1][index + 1][2] + faceNormals[i][index - 2][2] + faceNormals[i][index - 1][2] + faceNormals[i][index][2]) / 6;
vertexNormals[i][j] = point;
}
//Last row point
point[0] = (faceNormals[i - 1][(mapWidth * 2) - 3][0] + faceNormals[i][(mapWidth * 2) - 4][0] + faceNormals[i][(mapWidth * 2) - 3][0]) / 3;
point[1] = (faceNormals[i - 1][(mapWidth * 2) - 3][1] + faceNormals[i][(mapWidth * 2) - 4][1] + faceNormals[i][(mapWidth * 2) - 3][1]) / 3;
point[2] = (faceNormals[i - 1][(mapWidth * 2) - 3][2] + faceNormals[i][(mapWidth * 2) - 4][2] + faceNormals[i][(mapWidth * 2) - 3][2]) / 3;
vertexNormals[i][mapWidth - 1] = point;
}
//Last row, first point
point[0] = (faceNormals[mapLength - 2][0][0] + faceNormals[mapLength - 2][1][0]) / 2;
point[1] = (faceNormals[mapLength - 2][0][1] + faceNormals[mapLength - 2][1][1]) / 2;
point[2] = (faceNormals[mapLength - 2][0][2] + faceNormals[mapLength - 2][1][2]) / 2;
vertexNormals[mapLength - 1][0] = point;
//Last row, middle points
for (int i = 1; i < mapWidth - 1; i++)
{
index = i * 2;
point[0] = (faceNormals[mapLength - 2][index - 1][0] + faceNormals[mapLength - 2][index][0] + faceNormals[mapLength - 2][index + 1][0]) / 3;
point[1] = (faceNormals[mapLength - 2][index - 1][1] + faceNormals[mapLength - 2][index][1] + faceNormals[mapLength - 2][index + 1][1]) / 3;
point[2] = (faceNormals[mapLength - 2][index - 1][2] + faceNormals[mapLength - 2][index][2] + faceNormals[mapLength - 2][index + 1][2]) / 3;
vertexNormals[mapLength - 1][i] = point;
}
//Last row, last point
vertexNormals[mapLength - 1][mapWidth - 1] = faceNormals[mapLength - 2][(mapWidth * 2) - 3];
cout << "Done...(whew!)...drawing terrain..." << endl;
return;
}