Replace template meshes with native placeholders

This commit is contained in:
2026-05-16 02:45:00 -07:00
parent f78c552d30
commit b5d13598f8
18 changed files with 229 additions and 37 deletions
+1 -1
View File
@@ -458,7 +458,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
- [x] Add water source.
- [x] Replace grey-box environment presentation with an MVP natural environment pass: terrain material, grass, shrubs, bushes, trees, water-source visuals, and clearer Ground Zero biome dressing. Added repeatable Ground Zero environment material generation, applied terrain/foliage/resource/water materials in the map setup, regenerated the demo map, documented the pass, and added verification for material assets plus map assignments.
- [x] Add first-pass environment asset variation so trees, bushes, grass, resource nodes, and water do not read as repeated placeholders. Added repeatable labeled variation actors for tree canopies/trunks, rounded bushes, grass mats, rock slabs, and a freshwater surface using multiple prototype mesh silhouettes, unique scales, rotations, and Ground Zero material families; extended the natural-environment verifier to require variation coverage.
- [ ] Replace `LevelPrototyping` cube/cylinder mesh dependencies in Agrarian setup scripts and prototype Blueprints with Agrarian-native placeholder environment meshes.
- [x] Replace `LevelPrototyping` cube/cylinder mesh dependencies in Agrarian setup scripts and prototype Blueprints with Agrarian-native placeholder environment meshes. Added repeatable Agrarian-native placeholder mesh assets under `/Game/Agrarian/Environment/PlaceholderMeshes`, updated playable Blueprint and Ground Zero setup scripts to use those native paths, regenerated affected Blueprints/map content, and added verification that Blueprint, foliage, and variation meshes no longer point at template mesh paths.
- [ ] Add weather exposure zones if needed.
- [ ] Add landmark or ruin placeholder.
- [ ] Add spawn area with validation that the player spawns above sea level, above terrain by a safe offset, away from water, away from steep slopes, away from dense resource clusters, and with a known safe fallback coordinate.
Binary file not shown.
Binary file not shown.
+9 -8
View File
@@ -21,9 +21,9 @@ Keep temporarily:
- `ThirdPerson` compatibility assets only while references are audited. The
active player, controller, and game mode Blueprint paths now live under
`/Game/Agrarian/Blueprints/Characters`.
- `LevelPrototyping` meshes needed by current setup scripts and prototype
Blueprints until the version 0.1 environment pass replaces them with
Agrarian-native placeholder meshes.
- Source `LevelPrototyping` meshes may remain only as copy sources for
repeatable setup scripts. Active setup outputs now use Agrarian-native
placeholder mesh assets under `/Game/Agrarian/Environment/PlaceholderMeshes`.
- Shared mannequin/animation content only where current Agrarian assets still
reference it.
@@ -48,8 +48,8 @@ Cleanup performed:
4. Disabled `StateTree` and `GameplayStateTree` in `AgrarianGame.uproject`.
5. Replaced the current `ThirdPerson` player/game mode paths with Agrarian-owned
player, controller, and game mode Blueprint assets.
6. Kept the current `LevelPrototyping` mesh path until the Ground Zero
environment pass replaces the simple cube/cylinder placeholder dependencies.
6. Replaced the active `LevelPrototyping` cube/cylinder placeholder dependencies
with Agrarian-native placeholder mesh assets.
## Current State
@@ -60,6 +60,7 @@ version `0.01` project-structure completion:
- `ThirdPerson` active player/game mode paths have been replaced. Remaining
ThirdPerson compatibility assets should be deleted only after redirector and
editor reference checks confirm they are unused.
- `LevelPrototyping` stays because current Agrarian setup scripts and prototype
Blueprints still use its simple cube/cylinder meshes. This is now tracked as
version `0.1` environment replacement work.
- Active setup scripts, prototype Blueprints, and the Ground Zero map now use
Agrarian-native placeholder mesh assets. `LevelPrototyping` can be removed
after later reference audits confirm no editor-only copy-source path is still
needed.
@@ -14,8 +14,19 @@ space.
- First-pass asset variation actors add additional tree canopies/trunks, rounded
bushes, grass mats, rock slabs, and a visible freshwater surface with varied
meshes, scales, rotations, and material families.
- The foliage and asset variation layers now use Agrarian-native placeholder
mesh assets from `/Game/Agrarian/Environment/PlaceholderMeshes` instead of
direct `LevelPrototyping` mesh references.
- The setup remains repeatable through `Scripts/setup_ground_zero_demo_map.py`.
## Placeholder Mesh Assets
- `/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_Cube`
- `/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_ChamferCube`
- `/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_Cylinder`
- `/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_QuarterCylinder`
- `/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_Plane`
## Material Assets
- `/Game/Agrarian/Materials/M_AGR_GZ_Terrain_CoastalScrub`
@@ -35,11 +46,15 @@ the expected instance counts and material assignments, and resource/water actors
are visually dressed. It also checks the first-pass asset variation layer:
eleven labeled variation actors, at least four mesh silhouettes, unique scale
profiles, and coverage across tree, bush, grass, rock, and water visual
families.
families. `Scripts/verify_native_placeholder_meshes.py` checks that playable
resource/structure/water Blueprints, foliage components, and environment
variation actors use Agrarian-native placeholder meshes rather than template
mesh paths.
## Follow-Up
This pass deliberately keeps the current prototype geometry so it stays small
and stable. Later environment work should replace these prototype silhouettes
with region-appropriate sourced or custom meshes, then drive density and shape
selection from land-cover, slope, hydrography, and biome data.
and stable. Later environment work should replace the Agrarian-native
placeholder silhouettes with region-appropriate sourced or custom meshes, then
drive density and shape selection from land-cover, slope, hydrography, and biome
data.
+35 -8
View File
@@ -17,17 +17,29 @@ LANDSCAPE_MIN_XY = -50000.0
FOLIAGE_LABEL = "AGR_GroundZeroFoliage_FirstPass"
FOLIAGE_RANDOM_SEED = 4160544
PLACEHOLDER_MESH_FOLDER = "/Game/Agrarian/Environment/PlaceholderMeshes"
PLACEHOLDER_MESH_SOURCES = {
"SM_AGR_Placeholder_Cube": "/Game/LevelPrototyping/Meshes/SM_Cube",
"SM_AGR_Placeholder_ChamferCube": "/Game/LevelPrototyping/Meshes/SM_ChamferCube",
"SM_AGR_Placeholder_Cylinder": "/Game/LevelPrototyping/Meshes/SM_Cylinder",
"SM_AGR_Placeholder_QuarterCylinder": "/Game/LevelPrototyping/Meshes/SM_QuarterCylinder",
"SM_AGR_Placeholder_Plane": "/Game/LevelPrototyping/Meshes/SM_Plane",
}
PLACEHOLDER_MESHES = {
name: f"{PLACEHOLDER_MESH_FOLDER}/{name}"
for name in PLACEHOLDER_MESH_SOURCES
}
FOLIAGE_MESHES = {
"tree": "/Game/LevelPrototyping/Meshes/SM_Cylinder",
"shrub": "/Game/LevelPrototyping/Meshes/SM_Cube",
"grass": "/Game/LevelPrototyping/Meshes/SM_Cylinder",
"tree": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cylinder"],
"shrub": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cube"],
"grass": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cylinder"],
}
VARIATION_MESHES = {
"cube": "/Game/LevelPrototyping/Meshes/SM_Cube",
"chamfer_cube": "/Game/LevelPrototyping/Meshes/SM_ChamferCube",
"cylinder": "/Game/LevelPrototyping/Meshes/SM_Cylinder",
"quarter_cylinder": "/Game/LevelPrototyping/Meshes/SM_QuarterCylinder",
"plane": "/Game/LevelPrototyping/Meshes/SM_Plane",
"cube": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cube"],
"chamfer_cube": PLACEHOLDER_MESHES["SM_AGR_Placeholder_ChamferCube"],
"cylinder": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cylinder"],
"quarter_cylinder": PLACEHOLDER_MESHES["SM_AGR_Placeholder_QuarterCylinder"],
"plane": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Plane"],
}
MATERIAL_FOLDER = "/Game/Agrarian/Materials"
ENVIRONMENT_MATERIALS = {
@@ -405,6 +417,19 @@ def load_required_asset(path):
return asset
def ensure_native_placeholder_meshes():
unreal.EditorAssetLibrary.make_directory(PLACEHOLDER_MESH_FOLDER)
for asset_name, source_path in PLACEHOLDER_MESH_SOURCES.items():
destination_path = f"{PLACEHOLDER_MESH_FOLDER}/{asset_name}"
if unreal.EditorAssetLibrary.does_asset_exist(destination_path):
continue
if not unreal.EditorAssetLibrary.duplicate_asset(source_path, destination_path):
raise RuntimeError(f"Could not create native placeholder mesh {destination_path} from {source_path}")
unreal.EditorAssetLibrary.save_asset(destination_path)
unreal.log(f"Created native placeholder mesh: {destination_path}")
def ensure_environment_materials():
if not unreal.EditorAssetLibrary.does_directory_exist(MATERIAL_FOLDER):
unreal.EditorAssetLibrary.make_directory(MATERIAL_FOLDER)
@@ -669,6 +694,8 @@ def main():
if not unreal.EditorLevelLibrary.load_level(MAP_PATH):
raise RuntimeError(f"Could not load map: {MAP_PATH}")
ensure_native_placeholder_meshes()
labels = {spec["label"] for spec in DEMO_ACTORS}
labels.update(LEGACY_DEMO_LIGHTING_LABELS)
labels.update(spec["label"] for spec in BIOME_RESOURCE_ACTORS)
+26 -2
View File
@@ -11,8 +11,17 @@ WOOD_ITEM_PATH = "/Game/Agrarian/DataAssets/Items/DA_Item_Wood"
FIBER_ITEM_PATH = "/Game/Agrarian/DataAssets/Items/DA_Item_Fiber"
STONE_ITEM_PATH = "/Game/Agrarian/DataAssets/Items/DA_Item_Stone"
MESH_CUBE_PATH = "/Game/LevelPrototyping/Meshes/SM_Cube"
MESH_CYLINDER_PATH = "/Game/LevelPrototyping/Meshes/SM_Cylinder"
PLACEHOLDER_MESH_FOLDER = "/Game/Agrarian/Environment/PlaceholderMeshes"
PLACEHOLDER_MESH_SOURCES = {
"SM_AGR_Placeholder_Cube": "/Game/LevelPrototyping/Meshes/SM_Cube",
"SM_AGR_Placeholder_Cylinder": "/Game/LevelPrototyping/Meshes/SM_Cylinder",
}
PLACEHOLDER_MESHES = {
name: f"{PLACEHOLDER_MESH_FOLDER}/{name}"
for name in PLACEHOLDER_MESH_SOURCES
}
MESH_CUBE_PATH = PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cube"]
MESH_CYLINDER_PATH = PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cylinder"]
BLUEPRINTS = [
{
@@ -125,6 +134,19 @@ def load_required_asset(path):
return asset
def ensure_native_placeholder_meshes():
unreal.EditorAssetLibrary.make_directory(PLACEHOLDER_MESH_FOLDER)
for asset_name, source_path in PLACEHOLDER_MESH_SOURCES.items():
destination_path = f"{PLACEHOLDER_MESH_FOLDER}/{asset_name}"
if unreal.EditorAssetLibrary.does_asset_exist(destination_path):
continue
if not unreal.EditorAssetLibrary.duplicate_asset(source_path, destination_path):
raise RuntimeError(f"Could not create native placeholder mesh {destination_path} from {source_path}")
unreal.EditorAssetLibrary.save_asset(destination_path)
unreal.log(f"Created native placeholder mesh: {destination_path}")
def create_or_load_blueprint(asset_name, folder, parent_class):
path = f"{folder}/{asset_name}"
existing = unreal.EditorAssetLibrary.load_asset(path)
@@ -177,6 +199,8 @@ def apply_defaults(blueprint, config):
def main():
ensure_native_placeholder_meshes()
for folder in (RESOURCE_FOLDER, STRUCTURE_FOLDER, WILDLIFE_FOLDER, WORLD_FOLDER):
unreal.EditorAssetLibrary.make_directory(folder)
+110
View File
@@ -0,0 +1,110 @@
from pathlib import Path
import unreal
MAP_PATH = "/Game/Agrarian/Maps/L_GroundZeroTerrain_Test"
PROJECT_ROOT = Path(unreal.Paths.convert_relative_path_to_full(unreal.Paths.project_dir()))
PLACEHOLDER_MESH_FOLDER = "/Game/Agrarian/Environment/PlaceholderMeshes"
PLACEHOLDER_MESHES = {
"SM_AGR_Placeholder_Cube",
"SM_AGR_Placeholder_ChamferCube",
"SM_AGR_Placeholder_Cylinder",
"SM_AGR_Placeholder_QuarterCylinder",
"SM_AGR_Placeholder_Plane",
}
BLUEPRINT_PATHS = [
"/Game/Agrarian/Blueprints/Resources/BP_WoodResourceNode",
"/Game/Agrarian/Blueprints/Resources/BP_FiberResourceNode",
"/Game/Agrarian/Blueprints/Resources/BP_StoneResourceNode",
"/Game/Agrarian/Blueprints/Structures/BP_Campfire",
"/Game/Agrarian/Blueprints/Structures/BP_PrimitiveShelter",
"/Game/Agrarian/Blueprints/World/BP_FreshWaterSource",
]
DOC_SNIPPETS = [
(PROJECT_ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md", "[x] Replace `LevelPrototyping` cube/cylinder mesh dependencies"),
(PROJECT_ROOT / "Docs" / "Terrain" / "GroundZeroNaturalEnvironmentPass.md", "Agrarian-native placeholder"),
]
def asset_path(asset):
if not asset:
return ""
return asset.get_path_name().split(".", 1)[0]
def get_actor_label(actor):
try:
return actor.get_actor_label()
except Exception:
return actor.get_name()
def assert_native_mesh(path, failures):
if not path.startswith(PLACEHOLDER_MESH_FOLDER):
failures.append(f"expected native Agrarian placeholder mesh, got {path}")
if "LevelPrototyping" in path:
failures.append(f"template mesh reference remains: {path}")
def main():
failures = []
for mesh_name in PLACEHOLDER_MESHES:
mesh_path = f"{PLACEHOLDER_MESH_FOLDER}/{mesh_name}"
if not unreal.EditorAssetLibrary.does_asset_exist(mesh_path):
failures.append(f"missing native placeholder mesh asset: {mesh_path}")
for blueprint_path in BLUEPRINT_PATHS:
generated_class = unreal.EditorAssetLibrary.load_blueprint_class(blueprint_path)
if not generated_class:
failures.append(f"could not load Blueprint class: {blueprint_path}")
continue
cdo = unreal.get_default_object(generated_class)
mesh_component = cdo.get_editor_property("mesh")
assert_native_mesh(asset_path(mesh_component.get_editor_property("static_mesh")), failures)
if not unreal.EditorLevelLibrary.load_level(MAP_PATH):
failures.append(f"could not load map: {MAP_PATH}")
else:
actors = unreal.EditorLevelLibrary.get_all_level_actors()
foliage_actors = [
actor
for actor in actors
if get_actor_label(actor) == "AGR_GroundZeroFoliage_FirstPass"
]
if len(foliage_actors) != 1:
failures.append(f"expected one AGR_GroundZeroFoliage_FirstPass, found {len(foliage_actors)}")
else:
for property_name in ("tree_instances", "shrub_instances", "grass_instances"):
component = foliage_actors[0].get_editor_property(property_name)
assert_native_mesh(asset_path(component.get_editor_property("static_mesh")), failures)
for actor in actors:
label = get_actor_label(actor)
if not label.startswith("AGR_GZ_EnvVar_"):
continue
mesh_components = actor.get_components_by_class(unreal.StaticMeshComponent)
if not mesh_components:
failures.append(f"{label} has no static mesh component")
continue
assert_native_mesh(asset_path(mesh_components[0].get_editor_property("static_mesh")), failures)
for path, snippet in DOC_SNIPPETS:
text = path.read_text(encoding="utf-8")
if snippet not in text:
failures.append(f"{path} missing `{snippet}`")
if failures:
raise RuntimeError("Native placeholder mesh verification failed: " + "; ".join(failures))
unreal.log(
"Native placeholder mesh verification complete: "
f"{len(PLACEHOLDER_MESHES)} Agrarian mesh assets, {len(BLUEPRINT_PATHS)} Blueprint meshes, "
"foliage meshes, and environment variation meshes use native paths."
)
main()