TDME2  1.9.200
Terrain.cpp
Go to the documentation of this file.
2 
3 #include <array>
4 #include <map>
5 #include <memory>
6 #include <set>
7 #include <string>
8 #include <unordered_map>
9 #include <unordered_set>
10 #include <vector>
11 
12 #include <tdme/tdme.h>
13 #include <tdme/engine/Texture.h>
15 #include <tdme/engine/Color4.h>
16 #include <tdme/engine/model/Face.h>
20 #include <tdme/engine/model/Node.h>
26 #include <tdme/engine/Rotation.h>
27 #include <tdme/engine/Transform.h>
28 #include <tdme/math/Math.h>
29 #include <tdme/math/Matrix3x3.h>
30 #include <tdme/math/Vector2.h>
31 #include <tdme/math/Vector3.h>
32 #include <tdme/utilities/Console.h>
34 #include <tdme/utilities/Float.h>
35 #include <tdme/utilities/Integer.h>
37 
38 using std::array;
39 using std::make_unique;
40 using std::map;
41 using std::set;
42 using std::string;
43 using std::to_string;
44 using std::unordered_map;
45 using std::unordered_set;
46 using std::vector;
47 
49 
65 using tdme::math::Math;
73 
74 Texture* Terrain::createTerrainNavigationMap(float width, float depth, vector<float>& terrainHeightVector, float maxNavigationSlope) {
75  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(width / STEP_SIZE));
76  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(depth / STEP_SIZE));
77  //
78  auto navigationMapTextureByteBuffer = ByteBuffer(terrainHeightVectorVerticesPerX * 2 * terreinHeightVectorVerticesPerZ * 2 * 3);
79  for (auto z = 0; z < terreinHeightVectorVerticesPerZ * 2; z++) {
80  for (auto x = 0; x < terrainHeightVectorVerticesPerX * 2; x++) {
81  navigationMapTextureByteBuffer.put(0);
82  navigationMapTextureByteBuffer.put(0);
83  navigationMapTextureByteBuffer.put(0);
84  }
85  }
86  //
87  for (float z = 0.0f; z < depth; z+= STEP_SIZE) {
88  for (float x = 0.0f; x < width; x+= STEP_SIZE) {
89 
90  auto terrainHeightVectorX = static_cast<int>(x / STEP_SIZE);
91  auto terrainHeightVectorZ = static_cast<int>(z / STEP_SIZE);
92 
93  auto topNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ - 1);
94  auto topLeftNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX - 1, terrainHeightVectorZ - 1);
95  auto leftNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX - 1, terrainHeightVectorZ);
96  auto normal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ);
97 
98  //
99  {
100  auto terrainSlopeTop = Math::abs(180.0f / 3.14f * Math::acos(Math::clamp(Vector3::computeDotProduct(topNormal, Vector3(0.0f, 1.0f, 0.0f)), -1.0f, 1.0f)));
101  navigationMapTextureByteBuffer.setPosition((terrainHeightVectorZ * 2 * terrainHeightVectorVerticesPerX * 2) * 3 + (terrainHeightVectorX * 2 + 1) * 3);
102  if (terrainSlopeTop > maxNavigationSlope) {
103  navigationMapTextureByteBuffer.put(255);
104  navigationMapTextureByteBuffer.put(0);
105  navigationMapTextureByteBuffer.put(0);
106  } else {
107  navigationMapTextureByteBuffer.put(0);
108  navigationMapTextureByteBuffer.put(255);
109  navigationMapTextureByteBuffer.put(0);
110  }
111  }
112  //
113  {
114  auto terrainSlopeTopLeft = Math::abs(180.0f / 3.14f * Math::acos(Math::clamp(Vector3::computeDotProduct(topLeftNormal, Vector3(0.0f, 1.0f, 0.0f)), -1.0f, 1.0f)));
115  navigationMapTextureByteBuffer.setPosition((terrainHeightVectorZ * 2 * terrainHeightVectorVerticesPerX * 2) * 3 + terrainHeightVectorX * 2 * 3);
116  if (terrainSlopeTopLeft > maxNavigationSlope) {
117  navigationMapTextureByteBuffer.put(255);
118  navigationMapTextureByteBuffer.put(0);
119  navigationMapTextureByteBuffer.put(0);
120  } else {
121  navigationMapTextureByteBuffer.put(0);
122  navigationMapTextureByteBuffer.put(255);
123  navigationMapTextureByteBuffer.put(0);
124  }
125  }
126  //
127  {
128  auto terrainSlopeLeft = Math::abs(180.0f / 3.14f * Math::acos(Math::clamp(Vector3::computeDotProduct(leftNormal, Vector3(0.0f, 1.0f, 0.0f)), -1.0f, 1.0f)));
129  navigationMapTextureByteBuffer.setPosition(((terrainHeightVectorZ * 2 + 1) * terrainHeightVectorVerticesPerX * 2) * 3 + terrainHeightVectorX * 2 * 3);
130  if (terrainSlopeLeft > maxNavigationSlope) {
131  navigationMapTextureByteBuffer.put(255);
132  navigationMapTextureByteBuffer.put(0);
133  navigationMapTextureByteBuffer.put(0);
134  } else {
135  navigationMapTextureByteBuffer.put(0);
136  navigationMapTextureByteBuffer.put(255);
137  navigationMapTextureByteBuffer.put(0);
138  }
139  }
140  //
141  {
142  auto terrainSlope = Math::abs(180.0f / 3.14f * Math::acos(Math::clamp(Vector3::computeDotProduct(normal, Vector3(0.0f, 1.0f, 0.0f)), -1.0f, 1.0f)));
143  navigationMapTextureByteBuffer.setPosition(((terrainHeightVectorZ * 2 + 1) * terrainHeightVectorVerticesPerX * 2) * 3 + (terrainHeightVectorX * 2 + 1) * 3);
144  if (terrainSlope > maxNavigationSlope) {
145  navigationMapTextureByteBuffer.put(255);
146  navigationMapTextureByteBuffer.put(0);
147  navigationMapTextureByteBuffer.put(0);
148  } else {
149  navigationMapTextureByteBuffer.put(0);
150  navigationMapTextureByteBuffer.put(255);
151  navigationMapTextureByteBuffer.put(0);
152  }
153  }
154  }
155  }
156  //
157  auto navigationMapTexture = new Texture(
158  "terrain-navigationmaptexture",
159  Texture::TEXTUREDEPTH_RGB,
160  Texture::TEXTUREFORMAT_RGB,
161  terrainHeightVectorVerticesPerX * 2,
162  terreinHeightVectorVerticesPerZ * 2,
163  terrainHeightVectorVerticesPerX * 2,
164  terreinHeightVectorVerticesPerZ * 2,
165  Texture::TEXTUREFORMAT_RGB,
166  navigationMapTextureByteBuffer
167  );
168  navigationMapTexture->acquireReference();
169  //
170  return navigationMapTexture;
171 }
172 
173 void Terrain::createTerrainModels(float width, float depth, float y, vector<float>& terrainHeightVector, BoundingBox& terrainBoundingBox, vector<Model*>& terrainModels, bool createLODLevels)
174 {
175  vector<unordered_map<int, int>> partitionTerrainTriangles;
176  vector<vector<Vector3>> partitionTerrainVertices;
177  vector<vector<Vector3>> partitionTerrainNormals;
178  vector<vector<array<int, 6>>> partitionTerrainFaces;
179  vector<vector<int32_t>> partitionLod1Indices;
180  vector<vector<int32_t>> partitionLod2Indices;
181  vector<vector<int32_t>> partitionLod3Indices;
182  auto partitionsX = static_cast<int>(Math::ceil(width / PARTITION_SIZE));
183  auto partitionsZ = static_cast<int>(Math::ceil(depth / PARTITION_SIZE));
184  auto partitionCount = partitionsX * partitionsZ;
185  partitionTerrainTriangles.resize(partitionCount);
186  partitionTerrainVertices.resize(partitionCount);
187  partitionTerrainNormals.resize(partitionCount);
188  partitionTerrainFaces.resize(partitionCount);
189  if (terrainHeightVector.empty() == true) {
190  for (float z = 0.0f; z < depth; z+= STEP_SIZE) {
191  for (float x = 0.0f; x < width; x+= STEP_SIZE) {
192  terrainHeightVector.push_back(y);
193  }
194  }
195  }
196  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(width / STEP_SIZE));
197  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(depth / STEP_SIZE));
198  for (float z = 0.0f; z < depth; z+= STEP_SIZE) {
199  for (float x = 0.0f; x < width; x+= STEP_SIZE) {
200 
201  auto partitionX = static_cast<int>(x / PARTITION_SIZE);
202  auto partitionZ = static_cast<int>(z / PARTITION_SIZE);
203  auto partitionIdx = partitionZ * partitionsX + partitionX;
204 
205  auto& terrainVertices = partitionTerrainVertices[partitionIdx];
206  auto& terrainNormals = partitionTerrainNormals[partitionIdx];
207  auto& terrainFaces = partitionTerrainFaces[partitionIdx];
208  auto& terrainTriangles = partitionTerrainTriangles[partitionIdx];
209 
210  int normalIdx = terrainNormals.size();
211  int vertexIdx = terrainVertices.size();
212 
213  auto terrainHeightVectorX = static_cast<int>(x / STEP_SIZE);
214  auto terrainHeightVectorZ = static_cast<int>(z / STEP_SIZE);
215 
216  Vector3 topVertex;
217  Vector3 topLeftVertex;
218  Vector3 leftVertex;
219  Vector3 vertex;
220 
221  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ - 1, topVertex);
222  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX - 1, terrainHeightVectorZ - 1, topLeftVertex);
223  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX - 1, terrainHeightVectorZ, leftVertex);
224  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ, vertex);
225 
226  terrainVertices.push_back(topVertex);
227  terrainVertices.push_back(topLeftVertex);
228  terrainVertices.push_back(leftVertex);
229  terrainVertices.push_back(vertex);
230 
231  auto topNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ - 1);
232  auto topLeftNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX - 1, terrainHeightVectorZ - 1);
233  auto leftNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX - 1, terrainHeightVectorZ);
234  auto normal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ);
235 
236  terrainNormals.push_back(topNormal);
237  terrainNormals.push_back(topLeftNormal);
238  terrainNormals.push_back(leftNormal);
239  terrainNormals.push_back(normal);
240 
241  terrainFaces.push_back(
242  {
243  vertexIdx + 0,
244  vertexIdx + 1,
245  vertexIdx + 2,
246  normalIdx + 0,
247  normalIdx + 1,
248  normalIdx + 2
249  }
250  );
251  terrainFaces.push_back(
252  {
253  vertexIdx + 2,
254  vertexIdx + 3,
255  vertexIdx + 0,
256  normalIdx + 2,
257  normalIdx + 3,
258  normalIdx + 0
259  }
260  );
261  terrainTriangles[terrainHeightVectorZ]+= 2;
262  }
263  }
264  auto partitionIdx = 0;
265  for (auto partitionIdx = 0; partitionIdx < partitionCount; partitionIdx++) {
266  if (partitionTerrainFaces[partitionIdx].empty() == true) continue;
267  //
268  auto modelId = "terrain." + to_string(partitionIdx);
269  auto terrainModel = make_unique<Model>(modelId, modelId, UpVector::Y_UP, RotationOrder::ZYX, nullptr);
270  //
271  auto terrainMaterial = make_unique<Material>("terrain");
272  terrainMaterial->setSpecularMaterialProperties(make_unique<SpecularMaterialProperties>().release());
273  // TODO: Fix me! The textures seem to be much too dark
274  terrainMaterial->getSpecularMaterialProperties()->setAmbientColor(Color4(2.0f, 2.0f, 2.0f, 0.0f));
275  terrainMaterial->getSpecularMaterialProperties()->setDiffuseColor(Color4(1.0f, 1.0f, 1.0f, 1.0f));
276  terrainMaterial->getSpecularMaterialProperties()->setSpecularColor(Color4(0.0f, 0.0f, 0.0f, 0.0f));
277  //
278  auto terrainNode = make_unique<Node>(terrainModel.get(), nullptr, "terrain", "terrain");
279  FacesEntity nodeFacesEntityTerrain(terrainNode.get(), "terrain.facesentity");
280  nodeFacesEntityTerrain.setMaterial(terrainMaterial.get());
281  vector<FacesEntity> nodeFacesEntities;
282  vector<Face> nodeFaces;
283  //
284  auto trianglesPerX = partitionTerrainTriangles[partitionIdx].begin()->second;
285  auto trianglesPerZ = partitionTerrainTriangles[partitionIdx].size();
286  for (const auto& faceIndices: partitionTerrainFaces[partitionIdx]) {
287  nodeFaces.emplace_back(
288  terrainNode.get(),
289  faceIndices[0],
290  faceIndices[1],
291  faceIndices[2],
292  faceIndices[3],
293  faceIndices[4],
294  faceIndices[5]
295  );
296  };
297 
298  // create LOD levels?
299  if (createLODLevels == true) {
300  // lod1
301  {
302  vector<int32_t> lod1Indices;
303  const auto& facesIndices = partitionTerrainFaces[partitionIdx];
304  auto finishedZ = false;
305  for (auto z = 0; finishedZ == false && z < trianglesPerZ; z+= 4) {
306  if (z > trianglesPerZ - 4) {
307  z = trianglesPerZ - 4;
308  finishedZ = true;
309  }
310  auto finishedX = false;
311  for (auto x = 0; finishedX == false && x < trianglesPerX; x+= 8) {
312  if (x > trianglesPerX - 8) {
313  x = trianglesPerX - 8;
314  finishedX = true;
315  }
316  lod1Indices.push_back(facesIndices[z * trianglesPerX + x + 6][0]); // top
317  lod1Indices.push_back(facesIndices[z * trianglesPerX + x][1]); // top left
318  lod1Indices.push_back(facesIndices[(z + 3) * trianglesPerX + x][2]); // left
319  lod1Indices.push_back(facesIndices[(z + 3) * trianglesPerX + x][2]); // left
320  lod1Indices.push_back(facesIndices[(z + 3) * trianglesPerX + x + 7][1]); // vertex
321  lod1Indices.push_back(facesIndices[z * trianglesPerX + x + 6][0]); // top
322  }
323  }
324  nodeFacesEntityTerrain.setLOD1Indices(lod1Indices);
325  nodeFacesEntityTerrain.setLOD1Distance(64.0f);
326  }
327 
328  // lod2
329  {
330  vector<int32_t> lod2Indices;
331  const auto& facesIndices = partitionTerrainFaces[partitionIdx];
332  auto finishedZ = false;
333  for (auto z = 0; finishedZ == false && z < trianglesPerZ; z+= 8) {
334  if (z > trianglesPerZ - 8) {
335  z = trianglesPerZ - 8;
336  finishedZ = true;
337  }
338  auto finishedX = false;
339  for (auto x = 0; finishedX == false && x < trianglesPerX; x+= 16) {
340  if (x > trianglesPerX - 16) {
341  x = trianglesPerX - 16;
342  finishedX = true;
343  }
344  lod2Indices.push_back(facesIndices[z * trianglesPerX + x + 14][0]); // top
345  lod2Indices.push_back(facesIndices[z * trianglesPerX + x][1]); // top left
346  lod2Indices.push_back(facesIndices[(z + 7) * trianglesPerX + x][2]); // left
347  lod2Indices.push_back(facesIndices[(z + 7) * trianglesPerX + x][2]); // left
348  lod2Indices.push_back(facesIndices[(z + 7) * trianglesPerX + x + 15][1]); // vertex
349  lod2Indices.push_back(facesIndices[z * trianglesPerX + x + 14][0]); // top
350  }
351  }
352  nodeFacesEntityTerrain.setLOD2Indices(lod2Indices);
353  nodeFacesEntityTerrain.setLOD2Distance(128.0f);
354  }
355 
356  // lod3
357  {
358  vector<int32_t> lod3Indices;
359  const auto& facesIndices = partitionTerrainFaces[partitionIdx];
360  auto finishedZ = false;
361  for (auto z = 0; finishedZ == false && z < trianglesPerZ; z+= 16) {
362  if (z > trianglesPerZ - 16) {
363  z = trianglesPerZ - 16;
364  finishedZ = true;
365  }
366  auto finishedX = false;
367  for (auto x = 0; finishedX == false && x < trianglesPerX; x+= 32) {
368  if (x > trianglesPerX - 32) {
369  x = trianglesPerX - 32;
370  finishedX = true;
371  }
372  lod3Indices.push_back(facesIndices[z * trianglesPerX + x + 30][0]); // top
373  lod3Indices.push_back(facesIndices[z * trianglesPerX + x][1]); // top left
374  lod3Indices.push_back(facesIndices[(z + 15) * trianglesPerX + x][2]); // left
375  lod3Indices.push_back(facesIndices[(z + 15) * trianglesPerX + x][2]); // left
376  lod3Indices.push_back(facesIndices[(z + 15) * trianglesPerX + x + 31][1]); // vertex
377  lod3Indices.push_back(facesIndices[z * trianglesPerX + x + 30][0]); // top
378  }
379  }
380  nodeFacesEntityTerrain.setLOD3Indices(lod3Indices);
381  nodeFacesEntityTerrain.setLOD3Distance(192.0f);
382  }
383  }
384  //
385  nodeFacesEntityTerrain.setFaces(nodeFaces);
386  nodeFacesEntities.push_back(nodeFacesEntityTerrain);
387  //
388  terrainNode->setVertices(partitionTerrainVertices[partitionIdx]);
389  terrainNode->setNormals(partitionTerrainNormals[partitionIdx]);
390  terrainNode->setFacesEntities(nodeFacesEntities);
391  //
392  terrainModel->getNodes()[terrainNode->getId()] = terrainNode.get();
393  terrainModel->getSubNodes()[terrainNode->getId()] = terrainNode.get();
394  terrainNode.release();
395  //
396  terrainModel->getMaterials()[terrainMaterial->getId()] = terrainMaterial.get();
397  terrainMaterial.release();
398  //
399  terrainModel->invalidateBoundingBox();
400  if (partitionIdx == 0) {
401  terrainBoundingBox = *terrainModel->getBoundingBox();
402  } else {
403  terrainBoundingBox.extend(terrainModel->getBoundingBox());
404  }
405  //
406  ModelTools::createDefaultAnimation(terrainModel.get(), 1);
407  //
408  terrainModels.push_back(terrainModel.release());
409  }
410 }
411 
412 inline const Vector3 Terrain::computeTerrainVertexNormal(const vector<float>& terrainHeightVector, int verticesPerX, int verticesPerZ, int x, int z) {
413  Vector3 vertexNormal;
414 
415  Vector3 vertex;
416  auto haveVertex = getTerrainVertex(terrainHeightVector, verticesPerX, verticesPerZ, x, z - 1, vertex);
417  if (haveVertex == false) return Vector3(0.0f, 1.0f, 0.0f);
418 
419  Vector3 topVertex;
420  Vector3 topLeftVertex;
421  Vector3 leftVertex;
422  Vector3 bottomVertex;
423  Vector3 rightVertex;
424  Vector3 bottomRightVertex;
425 
426  auto haveTopVertex = getTerrainVertex(terrainHeightVector, verticesPerX, verticesPerZ, x, z - 1, topVertex);
427  auto haveTopLeftVertex = getTerrainVertex(terrainHeightVector, verticesPerX, verticesPerZ, x - 1, z - 1, topLeftVertex);
428  auto haveLeftVertex = getTerrainVertex(terrainHeightVector, verticesPerX, verticesPerZ, x - 1, z, leftVertex);
429  auto haveBottomVertex = getTerrainVertex(terrainHeightVector, verticesPerX, verticesPerZ, x, z + 1, bottomVertex);
430  auto haveRightVertex = getTerrainVertex(terrainHeightVector, verticesPerX, verticesPerZ, x + 1, z, rightVertex);
431  auto haveBottomRightVertex = getTerrainVertex(terrainHeightVector, verticesPerX, verticesPerZ, x + 1, z + 1, bottomRightVertex);
432 
433  Vector3 triangleNormal;
434  int normalCount = 0;
435  if (haveTopVertex == true && haveTopLeftVertex == true) {
436  triangleNormal = ModelTools::computeNormal(
437  {
438  topVertex,
439  topLeftVertex,
440  vertex
441  }
442  );
443  vertexNormal.add(triangleNormal);
444  normalCount++;
445  }
446  if (haveTopLeftVertex == true && haveLeftVertex == true) {
447  triangleNormal = ModelTools::computeNormal(
448  {
449  topLeftVertex,
450  leftVertex,
451  vertex
452  }
453  );
454  vertexNormal.add(triangleNormal);
455  normalCount++;
456  }
457  if (haveLeftVertex == true && haveBottomVertex == true) {
458  triangleNormal = ModelTools::computeNormal(
459  {
460  leftVertex,
461  bottomVertex,
462  vertex
463  }
464  );
465  vertexNormal.add(triangleNormal);
466  normalCount++;
467  }
468  if (haveBottomVertex == true && haveBottomRightVertex == true) {
469  triangleNormal = ModelTools::computeNormal(
470  {
471  bottomVertex,
472  bottomRightVertex,
473  vertex
474  }
475  );
476  vertexNormal.add(triangleNormal);
477  normalCount++;
478  }
479  if (haveBottomRightVertex == true && haveRightVertex == true) {
480  triangleNormal = ModelTools::computeNormal(
481  {
482  bottomRightVertex,
483  rightVertex,
484  vertex
485  }
486  );
487  vertexNormal.add(triangleNormal);
488  normalCount++;
489  }
490  if (haveRightVertex == true && haveTopVertex == true) {
491  triangleNormal = ModelTools::computeNormal(
492  {
493  rightVertex,
494  topVertex,
495  vertex
496  }
497  );
498  vertexNormal.add(triangleNormal);
499  normalCount++;
500  }
501  if (normalCount > 0) {
502  return vertexNormal.normalize();
503  }
504  Console::println("Terrain::computeTerrainVertexNormal(): no vertex normal available: normal count == 0");
505  return vertexNormal.set(0.0f, 1.0f, 0.0f);
506 }
507 
509  BoundingBox& terrainBoundingBox,
510  vector<Model*>& terrainModels,
511  vector<float>& terrainHeightVector,
512  const Vector3& brushCenterPosition,
513  Texture* brushTexture,
514  float brushScale,
515  float brushStrength,
516  BrushOperation brushOperation,
517  float brushHeight
518 ) {
519  // check if we have a texture
520  if (brushTexture == nullptr) return;
521  // check if we have a model
522  if (terrainModels.empty() == true) return;
523 
524  // apply brush
525  vector<vector<Vector3>> partitionTerrainVertices;
526  vector<vector<Vector3>> partitionTerrainNormals;
527  partitionTerrainVertices.resize(terrainModels.size());
528  partitionTerrainNormals.resize(terrainModels.size());
529  auto partitionsX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / PARTITION_SIZE));
530  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / STEP_SIZE));
531  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getZ() / STEP_SIZE));
532 
533  // water
534  if (brushOperation == BRUSHOPERATION_WATER_ADD) return;
535 
536  // other operations
537  auto textureData = brushTexture->getRGBTextureData();
538  auto textureWidth = brushTexture->getTextureWidth();
539  auto textureHeight = brushTexture->getTextureHeight();
540  auto textureBytePerPixel = brushTexture->getRGBDepthBitsPerPixel() == 32?4:3;
541  for (auto z = 0.0f; z < textureHeight * brushScale; z+= STEP_SIZE) {
542  auto brushPosition =
543  brushCenterPosition.
544  clone().
545  sub(
546  Vector3(
547  (static_cast<float>(textureWidth) * brushScale) / 2.0f,
548  0.0f,
549  (static_cast<float>(textureHeight) * brushScale) / 2.0f
550  )
551  ).
552  add(
553  Vector3(
554  0.0f,
555  0.0f,
556  z
557  )
558  );
559  for (auto x = 0.0f; x < textureWidth * brushScale; x+= STEP_SIZE) {
560  auto textureX = static_cast<int>(x / brushScale);
561  auto textureY = static_cast<int>(z / brushScale);
562  auto red = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 0);
563  auto green = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 1);
564  auto blue = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 2);
565  auto alpha = textureBytePerPixel == 3?255:textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 3);
566  auto appliedStrength = (static_cast<float>(red) + static_cast<float>(green) + static_cast<float>(blue)) / (255.0f * 3.0f) * brushStrength;
567  auto terrainHeightVectorX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / STEP_SIZE);
568  auto terrainHeightVectorZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / STEP_SIZE);
569  if (terrainHeightVectorX < 0 || terrainHeightVectorX >= terrainHeightVectorVerticesPerX ||
570  terrainHeightVectorZ < 0 || terrainHeightVectorZ >= terreinHeightVectorVerticesPerZ) {
571  brushPosition.add(
572  Vector3(
573  STEP_SIZE,
574  0.0f,
575  0.0f
576  )
577  );
578  continue;
579  }
580  auto vertexIdx = terrainHeightVectorZ * terrainHeightVectorVerticesPerX + terrainHeightVectorX;
581  auto terrainVertexHeight = terrainHeightVector[vertexIdx];
582  switch(brushOperation) {
583  case BRUSHOPERATION_ADD:
584  terrainVertexHeight+= appliedStrength;
585  break;
587  terrainVertexHeight-= appliedStrength;
588  break;
590  terrainVertexHeight = terrainVertexHeight * (1.0f - Math::clamp(appliedStrength, 0.0f, 1.0f)) + brushHeight * Math::clamp(appliedStrength, 0.0f, 1.0f);
591  break;
593  terrainVertexHeight = terrainVertexHeight * (1.0f - Math::clamp(appliedStrength, 0.0f, 1.0f)) + 0.0f * Math::clamp(appliedStrength, 0.0f, 1.0f);
594  break;
596  auto terrainVertexHeightNeighbours = 0.0f;
597  auto terrainVertexHeightNeighbourCount = 0;
598  Vector3 topVertex;
599  Vector3 leftVertex;
600  Vector3 bottomVertex;
601  Vector3 rightVertex;
602  auto haveTopVertex = getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ - 1, topVertex);
603  auto haveLeftVertex = getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX - 1, terrainHeightVectorZ, leftVertex);
604  auto haveBottomVertex = getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ + 1, bottomVertex);
605  auto haveRightVertex = getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX + 1, terrainHeightVectorZ, rightVertex);
606  if (haveTopVertex == true) {
607  terrainVertexHeightNeighbourCount++;
608  terrainVertexHeightNeighbours+= topVertex[1];
609  }
610  if (haveLeftVertex == true) {
611  terrainVertexHeightNeighbourCount++;
612  terrainVertexHeightNeighbours+= leftVertex[1];
613  }
614  if (haveBottomVertex == true) {
615  terrainVertexHeightNeighbourCount++;
616  terrainVertexHeightNeighbours+= bottomVertex[1];
617  }
618  if (haveRightVertex == true) {
619  terrainVertexHeightNeighbourCount++;
620  terrainVertexHeightNeighbours+= rightVertex[1];
621  }
622  if (terrainVertexHeightNeighbourCount > 0) {
623  auto terrainVertexHeightSmoothed = terrainVertexHeightNeighbours / static_cast<float>(terrainVertexHeightNeighbourCount);
624  terrainVertexHeight = terrainVertexHeight * (1.0f - Math::clamp(appliedStrength, 0.0f, 1.0f)) + terrainVertexHeightSmoothed * Math::clamp(appliedStrength, 0.0f, 1.0f);
625  }
626  break;
627  }
628  terrainHeightVector[vertexIdx] = terrainVertexHeight;
629 
630  // original
631  {
632  auto _brushPosition = brushPosition;
633  auto partitionX = static_cast<int>((_brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
634  auto partitionZ = static_cast<int>((_brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
635  auto partitionIdx = partitionZ * partitionsX + partitionX;
636  auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:nullptr;
637  auto terrainNode = terrainModel != nullptr?terrainModel->getNodeById("terrain"):nullptr;
638 
639  auto terrainModelX = terrainNode != nullptr?static_cast<int>((_brushPosition.getX() - terrainModel->getBoundingBox()->getMin().getX()) / STEP_SIZE):-1;
640  auto terrainModelZ = terrainNode != nullptr?static_cast<int>((_brushPosition.getZ() - terrainModel->getBoundingBox()->getMin().getZ()) / STEP_SIZE):-1;
641  auto terrainModelVerticesPerZ = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getZ() / STEP_SIZE)):-1;
642  auto terrainModelVerticesPerX = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getX() / STEP_SIZE)):-1;
643 
644  if (terrainNode != nullptr &&
645  terrainModelX >= 0 &&
646  terrainModelX < terrainModelVerticesPerX &&
647  terrainModelZ >= 0 &&
648  terrainModelZ < terrainModelVerticesPerZ) {
649  if (partitionTerrainVertices[partitionIdx].empty() == true) {
650  partitionTerrainVertices[partitionIdx] = terrainNode->getVertices();
651  }
652  auto& terrainVertices = partitionTerrainVertices[partitionIdx];
653  auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
654  terrainVertices[vertexIdx + 3][1] = terrainVertexHeight;
655  }
656  }
657 
658  // top
659  {
660  auto _brushPosition = brushPosition.clone().sub(Vector3(0.0f, 0.0f, -STEP_SIZE));
661  auto partitionX = static_cast<int>((_brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
662  auto partitionZ = static_cast<int>((_brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
663  auto partitionIdx = partitionZ * partitionsX + partitionX;
664  auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:nullptr;
665  auto terrainNode = terrainModel != nullptr?terrainModel->getNodeById("terrain"):nullptr;
666 
667  auto terrainModelX = terrainNode != nullptr?static_cast<int>((_brushPosition.getX() - terrainModel->getBoundingBox()->getMin().getX()) / STEP_SIZE):-1;
668  auto terrainModelZ = terrainNode != nullptr?static_cast<int>((_brushPosition.getZ() - terrainModel->getBoundingBox()->getMin().getZ()) / STEP_SIZE):-1;
669  auto terrainModelVerticesPerZ = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getZ() / STEP_SIZE)):-1;
670  auto terrainModelVerticesPerX = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getX() / STEP_SIZE)):-1;
671 
672  if (terrainNode != nullptr &&
673  terrainModelX >= 0 &&
674  terrainModelX < terrainModelVerticesPerX &&
675  terrainModelZ >= 0 &&
676  terrainModelZ < terrainModelVerticesPerZ) {
677  if (partitionTerrainVertices[partitionIdx].empty() == true) {
678  partitionTerrainVertices[partitionIdx] = terrainNode->getVertices();
679  }
680  auto& terrainVertices = partitionTerrainVertices[partitionIdx];
681  auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
682  terrainVertices[vertexIdx + 0][1] = terrainVertexHeight;
683  }
684  }
685 
686  // top, left
687  {
688  auto _brushPosition = brushPosition.clone().sub(Vector3(-STEP_SIZE, 0.0f, -STEP_SIZE));
689  auto partitionX = static_cast<int>((_brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
690  auto partitionZ = static_cast<int>((_brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
691  auto partitionIdx = partitionZ * partitionsX + partitionX;
692  auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:nullptr;
693  auto terrainNode = terrainModel != nullptr?terrainModel->getNodeById("terrain"):nullptr;
694 
695  auto terrainModelX = terrainNode != nullptr?static_cast<int>((_brushPosition.getX() - terrainModel->getBoundingBox()->getMin().getX()) / STEP_SIZE):-1;
696  auto terrainModelZ = terrainNode != nullptr?static_cast<int>((_brushPosition.getZ() - terrainModel->getBoundingBox()->getMin().getZ()) / STEP_SIZE):-1;
697  auto terrainModelVerticesPerZ = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getZ() / STEP_SIZE)):-1;
698  auto terrainModelVerticesPerX = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getX() / STEP_SIZE)):-1;
699 
700  if (terrainNode != nullptr &&
701  terrainModelX >= 0 &&
702  terrainModelX < terrainModelVerticesPerX &&
703  terrainModelZ >= 0 &&
704  terrainModelZ < terrainModelVerticesPerZ) {
705  if (partitionTerrainVertices[partitionIdx].empty() == true) {
706  partitionTerrainVertices[partitionIdx] = terrainNode->getVertices();
707  }
708  auto& terrainVertices = partitionTerrainVertices[partitionIdx];
709  auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
710  terrainVertices[vertexIdx + 1][1] = terrainVertexHeight;
711  }
712  }
713 
714  // left
715  {
716  auto _brushPosition = brushPosition.clone().sub(Vector3(-STEP_SIZE, 0.0f, 0.0f));
717  auto partitionX = static_cast<int>((_brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
718  auto partitionZ = static_cast<int>((_brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
719  auto partitionIdx = partitionZ * partitionsX + partitionX;
720  auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:nullptr;
721  auto terrainNode = terrainModel != nullptr?terrainModel->getNodeById("terrain"):nullptr;
722 
723  auto terrainModelX = terrainNode != nullptr?static_cast<int>((_brushPosition.getX() - terrainModel->getBoundingBox()->getMin().getX()) / STEP_SIZE):-1;
724  auto terrainModelZ = terrainNode != nullptr?static_cast<int>((_brushPosition.getZ() - terrainModel->getBoundingBox()->getMin().getZ()) / STEP_SIZE):-1;
725  auto terrainModelVerticesPerZ = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getZ() / STEP_SIZE)):-1;
726  auto terrainModelVerticesPerX = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getX() / STEP_SIZE)):-1;
727 
728  if (terrainNode != nullptr &&
729  terrainModelX >= 0 &&
730  terrainModelX < terrainModelVerticesPerX &&
731  terrainModelZ >= 0 &&
732  terrainModelZ < terrainModelVerticesPerZ) {
733  if (partitionTerrainVertices[partitionIdx].empty() == true) {
734  partitionTerrainVertices[partitionIdx] = terrainNode->getVertices();
735  }
736  auto& terrainVertices = partitionTerrainVertices[partitionIdx];
737  auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
738  terrainVertices[vertexIdx + 2][1] = terrainVertexHeight;
739  }
740  }
741 
742  //
743  brushPosition.add(
744  Vector3(
745  STEP_SIZE,
746  0.0f,
747  0.0f
748  )
749  );
750  }
751  }
752 
753  // normals
754  for (auto z = -STEP_SIZE * 8.0f; z < textureHeight * brushScale + STEP_SIZE * 16.0f; z+= STEP_SIZE) {
755  auto brushPosition =
756  brushCenterPosition.
757  clone().
758  sub(
759  Vector3(
760  (static_cast<float>(textureWidth) * brushScale) / 2.0f,
761  0.0f,
762  (static_cast<float>(textureHeight) * brushScale) / 2.0f
763  )
764  ).
765  add(
766  Vector3(
767  -STEP_SIZE * 8.0f,
768  0.0f,
769  z
770  )
771  );
772  for (auto x = -STEP_SIZE * 8.0f; x < textureWidth * brushScale + STEP_SIZE * 16.0f; x+= STEP_SIZE) {
773  auto partitionX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
774  auto partitionZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
775  auto partitionIdx = partitionZ * partitionsX + partitionX;
776  auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:nullptr;
777  auto terrainNode = terrainModel != nullptr?terrainModel->getNodeById("terrain"):nullptr;
778 
779  auto terrainHeightVectorX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / STEP_SIZE) + 1;
780  auto terrainHeightVectorZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / STEP_SIZE) + 1;
781  auto terrainModelX = terrainNode != nullptr?static_cast<int>((brushPosition.getX() - terrainModel->getBoundingBox()->getMin().getX()) / STEP_SIZE):-1;
782  auto terrainModelZ = terrainNode != nullptr?static_cast<int>((brushPosition.getZ() - terrainModel->getBoundingBox()->getMin().getZ()) / STEP_SIZE):-1;
783  auto terrainModelVerticesPerZ = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getZ() / STEP_SIZE)):-1;
784  auto terrainModelVerticesPerX = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getX() / STEP_SIZE)):-1;
785 
786  if (terrainNode != nullptr &&
787  terrainModelX >= 0 &&
788  terrainModelX < terrainModelVerticesPerX &&
789  terrainModelZ >= 0 &&
790  terrainModelZ < terrainModelVerticesPerZ) {
791  if (partitionTerrainNormals[partitionIdx].empty() == true) {
792  partitionTerrainNormals[partitionIdx] = terrainNode->getNormals();
793  }
794  auto& terrainNormals = partitionTerrainNormals[partitionIdx];
795 
796  auto topNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ - 1);
797  auto topLeftNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX - 1, terrainHeightVectorZ - 1);
798  auto leftNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX - 1, terrainHeightVectorZ);
799  auto normal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ);
800  auto normalIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
801 
802  terrainNormals[normalIdx + 0] = topNormal;
803  terrainNormals[normalIdx + 1] = topLeftNormal;
804  terrainNormals[normalIdx + 2] = leftNormal;
805  terrainNormals[normalIdx + 3] = normal;
806  }
807 
808  //
809  brushPosition.add(
810  Vector3(
811  STEP_SIZE,
812  0.0f,
813  0.0f
814  )
815  );
816  }
817  }
818 
819  // set terrain model vertices
820  {
821  auto partitionIdx = 0;
822  for (const auto& terrainVertices: partitionTerrainVertices) {
823  if (terrainVertices.empty() == false) {
824  auto terrainNode = terrainModels[partitionIdx]->getNodeById("terrain");
825  if (terrainNode != nullptr) {
826  terrainNode->setVertices(terrainVertices);
827  terrainModels[partitionIdx]->invalidateBoundingBox();
828  }
829  }
830  partitionIdx++;
831  }
832  }
833  // set terrain model normals
834  {
835  auto partitionIdx = 0;
836  for (const auto& terrainNormals: partitionTerrainNormals) {
837  if (terrainNormals.empty() == false) {
838  auto terrainNode = terrainModels[partitionIdx]->getNodeById("terrain");
839  if (terrainNode != nullptr) {
840  terrainNode->setNormals(terrainNormals);
841  terrainModels[partitionIdx]->invalidateBoundingBox();
842  }
843  }
844  partitionIdx++;
845  }
846  }
847 }
848 
850  BoundingBox& terrainBoundingBox, // TODO: constness
851  vector<Model*>& terrainModels,
852  vector<float>& terrainHeightVector,
853  const Vector3& brushCenterPosition,
854  Texture* brushTexture,
855  float brushRotation,
856  const Vector2& brushScale,
857  float heightMin,
858  float heightMax
859 ) {
860  // check if we have a texture
861  if (brushTexture == nullptr) return;
862  // check if we have a model
863  if (terrainModels.empty() == true) return;
864 
865  // apply brush
866  vector<vector<Vector3>> partitionTerrainVertices;
867  vector<vector<Vector3>> partitionTerrainNormals;
868  partitionTerrainVertices.resize(terrainModels.size());
869  partitionTerrainNormals.resize(terrainModels.size());
870  auto partitionsX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / PARTITION_SIZE));
871  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / STEP_SIZE));
872  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getZ() / STEP_SIZE));
873 
874  // texture
875  auto textureData = brushTexture->getRGBTextureData();
876  auto textureWidth = brushTexture->getTextureWidth();
877  auto textureHeight = brushTexture->getTextureHeight();
878  auto textureBytePerPixel = brushTexture->getRGBDepthBitsPerPixel() == 32?4:3;
879 
880  // brush texture matrix
881  Matrix3x3 brushTextureMatrix;
882  brushTextureMatrix.identity();
883  brushTextureMatrix.setTranslation(Vector2(static_cast<float>(textureWidth) / 2.0f, static_cast<float>(textureHeight) / 2.0f));
884  brushTextureMatrix.multiply((Matrix3x3()).identity().scale(Vector2(1.0f / brushScale.getX(), 1.0f / brushScale.getY())));
885  brushTextureMatrix.multiply((Matrix3x3()).identity().setAxes(brushRotation));
886  auto brushScaleMax = Math::max(brushScale.getX(), brushScale.getY());
887 
888  //
889  for (auto z = -textureHeight * brushScaleMax * 2.0f; z < textureHeight * brushScaleMax * 2.0f; z+= STEP_SIZE) {
890  auto brushPosition =
891  brushCenterPosition.
892  clone().
893  sub(
894  Vector3(
895  static_cast<float>(textureWidth) * brushScaleMax * 2.0f,
896  0.0f,
897  0.0f
898  )
899  ).
900  add(
901  Vector3(
902  0.0f,
903  0.0f,
904  z
905  )
906  );
907  for (auto x = -textureWidth * brushScaleMax * 2.0f; x < textureWidth * brushScaleMax * 2.0f; x+= STEP_SIZE) {
908  auto texturePositionUntransformed = Vector2(x, z);
909  auto texturePosition = brushTextureMatrix.multiply(texturePositionUntransformed);
910  auto textureX = static_cast<int>(texturePosition.getX());
911  auto textureY = static_cast<int>(texturePosition.getY());
912  if (textureX < 0 || textureX >= textureWidth ||
913  textureY < 0 || textureY >= textureHeight) {
914  brushPosition.add(
915  Vector3(
916  STEP_SIZE,
917  0.0f,
918  0.0f
919  )
920  );
921  continue;
922  }
923  auto red = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 0);
924  auto green = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 1);
925  auto blue = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 2);
926  auto alpha = textureBytePerPixel == 3?255:textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 3);
927  auto height = ((static_cast<float>(red) + static_cast<float>(green) + static_cast<float>(blue)) / (255.0f * 3.0f) * (heightMax - heightMin)) + heightMin;
928  auto terrainHeightVectorX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / STEP_SIZE);
929  auto terrainHeightVectorZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / STEP_SIZE);
930  if (terrainHeightVectorX < 0 || terrainHeightVectorX >= terrainHeightVectorVerticesPerX ||
931  terrainHeightVectorZ < 0 || terrainHeightVectorZ >= terreinHeightVectorVerticesPerZ) {
932  brushPosition.add(
933  Vector3(
934  STEP_SIZE,
935  0.0f,
936  0.0f
937  )
938  );
939  continue;
940  }
941  auto terrainVertexHeight = 0.0f;
942  {
943  auto vertexIdx = terrainHeightVectorZ * terrainHeightVectorVerticesPerX + terrainHeightVectorX;
944  terrainVertexHeight = terrainHeightVector[vertexIdx];
945  terrainVertexHeight = height > terrainVertexHeight?height:terrainVertexHeight;
946  terrainHeightVector[vertexIdx] = terrainVertexHeight;
947  }
948 
949  // original
950  {
951  auto _brushPosition = brushPosition;
952  auto partitionX = static_cast<int>((_brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
953  auto partitionZ = static_cast<int>((_brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
954  auto partitionIdx = partitionZ * partitionsX + partitionX;
955  auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:nullptr;
956  auto terrainNode = terrainModel != nullptr?terrainModel->getNodeById("terrain"):nullptr;
957 
958  auto terrainModelX = terrainNode != nullptr?static_cast<int>((_brushPosition.getX() - terrainModel->getBoundingBox()->getMin().getX()) / STEP_SIZE):-1;
959  auto terrainModelZ = terrainNode != nullptr?static_cast<int>((_brushPosition.getZ() - terrainModel->getBoundingBox()->getMin().getZ()) / STEP_SIZE):-1;
960  auto terrainModelVerticesPerZ = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getZ() / STEP_SIZE)):-1;
961  auto terrainModelVerticesPerX = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getX() / STEP_SIZE)):-1;
962 
963  if (terrainNode != nullptr &&
964  terrainModelX >= 0 &&
965  terrainModelX < terrainModelVerticesPerX &&
966  terrainModelZ >= 0 &&
967  terrainModelZ < terrainModelVerticesPerZ) {
968  if (partitionTerrainVertices[partitionIdx].empty() == true) {
969  partitionTerrainVertices[partitionIdx] = terrainNode->getVertices();
970  }
971  auto& terrainVertices = partitionTerrainVertices[partitionIdx];
972  auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
973  terrainVertices[vertexIdx + 3][1] = terrainVertexHeight;
974  }
975  }
976 
977  // top
978  {
979  auto _brushPosition = brushPosition.clone().sub(Vector3(0.0f, 0.0f, -STEP_SIZE));
980  auto partitionX = static_cast<int>((_brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
981  auto partitionZ = static_cast<int>((_brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
982  auto partitionIdx = partitionZ * partitionsX + partitionX;
983  auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:nullptr;
984  auto terrainNode = terrainModel != nullptr?terrainModel->getNodeById("terrain"):nullptr;
985 
986  auto terrainModelX = terrainNode != nullptr?static_cast<int>((_brushPosition.getX() - terrainModel->getBoundingBox()->getMin().getX()) / STEP_SIZE):-1;
987  auto terrainModelZ = terrainNode != nullptr?static_cast<int>((_brushPosition.getZ() - terrainModel->getBoundingBox()->getMin().getZ()) / STEP_SIZE):-1;
988  auto terrainModelVerticesPerZ = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getZ() / STEP_SIZE)):-1;
989  auto terrainModelVerticesPerX = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getX() / STEP_SIZE)):-1;
990 
991  if (terrainNode != nullptr &&
992  terrainModelX >= 0 &&
993  terrainModelX < terrainModelVerticesPerX &&
994  terrainModelZ >= 0 &&
995  terrainModelZ < terrainModelVerticesPerZ) {
996  if (partitionTerrainVertices[partitionIdx].empty() == true) {
997  partitionTerrainVertices[partitionIdx] = terrainNode->getVertices();
998  }
999  auto& terrainVertices = partitionTerrainVertices[partitionIdx];
1000  auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
1001  terrainVertices[vertexIdx + 0][1] = terrainVertexHeight;
1002  }
1003  }
1004 
1005  // top, left
1006  {
1007  auto _brushPosition = brushPosition.clone().sub(Vector3(-STEP_SIZE, 0.0f, -STEP_SIZE));
1008  auto partitionX = static_cast<int>((_brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
1009  auto partitionZ = static_cast<int>((_brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
1010  auto partitionIdx = partitionZ * partitionsX + partitionX;
1011  auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:nullptr;
1012  auto terrainNode = terrainModel != nullptr?terrainModel->getNodeById("terrain"):nullptr;
1013 
1014  auto terrainModelX = terrainNode != nullptr?static_cast<int>((_brushPosition.getX() - terrainModel->getBoundingBox()->getMin().getX()) / STEP_SIZE):-1;
1015  auto terrainModelZ = terrainNode != nullptr?static_cast<int>((_brushPosition.getZ() - terrainModel->getBoundingBox()->getMin().getZ()) / STEP_SIZE):-1;
1016  auto terrainModelVerticesPerZ = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getZ() / STEP_SIZE)):-1;
1017  auto terrainModelVerticesPerX = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getX() / STEP_SIZE)):-1;
1018 
1019  if (terrainNode != nullptr &&
1020  terrainModelX >= 0 &&
1021  terrainModelX < terrainModelVerticesPerX &&
1022  terrainModelZ >= 0 &&
1023  terrainModelZ < terrainModelVerticesPerZ) {
1024  if (partitionTerrainVertices[partitionIdx].empty() == true) {
1025  partitionTerrainVertices[partitionIdx] = terrainNode->getVertices();
1026  }
1027  auto& terrainVertices = partitionTerrainVertices[partitionIdx];
1028  auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
1029  terrainVertices[vertexIdx + 1][1] = terrainVertexHeight;
1030  }
1031  }
1032 
1033  // left
1034  {
1035  auto _brushPosition = brushPosition.clone().sub(Vector3(-STEP_SIZE, 0.0f, 0.0f));
1036  auto partitionX = static_cast<int>((_brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
1037  auto partitionZ = static_cast<int>((_brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
1038  auto partitionIdx = partitionZ * partitionsX + partitionX;
1039  auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:nullptr;
1040  auto terrainNode = terrainModel != nullptr?terrainModel->getNodeById("terrain"):nullptr;
1041 
1042  auto terrainModelX = terrainNode != nullptr?static_cast<int>((_brushPosition.getX() - terrainModel->getBoundingBox()->getMin().getX()) / STEP_SIZE):-1;
1043  auto terrainModelZ = terrainNode != nullptr?static_cast<int>((_brushPosition.getZ() - terrainModel->getBoundingBox()->getMin().getZ()) / STEP_SIZE):-1;
1044  auto terrainModelVerticesPerZ = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getZ() / STEP_SIZE)):-1;
1045  auto terrainModelVerticesPerX = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getX() / STEP_SIZE)):-1;
1046 
1047  if (terrainNode != nullptr &&
1048  terrainModelX >= 0 &&
1049  terrainModelX < terrainModelVerticesPerX &&
1050  terrainModelZ >= 0 &&
1051  terrainModelZ < terrainModelVerticesPerZ) {
1052  if (partitionTerrainVertices[partitionIdx].empty() == true) {
1053  partitionTerrainVertices[partitionIdx] = terrainNode->getVertices();
1054  }
1055  auto& terrainVertices = partitionTerrainVertices[partitionIdx];
1056  auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
1057  terrainVertices[vertexIdx + 2][1] = terrainVertexHeight;
1058  }
1059  }
1060 
1061  //
1062  brushPosition.add(
1063  Vector3(
1064  STEP_SIZE,
1065  0.0f,
1066  0.0f
1067  )
1068  );
1069  }
1070  }
1071 
1072  // normals
1073  for (auto z = -textureHeight * brushScaleMax * 2.0f; z < textureHeight * brushScaleMax * 2.0f; z+= STEP_SIZE) {
1074  auto brushPosition =
1075  brushCenterPosition.
1076  clone().
1077  sub(
1078  Vector3(
1079  static_cast<float>(textureWidth) * brushScaleMax * 2.0f,
1080  0.0f,
1081  0.0f
1082  )
1083  ).
1084  add(
1085  Vector3(
1086  0.0f,
1087  0.0f,
1088  z
1089  )
1090  );
1091  for (auto x = -textureWidth * brushScaleMax * 2.0f; x < textureWidth * brushScaleMax * 2.0f; x+= STEP_SIZE) {
1092  auto partitionX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
1093  auto partitionZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
1094  auto partitionIdx = partitionZ * partitionsX + partitionX;
1095  auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:nullptr;
1096  auto terrainNode = terrainModel != nullptr?terrainModel->getNodeById("terrain"):nullptr;
1097 
1098  auto terrainHeightVectorX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / STEP_SIZE) + 1;
1099  auto terrainHeightVectorZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / STEP_SIZE) + 1;
1100  auto terrainModelX = terrainNode != nullptr?static_cast<int>((brushPosition.getX() - terrainModel->getBoundingBox()->getMin().getX()) / STEP_SIZE):-1;
1101  auto terrainModelZ = terrainNode != nullptr?static_cast<int>((brushPosition.getZ() - terrainModel->getBoundingBox()->getMin().getZ()) / STEP_SIZE):-1;
1102  auto terrainModelVerticesPerZ = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getZ() / STEP_SIZE)):-1;
1103  auto terrainModelVerticesPerX = terrainNode != nullptr?static_cast<int>(Math::ceil(terrainModel->getBoundingBox()->getDimensions().getX() / STEP_SIZE)):-1;
1104 
1105  if (terrainNode != nullptr &&
1106  terrainModelX >= 0 &&
1107  terrainModelX < terrainModelVerticesPerX &&
1108  terrainModelZ >= 0 &&
1109  terrainModelZ < terrainModelVerticesPerZ) {
1110  if (partitionTerrainNormals[partitionIdx].empty() == true) {
1111  partitionTerrainNormals[partitionIdx] = terrainNode->getNormals();
1112  }
1113  auto& terrainNormals = partitionTerrainNormals[partitionIdx];
1114 
1115  auto topNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ - 1);
1116  auto topLeftNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX - 1, terrainHeightVectorZ - 1);
1117  auto leftNormal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX - 1, terrainHeightVectorZ);
1118  auto normal = computeTerrainVertexNormal(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorX, terrainHeightVectorZ);
1119  auto normalIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
1120 
1121  terrainNormals[normalIdx + 0] = topNormal;
1122  terrainNormals[normalIdx + 1] = topLeftNormal;
1123  terrainNormals[normalIdx + 2] = leftNormal;
1124  terrainNormals[normalIdx + 3] = normal;
1125  }
1126 
1127  //
1128  brushPosition.add(
1129  Vector3(
1130  STEP_SIZE,
1131  0.0f,
1132  0.0f
1133  )
1134  );
1135  }
1136  }
1137 
1138  // set terrain model vertices
1139  {
1140  auto partitionIdx = 0;
1141  for (const auto& terrainVertices: partitionTerrainVertices) {
1142  if (terrainVertices.empty() == false) {
1143  auto terrainNode = terrainModels[partitionIdx]->getNodeById("terrain");
1144  if (terrainNode != nullptr) {
1145  terrainNode->setVertices(terrainVertices);
1146  terrainModels[partitionIdx]->invalidateBoundingBox();
1147  }
1148  }
1149  partitionIdx++;
1150  }
1151  }
1152  // set terrain model normals
1153  {
1154  auto partitionIdx = 0;
1155  for (const auto& terrainNormals: partitionTerrainNormals) {
1156  if (terrainNormals.empty() == false) {
1157  auto terrainNode = terrainModels[partitionIdx]->getNodeById("terrain");
1158  if (terrainNode != nullptr) {
1159  terrainNode->setNormals(terrainNormals);
1160  terrainModels[partitionIdx]->invalidateBoundingBox();
1161  }
1162  }
1163  partitionIdx++;
1164  }
1165  }
1166 }
1167 
1168 bool Terrain::computeWaterPositionMap(BoundingBox& terrainBoundingBox, const vector<float>& terrainHeightVector, const Vector3& brushCenterPosition, float waterHeight, unordered_map<int, unordered_set<int>>& waterPositionMap) {
1169  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / STEP_SIZE));
1170  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getZ() / STEP_SIZE));
1171 
1172  auto brushPosition = brushCenterPosition;
1173  auto terrainHeightVectorXCenter = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / STEP_SIZE);
1174  auto terrainHeightVectorZCenter = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / STEP_SIZE);
1175 
1176  //
1177  waterPositionMap[terrainHeightVectorZCenter].insert(terrainHeightVectorXCenter);
1178 
1179  //
1180  Console::println("Terrain::determineWaterPositionSet: " + to_string(terrainHeightVectorXCenter) + " / " + to_string(terrainHeightVectorZCenter) + " @ " + to_string(waterHeight));
1181 
1182  //
1183  determineWaterXPositionSet(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorXCenter, terrainHeightVectorZCenter, waterHeight, waterPositionMap[terrainHeightVectorZCenter]);
1184 
1185  //
1186  {
1187  auto zLast = 0;
1188  auto zMin = -1;
1189  while (true == true) {
1190  auto terrainHeightVectorZLast = terrainHeightVectorZCenter + zLast;
1191  auto terrainHeightVectorZ = terrainHeightVectorZCenter + zMin;
1192  for (auto zLastWaterXPosition: waterPositionMap[terrainHeightVectorZLast]) {
1193  if (determineWater(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, zLastWaterXPosition, terrainHeightVectorZ, waterHeight) == true) {
1194  determineWaterXPositionSet(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, zLastWaterXPosition, terrainHeightVectorZ, waterHeight, waterPositionMap[terrainHeightVectorZ]);
1195  }
1196  }
1197  if (waterPositionMap[terrainHeightVectorZ].empty() == true) break;
1198  zLast = zMin;
1199  zMin--;
1200  }
1201  }
1202 
1203  //
1204  {
1205  auto zLast = 0;
1206  auto zMax = 1;
1207  while (true == true) {
1208  auto terrainHeightVectorZLast = terrainHeightVectorZCenter + zLast;
1209  auto terrainHeightVectorZ = terrainHeightVectorZCenter + zMax;
1210  for (auto zLastWaterXPosition: waterPositionMap[terrainHeightVectorZLast]) {
1211  if (determineWater(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, zLastWaterXPosition, terrainHeightVectorZ, waterHeight) == true) {
1212  determineWaterXPositionSet(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, zLastWaterXPosition, terrainHeightVectorZ, waterHeight, waterPositionMap[terrainHeightVectorZ]);
1213  }
1214  }
1215  if (waterPositionMap[terrainHeightVectorZ].empty() == true) break;
1216  zLast = zMax;
1217  zMax++;
1218  }
1219  }
1220 
1221  {
1222  auto waterPositionMapCopy = waterPositionMap;
1223  auto zMin = waterPositionMap.begin()->first;
1224  auto zMax = waterPositionMap.begin()->first;
1225  do {
1226  waterPositionMapCopy = waterPositionMap;
1227  for (const auto& [z, xValues]: waterPositionMap) {
1228  for (auto x: xValues) {
1229  if (hasWaterPosition(waterPositionMap, x, z - 1) == false &&
1230  determineWater(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, x, z - 1, waterHeight) == true) {
1232  terrainBoundingBox,
1233  terrainHeightVector,
1234  Vector3(
1235  terrainBoundingBox.getMin().getX() + x * STEP_SIZE,
1236  waterHeight,
1237  terrainBoundingBox.getMin().getZ() + ((z - 1) * STEP_SIZE)
1238  ),
1239  waterHeight,
1240  waterPositionMap
1241  );
1242  } else
1243  if (hasWaterPosition(waterPositionMap, x, z + 1) == false &&
1244  determineWater(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, x, z + 1, waterHeight) == true) {
1246  terrainBoundingBox,
1247  terrainHeightVector,
1248  Vector3(
1249  terrainBoundingBox.getMin().getX() + x * STEP_SIZE,
1250  waterHeight,
1251  terrainBoundingBox.getMin().getZ() + ((z + 1) * STEP_SIZE)
1252  ),
1253  waterHeight,
1254  waterPositionMap
1255  );
1256  }
1257  }
1258  }
1259  } while (waterPositionMapCopy.size() != waterPositionMap.size());
1260  waterPositionMap[zMax] = waterPositionMap[zMax - 1];
1261  }
1262 
1263  //
1264  auto haveWaterPositionSet = waterPositionMap.empty() == false;
1265  Console::println("Terrain::determineWaterPositionSet: Have water position set: " + to_string(haveWaterPositionSet));
1266  return haveWaterPositionSet;
1267 }
1268 
1269 Vector3 Terrain::computeWaterReflectionEnvironmentMappingPosition(const unordered_map<int, unordered_set<int>>& waterPositionMap, float waterHeight) {
1270  // determine reflection environment mapping position
1271  auto zMin = Integer::MAX_VALUE;
1272  auto zMax = Integer::MIN_VALUE;
1273  auto xMin = Integer::MAX_VALUE;
1274  auto xMax = Integer::MIN_VALUE;
1275  for (const auto& [z, xValues]: waterPositionMap) {
1276  if (z < zMin) zMin = z;
1277  if (z > zMax) zMax = z;
1278  for (auto x: xValues) {
1279  if (x < xMin) xMin = x;
1280  if (x > xMax) xMax = x;
1281  }
1282  }
1283 
1284  return Vector3(
1285  (static_cast<float>(xMin + xMax) / 2.0f) * STEP_SIZE,
1286  waterHeight + 2.0f,
1287  (static_cast<float>(zMin + zMax) / 2.0f) * STEP_SIZE
1288  );
1289 }
1290 
1292  BoundingBox& terrainBoundingBox,
1293  const unordered_map<int, unordered_set<int>>& waterPositionMap,
1294  float waterHeight,
1295  int waterModelIdx,
1296  vector<Model*>& waterModels
1297 ) {
1298  auto width = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX()));
1299  auto depth = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getZ()));
1300  auto partitionsX = static_cast<int>(Math::ceil(width / PARTITION_SIZE));
1301  auto partitionsZ = static_cast<int>(Math::ceil(depth / PARTITION_SIZE));
1302  auto partitionCount = partitionsX * partitionsZ;
1303  vector<vector<Vector3>> partitionTerrainVertices;
1304  vector<vector<Vector3>> partitionTerrainNormals;
1305  vector<vector<array<int, 6>>> partitionWaterFaces;
1306  partitionTerrainVertices.resize(partitionCount);
1307  partitionTerrainNormals.resize(partitionCount);
1308  partitionWaterFaces.resize(partitionCount);
1309  for (float z = 0.0f; z < depth; z+= STEP_SIZE) {
1310  for (float x = 0.0f; x < width; x+= STEP_SIZE) {
1311  auto terrainHeightVectorX = static_cast<int>(x / STEP_SIZE);
1312  auto terrainHeightVectorZ = static_cast<int>(z / STEP_SIZE);
1313 
1314  Vector3 topVertex;
1315  Vector3 topLeftVertex;
1316  Vector3 leftVertex;
1317  Vector3 vertex;
1318 
1319  auto hasTopLeft = hasWaterPosition(waterPositionMap, terrainHeightVectorX - 1, terrainHeightVectorZ - 1);
1320  auto hasTop = hasWaterPosition(waterPositionMap, terrainHeightVectorX, terrainHeightVectorZ - 1);
1321  auto hasLeft = hasWaterPosition(waterPositionMap, terrainHeightVectorX - 1, terrainHeightVectorZ);
1322  auto hasOrigin = hasWaterPosition(waterPositionMap, terrainHeightVectorX, terrainHeightVectorZ);
1323 
1324  auto haveVertexCount = 0;
1325  if (hasTop == true) haveVertexCount++;
1326  if (hasTopLeft == true) haveVertexCount++;
1327  if (hasLeft == true) haveVertexCount++;
1328  if (hasOrigin == true) haveVertexCount++;
1329  if (haveVertexCount < 3) continue;
1330 
1331  auto partitionX = static_cast<int>(x / PARTITION_SIZE);
1332  auto partitionZ = static_cast<int>(z / PARTITION_SIZE);
1333  auto partitionIdx = partitionZ * partitionsX + partitionX;
1334 
1335  auto& terrainVertices = partitionTerrainVertices[partitionIdx];
1336  auto& terrainNormals = partitionTerrainNormals[partitionIdx];
1337  auto& terrainFaces = partitionWaterFaces[partitionIdx];
1338 
1339  int normalIdx = terrainNormals.size();
1340  int vertexIdx = terrainVertices.size();
1341 
1342  getWaterVertex(terrainHeightVectorX, terrainHeightVectorZ - 1, waterHeight, topVertex);
1343  getWaterVertex(terrainHeightVectorX - 1, terrainHeightVectorZ - 1, waterHeight, topLeftVertex);
1344  getWaterVertex(terrainHeightVectorX - 1, terrainHeightVectorZ, waterHeight, leftVertex);
1345  getWaterVertex(terrainHeightVectorX, terrainHeightVectorZ, waterHeight, vertex);
1346 
1347  if (hasTop == true) terrainVertices.push_back(topVertex);
1348  if (hasTopLeft == true) terrainVertices.push_back(topLeftVertex);
1349  if (hasLeft == true) terrainVertices.push_back(leftVertex);
1350  if (hasOrigin == true) terrainVertices.push_back(vertex);
1351 
1352  auto normal = Vector3(0.0f, 1.0f, 0.0f);
1353  auto topNormal = Vector3(0.0f, 1.0f, 0.0f);
1354  auto topLeftNormal = Vector3(0.0f, 1.0f, 0.0f);
1355  auto leftNormal = Vector3(0.0f, 1.0f, 0.0f);
1356 
1357  if (hasTop == true) terrainNormals.push_back(topNormal);
1358  if (hasTopLeft == true) terrainNormals.push_back(topLeftNormal);
1359  if (hasLeft == true) terrainNormals.push_back(leftNormal);
1360  if (hasOrigin == true) terrainNormals.push_back(normal);
1361 
1362  if (hasTopLeft == false ||
1363  hasTop == false ||
1364  hasLeft == false ||
1365  hasOrigin == false) {
1366  terrainFaces.push_back(
1367  {
1368  vertexIdx + 0,
1369  vertexIdx + 1,
1370  vertexIdx + 2,
1371  normalIdx + 0,
1372  normalIdx + 1,
1373  normalIdx + 2
1374  }
1375  );
1376  } else {
1377  terrainFaces.push_back(
1378  {
1379  vertexIdx + 0,
1380  vertexIdx + 1,
1381  vertexIdx + 2,
1382  normalIdx + 0,
1383  normalIdx + 1,
1384  normalIdx + 2
1385  }
1386  );
1387  terrainFaces.push_back(
1388  {
1389  vertexIdx + 2,
1390  vertexIdx + 3,
1391  vertexIdx + 0,
1392  normalIdx + 2,
1393  normalIdx + 3,
1394  normalIdx + 0
1395  }
1396  );
1397  }
1398  }
1399  }
1400  for (auto partitionIdx = 0; partitionIdx < partitionCount; partitionIdx++) {
1401  if (partitionWaterFaces[partitionIdx].empty() == true) continue;
1402  auto modelId = "water." + to_string(waterModelIdx) + "." + to_string(partitionIdx);
1403  auto waterModel = make_unique<Model>(modelId, modelId, UpVector::Y_UP, RotationOrder::ZYX, nullptr);
1404  //
1405  auto waterMaterial = make_unique<Material>("water");
1406  waterMaterial->setDoubleSided(true);
1407  waterMaterial->setSpecularMaterialProperties(make_unique<SpecularMaterialProperties>().release());
1408  waterMaterial->getSpecularMaterialProperties()->setAmbientColor(Color4(0.022f, 0.13f, 0.56f, 1.0f));
1409  waterMaterial->getSpecularMaterialProperties()->setDiffuseColor(Color4(0.026f, 0.15f, 0.64f, 1.0f));
1410  waterMaterial->getSpecularMaterialProperties()->setSpecularColor(Color4(1.0f, 1.0f, 1.0f, 1.0f));
1411  waterMaterial->getSpecularMaterialProperties()->setShininess(100.0f);
1412  //
1413  auto waterNode = make_unique<Node>(waterModel.get(), nullptr, "water", "water");
1414  FacesEntity nodeFacesEntityWater(waterNode.get(), "water.facesentity");
1415  nodeFacesEntityWater.setMaterial(waterMaterial.get());
1416  vector<FacesEntity> nodeFacesEntities;
1417  vector<Face> nodeFaces;
1418  for (auto& faceIndices: partitionWaterFaces[partitionIdx]) {
1419  nodeFaces.emplace_back(
1420  waterNode.get(),
1421  faceIndices[0],
1422  faceIndices[1],
1423  faceIndices[2],
1424  faceIndices[3],
1425  faceIndices[4],
1426  faceIndices[5]
1427  );
1428  };
1429  nodeFacesEntityWater.setFaces(nodeFaces);
1430  nodeFacesEntities.push_back(nodeFacesEntityWater);
1431  //
1432  waterNode->setVertices(partitionTerrainVertices[partitionIdx]);
1433  waterNode->setNormals(partitionTerrainNormals[partitionIdx]);
1434  waterNode->setFacesEntities(nodeFacesEntities);
1435  //
1436  waterModel->getNodes()[waterNode->getId()] = waterNode.get();
1437  waterModel->getSubNodes()[waterNode->getId()] = waterNode.get();
1438  waterNode.release();
1439  //
1440  waterModel->getMaterials()[waterMaterial->getId()] = waterMaterial.get();
1441  waterMaterial.release();
1442  //
1443  ModelTools::prepareForIndexedRendering(waterModel.get());
1444  ModelTools::createDefaultAnimation(waterModel.get(), 1);
1445  //
1446  waterModels.push_back(waterModel.release());
1447  }
1448 }
1449 
1451  BoundingBox& terrainBoundingBox,
1452  vector<Model*>& terrainModels,
1453  vector<float>& terrainHeightVector,
1454  const Vector3& brushCenterPosition,
1455  float& brushHeight
1456 ) {
1457  // check if we have a model
1458  if (terrainModels.empty() == true) return false;
1459 
1460  // get height at brush position
1461  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / STEP_SIZE));
1462  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getZ() / STEP_SIZE));
1463  auto terrainHeightVectorX = static_cast<int>((brushCenterPosition.getX() - terrainBoundingBox.getMin().getX()) / STEP_SIZE);
1464  auto terrainHeightVectorZ = static_cast<int>((brushCenterPosition.getZ() - terrainBoundingBox.getMin().getZ()) / STEP_SIZE);
1465  if (terrainHeightVectorX < 0 || terrainHeightVectorX >= terreinHeightVectorVerticesPerZ ||
1466  terrainHeightVectorZ < 0 || terrainHeightVectorZ >= terreinHeightVectorVerticesPerZ) return false;
1467  brushHeight = terrainHeightVector[terrainHeightVectorZ * terrainHeightVectorVerticesPerX + terrainHeightVectorX];
1468 
1469  //
1470  return true;
1471 }
1472 
1474  BoundingBox& terrainBoundingBox,
1475  vector<unordered_map<int, vector<Transform>>>& foliageMaps
1476 ) {
1477  //
1478  auto width = terrainBoundingBox.getDimensions().getX();
1479  auto depth = terrainBoundingBox.getDimensions().getZ();
1480  createFoliageMaps(width, depth, foliageMaps);
1481 }
1482 
1484  float terrainWidth,
1485  float terrainDepth,
1486  vector<unordered_map<int, vector<Transform>>>& foliageMaps
1487 ) {
1488  //
1489  auto partitionsX = static_cast<int>(Math::ceil(terrainWidth / PARTITION_SIZE));
1490  auto partitionsZ = static_cast<int>(Math::ceil(terrainDepth / PARTITION_SIZE));
1491  auto partitionCount = partitionsX * partitionsZ;
1492  foliageMaps.resize(partitionCount);
1493  for (auto& foliageMap: foliageMaps) foliageMap.clear();
1494 }
1495 
1497  vector<unordered_map<int, vector<Transform>>>& foliageMaps
1498 ) {
1499  //
1500  for (auto& foliageMap: foliageMaps) foliageMap.clear();
1501 }
1502 
1504  BoundingBox& terrainBoundingBox,
1505  vector<float>& terrainHeightVector,
1506  const Vector3& brushCenterPosition,
1507  const FoliageBrush& foliageBrush,
1508  const vector<FoliageBrushPrototype>& foliageBrushPrototypes,
1509  BrushOperation brushOperation,
1510  vector<unordered_map<int, vector<Transform>>>& foliageMaps,
1511  vector<unordered_map<int, vector<Transform>>>& newFoliageMaps
1512 ) {
1513  // check if we have a texture
1514  if (foliageBrush.brushTexture == nullptr) return;
1515 
1516  // apply brush
1517  auto partitionsX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / PARTITION_SIZE));
1518  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / STEP_SIZE));
1519  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getZ() / STEP_SIZE));
1520 
1521  // other operations
1522  auto textureData = foliageBrush.brushTexture->getRGBTextureData();
1523  auto textureWidth = foliageBrush.brushTexture->getTextureWidth();
1524  auto textureHeight = foliageBrush.brushTexture->getTextureHeight();
1525  auto textureBytePerPixel = foliageBrush.brushTexture->getRGBDepthBitsPerPixel() == 32?4:3;
1526 
1527  //
1528  vector<unordered_map<int, float>> brushMapCountMapTemplate;
1529  auto brushMapCountMapWidth = static_cast<int>(textureWidth * foliageBrush.brushScale);
1530  auto brushMapCountMapDepth = static_cast<int>(textureHeight * foliageBrush.brushScale);
1531  for (auto z = 0.0f; z < textureHeight * foliageBrush.brushScale; z+= 1.0f) {
1532  for (auto x = 0.0f; x < textureWidth * foliageBrush.brushScale; x+= 1.0f) {
1533  auto textureX = static_cast<int>(x / foliageBrush.brushScale);
1534  auto textureY = static_cast<int>(z / foliageBrush.brushScale);
1535  auto red = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 0);
1536  auto green = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 1);
1537  auto blue = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 2);
1538  auto alpha = textureBytePerPixel == 3?255:textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 3);
1539  auto appliedDensity = (static_cast<float>(red) + static_cast<float>(green) + static_cast<float>(blue)) / (255.0f * 3.0f) * foliageBrush.brushDensity;
1540  unordered_map<int, float> brushMapCountMapEntity;
1541  for (auto& foliageBrushPrototype: foliageBrushPrototypes) {
1542  if (foliageBrushPrototype.prototypeId == -1) continue;
1543  auto foliageCount = foliageBrushPrototype.count;
1544  brushMapCountMapEntity[foliageBrushPrototype.prototypeId] = foliageCount * appliedDensity;
1545  }
1546  brushMapCountMapTemplate.push_back(brushMapCountMapEntity);
1547  }
1548  }
1549 
1550  //
1551  auto brushMapCountMap = brushMapCountMapTemplate;
1552  for (auto& foliageBrushPrototype: foliageBrushPrototypes) {
1553  if (foliageBrushPrototype.prototypeId == -1) continue;
1554  float totalCount = 0.0f;
1555  for (auto i = 0; i < brushMapCountMap.size(); i++) {
1556  auto& brushMapCountMapEntity = brushMapCountMap[i];
1557  auto count = brushMapCountMapEntity[foliageBrushPrototype.prototypeId];
1558  auto countFloor = Math::floor(count);
1559  totalCount+= count - countFloor;
1560  brushMapCountMapEntity[foliageBrushPrototype.prototypeId] = countFloor;
1561  auto totalCountFloor = Math::floor(totalCount);
1562  if (totalCount >= 1.0f) brushMapCountMapEntity[foliageBrushPrototype.prototypeId]+= totalCountFloor;
1563  totalCount-= totalCountFloor;
1564  }
1565  }
1566 
1567  // randomize
1568  unordered_map<int, unordered_map<int, vector<int>>> brushMapIdxPerDensityPerPrototype;
1569  for (const auto& foliageBrushPrototype: foliageBrushPrototypes) {
1570  for (auto i = 0; i < brushMapCountMapTemplate.size(); i++) {
1571  auto brushMapPrototypeCount = brushMapCountMapTemplate[i][foliageBrushPrototype.prototypeId];
1572  brushMapIdxPerDensityPerPrototype[foliageBrushPrototype.prototypeId][static_cast<int>(brushMapPrototypeCount * 1000.0f)].push_back(i);
1573  }
1574  }
1575  for (const auto& foliageBrushPrototype: foliageBrushPrototypes) {
1576  for (auto i = 0; i < brushMapCountMap.size(); i++) {
1577  auto brushMapPrototypeCountMapEntityITemplate = brushMapCountMapTemplate[i][foliageBrushPrototype.prototypeId];
1578  auto brushMapPrototypeCountMapEntityI = brushMapCountMap[i][foliageBrushPrototype.prototypeId];
1579  const auto& brushMapIdxPerDensityPerPrototypeVector = brushMapIdxPerDensityPerPrototype[foliageBrushPrototype.prototypeId][static_cast<int>(brushMapPrototypeCountMapEntityITemplate * 1000.0f)];
1580  auto j = brushMapIdxPerDensityPerPrototypeVector[static_cast<int>(brushMapIdxPerDensityPerPrototypeVector.size() - 1) * Math::random()];
1581  auto brushMapPrototypeCountMapEntityJ = brushMapCountMap[j][foliageBrushPrototype.prototypeId];
1582  brushMapCountMap[j][foliageBrushPrototype.prototypeId] = brushMapPrototypeCountMapEntityI;
1583  brushMapCountMap[i][foliageBrushPrototype.prototypeId] = brushMapPrototypeCountMapEntityJ;
1584  }
1585  }
1586 
1587  //
1588  for (auto z = 0.0f; z < textureHeight * foliageBrush.brushScale; z+= 1.0f) {
1589  auto brushPosition =
1590  brushCenterPosition.
1591  clone().
1592  sub(
1593  Vector3(
1594  (static_cast<float>(textureWidth) * foliageBrush.brushScale) / 2.0f,
1595  0.0f,
1596  ((static_cast<float>(textureHeight) * foliageBrush.brushScale) / 2.0f)
1597  )
1598  ).
1599  add(
1600  Vector3(
1601  0.0f,
1602  0.0f,
1603  z
1604  )
1605  );
1606  for (auto x = 0.0f; x < textureWidth * foliageBrush.brushScale; x+= 1.0f) {
1607  auto brushMapCountMapX = static_cast<int>(x);
1608  auto brushMapCountMapZ = static_cast<int>(z);
1609  auto brushMapCountMapEntity = brushMapCountMap[brushMapCountMapZ * brushMapCountMapWidth + brushMapCountMapX];
1610  auto terrainHeightVectorX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / STEP_SIZE);
1611  auto terrainHeightVectorZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / STEP_SIZE);
1612  if (brushPosition.getX() < 0.0f || brushPosition.getZ() < 0.0f ||
1613  terrainHeightVectorX < 0 || terrainHeightVectorX >= terrainHeightVectorVerticesPerX ||
1614  terrainHeightVectorZ < 0 || terrainHeightVectorZ >= terreinHeightVectorVerticesPerZ) {
1615  //
1616  brushPosition.add(
1617  Vector3(
1618  STEP_SIZE,
1619  0.0f,
1620  0.0f
1621  )
1622  );
1623  continue;
1624  }
1625 
1626  //
1627  switch(brushOperation) {
1628  case BRUSHOPERATION_ADD:
1629  for (const auto& [prototypeId, prototypeCount]: brushMapCountMapEntity) {
1630  if (prototypeId == -1) continue;
1631 
1632  //
1633  auto prototypeIdx = -1;
1634  for (auto i = 0; i < foliageBrushPrototypes.size(); i++) {
1635  if (foliageBrushPrototypes[i].prototypeId == prototypeId) prototypeIdx = i;
1636  }
1637  if (prototypeIdx == -1) continue;
1638 
1639  //
1640  for (auto i = 0; i < static_cast<int>(prototypeCount); i++) {
1641  auto prototypeScale = foliageBrushPrototypes[prototypeIdx].scaleMin + ((foliageBrushPrototypes[prototypeIdx].scaleMax - foliageBrushPrototypes[prototypeIdx].scaleMin) * Math::random());
1642 
1643  //
1644  Vector3 translation(
1645  Math::floor(brushPosition.getX()) + Math::random(),
1646  0.0f,
1647  Math::floor(brushPosition.getZ()) + Math::random()
1648  );
1649 
1650  //
1651  auto partitionX = static_cast<int>((translation.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
1652  auto partitionZ = static_cast<int>((translation.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
1653  auto partitionIdx = partitionZ * partitionsX + partitionX;
1654 
1655  //
1656  auto haveContact = false;
1657  Vector3 contact;
1658  Vector3 normal;
1659  float height;
1660  for (int _z = -1; _z < 2; _z++)
1661  for (int _x = -1; _x < 2; _x++) {
1662  Vector3 topVertex;
1663  Vector3 topLeftVertex;
1664  Vector3 leftVertex;
1665  Vector3 vertex;
1666 
1667  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX, _z + terrainHeightVectorZ - 1, topVertex);
1668  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX - 1, _z + terrainHeightVectorZ - 1, topLeftVertex);
1669  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX - 1, _z + terrainHeightVectorZ, leftVertex);
1670  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX, _z + terrainHeightVectorZ, vertex);
1671 
1672  if (LineSegment::doesLineSegmentCollideWithTriangle(topVertex, topLeftVertex, leftVertex, translation.clone().setY(-10000.0f), translation.clone().setY(+10000.0f), contact) == true) {
1673  haveContact = true;
1674  normal = ModelTools::computeNormal({topVertex, topLeftVertex, leftVertex});
1675  height = (topVertex.getY() + topLeftVertex.getY() + leftVertex.getY()) / 3.0f;
1676  break;
1677  } else
1678  if (LineSegment::doesLineSegmentCollideWithTriangle(leftVertex, vertex, topVertex, translation.clone().setY(-10000.0f), translation.clone().setY(+10000.0f), contact) == true) {
1679  haveContact = true;
1680  normal = ModelTools::computeNormal({leftVertex, vertex, topVertex});
1681  height = (leftVertex.getY() + vertex.getY() + topVertex.getY()) / 3.0f;
1682  break;
1683  }
1684  }
1685 
1686  // check height
1687  if (height < foliageBrushPrototypes[prototypeIdx].heightMin || height > foliageBrushPrototypes[prototypeIdx].heightMax) continue;
1688 
1689  //
1690  if (haveContact == false) {
1692  "Terrain::applyFoliageBrush(): no contact@" +
1693  to_string(translation.getX()) + ", " +
1694  to_string(translation.getZ())
1695  );
1696  contact = translation;
1697  continue;
1698  }
1699 
1700  // slope
1701  auto slope = Math::abs(180.0f / 3.14f * Math::acos(Math::clamp(Vector3::computeDotProduct(normal, Vector3(0.0f, 1.0f, 0.0f)), -1.0f, 1.0f)));
1702  if (slope < foliageBrushPrototypes[prototypeIdx].slopeMin || slope > foliageBrushPrototypes[prototypeIdx].slopeMax) continue;
1703 
1704  //
1705  Transform transform;
1706  transform.setTranslation(translation);
1707  auto xAxisRotation = foliageBrushPrototypes[prototypeIdx].rotationXMin + ((foliageBrushPrototypes[prototypeIdx].rotationXMax - foliageBrushPrototypes[prototypeIdx].rotationXMin) * Math::random());
1708  auto yAxisRotation = foliageBrushPrototypes[prototypeIdx].rotationYMin + ((foliageBrushPrototypes[prototypeIdx].rotationYMax - foliageBrushPrototypes[prototypeIdx].rotationYMin) * Math::random());
1709  auto zAxisRotation = foliageBrushPrototypes[prototypeIdx].rotationZMin + ((foliageBrushPrototypes[prototypeIdx].rotationZMax - foliageBrushPrototypes[prototypeIdx].rotationZMin) * Math::random());
1710  if (foliageBrushPrototypes[prototypeIdx].normalAlign == true) {
1711  xAxisRotation = Vector3::computeAngle(normal, Vector3(0.0f, 1.0f, 0.0f), Vector3(-1.0f, 0.0f, 0.0f));
1712  zAxisRotation = Vector3::computeAngle(normal, Vector3(0.0f, 1.0f, 0.0f), Vector3(0.0f, 0.0f, -1.0f));
1713  Transform _transform;
1714  _transform.addRotation(Rotation::Z_AXIS, zAxisRotation);
1715  _transform.addRotation(Rotation::X_AXIS, xAxisRotation);
1716  _transform.addRotation(Rotation::Y_AXIS, yAxisRotation);
1717  _transform.update();
1718  auto euler = _transform.getTransformMatrix().computeEulerAngles();
1719  zAxisRotation = euler.getZ();
1720  yAxisRotation = euler.getY();
1721  xAxisRotation = euler.getX();
1722  }
1723  transform.addRotation(Rotation::Z_AXIS, zAxisRotation);
1724  transform.addRotation(Rotation::Y_AXIS, yAxisRotation);
1725  transform.addRotation(Rotation::X_AXIS, xAxisRotation);
1726  transform.setScale(Vector3(prototypeScale, prototypeScale, prototypeScale));
1727 
1728  transform.setTranslation(translation.clone().setY(contact.getY()));
1729  transform.update();
1730 
1731  //
1732  foliageMaps[partitionIdx][prototypeId].push_back(transform);
1733  newFoliageMaps[partitionIdx][prototypeId].push_back(transform);
1734  }
1735  }
1736  break;
1737  }
1738 
1739  //
1740  brushPosition.add(
1741  Vector3(
1742  STEP_SIZE,
1743  0.0f,
1744  0.0f
1745  )
1746  );
1747  }
1748  }
1749 }
1750 
1752  BoundingBox& terrainBoundingBox, // TODO: constness
1753  const Vector3& brushCenterPosition,
1754  const FoliageBrush& foliageBrush,
1755  const vector<FoliageBrushPrototype>& foliageBrushPrototypes,
1756  BrushOperation brushOperation,
1757  vector<unordered_map<int, vector<Transform>>>& foliageMaps,
1758  unordered_set<int>& recreateFoliagePartitions
1759 ) {
1760  // check if we have a texture
1761  if (foliageBrush.brushTexture == nullptr) return;
1762 
1763  // apply brush
1764  auto partitionsX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / PARTITION_SIZE));
1765  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / STEP_SIZE));
1766  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getZ() / STEP_SIZE));
1767 
1768  // other operations
1769  auto textureData = foliageBrush.brushTexture->getRGBTextureData();
1770  auto textureWidth = foliageBrush.brushTexture->getTextureWidth();
1771  auto textureHeight = foliageBrush.brushTexture->getTextureHeight();
1772  auto textureBytePerPixel = foliageBrush.brushTexture->getRGBDepthBitsPerPixel() == 32?4:3;
1773 
1774  auto heightMin = Float::MAX_VALUE;
1775  auto heightMax = Float::MIN_VALUE;
1776  auto prototypeCount = 0;
1777  for (const auto& foliageBrushPrototype: foliageBrushPrototypes) {
1778  if (foliageBrushPrototype.prototypeId == -1) continue;
1779  heightMin = Math::min(heightMin, foliageBrushPrototype.heightMin);
1780  heightMax = Math::max(heightMax, foliageBrushPrototype.heightMax);
1781  prototypeCount++;
1782  }
1783  //
1784  for (auto z = 0.0f; z < textureHeight * foliageBrush.brushScale; z+= 1.0f) {
1785  auto brushPosition =
1786  brushCenterPosition.
1787  clone().
1788  sub(
1789  Vector3(
1790  (static_cast<float>(textureWidth) * foliageBrush.brushScale) / 2.0f,
1791  0.0f,
1792  ((static_cast<float>(textureHeight) * foliageBrush.brushScale) / 2.0f)
1793  )
1794  ).
1795  add(
1796  Vector3(
1797  0.0f,
1798  0.0f,
1799  z
1800  )
1801  );
1802  for (auto x = 0.0f; x < textureWidth * foliageBrush.brushScale; x+= 1.0f) {
1803  auto textureX = static_cast<int>(x / foliageBrush.brushScale);
1804  auto textureY = static_cast<int>(z / foliageBrush.brushScale);
1805  auto red = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 0);
1806  auto green = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 1);
1807  auto blue = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 2);
1808  auto alpha = textureBytePerPixel == 3?255:textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 3);
1809  auto appliedDensity = (static_cast<float>(red) + static_cast<float>(green) + static_cast<float>(blue)) / (255.0f * 3.0f);
1810 
1811  //
1812  auto terrainHeightVectorX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / STEP_SIZE);
1813  auto terrainHeightVectorZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / STEP_SIZE);
1814 
1815  //
1816  if (terrainHeightVectorX < 0 || terrainHeightVectorX >= terrainHeightVectorVerticesPerX ||
1817  terrainHeightVectorZ < 0 || terrainHeightVectorZ >= terreinHeightVectorVerticesPerZ) {
1818  //
1819  brushPosition.add(
1820  Vector3(
1821  STEP_SIZE,
1822  0.0f,
1823  0.0f
1824  )
1825  );
1826  continue;
1827  }
1828 
1829  //
1830  auto partitionX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
1831  auto partitionZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
1832  auto partitionIdx = partitionZ * partitionsX + partitionX;
1833 
1834  //
1835  switch(brushOperation) {
1836  case BRUSHOPERATION_DELETE:
1837  {
1838  Vector3 topVertex;
1839  Vector3 topLeftVertex;
1840  Vector3 leftVertex;
1841  Vector3 vertex;
1842 
1843  getTerrainVertex(terrainHeightVectorX, terrainHeightVectorZ - 1, topVertex);
1844  getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ - 1, topLeftVertex);
1845  getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ, leftVertex);
1846  getTerrainVertex(terrainHeightVectorX, terrainHeightVectorZ, vertex);
1847 
1848  for (auto& [prototypeId, transformVector]: foliageMaps[partitionIdx]) {
1849  for (auto i = 0; i < transformVector.size(); i++) {
1850  const auto& translation = transformVector[i].getTranslation();
1851  if (appliedDensity > 0.0f &&
1852  translation.getX() >= leftVertex.getX() - 0.01f &&
1853  translation.getX() <= vertex.getX() + 0.01f &&
1854  translation.getZ() >= topVertex.getZ() - 0.01f &&
1855  translation.getZ() <= vertex.getZ() + 0.01f &&
1856  (prototypeCount == 0 ||
1857  (translation.getY() >= heightMin &&
1858  translation.getY() <= heightMax))) {
1859  //
1860  transformVector.erase(transformVector.begin() + i);
1861  recreateFoliagePartitions.insert(partitionIdx);
1862  i--;
1863  }
1864  }
1865  }
1866  }
1867  break;
1868  }
1869 
1870  //
1871  brushPosition.add(
1872  Vector3(
1873  STEP_SIZE,
1874  0.0f,
1875  0.0f
1876  )
1877  );
1878  }
1879  }
1880 }
1881 
1883  BoundingBox& terrainBoundingBox, // TODO: constness
1884  vector<float>& terrainHeightVector,
1885  const Vector3& brushCenterPosition,
1886  const FoliageBrush& foliageBrush,
1887  vector<unordered_map<int, vector<Transform>>>& foliageMaps,
1888  unordered_set<int>& updateFoliagePartitions
1889 ) {
1890  // check if we have a texture
1891  if (foliageBrush.brushTexture == nullptr) return;
1892 
1893  // apply brush
1894  auto partitionsX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / PARTITION_SIZE));
1895  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / STEP_SIZE));
1896  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getZ() / STEP_SIZE));
1897 
1898  // other operations
1899  auto textureData = foliageBrush.brushTexture->getRGBTextureData();
1900  auto textureWidth = foliageBrush.brushTexture->getTextureWidth();
1901  auto textureHeight = foliageBrush.brushTexture->getTextureHeight();
1902  auto textureBytePerPixel = foliageBrush.brushTexture->getRGBDepthBitsPerPixel() == 32?4:3;
1903 
1904  //
1905  for (auto z = 0.0f; z < textureHeight * foliageBrush.brushScale; z+= 1.0f) {
1906  auto brushPosition =
1907  brushCenterPosition.
1908  clone().
1909  sub(
1910  Vector3(
1911  (static_cast<float>(textureWidth) * foliageBrush.brushScale) / 2.0f,
1912  0.0f,
1913  ((static_cast<float>(textureHeight) * foliageBrush.brushScale) / 2.0f)
1914  )
1915  ).
1916  add(
1917  Vector3(
1918  0.0f,
1919  0.0f,
1920  z
1921  )
1922  );
1923  for (auto x = 0.0f; x < textureWidth * foliageBrush.brushScale; x+= 1.0f) {
1924  auto brushMapCountMapX = static_cast<int>(x);
1925  auto brushMapCountMapZ = static_cast<int>(z);
1926  auto terrainHeightVectorX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / STEP_SIZE);
1927  auto terrainHeightVectorZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / STEP_SIZE);
1928  if (terrainHeightVectorX < 0 || terrainHeightVectorX >= terrainHeightVectorVerticesPerX ||
1929  terrainHeightVectorZ < 0 || terrainHeightVectorZ >= terreinHeightVectorVerticesPerZ) {
1930  //
1931  brushPosition.add(
1932  Vector3(
1933  STEP_SIZE,
1934  0.0f,
1935  0.0f
1936  )
1937  );
1938  continue;
1939  }
1940 
1941  //
1942  auto textureX = static_cast<int>(x / foliageBrush.brushScale);
1943  auto textureY = static_cast<int>(z / foliageBrush.brushScale);
1944  auto red = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 0);
1945  auto green = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 1);
1946  auto blue = textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 2);
1947  auto alpha = textureBytePerPixel == 3?255:textureData.get(textureY * textureWidth * textureBytePerPixel + textureX * textureBytePerPixel + 3);
1948  auto brushTextureDensity = (static_cast<float>(red) + static_cast<float>(green) + static_cast<float>(blue)) / (255.0f * 3.0f);
1949 
1950  //
1951  auto partitionX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
1952  auto partitionZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
1953  auto partitionIdx = partitionZ * partitionsX + partitionX;
1954 
1955  //
1956  updateFoliagePartitions.insert(partitionIdx);
1957 
1958  //
1959  Vector3 topVertex;
1960  Vector3 topLeftVertex;
1961  Vector3 leftVertex;
1962  Vector3 vertex;
1963 
1964  //
1965  getTerrainVertex(terrainHeightVectorX, terrainHeightVectorZ - 1, topVertex);
1966  getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ - 1, topLeftVertex);
1967  getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ, leftVertex);
1968  getTerrainVertex(terrainHeightVectorX, terrainHeightVectorZ, vertex);
1969 
1970  //
1971  for (auto& [prototypeId, transformVector]: foliageMaps[partitionIdx]) {
1972  if (prototypeId == -1) continue;
1973  for (auto& transform: transformVector) {
1974  const auto& translation = transform.getTranslation();
1975  if (brushTextureDensity > 0.0f &&
1976  translation.getX() >= leftVertex.getX() &&
1977  translation.getX() <= vertex.getX() &&
1978  translation.getZ() >= topVertex.getZ() &&
1979  translation.getZ() <= vertex.getZ()) {
1980  //
1981  auto haveContact = false;
1982  Vector3 contact;
1983  for (int _z = -1; _z < 2; _z++)
1984  for (int _x = -1; _x < 2; _x++) {
1985  Vector3 topVertex;
1986  Vector3 topLeftVertex;
1987  Vector3 leftVertex;
1988  Vector3 vertex;
1989 
1990  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX, _z + terrainHeightVectorZ - 1, topVertex);
1991  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX - 1, _z + terrainHeightVectorZ - 1, topLeftVertex);
1992  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX - 1, _z + terrainHeightVectorZ, leftVertex);
1993  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX, _z + terrainHeightVectorZ, vertex);
1994 
1995  if (LineSegment::doesLineSegmentCollideWithTriangle(topVertex, topLeftVertex, leftVertex, transform.getTranslation().clone().setY(-10000.0f), transform.getTranslation().clone().setY(+10000.0f), contact) == true) {
1996  haveContact = true;
1997  break;
1998  } else
1999  if (LineSegment::doesLineSegmentCollideWithTriangle(leftVertex, vertex, topVertex, transform.getTranslation().clone().setY(-10000.0f), transform.getTranslation().clone().setY(+10000.0f), contact) == true) {
2000  haveContact = true;
2001  break;
2002  }
2003  }
2004 
2005  //
2006  if (haveContact == false) {
2008  "Terrain::applyFoliageBrush(): no contact@" +
2009  to_string(transform.getTranslation().getX()) + ", " +
2010  to_string(transform.getTranslation().getZ())
2011  );
2012  contact = transform.getTranslation();
2013  }
2014 
2015  //
2016  transform.setTranslation(transform.getTranslation().clone().setY(contact.getY()));
2017  transform.update();
2018  }
2019  }
2020  }
2021 
2022  //
2023  brushPosition.add(
2024  Vector3(
2025  STEP_SIZE,
2026  0.0f,
2027  0.0f
2028  )
2029  );
2030  }
2031  }
2032 }
2033 
2035  BoundingBox& terrainBoundingBox, // TODO: constness
2036  vector<float>& terrainHeightVector,
2037  const Vector3& brushCenterPosition,
2038  Texture* brushTexture,
2039  float brushRotation,
2040  const Vector2& brushScale,
2041  vector<unordered_map<int, vector<Transform>>>& foliageMaps,
2042  unordered_set<int>& updateFoliagePartitions
2043 ) {
2044  // check if we have a texture
2045  if (brushTexture == nullptr) return;
2046 
2047  // apply brush
2048  vector<vector<Vector3>> partitionTerrainVertices;
2049  vector<vector<Vector3>> partitionTerrainNormals;
2050  auto partitionsX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / PARTITION_SIZE));
2051  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getX() / STEP_SIZE));
2052  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(terrainBoundingBox.getDimensions().getZ() / STEP_SIZE));
2053 
2054  // texture
2055  auto textureData = brushTexture->getRGBTextureData();
2056  auto textureWidth = brushTexture->getTextureWidth();
2057  auto textureHeight = brushTexture->getTextureHeight();
2058  auto textureBytePerPixel = brushTexture->getRGBDepthBitsPerPixel() == 32?4:3;
2059 
2060  // brush texture matrix
2061  Matrix3x3 brushTextureMatrix;
2062  brushTextureMatrix.identity();
2063  brushTextureMatrix.setTranslation(Vector2(static_cast<float>(textureWidth) / 2.0f, static_cast<float>(textureHeight) / 2.0f));
2064  brushTextureMatrix.multiply((Matrix3x3()).identity().scale(Vector2(1.0f / brushScale.getX(), 1.0f / brushScale.getY())));
2065  brushTextureMatrix.multiply((Matrix3x3()).identity().setAxes(brushRotation));
2066  auto brushScaleMax = Math::max(brushScale.getX(), brushScale.getY());
2067 
2068  //
2069  for (auto z = -textureHeight * brushScaleMax * 2.0f; z < textureHeight * brushScaleMax * 2.0f; z+= STEP_SIZE) {
2070  auto brushPosition =
2071  brushCenterPosition.
2072  clone().
2073  sub(
2074  Vector3(
2075  static_cast<float>(textureWidth) * brushScaleMax * 2.0f,
2076  0.0f,
2077  0.0f
2078  )
2079  ).
2080  add(
2081  Vector3(
2082  0.0f,
2083  0.0f,
2084  z
2085  )
2086  );
2087  for (auto x = -textureWidth * brushScaleMax * 2.0f; x < textureWidth * brushScaleMax * 2.0f; x+= STEP_SIZE) {
2088  auto texturePositionUntransformed = Vector2(x, z);
2089  auto texturePosition = brushTextureMatrix.multiply(texturePositionUntransformed);
2090  auto textureX = static_cast<int>(texturePosition.getX());
2091  auto textureY = static_cast<int>(texturePosition.getY());
2092  if (textureX < 0 || textureX >= textureWidth ||
2093  textureY < 0 || textureY >= textureHeight) {
2094  brushPosition.add(
2095  Vector3(
2096  STEP_SIZE,
2097  0.0f,
2098  0.0f
2099  )
2100  );
2101  continue;
2102  }
2103  auto terrainHeightVectorX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / STEP_SIZE);
2104  auto terrainHeightVectorZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / STEP_SIZE);
2105  if (terrainHeightVectorX < 0 || terrainHeightVectorX >= terrainHeightVectorVerticesPerX ||
2106  terrainHeightVectorZ < 0 || terrainHeightVectorZ >= terreinHeightVectorVerticesPerZ) {
2107  brushPosition.add(
2108  Vector3(
2109  STEP_SIZE,
2110  0.0f,
2111  0.0f
2112  )
2113  );
2114  continue;
2115  }
2116 
2117  //
2118  auto partitionX = static_cast<int>((brushPosition.getX() - terrainBoundingBox.getMin().getX()) / PARTITION_SIZE);
2119  auto partitionZ = static_cast<int>((brushPosition.getZ() - terrainBoundingBox.getMin().getZ()) / PARTITION_SIZE);
2120  auto partitionIdx = partitionZ * partitionsX + partitionX;
2121 
2122  //
2123  updateFoliagePartitions.insert(partitionIdx);
2124 
2125  //
2126  Vector3 topVertex;
2127  Vector3 topLeftVertex;
2128  Vector3 leftVertex;
2129  Vector3 vertex;
2130 
2131  //
2132  getTerrainVertex(terrainHeightVectorX, terrainHeightVectorZ - 1, topVertex);
2133  getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ - 1, topLeftVertex);
2134  getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ, leftVertex);
2135  getTerrainVertex(terrainHeightVectorX, terrainHeightVectorZ, vertex);
2136 
2137  //
2138  for (auto& [prototypeId, transformVector]: foliageMaps[partitionIdx]) {
2139  if (prototypeId == -1) continue;
2140  for (auto& transform: transformVector) {
2141  const auto& translation = transform.getTranslation();
2142  if (translation.getX() >= leftVertex.getX() &&
2143  translation.getX() <= vertex.getX() &&
2144  translation.getZ() >= topVertex.getZ() &&
2145  translation.getZ() <= vertex.getZ()) {
2146  //
2147  auto haveContact = false;
2148  Vector3 contact;
2149  for (int _z = -1; _z < 2; _z++)
2150  for (int _x = -1; _x < 2; _x++) {
2151  Vector3 topVertex;
2152  Vector3 topLeftVertex;
2153  Vector3 leftVertex;
2154  Vector3 vertex;
2155 
2156  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX, _z + terrainHeightVectorZ - 1, topVertex);
2157  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX - 1, _z + terrainHeightVectorZ - 1, topLeftVertex);
2158  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX - 1, _z + terrainHeightVectorZ, leftVertex);
2159  getTerrainVertex(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, _x + terrainHeightVectorX, _z + terrainHeightVectorZ, vertex);
2160 
2161  if (LineSegment::doesLineSegmentCollideWithTriangle(topVertex, topLeftVertex, leftVertex, transform.getTranslation().clone().setY(-10000.0f), transform.getTranslation().clone().setY(+10000.0f), contact) == true) {
2162  haveContact = true;
2163  break;
2164  } else
2165  if (LineSegment::doesLineSegmentCollideWithTriangle(leftVertex, vertex, topVertex, transform.getTranslation().clone().setY(-10000.0f), transform.getTranslation().clone().setY(+10000.0f), contact) == true) {
2166  haveContact = true;
2167  break;
2168  }
2169  }
2170 
2171  //
2172  if (haveContact == false) {
2174  "Terrain::applyFoliageBrush(): no contact@" +
2175  to_string(transform.getTranslation().getX()) + ", " +
2176  to_string(transform.getTranslation().getZ())
2177  );
2178  contact = transform.getTranslation();
2179  }
2180 
2181  //
2182  transform.setTranslation(transform.getTranslation().clone().setY(contact.getY()));
2183  transform.update();
2184  }
2185  }
2186  }
2187 
2188  //
2189  brushPosition.add(
2190  Vector3(
2191  STEP_SIZE,
2192  0.0f,
2193  0.0f
2194  )
2195  );
2196  }
2197  }
2198 }
2199 
2200 
2202  bool flipZ,
2203  float width,
2204  float depth,
2205  vector<float>& terrainHeightVector,
2206  unordered_map<int, float>& waterPositionMapsHeight,
2207  unordered_map<int, unordered_map<int, unordered_set<int>>>& waterPositionMaps,
2208  vector<unordered_map<int, vector<Transform>>>& foliageMaps
2209 ) {
2210  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(width / STEP_SIZE));
2211  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(depth / STEP_SIZE));
2212 
2213  // terrain
2214  vector<float> terrainHeightVectorMirrored;
2215  terrainHeightVectorMirrored.resize(terrainHeightVectorVerticesPerX * 2 * terreinHeightVectorVerticesPerZ);
2216  for (auto z = 0; z < terreinHeightVectorVerticesPerZ; z++) {
2217  for (auto x = 0; x < terrainHeightVectorVerticesPerX; x++) {
2218  auto _z = flipZ == true?terreinHeightVectorVerticesPerZ - z - 1:z;
2219  terrainHeightVectorMirrored[z * terrainHeightVectorVerticesPerX * 2 + x] = terrainHeightVector[z * terrainHeightVectorVerticesPerX + x];
2220  terrainHeightVectorMirrored[_z * terrainHeightVectorVerticesPerX * 2 + (terrainHeightVectorVerticesPerX * 2 - x - 1)] = terrainHeightVector[z * terrainHeightVectorVerticesPerX + x];
2221  }
2222  }
2223  terrainHeightVector = terrainHeightVectorMirrored;
2224 
2225  // water
2226  unordered_map<int, unordered_map<int, unordered_set<int>>> waterPositionMapsMirrored;
2227  unordered_map<int, float> waterPositionMapsHeightMirrored;
2228  auto idxMax = 0;
2229  for (const auto& [idx, waterPositionMap]: waterPositionMaps) {
2230  if (idx > idxMax) idxMax = idx;
2231  }
2232  idxMax++;
2233  for (const auto& [idx, waterPositionMap]: waterPositionMaps) {
2234  waterPositionMapsHeightMirrored[idx] = waterPositionMapsHeight[idx];
2235  waterPositionMapsHeightMirrored[idxMax + idx] = waterPositionMapsHeight[idx];
2236  for (const auto& [z, xValues]: waterPositionMap) {
2237  auto _z = flipZ == true?terreinHeightVectorVerticesPerZ - z - 1:z;
2238  for (const auto x: xValues) {
2239  waterPositionMapsMirrored[idx][z].insert(x);
2240  waterPositionMapsMirrored[idxMax + idx][_z].insert(terrainHeightVectorVerticesPerX * 2 - x - 1);
2241  }
2242  }
2243  }
2244  waterPositionMapsHeight = waterPositionMapsHeightMirrored;
2245  waterPositionMaps = waterPositionMapsMirrored;
2246 
2247  // foliage
2248  auto partitionsX = static_cast<int>(Math::ceil(width * 2.0f / PARTITION_SIZE));
2249  auto partitionsZ = static_cast<int>(Math::ceil(depth / PARTITION_SIZE));
2250  vector<unordered_map<int, vector<Transform>>> foliageMapsMirrored;
2251  createFoliageMaps(width * 2.0f, depth, foliageMapsMirrored);
2252  for (const auto& foliageMapPartition: foliageMaps) {
2253  for (const auto& [foliagePrototypeId, foliagePrototypeTransformVector]: foliageMapPartition) {
2254  for (const auto& transform: foliagePrototypeTransformVector) {
2255  {
2256  //
2257  auto partitionX = static_cast<int>((transform.getTranslation().getX()) / PARTITION_SIZE);
2258  auto partitionZ = static_cast<int>((transform.getTranslation().getZ()) / PARTITION_SIZE);
2259  auto partitionIdx = partitionZ * partitionsX + partitionX;
2260  foliageMapsMirrored[partitionIdx][foliagePrototypeId].push_back(transform);
2261  }
2262  {
2263  auto transformMirrored = transform;
2264  transformMirrored.setTranslation(
2265  Vector3(
2266  width * 2.0f - transformMirrored.getTranslation().getX(),
2267  transformMirrored.getTranslation().getY(),
2268  flipZ == true?depth - transformMirrored.getTranslation().getZ():transformMirrored.getTranslation().getZ()
2269  )
2270  );
2271  transformMirrored.addRotation(transformMirrored.getRotationAxis(0), -transformMirrored.getRotationAngle(0));
2272  transformMirrored.update();
2273  auto eulerAngles = transformMirrored.getTransformMatrix().computeEulerAngles();
2274  transformMirrored.removeRotation(3);
2275  transformMirrored.setRotationAngle(0, eulerAngles.getZ());
2276  transformMirrored.setRotationAngle(1, eulerAngles.getY());
2277  transformMirrored.setRotationAngle(2, eulerAngles.getX());
2278  transformMirrored.update();
2279  //
2280  auto partitionX = static_cast<int>((transformMirrored.getTranslation().getX()) / PARTITION_SIZE);
2281  auto partitionZ = static_cast<int>((transformMirrored.getTranslation().getZ()) / PARTITION_SIZE);
2282  if (partitionZ >= partitionsZ) partitionZ = partitionsZ - 1; // special case if translation = x, y, 0.0
2283  if (partitionX >= partitionsX) partitionX = partitionsX - 1; // special case if translation = 0.0, y, z
2284  auto partitionIdx = partitionZ * partitionsX + partitionX;
2285  foliageMapsMirrored[partitionIdx][foliagePrototypeId].push_back(transformMirrored);
2286  }
2287  }
2288  }
2289  }
2290  foliageMaps = foliageMapsMirrored;
2291 }
2292 
2294  bool flipX,
2295  float width,
2296  float depth,
2297  vector<float>& terrainHeightVector,
2298  unordered_map<int, float>& waterPositionMapsHeight,
2299  unordered_map<int, unordered_map<int, unordered_set<int>>>& waterPositionMaps,
2300  vector<unordered_map<int, vector<Transform>>>& foliageMaps
2301 ) {
2302  auto terrainHeightVectorVerticesPerX = static_cast<int>(Math::ceil(width / STEP_SIZE));
2303  auto terreinHeightVectorVerticesPerZ = static_cast<int>(Math::ceil(depth / STEP_SIZE));
2304 
2305  // terrain
2306  vector<float> terrainHeightVectorMirrored;
2307  terrainHeightVectorMirrored.resize(terrainHeightVectorVerticesPerX * terreinHeightVectorVerticesPerZ * 2);
2308  for (auto z = 0; z < terreinHeightVectorVerticesPerZ; z++) {
2309  for (auto x = 0; x < terrainHeightVectorVerticesPerX; x++) {
2310  auto _x = flipX == true?terrainHeightVectorVerticesPerX - x - 1:x;
2311  terrainHeightVectorMirrored[z * terrainHeightVectorVerticesPerX + x] = terrainHeightVector[z * terrainHeightVectorVerticesPerX + x];
2312  terrainHeightVectorMirrored[(terreinHeightVectorVerticesPerZ * 2 - z - 1) * terrainHeightVectorVerticesPerX + _x] = terrainHeightVector[z * terrainHeightVectorVerticesPerX + x];
2313  }
2314  }
2315  terrainHeightVector = terrainHeightVectorMirrored;
2316 
2317  // water
2318  unordered_map<int, unordered_map<int, unordered_set<int>>> waterPositionMapsMirrored;
2319  unordered_map<int, float> waterPositionMapsHeightMirrored;
2320  auto idxMax = 0;
2321  for (const auto& [idx, waterPositionMap]: waterPositionMaps) {
2322  if (idx > idxMax) idxMax = idx;
2323  }
2324  idxMax++;
2325  for (const auto& [idx, waterPositionMap]: waterPositionMaps) {
2326  waterPositionMapsHeightMirrored[idx] = waterPositionMapsHeight[idx];
2327  waterPositionMapsHeightMirrored[idxMax + idx] = waterPositionMapsHeight[idx];
2328  for (const auto& [z, xValues]: waterPositionMap) {
2329  for (const auto x: xValues) {
2330  auto _x = flipX == true?terrainHeightVectorVerticesPerX - x - 1:x;
2331  waterPositionMapsMirrored[idx][z].insert(x);
2332  waterPositionMapsMirrored[idxMax + idx][terreinHeightVectorVerticesPerZ * 2 - z - 1].insert(_x);
2333  }
2334  }
2335  }
2336  waterPositionMapsHeight = waterPositionMapsHeightMirrored;
2337  waterPositionMaps = waterPositionMapsMirrored;
2338 
2339  // foliage
2340  auto partitionsX = static_cast<int>(Math::ceil(width / PARTITION_SIZE));
2341  auto partitionsZ = static_cast<int>(Math::ceil(depth * 2.0f / PARTITION_SIZE));
2342  vector<unordered_map<int, vector<Transform>>> foliageMapsMirrored;
2343  createFoliageMaps(width, depth * 2.0f, foliageMapsMirrored);
2344  for (const auto& foliageMapPartition: foliageMaps) {
2345  for (const auto& [foliagePrototypeId, foliageMapPartitionTransformVector]: foliageMapPartition) {
2346  for (const auto& transform: foliageMapPartitionTransformVector) {
2347  {
2348  //
2349  auto partitionX = static_cast<int>((transform.getTranslation().getX()) / PARTITION_SIZE);
2350  auto partitionZ = static_cast<int>((transform.getTranslation().getZ()) / PARTITION_SIZE);
2351  auto partitionIdx = partitionZ * partitionsX + partitionX;
2352  foliageMapsMirrored[partitionIdx][foliagePrototypeId].push_back(transform);
2353  }
2354  {
2355  auto transformMirrored = transform;
2356  transformMirrored.setTranslation(
2357  Vector3(
2358  flipX == true?width - transformMirrored.getTranslation().getX():transformMirrored.getTranslation().getX(),
2359  transformMirrored.getTranslation().getY(),
2360  depth * 2.0f - transformMirrored.getTranslation().getZ()
2361  )
2362  );
2363  transformMirrored.addRotation(transformMirrored.getRotationAxis(2), -transformMirrored.getRotationAngle(2));
2364  transformMirrored.update();
2365  auto eulerAngles = transformMirrored.getTransformMatrix().computeEulerAngles();
2366  transformMirrored.removeRotation(3);
2367  transformMirrored.setRotationAngle(0, eulerAngles.getZ());
2368  transformMirrored.setRotationAngle(1, eulerAngles.getY());
2369  transformMirrored.setRotationAngle(2, eulerAngles.getX());
2370  transformMirrored.update();
2371  //
2372  auto partitionX = static_cast<int>((transformMirrored.getTranslation().getX()) / PARTITION_SIZE);
2373  auto partitionZ = static_cast<int>((transformMirrored.getTranslation().getZ()) / PARTITION_SIZE);
2374  if (partitionX >= partitionsX) partitionX = partitionsX - 1; // special case if translation = 0.0, y, z
2375  if (partitionZ >= partitionsZ) partitionZ = partitionsZ - 1; // special case if translation = x, y, 0.0
2376  auto partitionIdx = partitionZ * partitionsX + partitionX;
2377  foliageMapsMirrored[partitionIdx][foliagePrototypeId].push_back(transformMirrored);
2378  }
2379  }
2380  }
2381  }
2382  foliageMaps = foliageMapsMirrored;
2383 }
Color 4 definition class.
Definition: Color4.h:18
Rotation representation.
Definition: Rotation.h:18
Texture entity.
Definition: Texture.h:24
ByteBuffer getRGBTextureData()
Definition: Texture.h:253
uint8_t getRGBDepthBitsPerPixel() const
Definition: Texture.h:186
uint16_t getTextureHeight() const
Definition: Texture.h:211
uint16_t getTextureWidth() const
Definition: Texture.h:218
Transform which contain scale, rotations and translation.
Definition: Transform.h:29
void setTranslation(const Vector3 &translation)
Set translation.
Definition: Transform.h:64
void setScale(const Vector3 &scale)
Set scale.
Definition: Transform.h:79
virtual void update()
Computes transform matrix.
Definition: Transform.cpp:33
void addRotation(const Vector3 &axis, const float angle)
Add rotation.
Definition: Transform.h:113
const Matrix4x4 & getTransformMatrix() const
Definition: Transform.h:169
Represents a model face, consisting of vertex, normal, tangent and bitangent vectors,...
Definition: Face.h:18
Node faces entity A node can have multiple entities containing faces and a applied material.
Definition: FacesEntity.h:23
void setMaterial(Material *material)
Set up the entity's material.
Definition: FacesEntity.h:67
void setLOD1Distance(float lod1Distance)
Set LOD1 distance.
Definition: FacesEntity.h:116
void setLOD2Indices(const vector< int32_t > &lod2Indices)
Set LOD2 indices.
Definition: FacesEntity.cpp:76
void setLOD2Distance(float lod2Distance)
Set LOD2 distance.
Definition: FacesEntity.h:144
void setLOD3Distance(float lod3Distance)
Set LOD3 distance.
Definition: FacesEntity.h:172
void setLOD1Indices(const vector< int32_t > &lod1Indices)
Set LOD1 indices.
Definition: FacesEntity.cpp:68
void setLOD3Indices(const vector< int32_t > &lod3Indices)
Set LOD3 indices.
Definition: FacesEntity.cpp:84
void setFaces(const vector< Face > &faces)
Set up entity's faces.
Definition: FacesEntity.cpp:43
Represents a material.
Definition: Material.h:23
Representation of a 3D model.
Definition: Model.h:35
Model node.
Definition: Node.h:32
Represents rotation orders of a model.
Definition: RotationOrder.h:23
Represents specular material properties.
Model up vector.
Definition: UpVector.h:20
Axis aligned bounding box used for frustum, this is not directly connectable with physics engine.
Definition: BoundingBox.h:26
const Vector3 & getDimensions() const
Definition: BoundingBox.h:128
void extend(BoundingBox *boundingBox)
Extend bounding box with given bounding box.
Definition: BoundingBox.h:158
Line segment helper functions.
Definition: LineSegment.h:16
Standard math functions.
Definition: Math.h:19
Matrix3x3 class representing matrix3x3 mathematical structure and operations for 2d space.
Definition: Matrix3x3.h:20
Matrix3x3 & identity()
Creates identity matrix.
Definition: Matrix3x3.h:128
Matrix3x3 & setTranslation(const Vector2 &vector2)
Sets translation in matrix.
Definition: Matrix3x3.h:315
Matrix3x3 clone() const
Clones this matrix.
Definition: Matrix3x3.h:377
Matrix3x3 & multiply(const Matrix3x3 &matrix)
Multiplies this matrix with given matrix.
Definition: Matrix3x3.h:176
Vector3 computeEulerAngles() const
Compute Euler angles (rotation around x, y, z axes)
Definition: Matrix4x4.h:531
Vector2 class representing vector2 mathematical structure and operations with x, y components.
Definition: Vector2.h:20
float getY() const
Definition: Vector2.h:111
float getX() const
Definition: Vector2.h:94
Vector3 class representing vector3 mathematical structure and operations with x, y,...
Definition: Vector3.h:20
float getY() const
Definition: Vector3.h:117
float getX() const
Definition: Vector3.h:100
float getZ() const
Definition: Vector3.h:134
Vector3 & add(float scalar)
Adds a scalar.
Definition: Vector3.h:153
Vector3 & setY(float y)
Sets y component.
Definition: Vector3.h:126
Vector3 clone() const
Clones this vector3.
Definition: Vector3.h:374
Vector3 & set(float x, float y, float z)
Sets this vector3 by its components.
Definition: Vector3.h:70
Vector3 & normalize()
Normalizes this vector3.
Definition: Vector3.h:239
Byte buffer class.
Definition: ByteBuffer.h:27
Console class.
Definition: Console.h:29
static void println()
Print new line to console.
Definition: Console.cpp:92
Float class.
Definition: Float.h:27
static constexpr float MAX_VALUE
Definition: Float.h:29
static constexpr float MIN_VALUE
Definition: Float.h:30
Integer class.
Definition: Integer.h:25
static constexpr int MIN_VALUE
Definition: Integer.h:28
static constexpr int MAX_VALUE
Definition: Integer.h:27
Model tools functions class.
Definition: ModelTools.h:42
static void createDefaultAnimation(Model *model, int32_t frames)
Create default animation.
Definition: ModelTools.cpp:259
static void prepareForIndexedRendering(Model *model)
Prepare for indexed rendering.
Definition: ModelTools.cpp:86
static Vector3 computeNormal(const array< Vector3, 3 > &vertices)
Computes face normal for given face vertices.
Definition: ModelTools.h:92
Terrain utility.
Definition: Terrain.h:33
static void applyFoliageBrush(BoundingBox &terrainBoundingBox, vector< float > &terrainHeightVector, const Vector3 &brushCenterPosition, const FoliageBrush &foliageBrush, const vector< FoliageBrushPrototype > &foliageBrushPrototypes, BrushOperation brushOperation, vector< unordered_map< int, vector< Transform >>> &foliageMaps, vector< unordered_map< int, vector< Transform >>> &newFoliageMaps)
Apply foliage brush.
Definition: Terrain.cpp:1503
static void getWaterVertex(int x, int z, float waterHeight, Vector3 &vertex)
Get the terrain vertex for given x and z position.
Definition: Terrain.h:108
static void applyRampBrushToTerrainModels(BoundingBox &terrainBoundingBox, vector< Model * > &terrainModels, vector< float > &terrainHeightVector, const Vector3 &brushCenterPosition, Texture *brushTexture, float brushRotation, const Vector2 &brushScale, float flattenHeightMin, float flattenHeightMax)
Apply ramp brush to given terrain models.
Definition: Terrain.cpp:849
static void createFoliageMaps(BoundingBox &terrainBoundingBox, vector< unordered_map< int, vector< Transform >>> &foliageMaps)
Create foliage maps.
Definition: Terrain.cpp:1473
static void determineWaterXPositionSet(const vector< float > &terrainHeightVector, int verticesPerX, int verticesPerZ, int x, int z, float waterHeight, unordered_set< int > &waterXPositionSet)
Determine if water can be generated from left to right starting with x and z.
Definition: Terrain.h:171
static bool computeWaterPositionMap(BoundingBox &terrainBoundingBox, const vector< float > &terrainHeightVector, const Vector3 &brushCenterPosition, float waterHeight, unordered_map< int, unordered_set< int >> &waterPositionMap)
Compute water positions map using a auto fill like algorithm at given brush center position.
Definition: Terrain.cpp:1168
static constexpr float PARTITION_SIZE
Definition: Terrain.h:36
static void mirrorXAxis(bool flipZ, float width, float depth, vector< float > &terrainHeightVector, unordered_map< int, float > &waterPositionMapsHeight, unordered_map< int, unordered_map< int, unordered_set< int >>> &waterPositionMaps, vector< unordered_map< int, vector< Transform >>> &foliageMaps)
Mirror terrain around X axis.
Definition: Terrain.cpp:2201
static void updateFoliageTerrainBrush(BoundingBox &terrainBoundingBox, vector< float > &terrainHeightVector, const Vector3 &brushCenterPosition, const FoliageBrush &foliageBrush, vector< unordered_map< int, vector< Transform >>> &foliageMaps, unordered_set< int > &updateFoliagePartitions)
Update foliage after using terrain brush.
Definition: Terrain.cpp:1882
static void updateFoliageTerrainRampBrush(BoundingBox &terrainBoundingBox, vector< float > &terrainHeightVector, const Vector3 &brushCenterPosition, Texture *brushTexture, float brushRotation, const Vector2 &brushScale, vector< unordered_map< int, vector< Transform >>> &foliageMaps, unordered_set< int > &updateFoliagePartitions)
Update foliage after using terrain ramp brush.
Definition: Terrain.cpp:2034
static bool determineWater(const vector< float > &terrainHeightVector, int verticesPerX, int verticesPerZ, int x, int z, float waterHeight)
Determine if water can be generated.
Definition: Terrain.h:151
static void createTerrainModels(float width, float depth, float y, vector< float > &terrainHeightVector, BoundingBox &terrainBoundingBox, vector< Model * > &terrainModels, bool createLODLevels=false)
Create terrain models.
Definition: Terrain.cpp:173
static const Vector3 computeTerrainVertexNormal(const vector< float > &terrainHeightVector, int verticesPerX, int verticesPerZ, int x, int z)
Compute terrain vertex normal for given x and z position.
Definition: Terrain.cpp:412
static bool getTerrainModelsHeight(BoundingBox &terrainBoundingBox, vector< Model * > &terrainModels, vector< float > &terrainHeightVector, const Vector3 &brushCenterPosition, float &brushHeight)
Get terrain models height for e.g.
Definition: Terrain.cpp:1450
static void applyFoliageDeleteBrush(BoundingBox &terrainBoundingBox, const Vector3 &brushCenterPosition, const FoliageBrush &foliageBrush, const vector< FoliageBrushPrototype > &foliageBrushPrototypes, BrushOperation brushOperation, vector< unordered_map< int, vector< Transform >>> &foliageMaps, unordered_set< int > &recreateFoliagePartitions)
Apply foliage delete brush.
Definition: Terrain.cpp:1751
static constexpr float STEP_SIZE
Definition: Terrain.h:35
static void emptyFoliageMaps(vector< unordered_map< int, vector< Transform >>> &foliageMaps)
Empty foliage maps.
Definition: Terrain.cpp:1496
static Vector3 computeWaterReflectionEnvironmentMappingPosition(const unordered_map< int, unordered_set< int >> &waterPositionMap, float waterHeight)
Compute water reflection environment mapping position.
Definition: Terrain.cpp:1269
static bool hasWaterPosition(const unordered_map< int, unordered_set< int >> &waterPositionSet, int x, int z)
Definition: Terrain.h:122
static void mirrorZAxis(bool flipX, float width, float depth, vector< float > &terrainHeightVector, unordered_map< int, float > &waterPositionMapsHeight, unordered_map< int, unordered_map< int, unordered_set< int >>> &waterPositionMaps, vector< unordered_map< int, vector< Transform >>> &foliageMaps)
Mirror terrain around Z axis.
Definition: Terrain.cpp:2293
static void applyBrushToTerrainModels(BoundingBox &terrainBoundingBox, vector< Model * > &terrainModels, vector< float > &terrainHeightVector, const Vector3 &brushCenterPosition, Texture *brushTexture, float brushScale, float brushStrength, BrushOperation brushOperation, float flattenHeight=0.0f)
Apply brush to given terrain models.
Definition: Terrain.cpp:508
static void getTerrainVertex(int x, int z, Vector3 &vertex)
Get the terrain vertex for given x and z position without providing y component.
Definition: Terrain.h:69
static void createWaterModels(BoundingBox &terrainBoundingBox, const unordered_map< int, unordered_set< int >> &waterPositionMap, float waterHeight, int waterModelIdx, vector< Model * > &waterModels)
Create partitioned water models using given water position map.
Definition: Terrain.cpp:1291
std::exception Exception
Exception base class.
Definition: Exception.h:18