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 _init() -> void: var fx := EffectPlayer.new() fx.build({"particles": [{"emitter": {"TimeEventSizeX": [[0, 10]], "TimeEventSizeY": [[0, 30]]}, "particle": {"TimeEventScaleX": [[0, 0], [1, 4]], "TimeEventScaleY": [[0, 3], [1, 1]]}}, {"emitter": {"TimeEventSizeX": [[0, 0]], "TimeEventSizeY": [[0, 0]]}, "particle": {}}]}) var g: GPUParticles3D = fx._emitters[0] var pm: ParticleProcessMaterial = g.process_material check(g.draw_pass_1.size.is_equal_approx(Vector2(0.2, 0.6)), "independent half-sizes become full metre dimensions") check(pm.scale_min == 1.0 and pm.scale_max == 1.0, "no invented random size factor") check(pm.scale_curve is CurveXYZTexture, "independent XYZ scale texture") if pm.scale_curve is CurveXYZTexture: var tex: CurveXYZTexture = pm.scale_curve check(is_equal_approx(tex.curve_x.sample(0.25), 1.0), "X interpolates linearly") check(is_equal_approx(tex.curve_y.sample(0.25), 2.5), "Y has independent interpolation") check(is_equal_approx(tex.curve_x.sample(1.0), 4.0), "scale above two is retained") check(is_equal_approx(tex.curve_z.sample(0.5), 1.0), "depth scale remains neutral") var empty: GPUParticles3D = fx._emitters[1] check(empty.draw_pass_1.size == Vector2.ZERO, "zero source size is not made visible") var defaults: ParticleProcessMaterial = empty.process_material if defaults.scale_curve is CurveXYZTexture: check(defaults.scale_curve.curve_x.sample(1.0) == 1.0 and defaults.scale_curve.curve_y.sample(1.0) == 1.0, "absent scale tracks stay constant instead of shrinking") else: check(false, "default independent curves") fx.free() real_resources() print("effect_scale_test: failures=%d" % failures) quit(1 if failures else 0) func real_resources() -> void: var count := 0 var samples := 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 spec: Dictionary = preload("res://fx/mse.gd").new().parse_file(path) check(not spec.get("particles", []).is_empty(), "real size resource loaded: " + name) for particle in spec.get("particles", []): var fx := EffectPlayer.new() # Only source size/scale tracks: not a full texture or emission test. fx.build({"particles": [{"emitter": { "TimeEventSizeX": particle.emitter.get("TimeEventSizeX", []), "TimeEventSizeY": particle.emitter.get("TimeEventSizeY", [])}, "particle": { "TimeEventScaleX": particle.particle.get("TimeEventScaleX", []), "TimeEventScaleY": particle.particle.get("TimeEventScaleY", [])}}]}) var texture: Texture2D = fx._emitters[0].process_material.scale_curve check(texture is CurveXYZTexture, "real XY texture " + name) if texture is CurveXYZTexture: for axis in range(2): var key := "TimeEventScaleX" if axis == 0 else "TimeEventScaleY" var curve: Curve = texture.curve_x if axis == 0 else texture.curve_y var rows: Array = particle.particle.get(key, []) for i in rows.size(): var time := float(rows[i][0]) var duplicate := i > 0 and float(rows[i - 1][0]) == time if time >= 0.0 and time <= 1.0 and not duplicate: check(is_equal_approx(curve.sample(time), float(rows[i][1])), "real scale key %s %s t=%f got=%f expected=%f" % [name, key, time, curve.sample(time), float(rows[i][1])]) samples += 1 if i > 0 and not duplicate: var mid := (float(rows[i - 1][0]) + time) * 0.5 var value := (float(rows[i - 1][1]) + float(rows[i][1])) * 0.5 if mid >= 0.0 and mid <= 1.0: # Duplicate-key jumps occupy 1e-4 of normalized age; # permit the resulting small interpolation deviation. var after_jump := i > 1 and float(rows[i - 2][0]) == float(rows[i - 1][0]) var matches := absf(curve.sample(mid) - value) < 0.001 if after_jump else is_equal_approx(curve.sample(mid), value) check(matches, "real linear midpoint %s %s t=%f got=%f expected=%f" % [name, key, mid, curve.sample(mid), value]) samples += 1 count += 1 fx.free() print("real scale tracks: particles=%d samples=%d" % [count, samples])