From 05838406d0e627bf54da175b069184dd937ddf8c Mon Sep 17 00:00:00 2001 From: Voyager Date: Mon, 24 Aug 2026 16:31:55 -0400 Subject: [PATCH] Refresh generated terrain assets and projection --- tests/generated_world_runtime_validation.gd | 85 +++++++++++++++--- tools/blender/export_terrain_chunks.py | 68 ++++++++++++-- world/generation/chunks/assets/chunk_0000.glb | Bin 7700 -> 7700 bytes world/generation/chunks/assets/chunk_0001.glb | Bin 7660 -> 7660 bytes world/generation/chunks/assets/chunk_0002.glb | Bin 14472 -> 14472 bytes world/generation/chunks/assets/chunk_0003.glb | Bin 24372 -> 24348 bytes world/generation/chunks/assets/chunk_0004.glb | Bin 27748 -> 25744 bytes world/generation/chunks/assets/chunk_0005.glb | Bin 17972 -> 20480 bytes world/generation/chunks/assets/chunk_0006.glb | Bin 33760 -> 33952 bytes world/generation/chunks/assets/chunk_0007.glb | Bin 19004 -> 19004 bytes world/generation/chunks/assets/chunk_0008.glb | Bin 17996 -> 17996 bytes world/generation/chunks/assets/chunk_0009.glb | Bin 7700 -> 7700 bytes world/generation/chunks/assets/chunk_0010.glb | Bin 19908 -> 20036 bytes world/generation/chunks/assets/chunk_0011.glb | Bin 19428 -> 19420 bytes world/generation/chunks/assets/chunk_0012.glb | Bin 30804 -> 30816 bytes world/generation/chunks/assets/chunk_0013.glb | Bin 27908 -> 27908 bytes world/generation/chunks/assets/chunk_0014.glb | Bin 19420 -> 19420 bytes world/generation/chunks/assets/chunk_0015.glb | Bin 19908 -> 20040 bytes world/generation/chunks/assets/chunk_0016.glb | Bin 26224 -> 26200 bytes world/generation/chunks/assets/chunk_0017.glb | Bin 27692 -> 27632 bytes world/generation/chunks/assets/chunk_0018.glb | Bin 26616 -> 26528 bytes world/generation/chunks/assets/chunk_0019.glb | Bin 6996 -> 6972 bytes world/generation/chunks/assets/chunk_0020.glb | Bin 25916 -> 25852 bytes world/generation/chunks/assets/chunk_0021.glb | Bin 25860 -> 26724 bytes world/generation/chunks/assets/chunk_0022.glb | Bin 25988 -> 26292 bytes world/generation/chunks/assets/chunk_0023.glb | Bin 25988 -> 26292 bytes world/generation/chunks/assets/chunk_0024.glb | Bin 28540 -> 28544 bytes world/generation/chunks/assets/chunk_0025.glb | Bin 30292 -> 30760 bytes world/generation/chunks/assets/chunk_0026.glb | Bin 19988 -> 19980 bytes world/generation/chunks/assets/chunk_0027.glb | Bin 21096 -> 21160 bytes world/generation/chunks/assets/chunk_0028.glb | Bin 7700 -> 7700 bytes world/generation/chunks/assets/chunk_0029.glb | Bin 36656 -> 36644 bytes world/generation/chunks/assets/chunk_0030.glb | Bin 36656 -> 36648 bytes world/generation/chunks/assets/chunk_0031.glb | Bin 37872 -> 38052 bytes world/generation/chunks/assets/chunk_0032.glb | Bin 37868 -> 38052 bytes world/generation/chunks/assets/chunk_0033.glb | Bin 25700 -> 25700 bytes world/generation/chunks/assets/chunk_0034.glb | Bin 30684 -> 30680 bytes world/generation/chunks/assets/chunk_0035.glb | Bin 30304 -> 30292 bytes world/generation/chunks/assets/chunk_0036.glb | Bin 29524 -> 29524 bytes world/generation/chunks/assets/chunk_0037.glb | Bin 27692 -> 27592 bytes world/generation/generated_world_region.gd | 64 ++++++------- world/generation/props/assets/prop_palm.glb | Bin 26408 -> 26244 bytes world/generation/props/assets/prop_pine.glb | Bin 9408 -> 10168 bytes .../props/assets/prop_pine_large.glb | Bin 9624 -> 9500 bytes world/generation/props/assets/prop_tree_1.glb | Bin 8416 -> 8416 bytes world/generation/props/assets/prop_tree_2.glb | Bin 6324 -> 6328 bytes world/generation/props/assets/prop_tree_3.glb | Bin 7316 -> 7284 bytes .../props/assets/prop_tree_large.glb | Bin 4304 -> 13200 bytes .../generation/terrain_chunk_edge_profile.gd | 38 ++++++++ world/generation/terrain_chunk_generator.gd | 67 +++++++++++++- world/materials/generated_terrain_dirt.tres | 16 ++++ world/materials/generated_terrain_grass.tres | 16 ++++ world/materials/generated_terrain_sand.tres | 16 ++++ 53 files changed, 319 insertions(+), 51 deletions(-) create mode 100644 world/materials/generated_terrain_dirt.tres create mode 100644 world/materials/generated_terrain_grass.tres create mode 100644 world/materials/generated_terrain_sand.tres diff --git a/tests/generated_world_runtime_validation.gd b/tests/generated_world_runtime_validation.gd index b8ea847..ef933bf 100644 --- a/tests/generated_world_runtime_validation.gd +++ b/tests/generated_world_runtime_validation.gd @@ -481,6 +481,9 @@ func _validate_generated_region( assert(river_body_count == river_placement_count) _validate_authored_chunk_surfaces(region, generator) + _validate_projected_terrain_materials( + generator.get_generated_chunks_root() + ) var decorations := region.get_node("Decorations") as Node3D var anchors := region.get_node( @@ -1147,21 +1150,28 @@ func _validate_tree_gatherable_anchors( anchors: GatherableAnchorSet3D, ) -> void: var eligible_props: Dictionary[StringName, Node3D] = {} + var expected_anchor_count := 0 for child: Node in decorations.get_children(): var prop := child as Node3D if prop == null: continue var prop_id := StringName(prop.get_meta(&"terrain_prop_id", &"")) var definition := region.get_prop_catalog().definition_for_id(prop_id) - if definition != null and definition.has_gatherable_surface(): + var socket_count := _count_authored_beetle_sockets(prop) + if ( + definition != null + and definition.has_gatherable_surface() + and socket_count > 0 + ): eligible_props[prop.name] = prop + expected_anchor_count += socket_count var positions := anchors.get_spawn_positions() - assert(positions.size() == eligible_props.size()) + assert(positions.size() == expected_anchor_count) assert(positions.size() >= 12) for child: Node in anchors.get_children(): var anchor := child as Marker3D assert(anchor != null) - assert(bool(anchor.get_meta(&"mesh_surface_sampled", false))) + assert(bool(anchor.get_meta(&"authored_beetle_socket", false))) var prop_name := StringName(anchor.get_meta(&"terrain_prop_name", &"")) assert(eligible_props.has(prop_name)) var prop: Node3D = eligible_props[prop_name] @@ -1169,14 +1179,17 @@ func _validate_tree_gatherable_anchors( var definition := region.get_prop_catalog().definition_for_id(prop_id) assert(definition != null and definition.has_gatherable_surface()) var local_anchor := prop.to_local(anchor.global_position) - assert( - local_anchor.y - >= definition.gatherable_surface_minimum_height - 0.001 - ) - assert( - local_anchor.y - <= definition.gatherable_surface_maximum_height + 0.001 - ) + assert(prop_id != &"prop_palm") + assert(local_anchor.y >= 0.25 and local_anchor.y <= 2.0) + + +func _count_authored_beetle_sockets(root_node: Node) -> int: + var result := 0 + if String(root_node.name).to_lower().contains("beetle_socket"): + result += 1 + for child: Node in root_node.get_children(): + result += _count_authored_beetle_sockets(child) + return result func _find_mesh_instance(root_node: Node) -> MeshInstance3D: @@ -1357,6 +1370,56 @@ func _material_names(mesh_instance: MeshInstance3D) -> PackedStringArray: return result +func _validate_projected_terrain_materials(root: Node) -> void: + var expected_sizes := { + "grass_lite": 1.75, + "sand": 2.6, + "dirt": 2.5, + } + var surface_counts := { + "grass_lite": 0, + "sand": 0, + "dirt": 0, + } + _validate_projected_material_node(root, expected_sizes, surface_counts) + for material_name: String in expected_sizes: + assert(surface_counts[material_name] > 0) + + +func _validate_projected_material_node( + root: Node, + expected_sizes: Dictionary, + surface_counts: Dictionary, +) -> void: + var mesh_instance := root as MeshInstance3D + if mesh_instance != null and mesh_instance.mesh != null: + for surface_index: int in mesh_instance.mesh.get_surface_count(): + var material := mesh_instance.get_active_material(surface_index) + if material == null or not expected_sizes.has(material.resource_name): + continue + var shader_material := material as ShaderMaterial + assert(shader_material != null and shader_material.shader != null) + assert( + shader_material.shader.resource_path + == "res://world/materials/terrain_surface_projection.gdshader" + ) + assert( + is_equal_approx( + float(shader_material.get_shader_parameter( + &"tile_world_size" + )), + float(expected_sizes[material.resource_name]), + ) + ) + surface_counts[material.resource_name] += 1 + for child: Node in root.get_children(): + _validate_projected_material_node( + child, + expected_sizes, + surface_counts, + ) + + func _validate_pond_collision( region: GeneratedWorldRegion, pond: MeshInstance3D, diff --git a/tools/blender/export_terrain_chunks.py b/tools/blender/export_terrain_chunks.py index 2c35982..ced081e 100644 --- a/tools/blender/export_terrain_chunks.py +++ b/tools/blender/export_terrain_chunks.py @@ -10,10 +10,11 @@ Run through Blender rather than a standalone Python interpreter: Collections use ``chunk_####_description`` and contain one primary terrain mesh named ``chunk_####``. Additional production objects may live in the same collection. Reusable procedural props use individual ``prop_description`` -mesh objects. They may be arranged anywhere in the source file because each -object's authored origin becomes the exported runtime anchor. A same-named -``prop_description`` collection remains supported for multi-object props. -Unrelated objects and collections are ignored. +mesh objects. Their child empties are exported with them so authored sockets +remain attached to the prop. Props may be arranged anywhere in the source file +because each root object's authored origin becomes the exported runtime anchor. +A same-named ``prop_description`` collection remains supported for multi-object +props. Unrelated objects and collections are ignored. """ from __future__ import annotations @@ -43,6 +44,12 @@ PROP_COLLECTION_PATTERN = re.compile( r"^prop_(?P