100 lines
4.3 KiB
GDScript
100 lines
4.3 KiB
GDScript
extends SceneTree
|
|
## Verifica il rig del fuoco: rumore continuo, assenza di strobing, coerenza fra le
|
|
## luci e modulazione della fiamma. Il campione di rumore e' puro, quindi il test
|
|
## simula 60 s a 60 Hz senza dipendere dal frame rate reale.
|
|
const FireLight = preload("res://scripts/fire_light.gd")
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run_test")
|
|
|
|
func run_test() -> void:
|
|
var world = load("res://scenes/main.tscn").instantiate()
|
|
root.add_child(world)
|
|
var rig = world.get_node("OfficeAtmosphere").fire_rig
|
|
var main_light: OmniLight3D = rig.main_light
|
|
var bounce: OmniLight3D = rig.bounce_light
|
|
var core: OmniLight3D = rig.core_light
|
|
|
|
# --- limiti, variazione e assenza di strobing -----------------------------
|
|
var previous := -1.0
|
|
var maximum_step := 0.0
|
|
var minimum := INF
|
|
var maximum := -INF
|
|
var total := 0.0
|
|
var squares := 0.0
|
|
var samples := 0
|
|
rig._pulse = 0.0
|
|
for i in 3600: # 60 s a 60 Hz
|
|
rig._process(1.0 / 60.0)
|
|
var energy := main_light.light_energy
|
|
minimum = minf(minimum, energy)
|
|
maximum = maxf(maximum, energy)
|
|
total += energy
|
|
squares += energy * energy
|
|
samples += 1
|
|
if previous >= 0.0:
|
|
maximum_step = maxf(maximum_step, absf(energy - previous))
|
|
previous = energy
|
|
var mean := total / samples
|
|
var deviation := sqrt(squares / samples - mean * mean)
|
|
assert(minimum >= rig.MIN_ENERGY and maximum <= rig.MAX_ENERGY)
|
|
assert(deviation > 0.05) # non e' una luce fissa
|
|
assert(maximum_step < 0.05) # nessun salto: niente strobing
|
|
assert(maximum - minimum > 0.4) # la gamma viene usata davvero
|
|
print("PASS: flicker entro [%.2f, %.2f], deviazione %.3f, salto massimo per frame %.3f" % [minimum, maximum, deviation, maximum_step])
|
|
|
|
# --- coerenza fra le tre luci ---------------------------------------------
|
|
assert(bounce.light_energy > 0.0 and core.light_energy > 0.0)
|
|
assert(bounce.light_energy < main_light.light_energy)
|
|
assert(core.omni_range < main_light.omni_range)
|
|
assert(is_equal_approx(main_light.omni_range, rig.BASE_RANGE)) # raggio mai animato
|
|
rig._pulse = 1.0
|
|
rig._apply()
|
|
var bright := [main_light.light_energy, bounce.light_energy, core.light_energy]
|
|
rig._pulse = -1.0
|
|
rig._apply()
|
|
var dim := [main_light.light_energy, bounce.light_energy, core.light_energy]
|
|
for i in 3:
|
|
assert(bright[i] > dim[i])
|
|
print("PASS: principale, rimbalzo e nucleo pulsano insieme e il raggio resta fisso")
|
|
|
|
# --- ricetta ufficiale del rimbalzo e unicita' delle ombre ----------------
|
|
assert(not bounce.shadow_enabled and not core.shadow_enabled)
|
|
assert(bounce.light_specular == 0.0 and core.light_specular == 0.0)
|
|
assert(is_equal_approx(bounce.omni_range, rig.BASE_RANGE * rig.BOUNCE_RANGE_FACTOR))
|
|
assert(bounce.get_parent() == main_light and core.get_parent() == main_light)
|
|
assert(main_light.omni_shadow_mode == OmniLight3D.SHADOW_CUBE) # il dual paraboloid non esiste in Compatibility
|
|
assert(main_light.shadow_enabled and not bounce.shadow_enabled)
|
|
print("PASS: rimbalzo figlio senza ombre ne' specular, raggio +40%, ombre solo sulla principale")
|
|
|
|
# --- micro-scarto e fiamma ------------------------------------------------
|
|
var maximum_offset := 0.0
|
|
var flame_heights: Array[float] = []
|
|
rig._time = 0.0
|
|
for i in 600:
|
|
rig._process(1.0 / 60.0)
|
|
maximum_offset = maxf(maximum_offset, Vector2(main_light.position.x, main_light.position.z).length())
|
|
flame_heights.append(rig._flame_mesh.size.y)
|
|
assert(maximum_offset <= rig.JITTER * 1.45) # scarto piccolo e limitato
|
|
assert(maximum_offset > 0.005) # le ombre si muovono davvero
|
|
assert(flame_heights.max() > flame_heights.min())
|
|
assert(flame_heights.max() < rig._flame_height * 1.2)
|
|
print("PASS: scarto massimo %.3f m, altezza fiamma da %.2f a %.2f" % [maximum_offset, flame_heights.min(), flame_heights.max()])
|
|
|
|
# --- braci e determinismo -------------------------------------------------
|
|
assert(rig.embers is CPUParticles3D and rig.embers.amount == 8 and rig.embers.emitting)
|
|
assert(rig.embers.mesh.material.shading_mode == BaseMaterial3D.SHADING_MODE_UNSHADED)
|
|
var same := Node3D.new()
|
|
same.set_script(FireLight)
|
|
world.add_child(same)
|
|
var different := false
|
|
for i in 200:
|
|
var t := 0.01 * i
|
|
if not is_equal_approx(rig.pulse_at(t), same.pulse_at(t)):
|
|
different = true
|
|
assert(not different) # stesso seed -> stessa sequenza, test riproducibili
|
|
print("PASS: braci presenti e flicker deterministico con seed fisso")
|
|
world.queue_free()
|
|
await process_frame
|
|
quit()
|