Upgrade Ground Zero vegetation assets

This commit is contained in:
2026-05-21 15:43:14 +00:00
parent 98ab61a7a4
commit dd3d247539
20 changed files with 341 additions and 41 deletions
@@ -19,6 +19,20 @@ EXPECTED_FOLIAGE_COUNTS = {
"shrubs": 220,
"grass": 420,
}
EXPECTED_FOLIAGE_MESHES = {
"tree_instances": "/Game/Agrarian/Environment/Vegetation/SM_AGR_GZ_CoastalOak_Proxy",
"shrub_instances": "/Game/Agrarian/Environment/Vegetation/SM_AGR_GZ_CoyoteBrush_Proxy",
"grass_instances": "/Game/Agrarian/Environment/Vegetation/SM_AGR_GZ_DryGrassClump_Proxy",
}
EXPECTED_FOLIAGE_CULL_DISTANCES = {
"tree_instances": (65000, 95000),
"shrub_instances": (28000, 52000),
"grass_instances": (9000, 22000),
}
FORBIDDEN_FOLIAGE_MESH_PREFIXES = (
"/Engine/BasicShapes/",
"/Game/LevelPrototyping/",
)
RESOURCE_MATERIALS = {
"AGR_GZ_Wood": "wood_resource",
"AGR_GZ_Fiber": "fiber_resource",
@@ -61,6 +75,29 @@ def material_path(material):
return material.get_path_name().split(".", 1)[0]
def asset_path(asset):
if not asset:
return ""
return asset.get_path_name().split(".", 1)[0]
def get_component_property(component, property_name, default=None):
try:
return component.get_editor_property(property_name)
except Exception:
return getattr(component, property_name, default)
def static_mesh_vertex_count(mesh):
library = getattr(unreal, "EditorStaticMeshLibrary", None)
if not mesh or not library:
return -1
try:
return library.get_number_verts(mesh, 0)
except Exception:
return -1
def material_key_for_label(label):
for prefix, material_key in RESOURCE_MATERIALS.items():
if label.startswith(prefix):
@@ -104,6 +141,22 @@ def verify_terrain_material_is_not_flat(material, failures):
failures.append("terrain material should include coastal scrub color vector expressions")
def verify_foliage_material_has_variation(material_path_name, failures):
project_root = unreal.Paths.convert_relative_path_to_full(unreal.Paths.project_dir())
package_path = project_root + "Content" + material_path_name.replace("/Game", "") + ".uasset"
try:
with open(package_path, "rb") as handle:
package_bytes = handle.read()
except Exception as exc:
failures.append(f"could not inspect foliage material package {material_path_name}: {exc}")
return
if package_bytes.count(b"MaterialExpressionLinearInterpolate") < 1:
failures.append(f"{material_path_name} should blend foliage color variation")
if package_bytes.count(b"MaterialExpressionConstant3Vector") < 1:
failures.append(f"{material_path_name} should include foliage color vectors")
def main():
if not unreal.EditorLevelLibrary.load_level(MAP_PATH):
raise RuntimeError(f"Could not load map: {MAP_PATH}")
@@ -143,10 +196,31 @@ def main():
}
for property_name, material_key in component_expectations.items():
component = foliage.get_editor_property(property_name)
static_mesh = component.get_editor_property("static_mesh")
mesh_path = asset_path(static_mesh)
expected_mesh = EXPECTED_FOLIAGE_MESHES[property_name]
if mesh_path != expected_mesh:
failures.append(f"{property_name} mesh expected {expected_mesh}, got {mesh_path}")
if mesh_path.startswith(FORBIDDEN_FOLIAGE_MESH_PREFIXES):
failures.append(f"{property_name} still uses placeholder/basic mesh {mesh_path}")
vertex_count = static_mesh_vertex_count(static_mesh)
if vertex_count <= 0:
failures.append(f"{property_name} mesh {mesh_path} has no renderable vertices")
assigned = material_path(component.get_material(0))
expected = MATERIALS[material_key]
if assigned != expected:
failures.append(f"{property_name} material expected {expected}, got {assigned}")
verify_foliage_material_has_variation(expected, failures)
start_cull, end_cull = EXPECTED_FOLIAGE_CULL_DISTANCES[property_name]
actual_start = int(get_component_property(component, "instance_start_cull_distance", -1))
actual_end = int(get_component_property(component, "instance_end_cull_distance", -1))
if actual_start != start_cull or actual_end != end_cull:
failures.append(
f"{property_name} cull distances expected {start_cull}/{end_cull}, "
f"got {actual_start}/{actual_end}"
)
checked_resource_actors = 0
for actor in actors:
@@ -211,9 +285,11 @@ def main():
for path, snippet in [
(docs, "asset variation"),
(docs, "procedural coastal scrub terrain material"),
(docs, "native low-poly coastal scrub vegetation meshes"),
(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."),
(roadmap, "[x] Replace or upgrade grasses, shrubs, and trees with believable coastal-scrub vegetation assets"),
]:
with open(path, "r", encoding="utf-8") as handle:
text = handle.read()