Files
Trinittu/dynamic-sky/scripts/shadow_proxy_system.gd
2026-02-03 11:26:48 +01:00

221 lines
6.0 KiB
GDScript

@tool
class_name ShadowProxySystem
extends Node3D
@export_group("Shadow Settings")
@export var enabled: bool = true:
set(value):
enabled = value
if _shadow_mesh:
_shadow_mesh.visible = enabled
@export var shadow_texture: Texture2D:
set(value):
shadow_texture = value
_update_shadow_texture()
@export var shadow_opacity: float = 0.3:
set(value):
shadow_opacity = clampf(value, 0.0, 1.0)
_update_shadow_material()
@export var shadow_color: Color = Color(0.0, 0.0, 0.1):
set(value):
shadow_color = value
_update_shadow_material()
@export_group("Movement")
@export var flow_direction: Vector2 = Vector2(1.0, 0.0)
@export var flow_speed: float = 5.0
@export var sync_with_clouds: bool = true
@export_group("Projection")
@export var projection_size: float = 500.0:
set(value):
projection_size = value
_update_projection_size()
@export var projection_height: float = 0.1
@export var tiling: Vector2 = Vector2(2.0, 2.0):
set(value):
tiling = value
_update_shadow_material()
@export_group("Blur")
@export var blur_enabled: bool = true
@export var blur_amount: float = 0.5:
set(value):
blur_amount = clampf(value, 0.0, 1.0)
_update_shadow_material()
@export_group("Performance")
@export var follow_camera: bool = true
@export var update_distance: float = 50.0
var _shadow_mesh: MeshInstance3D
var _shadow_material: ShaderMaterial
var _uv_offset: Vector2 = Vector2.ZERO
var _last_camera_position: Vector3 = Vector3.ZERO
func _ready() -> void:
_create_shadow_mesh()
_create_shadow_material()
_update_shadow_material()
func _process(delta: float) -> void:
if not enabled:
return
_update_flow(delta)
if follow_camera:
_follow_camera()
func _create_shadow_mesh() -> void:
if _shadow_mesh:
_shadow_mesh.queue_free()
_shadow_mesh = MeshInstance3D.new()
_shadow_mesh.name = "CloudShadowMesh"
var plane := PlaneMesh.new()
plane.size = Vector2(projection_size, projection_size)
_shadow_mesh.mesh = plane
_shadow_mesh.position.y = projection_height
_shadow_mesh.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
add_child(_shadow_mesh)
func _create_shadow_material() -> void:
_shadow_material = ShaderMaterial.new()
_shadow_material.shader = _get_shadow_shader()
if _shadow_mesh:
_shadow_mesh.material_override = _shadow_material
func _get_shadow_shader() -> Shader:
var shader := Shader.new()
shader.code = """
shader_type spatial;
render_mode unshaded, depth_draw_opaque, cull_disabled, shadows_disabled;
uniform sampler2D shadow_texture : source_color, filter_linear_mipmap, repeat_enable;
uniform vec4 shadow_color : source_color = vec4(0.0, 0.0, 0.1, 1.0);
uniform float opacity = 0.3;
uniform vec2 tiling = vec2(2.0, 2.0);
uniform vec2 uv_offset = vec2(0.0, 0.0);
uniform float blur_amount = 0.5;
uniform float edge_fade = 0.1;
varying vec2 world_uv;
void vertex() {
world_uv = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xz;
}
void fragment() {
vec2 uv = world_uv * tiling * 0.001 + uv_offset;
vec4 shadow_sample;
if (blur_amount > 0.01) {
float blur = blur_amount * 0.01;
shadow_sample = texture(shadow_texture, uv) * 0.25;
shadow_sample += texture(shadow_texture, uv + vec2(blur, 0.0)) * 0.125;
shadow_sample += texture(shadow_texture, uv - vec2(blur, 0.0)) * 0.125;
shadow_sample += texture(shadow_texture, uv + vec2(0.0, blur)) * 0.125;
shadow_sample += texture(shadow_texture, uv - vec2(0.0, blur)) * 0.125;
shadow_sample += texture(shadow_texture, uv + vec2(blur, blur)) * 0.0625;
shadow_sample += texture(shadow_texture, uv - vec2(blur, blur)) * 0.0625;
shadow_sample += texture(shadow_texture, uv + vec2(blur, -blur)) * 0.0625;
shadow_sample += texture(shadow_texture, uv + vec2(-blur, blur)) * 0.0625;
} else {
shadow_sample = texture(shadow_texture, uv);
}
float shadow_value = shadow_sample.r;
vec2 local_uv = fract(world_uv * 0.001);
float edge = smoothstep(0.0, edge_fade, local_uv.x);
edge *= smoothstep(0.0, edge_fade, local_uv.y);
edge *= smoothstep(0.0, edge_fade, 1.0 - local_uv.x);
edge *= smoothstep(0.0, edge_fade, 1.0 - local_uv.y);
ALBEDO = shadow_color.rgb;
ALPHA = shadow_value * opacity * edge;
}
"""
return shader
func _update_shadow_material() -> void:
if _shadow_material == null:
return
_shadow_material.set_shader_parameter("shadow_color", shadow_color)
_shadow_material.set_shader_parameter("opacity", shadow_opacity)
_shadow_material.set_shader_parameter("tiling", tiling)
_shadow_material.set_shader_parameter("blur_amount", blur_amount if blur_enabled else 0.0)
func _update_shadow_texture() -> void:
if _shadow_material and shadow_texture:
_shadow_material.set_shader_parameter("shadow_texture", shadow_texture)
func _update_projection_size() -> void:
if _shadow_mesh and _shadow_mesh.mesh is PlaneMesh:
var plane := _shadow_mesh.mesh as PlaneMesh
plane.size = Vector2(projection_size, projection_size)
func _update_flow(delta: float) -> void:
_uv_offset += flow_direction * flow_speed * delta * 0.001
_uv_offset.x = fmod(_uv_offset.x, 1.0)
_uv_offset.y = fmod(_uv_offset.y, 1.0)
if _shadow_material:
_shadow_material.set_shader_parameter("uv_offset", _uv_offset)
func _follow_camera() -> void:
var camera := get_viewport().get_camera_3d()
if camera == null:
return
var camera_pos := camera.global_position
var distance := camera_pos.distance_to(_last_camera_position)
if distance >= update_distance:
_last_camera_position = camera_pos
global_position.x = camera_pos.x
global_position.z = camera_pos.z
func set_flow(direction: Vector2, speed: float) -> void:
flow_direction = direction.normalized()
flow_speed = speed
func set_shadow_appearance(color: Color, opacity_value: float, blur: float) -> void:
shadow_color = color
shadow_opacity = opacity_value
blur_amount = blur
_update_shadow_material()
func sync_with_cloud_system(cloud_system: CloudSystem2D) -> void:
if sync_with_clouds and cloud_system:
flow_direction = cloud_system.global_wind_direction
flow_speed = cloud_system.global_wind_speed * 5.0
func set_cloud_coverage(coverage: float) -> void:
shadow_opacity = coverage * 0.5
_update_shadow_material()