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: call_deferred("run") func run() -> void: var image := Image.create(2, 2, false, Image.FORMAT_RGBA8) image.fill(Color.WHITE) var texture := ImageTexture.create_from_image(image) var fx := EffectPlayer.new() fx.build({"particles": [{"particle": {"BillboardType": 0, "SrcBlendType": 5, "DestBlendType": 6}}]}) var particle_mat: StandardMaterial3D = fx._emitters[0].material_override # A plain white texture and a static quad isolate material face visibility. particle_mat.albedo_texture = texture var materials := [particle_mat, fx._mesh_material({"BlendingSrcType": 5, "BlendingDestType": 6}, [texture])] for i in materials.size(): check(materials[i].cull_mode == BaseMaterial3D.CULL_DISABLED, "effect material %d is two-sided" % i) check(materials[i].depth_draw_mode == BaseMaterial3D.DEPTH_DRAW_DISABLED, "effect material %d does not write depth" % i) check(not materials[i].no_depth_test, "effect material %d still tests world depth" % i) if DisplayServer.get_name() != "headless": materials.append(preload("res://fx/particle_color_material.gd").build(particle_mat, 5)) materials.append(preload("res://fx/particle_color_material.gd").build(particle_mat, 6)) await visual(materials) else: print("SKIP: pixel checks require a rendering backend") fx.free() print("effect_surface_test: failures=%d" % failures) quit(1 if failures else 0) func visual(materials: Array) -> void: var viewport := SubViewport.new() viewport.size = Vector2i(96, 96) viewport.own_world_3d = true viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS root.add_child(viewport) var environment := WorldEnvironment.new() environment.environment = Environment.new() environment.environment.background_mode = Environment.BG_COLOR environment.environment.background_color = Color.BLACK viewport.add_child(environment) var quad := MeshInstance3D.new() quad.mesh = QuadMesh.new() quad.mesh.size = Vector2(2, 2) viewport.add_child(quad) var camera := Camera3D.new() viewport.add_child(camera) camera.current = true var blocker := MeshInstance3D.new() blocker.mesh = BoxMesh.new() blocker.mesh.size = Vector3(2, 2, 0.1) var black := StandardMaterial3D.new() black.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED black.albedo_color = Color.BLACK blocker.material_override = black viewport.add_child(blocker) for i in materials.size(): quad.material_override = materials[i] for side in [1.0, -1.0]: camera.position = Vector3(0, 0, side * 3.0) camera.look_at(Vector3.ZERO) blocker.visible = false var color: Color = await center(viewport) check(color.r > 0.8, "material %d side %d visible pixel=%s" % [i, int(side), color]) blocker.position = Vector3(0, 0, side) blocker.visible = true color = await center(viewport) check(color.r < 0.05, "material %d side %d occluded by world" % [i, int(side)]) viewport.queue_free() await process_frame func center(viewport: SubViewport) -> Color: await process_frame await process_frame await RenderingServer.frame_post_draw return viewport.get_texture().get_image().get_pixel(48, 48)