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
25namespace Mercator {
26
27
37Segment::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
60void 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
70void 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
83void Segment::invalidate(bool points)
84{
85 if (points) {
86 m_heightMap.invalidate();
87 }
88 m_normals = {};
89
90 invalidateSurfaces();
91}
92
98void 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.empty()) {
118 m_normals.reserve(m_size * m_size * 3);
119 }
120
121 auto * 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
206void Segment::getHeight(float x, float y, float &h) const
207{
208 m_heightMap.getHeight(x, y, h);
209}
210
223void 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
238bool 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
260void 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
287void 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
307void 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
337WFMath::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 invalidate()
De-allocate the storage for this buffer.
Definition: Buffer.h:90
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 allocate()
Allocate the storage required by the buffer.
Definition: Buffer.h:76
const WFMath::AxisBox< 2 > & bbox() const
Accessor for the bounding box of the geometric shape.
Definition: Effector.h:37
Class storing heightfield and other data for a single fixed size square area of terrain defined by fo...
Definition: HeightMap.h:21
void fill2d(const BasePoint &p1, const BasePoint &p2, const BasePoint &p3, const BasePoint &p4)
Two dimensional midpoint displacement fractal.
Definition: HeightMap.cpp:201
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 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
float get(int x, int z) const
Get the height at a relative integer position in the Segment.
Definition: HeightMap.h:40
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 populate()
Populate the Segment with heightfield data.
Definition: Segment.cpp:60
WFMath::AxisBox< 2 > getRect() const
The 2d area covered by this segment.
Definition: Segment.cpp:337
void populateNormals()
Populate the Segment with surface normal data.
Definition: Segment.cpp:111
Segment(int x, int z, int resolution)
Construct an empty segment with the given resolution.
Definition: Segment.cpp:37
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 populateSurfaces()
Populate the surfaces associated with this Segment.
Definition: Segment.cpp:197
void invalidate(bool points=true)
Mark the contents of this Segment as stale.
Definition: Segment.cpp:83
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: Segment.h:173
~Segment()
Destruct the Segment.
Definition: Segment.cpp:50
Base class for modifiers to the procedurally generated terrain.
Definition: TerrainMod.h:21
virtual void apply(float &point, int x, int z) const =0
Apply this modifier on a terrain segment.