mercator  0.4.0
A terrain generation library for the Worldforge system.
Segment.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 "Segment.h"
12 #include "Terrain.h"
13 #include "TerrainMod.h"
14 #include "Surface.h"
15 #include "BasePoint.h"
16 #include "Area.h"
17 #include "Shader.h"
18 
19 #include <wfmath/MersenneTwister.h>
20 
21 #include <cmath>
22 #include <cassert>
23 #include <algorithm>
24 
25 namespace Mercator {
26 
27 
37 Segment::Segment(int x, int z, int resolution) :
38  m_res(resolution), m_size(m_res+1),
39  m_xRef(x), m_zRef(z),
40  m_heightMap(resolution)
41 {
42 }
43 
51 {
52  clearMods();
53 }
54 
60 void Segment::populate() // const Matrix<2, 2, BasePoint> & base)
61 {
62  m_heightMap.allocate();
63  populateHeightMap(m_heightMap);
64 
65  for (auto& entry : m_terrainMods) {
66  applyMod(entry.second);
67  }
68 }
69 
70 void Segment::populateHeightMap(HeightMap& heightMap)
71 {
72  heightMap.fill2d(m_controlPoints(0, 0), m_controlPoints(1, 0),
73  m_controlPoints(1, 1), m_controlPoints(0, 1));
74 }
75 
76 
83 void Segment::invalidate(bool points)
84 {
85  if (points) {
86  m_heightMap.invalidate();
87  }
88  m_normals.reset();
89 
90  invalidateSurfaces();
91 }
92 
98 void Segment::invalidateSurfaces()
99 {
100  for(auto& entry : m_surfaces) {
101  entry.second->invalidate();
102  }
103 }
104 
112 {
113  assert(m_heightMap.isValid());
114  assert(m_size != 0);
115  assert(m_res == m_size - 1);
116 
117  if (!m_normals) {
118  m_normals = std::make_unique<std::vector<float>>(m_size * m_size * 3);
119  }
120 
121  float * np = m_normals->data();
122 
123  // Fill in the damn normals
124  float h1,h2,h3,h4;
125  for (int j = 1; j < m_res; ++j) {
126  for (int i = 1; i < m_res; ++i) {
127  h1 = get(i - 1, j);
128  h2 = get(i, j + 1);
129  h3 = get(i + 1, j);
130  h4 = get(i, j - 1);
131 
132  // Caclulate the normal vector.
133  np[j * m_size * 3 + i * 3] = (h1 - h3) / 2.f;
134  np[j * m_size * 3 + i * 3 + 1] = 1.0;
135  np[j * m_size * 3 + i * 3 + 2] = (h4 - h2) / 2.f;
136  }
137  }
138 
139  //edges have one axis pegged to 0
140 
141  //top and bottom boundary
142  for (int i=1; i < m_res; ++i) {
143  h1 = m_heightMap.get(i - 1, 0);
144  h2 = m_heightMap.get(i + 1, 0);
145 
146  np[i * 3] = (h1 - h2) / 2.f;
147  np[i * 3 + 1] = 1.0;
148  np[i * 3 + 2] = 0.0;
149 
150  h1 = m_heightMap.get(i - 1, m_res);
151  h2 = m_heightMap.get(i + 1, m_res);
152 
153  np[m_res * m_size * 3 + i * 3] = (h1 - h2) / 2.f;
154  np[m_res * m_size * 3 + i * 3 + 1] = 1.0f;
155  np[m_res * m_size * 3 + i * 3 + 2] = 0.0f;
156  }
157 
158  //left and right boundary
159  for (int j=1; j < m_res; ++j) {
160  h1 = m_heightMap.get(0, j - 1);
161  h2 = m_heightMap.get(0, j + 1);
162 
163  np[j * m_size * 3] = 0;
164  np[j * m_size * 3 + 1] = 1.f;
165  np[j * m_size * 3 + 2] = (h1 - h2) / 2.f;
166 
167  h1 = m_heightMap.get(m_res, j - 1);
168  h2 = m_heightMap.get(m_res, j + 1);
169 
170  np[j * m_size * 3 + m_res * 3] = 0.f;
171  np[j * m_size * 3 + m_res * 3 + 1] = 1.f;
172  np[j * m_size * 3 + m_res * 3 + 2] = (h1 - h2) / 2.f;
173  }
174 
175  //corners - these are all treated as flat
176  //so the normal points straight up
177  np[0] = 0.f;
178  np[1] = 1.f;
179  np[2] = 0.f;
180 
181  np[m_res * m_size * 3] = 0.f;
182  np[m_res * m_size * 3 + 1] = 1.f;
183  np[m_res * m_size * 3 + 2] = 0.f;
184 
185  np[m_res * 3] = 0.f;
186  np[m_res * 3 + 1] = 1.f;
187  np[m_res * 3 + 2] = 0.f;
188 
189  np[m_res * m_size * 3 + m_res * 3] = 0.f;
190  np[m_res * m_size * 3 + m_res * 3 + 1] = 1.f;
191  np[m_res * m_size * 3 + m_res * 3 + 2] = 0.f;
192 }
193 
198 {
199  for (const auto& entry : m_surfaces) {
200  if (entry.second->m_shader.checkIntersect(*this)) {
201  entry.second->populate();
202  }
203  }
204 }
205 
206 void Segment::getHeight(float x, float y, float &h) const
207 {
208  m_heightMap.getHeight(x, y, h);
209 }
210 
223 void Segment::getHeightAndNormal(float x, float z, float& h,
224  WFMath::Vector<3> &normal) const
225 {
226  m_heightMap.getHeightAndNormal(x, z, h, normal);
227 }
228 
238 bool Segment::clipToSegment(const WFMath::AxisBox<2> &bbox,
239  int &lx, int &hx, int &lz, int &hz) const
240 {
241  lx = I_ROUND(bbox.lowCorner()[0]);
242  if (lx > m_res) return false;
243  if (lx < 0) lx = 0;
244 
245  hx = I_ROUND(bbox.highCorner()[0]);
246  if (hx < 0) return false;
247  if (hx > m_res) hx = m_res;
248 
249  lz = I_ROUND(bbox.lowCorner()[1]);
250  if (lz > m_res) return false;
251  if (lz < 0) lz = 0;
252 
253  hz = I_ROUND(bbox.highCorner()[1]);
254  if (hz < 0) return false;
255  if (hz > m_res) hz = m_res;
256 
257  return true;
258 }
259 
260 void Segment::updateMod(long id, const TerrainMod *t)
261 {
262  if (t) {
263  m_terrainMods[id] = t;
264  } else {
265  m_terrainMods.erase(id);
266  }
267  invalidate();
268 }
269 
275 {
276  if (!m_terrainMods.empty()) {
277  m_terrainMods.clear();
278  invalidate();
279  }
280 }
281 
287 void Segment::applyMod(const TerrainMod *t)
288 {
289  int lx,hx,lz,hz;
290  float* points = m_heightMap.getData();
291  WFMath::AxisBox<2> bbox=t->bbox();
292  bbox.shift(WFMath::Vector<2>(-m_xRef, -m_zRef));
293  if (clipToSegment(bbox, lx, hx, lz, hz)) {
294  for (int i=lz; i<=hz; i++) {
295  for (int j=lx; j<=hx; j++) {
296  float& h = points[i * m_size + j];
297  t->apply(h, j + m_xRef, i + m_zRef);
298  m_heightMap.checkMaxMin(h);
299  }
300  }
301  }
302 
303  //currently mods dont fix the normals
304  invalidate(false);
305 }
306 
307 void Segment::updateArea(long id, const Area* area, const Shader* shader)
308 {
309  auto areaLookupI = m_areaLookup.find(id);
310  if (areaLookupI != m_areaLookup.end()) {
311  auto& areaEntry = areaLookupI->second->second;
312  auto J = m_surfaces.find(areaEntry.area->getLayer());
313  if (J != m_surfaces.end()) {
314  // segment already has a surface for this shader, mark it
315  // for re-generation
316  J->second->invalidate();
317  }
318  m_areas.erase(areaLookupI->second);
319  m_areaLookup.erase(areaLookupI);
320  }
321 
322 
323  if (area) {
324  auto result = m_areas.emplace(area->getLayer(), AreaEntry{id, area});
325  m_areaLookup.emplace(id, result);
326  auto J = m_surfaces.find(area->getLayer());
327  if (J != m_surfaces.end()) {
328  J->second->invalidate();
329  } else {
330  if (shader) {
331  m_surfaces[area->getLayer()] = shader->newSurface(*this);
332  }
333  }
334  }
335 }
336 
337 WFMath::AxisBox<2> Segment::getRect() const
338 {
339  WFMath::Point<2> lp(m_xRef, m_zRef),
340  hp(lp.x() + m_res, lp.y() + m_res);
341  return WFMath::AxisBox<2>(lp, hp);
342 }
343 
344 } // namespace Mercator
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
void fill2d(const BasePoint &p1, const BasePoint &p2, const BasePoint &p3, const BasePoint &p4)
Two dimensional midpoint displacement fractal.
Definition: HeightMap.cpp:201
Class storing heightfield and other data for a single fixed size square area of terrain defined by fo...
Definition: HeightMap.h:21
DataType * getData()
Accessor for a pointer to buffer containing data values.
Definition: Buffer.h:63
bool isValid() const
Determine if this buffer has valid allocated storage.
Definition: Buffer.h:83
void populateSurfaces()
Populate the surfaces associated with this Segment.
Definition: Segment.cpp:197
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: HeightMap.cpp:380
void invalidate()
De-allocate the storage for this buffer.
Definition: Buffer.h:90
WFMath::AxisBox< 2 > getRect() const
The 2d area covered by this segment.
Definition: Segment.cpp:337
virtual void apply(float &point, int x, int z) const =0
Apply this modifier on a terrain segment.
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 Shader objects which create surface data for use when rendering terrain.
Definition: Shader.h:25
Segment(int x, int z, int resolution)
Construct an empty segment with the given resolution.
Definition: Segment.cpp:37
void invalidate(bool points=true)
Mark the contents of this Segment as stale.
Definition: Segment.cpp:83
const WFMath::AxisBox< 2 > & bbox() const
Accessor for the bounding box of the geometric shape.
Definition: Effector.h:37
void allocate()
Allocate the storage required by the buffer.
Definition: Buffer.h:76
bool clipToSegment(const WFMath::AxisBox< 2 > &bbox, int &lx, int &hx, int &lz, int &hz) const
Determine the intersection between an axis aligned box and this segment.
Definition: Segment.cpp:238
void clearMods()
Delete all the modifications applied to this Segment.
Definition: Segment.cpp:274
float get(int x, int z) const
Get the height at a relative integer position in the Segment.
Definition: HeightMap.h:40
int getLayer() const
Accessor for the layer number.
Definition: Area.h:44
Region of terrain surface which is modified.
Definition: Area.h:28
void checkMaxMin(float h)
Check a value against m_min and m_max and set one of them if appropriate.
Definition: HeightMap.cpp:117
void populateNormals()
Populate the Segment with surface normal data.
Definition: Segment.cpp:111
~Segment()
Destruct the Segment.
Definition: Segment.cpp:50
Base class for modifiers to the procedurally generated terrain.
Definition: TerrainMod.h:20
void populate()
Populate the Segment with heightfield data.
Definition: Segment.cpp:60