8 #include <unordered_map>
9 #include <unordered_set>
39 using std::make_unique;
44 using std::unordered_map;
45 using std::unordered_set;
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));
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);
87 for (
float z = 0.0f; z < depth; z+=
STEP_SIZE) {
88 for (
float x = 0.0f; x < width; x+=
STEP_SIZE) {
90 auto terrainHeightVectorX =
static_cast<int>(x /
STEP_SIZE);
91 auto terrainHeightVectorZ =
static_cast<int>(z /
STEP_SIZE);
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);
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);
107 navigationMapTextureByteBuffer.put(0);
108 navigationMapTextureByteBuffer.put(255);
109 navigationMapTextureByteBuffer.put(0);
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);
121 navigationMapTextureByteBuffer.put(0);
122 navigationMapTextureByteBuffer.put(255);
123 navigationMapTextureByteBuffer.put(0);
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);
135 navigationMapTextureByteBuffer.put(0);
136 navigationMapTextureByteBuffer.put(255);
137 navigationMapTextureByteBuffer.put(0);
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);
149 navigationMapTextureByteBuffer.put(0);
150 navigationMapTextureByteBuffer.put(255);
151 navigationMapTextureByteBuffer.put(0);
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
168 navigationMapTexture->acquireReference();
170 return navigationMapTexture;
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);
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) {
203 auto partitionIdx = partitionZ * partitionsX + partitionX;
205 auto& terrainVertices = partitionTerrainVertices[partitionIdx];
206 auto& terrainNormals = partitionTerrainNormals[partitionIdx];
207 auto& terrainFaces = partitionTerrainFaces[partitionIdx];
208 auto& terrainTriangles = partitionTerrainTriangles[partitionIdx];
210 int normalIdx = terrainNormals.size();
211 int vertexIdx = terrainVertices.size();
213 auto terrainHeightVectorX =
static_cast<int>(x /
STEP_SIZE);
214 auto terrainHeightVectorZ =
static_cast<int>(z /
STEP_SIZE);
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);
226 terrainVertices.push_back(topVertex);
227 terrainVertices.push_back(topLeftVertex);
228 terrainVertices.push_back(leftVertex);
229 terrainVertices.push_back(vertex);
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);
236 terrainNormals.push_back(topNormal);
237 terrainNormals.push_back(topLeftNormal);
238 terrainNormals.push_back(leftNormal);
239 terrainNormals.push_back(normal);
241 terrainFaces.push_back(
251 terrainFaces.push_back(
261 terrainTriangles[terrainHeightVectorZ]+= 2;
264 auto partitionIdx = 0;
265 for (
auto partitionIdx = 0; partitionIdx < partitionCount; partitionIdx++) {
266 if (partitionTerrainFaces[partitionIdx].empty() ==
true)
continue;
268 auto modelId =
"terrain." + to_string(partitionIdx);
269 auto terrainModel = make_unique<Model>(modelId, modelId, UpVector::Y_UP, RotationOrder::ZYX,
nullptr);
271 auto terrainMaterial = make_unique<Material>(
"terrain");
272 terrainMaterial->setSpecularMaterialProperties(make_unique<SpecularMaterialProperties>().release());
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));
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;
284 auto trianglesPerX = partitionTerrainTriangles[partitionIdx].begin()->second;
285 auto trianglesPerZ = partitionTerrainTriangles[partitionIdx].size();
286 for (
const auto& faceIndices: partitionTerrainFaces[partitionIdx]) {
287 nodeFaces.emplace_back(
299 if (createLODLevels ==
true) {
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;
310 auto finishedX =
false;
311 for (
auto x = 0; finishedX ==
false && x < trianglesPerX; x+= 8) {
312 if (x > trianglesPerX - 8) {
313 x = trianglesPerX - 8;
316 lod1Indices.push_back(facesIndices[z * trianglesPerX + x + 6][0]);
317 lod1Indices.push_back(facesIndices[z * trianglesPerX + x][1]);
318 lod1Indices.push_back(facesIndices[(z + 3) * trianglesPerX + x][2]);
319 lod1Indices.push_back(facesIndices[(z + 3) * trianglesPerX + x][2]);
320 lod1Indices.push_back(facesIndices[(z + 3) * trianglesPerX + x + 7][1]);
321 lod1Indices.push_back(facesIndices[z * trianglesPerX + x + 6][0]);
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;
338 auto finishedX =
false;
339 for (
auto x = 0; finishedX ==
false && x < trianglesPerX; x+= 16) {
340 if (x > trianglesPerX - 16) {
341 x = trianglesPerX - 16;
344 lod2Indices.push_back(facesIndices[z * trianglesPerX + x + 14][0]);
345 lod2Indices.push_back(facesIndices[z * trianglesPerX + x][1]);
346 lod2Indices.push_back(facesIndices[(z + 7) * trianglesPerX + x][2]);
347 lod2Indices.push_back(facesIndices[(z + 7) * trianglesPerX + x][2]);
348 lod2Indices.push_back(facesIndices[(z + 7) * trianglesPerX + x + 15][1]);
349 lod2Indices.push_back(facesIndices[z * trianglesPerX + x + 14][0]);
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;
366 auto finishedX =
false;
367 for (
auto x = 0; finishedX ==
false && x < trianglesPerX; x+= 32) {
368 if (x > trianglesPerX - 32) {
369 x = trianglesPerX - 32;
372 lod3Indices.push_back(facesIndices[z * trianglesPerX + x + 30][0]);
373 lod3Indices.push_back(facesIndices[z * trianglesPerX + x][1]);
374 lod3Indices.push_back(facesIndices[(z + 15) * trianglesPerX + x][2]);
375 lod3Indices.push_back(facesIndices[(z + 15) * trianglesPerX + x][2]);
376 lod3Indices.push_back(facesIndices[(z + 15) * trianglesPerX + x + 31][1]);
377 lod3Indices.push_back(facesIndices[z * trianglesPerX + x + 30][0]);
385 nodeFacesEntityTerrain.
setFaces(nodeFaces);
386 nodeFacesEntities.push_back(nodeFacesEntityTerrain);
388 terrainNode->setVertices(partitionTerrainVertices[partitionIdx]);
389 terrainNode->setNormals(partitionTerrainNormals[partitionIdx]);
390 terrainNode->setFacesEntities(nodeFacesEntities);
392 terrainModel->getNodes()[terrainNode->getId()] = terrainNode.get();
393 terrainModel->getSubNodes()[terrainNode->getId()] = terrainNode.get();
394 terrainNode.release();
396 terrainModel->getMaterials()[terrainMaterial->getId()] = terrainMaterial.get();
397 terrainMaterial.release();
399 terrainModel->invalidateBoundingBox();
400 if (partitionIdx == 0) {
401 terrainBoundingBox = *terrainModel->getBoundingBox();
403 terrainBoundingBox.
extend(terrainModel->getBoundingBox());
408 terrainModels.push_back(terrainModel.release());
416 auto haveVertex =
getTerrainVertex(terrainHeightVector, verticesPerX, verticesPerZ, x, z - 1, vertex);
417 if (haveVertex ==
false)
return Vector3(0.0f, 1.0f, 0.0f);
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);
435 if (haveTopVertex ==
true && haveTopLeftVertex ==
true) {
443 vertexNormal.
add(triangleNormal);
446 if (haveTopLeftVertex ==
true && haveLeftVertex ==
true) {
454 vertexNormal.
add(triangleNormal);
457 if (haveLeftVertex ==
true && haveBottomVertex ==
true) {
465 vertexNormal.
add(triangleNormal);
468 if (haveBottomVertex ==
true && haveBottomRightVertex ==
true) {
476 vertexNormal.
add(triangleNormal);
479 if (haveBottomRightVertex ==
true && haveRightVertex ==
true) {
487 vertexNormal.
add(triangleNormal);
490 if (haveRightVertex ==
true && haveTopVertex ==
true) {
498 vertexNormal.
add(triangleNormal);
501 if (normalCount > 0) {
504 Console::println(
"Terrain::computeTerrainVertexNormal(): no vertex normal available: normal count == 0");
505 return vertexNormal.
set(0.0f, 1.0f, 0.0f);
510 vector<Model*>& terrainModels,
511 vector<float>& terrainHeightVector,
512 const Vector3& brushCenterPosition,
520 if (brushTexture ==
nullptr)
return;
522 if (terrainModels.empty() ==
true)
return;
525 vector<vector<Vector3>> partitionTerrainVertices;
526 vector<vector<Vector3>> partitionTerrainNormals;
527 partitionTerrainVertices.resize(terrainModels.size());
528 partitionTerrainNormals.resize(terrainModels.size());
541 for (
auto z = 0.0f; z < textureHeight * brushScale; z+=
STEP_SIZE) {
547 (
static_cast<float>(textureWidth) * brushScale) / 2.0f,
549 (
static_cast<float>(textureHeight) * brushScale) / 2.0f
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) {
580 auto vertexIdx = terrainHeightVectorZ * terrainHeightVectorVerticesPerX + terrainHeightVectorX;
581 auto terrainVertexHeight = terrainHeightVector[vertexIdx];
582 switch(brushOperation) {
584 terrainVertexHeight+= appliedStrength;
587 terrainVertexHeight-= appliedStrength;
590 terrainVertexHeight = terrainVertexHeight * (1.0f - Math::clamp(appliedStrength, 0.0f, 1.0f)) + brushHeight * Math::clamp(appliedStrength, 0.0f, 1.0f);
593 terrainVertexHeight = terrainVertexHeight * (1.0f - Math::clamp(appliedStrength, 0.0f, 1.0f)) + 0.0f * Math::clamp(appliedStrength, 0.0f, 1.0f);
596 auto terrainVertexHeightNeighbours = 0.0f;
597 auto terrainVertexHeightNeighbourCount = 0;
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];
610 if (haveLeftVertex ==
true) {
611 terrainVertexHeightNeighbourCount++;
612 terrainVertexHeightNeighbours+= leftVertex[1];
614 if (haveBottomVertex ==
true) {
615 terrainVertexHeightNeighbourCount++;
616 terrainVertexHeightNeighbours+= bottomVertex[1];
618 if (haveRightVertex ==
true) {
619 terrainVertexHeightNeighbourCount++;
620 terrainVertexHeightNeighbours+= rightVertex[1];
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);
628 terrainHeightVector[vertexIdx] = terrainVertexHeight;
632 auto _brushPosition = brushPosition;
635 auto partitionIdx = partitionZ * partitionsX + partitionX;
636 auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:
nullptr;
637 auto terrainNode = terrainModel !=
nullptr?terrainModel->getNodeById(
"terrain"):
nullptr;
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;
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();
652 auto& terrainVertices = partitionTerrainVertices[partitionIdx];
653 auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
654 terrainVertices[vertexIdx + 3][1] = terrainVertexHeight;
660 auto _brushPosition = brushPosition.clone().sub(
Vector3(0.0f, 0.0f, -
STEP_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;
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;
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();
680 auto& terrainVertices = partitionTerrainVertices[partitionIdx];
681 auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
682 terrainVertices[vertexIdx + 0][1] = terrainVertexHeight;
691 auto partitionIdx = partitionZ * partitionsX + partitionX;
692 auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:
nullptr;
693 auto terrainNode = terrainModel !=
nullptr?terrainModel->getNodeById(
"terrain"):
nullptr;
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;
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();
708 auto& terrainVertices = partitionTerrainVertices[partitionIdx];
709 auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
710 terrainVertices[vertexIdx + 1][1] = terrainVertexHeight;
716 auto _brushPosition = brushPosition.clone().sub(
Vector3(-
STEP_SIZE, 0.0f, 0.0f));
719 auto partitionIdx = partitionZ * partitionsX + partitionX;
720 auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:
nullptr;
721 auto terrainNode = terrainModel !=
nullptr?terrainModel->getNodeById(
"terrain"):
nullptr;
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;
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();
736 auto& terrainVertices = partitionTerrainVertices[partitionIdx];
737 auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
738 terrainVertices[vertexIdx + 2][1] = terrainVertexHeight;
760 (
static_cast<float>(textureWidth) * brushScale) / 2.0f,
762 (
static_cast<float>(textureHeight) * brushScale) / 2.0f
775 auto partitionIdx = partitionZ * partitionsX + partitionX;
776 auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:
nullptr;
777 auto terrainNode = terrainModel !=
nullptr?terrainModel->getNodeById(
"terrain"):
nullptr;
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;
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();
794 auto& terrainNormals = partitionTerrainNormals[partitionIdx];
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);
802 terrainNormals[normalIdx + 0] = topNormal;
803 terrainNormals[normalIdx + 1] = topLeftNormal;
804 terrainNormals[normalIdx + 2] = leftNormal;
805 terrainNormals[normalIdx + 3] = normal;
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();
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();
851 vector<Model*>& terrainModels,
852 vector<float>& terrainHeightVector,
853 const Vector3& brushCenterPosition,
861 if (brushTexture ==
nullptr)
return;
863 if (terrainModels.empty() ==
true)
return;
866 vector<vector<Vector3>> partitionTerrainVertices;
867 vector<vector<Vector3>> partitionTerrainNormals;
868 partitionTerrainVertices.resize(terrainModels.size());
869 partitionTerrainNormals.resize(terrainModels.size());
883 brushTextureMatrix.
setTranslation(
Vector2(
static_cast<float>(textureWidth) / 2.0f,
static_cast<float>(textureHeight) / 2.0f));
886 auto brushScaleMax = Math::max(brushScale.
getX(), brushScale.
getY());
889 for (
auto z = -textureHeight * brushScaleMax * 2.0f; z < textureHeight * brushScaleMax * 2.0f; z+=
STEP_SIZE) {
895 static_cast<float>(textureWidth) * brushScaleMax * 2.0f,
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) {
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) {
941 auto terrainVertexHeight = 0.0f;
943 auto vertexIdx = terrainHeightVectorZ * terrainHeightVectorVerticesPerX + terrainHeightVectorX;
944 terrainVertexHeight = terrainHeightVector[vertexIdx];
945 terrainVertexHeight = height > terrainVertexHeight?height:terrainVertexHeight;
946 terrainHeightVector[vertexIdx] = terrainVertexHeight;
951 auto _brushPosition = brushPosition;
954 auto partitionIdx = partitionZ * partitionsX + partitionX;
955 auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:
nullptr;
956 auto terrainNode = terrainModel !=
nullptr?terrainModel->getNodeById(
"terrain"):
nullptr;
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;
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();
971 auto& terrainVertices = partitionTerrainVertices[partitionIdx];
972 auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
973 terrainVertices[vertexIdx + 3][1] = terrainVertexHeight;
982 auto partitionIdx = partitionZ * partitionsX + partitionX;
983 auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:
nullptr;
984 auto terrainNode = terrainModel !=
nullptr?terrainModel->getNodeById(
"terrain"):
nullptr;
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;
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();
999 auto& terrainVertices = partitionTerrainVertices[partitionIdx];
1000 auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
1001 terrainVertices[vertexIdx + 0][1] = terrainVertexHeight;
1010 auto partitionIdx = partitionZ * partitionsX + partitionX;
1011 auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:
nullptr;
1012 auto terrainNode = terrainModel !=
nullptr?terrainModel->getNodeById(
"terrain"):
nullptr;
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;
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();
1027 auto& terrainVertices = partitionTerrainVertices[partitionIdx];
1028 auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
1029 terrainVertices[vertexIdx + 1][1] = terrainVertexHeight;
1038 auto partitionIdx = partitionZ * partitionsX + partitionX;
1039 auto terrainModel = partitionIdx < terrainModels.size()?terrainModels[partitionIdx]:
nullptr;
1040 auto terrainNode = terrainModel !=
nullptr?terrainModel->getNodeById(
"terrain"):
nullptr;
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;
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();
1055 auto& terrainVertices = partitionTerrainVertices[partitionIdx];
1056 auto vertexIdx = (terrainModelZ * terrainModelVerticesPerX * 4) + (terrainModelX * 4);
1057 terrainVertices[vertexIdx + 2][1] = terrainVertexHeight;
1073 for (
auto z = -textureHeight * brushScaleMax * 2.0f; z < textureHeight * brushScaleMax * 2.0f; z+=
STEP_SIZE) {
1074 auto brushPosition =
1075 brushCenterPosition.
1079 static_cast<float>(textureWidth) * brushScaleMax * 2.0f,
1091 for (
auto x = -textureWidth * brushScaleMax * 2.0f; x < textureWidth * brushScaleMax * 2.0f; x+=
STEP_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;
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;
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();
1113 auto& terrainNormals = partitionTerrainNormals[partitionIdx];
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);
1121 terrainNormals[normalIdx + 0] = topNormal;
1122 terrainNormals[normalIdx + 1] = topLeftNormal;
1123 terrainNormals[normalIdx + 2] = leftNormal;
1124 terrainNormals[normalIdx + 3] = normal;
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();
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();
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);
1177 waterPositionMap[terrainHeightVectorZCenter].insert(terrainHeightVectorXCenter);
1180 Console::println(
"Terrain::determineWaterPositionSet: " + to_string(terrainHeightVectorXCenter) +
" / " + to_string(terrainHeightVectorZCenter) +
" @ " + to_string(waterHeight));
1183 determineWaterXPositionSet(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, terrainHeightVectorXCenter, terrainHeightVectorZCenter, waterHeight, waterPositionMap[terrainHeightVectorZCenter]);
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]);
1197 if (waterPositionMap[terrainHeightVectorZ].empty() ==
true)
break;
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]);
1215 if (waterPositionMap[terrainHeightVectorZ].empty() ==
true)
break;
1222 auto waterPositionMapCopy = waterPositionMap;
1223 auto zMin = waterPositionMap.begin()->first;
1224 auto zMax = waterPositionMap.begin()->first;
1226 waterPositionMapCopy = waterPositionMap;
1227 for (
const auto& [z, xValues]: waterPositionMap) {
1228 for (
auto x: xValues) {
1230 determineWater(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, x, z - 1, waterHeight) ==
true) {
1233 terrainHeightVector,
1244 determineWater(terrainHeightVector, terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, x, z + 1, waterHeight) ==
true) {
1247 terrainHeightVector,
1259 }
while (waterPositionMapCopy.size() != waterPositionMap.size());
1260 waterPositionMap[zMax] = waterPositionMap[zMax - 1];
1264 auto haveWaterPositionSet = waterPositionMap.empty() ==
false;
1265 Console::println(
"Terrain::determineWaterPositionSet: Have water position set: " + to_string(haveWaterPositionSet));
1266 return haveWaterPositionSet;
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;
1285 (
static_cast<float>(xMin + xMax) / 2.0f) *
STEP_SIZE,
1287 (
static_cast<float>(zMin + zMax) / 2.0f) *
STEP_SIZE
1293 const unordered_map<
int, unordered_set<int>>& waterPositionMap,
1296 vector<Model*>& waterModels
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);
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);
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;
1333 auto partitionIdx = partitionZ * partitionsX + partitionX;
1335 auto& terrainVertices = partitionTerrainVertices[partitionIdx];
1336 auto& terrainNormals = partitionTerrainNormals[partitionIdx];
1337 auto& terrainFaces = partitionWaterFaces[partitionIdx];
1339 int normalIdx = terrainNormals.size();
1340 int vertexIdx = terrainVertices.size();
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);
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);
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);
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);
1362 if (hasTopLeft ==
false ||
1365 hasOrigin ==
false) {
1366 terrainFaces.push_back(
1377 terrainFaces.push_back(
1387 terrainFaces.push_back(
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);
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);
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(
1429 nodeFacesEntityWater.
setFaces(nodeFaces);
1430 nodeFacesEntities.push_back(nodeFacesEntityWater);
1432 waterNode->setVertices(partitionTerrainVertices[partitionIdx]);
1433 waterNode->setNormals(partitionTerrainNormals[partitionIdx]);
1434 waterNode->setFacesEntities(nodeFacesEntities);
1436 waterModel->getNodes()[waterNode->getId()] = waterNode.get();
1437 waterModel->getSubNodes()[waterNode->getId()] = waterNode.get();
1438 waterNode.release();
1440 waterModel->getMaterials()[waterMaterial->getId()] = waterMaterial.get();
1441 waterMaterial.release();
1446 waterModels.push_back(waterModel.release());
1452 vector<Model*>& terrainModels,
1453 vector<float>& terrainHeightVector,
1454 const Vector3& brushCenterPosition,
1458 if (terrainModels.empty() ==
true)
return false;
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];
1475 vector<unordered_map<
int, vector<Transform>>>& foliageMaps
1486 vector<unordered_map<
int, vector<Transform>>>& foliageMaps
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();
1497 vector<unordered_map<
int, vector<Transform>>>& foliageMaps
1500 for (
auto& foliageMap: foliageMaps) foliageMap.clear();
1505 vector<float>& terrainHeightVector,
1506 const Vector3& brushCenterPosition,
1508 const vector<FoliageBrushPrototype>& foliageBrushPrototypes,
1510 vector<unordered_map<
int, vector<Transform>>>& foliageMaps,
1511 vector<unordered_map<
int, vector<Transform>>>& newFoliageMaps
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;
1546 brushMapCountMapTemplate.push_back(brushMapCountMapEntity);
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;
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);
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;
1588 for (
auto z = 0.0f; z < textureHeight * foliageBrush.
brushScale; z+= 1.0f) {
1589 auto brushPosition =
1590 brushCenterPosition.
1594 (
static_cast<float>(textureWidth) * foliageBrush.
brushScale) / 2.0f,
1596 ((
static_cast<float>(textureHeight) * foliageBrush.
brushScale) / 2.0f)
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) {
1627 switch(brushOperation) {
1629 for (
const auto& [prototypeId, prototypeCount]: brushMapCountMapEntity) {
1630 if (prototypeId == -1)
continue;
1633 auto prototypeIdx = -1;
1634 for (
auto i = 0; i < foliageBrushPrototypes.size(); i++) {
1635 if (foliageBrushPrototypes[i].prototypeId == prototypeId) prototypeIdx = i;
1637 if (prototypeIdx == -1)
continue;
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());
1645 Math::floor(brushPosition.getX()) + Math::random(),
1647 Math::floor(brushPosition.getZ()) + Math::random()
1653 auto partitionIdx = partitionZ * partitionsX + partitionX;
1656 auto haveContact =
false;
1660 for (
int _z = -1; _z < 2; _z++)
1661 for (
int _x = -1; _x < 2; _x++) {
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);
1672 if (LineSegment::doesLineSegmentCollideWithTriangle(topVertex, topLeftVertex, leftVertex, translation.
clone().
setY(-10000.0f), translation.
clone().
setY(+10000.0f), contact) ==
true) {
1675 height = (topVertex.
getY() + topLeftVertex.
getY() + leftVertex.
getY()) / 3.0f;
1678 if (LineSegment::doesLineSegmentCollideWithTriangle(leftVertex, vertex, topVertex, translation.
clone().
setY(-10000.0f), translation.
clone().
setY(+10000.0f), contact) ==
true) {
1681 height = (leftVertex.
getY() + vertex.
getY() + topVertex.
getY()) / 3.0f;
1687 if (height < foliageBrushPrototypes[prototypeIdx].heightMin || height > foliageBrushPrototypes[prototypeIdx].heightMax)
continue;
1690 if (haveContact ==
false) {
1692 "Terrain::applyFoliageBrush(): no contact@" +
1693 to_string(translation.
getX()) +
", " +
1694 to_string(translation.
getZ())
1696 contact = translation;
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;
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));
1714 _transform.
addRotation(Rotation::Z_AXIS, zAxisRotation);
1715 _transform.
addRotation(Rotation::X_AXIS, xAxisRotation);
1716 _transform.
addRotation(Rotation::Y_AXIS, yAxisRotation);
1719 zAxisRotation = euler.
getZ();
1720 yAxisRotation = euler.getY();
1721 xAxisRotation = euler.getX();
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));
1732 foliageMaps[partitionIdx][prototypeId].push_back(transform);
1733 newFoliageMaps[partitionIdx][prototypeId].push_back(transform);
1753 const Vector3& brushCenterPosition,
1755 const vector<FoliageBrushPrototype>& foliageBrushPrototypes,
1757 vector<unordered_map<
int, vector<Transform>>>& foliageMaps,
1758 unordered_set<int>& recreateFoliagePartitions
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);
1784 for (
auto z = 0.0f; z < textureHeight * foliageBrush.
brushScale; z+= 1.0f) {
1785 auto brushPosition =
1786 brushCenterPosition.
1790 (
static_cast<float>(textureWidth) * foliageBrush.
brushScale) / 2.0f,
1792 ((
static_cast<float>(textureHeight) * foliageBrush.
brushScale) / 2.0f)
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);
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);
1816 if (terrainHeightVectorX < 0 || terrainHeightVectorX >= terrainHeightVectorVerticesPerX ||
1817 terrainHeightVectorZ < 0 || terrainHeightVectorZ >= terreinHeightVectorVerticesPerZ) {
1832 auto partitionIdx = partitionZ * partitionsX + partitionX;
1835 switch(brushOperation) {
1843 getTerrainVertex(terrainHeightVectorX, terrainHeightVectorZ - 1, topVertex);
1844 getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ - 1, topLeftVertex);
1845 getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ, leftVertex);
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))) {
1860 transformVector.erase(transformVector.begin() + i);
1861 recreateFoliagePartitions.insert(partitionIdx);
1884 vector<float>& terrainHeightVector,
1885 const Vector3& brushCenterPosition,
1887 vector<unordered_map<
int, vector<Transform>>>& foliageMaps,
1888 unordered_set<int>& updateFoliagePartitions
1905 for (
auto z = 0.0f; z < textureHeight * foliageBrush.
brushScale; z+= 1.0f) {
1906 auto brushPosition =
1907 brushCenterPosition.
1911 (
static_cast<float>(textureWidth) * foliageBrush.
brushScale) / 2.0f,
1913 ((
static_cast<float>(textureHeight) * foliageBrush.
brushScale) / 2.0f)
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) {
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);
1953 auto partitionIdx = partitionZ * partitionsX + partitionX;
1956 updateFoliagePartitions.insert(partitionIdx);
1965 getTerrainVertex(terrainHeightVectorX, terrainHeightVectorZ - 1, topVertex);
1966 getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ - 1, topLeftVertex);
1967 getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ, leftVertex);
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()) {
1981 auto haveContact =
false;
1983 for (
int _z = -1; _z < 2; _z++)
1984 for (
int _x = -1; _x < 2; _x++) {
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);
1995 if (LineSegment::doesLineSegmentCollideWithTriangle(topVertex, topLeftVertex, leftVertex, transform.getTranslation().
clone().
setY(-10000.0f), transform.getTranslation().
clone().
setY(+10000.0f), contact) ==
true) {
1999 if (LineSegment::doesLineSegmentCollideWithTriangle(leftVertex, vertex, topVertex, transform.getTranslation().
clone().
setY(-10000.0f), transform.getTranslation().
clone().
setY(+10000.0f), contact) ==
true) {
2006 if (haveContact ==
false) {
2008 "Terrain::applyFoliageBrush(): no contact@" +
2009 to_string(transform.getTranslation().getX()) +
", " +
2010 to_string(transform.getTranslation().getZ())
2012 contact = transform.getTranslation();
2016 transform.setTranslation(transform.getTranslation().clone().setY(contact.
getY()));
2036 vector<float>& terrainHeightVector,
2037 const Vector3& brushCenterPosition,
2039 float brushRotation,
2041 vector<unordered_map<
int, vector<Transform>>>& foliageMaps,
2042 unordered_set<int>& updateFoliagePartitions
2045 if (brushTexture ==
nullptr)
return;
2048 vector<vector<Vector3>> partitionTerrainVertices;
2049 vector<vector<Vector3>> partitionTerrainNormals;
2063 brushTextureMatrix.
setTranslation(
Vector2(
static_cast<float>(textureWidth) / 2.0f,
static_cast<float>(textureHeight) / 2.0f));
2066 auto brushScaleMax = Math::max(brushScale.
getX(), brushScale.
getY());
2069 for (
auto z = -textureHeight * brushScaleMax * 2.0f; z < textureHeight * brushScaleMax * 2.0f; z+=
STEP_SIZE) {
2070 auto brushPosition =
2071 brushCenterPosition.
2075 static_cast<float>(textureWidth) * brushScaleMax * 2.0f,
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) {
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) {
2120 auto partitionIdx = partitionZ * partitionsX + partitionX;
2123 updateFoliagePartitions.insert(partitionIdx);
2132 getTerrainVertex(terrainHeightVectorX, terrainHeightVectorZ - 1, topVertex);
2133 getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ - 1, topLeftVertex);
2134 getTerrainVertex(terrainHeightVectorX - 1, terrainHeightVectorZ, leftVertex);
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()) {
2147 auto haveContact =
false;
2149 for (
int _z = -1; _z < 2; _z++)
2150 for (
int _x = -1; _x < 2; _x++) {
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);
2161 if (LineSegment::doesLineSegmentCollideWithTriangle(topVertex, topLeftVertex, leftVertex, transform.getTranslation().
clone().
setY(-10000.0f), transform.getTranslation().
clone().
setY(+10000.0f), contact) ==
true) {
2165 if (LineSegment::doesLineSegmentCollideWithTriangle(leftVertex, vertex, topVertex, transform.getTranslation().
clone().
setY(-10000.0f), transform.getTranslation().
clone().
setY(+10000.0f), contact) ==
true) {
2172 if (haveContact ==
false) {
2174 "Terrain::applyFoliageBrush(): no contact@" +
2175 to_string(transform.getTranslation().getX()) +
", " +
2176 to_string(transform.getTranslation().getZ())
2178 contact = transform.getTranslation();
2182 transform.setTranslation(transform.getTranslation().clone().setY(contact.
getY()));
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
2210 auto terrainHeightVectorVerticesPerX =
static_cast<int>(Math::ceil(width /
STEP_SIZE));
2211 auto terreinHeightVectorVerticesPerZ =
static_cast<int>(Math::ceil(depth /
STEP_SIZE));
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];
2223 terrainHeightVector = terrainHeightVectorMirrored;
2226 unordered_map<int, unordered_map<int, unordered_set<int>>> waterPositionMapsMirrored;
2227 unordered_map<int, float> waterPositionMapsHeightMirrored;
2229 for (
const auto& [idx, waterPositionMap]: waterPositionMaps) {
2230 if (idx > idxMax) idxMax = idx;
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);
2244 waterPositionMapsHeight = waterPositionMapsHeightMirrored;
2245 waterPositionMaps = waterPositionMapsMirrored;
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;
2252 for (
const auto& foliageMapPartition: foliageMaps) {
2253 for (
const auto& [foliagePrototypeId, foliagePrototypeTransformVector]: foliageMapPartition) {
2254 for (
const auto& transform: foliagePrototypeTransformVector) {
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);
2263 auto transformMirrored = transform;
2264 transformMirrored.setTranslation(
2266 width * 2.0f - transformMirrored.getTranslation().getX(),
2267 transformMirrored.getTranslation().getY(),
2268 flipZ ==
true?depth - transformMirrored.getTranslation().getZ():transformMirrored.getTranslation().getZ()
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();
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;
2283 if (partitionX >= partitionsX) partitionX = partitionsX - 1;
2284 auto partitionIdx = partitionZ * partitionsX + partitionX;
2285 foliageMapsMirrored[partitionIdx][foliagePrototypeId].push_back(transformMirrored);
2290 foliageMaps = foliageMapsMirrored;
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
2302 auto terrainHeightVectorVerticesPerX =
static_cast<int>(Math::ceil(width /
STEP_SIZE));
2303 auto terreinHeightVectorVerticesPerZ =
static_cast<int>(Math::ceil(depth /
STEP_SIZE));
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];
2315 terrainHeightVector = terrainHeightVectorMirrored;
2318 unordered_map<int, unordered_map<int, unordered_set<int>>> waterPositionMapsMirrored;
2319 unordered_map<int, float> waterPositionMapsHeightMirrored;
2321 for (
const auto& [idx, waterPositionMap]: waterPositionMaps) {
2322 if (idx > idxMax) idxMax = idx;
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);
2336 waterPositionMapsHeight = waterPositionMapsHeightMirrored;
2337 waterPositionMaps = waterPositionMapsMirrored;
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;
2344 for (
const auto& foliageMapPartition: foliageMaps) {
2345 for (
const auto& [foliagePrototypeId, foliageMapPartitionTransformVector]: foliageMapPartition) {
2346 for (
const auto& transform: foliageMapPartitionTransformVector) {
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);
2355 auto transformMirrored = transform;
2356 transformMirrored.setTranslation(
2358 flipX ==
true?width - transformMirrored.getTranslation().getX():transformMirrored.getTranslation().getX(),
2359 transformMirrored.getTranslation().getY(),
2360 depth * 2.0f - transformMirrored.getTranslation().getZ()
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();
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;
2375 if (partitionZ >= partitionsZ) partitionZ = partitionsZ - 1;
2376 auto partitionIdx = partitionZ * partitionsX + partitionX;
2377 foliageMapsMirrored[partitionIdx][foliagePrototypeId].push_back(transformMirrored);
2382 foliageMaps = foliageMapsMirrored;
Color 4 definition class.
ByteBuffer getRGBTextureData()
uint8_t getRGBDepthBitsPerPixel() const
uint16_t getTextureHeight() const
uint16_t getTextureWidth() const
Represents a model face, consisting of vertex, normal, tangent and bitangent vectors,...
Node faces entity A node can have multiple entities containing faces and a applied material.
void setMaterial(Material *material)
Set up the entity's material.
void setLOD1Distance(float lod1Distance)
Set LOD1 distance.
void setLOD2Indices(const vector< int32_t > &lod2Indices)
Set LOD2 indices.
void setLOD2Distance(float lod2Distance)
Set LOD2 distance.
void setLOD3Distance(float lod3Distance)
Set LOD3 distance.
void setLOD1Indices(const vector< int32_t > &lod1Indices)
Set LOD1 indices.
void setLOD3Indices(const vector< int32_t > &lod3Indices)
Set LOD3 indices.
void setFaces(const vector< Face > &faces)
Set up entity's faces.
Representation of a 3D model.
Represents rotation orders of a model.
Represents specular material properties.
Axis aligned bounding box used for frustum, this is not directly connectable with physics engine.
const Vector3 & getDimensions() const
void extend(BoundingBox *boundingBox)
Extend bounding box with given bounding box.
Line segment helper functions.
Matrix3x3 class representing matrix3x3 mathematical structure and operations for 2d space.
Matrix3x3 & identity()
Creates identity matrix.
Matrix3x3 & setTranslation(const Vector2 &vector2)
Sets translation in matrix.
Matrix3x3 clone() const
Clones this matrix.
Matrix3x3 & multiply(const Matrix3x3 &matrix)
Multiplies this matrix with given matrix.
Vector3 computeEulerAngles() const
Compute Euler angles (rotation around x, y, z axes)
Vector2 class representing vector2 mathematical structure and operations with x, y components.
Vector3 class representing vector3 mathematical structure and operations with x, y,...
Vector3 & add(float scalar)
Adds a scalar.
Vector3 & setY(float y)
Sets y component.
Vector3 clone() const
Clones this vector3.
Vector3 & set(float x, float y, float z)
Sets this vector3 by its components.
Vector3 & normalize()
Normalizes this vector3.
static void println()
Print new line to console.
static constexpr float MAX_VALUE
static constexpr float MIN_VALUE
static constexpr int MIN_VALUE
static constexpr int MAX_VALUE
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.
static void getWaterVertex(int x, int z, float waterHeight, Vector3 &vertex)
Get the terrain vertex for given x and z position.
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.
static void createFoliageMaps(BoundingBox &terrainBoundingBox, vector< unordered_map< int, vector< Transform >>> &foliageMaps)
Create foliage maps.
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.
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.
static constexpr float PARTITION_SIZE
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.
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.
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.
static bool determineWater(const vector< float > &terrainHeightVector, int verticesPerX, int verticesPerZ, int x, int z, float waterHeight)
Determine if water can be generated.
static void createTerrainModels(float width, float depth, float y, vector< float > &terrainHeightVector, BoundingBox &terrainBoundingBox, vector< Model * > &terrainModels, bool createLODLevels=false)
Create terrain models.
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.
static bool getTerrainModelsHeight(BoundingBox &terrainBoundingBox, vector< Model * > &terrainModels, vector< float > &terrainHeightVector, const Vector3 &brushCenterPosition, float &brushHeight)
Get terrain models height for e.g.
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.
static constexpr float STEP_SIZE
static void emptyFoliageMaps(vector< unordered_map< int, vector< Transform >>> &foliageMaps)
Empty foliage maps.
static Vector3 computeWaterReflectionEnvironmentMappingPosition(const unordered_map< int, unordered_set< int >> &waterPositionMap, float waterHeight)
Compute water reflection environment mapping position.
@ BRUSHOPERATION_WATER_ADD
@ BRUSHOPERATION_SUBTRACT
static bool hasWaterPosition(const unordered_map< int, unordered_set< int >> &waterPositionSet, int x, int z)
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.
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.
static void getTerrainVertex(int x, int z, Vector3 &vertex)
Get the terrain vertex for given x and z position without providing y component.
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.
std::exception Exception
Exception base class.