mercator 0.4.0
A terrain generation library for the Worldforge system.
Terrain.cpp
1// This file may be redistributed and modified only under the terms of
2// the GNU General Public License (See COPYING for details).
3// Copyright (C) 2003 Alistair Riddoch, Damien McGinnes
4
5#ifdef HAVE_CONFIG_H
6#include "config.h"
7#endif
8
9#include "iround.h"
10
11#include "Terrain.h"
12
13#include "Matrix.h"
14#include "Segment.h"
15#include "TerrainMod.h"
16#include "Shader.h"
17#include "Area.h"
18#include "Surface.h"
19
20#include <iostream>
21#include <algorithm>
22
23#include <cstdio>
24
25namespace Mercator {
26
27const unsigned int Terrain::DEFAULT;
28const unsigned int Terrain::SHADED;
29constexpr float Terrain::defaultLevel;
30
31
32Terrain::Terrain(unsigned int options, int resolution) : m_options(options),
33 m_res(resolution),
34 m_spacing((float)resolution)
35{
36}
37
38Terrain::~Terrain() = default;
39
40void Terrain::addShader(const Shader * t, int id)
41{
42 if (m_shaders.count(id)) {
43 std::cerr << "WARNING: duplicate use of shader ID " << id << std::endl;
44 }
45
46 m_shaders[id] = t;
47
48 auto I = m_segments.begin();
49 auto Iend = m_segments.end();
50 for (; I != Iend; ++I) {
51 auto J = I->second.begin();
52 auto Jend = I->second.end();
53 for (; J != Jend; ++J) {
54 auto& seg=J->second;
55
56 Segment::Surfacestore & sss = seg->getSurfaces();
57 sss[id] = t->newSurface(*seg);
58 }
59 }
60}
61
62void Terrain::removeShader(const Shader * t, int id)
63{
64
65 m_shaders.erase(m_shaders.find(id));
66
67 // Delete all surfaces for this shader
68 auto I = m_segments.begin();
69 auto Iend = m_segments.end();
70 for (; I != Iend; ++I) {
71 auto J = I->second.begin();
72 auto Jend = I->second.end();
73 for (; J != Jend; ++J) {
74 auto& seg=J->second;
75
76 Segment::Surfacestore & sss = seg->getSurfaces();
77 auto K = sss.find(id);
78 if (K != sss.end()) {
79 sss.erase(K);
80 }
81 }
82 }
83}
84
85
86
87void Terrain::addSurfaces(Segment & seg)
88{
90 if (!sss.empty()) {
91 std::cerr << "WARNING: Adding surfaces to a terrain segment which has surfaces"
92 << std::endl << std::flush;
93 sss.clear();
94 }
95
96 auto I = m_shaders.begin();
97 auto Iend = m_shaders.end();
98 for (; I != Iend; ++I) {
99 // shader doesn't touch this segment, skip
100 if (!I->second->checkIntersect(seg)) {
101 continue;
102 }
103
104 sss[I->first] = I->second->newSurface(seg);
105 }
106}
107
108void Terrain::shadeSurfaces(Segment & seg)
109{
110 seg.populateSurfaces();
111}
112
113
114float Terrain::get(float x, float z) const
115{
117 if ((s == nullptr) || (!s->isValid())) {
119 }
120 return s->get(I_ROUND(x) - s->getXRef(), I_ROUND(z) - s->getZRef());
121}
122
123bool Terrain::getHeight(float x, float z, float& h) const
124{
126 if ((!s) || (!s->isValid())) {
127 return false;
128 }
129 s->getHeight(x - s->getXRef(), z - s->getZRef(), h);
130 return true;
131}
132
133bool Terrain::getHeightAndNormal(float x, float z, float & h,
134 WFMath::Vector<3> & n) const
135{
137 if ((!s) || (!s->isValid())) {
138 return false;
139 }
140 s->getHeightAndNormal(x - s->getXRef(), z - s->getZRef(), h, n);
141 return true;
142}
143
144bool Terrain::getBasePoint(int x, int z, BasePoint& y) const
145{
146 auto I = m_basePoints.find(x);
147 if (I == m_basePoints.end()) {
148 return false;
149 }
150 auto J = I->second.find(z);
151 if (J == I->second.end()) {
152 return false;
153 }
154 y = J->second;
155 return true;
156}
157
158void Terrain::setBasePoint(int x, int z, const BasePoint& y)
159{
160 m_basePoints[x][z] = y;
161 bool pointIsSet[3][3];
162 BasePoint existingPoint[3][3];
163 for(int i = x - 1, ri = 0; i < x + 2; ++i, ++ri) {
164 for(int j = z - 1, rj = 0; j < z + 2; ++j, ++rj) {
165 pointIsSet[ri][rj] = getBasePoint(i, j, existingPoint[ri][rj]);
166 }
167 }
168 for(int i = x - 1, ri = 0; i < x + 1; ++i, ++ri) {
169 for(int j = z - 1, rj = 0; j < z + 1; ++j, ++rj) {
170 Segment * existingSegment = getSegmentAtIndex(i, j);
171 if (!existingSegment) {
172 bool complete = pointIsSet[ri][rj] &&
173 pointIsSet[ri + 1][rj + 1] &&
174 pointIsSet[ri + 1][rj] &&
175 pointIsSet[ri][rj + 1];
176 if (!complete) {
177 continue;
178 }
179 auto newSegment = std::make_unique<Segment>(i * m_res, j * m_res, m_res);
180 Matrix<2, 2, BasePoint> & cp = newSegment->getControlPoints();
181 for(unsigned int k = 0; k < 2; ++k) {
182 for(unsigned int l = 0; l < 2; ++l) {
183 cp(k, l) = existingPoint[ri + k][rj + l];
184 }
185 }
186
187 for (auto& entry : m_terrainMods) {
188 auto& terrainMod = entry.second.terrainMod;
189 if (terrainMod->checkIntersects(*newSegment)) {
190 newSegment->updateMod(entry.first, terrainMod.get());
191 }
192 }
193
194 // apply shaders last, after all other data is in place
195 if (isShaded()) {
196 addSurfaces(*newSegment);
197 }
198
199 newSegment->setCornerPoint(ri ? 0 : 1, rj ? 0 : 1, y);
200 m_segments[i][j] = std::move(newSegment);
201 } else {
202 existingSegment->setCornerPoint(ri ? 0 : 1, rj ? 0 : 1, y);
203 }
204 }
205 }
206}
207
209{
210 auto I = m_segments.find(x);
211 if (I == m_segments.end()) {
212 return nullptr;
213 }
214 auto J = I->second.find(z);
215 if (J == I->second.end()) {
216 return nullptr;
217 }
218 return J->second.get();
219}
220
221void Terrain::processSegments(const WFMath::AxisBox<2>& area,
222 const std::function<void(Segment&, int, int)>& func) const
223{
224 int lx = I_ROUND(std::floor((area.lowCorner()[0]) / m_spacing));
225 int lz = I_ROUND(std::floor((area.lowCorner()[1]) / m_spacing));
226 int hx = I_ROUND(std::ceil((area.highCorner()[0]) / m_spacing));
227 int hz = I_ROUND(std::ceil((area.highCorner()[1]) / m_spacing));
228
229 for (int i = lx; i < hx; ++i) {
230 for (int j = lz; j < hz; ++j) {
231 Segment *s = getSegmentAtIndex(i, j);
232 if (!s) {
233 continue;
234 }
235 func(*s, i, j);
236 }
237 }
238}
239
240
241Terrain::Rect Terrain::updateMod(long id, std::unique_ptr<TerrainMod> mod)
242{
243 std::set<Segment*> removed, added, updated;
244 std::unique_ptr<TerrainMod> old_mod;
245 auto I = m_terrainMods.find(id);
246
247 Rect old_box;
248 if (I != m_terrainMods.end()) {
249 auto& entry = I->second;
250
251 old_box = entry.rect;
252 //Make sure old mod survives within this method.
253 old_mod = std::move(entry.terrainMod);
254
255
256 int lx=I_ROUND(std::floor((old_box.lowCorner()[0] - 1.f) / m_spacing));
257 int lz=I_ROUND(std::floor((old_box.lowCorner()[1] - 1.f) / m_spacing));
258 int hx=I_ROUND(std::ceil((old_box.highCorner()[0] + 1.f) / m_spacing));
259 int hz=I_ROUND(std::ceil((old_box.highCorner()[1] + 1.f) / m_spacing));
260
261 for (int i=lx;i<hx;++i) {
262 for (int j=lz;j<hz;++j) {
264 if (!s) {
265 continue;
266 }
267
268 removed.insert(s);
269
270 } // of y loop
271 } // of x loop
272
273 if (!mod) {
274 m_terrainMods.erase(id);
275 }
276 }
277
278 if (mod) {
279 auto rect = mod->bbox();
280 int lx=I_ROUND(std::floor((rect.lowCorner()[0] - 1.f) / m_spacing));
281 int lz=I_ROUND(std::floor((rect.lowCorner()[1] - 1.f) / m_spacing));
282 int hx=I_ROUND(std::ceil((rect.highCorner()[0] + 1.f) / m_spacing));
283 int hz=I_ROUND(std::ceil((rect.highCorner()[1] + 1.f) / m_spacing));
284
285 for (int i=lx;i<hx;++i) {
286 for (int j=lz;j<hz;++j) {
288 if (!s) {
289 continue;
290 }
291
292 auto J = removed.find(s);
293 if (J == removed.end()) {
294 added.insert(s);
295 } else {
296 updated.insert(s);
297 removed.erase(J);
298 }
299 } // of y loop
300 } // of x loop
301
302 for (auto& segment : added) {
303 if (mod->checkIntersects(*segment)) {
304 segment->updateMod(id, mod.get());
305 }
306 }
307 for (auto& segment : updated) {
308 if (mod->checkIntersects(*segment)) {
309 segment->updateMod(id, mod.get());
310 } else {
311 segment->updateMod(id, nullptr);
312 }
313 }
314
315 m_terrainMods[id] = TerrainModEntry{std::move(mod), rect};
316 }
317
318 for (auto& segment : removed) {
319 segment->updateMod(id, nullptr);
320 }
321
322
323 return old_box;
324}
325
326bool Terrain::hasMod(long id) const
327{
328 return m_terrainMods.find(id) != m_terrainMods.end();
329}
330
331const TerrainMod* Terrain::getMod(long id) const
332{
333 auto I = m_terrainMods.find(id);
334 if (I != m_terrainMods.end()) {
335 return I->second.terrainMod.get();
336 }
337 return nullptr;
338}
339
340const Area* Terrain::getArea(long id) const
341{
342 auto I = m_terrainAreas.find(id);
343 if (I != m_terrainAreas.end()) {
344 return I->second.terrainArea.get();
345 }
346 return nullptr;
347}
348
349
350Terrain::Rect Terrain::updateArea(long id, std::unique_ptr<Area> area)
351{
352 std::set<Segment*> removed, added, updated;
353 std::unique_ptr<Area> old_area;
354
355 auto I = m_terrainAreas.find(id);
356
357 Rect old_box;
358 if (I != m_terrainAreas.end()) {
359 auto& entry = I->second;
360
361 old_box = entry.rect;
362 //Make sure old area survives within this method.
363 old_area = std::move(entry.terrainArea);
364
365
366 int lx=I_ROUND(std::floor((old_box.lowCorner()[0] - 1.f) / m_spacing));
367 int lz=I_ROUND(std::floor((old_box.lowCorner()[1] - 1.f) / m_spacing));
368 int hx=I_ROUND(std::ceil((old_box.highCorner()[0] + 1.f) / m_spacing));
369 int hz=I_ROUND(std::ceil((old_box.highCorner()[1] + 1.f) / m_spacing));
370
371 for (int i=lx;i<hx;++i) {
372 for (int j=lz;j<hz;++j) {
374 if (!s) {
375 continue;
376 }
377
378 removed.insert(s);
379
380 } // of y loop
381 } // of x loop
382
383 if (!area) {
384 m_terrainAreas.erase(id);
385 }
386 }
387
388 if (area) {
389 auto rect = area->bbox();
390 int lx=I_ROUND(std::floor((rect.lowCorner()[0] - 1.f) / m_spacing));
391 int lz=I_ROUND(std::floor((rect.lowCorner()[1] - 1.f) / m_spacing));
392 int hx=I_ROUND(std::ceil((rect.highCorner()[0] + 1.f) / m_spacing));
393 int hz=I_ROUND(std::ceil((rect.highCorner()[1] + 1.f) / m_spacing));
394
395 for (int i=lx;i<hx;++i) {
396 for (int j=lz;j<hz;++j) {
398 if (!s) {
399 continue;
400 }
401
402 auto J = removed.find(s);
403 if (J == removed.end()) {
404 added.insert(s);
405 } else {
406 updated.insert(s);
407 removed.erase(J);
408 }
409 } // of y loop
410 } // of x loop
411
412 const Shader* shader = nullptr;
413 auto shaderI = m_shaders.find(area->getLayer());
414 if (shaderI != m_shaders.end()) {
415 shader = shaderI->second;
416 }
417
418 for (auto& segment : added) {
419 if (area->checkIntersects(*segment)) {
420 segment->updateArea(id, area.get(), shader);
421 }
422 }
423 for (auto& segment : updated) {
424 if (area->checkIntersects(*segment)) {
425 segment->updateArea(id, area.get(), shader);
426 } else {
427 segment->updateArea(id, nullptr, nullptr);
428 }
429 }
430
431 m_terrainAreas[id] = TerrainAreaEntry{std::move(area), rect};
432 }
433
434 for (auto& segment : removed) {
435 segment->updateArea(id, nullptr, nullptr);
436 }
437
438
439 return old_box;
440}
441
442
443
444} // namespace Mercator
Point on the fundamental grid that is used as the basis for terrain.
Definition: BasePoint.h:19
A fixed sized array of objects.
Definition: Matrix.h:14
Class storing heightfield and other data for a single fixed size square area of terrain defined by fo...
Definition: Segment.h:37
bool isValid() const
Check whether this Segment contains valid point data.
Definition: Segment.h:105
void setCornerPoint(unsigned int x, unsigned int z, const BasePoint &bp)
Set the BasePoint data for one of the four that define this Segment.
Definition: Segment.h:117
const Surfacestore & getSurfaces() const
Accessor for list of attached Surface objects.
Definition: Segment.h:133
int getXRef() const
Accessor for Global x reference of this segment.
Definition: Segment.h:93
std::map< int, std::unique_ptr< Surface > > Surfacestore
STL map of pointers to Surface objects.
Definition: Segment.h:40
void getHeightAndNormal(float x, float z, float &h, WFMath::Vector< 3 > &normal) const
Get an accurate height and normal vector at a given coordinate relative to this segment.
Definition: Segment.cpp:223
int getZRef() const
Accessor for Global y reference of this segment.
Definition: Segment.h:98
float get(int x, int z) const
Get the height at a relative integer position in the Segment.
Definition: Segment.h:173
Base class for Shader objects which create surface data for use when rendering terrain.
Definition: Shader.h:25
std::unique_ptr< Surface > newSurface(const Segment &) const
Create a new Surface which matches the requirements of this shader.
Definition: Shader.cpp:27
Base class for modifiers to the procedurally generated terrain.
Definition: TerrainMod.h:21
bool getHeight(float x, float z, float &h) const
Get an accurate height at a given coordinate x,z.
Definition: Terrain.cpp:123
bool getHeightAndNormal(float x, float z, float &h, WFMath::Vector< 3 > &n) const
Get an accurate height and normal vector at a given coordinate x,z.
Definition: Terrain.cpp:133
bool getBasePoint(int x, int z, BasePoint &y) const
Get the BasePoint at a given base point coordinate.
Definition: Terrain.cpp:144
void processSegments(const WFMath::AxisBox< 2 > &area, const std::function< void(Segment &, int, int)> &func) const
Definition: Terrain.cpp:221
void addShader(const Shader *t, int id)
Add a new Shader to the list for this terrain.
Definition: Terrain.cpp:40
bool hasMod(long id) const
Checks if a mod with the supplied id has been registered with the terrain.
Definition: Terrain.cpp:326
Rect updateMod(long id, std::unique_ptr< TerrainMod > mod)
Updates the terrain with a mod.
Definition: Terrain.cpp:241
WFMath::AxisBox< 2 > Rect
Bounding box.
Definition: Terrain.h:40
static constexpr float defaultLevel
Height value used when no data is available.
Definition: Terrain.h:140
Segment * getSegmentAtIndex(int x, int z) const
Get the Segment at a given index.
Definition: Terrain.cpp:208
void removeShader(const Shader *t, int id)
remove a Shader from the list for this terrain.
Definition: Terrain.cpp:62
int posToIndex(float pos) const
Converts the supplied position into a segment index.
Definition: Terrain.h:330
float get(float x, float z) const
Get the height value at a given coordinate x,z.
Definition: Terrain.cpp:114
Rect updateArea(long id, std::unique_ptr< Area > a)
Updates the terrain affected by an area.
Definition: Terrain.cpp:350
~Terrain()
Destroy Terrain object, deleting contained objects.
static const unsigned int DEFAULT
value provided for no flags set.
Definition: Terrain.h:56
Terrain(unsigned int options=DEFAULT, int resolution=defaultResolution)
Construct a new Terrain object with optional options and resolution.
Definition: Terrain.cpp:32
void setBasePoint(int x, int z, const BasePoint &y)
Set the BasePoint value at a given base point coordinate.
Definition: Terrain.cpp:158
static const unsigned int SHADED
set if shaders are going to be used on this terrain.
Definition: Terrain.h:58