Package a Forward+ preset with volumetric fog, SDFGI and a volumetric flashlight for a separate Linux/Windows Desktop HQ zip. Gate the effects on a real RenderingDevice, tune LATE fog, and adapt local light falloff for Godot 4.7. Record the Compatibility/Forward+ A/B captures and metrics, add the artifact to the build/publish scripts, and keep the default Compatibility/Web path unchanged. Headless 231/231, Python 87/87; exported HQ binary boots with Vulkan.
103 lines
5.1 KiB
GDScript
103 lines
5.1 KiB
GDScript
extends RefCounted
|
|
## Headless tests for the shared surface/environment recipes (ADR-0012).
|
|
|
|
const Materials := preload("res://src/game/materials.gd")
|
|
const GameManager := preload("res://src/game/game_manager.gd")
|
|
const Rules := preload("res://src/core/rules.gd")
|
|
|
|
func test_every_profile_has_physical_values() -> bool:
|
|
var ok := true
|
|
for role in ["brick", "concrete", "asphalt", "awning", "stone", "metal",
|
|
"painted_metal", "wood", "cloth", "skin", "glass", "rubber"]:
|
|
var profile: Dictionary = Materials.profile(role)
|
|
var roughness: float = float(profile.get("roughness", -1.0))
|
|
var metallic: float = float(profile.get("metallic", -1.0))
|
|
if roughness < 0.0 or roughness > 1.0:
|
|
ok = false
|
|
if metallic < 0.0 or metallic > 1.0:
|
|
ok = false
|
|
return TestUtils.check(ok, "all roles have roughness/metallic in 0..1")
|
|
|
|
func test_textured_recipes_keep_their_texture_and_profile() -> bool:
|
|
var brick := Materials.brick()
|
|
var concrete := Materials.concrete()
|
|
var asphalt := Materials.asphalt()
|
|
var awning := Materials.awning()
|
|
var a := TestUtils.check(brick.albedo_texture != null, "brick keeps its texture")
|
|
var b := TestUtils.check(concrete.uv1_scale.x > 1.0, "concrete tiles across the sidewalk")
|
|
var c := TestUtils.check(asphalt.roughness < brick.roughness, "asphalt is smoother than brick")
|
|
var d := TestUtils.check(awning.albedo_texture != null, "awning keeps its texture")
|
|
var e := TestUtils.check(Materials.flat(Color.RED, "metal").metallic > 0.5, "metal role is metallic")
|
|
return a and b and c and d and e
|
|
|
|
func test_realism_pick_tiles_and_wet_asphalt() -> bool:
|
|
## The realism pass swapped the flat first-generation tiles for the v2 picks.
|
|
## The street has a dry and a wet variant for day and late night.
|
|
var brick := Materials.brick()
|
|
var dry := Materials.asphalt()
|
|
var wet := Materials.asphalt(Color(0.46, 0.48, 0.53), Vector2(30, 7), true)
|
|
var a := TestUtils.check(brick.albedo_texture.resource_path.contains("v2/picks/"),
|
|
"brick uses the promoted v2 pick")
|
|
var b := TestUtils.check(brick.normal_enabled and brick.normal_texture != null,
|
|
"brick carries its normal map")
|
|
var c := TestUtils.check(dry.albedo_texture != wet.albedo_texture,
|
|
"wet asphalt uses its own albedo")
|
|
var d := TestUtils.check(wet.roughness < dry.roughness,
|
|
"wet asphalt is glossier than dry asphalt")
|
|
var e := TestUtils.check(wet.metallic_specular > dry.metallic_specular,
|
|
"wet asphalt catches the lamps")
|
|
var f := TestUtils.check(wet.albedo_texture.resource_path.contains("picks"),
|
|
"the wet map is a promoted pick")
|
|
return a and b and c and d and e and f
|
|
|
|
func test_glass_and_emissive_recipes() -> bool:
|
|
var glass := Materials.glass()
|
|
var pane := Materials.window_pane()
|
|
var bar := Materials.emissive(Color(1.0, 0.35, 0.75), 2.5)
|
|
var a := TestUtils.check(glass.transparency == BaseMaterial3D.TRANSPARENCY_ALPHA,
|
|
"glass is alpha blended")
|
|
var b := TestUtils.check(glass.roughness < 0.2, "glass is smooth")
|
|
var c := TestUtils.check(pane.emission_enabled, "window panes can light up")
|
|
var d := TestUtils.check(is_equal_approx(pane.emission_energy_multiplier, 0.0),
|
|
"window panes start dark")
|
|
var e := TestUtils.check(is_equal_approx(bar.emission_energy_multiplier, 2.5),
|
|
"emissive energy is applied")
|
|
return a and b and c and d and e
|
|
|
|
func test_environment_tuning_applies_phase_values() -> bool:
|
|
var env := Environment.new()
|
|
var late: Dictionary = GameManager.PHASE_LIGHTING[Rules.Phase.LATE]
|
|
Materials.configure_environment(env, late)
|
|
var a := TestUtils.check(env.tonemap_mode == Environment.TONE_MAPPER_ACES,
|
|
"ACES tonemapping")
|
|
var b := TestUtils.check(env.glow_enabled, "glow enabled for the marquee")
|
|
var c := TestUtils.check(env.fog_enabled, "fog enabled for distance haze")
|
|
var d := TestUtils.check(env.fog_density > 0.0, "fog has a density")
|
|
var e := TestUtils.check(env.adjustment_enabled, "colour grading enabled")
|
|
var f := TestUtils.check(
|
|
env.fog_light_color.is_equal_approx(late["fog_color"]),
|
|
"phase fog colour is used")
|
|
var advanced := Materials.forward_plus_active()
|
|
var g := TestUtils.check(env.volumetric_fog_enabled == advanced,
|
|
"volumetric fog is gated to Forward+ (advanced=%s)" % advanced)
|
|
var h := TestUtils.check(env.sdfgi_enabled == advanced,
|
|
"SDFGI is gated to Forward+ (advanced=%s)" % advanced)
|
|
return a and b and c and d and e and f and g and h
|
|
|
|
func test_every_phase_has_environment_keys() -> bool:
|
|
var ok := true
|
|
for phase in [Rules.Phase.EARLY, Rules.Phase.MIDDAY, Rules.Phase.EVENING, Rules.Phase.LATE]:
|
|
var cfg: Dictionary = GameManager.PHASE_LIGHTING.get(phase, {})
|
|
for key in ["exposure", "glow", "fog_color", "fog_density", "fog_energy",
|
|
"contrast", "saturation", "volumetric_density", "volumetric_energy"]:
|
|
if not cfg.has(key):
|
|
ok = false
|
|
return TestUtils.check(ok, "all phases set exposure/glow/fog/grading")
|
|
|
|
func test_night_phases_are_night() -> bool:
|
|
var evening: Dictionary = GameManager.PHASE_LIGHTING[Rules.Phase.EVENING]
|
|
var late: Dictionary = GameManager.PHASE_LIGHTING[Rules.Phase.LATE]
|
|
var early: Dictionary = GameManager.PHASE_LIGHTING[Rules.Phase.EARLY]
|
|
return TestUtils.check(bool(evening["night"]) and bool(late["night"]) and not bool(early["night"]),
|
|
"night flag matches the phase")
|