134 lines
6 KiB
GDScript3
134 lines
6 KiB
GDScript3
|
|
extends SceneTree
|
||
|
|
|
||
|
|
# Headless test: run with
|
||
|
|
# godot --headless -s res://tests/test_surface_background_shared.gd
|
||
|
|
# The landed surface and the setup editor drew DIFFERENT backgrounds for the
|
||
|
|
# same planet. Each scene carried its own hand-written ShaderMaterial (the
|
||
|
|
# game's baked-and-tiled lunar shader, the editor's per-pixel procedural one)
|
||
|
|
# and only the game's side ever swapped in a profile's own shader - so a gas
|
||
|
|
# giant authored in the editor showed recoloured craters while the game showed
|
||
|
|
# cloud bands. An editor that lies about what the player will see is worse
|
||
|
|
# than no preview.
|
||
|
|
#
|
||
|
|
# Both scenes now instance ONE SurfaceBackground.tscn and configure it through
|
||
|
|
# ONE method. This test guards that, since nothing about the code stops
|
||
|
|
# someone re-adding a local material to one scene and pulling them apart
|
||
|
|
# again.
|
||
|
|
#
|
||
|
|
# Covers:
|
||
|
|
# A. both scenes get their backdrop from the same PackedScene file
|
||
|
|
# B. neither overrides the shared material - the two start out identical
|
||
|
|
# C. every surface profile names its own background_shader; a null one
|
||
|
|
# would mean "whatever this scene already had", which is the ambiguity
|
||
|
|
# that caused the drift
|
||
|
|
# D. applying each profile through the shared entry point produces the same
|
||
|
|
# shader AND the same colours on both scenes, for every profile
|
||
|
|
|
||
|
|
const BACKGROUND_SCENE_PATH := "res://SurfaceBackground.tscn"
|
||
|
|
const HOST_SCENES := {
|
||
|
|
"PlanetSurface.tscn": "res://PlanetSurface.tscn",
|
||
|
|
"PlanetSetupEditor.tscn": "res://PlanetSetupEditor.tscn",
|
||
|
|
}
|
||
|
|
const COLOR_PARAMETERS := ["base_color", "crater_color", "highlight_color"]
|
||
|
|
|
||
|
|
var backgrounds: Dictionary = {}
|
||
|
|
var hosts: Array = []
|
||
|
|
|
||
|
|
|
||
|
|
func _initialize() -> void:
|
||
|
|
var surface_profiles: Script = load("res://PlanetSurfaceProfiles.gd")
|
||
|
|
|
||
|
|
# --- A/B: same source scene, same untouched material ---------------------
|
||
|
|
for scene_name in HOST_SCENES:
|
||
|
|
var host: Node = load(HOST_SCENES[scene_name]).instantiate()
|
||
|
|
hosts.append(host)
|
||
|
|
var layer: Node = host.get_node_or_null("BackgroundLayer")
|
||
|
|
if layer == null:
|
||
|
|
_fail("%s has no BackgroundLayer" % scene_name)
|
||
|
|
return
|
||
|
|
if layer.scene_file_path != BACKGROUND_SCENE_PATH:
|
||
|
|
_fail("%s builds its own background (%s) instead of instancing %s" \
|
||
|
|
% [scene_name, layer.scene_file_path, BACKGROUND_SCENE_PATH])
|
||
|
|
return
|
||
|
|
var background: Node = layer.get_node_or_null("Background")
|
||
|
|
if background == null:
|
||
|
|
_fail("%s's background layer has no Background node" % scene_name)
|
||
|
|
return
|
||
|
|
if not background.has_method("apply_profile"):
|
||
|
|
_fail("%s's background has no apply_profile() - the shared entry point is gone" % scene_name)
|
||
|
|
return
|
||
|
|
backgrounds[scene_name] = background
|
||
|
|
print("both scenes instance %s and expose apply_profile()" % BACKGROUND_SCENE_PATH)
|
||
|
|
|
||
|
|
var names: Array = backgrounds.keys()
|
||
|
|
var reference: ShaderMaterial = backgrounds[names[0]].material
|
||
|
|
for i in range(1, names.size()):
|
||
|
|
var other: ShaderMaterial = backgrounds[names[i]].material
|
||
|
|
# Same configuration, SEPARATE objects. Sub-resources in a PackedScene
|
||
|
|
# are shared between instances by default, which would make the whole
|
||
|
|
# comparison below vacuous - it would be reading one material twice -
|
||
|
|
# and would mean two live surfaces repainting each other, baked
|
||
|
|
# texture included. SurfaceBackground.tscn marks the material
|
||
|
|
# resource_local_to_scene for exactly this reason.
|
||
|
|
if other == reference:
|
||
|
|
_fail("%s and %s share one ShaderMaterial object, so applying a profile to either repaints both" \
|
||
|
|
% [names[0], names[i]])
|
||
|
|
return
|
||
|
|
if other.shader != reference.shader:
|
||
|
|
_fail("%s and %s start on different shaders (%s vs %s), so one of them overrides the shared material" \
|
||
|
|
% [names[0], names[i], reference.shader.resource_path, other.shader.resource_path])
|
||
|
|
return
|
||
|
|
print("both start on the same shader: %s" % reference.shader.resource_path)
|
||
|
|
|
||
|
|
# --- C: no profile leaves its shader unstated ----------------------------
|
||
|
|
for profile_id in surface_profiles.PROFILES:
|
||
|
|
var profile: Resource = surface_profiles.get_profile(profile_id)
|
||
|
|
if profile.background_shader == null:
|
||
|
|
_fail("the '%s' profile does not name a background_shader, so what it draws depends on which scene happens to be hosting it" % profile_id)
|
||
|
|
return
|
||
|
|
print("all %d profiles name their own background shader" % surface_profiles.PROFILES.size())
|
||
|
|
|
||
|
|
# --- D: applying a profile lands identically on both ---------------------
|
||
|
|
for profile_id in surface_profiles.PROFILES:
|
||
|
|
var profile: Resource = surface_profiles.get_profile(profile_id)
|
||
|
|
for scene_name in backgrounds:
|
||
|
|
backgrounds[scene_name].apply_profile(profile)
|
||
|
|
|
||
|
|
var first: ShaderMaterial = backgrounds[names[0]].material
|
||
|
|
if first.shader != profile.background_shader:
|
||
|
|
_fail("applying '%s' did not put its own shader on the background" % profile_id)
|
||
|
|
return
|
||
|
|
for i in range(1, names.size()):
|
||
|
|
var other: ShaderMaterial = backgrounds[names[i]].material
|
||
|
|
if other.shader != first.shader:
|
||
|
|
_fail("'%s' resolved to %s in %s but %s in %s" \
|
||
|
|
% [profile_id, first.shader.resource_path, names[0],
|
||
|
|
other.shader.resource_path, names[i]])
|
||
|
|
return
|
||
|
|
for parameter in COLOR_PARAMETERS:
|
||
|
|
var a: Color = first.get_shader_parameter(parameter)
|
||
|
|
var b: Color = other.get_shader_parameter(parameter)
|
||
|
|
if not a.is_equal_approx(b):
|
||
|
|
_fail("'%s' set %s to %s in %s but %s in %s" \
|
||
|
|
% [profile_id, parameter, a, names[0], b, names[i]])
|
||
|
|
return
|
||
|
|
# Identical to each other is only half of it - both have to match the
|
||
|
|
# profile, or the shared path could be uniformly ignoring it.
|
||
|
|
for parameter in COLOR_PARAMETERS:
|
||
|
|
var applied: Color = first.get_shader_parameter(parameter)
|
||
|
|
var declared: Color = profile.get(parameter)
|
||
|
|
if not applied.is_equal_approx(declared):
|
||
|
|
_fail("'%s' declares %s %s but the background ended up with %s" \
|
||
|
|
% [profile_id, parameter, declared, applied])
|
||
|
|
return
|
||
|
|
print(" '%s' -> %s, identical on both" % [profile_id, first.shader.resource_path.get_file()])
|
||
|
|
|
||
|
|
for host in hosts:
|
||
|
|
host.free()
|
||
|
|
print("TEST_SURFACE_BACKGROUND_SHARED: PASS")
|
||
|
|
quit(0)
|
||
|
|
|
||
|
|
|
||
|
|
func _fail(message: String) -> void:
|
||
|
|
print("TEST_SURFACE_BACKGROUND_SHARED: FAIL (%s)" % message)
|
||
|
|
quit(1)
|