OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / maps / dc_world_setup.gd

89 lines · gd ref:
class_name DCWorldSetup
extends RefCounted
## Shared DCTerrain configuration — level-mesh-implementation.md §5a.1.
##
## Extracted from map_dc_terrain.gd when map_dc_world became a second scene that
## instantiates a DCTerrain. The values are not arbitrary and the grid-align one
## is a CONTRACT with the importer, so a second hand-typed copy of it is a silent
## divergence waiting to happen: nothing would error, the level would simply stop
## landing on voxel boundaries and start wobbling.


## Quake's grid is 8 units. voxel_size = 8 * world_scale(0.028) = 0.224 m, so an
## axis-aligned brush face authored on that grid lands EXACTLY on a voxel
## boundary and DC resolves it with no sub-voxel wobble — 91.7% of START's faces.
##
## MUST MATCH the importer's `vox` (quake_map_importer.gd: `world_scale * 8.0`),
## which is what the map centre and the world offset are snapped to. Change one
## without the other and the alignment this buys is gone.
const VOXEL_SIZE := 0.224

## How far out colliders are generated. Beyond this the terrain renders but the
## player falls through it.
const COLLISION_DISTANCE := 200.0

## Octree splits admitted per frame. The cap is what keeps a settling clipmap
## from stalling the frame it settles in.
const MAX_SPLITS_PER_FRAME := 16


## The generator settings map_dc_terrain.tscn carries on its DCTerrain node.
##
## THESE WERE THE WHOLE BUG. map_dc_world.tscn was authored by hand as a bare
## `[node name="DCTerrain" type="DCTerrain" parent="."]`, and this helper was
## created to stop a second scene diverging — but it only ever held the three
## SCRIPT-side values, while eleven more sat in the .tscn and were never copied.
## So map_dc_world generated a different world from defaults AND, fatally,
## use_gpu stayed at its default of false:
##
##     libdc/include/dc_density.h:114   bool use_gpu = false;  // default off
##
## `req.gpu_eligible = config_.use_gpu && ...` (dc_terrain.cpp:931, 1321), so
## every chunk meshed on the CPU and the GPU thread was never created. That is
## survivable for plain noise. It is not survivable with the §5a.2 shell: the mesh
## SDF costs ~4587 ns/sample (tests/test_mesh_sdf_bench.cpp), which is 150 ms per
## 32^3 chunk and 1.2 s per 64^3, so the terrain around the level takes minutes to
## catch up and what you see is geometry meshed before the subtract existed.
##
## Reported as "the terrain was inside the start.map", and it survived reverting
## every commit — because it was never in a commit. It was a missing node
## property, from the first version of the scene.
const GENERATOR := {
	"use_gpu": true,
	"seed": 44,
	"primary_frequency": 0.01,
	"flat_center_radius": 26.0,
	"terrain_size": 443.0,
	"floor_strength": 2.13,
	"floor_height_min": -50.0,
	"floor_height_max": 50.0,
	"room_height": 15.0,
	"octree_depth": 12,
}


## Configure a DCTerrain for a Quake-hosting world and kick the first build.
##
## Safe to call with null (a headless peer frees its terrain — see
## map_dc_world.gd), so callers do not each need the guard.
static func configure_terrain(terrain: Node) -> void:
	if terrain == null or not is_instance_valid(terrain):
		return
	# GENERATOR IS NOT APPLIED HERE — see its comment. Every DCTerrain setter is
	# DC_SET_CONFIG, which calls rebuild() on change, so writing eleven of them
	# from _ready() fired thirteen rebuilds and the mesher never converged. They
	# live in map_dc_world.tscn instead, applied at load. This only asserts that
	# they arrived, because a bare DCTerrain node is exactly the mistake that
	# started this and it is invisible until the terrain misbehaves.
	if not bool(terrain.get("use_gpu")):
		push_warning("[DCWorldSetup] use_gpu is FALSE — the scene is missing the " +
				"DCTerrain properties. Every chunk will mesh on the CPU, which a " +
				"level shell (563k tris, ~150 ms/chunk) will not survive.")
	terrain.collision_distance = COLLISION_DISTANCE
	# set() rather than direct assignment: these are GDExtension properties, and a
	# build of libdc_terrain that predates one of them should degrade to "ignored"
	# rather than take the map down on a nonexistent-property error.
	terrain.set("max_splits_per_frame", MAX_SPLITS_PER_FRAME)
	terrain.set("voxel_size", VOXEL_SIZE)
	terrain.rebuild()