Files
security-game/src/test/capture_prop_gallery.gd
T
zwitschi d96f53c285 Add a repeatable screenshot capture and compare smoke
Both capture helpers now accept --out=res://docs/screenshots, and
tools/check_screenshots.sh renders the staged set (using xvfb-run when headless)
before running screen_designs.py compare. Record the judge result: 7 match,
4 review, 0 diff. Headless 226/226 and the smoke both pass in a clean worktree.
2026-11-01 16:43:10 +01:00

113 lines
3.9 KiB
GDScript

extends SceneTree
## Dev helper: render every generated prop side by side on a plain backdrop and
## save user://screenshots/10-prop-gallery.png by default. The image feeds
## `tools/screen_designs.py compare` so a prop regression is visible.
##
## Usage: godot --path . --script res://src/test/capture_prop_gallery.gd
## godot --path . --script res://src/test/capture_prop_gallery.gd -- --out=res://docs/screenshots
var out_dir := "user://screenshots"
const MODEL_DIR := "res://assets/models/generated/"
const LIBRARY_DIR := "res://assets/models/blender/props/"
const PROPS := ["bollard", "bench", "trash_bin", "planter_box",
"newspaper_box", "fire_hydrant"]
const SPACING := 1.9
var frames := 0
func _initialize() -> void:
for arg in OS.get_cmdline_user_args():
if arg.begins_with("--out="):
out_dir = arg.trim_prefix("--out=")
root.size = Vector2i(1280, 720)
_build_stage()
func _build_stage() -> void:
var stage := Node3D.new()
stage.name = "PropGallery"
root.add_child(stage)
var environment := Environment.new()
environment.background_mode = Environment.BG_COLOR
environment.background_color = Color(0.52, 0.54, 0.58)
environment.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
environment.ambient_light_color = Color(0.78, 0.80, 0.84)
environment.ambient_light_energy = 0.8
var world_env := WorldEnvironment.new()
world_env.environment = environment
stage.add_child(world_env)
# Floor and a plain backdrop wall: a studio line-up with no street context.
var floor_mesh := PlaneMesh.new()
floor_mesh.size = Vector2(14, 8)
var floor := MeshInstance3D.new()
floor.mesh = floor_mesh
floor.material_override = _flat(Color(0.50, 0.51, 0.54), 0.9)
stage.add_child(floor)
var backdrop_mesh := BoxMesh.new()
backdrop_mesh.size = Vector3(14, 6, 0.1)
var backdrop := MeshInstance3D.new()
backdrop.mesh = backdrop_mesh
backdrop.material_override = _flat(Color(0.55, 0.57, 0.61), 0.95)
backdrop.position = Vector3(0, 3.0, -3.4)
stage.add_child(backdrop)
# Raised back row so both rows read in a single shot.
var platform_mesh := BoxMesh.new()
platform_mesh.size = Vector3(6.8, 0.8, 1.9)
var platform := MeshInstance3D.new()
platform.mesh = platform_mesh
platform.material_override = _flat(Color(0.38, 0.39, 0.42), 0.85)
platform.position = Vector3(0, 0.4, -2.1)
stage.add_child(platform)
var light := DirectionalLight3D.new()
light.rotation_degrees = Vector3(-48, -35, 0)
light.light_energy = 0.85
light.shadow_enabled = true
stage.add_child(light)
var camera := Camera3D.new()
camera.position = Vector3(0, 2.1, 6.2)
camera.fov = 50.0
camera.look_at_from_position(camera.position, Vector3(0, 0.75, -1.0))
camera.current = true
stage.add_child(camera)
# 3 x 2 grid: three props on the front row, three on the back row.
var columns := [-1.95, 0.0, 1.95]
for index in PROPS.size():
var prop_name: String = PROPS[index]
var path := LIBRARY_DIR + prop_name + ".glb"
if not ResourceLoader.exists(path):
path = MODEL_DIR + prop_name + ".glb"
var packed := ResourceLoader.load(path) as PackedScene
if packed == null:
continue
var model := packed.instantiate() as Node3D
var col: int = index % 3
var row: int = index / 3
model.position = Vector3(columns[col], 0.8 if row == 1 else 0.0, -2.1 * row)
model.rotation_degrees.y = 14.0
stage.add_child(model)
func _flat(color: Color, roughness: float) -> StandardMaterial3D:
var material := StandardMaterial3D.new()
material.albedo_color = color
material.roughness = roughness
return material
func _process(_delta: float) -> bool:
frames += 1
if frames < 20:
return false
var dir := DirAccess.open("user://")
if dir == null or not dir.dir_exists("screenshots"):
dir.make_dir_recursive("screenshots")
var image := root.get_texture().get_image()
var err := image.save_png(out_dir.path_join("10-prop-gallery.png"))
print("10-prop-gallery.png -> err=%d (%dx%d)" % [err, image.get_width(), image.get_height()])
quit(0)
return true