Add wind motion to foliage

This commit is contained in:
Alexander Sellite 2026-08-09 12:44:45 -04:00
parent 93c1cd4421
commit d73fc76698
3 changed files with 124 additions and 0 deletions

View file

@ -0,0 +1,44 @@
shader_type spatial;
render_mode cull_back, depth_draw_opaque;
uniform vec4 albedo_color : source_color = vec4(1.0);
uniform float material_roughness = 1.0;
uniform float material_metallic = 0.0;
uniform float local_min_y = 0.0;
uniform float local_height = 1.0;
uniform float local_wind_strength = 0.08;
uniform float wind_speed = 0.9;
uniform float wind_phase = 0.0;
void vertex() {
float height_ratio = clamp(
(VERTEX.y - local_min_y) / max(local_height, 0.001),
0.0,
1.0
);
float bend_mask = smoothstep(0.08, 1.0, height_ratio);
vec3 world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
float broad_wave = sin(
TIME * wind_speed
+ dot(world_position.xz, vec2(0.23, 0.17))
+ wind_phase
);
float leaf_flutter = sin(
TIME * wind_speed * 2.17
+ dot(world_position.xz, vec2(-0.41, 0.37))
+ wind_phase * 1.7
);
float displacement = (
broad_wave * 0.72 + leaf_flutter * 0.28
) * local_wind_strength * bend_mask;
VERTEX.x += displacement;
VERTEX.z += displacement * 0.42;
}
void fragment() {
ALBEDO = albedo_color.rgb;
ROUGHNESS = material_roughness;
METALLIC = material_metallic;
}

View file

@ -0,0 +1 @@
uid://c3q7l8m9n2v4x

View file

@ -5,6 +5,14 @@ extends WorldRegion
const FishingShopInteractionType = preload(
"res://world/fishing_shop_interaction.gd"
)
const FOLIAGE_WIND_SHADER: Shader = preload(
"res://world/materials/foliage_wind.gdshader"
)
const FOLIAGE_MATERIAL_NAMES: Array[StringName] = [
&"leaf",
&"leaf_light",
&"leaf_dark",
]
@export_group("Owned Nodes")
@export_node_path("MeshInstance3D")
@ -25,10 +33,14 @@ var fishing_shop_path: NodePath = (
)
@export_group("Terrain Collision")
@export var rebuild_terrain_collision_on_ready: bool = false
@export_group("Foliage Wind")
@export_range(0.0, 0.4, 0.005) var foliage_wind_strength: float = 0.12
@export_range(0.0, 4.0, 0.05) var foliage_wind_speed: float = 0.9
func _ready() -> void:
if rebuild_terrain_collision_on_ready or not has_terrain_collision():
_build_terrain_collision()
_apply_foliage_wind()
if Engine.is_editor_hint():
update_configuration_warnings()
@ -107,6 +119,73 @@ func _collect_terrain_mesh_instances(root: Node) -> Array[MeshInstance3D]:
return mesh_instances
func _apply_foliage_wind() -> void:
var terrain_root: Node = get_node_or_null(terrain_visual_root_path)
if terrain_root == null:
return
for mesh_instance: MeshInstance3D in _collect_terrain_mesh_instances(
terrain_root
):
_apply_foliage_wind_to_mesh(mesh_instance)
func _apply_foliage_wind_to_mesh(mesh_instance: MeshInstance3D) -> void:
if mesh_instance.mesh == null:
return
var bounds: AABB = mesh_instance.get_aabb()
var global_scale: Vector3 = mesh_instance.global_basis.get_scale().abs()
var horizontal_scale: float = maxf(
(global_scale.x + global_scale.z) * 0.5,
0.001,
)
var local_strength: float = foliage_wind_strength / horizontal_scale
var phase: float = fposmod(
mesh_instance.global_position.x * 0.73
+ mesh_instance.global_position.z * 0.41,
TAU,
)
for surface_index: int in mesh_instance.mesh.get_surface_count():
var source_material: Material = mesh_instance.get_active_material(
surface_index
)
if not source_material is StandardMaterial3D:
continue
if not FOLIAGE_MATERIAL_NAMES.has(
StringName(source_material.resource_name)
):
continue
var source_standard := source_material as StandardMaterial3D
var wind_material := ShaderMaterial.new()
wind_material.shader = FOLIAGE_WIND_SHADER
wind_material.set_shader_parameter(
"albedo_color",
source_standard.albedo_color,
)
wind_material.set_shader_parameter(
"material_roughness",
source_standard.roughness,
)
wind_material.set_shader_parameter(
"material_metallic",
source_standard.metallic,
)
wind_material.set_shader_parameter("local_min_y", bounds.position.y)
wind_material.set_shader_parameter(
"local_height",
maxf(bounds.size.y, 0.001),
)
wind_material.set_shader_parameter(
"local_wind_strength",
local_strength,
)
wind_material.set_shader_parameter("wind_speed", foliage_wind_speed)
wind_material.set_shader_parameter("wind_phase", phase)
mesh_instance.set_surface_override_material(
surface_index,
wind_material,
)
func _get_configuration_warnings() -> PackedStringArray:
var warnings := PackedStringArray()
var visual_mesh := get_node_or_null(visual_mesh_path) as MeshInstance3D