79 lines
2.6 KiB
GDScript
79 lines
2.6 KiB
GDScript
extends Node
|
|
|
|
const SHADER_INTERACTOR_LIMIT := 8
|
|
const CLEARING_LIMIT := 4
|
|
|
|
@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
|
|
@export var path_clearings: Array[NodePath] = []
|
|
|
|
var _elapsed := 0.0
|
|
|
|
|
|
func _ready() -> void:
|
|
# JajceWorld first isolates its mutable materials in the parent's _ready().
|
|
call_deferred("_configure_path_clearings")
|
|
_refresh_interactors()
|
|
|
|
|
|
func _configure_path_clearings() -> void:
|
|
if grass_material == null:
|
|
return
|
|
var segments := PackedVector4Array()
|
|
segments.resize(CLEARING_LIMIT)
|
|
var widths := Vector4.ZERO
|
|
var count := 0
|
|
for path in path_clearings.slice(0, CLEARING_LIMIT):
|
|
var strip := get_node_or_null(path) as MeshInstance3D
|
|
if strip == null or not strip.mesh is BoxMesh:
|
|
continue
|
|
var box := strip.mesh as BoxMesh
|
|
var half_length := strip.global_basis.z * box.size.z * 0.5
|
|
var start := strip.global_position - half_length
|
|
var end := strip.global_position + half_length
|
|
segments[count] = Vector4(start.x, start.z, end.x, end.z)
|
|
widths[count] = strip.global_basis.x.length() * box.size.x * 0.5 + 0.12
|
|
count += 1
|
|
grass_material.set_shader_parameter("clearing_count", count)
|
|
grass_material.set_shader_parameter("clearing_segments", segments)
|
|
grass_material.set_shader_parameter("clearing_half_widths", widths)
|
|
|
|
|
|
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)
|