Upgrade Ground Zero terrain material

This commit is contained in:
2026-05-21 15:12:30 +00:00
parent 13e931eb04
commit 98ab61a7a4
14 changed files with 148 additions and 23 deletions
+95
View File
@@ -48,6 +48,10 @@ ENVIRONMENT_MATERIALS = {
"terrain": {
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Terrain_CoastalScrub",
"color": unreal.LinearColor(0.16, 0.23, 0.12, 1.0),
"dry_soil_color": unreal.LinearColor(0.28, 0.24, 0.16, 1.0),
"scrub_green_color": unreal.LinearColor(0.12, 0.22, 0.10, 1.0),
"sandy_path_color": unreal.LinearColor(0.42, 0.36, 0.23, 1.0),
"noise_scale": 42.0,
"roughness": 0.92,
},
"tree": {
@@ -837,11 +841,102 @@ def ensure_environment_materials():
material.set_editor_property("used_with_instanced_static_meshes", True)
unreal.MaterialEditingLibrary.recompile_material(material)
unreal.EditorAssetLibrary.save_asset(spec["path"], only_if_is_dirty=False)
if key == "terrain":
rebuild_ground_zero_terrain_material(material, spec)
created_or_loaded[key] = material
return created_or_loaded
def set_expression_property(expression, property_name, value):
try:
expression.set_editor_property(property_name, value)
return True
except Exception:
return False
def connect_expression(source, source_output, target, target_input):
try:
unreal.MaterialEditingLibrary.connect_material_expressions(source, source_output, target, target_input)
return True
except Exception as exc:
unreal.log_warning(f"Could not connect material expression input {target_input}: {exc}")
return False
def create_constant_color(material, color, x, y):
expression = unreal.MaterialEditingLibrary.create_material_expression(
material, unreal.MaterialExpressionConstant3Vector, x, y
)
expression.set_editor_property("constant", color)
return expression
def create_constant_scalar(material, value, x, y):
expression = unreal.MaterialEditingLibrary.create_material_expression(
material, unreal.MaterialExpressionConstant, x, y
)
expression.set_editor_property("r", value)
return expression
def rebuild_ground_zero_terrain_material(material, spec):
"""Build a simple procedural material so the landscape no longer reads as a flat color."""
if hasattr(unreal.MaterialEditingLibrary, "delete_all_material_expressions"):
unreal.MaterialEditingLibrary.delete_all_material_expressions(material)
dry_soil = create_constant_color(material, spec["dry_soil_color"], -900, -260)
scrub_green = create_constant_color(material, spec["scrub_green_color"], -900, -80)
sandy_path = create_constant_color(material, spec["sandy_path_color"], -900, 100)
broad_noise = unreal.MaterialEditingLibrary.create_material_expression(
material, unreal.MaterialExpressionNoise, -560, -160
)
set_expression_property(broad_noise, "scale", spec.get("noise_scale", 42.0))
set_expression_property(broad_noise, "quality", 3)
set_expression_property(broad_noise, "levels", 5)
fine_noise = unreal.MaterialEditingLibrary.create_material_expression(
material, unreal.MaterialExpressionNoise, -560, 100
)
set_expression_property(fine_noise, "scale", 145.0)
set_expression_property(fine_noise, "quality", 2)
set_expression_property(fine_noise, "levels", 3)
scrub_blend = unreal.MaterialEditingLibrary.create_material_expression(
material, unreal.MaterialExpressionLinearInterpolate, -260, -180
)
connect_expression(dry_soil, "", scrub_blend, "A")
connect_expression(scrub_green, "", scrub_blend, "B")
connect_expression(broad_noise, "", scrub_blend, "Alpha")
final_blend = unreal.MaterialEditingLibrary.create_material_expression(
material, unreal.MaterialExpressionLinearInterpolate, 40, -70
)
connect_expression(scrub_blend, "", final_blend, "A")
connect_expression(sandy_path, "", final_blend, "B")
connect_expression(fine_noise, "", final_blend, "Alpha")
unreal.MaterialEditingLibrary.connect_material_property(
final_blend, "", unreal.MaterialProperty.MP_BASE_COLOR
)
roughness = create_constant_scalar(material, spec["roughness"], -260, 160)
unreal.MaterialEditingLibrary.connect_material_property(
roughness, "", unreal.MaterialProperty.MP_ROUGHNESS
)
specular = create_constant_scalar(material, 0.18, -260, 260)
unreal.MaterialEditingLibrary.connect_material_property(
specular, "", unreal.MaterialProperty.MP_SPECULAR
)
material.set_editor_property("use_material_attributes", False)
unreal.MaterialEditingLibrary.recompile_material(material)
unreal.EditorAssetLibrary.save_asset(spec["path"], only_if_is_dirty=False)
unreal.log("Rebuilt Ground Zero terrain material with coastal scrub color variation and procedural noise.")
def apply_material_to_actor_meshes(actor, material):
applied_count = 0
for component in actor.get_components_by_class(unreal.StaticMeshComponent):
@@ -82,6 +82,28 @@ def assert_asset(path):
return asset
def verify_terrain_material_is_not_flat(material, failures):
project_root = unreal.Paths.convert_relative_path_to_full(unreal.Paths.project_dir())
material_package = project_root + "Content/Agrarian/Materials/M_AGR_GZ_Terrain_CoastalScrub.uasset"
try:
with open(material_package, "rb") as handle:
package_bytes = handle.read()
except Exception as exc:
failures.append(f"could not inspect terrain material package: {exc}")
return
noise_count = package_bytes.count(b"MaterialExpressionNoise")
lerp_count = package_bytes.count(b"MaterialExpressionLinearInterpolate")
color_count = package_bytes.count(b"MaterialExpressionConstant3Vector")
if noise_count < 1:
failures.append("terrain material should include a noise expression for color breakup")
if lerp_count < 1:
failures.append("terrain material should include a blend expression instead of a flat base color")
if color_count < 1:
failures.append("terrain material should include coastal scrub color vector expressions")
def main():
if not unreal.EditorLevelLibrary.load_level(MAP_PATH):
raise RuntimeError(f"Could not load map: {MAP_PATH}")
@@ -98,6 +120,7 @@ def main():
expected = MATERIALS["terrain"]
if assigned != expected:
failures.append(f"landscape material expected {expected}, got {assigned}")
verify_terrain_material_is_not_flat(materials["terrain"], failures)
foliage_actors = [actor for actor in actors if get_actor_label(actor) == FOLIAGE_LABEL]
if len(foliage_actors) != 1:
@@ -187,8 +210,10 @@ def main():
roadmap = unreal.Paths.convert_relative_path_to_full(unreal.Paths.project_dir()) + "AGRARIAN_DEVELOPMENT_ROADMAP.md"
for path, snippet in [
(docs, "asset variation"),
(docs, "procedural coastal scrub terrain material"),
(roadmap, "[x] Replace grey-box environment presentation with an MVP natural environment pass"),
(roadmap, "[x] Add first-pass environment asset variation"),
(roadmap, "[x] Replace or upgrade the terrain material first so Ground Zero no longer reads as flat tan placeholder ground."),
]:
with open(path, "r", encoding="utf-8") as handle:
text = handle.read()