This commit is contained in:
Rainer Leit
2026-09-25 17:02:24 +03:00
parent cc43ed8dc8
commit 9597629951
2149 changed files with 460234 additions and 1770 deletions
+21 -10
View File
@@ -1,8 +1,14 @@
"""The world manifest: RawContent/World/World.json, the one place that says how big L_World is, what a
"""The canvas manifest: RawContent/World/World.json, the one place that says how big L_Canvas_Proto is, what a
heightmap value means in metres, and where the height comes from. Pure Python (no numpy, no engine), shared by
generate_heightmap.py (writes the PNGs) and create_world.py (imports them), so both agree without either
knowing about the other.
**This is not the world.** `L_World` is the planet-map region in Region.json (D-72); this pipeline is the numpy
square canvas, legacy by D-47 and kept only because it is still the one path that carries the erosion pass's
flow, wear and deposit maps into Unreal. The level name is deliberately not the real world's: create_world.py
empties whatever level it is handed, so a manifest pointing at L_World would replace ninety-eight landscapes
with a 14 km square on one run.
The height contract. The landscape is `vertices_per_side` vertices a side at `quad_cm` a quad. The 16-bit
heightmap spans `elevation_m.min` (value 0) to `elevation_m.max` (value 65535), and the level places the
landscape so that world Z 0 is elevation 0 m: sea level, when `sea_level_m` is 0. From that the engine's
@@ -22,22 +28,25 @@ WORLD_DIR = os.path.join(PROJECT_ROOT, "RawContent", "World")
MANIFEST_PATH = os.path.join(WORLD_DIR, "World.json")
HEIGHTMAP_DIR = os.path.join(WORLD_DIR, "Heightmaps")
HEIGHTMAP_FILE = "L_World_Height.png"
# Named for the level this pipeline builds, which is L_Canvas_Proto and not L_World (D-72). The region
# tiles in RegionTiles/ are L_World_x0_y0_Height.png and the like; two files a folder apart differing only
# in a tile suffix is a confusion worth one rename.
HEIGHTMAP_FILE = "L_Canvas_Proto_Height.png"
# Paint layer name (as the Elite_RockyMeadows landscape material calls it) -> weightmap file. The names mislead:
# in this pack Base_Layer is the rock, Layer_02 the grass (the meadow) and Layer_03 the high rock.
LAYER_FILES = {
"Base_Layer": "L_World_Base_Layer.png",
"Layer_02": "L_World_Layer_02.png",
"Layer_03": "L_World_Layer_03.png",
"Base_Layer": "L_Canvas_Proto_Base_Layer.png",
"Layer_02": "L_Canvas_Proto_Layer_02.png",
"Layer_03": "L_Canvas_Proto_Layer_03.png",
}
# Derivative maps the erosion pass leaves behind, 8-bit, for painting and for a material that wants them later:
# how much water passed (log scaled), how much bedrock was scraped, how much sediment was laid down, and the
# curvature (128 flat, brighter convex, darker concave).
DERIVED_FILES = {
"flow": "L_World_Flow.png",
"wear": "L_World_Wear.png",
"deposit": "L_World_Deposit.png",
"curvature": "L_World_Curvature.png",
"flow": "L_Canvas_Proto_Flow.png",
"wear": "L_Canvas_Proto_Wear.png",
"deposit": "L_Canvas_Proto_Deposit.png",
"curvature": "L_Canvas_Proto_Curvature.png",
}
# The engine maps heightmap value v to local height (v - 32768) / 128 * ZScale cm, so ZScale 100 spans 512 m.
@@ -47,7 +56,9 @@ ENGINE_SPAN_M_AT_SCALE_100 = 512.0
class WorldManifest:
def __init__(self, data, path=MANIFEST_PATH):
self.path = path
self.level = data.get("level", "/Game/Maps/L_World")
# Not L_World: that is the region's now. A manifest with no `level` must not default to the world the
# game uses, because create_world.py empties whatever level it is handed before rebuilding it.
self.level = data.get("level", "/Game/Maps/L_Canvas_Proto")
self.vertices_per_side = int(data["vertices_per_side"])
self.quad_cm = float(data["quad_cm"])
self.elevation_min_m = float(data["elevation_m"]["min"])