extends Node3D # Creates horizontal caustic planes at multiple depths, follows the player # Add as child of any node that follows the player (e.g. PlanktonFollower) const LAYER_DEPTHS := [-5.0, -15.0, -25.0] const LAYER_SIZE := 60.0 var caustic_meshes: Array[MeshInstance3D] = [] var caustic_material: ShaderMaterial func _ready() -> void: var shader := load("res://shaders/caustics.gdshader") if not shader: push_warning("CausticsLayer: caustics.gdshader not found") return caustic_material = ShaderMaterial.new() caustic_material.shader = shader for depth in LAYER_DEPTHS: var mi := MeshInstance3D.new() var plane := PlaneMesh.new() plane.size = Vector2(LAYER_SIZE, LAYER_SIZE) mi.mesh = plane # Each layer gets its own material instance to allow per-depth tuning var mat := caustic_material.duplicate() as ShaderMaterial # Lower layers = less intense var fade := clamp(1.0 + depth / 30.0, 0.0, 1.0) mat.set_shader_parameter("intensity", 1.5 * fade) mi.material_override = mat mi.position = Vector3(0.0, depth, 0.0) add_child(mi) caustic_meshes.append(mi) func _process(_delta: float) -> void: # Follow camera XZ, keep fixed Y depths var cam := get_viewport().get_camera_3d() if cam: var cx := cam.global_position.x var cz := cam.global_position.z for i in caustic_meshes.size(): var mi := caustic_meshes[i] mi.global_position.x = cx mi.global_position.z = cz # Keep the fixed depth offset mi.global_position.y = LAYER_DEPTHS[i]