84 lines
4.1 KiB
GDScript
84 lines
4.1 KiB
GDScript
extends SceneTree
|
|
|
|
const EffectPlayer = preload("res://fx/effect_player.gd")
|
|
var failures := 0
|
|
|
|
func check(ok: bool, label: String) -> void:
|
|
if not ok:
|
|
failures += 1
|
|
printerr("FAIL: " + label)
|
|
|
|
func effective(pm: ParticleProcessMaterial, age: float) -> Color:
|
|
return pm.color * pm.color_ramp.gradient.sample(age) if pm.color_ramp else pm.color
|
|
|
|
func _init() -> void:
|
|
var fx := EffectPlayer.new()
|
|
fx.build({"particles": [{"particle": {
|
|
"TimeEventColorRed": [[0.0, 0.2], [0.5, 0.8], [1.0, 0.4]],
|
|
"TimeEventColorGreen": [[0.25, 0.4], [0.75, 0.8]],
|
|
"TimeEventColorBlue": [[0.0, 0.5]],
|
|
"TimeEventAlpha": [[0.0, 1.0], [1.0, 0.0]]}},
|
|
{"particle": {"TimeEventColorRed": [[0.0, 0.5]], "TimeEventAlpha": [[0.0, 1.0]]}},
|
|
{"particle": {"TimeEventColorBlue": [[0.0, 0.2], [1.0, 0.8]]}},
|
|
{"particle": {}}]})
|
|
var pm: ParticleProcessMaterial = fx._emitters[0].process_material
|
|
check(pm.color == Color.WHITE, "neutral base prevents double tint")
|
|
check(effective(pm, 0.0).is_equal_approx(Color(0.2, 0.4, 0.5, 1.0)), "birth RGBA")
|
|
check(effective(pm, 0.25).is_equal_approx(Color(0.5, 0.4, 0.5, 0.75)), "independent green knot")
|
|
check(effective(pm, 0.5).is_equal_approx(Color(0.8, 0.6, 0.5, 0.5)), "middle RGBA")
|
|
check(effective(pm, 0.75).is_equal_approx(Color(0.6, 0.8, 0.5, 0.25)), "late RGBA")
|
|
check(effective(pm, 1.0).is_equal_approx(Color(0.4, 0.8, 0.5, 0.0)), "end RGBA")
|
|
check(effective(fx._emitters[1].process_material, 0.5).is_equal_approx(Color(0.5, 1.0, 1.0)),
|
|
"constant 0.5 tint remains 0.5, not 0.25")
|
|
check(effective(fx._emitters[2].process_material, 0.5).is_equal_approx(Color(1.0, 1.0, 0.5)),
|
|
"RGB animates without alpha track")
|
|
check(effective(fx._emitters[3].process_material, 0.5) == Color.WHITE, "missing tracks default white")
|
|
fx.free()
|
|
var malformed := EffectPlayer.new()
|
|
var cleaned: GradientTexture1D = malformed._color_ramp({"TimeEventColorRed": [
|
|
[1.0, 0.8], [], [0.0, 0.1], [0.0, 0.2], [NAN, 0.5], [0.5, INF], ["bad", 0.4]]})
|
|
check(cleaned.gradient.sample(0.5).is_equal_approx(Color(0.5, 1, 1)),
|
|
"unordered/duplicate keys normalized and invalid values ignored")
|
|
malformed.free()
|
|
real_resources()
|
|
print("effect_color_test: failures=%d" % failures)
|
|
quit(1 if failures else 0)
|
|
|
|
# Independent scalar interpolation oracle for the sorted, valid asset tracks.
|
|
func reference(rows: Array, age: float) -> float:
|
|
if rows.is_empty():
|
|
return 1.0
|
|
if age <= float(rows[0][0]):
|
|
return float(rows[0][1])
|
|
for i in range(1, rows.size()):
|
|
if age <= float(rows[i][0]):
|
|
var fraction := (age - float(rows[i - 1][0])) / (float(rows[i][0]) - float(rows[i - 1][0]))
|
|
return float(rows[i - 1][1]) * (1.0 - fraction) + float(rows[i][1]) * fraction
|
|
return float(rows[-1][1])
|
|
|
|
func real_resources() -> void:
|
|
var count := 0
|
|
for name in ["warrior/effect/palbang_4_sword.mse", "assassin/effect/beam_blow.mse",
|
|
"sura/effect/pabeopsul_4_blow.mse", "shaman/effect/noejeon_making_1.mse"]:
|
|
var path := AssetRoot.path().path_join("PC/ymir work/pc/" + name)
|
|
var parsed: Dictionary = preload("res://fx/mse.gd").new().parse_file(path)
|
|
check(not parsed.get("particles", []).is_empty(), "real resource loaded: " + name)
|
|
for particle in parsed.get("particles", []):
|
|
# Exercise real RGBA through material construction; texture loading and
|
|
# emission geometry are deliberately outside this color-only regression.
|
|
var prop: Dictionary = {}
|
|
for key in ["TimeEventColorRed", "TimeEventColorGreen", "TimeEventColorBlue", "TimeEventAlpha"]:
|
|
prop[key] = particle.particle.get(key, [])
|
|
var fx := EffectPlayer.new()
|
|
fx.build({"particles": [{"particle": prop}]})
|
|
var pm: ParticleProcessMaterial = fx._emitters[0].process_material
|
|
for step in range(41):
|
|
var age := float(step) / 40.0
|
|
var expected := Color(reference(prop.TimeEventColorRed, age),
|
|
reference(prop.TimeEventColorGreen, age), reference(prop.TimeEventColorBlue, age),
|
|
reference(prop.TimeEventAlpha, age))
|
|
check(effective(pm, age).is_equal_approx(expected), "real RGBA %s particle %d t=%f" % [name, count, age])
|
|
count += 1
|
|
fx.free()
|
|
print("real color tracks: particles=%d samples=%d" % [count, count * 41])
|