Cogs.Core
HeightMapSystem.cpp
1#include <limits>
2#include "Context.h"
3#include "Resources/Mesh.h"
4#include "Resources/MeshManager.h"
5#include "Resources/MaterialManager.h"
6#include "Resources/VertexFormats.h"
7#include "DataSet2DComponent.h"
8#include "Components/Core/MeshComponent.h"
9#include "Components/Appearance/MaterialComponent.h"
10
11#include "HeightMapSystem.h"
12
13namespace {
14
15
16 void updateVertices(std::vector<Cogs::Core::PositionNormalTexVertex>& V,
17 const std::vector<float>& values, int Ni, int Nj, float minVal, float maxVal,
18 float si, float sj, float sk,
19 bool gridLines, const glm::vec3& gridLinesOffset,
21 {
22 V.resize((gridLines?2:1)*Ni*Nj);
23
24 // For compatibility we do not normalize the value before
25 // multiplying with height (check HeightField.cpp)
26 float skk = sk; // / std::max(std::numeric_limits<float>::epsilon(), maxVal - minVal);
27 float ukk = 1.f / std::max(std::numeric_limits<float>::epsilon(), maxVal - minVal);
28
29 float shiftX = 0.5f*si*(Ni - 1);
30 float shiftY = 0.5f*sj*(Nj - 1);
31
33 for (int j = 0; j < Nj; j++) {
34 for (int i = 0; i < Ni; i++) {
35 //we want to compute normals by looking at difference of left and right value, and up and down value,
36 //but we don't want to move outside grid or use non-finite values. Find indexes for the value to use
37
38 int jm = (j > 0 && std::isfinite(values[Ni*(j - 1) + i])) ? j - 1 : j; //jm = j minus 1
39 int jp = (j < (Nj - 1) && std::isfinite(values[Ni *(j + 1) + i])) ? j + 1 : j; //jp = j plus + 1
40
41 int im = (i > 0 && std::isfinite(values[Ni*j + (i - 1)])) ? i - 1 : i; //im = i minus 1
42 int ip = (i < (Ni - 1) && std::isfinite(values[Ni*j + (i + 1)])) ? i + 1 : i; //ip = i plus 1
43
44 float v = values[Ni*j + i]-minVal;
45 float di = (ip == im) ? 0 : (skk / (si*(ip - im)))*(values[Ni*j + ip] - values[Ni*j + im]); // central diff unless on boundary,
46 float dj = (jp == jm) ? 0 : (skk / (sj*(jp - jm)))*(values[Ni*jp + i] - values[Ni*jm + i]); // where we use fwd or bwd diff.
47 glm::vec3 p(si*i-shiftX, sj*j-shiftY, skk*v);
48 glm::vec3 n = glm::normalize(glm::vec3(-di, -dj, 1.f));
49
50 glm::vec2 textureCoords;
51 if (textureDomain == Cogs::Core::HeightMapComponent::TextureDomain::Height) {
52 textureCoords = glm::vec2(ukk*v, 0.f);
53 }
55 textureCoords = glm::vec2(static_cast<float>(i) / std::max(Ni - 1, 1),
56 static_cast<float>(j) / std::max(Nj - 1, 1));
57 }
58
59 *Vp++ = { p, n, textureCoords};
60
61 }
62 }
63
64 if (gridLines) {
65 for (int j = 0; j < Nj; j++) {
66 for (int i = 0; i < Ni; i++) {
67 float v = values[Ni*j + i] - minVal;
68 glm::vec3 p(si*i - shiftX, sj*j - shiftY, skk*v);
69 *Vp++ = { p + gridLinesOffset, glm::vec3(0.f), glm::vec2(0.f) };
70 }
71 }
72 }
73 }
74
75 struct {
76 unsigned char n;
77 unsigned char e[2];
78 } isoLineCases[16] = {
79 /* 0000 */{ 0, {} },
80 /* 0001 */{ 1, { ((0 << 4) | 3) } },
81 /* 0010 */{ 1, { ((0 << 4) | 1) } },
82 /* 0011 */{ 1, { ((1 << 4) | 3) } },
83 /* 0100 */{ 1, { ((1 << 4) | 2) } },
84 /* 0101 */{ 2, { ((1 << 4) | 2), ((0 << 4) | 3) } },
85 /* 0110 */{ 1, { ((0 << 4) | 2) } },
86 /* 0111 */{ 1, { ((2 << 4) | 3) } },
87 /* 1000 */{ 1, { ((2 << 4) | 3) } },
88 /* 1001 */{ 1, { ((0 << 4) | 2) } },
89 /* 1010 */{ 2, { ((1 << 4) | 2), ((0 << 4) | 3) } },
90 /* 1011 */{ 1, { ((1 << 4) | 2) } },
91 /* 1100 */{ 1, { ((1 << 4) | 3) } },
92 /* 1101 */{ 1, { ((0 << 4) | 1) } },
93 /* 1110 */{ 1, { ((0 << 4) | 3) } },
94 /* 1111 */{ 0, {} }
95 };
96
97 void extractIsoLines(std::vector<Cogs::Core::PositionNormalTexVertex>& V,
98 std::vector<unsigned int>& lines,
99 const std::vector<float>& values, int Ni, int Nj,
100 float minValue, float maxValue,
101 float isoOffset, float isoDistance,
102 float zScale, float zShift )
103 {
104 static const int maxIsoLevels = 1000; // Maximum number of iso-levels.
105 float isoLevelsf = (maxValue - minValue) / isoDistance; // Current number of iso-levels.
106 int isoLevels = int(std::floor( isoLevelsf )) + 1; // Current number of iso-levels.
107 float scaleToInt = 1.f / isoDistance; // Scale factor to map iso-levels to integers.
108 float minIsoLevel = isoOffset - std::floor(isoOffset - minValue); // Value of the smallest iso-level.
109
110 // sanity checks.
111 if (!std::isfinite(scaleToInt) ||
112 !std::isfinite(minIsoLevel) ||
113 !std::isfinite(isoLevelsf) ||
114 (maxIsoLevels < isoLevels))
115 {
116 return;
117 }
118
119 int Mi = std::max(0, Ni - 1);
120 int Mj = std::max(0, Nj - 1);
121 for (int j = 0; j < Mj; j++) {
122 for (int i = 0; i < Mi; i++) {
123 glm::ivec4 ix(Ni*j + i, Ni*(j + 1) + i, Ni*(j + 1) + i + 1, Ni*j + i + 1);
124 glm::vec4 _v(values[ix.x], values[ix.y], values[ix.z], values[ix.w]);
125 glm::vec4 v = scaleToInt*(_v - glm::vec4(minIsoLevel));
126
127 // Find local max and min, and the set of iso levels present in this cell
128 float vmin = glm::min(glm::min(v.x, v.y), glm::min(v.z, v.w));
129 float vmax = glm::max(glm::max(v.x, v.y), glm::max(v.z, v.w));
130 int l = glm::max(0,int(glm::ceil(vmin)));
131 int h = glm::min(isoLevels, int(glm::floor(vmax)));
132
133 // Process each of the iso levels.
134 for (int k = l; k <= h; k++) {
135
136 // Check if each of the four corners is below threshold
137 float threshold = float(k);
138 glm::bvec4 m = glm::lessThan(v, glm::vec4(threshold));
139
140 // Calculate intersection points on the edges
141 int vix[4] = {};
142 for (int o = 0; o < 4; o++) {
143 int op = (o + 1) & 3;
144 if (m[o] != m[op]) {
145 vix[o] = int(V.size());
146 float f0 = v[o] - threshold;
147 float f1 = v[op] - threshold;
148 float t;
149 if (f0*f1 < 0.f) {
150 t = f0 / (f0 - f1);
151 }
152 else {
153 t = glm::abs(f0) < glm::abs(f1) ? 0.f : 1.f;
154 }
155 glm::vec3 p = glm::mix(V[ix[o]].position, V[ix[op]].position, t);
156 p.z = zScale*p.z + zShift;
157 V.push_back({ p, glm::vec3(0.f), glm::vec2(threshold, 0.f) });
158 }
159 }
160
161 // Add edge indices
162 int code = (m.x ? 1 : 0) | (m.y ? 2 : 0) | (m.z ? 4 : 0) | (m.w ? 8 : 0);
163 for (int o = 0; o <isoLineCases[code].n; o++) {
164 lines.push_back(vix[isoLineCases[code].e[o] >> 4]);
165 lines.push_back(vix[isoLineCases[code].e[o] & 3]);
166 }
167 }
168 }
169 }
170 }
171
172 void surfaceIndices(std::vector<unsigned int>& surface,
173 const std::vector<Cogs::Core::PositionNormalTexVertex>& V,
174 int Ni, int Nj)
175 {
176 int Mi = std::max(0, Ni - 1);
177 int Mj = std::max(0, Nj - 1);
178
179 surface.reserve(6 * Mi*Mj);
180 for (int j = 0; j < Mj; j++) {
181 for (int i = 0; i < Mi; i++) {
182 int vo = Ni*j + i;
183 float d0 = glm::dot(V[vo].normal, V[vo + Ni + 1].normal);
184 float d1 = glm::dot(V[vo+1].normal, V[vo + Ni].normal);
185 if (d0 < d1) {
186 surface.push_back(vo);
187 surface.push_back(vo + 1);
188 surface.push_back(vo + Ni);
189
190 surface.push_back(vo + 1);
191 surface.push_back(vo + Ni + 1);
192 surface.push_back(vo + Ni);
193 }
194 else {
195 surface.push_back(vo);
196 surface.push_back(vo + 1);
197 surface.push_back(vo + Ni + 1 );
198
199 surface.push_back(vo + Ni + 1);
200 surface.push_back(vo + Ni );
201 surface.push_back(vo);
202
203 }
204
205 }
206 }
207 }
208
209 void gridLineIndices(std::vector<unsigned int>& gridLines, int Ni, int Nj)
210 {
211 int Mi = std::max(0, Ni - 1);
212 int Mj = std::max(0, Nj - 1);
213 for (int j = 0; j < Mj; j++) {
214 for (int i = 0; i < Mi; i++) {
215 int vo = Ni*j + i + Ni*Nj;
216 gridLines.push_back(vo);
217 gridLines.push_back(vo + 1);
218 gridLines.push_back(vo);
219 gridLines.push_back(vo + Ni);
220 }
221 }
222 }
223
224
225}
226
227namespace Cogs
228{
229 namespace Core
230 {
232 {
233 for (auto & hmapComp : pool) {
234 if (!hmapComp.dataSet) {
235 continue;
236 }
237
238 auto dataSetComp = hmapComp.dataSet->getComponent<DataSet2DComponent>();
239 auto meshComp = hmapComp.getComponent<MeshComponent>();
240 auto matComp = hmapComp.getComponent<MaterialComponent>();
241
242 // sanity checks
243 if (!dataSetComp || !meshComp || !matComp) {
244 continue;
245 }
246
247 if ((hmapComp.hasChanged() || dataSetComp->hasChanged()) && dataSetComp->hasData) {
248 if (meshComp->meshHandle == MeshHandle::NoHandle) {
249 meshComp->meshHandle = context->meshManager->create();
250 }
251 auto mesh = context->meshManager->get(meshComp->meshHandle);
252
253 float minValue = dataSetComp->minValue;
254 float maxValue = dataSetComp->maxValue;
255 if (std::isfinite(hmapComp.minValue) &&
256 std::isfinite(hmapComp.maxValue) &&
257 (hmapComp.minValue < hmapComp.maxValue))
258 {
259 minValue = hmapComp.minValue;
260 maxValue = hmapComp.maxValue;
261 }
262
263 std::vector<Cogs::Core::PositionNormalTexVertex> V;
264 updateVertices(V, dataSetComp->data, dataSetComp->sizeI, dataSetComp->sizeJ, minValue, maxValue,
265 hmapComp.xIncrement, hmapComp.yIncrement, hmapComp.height,
266 hmapComp.gridLines, hmapComp.gridLineOffset, hmapComp.textureDomain );
267
268 std::vector<unsigned int> surface;
269 surfaceIndices(surface, V, dataSetComp->sizeI, dataSetComp->sizeJ);
270
271 std::vector<unsigned int> gridLines;
272 if (hmapComp.gridLines) {
273 gridLineIndices(gridLines, dataSetComp->sizeI, dataSetComp->sizeJ);
274 }
275
276 std::vector<unsigned int> isoLines;
277 float isoLineOrigin = hmapComp.isoLineOrigin;
278 float isoLineDistance = hmapComp.isoLineDistance;
279 if (!std::isfinite(isoLineOrigin) || !std::isfinite(isoLineDistance)) {
280 isoLineOrigin = minValue;
281 isoLineDistance = (maxValue - minValue) / 10.f;
282 }
283 switch (hmapComp.isoLine)
284 {
286 break;
288 extractIsoLines(V, isoLines,
289 dataSetComp->data, dataSetComp->sizeI, dataSetComp->sizeJ,
290 minValue, maxValue,
291 isoLineOrigin, isoLineDistance,
292 1.f, hmapComp.gridLineOffset.z );
293 break;
295 extractIsoLines(V, isoLines,
296 dataSetComp->data, dataSetComp->sizeI, dataSetComp->sizeJ,
297 minValue, maxValue,
298 isoLineOrigin, isoLineDistance,
299 0.f, (maxValue-minValue)*hmapComp.height);
300 break;
302 extractIsoLines(V, isoLines,
303 dataSetComp->data, dataSetComp->sizeI, dataSetComp->sizeJ,
304 minValue, maxValue,
305 isoLineOrigin, isoLineDistance,
306 0.f, 0.f);
307 break;
308 }
309
310 glm::vec3 extent(dataSetComp->sizeI*hmapComp.xIncrement,
311 dataSetComp->sizeJ*hmapComp.yIncrement,
312 hmapComp.height*(maxValue-minValue));
313
314 mesh->setBounds({ glm::vec3(-0.5f*extent.x, -0.5f*extent.y, glm::min(extent.z, 0.f)),
315 glm::vec3(0.5f*extent.x, 0.5*extent.y, glm::max(extent.z, 0.f)) });
316 mesh->setVertexData(V.data(), V.size());
317
318 mesh->clearIndexes();
319 mesh->addSubMesh(std::span(surface), Cogs::PrimitiveType::TriangleList);
320 mesh->addSubMesh(std::span(gridLines), Cogs::PrimitiveType::LineList);
321 mesh->addSubMesh(std::span(isoLines), Cogs::PrimitiveType::LineList);
322
323 if (hmapComp.gridLines || (hmapComp.isoLine != HeightMapComponent::IsoLine::None)) {
324 matComp->depthBiasConstant = 1.0f;
325 matComp->depthBiasSlope = 1.0f;
326 } else {
327 matComp->depthBiasConstant = 0.0f;
328 matComp->depthBiasSlope = 0.0f;
329 }
330
331 matComp->setChanged();
332 }
333
334 if (hmapComp.colorMap) {
335 auto cmapComp = hmapComp.colorMap->getComponent<MaterialComponent>();
336
337 if (cmapComp) {
338 matComp->diffuseMap = cmapComp->diffuseMap;
339 } else {
340 matComp->diffuseMap = TextureHandle::NoHandle;
341 }
342 } else {
343 matComp->diffuseMap = TextureHandle::NoHandle;
344 }
345
346 matComp->setChanged();
347 }
348 }
349 } // namespace Core
350} // namespace Cogs
void setChanged()
Sets the component to the ComponentFlags::Changed state with carry.
Definition: Component.h:202
ComponentType * getComponent() const
Definition: Component.h:159
Context * context
Pointer to the Context instance the system lives in.
void update()
Updates the system state to that of the current frame.
ComponentPool< ComponentType > pool
Pool of components managed by the system.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
Definition: MeshComponent.h:15
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
@ Bottom
Iso-line on bottom of bounding box.
@ Top
Iso-line on top of bounding box.
@ XY
compute texture coordinates based on the height of the points
Exposes material properties for legacy entities and code.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
@ LineList
List of lines.
Definition: Common.h:120
@ TriangleList
List of triangles.
Definition: Common.h:116