extends Node const SHADER_INTERACTOR_LIMIT := 8 @export var grass_material: ShaderMaterial @export_range(1, SHADER_INTERACTOR_LIMIT, 1) var max_interactors := 8 @export_range(0.02, 0.5, 0.01) var update_interval := 0.08 var _elapsed := 0.0 func _ready() -> void: _refresh_interactors() func _process(delta: float) -> void: _elapsed += delta if _elapsed < update_interval: return _elapsed = 0.0 _refresh_interactors() func _refresh_interactors() -> void: if grass_material == null: return var camera := get_viewport().get_camera_3d() var origin := camera.global_position if camera != null else Vector3.ZERO var nearest: Array[Node3D] = [] for value in get_tree().get_nodes_in_group("grass_interactors"): var candidate := value as Node3D if candidate == null: continue var insert_at := nearest.size() var candidate_distance := origin.distance_squared_to(candidate.global_position) for index in nearest.size(): if candidate_distance < origin.distance_squared_to(nearest[index].global_position): insert_at = index break nearest.insert(insert_at, candidate) if nearest.size() > max_interactors: nearest.pop_back() var positions := PackedVector3Array() positions.resize(SHADER_INTERACTOR_LIMIT) for index in SHADER_INTERACTOR_LIMIT: positions[index] = Vector3(0.0, -1000.0, 0.0) for index in nearest.size(): positions[index] = nearest[index].global_position grass_material.set_shader_parameter("interactor_count", nearest.size()) grass_material.set_shader_parameter("interactor_positions", positions)