302 lines
9.4 KiB
GDScript
302 lines
9.4 KiB
GDScript
@tool
|
|
class_name CloudSystem2D
|
|
extends Node3D
|
|
|
|
signal cloud_coverage_changed(coverage: float)
|
|
|
|
const MAX_LAYERS := 4
|
|
|
|
@export_group("Cloud Layers")
|
|
@export var layer_configs: Array[CloudLayerConfig] = []
|
|
@export var global_coverage: float = 0.5:
|
|
set(value):
|
|
global_coverage = clampf(value, 0.0, 1.0)
|
|
_update_coverage()
|
|
cloud_coverage_changed.emit(global_coverage)
|
|
|
|
@export_group("Evolution")
|
|
@export var evolution_enabled: bool = true
|
|
@export var evolution_speed: float = 0.01
|
|
@export var evolution_scale: float = 1.0
|
|
@export var evolution_seed: int = 0
|
|
|
|
@export_group("Wind Override")
|
|
@export var use_global_wind: bool = true
|
|
@export var global_wind_direction: Vector2 = Vector2(1.0, 0.0)
|
|
@export var global_wind_speed: float = 1.0
|
|
|
|
@export_group("Lighting")
|
|
@export var sun_direction: Vector3 = Vector3(0.0, 1.0, 0.0)
|
|
@export var sun_color: Color = Color.WHITE
|
|
@export var sun_intensity: float = 1.0
|
|
@export var moon_direction: Vector3 = Vector3(0.0, -1.0, 0.0)
|
|
@export var moon_color: Color = Color(0.7, 0.8, 1.0)
|
|
@export var moon_intensity: float = 0.3
|
|
|
|
@export_group("Performance")
|
|
@export var update_rate: float = 60.0
|
|
|
|
var _cloud_materials: Array[ShaderMaterial] = []
|
|
var _cloud_meshes: Array[MeshInstance3D] = []
|
|
var _time_accumulator: float = 0.0
|
|
var _evolution_offset: float = 0.0
|
|
var _update_timer: float = 0.0
|
|
|
|
|
|
func _ready() -> void:
|
|
if layer_configs.is_empty():
|
|
_initialize_default_layers()
|
|
_create_cloud_meshes()
|
|
_update_all_layers()
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
_update_timer += delta
|
|
var update_interval := 1.0 / update_rate
|
|
|
|
if _update_timer >= update_interval:
|
|
_update_timer = 0.0
|
|
_time_accumulator += delta * update_rate * update_interval
|
|
|
|
if evolution_enabled:
|
|
_evolution_offset += delta * evolution_speed
|
|
|
|
_update_flow(delta)
|
|
_update_lighting()
|
|
|
|
|
|
func _initialize_default_layers() -> void:
|
|
layer_configs.clear()
|
|
|
|
var low_layer := CloudLayerConfig.new()
|
|
low_layer.layer_type = CloudLayerConfig.LayerType.LOW
|
|
low_layer.opacity = 0.9
|
|
low_layer.flow_speed = 0.03
|
|
low_layer.parallax_depth = 0.3
|
|
low_layer.tiling = Vector2(2.0, 2.0)
|
|
layer_configs.append(low_layer)
|
|
|
|
var mid_layer := CloudLayerConfig.new()
|
|
mid_layer.layer_type = CloudLayerConfig.LayerType.MID
|
|
mid_layer.opacity = 0.7
|
|
mid_layer.flow_speed = 0.02
|
|
mid_layer.parallax_depth = 0.6
|
|
mid_layer.tiling = Vector2(3.0, 3.0)
|
|
layer_configs.append(mid_layer)
|
|
|
|
var high_layer := CloudLayerConfig.new()
|
|
high_layer.layer_type = CloudLayerConfig.LayerType.HIGH
|
|
high_layer.opacity = 0.5
|
|
high_layer.flow_speed = 0.01
|
|
high_layer.parallax_depth = 0.9
|
|
high_layer.tiling = Vector2(4.0, 4.0)
|
|
layer_configs.append(high_layer)
|
|
|
|
var wisps_layer := CloudLayerConfig.new()
|
|
wisps_layer.layer_type = CloudLayerConfig.LayerType.WISPS
|
|
wisps_layer.opacity = 0.3
|
|
wisps_layer.flow_speed = 0.015
|
|
wisps_layer.parallax_depth = 1.0
|
|
wisps_layer.tiling = Vector2(6.0, 6.0)
|
|
layer_configs.append(wisps_layer)
|
|
|
|
|
|
func _create_cloud_meshes() -> void:
|
|
for mesh in _cloud_meshes:
|
|
if is_instance_valid(mesh):
|
|
mesh.queue_free()
|
|
_cloud_meshes.clear()
|
|
_cloud_materials.clear()
|
|
|
|
for i in layer_configs.size():
|
|
var config := layer_configs[i]
|
|
if not config.enabled:
|
|
continue
|
|
|
|
var mesh_instance := MeshInstance3D.new()
|
|
mesh_instance.name = "CloudLayer_%d" % i
|
|
|
|
var quad := QuadMesh.new()
|
|
quad.size = Vector2(1000.0, 1000.0)
|
|
quad.orientation = PlaneMesh.FACE_Y
|
|
mesh_instance.mesh = quad
|
|
|
|
var material := _create_cloud_material(config)
|
|
mesh_instance.material_override = material
|
|
|
|
var height := config.get_layer_height() * 100.0 + 50.0
|
|
mesh_instance.position.y = height
|
|
|
|
add_child(mesh_instance)
|
|
_cloud_meshes.append(mesh_instance)
|
|
_cloud_materials.append(material)
|
|
|
|
|
|
func _create_cloud_material(config: CloudLayerConfig) -> ShaderMaterial:
|
|
var material := ShaderMaterial.new()
|
|
material.shader = _get_cloud_shader()
|
|
|
|
material.set_shader_parameter("tiling", config.tiling)
|
|
material.set_shader_parameter("opacity", config.opacity * global_coverage)
|
|
material.set_shader_parameter("color_tint", config.color_tint)
|
|
material.set_shader_parameter("shadow_color", config.shadow_color)
|
|
material.set_shader_parameter("flow_direction", config.flow_direction)
|
|
material.set_shader_parameter("flow_speed", config.flow_speed)
|
|
material.set_shader_parameter("light_influence", config.light_influence)
|
|
material.set_shader_parameter("rim_glow_intensity", config.rim_glow_intensity)
|
|
|
|
if config.texture:
|
|
material.set_shader_parameter("cloud_texture", config.texture)
|
|
|
|
return material
|
|
|
|
|
|
func _get_cloud_shader() -> Shader:
|
|
var shader := Shader.new()
|
|
shader.code = """
|
|
shader_type spatial;
|
|
render_mode unshaded, depth_draw_never, cull_disabled;
|
|
|
|
uniform sampler2D cloud_texture : source_color, filter_linear_mipmap, repeat_enable;
|
|
uniform vec2 tiling = vec2(4.0, 4.0);
|
|
uniform float opacity = 0.8;
|
|
uniform vec4 color_tint : source_color = vec4(1.0);
|
|
uniform vec4 shadow_color : source_color = vec4(0.6, 0.6, 0.7, 1.0);
|
|
uniform vec2 flow_direction = vec2(1.0, 0.0);
|
|
uniform float flow_speed = 0.02;
|
|
uniform float flow_offset = 0.0;
|
|
uniform vec3 light_direction = vec3(0.0, 1.0, 0.0);
|
|
uniform vec4 light_color : source_color = vec4(1.0);
|
|
uniform float light_intensity = 1.0;
|
|
uniform float light_influence = 1.0;
|
|
uniform float rim_glow_intensity = 0.3;
|
|
uniform float evolution_offset = 0.0;
|
|
uniform float coverage = 1.0;
|
|
|
|
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 += flow_direction * flow_offset;
|
|
|
|
vec2 distorted_uv = uv + vec2(sin(uv.y * 10.0 + evolution_offset) * 0.01, cos(uv.x * 10.0 + evolution_offset) * 0.01);
|
|
|
|
vec4 cloud_sample = texture(cloud_texture, distorted_uv);
|
|
float cloud_alpha = cloud_sample.r * opacity * coverage;
|
|
|
|
float light_dot = dot(normalize(light_direction), vec3(0.0, 1.0, 0.0));
|
|
float light_factor = mix(0.5, 1.0, (light_dot + 1.0) * 0.5) * light_influence;
|
|
|
|
vec3 lit_color = mix(shadow_color.rgb, color_tint.rgb, light_factor);
|
|
lit_color *= light_color.rgb * light_intensity;
|
|
|
|
float rim = pow(1.0 - cloud_sample.r, 2.0) * rim_glow_intensity;
|
|
lit_color += light_color.rgb * rim;
|
|
|
|
ALBEDO = lit_color;
|
|
ALPHA = cloud_alpha;
|
|
}
|
|
"""
|
|
return shader
|
|
|
|
|
|
func _update_flow(delta: float) -> void:
|
|
for i in _cloud_materials.size():
|
|
if i >= layer_configs.size():
|
|
continue
|
|
|
|
var config := layer_configs[i]
|
|
var material := _cloud_materials[i]
|
|
|
|
var wind_dir := global_wind_direction if use_global_wind else config.flow_direction
|
|
var wind_spd := global_wind_speed if use_global_wind else 1.0
|
|
#var flow_offset: float = material.get_shader_parameter("flow_offset")
|
|
#flow_offset += config.flow_speed * wind_spd * delta
|
|
#
|
|
#material.set_shader_parameter("flow_offset", flow_offset)
|
|
#material.set_shader_parameter("flow_direction", wind_dir)
|
|
#smaterial.set_shader_parameter("evolution_offset", _evolution_offset)
|
|
|
|
|
|
func _update_lighting() -> void:
|
|
for material in _cloud_materials:
|
|
material.set_shader_parameter("light_direction", sun_direction)
|
|
material.set_shader_parameter("light_color", sun_color)
|
|
material.set_shader_parameter("light_intensity", sun_intensity)
|
|
|
|
|
|
func _update_coverage() -> void:
|
|
for i in _cloud_materials.size():
|
|
if i >= layer_configs.size():
|
|
continue
|
|
var config := layer_configs[i]
|
|
_cloud_materials[i].set_shader_parameter("coverage", global_coverage)
|
|
_cloud_materials[i].set_shader_parameter("opacity", config.opacity)
|
|
|
|
|
|
func _update_all_layers() -> void:
|
|
for i in _cloud_materials.size():
|
|
if i >= layer_configs.size():
|
|
continue
|
|
var config := layer_configs[i]
|
|
var material := _cloud_materials[i]
|
|
|
|
material.set_shader_parameter("tiling", config.tiling)
|
|
material.set_shader_parameter("opacity", config.opacity)
|
|
material.set_shader_parameter("color_tint", config.color_tint)
|
|
material.set_shader_parameter("shadow_color", config.shadow_color)
|
|
material.set_shader_parameter("flow_direction", config.flow_direction)
|
|
material.set_shader_parameter("flow_speed", config.flow_speed)
|
|
material.set_shader_parameter("light_influence", config.light_influence)
|
|
material.set_shader_parameter("rim_glow_intensity", config.rim_glow_intensity)
|
|
material.set_shader_parameter("coverage", global_coverage)
|
|
|
|
|
|
func set_layer_enabled(layer_index: int, enabled: bool) -> void:
|
|
if layer_index < 0 or layer_index >= _cloud_meshes.size():
|
|
return
|
|
_cloud_meshes[layer_index].visible = enabled
|
|
|
|
|
|
func set_layer_opacity(layer_index: int, opacity: float) -> void:
|
|
if layer_index < 0 or layer_index >= _cloud_materials.size():
|
|
return
|
|
if layer_index < layer_configs.size():
|
|
layer_configs[layer_index].opacity = opacity
|
|
_cloud_materials[layer_index].set_shader_parameter("opacity", opacity)
|
|
|
|
|
|
func set_layer_color(layer_index: int, color: Color) -> void:
|
|
if layer_index < 0 or layer_index >= _cloud_materials.size():
|
|
return
|
|
if layer_index < layer_configs.size():
|
|
layer_configs[layer_index].color_tint = color
|
|
_cloud_materials[layer_index].set_shader_parameter("color_tint", color)
|
|
|
|
|
|
func set_global_wind(direction: Vector2, speed: float) -> void:
|
|
global_wind_direction = direction.normalized()
|
|
global_wind_speed = speed
|
|
|
|
|
|
func set_sun_lighting(direction: Vector3, color: Color, intensity: float) -> void:
|
|
sun_direction = direction.normalized()
|
|
sun_color = color
|
|
sun_intensity = intensity
|
|
_update_lighting()
|
|
|
|
|
|
func set_moon_lighting(direction: Vector3, color: Color, intensity: float) -> void:
|
|
moon_direction = direction.normalized()
|
|
moon_color = color
|
|
moon_intensity = intensity
|
|
|
|
|
|
func rebuild_layers() -> void:
|
|
_create_cloud_meshes()
|
|
_update_all_layers()
|