221 lines
6.0 KiB
GDScript
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()
|