test: harden jajce terrain navigation

This commit is contained in:
2026-07-08 15:47:01 +02:00
parent d2eca15ecd
commit cc0d6ef548
5 changed files with 71 additions and 3 deletions
+7 -2
View File
@@ -623,11 +623,16 @@ Completed:
6. `JajceWorld` runtime integration with bindings and regression coverage. 6. `JajceWorld` runtime integration with bindings and regression coverage.
7. Bounded Jajce beauty baseline with six terrain layers, water, atmosphere, 7. Bounded Jajce beauty baseline with six terrain layers, water, atmosphere,
foliage motion, building blockouts, and smoke. foliage motion, building blockouts, and smoke.
8. Base Terrain3D collision/navigation guardrails: runtime tests now keep
Terrain3D collision enabled, keep the old greybox ground out of physics, and
verify required navigation paths track the authored height field.
Next: Next:
1. Capture and visually review “Jajce Lookdev 01.” 1. Continue replacing the temporary greybox navigation source with baked
2. Add the cinematic/debug toggle and repeatable demo reset. Terrain3D-derived navigation for the sculpted playable loop.
2. Capture and visually review “Jajce Lookdev 01.”
3. Capture “Simulation Garden 01.” 3. Capture “Simulation Garden 01.”
Do not start with GIS data, a full city, a large asset pack, or more NPC Do not start with GIS data, a full city, a large asset pack, or more NPC
+4
View File
@@ -670,6 +670,10 @@ Recently completed:
- Activity-site capacity enforcement for rest, study, and patrol target - Activity-site capacity enforcement for rest, study, and patrol target
resolution, derived from NPC target claims rather than presentation-only resolution, derived from NPC target claims rather than presentation-only
counters. counters.
- Terrain3D collision/navigation hardening began: scaffold and runtime tests
now assert Terrain3D collision remains enabled, the hidden greybox ground no
longer provides physics collision, and tested navigation routes stay close to
the authored Terrain3D height field.
This order strengthens the simulation while regularly producing visible This order strengthens the simulation while regularly producing visible
progress suitable for public development updates. progress suitable for public development updates.
+8 -1
View File
@@ -193,7 +193,8 @@ study, and patrol target typed activity sites. The migration is documented in
- **Main scene:** `res://main.tscn` - **Main scene:** `res://main.tscn`
- **Terrain:** Terrain3D 1.0.2 is installed and enabled - **Terrain:** Terrain3D 1.0.2 is installed and enabled
- **Jajce runtime:** reusable 512 m Terrain3D seed, greybox landmarks, stable - **Jajce runtime:** reusable 512 m Terrain3D seed, greybox landmarks, stable
ResourceNode placement, lookdev camera, and tested runtime navigation ResourceNode placement, lookdev camera, tested runtime navigation, and base
Terrain3D collision/navigation guardrails
- **Terrain compatibility floor:** Godot 4.4 according to the bundled extension - **Terrain compatibility floor:** Godot 4.4 according to the bundled extension
- **Version control:** Git - **Version control:** Git
- **Primary branch:** `main` - **Primary branch:** `main`
@@ -328,6 +329,9 @@ distance with resource `safety_risk`, `comfort_distance`, and
- eight ResourceNodes preserving stable food/wood discovery IDs; - eight ResourceNodes preserving stable food/wood discovery IDs;
- typed village pantry storage and typed guard, study, and rest activity sites; - typed village pantry storage and typed guard, study, and rest activity sites;
- a temporary baked navigation loop with 24 tested routes; - a temporary baked navigation loop with 24 tested routes;
- base collision/navigation guardrails that keep Terrain3D collision enabled,
keep the hidden greybox ground out of runtime physics, and compare required
navigation paths against the Terrain3D height field;
- a look-development scene and camera. - a look-development scene and camera.
This is a runtime integration proof, not the final terrain composition. This is a runtime integration proof, not the final terrain composition.
@@ -534,6 +538,9 @@ These are expected prototype constraints, not necessarily isolated bugs:
mutation APIs, but selection, execution, target resolution, and active-world mutation APIs, but selection, execution, target resolution, and active-world
queries now have focused collaborators. queries now have focused collaborators.
- Path failure and interruption emit a `navigation_failed` signal and send the NPC to wander; this is functional but not yet polished. - Path failure and interruption emit a `navigation_failed` signal and send the NPC to wander; this is functional but not yet polished.
- The old greybox navigation source remains temporary; current tests now guard
against collision/nav regressions, but the next terrain base phase is a
Terrain3D-derived bake for the sculpted playable loop.
- Terrain3D and the bounded Jajce beauty baseline now run in the main game - Terrain3D and the bounded Jajce beauty baseline now run in the main game
scene; final visual review and character/profession readability remain. scene; final visual review and character/profession readability remain.
- Combat, companions, factions, politics, trade, rumours, quests, persistence, - Combat, companions, factions, politics, trade, rumours, quests, persistence,
+26
View File
@@ -26,6 +26,12 @@ func _run() -> void:
terrain.get_camera() == main_scene.get_node("CameraRig/Camera3D"), terrain.get_camera() == main_scene.get_node("CameraRig/Camera3D"),
"Runtime Terrain3D should bind to the gameplay camera" "Runtime Terrain3D should bind to the gameplay camera"
) )
_check(terrain.collision_mode != 0, "Runtime Terrain3D collision should be enabled")
var greybox_ground := main_scene.get_node("JajceWorld/NavigationRegion3D/GreyboxGround") as StaticBody3D
_check(
greybox_ground.collision_layer == 0 and greybox_ground.collision_mask == 0,
"Runtime GreyboxGround should not provide physics collision"
)
_check( _check(
( (
not main_scene.has_node("World") not main_scene.has_node("World")
@@ -73,6 +79,9 @@ func _run() -> void:
not path.is_empty(), not path.is_empty(),
"Navigation path should exist from %s to %s" % [origin, destination] "Navigation path should exist from %s to %s" % [origin, destination]
) )
_check_path_tracks_terrain(
terrain, path, "Runtime navigation path should track Terrain3D height"
)
var village := SimVillage.new() var village := SimVillage.new()
var worker := SimNPC.new(100, "BaselineWorker", SimulationIds.PROFESSION_SCHOLAR, 5.0, 5.0) var worker := SimNPC.new(100, "BaselineWorker", SimulationIds.PROFESSION_SCHOLAR, 5.0, 5.0)
@@ -126,3 +135,20 @@ func _wait_for_navigation_map(navigation_map: RID) -> void:
func _check(condition: bool, message: String) -> void: func _check(condition: bool, message: String) -> void:
if not condition: if not condition:
failures.append(message) failures.append(message)
func _check_path_tracks_terrain(terrain: Terrain3D, path: PackedVector3Array, message: String) -> void:
if path.is_empty():
return
for point in path:
var terrain_height: float = terrain.data.get_height(point)
if is_nan(terrain_height):
failures.append("%s: missing terrain height at %s" % [message, point])
return
if absf(point.y - terrain_height) > 0.85:
failures.append(
"%s: path point %s is too far from terrain height %.2f"
% [message, point, terrain_height]
)
return
+26
View File
@@ -26,6 +26,12 @@ func _run() -> void:
terrain.data_directory == "res://terrain/jajce/data", terrain.data_directory == "res://terrain/jajce/data",
"Terrain3D should use dedicated Jajce data" "Terrain3D should use dedicated Jajce data"
) )
_check(terrain.collision_mode != 0, "Terrain3D collision should be enabled for gameplay")
var greybox_ground := world.get_node("NavigationRegion3D/GreyboxGround") as StaticBody3D
_check(
greybox_ground.collision_layer == 0 and greybox_ground.collision_mask == 0,
"GreyboxGround should not provide runtime physics collision"
)
_check( _check(
configured_texture_count == 6, configured_texture_count == 6,
( (
@@ -215,6 +221,9 @@ func _run() -> void:
not path.is_empty(), not path.is_empty(),
"Jajce navigation path should exist from %s to %s" % [origin, destination] "Jajce navigation path should exist from %s to %s" % [origin, destination]
) )
_check_path_tracks_terrain(
terrain, path, "Jajce navigation path should track Terrain3D height"
)
var adapter := ActiveWorldAdapter.new() var adapter := ActiveWorldAdapter.new()
adapter.pantry_storage = pantry adapter.pantry_storage = pantry
@@ -304,3 +313,20 @@ func _wait_for_navigation_map(navigation_map: RID) -> void:
func _check(condition: bool, message: String) -> void: func _check(condition: bool, message: String) -> void:
if not condition: if not condition:
failures.append(message) failures.append(message)
func _check_path_tracks_terrain(terrain: Terrain3D, path: PackedVector3Array, message: String) -> void:
if path.is_empty():
return
for point in path:
var terrain_height: float = terrain.data.get_height(point)
if is_nan(terrain_height):
failures.append("%s: missing terrain height at %s" % [message, point])
return
if absf(point.y - terrain_height) > 0.85:
failures.append(
"%s: path point %s is too far from terrain height %.2f"
% [message, point, terrain_height]
)
return