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 
25 namespace Mercator {
26 
27 const unsigned int Terrain::DEFAULT;
28 const unsigned int Terrain::SHADED;
29 constexpr float Terrain::defaultLevel;
30 
31 
32 Terrain::Terrain(unsigned int options, int resolution) : m_options(options),
33  m_res(resolution),
34  m_spacing((float)resolution)
35 {
36 }
37 
38 Terrain::~Terrain() = default;
39 
40 void 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 
62 void 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 
87 void Terrain::addSurfaces(Segment & seg)
88 {
89  Segment::Surfacestore & sss = seg.getSurfaces();
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 
108 void Terrain::shadeSurfaces(Segment & seg)
109 {
110  seg.populateSurfaces();
111 }
112 
113 
114 float Terrain::get(float x, float z) const
115 {
117  if ((s == nullptr) || (!s->isValid())) {
118  return Terrain::defaultLevel;
119  }
120  return s->get(I_ROUND(x) - s->getXRef(), I_ROUND(z) - s->getZRef());
121 }
122 
123 bool 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 
133 bool 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 
144 bool 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 
158 void 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 
208 Segment * Terrain::getSegmentAtIndex(int x, int z) const
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 
221 void 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 
241 Terrain::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) {
263  Segment *s=getSegmentAtIndex(i,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) {
287  Segment *s=getSegmentAtIndex(i,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 
326 bool Terrain::hasMod(long id) const
327 {
328  return m_terrainMods.find(id) != m_terrainMods.end();
329 }
330 
331 const 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 
340 const 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 
350 Terrain::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) {
373  Segment *s=getSegmentAtIndex(i,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) {
397  Segment *s=getSegmentAtIndex(i,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
Mercator::Segment::Surfacestore
std::map< int, std::unique_ptr< Surface > > Surfacestore
STL map of pointers to Surface objects.
Definition: Segment.h:40
Mercator::Segment::getXRef
int getXRef() const
Accessor for Global x reference of this segment.
Definition: Segment.h:93
Mercator::Segment::getZRef
int getZRef() const
Accessor for Global y reference of this segment.
Definition: Segment.h:98
Mercator::Terrain::DEFAULT
static const unsigned int DEFAULT
value provided for no flags set.
Definition: Terrain.h:56
Mercator::Terrain::defaultLevel
static constexpr float defaultLevel
Height value used when no data is available.
Definition: Terrain.h:140
Mercator::Terrain::getSegmentAtIndex
Segment * getSegmentAtIndex(int x, int z) const
Get the Segment at a given index.
Definition: Terrain.cpp:208
Mercator::Segment::getSurfaces
const Surfacestore & getSurfaces() const
Accessor for list of attached Surface objects.
Definition: Segment.h:133
Mercator::Segment::getHeightAndNormal
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
Mercator::Shader::newSurface
std::unique_ptr< Surface > newSurface(const Segment &) const
Create a new Surface which matches the requirements of this shader.
Definition: Shader.cpp:27
Mercator::Terrain::getHeightAndNormal
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
Mercator::Terrain::hasMod
bool hasMod(long id) const
Checks if a mod with the supplied id has been registered with the terrain.
Definition: Terrain.cpp:326
Mercator::Segment
Class storing heightfield and other data for a single fixed size square area of terrain defined by fo...
Definition: Segment.h:37
Mercator::Terrain::updateArea
Rect updateArea(long id, std::unique_ptr< Area > a)
Updates the terrain affected by an area.
Definition: Terrain.cpp:350
Mercator::Matrix
A fixed sized array of objects.
Definition: Matrix.h:14
Mercator::Terrain::updateMod
Rect updateMod(long id, std::unique_ptr< TerrainMod > mod)
Updates the terrain with a mod.
Definition: Terrain.cpp:241
Mercator::Terrain::getHeight
bool getHeight(float x, float z, float &h) const
Get an accurate height at a given coordinate x,z.
Definition: Terrain.cpp:123
Mercator::Terrain::getBasePoint
bool getBasePoint(int x, int z, BasePoint &y) const
Get the BasePoint at a given base point coordinate.
Definition: Terrain.cpp:144
Mercator::Terrain::setBasePoint
void setBasePoint(int x, int z, const BasePoint &y)
Set the BasePoint value at a given base point coordinate.
Definition: Terrain.cpp:158
Mercator::Segment::get
float get(int x, int z) const
Get the height at a relative integer position in the Segment.
Definition: Segment.h:173
Mercator::BasePoint
Point on the fundamental grid that is used as the basis for terrain.
Definition: BasePoint.h:19
Mercator::Segment::isValid
bool isValid() const
Check whether this Segment contains valid point data.
Definition: Segment.h:105
Mercator::Terrain::processSegments
void processSegments(const WFMath::AxisBox< 2 > &area, const std::function< void(Segment &, int, int)> &func) const
Definition: Terrain.cpp:221
Mercator::Shader
Base class for Shader objects which create surface data for use when rendering terrain.
Definition: Shader.h:25
Mercator::Terrain::removeShader
void removeShader(const Shader *t, int id)
remove a Shader from the list for this terrain.
Definition: Terrain.cpp:62
Mercator::Terrain::Terrain
Terrain(unsigned int options=DEFAULT, int resolution=defaultResolution)
Construct a new Terrain object with optional options and resolution.
Definition: Terrain.cpp:32
Mercator::Terrain::get
float get(float x, float z) const
Get the height value at a given coordinate x,z.
Definition: Terrain.cpp:114
Mercator::Segment::setCornerPoint
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
Mercator::Terrain::~Terrain
~Terrain()
Destroy Terrain object, deleting contained objects.
Mercator::TerrainMod
Base class for modifiers to the procedurally generated terrain.
Definition: TerrainMod.h:21
Mercator::Terrain::posToIndex
int posToIndex(float pos) const
Converts the supplied position into a segment index.
Definition: Terrain.h:330
Mercator::Terrain::Rect
WFMath::AxisBox< 2 > Rect
Bounding box.
Definition: Terrain.h:40
Mercator::Terrain::SHADED
static const unsigned int SHADED
set if shaders are going to be used on this terrain.
Definition: Terrain.h:58
Mercator::Terrain::addShader
void addShader(const Shader *t, int id)
Add a new Shader to the list for this terrain.
Definition: Terrain.cpp:40