From 409495cc02b53e09933e20f230e977122d5eb742 Mon Sep 17 00:00:00 2001
From: Stan Soldatov <118521851+iwatkot@users.noreply.github.com>
Date: Tue, 24 Dec 2024 04:17:44 +0100
Subject: [PATCH] Water planes generation, water depth option for DEM
* Textures and schema update for FS25.
* Safely fit polygon to the bounds.
* Water planes generation.
* Water Planes tutorial.
* README update.
* README update.
* README update.
* README update.
* README update.
---
README.md | 8 ++
data/fs25-texture-schema.json | 8 +-
data/fs25-tree-schema.json | 4 -
docs/create_water_planes.md | 82 ++++++++++++++
maps4fs/generator/background.py | 192 +++++++++++++++++++++++++++++---
maps4fs/generator/component.py | 10 +-
maps4fs/generator/texture.py | 42 +++++--
webui/generator.py | 22 +++-
webui/templates.py | 8 ++
9 files changed, 342 insertions(+), 34 deletions(-)
create mode 100644 docs/create_water_planes.md
diff --git a/README.md b/README.md
index c1ada866..adb88c2c 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,7 @@
đŊ Automatically generates farmlands đ
đŋ Automatically generates decorative foliage đ
đ˛ Automatically generates forests đ
+đ Automatically generates water planes đ
đ Based on real-world data from OpenStreetMap
đī¸ Generates height map using SRTM dataset
đĻ Provides a ready-to-use map template for the Giants Editor
@@ -65,6 +66,8 @@
đŋ Automatically generates decorative foliage.

đ˛ Automatically generates forests.
+
+đ Automatically generates water planes.

đī¸ True-to-life blueprints for fast and precise modding.

@@ -76,6 +79,9 @@
How to Generate a Map for Farming Simulator 25 and 22 from a real place using maps4FS.
+
+Map example generated with maps4fs.
+
## Quick Start
There are several ways to use the tool. You obviously need the **first one**, but you can choose any of the others depending on your needs.
### đ For most users
@@ -451,6 +457,8 @@ You can also apply some advanced settings to the map generation process. Note th
- Plateau height: this value will be added to each pixel of the DEM image, making it "higher". It's useful when you want to add some negative heights on the map, that appear to be in a "low" place. By default, it's set to 0.
+- Water depth: this value will be subtracted from each pixel of the DEM image, where water resources are located. Pay attention that it's not in meters, instead it in the pixel value of DEM, which is 16 bit image with possible values from 0 to 65535. When this value is set, the same value will be added to the plateau setting to avoid negative heights.
+
### Texture Advanced settings
- Fields padding - this value (in meters) will be applied to each field, making it smaller. It's useful when the fields are too close to each other and you want to make them smaller. By default, it's set to 0.
diff --git a/data/fs25-texture-schema.json b/data/fs25-texture-schema.json
index aec70fed..d7af448d 100644
--- a/data/fs25-texture-schema.json
+++ b/data/fs25-texture-schema.json
@@ -138,7 +138,7 @@
{
"name": "mudDark",
"count": 2,
- "tags": { "landuse": "farmland" },
+ "tags": { "landuse": ["farmland", "meadow"] },
"color": [47, 107, 85],
"priority": 4,
"info_layer": "fields"
@@ -171,7 +171,8 @@
"count": 2,
"tags": { "waterway": ["ditch", "drain"] },
"width": 2,
- "color": [33, 67, 101]
+ "color": [33, 67, 101],
+ "background": true
},
{
"name": "mudTracks",
@@ -210,6 +211,7 @@
"landuse": "basin"
},
"width": 10,
- "color": [255, 20, 20]
+ "color": [255, 20, 20],
+ "background": true
}
]
diff --git a/data/fs25-tree-schema.json b/data/fs25-tree-schema.json
index 597dc55a..35cfb82c 100644
--- a/data/fs25-tree-schema.json
+++ b/data/fs25-tree-schema.json
@@ -327,10 +327,6 @@
"name": "tiliaAmurensis_stage04",
"reference_id": 1081
},
- {
- "name": "transportTree",
- "reference_id": 1082
- },
{
"name": "treesRavaged",
"reference_id": 1083
diff --git a/docs/create_water_planes.md b/docs/create_water_planes.md
new file mode 100644
index 00000000..3f920377
--- /dev/null
+++ b/docs/create_water_planes.md
@@ -0,0 +1,82 @@
+## How to create water planes
+
+The generator will automatically generates the obj files for the water planes, but you need to process them both in Blender and in Giants Editor for them to look correctly in the game.
+
+1. Find the obj file in the `water` directory.
+2. Import the obj file in Blender.
+
+
+
+3. Ensure that imported object selected, right click anywhere and select **Set Origin** -> **Origin to Geometry**.
+
+
+
+4. Press the **N** key to open the **Transform** panel and set the **Location** to **0, 0, 0** and **Rotation** to **0, 0, 0**.
+DO NOT TOUCH SCALE AND DIMENSIONS!
+
+
+
+5. Add an empty material to the object.
+
+
+
+6. Change the emission color to fully black.
+
+
+
+
+
+7. Apply the **Decimate** modifier to the object and **Shade Smooth**. You can find the example of this in the tutorial about [Background Terrain](https://github.com/iwatkot/maps4fs/blob/main/docs/create_background_terrain.md).
+
+8. Open the **Giants Editor I3D Exporter** and set the path to the directory where the game is installed.
+
+
+
+9. Go to the **Material** tab and press the **Detect Path** button.
+
+
+
+10. Select shader **oceanShader.xml**.
+
+
+
+11. SAVE THE BLENDER FILE! Then press the **Apply** button.
+
+
+
+12. Go to the **Export**, ensure that your object is selected and press the **Export selected** button.
+
+
+
+13. Open the Giants Editor and import the i3d file. It will be black, but don't worry, it's normal.
+After it, position the water plane in the correct place.
+
+
+
+14. Open the **Material Editing** window and select your water plane.
+
+15. Change the **Variation** to **simple** and then edit values as on the screenshot.
+Those are default values for the water plane, but you can play with them to achieve the desired effect.
+
+
+
+16. Set **Smoothness** and **Metalness** to **1**.
+
+17. Click on the button near the **Normal map**.
+
+
+
+18. Click on the **...** button and provide the path to the **water_normal.dds** file.
+It's placed in `where-the-game-is-installed/data/maps/textures/shared/water_normal.dds`.
+
+
+
+19. You should see the normal map in the window. Press the **OK** button.
+
+
+
+20. It should look like this.
+
+
+
+We're done here!
\ No newline at end of file
diff --git a/maps4fs/generator/background.py b/maps4fs/generator/background.py
index 46c7b36a..4f0e5751 100644
--- a/maps4fs/generator/background.py
+++ b/maps4fs/generator/background.py
@@ -3,8 +3,10 @@
from __future__ import annotations
+import json
import os
import shutil
+from copy import deepcopy
import cv2
import numpy as np
@@ -17,6 +19,7 @@
DEFAULT_PLATEAU,
DEM,
)
+from maps4fs.generator.texture import Texture
DEFAULT_DISTANCE = 2048
RESIZE_FACTOR = 1 / 8
@@ -25,6 +28,7 @@
ELEMENTS = [FULL_NAME, FULL_PREVIEW_NAME]
+# pylint: disable=R0902
class Background(Component):
"""Component for creating 3D obj files based on DEM data around the map.
@@ -43,7 +47,9 @@ class Background(Component):
def preprocess(self) -> None:
"""Registers the DEMs for the background terrain."""
self.light_version = self.kwargs.get("light_version", False)
+ self.water_depth = self.kwargs.get("water_depth", 0)
self.stl_preview_path: str | None = None
+ self.water_resources_path: str | None = None
if self.rotation:
self.logger.debug("Rotation is enabled: %s.", self.rotation)
@@ -51,22 +57,29 @@ def preprocess(self) -> None:
else:
output_size_multiplier = 1
- background_size = self.map_size + DEFAULT_DISTANCE * 2
- rotated_size = int(background_size * output_size_multiplier)
+ self.background_size = self.map_size + DEFAULT_DISTANCE * 2
+ self.rotated_size = int(self.background_size * output_size_multiplier)
self.background_directory = os.path.join(self.map_directory, "background")
+ self.water_directory = os.path.join(self.map_directory, "water")
os.makedirs(self.background_directory, exist_ok=True)
+ os.makedirs(self.water_directory, exist_ok=True)
autoprocesses = [self.kwargs.get("auto_process", False), False]
+ self.output_paths = [
+ os.path.join(self.background_directory, f"{name}.png") for name in ELEMENTS
+ ]
+ self.not_substracted_path = os.path.join(self.background_directory, "not_substracted.png")
+
dems = []
- for name, autoprocess in zip(ELEMENTS, autoprocesses):
+ for name, autoprocess, output_path in zip(ELEMENTS, autoprocesses, self.output_paths):
dem = DEM(
self.game,
self.map,
self.coordinates,
- background_size,
- rotated_size,
+ self.background_size,
+ self.rotated_size,
self.rotation,
self.map_directory,
self.logger,
@@ -77,8 +90,8 @@ def preprocess(self) -> None:
)
dem.preprocess()
dem.is_preview = self.is_preview(name) # type: ignore
- dem.set_output_resolution((rotated_size, rotated_size))
- dem.set_dem_path(os.path.join(self.background_directory, f"{name}.png"))
+ dem.set_output_resolution((self.rotated_size, self.rotated_size))
+ dem.set_dem_path(output_path)
dems.append(dem)
self.dems = dems
@@ -98,8 +111,17 @@ def process(self) -> None:
"""Launches the component processing. Iterates over all tiles and processes them
as a result the DEM files will be saved, then based on them the obj files will be
generated."""
+ self.create_background_textures()
+
for dem in self.dems:
dem.process()
+ if not dem.is_preview: # type: ignore
+ shutil.copyfile(dem.dem_path, self.not_substracted_path)
+
+ if self.water_depth:
+ self.subtraction()
+
+ for dem in self.dems:
if not dem.is_preview: # type: ignore
cutted_dem_path = self.cutout(dem.dem_path)
if self.game.additional_dem_name is not None:
@@ -107,6 +129,7 @@ def process(self) -> None:
if not self.light_version:
self.generate_obj_files()
+ self.generate_water_resources_obj()
else:
self.logger.info("Light version is enabled, obj files will not be generated.")
@@ -223,13 +246,20 @@ def cutout(self, dem_path: str) -> str:
return main_dem_path
# pylint: disable=too-many-locals
- def plane_from_np(self, dem_data: np.ndarray, save_path: str, is_preview: bool = False) -> None:
+ def plane_from_np(
+ self,
+ dem_data: np.ndarray,
+ save_path: str,
+ is_preview: bool = False,
+ include_zeros: bool = True,
+ ) -> None:
"""Generates a 3D obj file based on DEM data.
Arguments:
dem_data (np.ndarray) -- The DEM data as a numpy array.
save_path (str) -- The path where the obj file will be saved.
is_preview (bool, optional) -- If True, the preview mesh will be generated.
+ include_zeros (bool, optional) -- If True, the mesh will include the zero height values.
"""
dem_data = cv2.resize( # pylint: disable=no-member
dem_data, (0, 0), fx=RESIZE_FACTOR, fy=RESIZE_FACTOR
@@ -247,6 +277,9 @@ def plane_from_np(self, dem_data: np.ndarray, save_path: str, is_preview: bool =
x, y = np.meshgrid(x, y)
z = dem_data
+ ground = z.max()
+ self.logger.debug("Ground level: %s", ground)
+
self.logger.debug(
"Starting to generate a mesh for with shape: %s x %s. This may take a while...",
cols,
@@ -256,6 +289,8 @@ def plane_from_np(self, dem_data: np.ndarray, save_path: str, is_preview: bool =
vertices = np.column_stack([x.ravel(), y.ravel(), z.ravel()])
faces = []
+ skipped = 0
+
for i in range(rows - 1):
for j in range(cols - 1):
top_left = i * cols + j
@@ -263,9 +298,15 @@ def plane_from_np(self, dem_data: np.ndarray, save_path: str, is_preview: bool =
bottom_left = top_left + cols
bottom_right = bottom_left + 1
+ if ground in [z[i, j], z[i, j + 1], z[i + 1, j], z[i + 1, j + 1]]:
+ skipped += 1
+ continue
+
faces.append([top_left, bottom_left, bottom_right])
faces.append([top_left, bottom_right, top_right])
+ self.logger.debug("Skipped faces: %s", skipped)
+
faces = np.array(faces) # type: ignore
mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
@@ -283,13 +324,14 @@ def plane_from_np(self, dem_data: np.ndarray, save_path: str, is_preview: bool =
mesh.apply_scale([0.5, 0.5, 0.5])
self.mesh_to_stl(mesh)
else:
- multiplier = self.kwargs.get("multiplier", DEFAULT_MULTIPLIER)
- if multiplier != 1:
- z_scaling_factor = 1 / multiplier
- else:
- z_scaling_factor = 1 / 2**5
- self.logger.debug("Z scaling factor: %s", z_scaling_factor)
- mesh.apply_scale([1 / RESIZE_FACTOR, 1 / RESIZE_FACTOR, z_scaling_factor])
+ if not include_zeros:
+ multiplier = self.kwargs.get("multiplier", DEFAULT_MULTIPLIER)
+ if multiplier != 1:
+ z_scaling_factor = 1 / multiplier
+ else:
+ z_scaling_factor = 1 / 2**5
+ self.logger.debug("Z scaling factor: %s", z_scaling_factor)
+ mesh.apply_scale([1 / RESIZE_FACTOR, 1 / RESIZE_FACTOR, z_scaling_factor])
mesh.export(save_path)
self.logger.debug("Obj file saved: %s", save_path)
@@ -413,3 +455,123 @@ def colored_preview(self, image_path: str) -> str:
cv2.imwrite(colored_dem_path, dem_data_colored)
return colored_dem_path
+
+ def create_background_textures(self) -> None:
+ """Creates background textures for the map."""
+ if not os.path.isfile(self.game.texture_schema):
+ self.logger.warning("Texture schema file not found: %s", self.game.texture_schema)
+ return
+
+ with open(self.game.texture_schema, "r", encoding="utf-8") as f:
+ layers_schema = json.load(f)
+
+ background_layers = []
+ for layer in layers_schema:
+ if layer.get("background") is True:
+ layer_copy = deepcopy(layer)
+ layer_copy["count"] = 1
+ layer_copy["name"] = f"{layer['name']}_background"
+ background_layers.append(layer_copy)
+
+ if not background_layers:
+ return
+
+ self.background_texture = Texture( # pylint: disable=W0201
+ self.game,
+ self.map,
+ self.coordinates,
+ self.background_size,
+ self.rotated_size,
+ rotation=self.rotation,
+ map_directory=self.map_directory,
+ logger=self.logger,
+ light_version=self.light_version,
+ custom_schema=background_layers,
+ )
+
+ self.background_texture.preprocess()
+ self.background_texture.process()
+
+ processed_layers = self.background_texture.get_background_layers()
+ weights_directory = self.game.weights_dir_path(self.map_directory)
+ background_paths = [layer.path(weights_directory) for layer in processed_layers]
+ self.logger.debug("Found %s background textures.", len(background_paths))
+
+ if not background_paths:
+ self.logger.warning("No background textures found.")
+ return
+
+ # Merge all images into one.
+ background_image = np.zeros((self.background_size, self.background_size), dtype=np.uint8)
+ for path in background_paths:
+ layer = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
+ background_image = cv2.add(background_image, layer) # type: ignore
+
+ background_save_path = os.path.join(self.water_directory, "water_resources.png")
+ cv2.imwrite(background_save_path, background_image)
+ self.logger.info("Background texture saved: %s", background_save_path)
+ self.water_resources_path = background_save_path # pylint: disable=W0201
+
+ def subtraction(self) -> None:
+ """Subtracts the water depth from the DEM data where the water resources are located."""
+ if not self.water_resources_path:
+ self.logger.warning("Water resources texture not found.")
+ return
+
+ # Single channeled 8 bit image, where the water have values of 255, and the rest 0.
+ water_resources_image = cv2.imread(self.water_resources_path, cv2.IMREAD_UNCHANGED)
+ mask = water_resources_image == 255
+
+ # Make mask a little bit smaller (1 pixel).
+ mask = cv2.erode(mask.astype(np.uint8), np.ones((3, 3), np.uint8), iterations=1).astype(
+ bool
+ )
+
+ for output_path in self.output_paths:
+ if FULL_PREVIEW_NAME in output_path:
+ continue
+ dem_image = cv2.imread(output_path, cv2.IMREAD_UNCHANGED)
+
+ # Create a mask where water_resources_image is 255 (or not 0)
+ # Subtract water_depth from dem_image where mask is True
+ dem_image[mask] = dem_image[mask] - self.water_depth
+
+ # Save the modified dem_image back to the output path
+ cv2.imwrite(output_path, dem_image)
+ self.logger.debug("Water depth subtracted from DEM data: %s", output_path)
+
+ def generate_water_resources_obj(self) -> None:
+ """Generates 3D obj files based on water resources data."""
+ if not self.water_resources_path:
+ self.logger.warning("Water resources texture not found.")
+ return
+
+ # Single channeled 8 bit image, where the water have values of 255, and the rest 0.
+ plane_water = cv2.imread(self.water_resources_path, cv2.IMREAD_UNCHANGED)
+ dilated_plane_water = cv2.dilate(
+ plane_water.astype(np.uint8), np.ones((5, 5), np.uint8), iterations=5
+ ).astype(np.uint8)
+ plane_save_path = os.path.join(self.water_directory, "plane_water.obj")
+ self.plane_from_np(
+ dilated_plane_water, plane_save_path, is_preview=False, include_zeros=False
+ )
+
+ # Single channeled 16 bit DEM image of terrain.
+ background_dem = cv2.imread(self.not_substracted_path, cv2.IMREAD_UNCHANGED)
+
+ # Remove all the values from the background dem where the plane_water is 0.
+ background_dem[plane_water == 0] = 0
+
+ # Dilate the background dem to make the water more smooth.
+ elevated_water = cv2.dilate(background_dem, np.ones((3, 3), np.uint16), iterations=10)
+
+ # Use the background dem as a mask to prevent the original values from being overwritten.
+ mask = background_dem > 0
+
+ # Combine the dilated background dem with non-dilated background dem.
+ elevated_water = np.where(mask, background_dem, elevated_water)
+ elevated_save_path = os.path.join(self.water_directory, "elevated_water.obj")
+
+ self.plane_from_np(
+ elevated_water, elevated_save_path, is_preview=False, include_zeros=False
+ )
diff --git a/maps4fs/generator/component.py b/maps4fs/generator/component.py
index f93f3cec..d01b6c8e 100644
--- a/maps4fs/generator/component.py
+++ b/maps4fs/generator/component.py
@@ -330,6 +330,7 @@ def top_left_coordinates_to_center(self, top_left: tuple[int, int]) -> tuple[int
return cs_x, cs_y
+ # pylint: disable=R0914
def fit_polygon_into_bounds(
self, polygon_points: list[tuple[int, int]], margin: int = 0, angle: int = 0
) -> list[tuple[int, int]]:
@@ -371,8 +372,13 @@ def fit_polygon_into_bounds(
bounds = box(min_x, min_y, max_x, max_y)
# Intersect the polygon with the bounds to fit it within the map
- fitted_polygon = polygon.intersection(bounds)
- self.logger.debug("Fitted the polygon into the bounds: %s", bounds)
+ try:
+ fitted_polygon = polygon.intersection(bounds)
+ self.logger.debug("Fitted the polygon into the bounds: %s", bounds)
+ except Exception as e:
+ raise ValueError( # pylint: disable=W0707
+ f"Could not fit the polygon into the bounds: {e}"
+ )
if not isinstance(fitted_polygon, Polygon):
raise ValueError("The fitted polygon is not a valid polygon.")
diff --git a/maps4fs/generator/texture.py b/maps4fs/generator/texture.py
index 99351072..df7df684 100644
--- a/maps4fs/generator/texture.py
+++ b/maps4fs/generator/texture.py
@@ -64,6 +64,7 @@ def __init__( # pylint: disable=R0917
priority: int | None = None,
info_layer: str | None = None,
usage: str | None = None,
+ background: bool = False,
):
self.name = name
self.count = count
@@ -74,6 +75,7 @@ def __init__( # pylint: disable=R0917
self.priority = priority
self.info_layer = info_layer
self.usage = usage
+ self.background = background
def to_json(self) -> dict[str, str | list[str] | bool]: # type: ignore
"""Returns dictionary with layer data.
@@ -90,6 +92,7 @@ def to_json(self) -> dict[str, str | list[str] | bool]: # type: ignore
"priority": self.priority,
"info_layer": self.info_layer,
"usage": self.usage,
+ "background": self.background,
}
data = {k: v for k, v in data.items() if v is not None}
@@ -178,17 +181,30 @@ def preprocess(self) -> None:
self.fields_padding = self.kwargs.get("fields_padding", 0)
self.logger.debug("Light version: %s.", self.light_version)
- if not os.path.isfile(self.game.texture_schema):
- raise FileNotFoundError(f"Texture layers schema not found: {self.game.texture_schema}")
+ self.custom_schema: list[dict[str, str | dict[str, str] | int]] | None = self.kwargs.get(
+ "custom_schema"
+ )
- try:
- with open(self.game.texture_schema, "r", encoding="utf-8") as f:
- layers_schema = json.load(f)
- except json.JSONDecodeError as e:
- raise ValueError(f"Error loading texture layers schema: {e}") from e
+ if self.custom_schema:
+ layers_schema = self.custom_schema
+ self.logger.info("Custom schema loaded with %s layers.", len(layers_schema))
+ else:
+ if not os.path.isfile(self.game.texture_schema):
+ raise FileNotFoundError(
+ f"Texture layers schema not found: {self.game.texture_schema}"
+ )
+
+ try:
+ with open(self.game.texture_schema, "r", encoding="utf-8") as f:
+ layers_schema = json.load(f)
+ except json.JSONDecodeError as e:
+ raise ValueError(f"Error loading texture layers schema: {e}") from e
- self.layers = [self.Layer.from_json(layer) for layer in layers_schema]
- self.logger.info("Loaded %s layers.", len(self.layers))
+ try:
+ self.layers = [self.Layer.from_json(layer) for layer in layers_schema]
+ self.logger.info("Loaded %s layers.", len(self.layers))
+ except Exception as e: # pylint: disable=W0703
+ raise ValueError(f"Error loading texture layers: {e}") from e
base_layer = self.get_base_layer()
if base_layer:
@@ -215,6 +231,14 @@ def get_base_layer(self) -> Layer | None:
return layer
return None
+ def get_background_layers(self) -> list[Layer]:
+ """Returns list of background layers.
+
+ Returns:
+ list[Layer]: List of background layers.
+ """
+ return [layer for layer in self.layers if layer.background]
+
def get_layer_by_usage(self, usage: str) -> Layer | None:
"""Returns layer by usage.
diff --git a/webui/generator.py b/webui/generator.py
index c1316680..6e4685bd 100644
--- a/webui/generator.py
+++ b/webui/generator.py
@@ -230,6 +230,7 @@ def add_left_widgets(self) -> None:
self.farmland_margin = 3
self.forest_density = 10
self.randomize_plants = True
+ self.water_depth = 200
if not self.auto_process:
self.logger.info("Auto preset is disabled.")
@@ -295,6 +296,18 @@ def add_left_widgets(self) -> None:
label_visibility="collapsed",
)
+ st.write("Enter the water depth (pixel value):")
+ st.write(Messages.WATER_DEPTH_INFO)
+
+ self.water_depth = st.number_input(
+ "Water Depth",
+ value=200,
+ min_value=0,
+ max_value=10000,
+ key="water_depth",
+ label_visibility="collapsed",
+ )
+
with st.expander("Textures Advanced Settings", icon="đ¨"):
st.info(
"âšī¸ Settings related to the textures of the map, which represent different "
@@ -448,6 +461,12 @@ def generate_map(self) -> None:
# Create an instance of the Map class and generate the map.
multiplier = self.multiplier_input if not self.auto_process else 1
+ plateau = (
+ self.plateau_height_input
+ if not self.water_depth
+ else self.plateau_height_input + self.water_depth
+ )
+
mp = mfs.Map(
game,
coordinates,
@@ -458,12 +477,13 @@ def generate_map(self) -> None:
multiplier=multiplier,
blur_radius=self.blur_radius_input,
auto_process=self.auto_process,
- plateau=self.plateau_height_input,
+ plateau=plateau,
light_version=self.community,
fields_padding=self.fields_padding,
farmland_margin=self.farmland_margin,
forest_density=self.forest_density,
randomize_plants=self.randomize_plants,
+ water_depth=self.water_depth,
)
if self.community:
diff --git a/webui/templates.py b/webui/templates.py
index 48658189..3e0fbc65 100644
--- a/webui/templates.py
+++ b/webui/templates.py
@@ -16,6 +16,7 @@ class Messages:
"StreamLit community hosting has some limitations, such as: \n"
"đ¸ Maximum map size is 2048x2048 meters. \n"
"đ¸ Background terrain will not be generated. \n"
+ "đ¸ Water planes will not be generated. \n"
"đ¸ Map rotation is disabled. \n"
"đ¸ Texture dissolving is disabled (they will look worse). \n \n"
"If you run the application locally, you won't have any of these limitations "
@@ -106,3 +107,10 @@ class Messages:
"If checked, random species of plants will be generated. "
"If unchecked, only basic smallDenseMix will be applied."
)
+ WATER_DEPTH_INFO = (
+ "This value will be subtracted from the DEM image, making the water deeper. "
+ "Pay attention to the fact, that this value IS NOT IN METERS, instead it uses the pixel "
+ "value from the DEM image. So, if you set low values, you will probably see no "
+ "difference. Also, this value will be added to the plateau value, to avoid negative "
+ "height."
+ )