Start dynamic sky
This commit is contained in:
283
dynamic-sky/README.md
Normal file
283
dynamic-sky/README.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# Dynamic Sky System for Godot 4.6
|
||||
|
||||
A stylized dynamic sky system with day/night cycles, weather, clouds, and VFX.
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### Step 1: Open Project in Godot
|
||||
1. Open your Godot 4.6 project
|
||||
2. Wait for the editor to import all scripts (check the bottom progress bar)
|
||||
3. If you see script errors, click **Project > Reload Current Project**
|
||||
|
||||
### Step 2: Add to Your Scene
|
||||
1. Open the scene where you want the sky system
|
||||
2. Drag `dynamic-sky/dynamic_sky.tscn` into your scene tree
|
||||
3. Or right-click in the scene tree → **Instance Child Scene** → select `dynamic_sky.tscn`
|
||||
|
||||
### Step 3: Configure the Environment
|
||||
The DynamicSky node auto-creates required children. To link an existing environment:
|
||||
|
||||
1. Select the **DynamicSky** node
|
||||
2. In the Inspector, under **Environment**:
|
||||
- Assign your existing `WorldEnvironment` node (or leave empty to auto-create)
|
||||
- Assign existing `DirectionalLight3D` for sun/moon (or leave empty)
|
||||
|
||||
### Step 4: Generate Cloud Textures
|
||||
Clouds need textures. Use the procedural generator:
|
||||
|
||||
```gdscript
|
||||
# In a tool script or _ready():
|
||||
var cloud_tex = ProceduralCloudTexture.create_default_cloud_texture(1)
|
||||
$DynamicSky/CloudSystem.layer_configs[0].texture = cloud_tex
|
||||
```
|
||||
|
||||
Or assign existing cloud textures to each `CloudLayerConfig` in the Inspector.
|
||||
|
||||
### Step 5: Configure Shadow Texture
|
||||
```gdscript
|
||||
var shadow_tex = ProceduralCloudTexture.create_shadow_texture()
|
||||
$DynamicSky/ShadowProxySystem.shadow_texture = shadow_tex
|
||||
```
|
||||
|
||||
## Testing the System
|
||||
|
||||
### Test 1: Basic Scene Test
|
||||
1. Create a new 3D scene with a `Camera3D` and a `MeshInstance3D` (e.g., plane for ground)
|
||||
2. Instance `dynamic_sky.tscn`
|
||||
3. Run the scene (F5)
|
||||
4. You should see a gradient sky with sun light
|
||||
|
||||
### Test 2: Time of Day
|
||||
Add this script to test time changes:
|
||||
|
||||
```gdscript
|
||||
extends Node
|
||||
|
||||
@onready var sky = $DynamicSky
|
||||
|
||||
func _ready():
|
||||
# Start at morning
|
||||
sky.set_time_of_day(0.25)
|
||||
|
||||
func _process(delta):
|
||||
# Press keys to change time
|
||||
if Input.is_action_just_pressed("ui_right"):
|
||||
sky.set_time_of_day(sky.get_time_of_day() + 0.05)
|
||||
if Input.is_action_just_pressed("ui_left"):
|
||||
sky.set_time_of_day(sky.get_time_of_day() - 0.05)
|
||||
|
||||
# Print current time
|
||||
if Input.is_action_just_pressed("ui_accept"):
|
||||
print("Time: ", sky.get_time_of_day())
|
||||
```
|
||||
|
||||
### Test 3: Weather System
|
||||
```gdscript
|
||||
func _input(event):
|
||||
if event.is_action_pressed("ui_up"):
|
||||
$DynamicSky.set_weather(WeatherProfile.WeatherState.RAIN)
|
||||
if event.is_action_pressed("ui_down"):
|
||||
$DynamicSky.set_weather(WeatherProfile.WeatherState.CLEAR)
|
||||
```
|
||||
|
||||
### Test 4: Debug Panel
|
||||
1. Add a `CanvasLayer` to your scene
|
||||
2. Add a `Control` node as child
|
||||
3. Attach the `sky_debug_panel.gd` script
|
||||
4. In Inspector, assign the `DynamicSky` node to the `dynamic_sky` property
|
||||
5. Run and use the UI controls
|
||||
|
||||
### Test 5: Meteor Shower
|
||||
```gdscript
|
||||
func _ready():
|
||||
# Ensure it's night for visibility
|
||||
$DynamicSky.set_time_of_day(0.0)
|
||||
# Wait a moment then trigger
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
$DynamicSky.trigger_meteor_shower(30.0)
|
||||
```
|
||||
|
||||
### Test 6: Kill Volumes
|
||||
1. Add an `Area3D` to your scene
|
||||
2. Attach `weather_kill_volume.gd` script
|
||||
3. Configure the shape and size in Inspector
|
||||
4. Precipitation will be blocked inside this volume
|
||||
|
||||
## Complete Test Scene Example
|
||||
|
||||
Create a new scene with this structure:
|
||||
|
||||
```
|
||||
TestScene (Node3D)
|
||||
├── DynamicSky (instance of dynamic_sky.tscn)
|
||||
├── Camera3D
|
||||
├── Ground (MeshInstance3D with PlaneMesh)
|
||||
├── CanvasLayer
|
||||
│ └── DebugPanel (Control with sky_debug_panel.gd)
|
||||
└── TestScript (Node with test script below)
|
||||
```
|
||||
|
||||
Test script:
|
||||
```gdscript
|
||||
extends Node
|
||||
|
||||
@onready var sky = $"../DynamicSky"
|
||||
|
||||
func _ready():
|
||||
# Generate cloud textures
|
||||
_setup_clouds()
|
||||
|
||||
# Start at mid-morning
|
||||
sky.set_time_of_day(0.35)
|
||||
|
||||
print("Dynamic Sky Test Ready!")
|
||||
print("Arrow keys: Change time")
|
||||
print("1-6: Change weather")
|
||||
print("M: Meteor shower")
|
||||
print("P: Pause/Resume time")
|
||||
|
||||
func _setup_clouds():
|
||||
var cloud_system = sky.cloud_system
|
||||
if cloud_system:
|
||||
for i in cloud_system.layer_configs.size():
|
||||
var tex = ProceduralCloudTexture.create_default_cloud_texture(i)
|
||||
cloud_system.layer_configs[i].texture = tex
|
||||
cloud_system.rebuild_layers()
|
||||
|
||||
var shadow_system = sky.shadow_proxy_system
|
||||
if shadow_system:
|
||||
shadow_system.shadow_texture = ProceduralCloudTexture.create_shadow_texture()
|
||||
|
||||
func _input(event):
|
||||
if event is InputEventKey and event.pressed:
|
||||
match event.keycode:
|
||||
KEY_RIGHT:
|
||||
sky.set_time_of_day(wrapf(sky.get_time_of_day() + 0.02, 0, 1))
|
||||
KEY_LEFT:
|
||||
sky.set_time_of_day(wrapf(sky.get_time_of_day() - 0.02, 0, 1))
|
||||
KEY_1:
|
||||
sky.set_weather(WeatherProfile.WeatherState.CLEAR)
|
||||
KEY_2:
|
||||
sky.set_weather(WeatherProfile.WeatherState.CLOUDY)
|
||||
KEY_3:
|
||||
sky.set_weather(WeatherProfile.WeatherState.OVERCAST)
|
||||
KEY_4:
|
||||
sky.set_weather(WeatherProfile.WeatherState.RAIN)
|
||||
KEY_5:
|
||||
sky.set_weather(WeatherProfile.WeatherState.STORM)
|
||||
KEY_6:
|
||||
sky.set_weather(WeatherProfile.WeatherState.SNOW)
|
||||
KEY_M:
|
||||
sky.trigger_meteor_shower(30.0)
|
||||
KEY_P:
|
||||
if sky.day_night_controller.paused:
|
||||
sky.resume_time()
|
||||
else:
|
||||
sky.pause_time()
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Script errors on load | Reload project: Project → Reload Current Project |
|
||||
| No sky visible | Ensure camera is looking up; check WorldEnvironment exists |
|
||||
| Clouds not visible | Assign textures to CloudLayerConfig or generate procedurally |
|
||||
| Shadows not visible | Assign shadow_texture to ShadowProxySystem |
|
||||
| Stars not visible | Set time to night (0.0 or 0.9+); check stars_visibility curve |
|
||||
| Time not advancing | Check `paused` is false on DayNightController |
|
||||
| Weather not changing | Check WeatherController has profiles; look at transition progress |
|
||||
|
||||
## Features
|
||||
|
||||
### Preset Skybox Nodes
|
||||
- 6 presets: Clear Day, Sunset, Night, Overcast, Storm, Dawn
|
||||
- Runtime preset switching with smooth transitions
|
||||
- Non-destructive overrides
|
||||
|
||||
### Day/Night Cycle
|
||||
- Configurable day length (1-1440 minutes)
|
||||
- Sun/moon arc movement
|
||||
- Color/intensity curves for sun, moon, ambient, fog
|
||||
- Star visibility at night
|
||||
- Time events (sunrise, sunset, noon)
|
||||
|
||||
### Weather System
|
||||
- States: Clear, Cloudy, Overcast, Rain, Storm, Snow
|
||||
- Smooth transitions between states
|
||||
- Per-state cloud, wind, and VFX settings
|
||||
- Random or scripted weather timelines
|
||||
|
||||
### 2D Cloudscapes
|
||||
- 4 independent layers (low, mid, high, wisps)
|
||||
- Per-layer texture, tiling, opacity, color
|
||||
- Flow animation with wind influence
|
||||
- Evolution/morphing over time
|
||||
- Reactive to sun/moon lighting
|
||||
|
||||
### Post-Processing
|
||||
- Sky gradient shading
|
||||
- Height and distance fog
|
||||
- Sun glow halo
|
||||
- Dawn/dusk enhancement
|
||||
- Procedural stars
|
||||
|
||||
### VFX
|
||||
- Shooting stars (randomized)
|
||||
- Meteor showers (event-triggered)
|
||||
- Configurable appearance
|
||||
|
||||
### Cloud Shadows
|
||||
- Projected shadow overlay
|
||||
- Syncs with cloud movement
|
||||
- Blur and opacity controls
|
||||
|
||||
### Kill Volumes
|
||||
- Block precipitation in indoor areas
|
||||
- Box, sphere, cylinder shapes
|
||||
- Fade-out margins
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Add `dynamic_sky.tscn` to your scene
|
||||
2. Reference the DynamicSky node in your scripts
|
||||
3. Use the API:
|
||||
|
||||
```gdscript
|
||||
# Get reference
|
||||
@onready var sky = $DynamicSky
|
||||
|
||||
# Set time (0.0-1.0, where 0.5 = noon)
|
||||
sky.set_time_of_day(0.5)
|
||||
|
||||
# Change weather
|
||||
sky.set_weather(WeatherProfile.WeatherState.RAIN)
|
||||
|
||||
# Apply preset
|
||||
sky.apply_preset("Sunset")
|
||||
|
||||
# Trigger meteor shower
|
||||
sky.trigger_meteor_shower(60.0)
|
||||
```
|
||||
|
||||
## Debug Panel
|
||||
|
||||
Add `SkyDebugPanel` to your UI and assign the DynamicSky reference to see:
|
||||
- Current time and weather
|
||||
- Cloud coverage
|
||||
- Controls for testing
|
||||
|
||||
## Resources
|
||||
|
||||
- `SkyConfig`: Global sky parameters
|
||||
- `SkyPreset`: Complete preset configuration
|
||||
- `WeatherProfile`: Per-weather-state settings
|
||||
- `CloudLayerConfig`: Per-layer cloud settings
|
||||
- `PostProcessProfile`: Post-processing parameters
|
||||
|
||||
## Performance
|
||||
|
||||
- Disable unused cloud layers
|
||||
- Reduce cloud texture resolution
|
||||
- Lower VFX update rates
|
||||
- Disable shadow blur on low-end hardware
|
||||
180
dynamic-sky/SPEC.md
Normal file
180
dynamic-sky/SPEC.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# Stylized Dynamic Sky Specification (Godot 4.6)
|
||||
|
||||
## Overview
|
||||
This document specifies a stylized dynamic sky system for Godot 4.6. It defines the required features, behavior, data structures, and integration points without prescribing implementation code.
|
||||
|
||||
## Goals
|
||||
- Provide a visually stylized, dynamic sky suitable for stylized or semi-realistic games.
|
||||
- Offer modular components that can be combined or replaced.
|
||||
- Enable deterministic playback for cutscenes and synchronized multiplayer.
|
||||
- Support art-direction via presets and editor-friendly controls.
|
||||
|
||||
## Non-Goals
|
||||
- Physically accurate atmospheric scattering.
|
||||
- Real-world meteorology simulation.
|
||||
- Automated terrain-aware shadow projections.
|
||||
|
||||
## System Architecture
|
||||
- **DynamicSkyRoot**: Top-level scene controlling time, weather, and global parameters.
|
||||
- **SkyboxPresetLibrary**: Collection of preset skybox nodes (configured resources/scenes).
|
||||
- **DayNightController**: Controls sun/moon movement, ambient light, and time-of-day curves.
|
||||
- **WeatherController**: Drives weather states, transitions, and VFX spawning.
|
||||
- **CloudSystem2D**: Handles layered 2D cloudscapes, flow, animation, and evolution.
|
||||
- **PostProcessController**: Applies stylized gradients, fog shaping, and sun glow.
|
||||
- **VFXController**: Shooting stars and meteor showers.
|
||||
- **ShadowProxySystem**: Faked cloud shadows projected onto world.
|
||||
|
||||
## Feature Specifications
|
||||
|
||||
### Preset Skybox Nodes
|
||||
**Description**: Pre-authored skybox configurations that can be swapped at runtime.
|
||||
|
||||
**Requirements**:
|
||||
- At least 6 presets: Clear Day, Sunset, Night, Overcast, Storm, Dawn.
|
||||
- Each preset includes:
|
||||
- Skybox texture/gradient resources.
|
||||
- Sun and moon color presets.
|
||||
- Ambient and fog color presets.
|
||||
- Default cloud layers and post-process settings.
|
||||
- Presets can be overridden by runtime controllers without losing base values.
|
||||
- Preset application is non-destructive and reversible.
|
||||
|
||||
**Editor UX**:
|
||||
- Preset picker dropdown.
|
||||
- “Apply Preset” button and “Revert to Preset” button.
|
||||
|
||||
### Day/Night Cycle
|
||||
**Description**: Stylized, configurable cycle controlling lighting and sky appearance.
|
||||
|
||||
**Requirements**:
|
||||
- Time scale with configurable length (e.g., 10–240 minutes per day).
|
||||
- Supports manual time-of-day override and pause.
|
||||
- Sun and moon directional motion (arc across sky dome).
|
||||
- Color and intensity curves:
|
||||
- Sun color and intensity.
|
||||
- Moon color and intensity.
|
||||
- Ambient color and intensity.
|
||||
- Fog color and density.
|
||||
- Smooth transitions between time states.
|
||||
- Optional star visibility curve.
|
||||
|
||||
**Data**:
|
||||
- TimeOfDay (0.0–1.0 normalized)
|
||||
- Curves for color/intensity across time.
|
||||
|
||||
### Weather System
|
||||
**Description**: State-based weather controller with transitions and parameters.
|
||||
|
||||
**Weather States**:
|
||||
- Clear
|
||||
- Cloudy
|
||||
- Overcast
|
||||
- Rain
|
||||
- Storm
|
||||
- Snow (optional, can be disabled)
|
||||
|
||||
**Requirements**:
|
||||
- State machine with configurable transition durations.
|
||||
- Each state defines:
|
||||
- Cloud density, opacity, and coverage.
|
||||
- Wind speed and direction (affects cloud flow).
|
||||
- VFX sets (rain, lightning, snow).
|
||||
- Ambient and fog adjustments.
|
||||
- Randomized or scripted weather timeline support.
|
||||
- Events/callbacks on state enter/exit.
|
||||
|
||||
### 2D Customizable Cloudscapes
|
||||
**Description**: Layered 2D clouds rendered on the sky dome or as screen-space quads.
|
||||
|
||||
**Requirements**:
|
||||
- Up to 4 independent layers: low, mid, high, wisps.
|
||||
- Per-layer settings:
|
||||
- Texture/atlas selection.
|
||||
- Tiling and scale.
|
||||
- Opacity and color tint.
|
||||
- Parallax depth factor.
|
||||
- Flow direction and speed.
|
||||
- Global cloud coverage control.
|
||||
|
||||
### Cloud Flow, Animation, and Evolution
|
||||
**Description**: Dynamic motion and shape changes over time.
|
||||
|
||||
**Requirements**:
|
||||
- Continuous UV flow with wind influence.
|
||||
- Optional noise-based distortion for stylized movement.
|
||||
- Evolution parameter to morph coverage and shape (low-frequency drift).
|
||||
- Time-based variation seed to keep deterministic playback when seeded.
|
||||
|
||||
### Cloud Lighting Reacts with Sun and Moon
|
||||
**Description**: Cloud shading changes based on celestial light direction and intensity.
|
||||
|
||||
**Requirements**:
|
||||
- Light direction taken from sun/moon controller.
|
||||
- Bright side and shadowed side color control.
|
||||
- Night-time soft illumination from moon.
|
||||
- Optional rim glow at dawn/dusk.
|
||||
|
||||
### Weather Particle Kill Volumes
|
||||
**Description**: Volumes that stop precipitation or weather particles in indoor or sheltered spaces.
|
||||
|
||||
**Requirements**:
|
||||
- Axis-aligned and/or custom-shaped kill volumes.
|
||||
- Particles inside kill volume are disabled or instantly culled.
|
||||
- Priority/stacking rules when volumes overlap.
|
||||
- Optional fade-out margin to avoid hard cutoffs.
|
||||
|
||||
### Stylized Post Process for Fog and Sun Gradients
|
||||
**Description**: Screen-space effects to enhance gradients and fog stylization.
|
||||
|
||||
**Requirements**:
|
||||
- Adjustable sky gradient curve blending.
|
||||
- Fog shaping controls (height fog, distance fog).
|
||||
- Sun glow halo with stylized falloff.
|
||||
- Dusk/dawn gradient emphasis.
|
||||
- Option to toggle for performance profiles.
|
||||
|
||||
### Shooting Star and Meteor Shower VFX
|
||||
**Description**: Procedural or timed visual effects visible in the sky.
|
||||
|
||||
**Requirements**:
|
||||
- Occasional shooting stars (randomized frequency).
|
||||
- Meteor shower events with configurable duration and intensity.
|
||||
- Trail length, brightness, and color control.
|
||||
- Optional audio event hook for meteor shower start.
|
||||
|
||||
### Faked Cloud Shadows
|
||||
**Description**: Stylized cloud shadow overlay projected onto the world.
|
||||
|
||||
**Requirements**:
|
||||
- Shadow texture projected in world space or onto a large quad.
|
||||
- Movement matches cloud flow direction and speed.
|
||||
- Darkness/opacity controls and color tint.
|
||||
- Optional blur for softer look.
|
||||
|
||||
## Data and Configuration
|
||||
- **SkyConfig Resource**: Stores defaults for all controllers.
|
||||
- **Preset Resource**: Encapsulates a SkyConfig plus skybox assets.
|
||||
- **WeatherProfile**: Per-state settings and VFX parameters.
|
||||
- **CloudLayerConfig**: Texture/flow/lighting settings per layer.
|
||||
- **PostProcessProfile**: Gradient, fog, and sun glow parameters.
|
||||
|
||||
## Performance Targets
|
||||
- Target 60 FPS on mid-range hardware.
|
||||
- Cloud layers should allow selective disabling.
|
||||
- VFX update rates configurable.
|
||||
|
||||
## Debug and Tooling
|
||||
- On-screen debug panel showing time, weather state, cloud parameters.
|
||||
- Seed display for deterministic playback.
|
||||
- Editor gizmos for kill volumes.
|
||||
|
||||
## Acceptance Criteria
|
||||
- All required features are present and configurable in editor.
|
||||
- Presets can be applied and reverted without data loss.
|
||||
- Day/night transitions are smooth with no visible jumps.
|
||||
- Weather transitions alter clouds, lighting, and VFX consistently.
|
||||
- Cloud lighting visibly reacts to sun/moon changes.
|
||||
- Kill volumes prevent precipitation in indoor spaces.
|
||||
- Post process improves gradients and fog stylization.
|
||||
- Shooting stars and meteor showers visible at night.
|
||||
- Cloud shadows visibly move across the world.
|
||||
394
dynamic-sky/dynamic_sky.tscn
Normal file
394
dynamic-sky/dynamic_sky.tscn
Normal file
@@ -0,0 +1,394 @@
|
||||
[gd_scene format=3 uid="uid://doiojb8xxlp2s"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://8r3do6ow5nmn" path="res://dynamic-sky/scripts/dynamic_sky_root.gd" id="1_dynamic_sky"]
|
||||
[ext_resource type="Script" uid="uid://cb8c6ug44jx4t" path="res://dynamic-sky/scripts/skybox_preset_library.gd" id="2_preset_library"]
|
||||
[ext_resource type="Script" uid="uid://c48eq2y4ikok8" path="res://dynamic-sky/resources/sky_preset.gd" id="3_c6jpa"]
|
||||
[ext_resource type="Script" uid="uid://gypnoh5ikwjn" path="res://dynamic-sky/scripts/day_night_controller.gd" id="3_day_night"]
|
||||
[ext_resource type="Script" uid="uid://beu0xhk0rvpky" path="res://dynamic-sky/resources/cloud_layer_config.gd" id="4_ssi15"]
|
||||
[ext_resource type="Script" uid="uid://blyx8v2gq8nlw" path="res://dynamic-sky/scripts/weather_controller.gd" id="4_weather"]
|
||||
[ext_resource type="Script" uid="uid://c3iniqgu2l0ek" path="res://dynamic-sky/resources/sky_config.gd" id="5_2w5ig"]
|
||||
[ext_resource type="Script" uid="uid://cwplvsarljsf" path="res://dynamic-sky/scripts/cloud_system_2d.gd" id="5_clouds"]
|
||||
[ext_resource type="Script" uid="uid://cwqg0bg2m2bme" path="res://dynamic-sky/scripts/post_process_controller.gd" id="6_post_process"]
|
||||
[ext_resource type="Texture2D" uid="uid://crrj260kiyoy" path="res://dynamic-sky/sky_44_2k.png" id="6_ssi15"]
|
||||
[ext_resource type="Script" uid="uid://cnvj3m5fpsdtj" path="res://dynamic-sky/scripts/vfx_controller.gd" id="7_vfx"]
|
||||
[ext_resource type="Script" uid="uid://d2srir3wsi045" path="res://dynamic-sky/resources/weather_profile.gd" id="8_cnx2n"]
|
||||
[ext_resource type="Script" uid="uid://hvs2g6acqryq" path="res://dynamic-sky/scripts/shadow_proxy_system.gd" id="8_shadows"]
|
||||
[ext_resource type="Script" uid="uid://ctrw2ycud36yd" path="res://dynamic-sky/resources/post_process_profile.gd" id="11_djhca"]
|
||||
[ext_resource type="Environment" uid="uid://dq64sos14f8hu" path="res://dynamic-sky/environment.tres" id="13_2w5ig"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_c27no"]
|
||||
script = ExtResource("5_2w5ig")
|
||||
sun_color = Color(1, 0.95, 0.85, 1)
|
||||
sun_intensity = 1.2
|
||||
moon_intensity = 0.0
|
||||
ambient_color = Color(0.5, 0.6, 0.8, 1)
|
||||
ambient_intensity = 0.6
|
||||
fog_color = Color(0.7, 0.8, 0.95, 1)
|
||||
fog_density = 0.005
|
||||
sky_top_color = Color(0.2, 0.4, 0.85, 1)
|
||||
sky_horizon_color = Color(0.6, 0.75, 0.95, 1)
|
||||
sky_bottom_color = Color(0.5, 0.6, 0.7, 1)
|
||||
cloud_coverage = 0.2
|
||||
cloud_opacity = 0.7
|
||||
sun_glow_intensity = 0.3
|
||||
|
||||
[sub_resource type="Resource" id="Resource_ssi15"]
|
||||
script = ExtResource("11_djhca")
|
||||
metadata/_custom_type_script = "uid://ctrw2ycud36yd"
|
||||
|
||||
[sub_resource type="Resource" id="Resource_ajt3x"]
|
||||
script = ExtResource("3_c6jpa")
|
||||
preset_name = "Clear Day"
|
||||
preset_type = 0
|
||||
config = SubResource("Resource_c27no")
|
||||
skybox_texture = ExtResource("6_ssi15")
|
||||
post_process_profile = SubResource("Resource_ssi15")
|
||||
|
||||
[sub_resource type="Resource" id="Resource_cp2fs"]
|
||||
script = ExtResource("5_2w5ig")
|
||||
sun_color = Color(1, 0.5, 0.2, 1)
|
||||
sun_intensity = 0.8
|
||||
moon_intensity = 0.0
|
||||
ambient_color = Color(0.8, 0.5, 0.4, 1)
|
||||
ambient_intensity = 0.4
|
||||
fog_color = Color(0.9, 0.6, 0.4, 1)
|
||||
fog_density = 0.015
|
||||
sky_top_color = Color(0.3, 0.2, 0.5, 1)
|
||||
sky_horizon_color = Color(1, 0.5, 0.3, 1)
|
||||
sky_bottom_color = Color(0.9, 0.4, 0.2, 1)
|
||||
stars_visibility = 0.1
|
||||
cloud_opacity = 0.9
|
||||
cloud_color = Color(1, 0.7, 0.5, 1)
|
||||
cloud_shadow_color = Color(0.4, 0.2, 0.3, 1)
|
||||
sun_glow_intensity = 0.8
|
||||
sun_glow_size = 0.4
|
||||
|
||||
[sub_resource type="Resource" id="Resource_sptlx"]
|
||||
script = ExtResource("3_c6jpa")
|
||||
preset_name = "Sunset"
|
||||
preset_type = 1
|
||||
config = SubResource("Resource_cp2fs")
|
||||
|
||||
[sub_resource type="Resource" id="Resource_bmqu2"]
|
||||
script = ExtResource("5_2w5ig")
|
||||
sun_intensity = 0.0
|
||||
moon_color = Color(0.7, 0.8, 1, 1)
|
||||
moon_intensity = 0.4
|
||||
ambient_color = Color(0.1, 0.15, 0.25, 1)
|
||||
ambient_intensity = 0.2
|
||||
fog_color = Color(0.1, 0.12, 0.2, 1)
|
||||
fog_density = 0.02
|
||||
sky_top_color = Color(0.02, 0.03, 0.08, 1)
|
||||
sky_horizon_color = Color(0.1, 0.12, 0.2, 1)
|
||||
sky_bottom_color = Color(0.05, 0.06, 0.1, 1)
|
||||
stars_visibility = 1.0
|
||||
cloud_coverage = 0.15
|
||||
cloud_opacity = 0.5
|
||||
cloud_color = Color(0.2, 0.22, 0.3, 1)
|
||||
cloud_shadow_color = Color(0.05, 0.05, 0.1, 1)
|
||||
sun_glow_intensity = 0.0
|
||||
|
||||
[sub_resource type="Resource" id="Resource_fqsrh"]
|
||||
script = ExtResource("3_c6jpa")
|
||||
preset_name = "Night"
|
||||
preset_type = 2
|
||||
config = SubResource("Resource_bmqu2")
|
||||
|
||||
[sub_resource type="Resource" id="Resource_4i5ra"]
|
||||
script = ExtResource("5_2w5ig")
|
||||
sun_color = Color(0.9, 0.9, 0.85, 1)
|
||||
sun_intensity = 0.4
|
||||
moon_intensity = 0.0
|
||||
ambient_color = Color(0.5, 0.5, 0.55, 1)
|
||||
ambient_intensity = 0.7
|
||||
fog_color = Color(0.6, 0.62, 0.65, 1)
|
||||
fog_density = 0.03
|
||||
sky_top_color = Color(0.45, 0.48, 0.55, 1)
|
||||
sky_horizon_color = Color(0.6, 0.62, 0.65, 1)
|
||||
sky_bottom_color = Color(0.5, 0.52, 0.55, 1)
|
||||
cloud_coverage = 0.9
|
||||
cloud_opacity = 1.0
|
||||
cloud_color = Color(0.7, 0.72, 0.75, 1)
|
||||
cloud_shadow_color = Color(0.4, 0.42, 0.45, 1)
|
||||
sun_glow_intensity = 0.1
|
||||
|
||||
[sub_resource type="Resource" id="Resource_drex5"]
|
||||
script = ExtResource("3_c6jpa")
|
||||
preset_name = "Overcast"
|
||||
preset_type = 3
|
||||
config = SubResource("Resource_4i5ra")
|
||||
|
||||
[sub_resource type="Resource" id="Resource_gm4lj"]
|
||||
script = ExtResource("5_2w5ig")
|
||||
sun_color = Color(0.7, 0.7, 0.75, 1)
|
||||
sun_intensity = 0.2
|
||||
moon_intensity = 0.0
|
||||
ambient_color = Color(0.35, 0.38, 0.45, 1)
|
||||
fog_color = Color(0.4, 0.42, 0.5, 1)
|
||||
fog_density = 0.05
|
||||
sky_top_color = Color(0.25, 0.28, 0.35, 1)
|
||||
sky_horizon_color = Color(0.4, 0.42, 0.48, 1)
|
||||
sky_bottom_color = Color(0.35, 0.38, 0.42, 1)
|
||||
cloud_coverage = 1.0
|
||||
cloud_opacity = 1.0
|
||||
cloud_color = Color(0.4, 0.42, 0.5, 1)
|
||||
cloud_shadow_color = Color(0.2, 0.22, 0.28, 1)
|
||||
sun_glow_intensity = 0.0
|
||||
|
||||
[sub_resource type="Resource" id="Resource_5lttf"]
|
||||
script = ExtResource("3_c6jpa")
|
||||
preset_name = "Storm"
|
||||
preset_type = 4
|
||||
config = SubResource("Resource_gm4lj")
|
||||
|
||||
[sub_resource type="Resource" id="Resource_ym26c"]
|
||||
script = ExtResource("5_2w5ig")
|
||||
sun_color = Color(1, 0.7, 0.5, 1)
|
||||
sun_intensity = 0.5
|
||||
moon_intensity = 0.1
|
||||
ambient_color = Color(0.4, 0.35, 0.45, 1)
|
||||
ambient_intensity = 0.35
|
||||
fog_color = Color(0.6, 0.5, 0.55, 1)
|
||||
fog_density = 0.025
|
||||
sky_top_color = Color(0.15, 0.2, 0.45, 1)
|
||||
sky_horizon_color = Color(0.9, 0.6, 0.5, 1)
|
||||
sky_bottom_color = Color(0.7, 0.45, 0.4, 1)
|
||||
stars_visibility = 0.3
|
||||
cloud_coverage = 0.25
|
||||
cloud_color = Color(0.95, 0.7, 0.6, 1)
|
||||
cloud_shadow_color = Color(0.3, 0.25, 0.35, 1)
|
||||
sun_glow_intensity = 0.6
|
||||
sun_glow_size = 0.35
|
||||
|
||||
[sub_resource type="Resource" id="Resource_umd7u"]
|
||||
script = ExtResource("3_c6jpa")
|
||||
preset_name = "Dawn"
|
||||
preset_type = 5
|
||||
config = SubResource("Resource_ym26c")
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_sxvon"]
|
||||
offsets = PackedFloat32Array(0, 0.25, 0.5, 0.75, 1)
|
||||
colors = PackedColorArray(1, 0.6, 0.4, 1, 1, 0.5, 0.3, 1, 1, 1, 0.95, 1, 1, 0.7, 0.4, 1, 1, 1, 1, 1)
|
||||
|
||||
[sub_resource type="Curve" id="Curve_fm80q"]
|
||||
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.2, 0), 0.0, 0.0, 0, 0, Vector2(0.3, 0.8), 0.0, 0.0, 0, 0, Vector2(0.5, 1), 0.0, 0.0, 0, 0, Vector2(0.7, 0.8), 0.0, 0.0, 0, 0, Vector2(0.85, 0), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
|
||||
point_count = 7
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_kcyr7"]
|
||||
colors = PackedColorArray(0.7, 0.8, 1, 1, 0.7, 0.8, 1, 1)
|
||||
|
||||
[sub_resource type="Curve" id="Curve_qti3v"]
|
||||
_data = [Vector2(0, 0.4), 0.0, 0.0, 0, 0, Vector2(0.2, 0.3), 0.0, 0.0, 0, 0, Vector2(0.25, 0), 0.0, 0.0, 0, 0, Vector2(0.75, 0), 0.0, 0.0, 0, 0, Vector2(0.85, 0.3), 0.0, 0.0, 0, 0, Vector2(1, 0.4), 0.0, 0.0, 0, 0]
|
||||
point_count = 6
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_bq8yq"]
|
||||
offsets = PackedFloat32Array(0, 0.25, 0.5, 0.75, 1)
|
||||
colors = PackedColorArray(0.15, 0.18, 0.3, 1, 0.15, 0.18, 0.3, 1, 0.6, 0.65, 0.75, 1, 0.55, 0.5, 0.55, 1, 1, 1, 1, 1)
|
||||
|
||||
[sub_resource type="Curve" id="Curve_x6xf2"]
|
||||
_data = [Vector2(0, 0.2), 0.0, 0.0, 0, 0, Vector2(0.25, 0.5), 0.0, 0.0, 0, 0, Vector2(0.5, 0.7), 0.0, 0.0, 0, 0, Vector2(0.75, 0.5), 0.0, 0.0, 0, 0, Vector2(1, 0.2), 0.0, 0.0, 0, 0]
|
||||
point_count = 5
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_tn5up"]
|
||||
offsets = PackedFloat32Array(0, 0.25, 0.5, 0.75, 1)
|
||||
colors = PackedColorArray(0.1, 0.12, 0.2, 1, 0.1, 0.12, 0.2, 1, 0.7, 0.75, 0.85, 1, 0.7, 0.55, 0.5, 1, 1, 1, 1, 1)
|
||||
|
||||
[sub_resource type="Curve" id="Curve_ej5u1"]
|
||||
_data = [Vector2(0, 0.02), 0.0, 0.0, 0, 0, Vector2(0.25, 0.015), 0.0, 0.0, 0, 0, Vector2(0.5, 0.01), 0.0, 0.0, 0, 0, Vector2(0.75, 0.015), 0.0, 0.0, 0, 0, Vector2(1, 0.02), 0.0, 0.0, 0, 0]
|
||||
point_count = 5
|
||||
|
||||
[sub_resource type="Curve" id="Curve_ta3fj"]
|
||||
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(0.2, 0.8), 0.0, 0.0, 0, 0, Vector2(0.3, 0), 0.0, 0.0, 0, 0, Vector2(0.7, 0), 0.0, 0.0, 0, 0, Vector2(0.85, 0.8), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
|
||||
point_count = 6
|
||||
|
||||
[sub_resource type="Resource" id="Resource_dfjcy"]
|
||||
script = ExtResource("8_cnx2n")
|
||||
cloud_density = 0.1
|
||||
cloud_opacity = 0.6
|
||||
cloud_coverage = 0.2
|
||||
wind_speed = 0.5
|
||||
|
||||
[sub_resource type="Resource" id="Resource_sjmt0"]
|
||||
script = ExtResource("8_cnx2n")
|
||||
weather_state = 1
|
||||
display_name = "Cloudy"
|
||||
cloud_density = 0.5
|
||||
cloud_opacity = 0.8
|
||||
cloud_coverage = 0.5
|
||||
ambient_intensity_multiplier = 0.9
|
||||
|
||||
[sub_resource type="Resource" id="Resource_uap2q"]
|
||||
script = ExtResource("8_cnx2n")
|
||||
weather_state = 2
|
||||
display_name = "Overcast"
|
||||
cloud_density = 0.9
|
||||
cloud_opacity = 1.0
|
||||
cloud_coverage = 0.9
|
||||
cloud_color = Color(0.7, 0.72, 0.75, 1)
|
||||
wind_speed = 1.5
|
||||
ambient_intensity_multiplier = 0.7
|
||||
fog_density_multiplier = 1.5
|
||||
|
||||
[sub_resource type="Resource" id="Resource_mcoyn"]
|
||||
script = ExtResource("8_cnx2n")
|
||||
weather_state = 3
|
||||
display_name = "Rain"
|
||||
cloud_density = 0.85
|
||||
cloud_opacity = 0.95
|
||||
cloud_coverage = 0.85
|
||||
cloud_color = Color(0.55, 0.58, 0.65, 1)
|
||||
wind_speed = 2.0
|
||||
ambient_intensity_multiplier = 0.6
|
||||
fog_density_multiplier = 2.0
|
||||
rain_intensity = 0.7
|
||||
default_transition_duration = 8.0
|
||||
|
||||
[sub_resource type="Resource" id="Resource_mr14h"]
|
||||
script = ExtResource("8_cnx2n")
|
||||
weather_state = 4
|
||||
display_name = "Storm"
|
||||
cloud_density = 1.0
|
||||
cloud_opacity = 1.0
|
||||
cloud_coverage = 1.0
|
||||
cloud_color = Color(0.4, 0.42, 0.5, 1)
|
||||
cloud_shadow_color = Color(0.2, 0.22, 0.28, 1)
|
||||
wind_speed = 4.0
|
||||
wind_turbulence = 0.4
|
||||
ambient_intensity_multiplier = 0.4
|
||||
fog_density_multiplier = 3.0
|
||||
rain_intensity = 1.0
|
||||
lightning_enabled = true
|
||||
lightning_frequency = 0.15
|
||||
default_transition_duration = 10.0
|
||||
|
||||
[sub_resource type="Resource" id="Resource_ps44b"]
|
||||
script = ExtResource("8_cnx2n")
|
||||
weather_state = 5
|
||||
display_name = "Snow"
|
||||
cloud_density = 0.8
|
||||
cloud_opacity = 0.9
|
||||
cloud_coverage = 0.8
|
||||
cloud_color = Color(0.85, 0.87, 0.92, 1)
|
||||
ambient_color_multiplier = Color(0.9, 0.92, 1, 1)
|
||||
ambient_intensity_multiplier = 0.75
|
||||
fog_color_multiplier = Color(0.95, 0.97, 1, 1)
|
||||
fog_density_multiplier = 1.8
|
||||
snow_intensity = 0.8
|
||||
default_transition_duration = 12.0
|
||||
|
||||
[sub_resource type="Curve" id="Curve_uhdsh"]
|
||||
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.5, 0.5), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
|
||||
point_count = 3
|
||||
|
||||
[sub_resource type="Resource" id="Resource_xh4oc"]
|
||||
script = ExtResource("4_ssi15")
|
||||
layer_type = 0
|
||||
tiling = Vector2(2, 2)
|
||||
opacity = 0.9
|
||||
flow_speed = 0.03
|
||||
parallax_depth = 0.3
|
||||
|
||||
[sub_resource type="Resource" id="Resource_ahi10"]
|
||||
script = ExtResource("4_ssi15")
|
||||
tiling = Vector2(3, 3)
|
||||
opacity = 0.7
|
||||
parallax_depth = 0.6
|
||||
|
||||
[sub_resource type="Resource" id="Resource_a5xai"]
|
||||
script = ExtResource("4_ssi15")
|
||||
layer_type = 2
|
||||
opacity = 0.5
|
||||
flow_speed = 0.01
|
||||
parallax_depth = 0.9
|
||||
|
||||
[sub_resource type="Resource" id="Resource_uxs4q"]
|
||||
script = ExtResource("4_ssi15")
|
||||
layer_type = 3
|
||||
tiling = Vector2(6, 6)
|
||||
opacity = 0.3
|
||||
flow_speed = 0.015
|
||||
|
||||
[sub_resource type="Compositor" id="Compositor_ssi15"]
|
||||
|
||||
[node name="DynamicSky" type="Node3D" unique_id=516593260 node_paths=PackedStringArray("preset_library", "day_night_controller", "weather_controller", "cloud_system", "post_process_controller", "vfx_controller", "shadow_proxy_system", "world_environment", "sun_light", "moon_light")]
|
||||
script = ExtResource("1_dynamic_sky")
|
||||
preset_library = NodePath("PresetLibrary")
|
||||
day_night_controller = NodePath("DayNightController")
|
||||
weather_controller = NodePath("WeatherController")
|
||||
cloud_system = NodePath("CloudSystem")
|
||||
post_process_controller = NodePath("PostProcessController")
|
||||
vfx_controller = NodePath("VFXController")
|
||||
shadow_proxy_system = NodePath("ShadowProxySystem")
|
||||
world_environment = NodePath("WorldEnvironment")
|
||||
sun_light = NodePath("SunLight")
|
||||
moon_light = NodePath("MoonLight")
|
||||
|
||||
[node name="PresetLibrary" type="Node" parent="." unique_id=1947927418]
|
||||
script = ExtResource("2_preset_library")
|
||||
presets = Array[ExtResource("3_c6jpa")]([SubResource("Resource_ajt3x"), SubResource("Resource_sptlx"), SubResource("Resource_fqsrh"), SubResource("Resource_drex5"), SubResource("Resource_5lttf"), SubResource("Resource_umd7u")])
|
||||
|
||||
[node name="DayNightController" type="Node" parent="." unique_id=1083149574]
|
||||
script = ExtResource("3_day_night")
|
||||
time_of_day = 0.1482273141975634
|
||||
current_day = 4
|
||||
sun_color_curve = SubResource("Gradient_sxvon")
|
||||
sun_intensity_curve = SubResource("Curve_fm80q")
|
||||
moon_color_curve = SubResource("Gradient_kcyr7")
|
||||
moon_intensity_curve = SubResource("Curve_qti3v")
|
||||
ambient_color_curve = SubResource("Gradient_bq8yq")
|
||||
ambient_intensity_curve = SubResource("Curve_x6xf2")
|
||||
fog_color_curve = SubResource("Gradient_tn5up")
|
||||
fog_density_curve = SubResource("Curve_ej5u1")
|
||||
stars_visibility_curve = SubResource("Curve_ta3fj")
|
||||
|
||||
[node name="WeatherController" type="Node" parent="." unique_id=1440281936]
|
||||
script = ExtResource("4_weather")
|
||||
weather_profiles = Array[ExtResource("8_cnx2n")]([SubResource("Resource_dfjcy"), SubResource("Resource_sjmt0"), SubResource("Resource_uap2q"), SubResource("Resource_mcoyn"), SubResource("Resource_mr14h"), SubResource("Resource_ps44b")])
|
||||
transition_curve = SubResource("Curve_uhdsh")
|
||||
|
||||
[node name="CloudSystem" type="Node3D" parent="." unique_id=680200253]
|
||||
script = ExtResource("5_clouds")
|
||||
layer_configs = Array[ExtResource("4_ssi15")]([SubResource("Resource_xh4oc"), SubResource("Resource_ahi10"), SubResource("Resource_a5xai"), SubResource("Resource_uxs4q")])
|
||||
global_coverage = 0.2
|
||||
global_wind_speed = 0.5
|
||||
sun_direction = Vector3(0.6647294, -0.6939493, -0.2767116)
|
||||
sun_color = Color(1, 0.55136895, 0.3513689, 1)
|
||||
sun_intensity = 0.0
|
||||
moon_direction = Vector3(0.6799649, 0.7098545, 0.18372385)
|
||||
moon_intensity = 0.33406785
|
||||
|
||||
[node name="PostProcessController" type="Node" parent="." unique_id=186747041]
|
||||
script = ExtResource("6_post_process")
|
||||
profile = SubResource("Resource_ssi15")
|
||||
target_environment = ExtResource("13_2w5ig")
|
||||
sky_top_color = Color(0.2, 0.4, 0.85, 1)
|
||||
sky_horizon_color = Color(0.6, 0.75, 0.95, 1)
|
||||
sky_bottom_color = Color(0.5, 0.6, 0.7, 1)
|
||||
skybox_texture = ExtResource("6_ssi15")
|
||||
fog_color = Color(0.1, 0.12, 0.2, 1)
|
||||
fog_density = 0.017602645
|
||||
sun_direction = Vector3(0.6647294, -0.6939493, -0.2767116)
|
||||
|
||||
[node name="VFXController" type="Node3D" parent="." unique_id=207251300]
|
||||
script = ExtResource("7_vfx")
|
||||
|
||||
[node name="ShadowProxySystem" type="Node3D" parent="." unique_id=1982828842]
|
||||
script = ExtResource("8_shadows")
|
||||
shadow_opacity = 0.1
|
||||
flow_speed = 2.5
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=700760798]
|
||||
environment = ExtResource("13_2w5ig")
|
||||
compositor = SubResource("Compositor_ssi15")
|
||||
|
||||
[node name="SunLight" type="DirectionalLight3D" parent="." unique_id=1278065589]
|
||||
transform = Transform3D(-0.38430893, -0.9231061, 0.0134779755, 0, 0.014599117, 0.99989367, -0.9232045, 0.384268, -0.0056105736, 0, 50, 0)
|
||||
visible = false
|
||||
light_color = Color(1, 0.55136895, 0.3513689, 1)
|
||||
light_energy = 0.0
|
||||
shadow_enabled = true
|
||||
|
||||
[node name="MoonLight" type="DirectionalLight3D" parent="." unique_id=52072807]
|
||||
transform = Transform3D(0.2608423, -0.9652886, 0.013407094, 0, 0.013887873, 0.9999038, -0.9653814, -0.26081723, 0.003622545, 0, 50, 0)
|
||||
light_color = Color(0.7, 0.8, 1, 1)
|
||||
light_energy = 0.33406785
|
||||
25
dynamic-sky/dynamic_sky_toy.tscn
Normal file
25
dynamic-sky/dynamic_sky_toy.tscn
Normal file
@@ -0,0 +1,25 @@
|
||||
[gd_scene format=3 uid="uid://c0752ygxeq4rc"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://doiojb8xxlp2s" path="res://dynamic-sky/dynamic_sky.tscn" id="1_cuadp"]
|
||||
[ext_resource type="Script" uid="uid://cimhpqq1qo80b" path="res://dynamic-sky/scripts/sky_debug_panel.gd" id="2_o3fwy"]
|
||||
|
||||
[node name="DynamicSkyToy" type="Node3D" unique_id=1968105337]
|
||||
|
||||
[node name="DynamicSky" parent="." unique_id=516593260 instance=ExtResource("1_cuadp")]
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="." unique_id=805686804]
|
||||
|
||||
[node name="Control" type="Control" parent="." unique_id=83422594]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="SkyDebugPanel" type="Control" parent="Control" unique_id=1408698131]
|
||||
anchors_preset = 0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
script = ExtResource("2_o3fwy")
|
||||
metadata/_custom_type_script = "uid://cimhpqq1qo80b"
|
||||
136
dynamic-sky/environment.tres
Normal file
136
dynamic-sky/environment.tres
Normal file
@@ -0,0 +1,136 @@
|
||||
[gd_resource type="Environment" format=3 uid="uid://dq64sos14f8hu"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://crrj260kiyoy" path="res://dynamic-sky/sky_44_2k.png" id="1_vd8dq"]
|
||||
|
||||
[sub_resource type="Shader" id="Shader_porxs"]
|
||||
code = "
|
||||
shader_type sky;
|
||||
|
||||
uniform sampler2D skybox_texture : source_color, filter_linear_mipmap, repeat_enable;
|
||||
uniform bool skybox_enabled = false;
|
||||
uniform float skybox_blend = 1.0;
|
||||
|
||||
uniform vec4 sky_top_color : source_color = vec4(0.2, 0.4, 0.8, 1.0);
|
||||
uniform vec4 sky_horizon_color : source_color = vec4(0.6, 0.7, 0.9, 1.0);
|
||||
uniform vec4 sky_bottom_color : source_color = vec4(0.4, 0.5, 0.6, 1.0);
|
||||
uniform float gradient_intensity = 1.0;
|
||||
uniform float horizon_blend = 0.5;
|
||||
|
||||
uniform bool sun_glow_enabled = true;
|
||||
uniform vec3 sun_direction = vec3(0.0, 1.0, 0.0);
|
||||
uniform vec4 sun_glow_color : source_color = vec4(1.0, 0.95, 0.8, 1.0);
|
||||
uniform float sun_glow_intensity = 0.5;
|
||||
uniform float sun_glow_size = 0.2;
|
||||
uniform float sun_glow_falloff = 2.0;
|
||||
|
||||
uniform bool dawn_dusk_enabled = true;
|
||||
uniform vec4 dawn_dusk_color : source_color = vec4(1.0, 0.6, 0.4, 1.0);
|
||||
uniform float dawn_dusk_intensity = 0.5;
|
||||
uniform float dawn_dusk_blend = 0.0;
|
||||
|
||||
uniform float stars_visibility = 0.0;
|
||||
uniform vec4 stars_tint : source_color = vec4(1.0);
|
||||
|
||||
float hash(vec2 p) {
|
||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
|
||||
}
|
||||
|
||||
float stars(vec3 dir) {
|
||||
vec2 uv = dir.xz / (abs(dir.y) + 0.001) * 50.0;
|
||||
vec2 cell = floor(uv);
|
||||
vec2 local = fract(uv) - 0.5;
|
||||
|
||||
float star = 0.0;
|
||||
float h = hash(cell);
|
||||
if (h > 0.97) {
|
||||
float dist = length(local);
|
||||
star = smoothstep(0.1, 0.0, dist) * (h - 0.97) / 0.03;
|
||||
star *= step(0.0, dir.y);
|
||||
}
|
||||
return star;
|
||||
}
|
||||
|
||||
vec2 pano_uv(vec3 dir) {
|
||||
dir = normalize(dir);
|
||||
float u = atan(dir.x, dir.z) / (2.0 * PI) + 0.5;
|
||||
float v = acos(clamp(dir.y, -1.0, 1.0)) / PI;
|
||||
return vec2(u, v);
|
||||
}
|
||||
|
||||
void sky() {
|
||||
vec3 dir = EYEDIR;
|
||||
float y = dir.y;
|
||||
|
||||
vec3 color;
|
||||
if (y > 0.0) {
|
||||
float t = pow(y, horizon_blend);
|
||||
color = mix(sky_horizon_color.rgb, sky_top_color.rgb, t);
|
||||
} else {
|
||||
float t = pow(-y, horizon_blend);
|
||||
color = mix(sky_horizon_color.rgb, sky_bottom_color.rgb, t);
|
||||
}
|
||||
|
||||
color = mix(vec3(0.5), color, gradient_intensity);
|
||||
|
||||
if (skybox_enabled) {
|
||||
vec3 pano_color = texture(skybox_texture, pano_uv(dir)).rgb;
|
||||
color = mix(color, pano_color, skybox_blend);
|
||||
}
|
||||
|
||||
if (sun_glow_enabled && sun_direction.y > -0.2) {
|
||||
float sun_dot = max(0.0, dot(dir, normalize(sun_direction)));
|
||||
float glow = pow(sun_dot, 1.0 / max(0.001, sun_glow_size));
|
||||
glow = pow(glow, sun_glow_falloff);
|
||||
color += sun_glow_color.rgb * glow * sun_glow_intensity;
|
||||
}
|
||||
|
||||
if (dawn_dusk_enabled && dawn_dusk_blend > 0.0) {
|
||||
float horizon_factor = 1.0 - abs(y);
|
||||
horizon_factor = pow(horizon_factor, 2.0);
|
||||
color = mix(color, dawn_dusk_color.rgb, horizon_factor * dawn_dusk_intensity * dawn_dusk_blend);
|
||||
}
|
||||
|
||||
if (stars_visibility > 0.0 && y > 0.0) {
|
||||
float star_value = stars(dir) * stars_visibility;
|
||||
color += stars_tint.rgb * star_value;
|
||||
}
|
||||
|
||||
COLOR = color;
|
||||
}
|
||||
"
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_jmmec"]
|
||||
shader = SubResource("Shader_porxs")
|
||||
shader_parameter/skybox_texture = ExtResource("1_vd8dq")
|
||||
shader_parameter/skybox_enabled = true
|
||||
shader_parameter/skybox_blend = 1.0
|
||||
shader_parameter/sky_top_color = Color(0.2, 0.4, 0.85, 1)
|
||||
shader_parameter/sky_horizon_color = Color(0.6, 0.75, 0.95, 1)
|
||||
shader_parameter/sky_bottom_color = Color(0.5, 0.6, 0.7, 1)
|
||||
shader_parameter/gradient_intensity = 1.0
|
||||
shader_parameter/horizon_blend = 0.5
|
||||
shader_parameter/sun_glow_enabled = true
|
||||
shader_parameter/sun_direction = Vector3(0.9079945, -0.38918287, -0.1551863)
|
||||
shader_parameter/sun_glow_color = Color(1, 0.5257787, 0.32577872, 1)
|
||||
shader_parameter/sun_glow_intensity = 0.0
|
||||
shader_parameter/sun_glow_size = 0.2
|
||||
shader_parameter/sun_glow_falloff = 2.0
|
||||
shader_parameter/dawn_dusk_enabled = true
|
||||
shader_parameter/dawn_dusk_color = Color(1, 0.6, 0.4, 1)
|
||||
shader_parameter/dawn_dusk_intensity = 0.5
|
||||
shader_parameter/dawn_dusk_blend = 0.0
|
||||
shader_parameter/stars_visibility = 0.80297995
|
||||
shader_parameter/stars_tint = Color(1, 1, 1, 1)
|
||||
|
||||
[sub_resource type="Sky" id="Sky_ssi15"]
|
||||
sky_material = SubResource("ShaderMaterial_jmmec")
|
||||
|
||||
[resource]
|
||||
background_mode = 2
|
||||
sky = SubResource("Sky_ssi15")
|
||||
ambient_light_color = Color(0.15, 0.18, 0.3, 1)
|
||||
ambient_light_energy = 0.4504698
|
||||
fog_enabled = true
|
||||
fog_light_color = Color(0.1, 0.12, 0.2, 1)
|
||||
fog_density = 0.015825503
|
||||
fog_height_density = 0.1
|
||||
17
dynamic-sky/presets/clear_day.tres
Normal file
17
dynamic-sky/presets/clear_day.tres
Normal file
@@ -0,0 +1,17 @@
|
||||
[gd_resource type="Resource" script_class="SkyPreset" format=3 uid="uid://dose3mxe2snku"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c48eq2y4ikok8" path="res://dynamic-sky/resources/sky_preset.gd" id="1"]
|
||||
[ext_resource type="Script" uid="uid://beu0xhk0rvpky" path="res://dynamic-sky/resources/cloud_layer_config.gd" id="1_5fmul"]
|
||||
[ext_resource type="Script" uid="uid://c3iniqgu2l0ek" path="res://dynamic-sky/resources/sky_config.gd" id="2_2a7hp"]
|
||||
[ext_resource type="Texture2D" uid="uid://crrj260kiyoy" path="res://dynamic-sky/sky_44_2k.png" id="4_a3ytv"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_ktpc1"]
|
||||
script = ExtResource("2_2a7hp")
|
||||
metadata/_custom_type_script = "uid://c3iniqgu2l0ek"
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
preset_name = "Clear Day"
|
||||
preset_type = 0
|
||||
config = SubResource("Resource_ktpc1")
|
||||
skybox_texture = ExtResource("4_a3ytv")
|
||||
17
dynamic-sky/presets/dawn.tres
Normal file
17
dynamic-sky/presets/dawn.tres
Normal file
@@ -0,0 +1,17 @@
|
||||
[gd_resource type="Resource" script_class="SkyPreset" format=3 uid="uid://cg0qi1v475fvo"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c48eq2y4ikok8" path="res://dynamic-sky/resources/sky_preset.gd" id="1"]
|
||||
[ext_resource type="Script" uid="uid://beu0xhk0rvpky" path="res://dynamic-sky/resources/cloud_layer_config.gd" id="1_etfyk"]
|
||||
[ext_resource type="Script" uid="uid://c3iniqgu2l0ek" path="res://dynamic-sky/resources/sky_config.gd" id="2_yd2ys"]
|
||||
[ext_resource type="Texture2D" uid="uid://sl180wnwa3su" path="res://dynamic-sky/sky_25_2k.png" id="4_ckylf"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_o0y23"]
|
||||
script = ExtResource("2_yd2ys")
|
||||
metadata/_custom_type_script = "uid://c3iniqgu2l0ek"
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
preset_name = "Dawn"
|
||||
preset_type = 5
|
||||
config = SubResource("Resource_o0y23")
|
||||
skybox_texture = ExtResource("4_ckylf")
|
||||
11
dynamic-sky/presets/night.tres
Normal file
11
dynamic-sky/presets/night.tres
Normal file
@@ -0,0 +1,11 @@
|
||||
[gd_resource type="Resource" script_class="SkyPreset" format=3 uid="uid://b08fj288b38q6"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c48eq2y4ikok8" path="res://dynamic-sky/resources/sky_preset.gd" id="1"]
|
||||
[ext_resource type="Script" uid="uid://beu0xhk0rvpky" path="res://dynamic-sky/resources/cloud_layer_config.gd" id="1_eyyte"]
|
||||
[ext_resource type="Texture2D" uid="uid://cu4ogwg6537jc" path="res://dynamic-sky/sky_16_2k.png" id="3_75xut"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
preset_name = "Night"
|
||||
preset_type = 2
|
||||
skybox_texture = ExtResource("3_75xut")
|
||||
15
dynamic-sky/presets/overcast.tres
Normal file
15
dynamic-sky/presets/overcast.tres
Normal file
@@ -0,0 +1,15 @@
|
||||
[gd_resource type="Resource" script_class="SkyPreset" format=3 uid="uid://b6hytb5q01khi"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c48eq2y4ikok8" path="res://dynamic-sky/resources/sky_preset.gd" id="1"]
|
||||
[ext_resource type="Script" uid="uid://beu0xhk0rvpky" path="res://dynamic-sky/resources/cloud_layer_config.gd" id="1_vtd2i"]
|
||||
[ext_resource type="Script" uid="uid://c3iniqgu2l0ek" path="res://dynamic-sky/resources/sky_config.gd" id="2_1tetv"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_k34u2"]
|
||||
script = ExtResource("2_1tetv")
|
||||
metadata/_custom_type_script = "uid://c3iniqgu2l0ek"
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
preset_name = "Overcast"
|
||||
preset_type = 3
|
||||
config = SubResource("Resource_k34u2")
|
||||
8
dynamic-sky/presets/storm.tres
Normal file
8
dynamic-sky/presets/storm.tres
Normal file
@@ -0,0 +1,8 @@
|
||||
[gd_resource type="Resource" script_class="SkyPreset" load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://dynamic-sky/resources/sky_preset.gd" id="1"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
preset_name = "Storm"
|
||||
preset_type = 4
|
||||
8
dynamic-sky/presets/sunset.tres
Normal file
8
dynamic-sky/presets/sunset.tres
Normal file
@@ -0,0 +1,8 @@
|
||||
[gd_resource type="Resource" script_class="SkyPreset" load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://dynamic-sky/resources/sky_preset.gd" id="1"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
preset_name = "Sunset"
|
||||
preset_type = 1
|
||||
65
dynamic-sky/resources/cloud_layer_config.gd
Normal file
65
dynamic-sky/resources/cloud_layer_config.gd
Normal file
@@ -0,0 +1,65 @@
|
||||
@tool
|
||||
class_name CloudLayerConfig
|
||||
extends Resource
|
||||
|
||||
enum LayerType {
|
||||
LOW,
|
||||
MID,
|
||||
HIGH,
|
||||
WISPS
|
||||
}
|
||||
|
||||
@export var layer_type: LayerType = LayerType.MID
|
||||
@export var enabled: bool = true
|
||||
|
||||
@export_group("Texture")
|
||||
@export var texture: Texture2D
|
||||
@export var texture_scale: Vector2 = Vector2(1.0, 1.0)
|
||||
@export var tiling: Vector2 = Vector2(4.0, 4.0)
|
||||
|
||||
@export_group("Appearance")
|
||||
@export var opacity: float = 0.8
|
||||
@export var color_tint: Color = Color.WHITE
|
||||
@export var shadow_color: Color = Color(0.6, 0.6, 0.7)
|
||||
|
||||
@export_group("Motion")
|
||||
@export var flow_direction: Vector2 = Vector2(1.0, 0.0)
|
||||
@export var flow_speed: float = 0.02
|
||||
@export var parallax_depth: float = 1.0
|
||||
|
||||
@export_group("Lighting")
|
||||
@export var receive_light: bool = true
|
||||
@export var light_influence: float = 1.0
|
||||
@export var rim_glow_intensity: float = 0.3
|
||||
|
||||
|
||||
func get_layer_height() -> float:
|
||||
match layer_type:
|
||||
LayerType.LOW:
|
||||
return 0.2
|
||||
LayerType.MID:
|
||||
return 0.5
|
||||
LayerType.HIGH:
|
||||
return 0.8
|
||||
LayerType.WISPS:
|
||||
return 0.9
|
||||
return 0.5
|
||||
|
||||
|
||||
func duplicate_config() -> CloudLayerConfig:
|
||||
var copy := CloudLayerConfig.new()
|
||||
copy.layer_type = layer_type
|
||||
copy.enabled = enabled
|
||||
copy.texture = texture
|
||||
copy.texture_scale = texture_scale
|
||||
copy.tiling = tiling
|
||||
copy.opacity = opacity
|
||||
copy.color_tint = color_tint
|
||||
copy.shadow_color = shadow_color
|
||||
copy.flow_direction = flow_direction
|
||||
copy.flow_speed = flow_speed
|
||||
copy.parallax_depth = parallax_depth
|
||||
copy.receive_light = receive_light
|
||||
copy.light_influence = light_influence
|
||||
copy.rim_glow_intensity = rim_glow_intensity
|
||||
return copy
|
||||
1
dynamic-sky/resources/cloud_layer_config.gd.uid
Normal file
1
dynamic-sky/resources/cloud_layer_config.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://beu0xhk0rvpky
|
||||
55
dynamic-sky/resources/post_process_profile.gd
Normal file
55
dynamic-sky/resources/post_process_profile.gd
Normal file
@@ -0,0 +1,55 @@
|
||||
@tool
|
||||
class_name PostProcessProfile
|
||||
extends Resource
|
||||
|
||||
@export_group("Sky Gradient")
|
||||
@export var gradient_enabled: bool = true
|
||||
@export var gradient_intensity: float = 1.0
|
||||
@export var gradient_curve: Curve
|
||||
|
||||
@export_group("Fog Shaping")
|
||||
@export var fog_enabled: bool = true
|
||||
@export var height_fog_enabled: bool = true
|
||||
@export var height_fog_start: float = 0.0
|
||||
@export var height_fog_end: float = 100.0
|
||||
@export var height_fog_curve: float = 1.0
|
||||
@export var distance_fog_enabled: bool = true
|
||||
@export var distance_fog_start: float = 50.0
|
||||
@export var distance_fog_end: float = 500.0
|
||||
|
||||
@export_group("Sun Glow")
|
||||
@export var sun_glow_enabled: bool = true
|
||||
@export var sun_glow_intensity: float = 0.5
|
||||
@export var sun_glow_size: float = 0.2
|
||||
@export var sun_glow_falloff: float = 2.0
|
||||
@export var sun_glow_color: Color = Color(1.0, 0.95, 0.8)
|
||||
|
||||
@export_group("Dawn/Dusk Enhancement")
|
||||
@export var dawn_dusk_enabled: bool = true
|
||||
@export var dawn_dusk_intensity: float = 0.5
|
||||
@export var dawn_dusk_color: Color = Color(1.0, 0.6, 0.4)
|
||||
@export var dawn_dusk_blend_range: float = 0.15
|
||||
|
||||
|
||||
func lerp_to(other: PostProcessProfile, weight: float) -> PostProcessProfile:
|
||||
var result := PostProcessProfile.new()
|
||||
result.gradient_enabled = gradient_enabled if weight < 0.5 else other.gradient_enabled
|
||||
result.gradient_intensity = lerpf(gradient_intensity, other.gradient_intensity, weight)
|
||||
result.fog_enabled = fog_enabled if weight < 0.5 else other.fog_enabled
|
||||
result.height_fog_enabled = height_fog_enabled if weight < 0.5 else other.height_fog_enabled
|
||||
result.height_fog_start = lerpf(height_fog_start, other.height_fog_start, weight)
|
||||
result.height_fog_end = lerpf(height_fog_end, other.height_fog_end, weight)
|
||||
result.height_fog_curve = lerpf(height_fog_curve, other.height_fog_curve, weight)
|
||||
result.distance_fog_enabled = distance_fog_enabled if weight < 0.5 else other.distance_fog_enabled
|
||||
result.distance_fog_start = lerpf(distance_fog_start, other.distance_fog_start, weight)
|
||||
result.distance_fog_end = lerpf(distance_fog_end, other.distance_fog_end, weight)
|
||||
result.sun_glow_enabled = sun_glow_enabled if weight < 0.5 else other.sun_glow_enabled
|
||||
result.sun_glow_intensity = lerpf(sun_glow_intensity, other.sun_glow_intensity, weight)
|
||||
result.sun_glow_size = lerpf(sun_glow_size, other.sun_glow_size, weight)
|
||||
result.sun_glow_falloff = lerpf(sun_glow_falloff, other.sun_glow_falloff, weight)
|
||||
result.sun_glow_color = sun_glow_color.lerp(other.sun_glow_color, weight)
|
||||
result.dawn_dusk_enabled = dawn_dusk_enabled if weight < 0.5 else other.dawn_dusk_enabled
|
||||
result.dawn_dusk_intensity = lerpf(dawn_dusk_intensity, other.dawn_dusk_intensity, weight)
|
||||
result.dawn_dusk_color = dawn_dusk_color.lerp(other.dawn_dusk_color, weight)
|
||||
result.dawn_dusk_blend_range = lerpf(dawn_dusk_blend_range, other.dawn_dusk_blend_range, weight)
|
||||
return result
|
||||
1
dynamic-sky/resources/post_process_profile.gd.uid
Normal file
1
dynamic-sky/resources/post_process_profile.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ctrw2ycud36yd
|
||||
104
dynamic-sky/resources/sky_config.gd
Normal file
104
dynamic-sky/resources/sky_config.gd
Normal file
@@ -0,0 +1,104 @@
|
||||
@tool
|
||||
class_name SkyConfig
|
||||
extends Resource
|
||||
|
||||
@export_group("Sun Settings")
|
||||
@export var sun_color: Color = Color(1.0, 0.95, 0.8)
|
||||
@export var sun_intensity: float = 1.0
|
||||
@export var sun_size: float = 0.05
|
||||
|
||||
@export_group("Moon Settings")
|
||||
@export var moon_color: Color = Color(0.8, 0.85, 1.0)
|
||||
@export var moon_intensity: float = 0.3
|
||||
@export var moon_size: float = 0.03
|
||||
|
||||
@export_group("Ambient Settings")
|
||||
@export var ambient_color: Color = Color(0.4, 0.5, 0.6)
|
||||
@export var ambient_intensity: float = 0.5
|
||||
|
||||
@export_group("Fog Settings")
|
||||
@export var fog_color: Color = Color(0.7, 0.8, 0.9)
|
||||
@export var fog_density: float = 0.01
|
||||
@export var fog_height: float = 0.0
|
||||
@export var fog_height_density: float = 0.1
|
||||
|
||||
@export_group("Sky Gradient")
|
||||
@export var sky_top_color: Color = Color(0.2, 0.4, 0.8)
|
||||
@export var sky_horizon_color: Color = Color(0.6, 0.7, 0.9)
|
||||
@export var sky_bottom_color: Color = Color(0.4, 0.5, 0.6)
|
||||
@export var horizon_blend: float = 0.5
|
||||
|
||||
@export_group("Stars")
|
||||
@export var stars_visibility: float = 0.0
|
||||
@export var stars_tint: Color = Color.WHITE
|
||||
|
||||
@export_group("Cloud Defaults")
|
||||
@export var cloud_coverage: float = 0.3
|
||||
@export var cloud_opacity: float = 0.8
|
||||
@export var cloud_color: Color = Color.WHITE
|
||||
@export var cloud_shadow_color: Color = Color(0.6, 0.6, 0.7)
|
||||
|
||||
@export_group("Post Process")
|
||||
@export var sun_glow_intensity: float = 0.5
|
||||
@export var sun_glow_size: float = 0.2
|
||||
@export var gradient_intensity: float = 1.0
|
||||
|
||||
|
||||
func lerp_to(other: SkyConfig, weight: float) -> SkyConfig:
|
||||
var result := SkyConfig.new()
|
||||
result.sun_color = sun_color.lerp(other.sun_color, weight)
|
||||
result.sun_intensity = lerpf(sun_intensity, other.sun_intensity, weight)
|
||||
result.sun_size = lerpf(sun_size, other.sun_size, weight)
|
||||
result.moon_color = moon_color.lerp(other.moon_color, weight)
|
||||
result.moon_intensity = lerpf(moon_intensity, other.moon_intensity, weight)
|
||||
result.moon_size = lerpf(moon_size, other.moon_size, weight)
|
||||
result.ambient_color = ambient_color.lerp(other.ambient_color, weight)
|
||||
result.ambient_intensity = lerpf(ambient_intensity, other.ambient_intensity, weight)
|
||||
result.fog_color = fog_color.lerp(other.fog_color, weight)
|
||||
result.fog_density = lerpf(fog_density, other.fog_density, weight)
|
||||
result.fog_height = lerpf(fog_height, other.fog_height, weight)
|
||||
result.fog_height_density = lerpf(fog_height_density, other.fog_height_density, weight)
|
||||
result.sky_top_color = sky_top_color.lerp(other.sky_top_color, weight)
|
||||
result.sky_horizon_color = sky_horizon_color.lerp(other.sky_horizon_color, weight)
|
||||
result.sky_bottom_color = sky_bottom_color.lerp(other.sky_bottom_color, weight)
|
||||
result.horizon_blend = lerpf(horizon_blend, other.horizon_blend, weight)
|
||||
result.stars_visibility = lerpf(stars_visibility, other.stars_visibility, weight)
|
||||
result.stars_tint = stars_tint.lerp(other.stars_tint, weight)
|
||||
result.cloud_coverage = lerpf(cloud_coverage, other.cloud_coverage, weight)
|
||||
result.cloud_opacity = lerpf(cloud_opacity, other.cloud_opacity, weight)
|
||||
result.cloud_color = cloud_color.lerp(other.cloud_color, weight)
|
||||
result.cloud_shadow_color = cloud_shadow_color.lerp(other.cloud_shadow_color, weight)
|
||||
result.sun_glow_intensity = lerpf(sun_glow_intensity, other.sun_glow_intensity, weight)
|
||||
result.sun_glow_size = lerpf(sun_glow_size, other.sun_glow_size, weight)
|
||||
result.gradient_intensity = lerpf(gradient_intensity, other.gradient_intensity, weight)
|
||||
return result
|
||||
|
||||
|
||||
func duplicate_config() -> SkyConfig:
|
||||
var copy := SkyConfig.new()
|
||||
copy.sun_color = sun_color
|
||||
copy.sun_intensity = sun_intensity
|
||||
copy.sun_size = sun_size
|
||||
copy.moon_color = moon_color
|
||||
copy.moon_intensity = moon_intensity
|
||||
copy.moon_size = moon_size
|
||||
copy.ambient_color = ambient_color
|
||||
copy.ambient_intensity = ambient_intensity
|
||||
copy.fog_color = fog_color
|
||||
copy.fog_density = fog_density
|
||||
copy.fog_height = fog_height
|
||||
copy.fog_height_density = fog_height_density
|
||||
copy.sky_top_color = sky_top_color
|
||||
copy.sky_horizon_color = sky_horizon_color
|
||||
copy.sky_bottom_color = sky_bottom_color
|
||||
copy.horizon_blend = horizon_blend
|
||||
copy.stars_visibility = stars_visibility
|
||||
copy.stars_tint = stars_tint
|
||||
copy.cloud_coverage = cloud_coverage
|
||||
copy.cloud_opacity = cloud_opacity
|
||||
copy.cloud_color = cloud_color
|
||||
copy.cloud_shadow_color = cloud_shadow_color
|
||||
copy.sun_glow_intensity = sun_glow_intensity
|
||||
copy.sun_glow_size = sun_glow_size
|
||||
copy.gradient_intensity = gradient_intensity
|
||||
return copy
|
||||
1
dynamic-sky/resources/sky_config.gd.uid
Normal file
1
dynamic-sky/resources/sky_config.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c3iniqgu2l0ek
|
||||
164
dynamic-sky/resources/sky_preset.gd
Normal file
164
dynamic-sky/resources/sky_preset.gd
Normal file
@@ -0,0 +1,164 @@
|
||||
@tool
|
||||
class_name SkyPreset
|
||||
extends Resource
|
||||
|
||||
enum PresetType {
|
||||
CLEAR_DAY,
|
||||
SUNSET,
|
||||
NIGHT,
|
||||
OVERCAST,
|
||||
STORM,
|
||||
DAWN,
|
||||
CUSTOM
|
||||
}
|
||||
|
||||
@export var preset_name: String = "Custom"
|
||||
@export var preset_type: PresetType = PresetType.CUSTOM
|
||||
@export var config: SkyConfig
|
||||
@export var skybox_texture: Texture2D
|
||||
@export var cloud_layer_configs: Array[CloudLayerConfig]
|
||||
@export var post_process_profile: PostProcessProfile
|
||||
|
||||
|
||||
static func create_clear_day() -> SkyPreset:
|
||||
var preset := SkyPreset.new()
|
||||
preset.preset_name = "Clear Day"
|
||||
preset.preset_type = PresetType.CLEAR_DAY
|
||||
preset.config = SkyConfig.new()
|
||||
preset.config.sun_color = Color(1.0, 0.95, 0.85)
|
||||
preset.config.sun_intensity = 1.2
|
||||
preset.config.moon_intensity = 0.0
|
||||
preset.config.ambient_color = Color(0.5, 0.6, 0.8)
|
||||
preset.config.ambient_intensity = 0.6
|
||||
preset.config.fog_color = Color(0.7, 0.8, 0.95)
|
||||
preset.config.fog_density = 0.005
|
||||
preset.config.sky_top_color = Color(0.2, 0.4, 0.85)
|
||||
preset.config.sky_horizon_color = Color(0.6, 0.75, 0.95)
|
||||
preset.config.sky_bottom_color = Color(0.5, 0.6, 0.7)
|
||||
preset.config.stars_visibility = 0.0
|
||||
preset.config.cloud_coverage = 0.2
|
||||
preset.config.cloud_opacity = 0.7
|
||||
preset.config.sun_glow_intensity = 0.3
|
||||
return preset
|
||||
|
||||
|
||||
static func create_sunset() -> SkyPreset:
|
||||
var preset := SkyPreset.new()
|
||||
preset.preset_name = "Sunset"
|
||||
preset.preset_type = PresetType.SUNSET
|
||||
preset.config = SkyConfig.new()
|
||||
preset.config.sun_color = Color(1.0, 0.5, 0.2)
|
||||
preset.config.sun_intensity = 0.8
|
||||
preset.config.moon_intensity = 0.0
|
||||
preset.config.ambient_color = Color(0.8, 0.5, 0.4)
|
||||
preset.config.ambient_intensity = 0.4
|
||||
preset.config.fog_color = Color(0.9, 0.6, 0.4)
|
||||
preset.config.fog_density = 0.015
|
||||
preset.config.sky_top_color = Color(0.3, 0.2, 0.5)
|
||||
preset.config.sky_horizon_color = Color(1.0, 0.5, 0.3)
|
||||
preset.config.sky_bottom_color = Color(0.9, 0.4, 0.2)
|
||||
preset.config.stars_visibility = 0.1
|
||||
preset.config.cloud_coverage = 0.3
|
||||
preset.config.cloud_opacity = 0.9
|
||||
preset.config.cloud_color = Color(1.0, 0.7, 0.5)
|
||||
preset.config.cloud_shadow_color = Color(0.4, 0.2, 0.3)
|
||||
preset.config.sun_glow_intensity = 0.8
|
||||
preset.config.sun_glow_size = 0.4
|
||||
return preset
|
||||
|
||||
|
||||
static func create_night() -> SkyPreset:
|
||||
var preset := SkyPreset.new()
|
||||
preset.preset_name = "Night"
|
||||
preset.preset_type = PresetType.NIGHT
|
||||
preset.config = SkyConfig.new()
|
||||
preset.config.sun_intensity = 0.0
|
||||
preset.config.moon_color = Color(0.7, 0.8, 1.0)
|
||||
preset.config.moon_intensity = 0.4
|
||||
preset.config.ambient_color = Color(0.1, 0.15, 0.25)
|
||||
preset.config.ambient_intensity = 0.2
|
||||
preset.config.fog_color = Color(0.1, 0.12, 0.2)
|
||||
preset.config.fog_density = 0.02
|
||||
preset.config.sky_top_color = Color(0.02, 0.03, 0.08)
|
||||
preset.config.sky_horizon_color = Color(0.1, 0.12, 0.2)
|
||||
preset.config.sky_bottom_color = Color(0.05, 0.06, 0.1)
|
||||
preset.config.stars_visibility = 1.0
|
||||
preset.config.cloud_coverage = 0.15
|
||||
preset.config.cloud_opacity = 0.5
|
||||
preset.config.cloud_color = Color(0.2, 0.22, 0.3)
|
||||
preset.config.cloud_shadow_color = Color(0.05, 0.05, 0.1)
|
||||
preset.config.sun_glow_intensity = 0.0
|
||||
return preset
|
||||
|
||||
|
||||
static func create_overcast() -> SkyPreset:
|
||||
var preset := SkyPreset.new()
|
||||
preset.preset_name = "Overcast"
|
||||
preset.preset_type = PresetType.OVERCAST
|
||||
preset.config = SkyConfig.new()
|
||||
preset.config.sun_color = Color(0.9, 0.9, 0.85)
|
||||
preset.config.sun_intensity = 0.4
|
||||
preset.config.moon_intensity = 0.0
|
||||
preset.config.ambient_color = Color(0.5, 0.5, 0.55)
|
||||
preset.config.ambient_intensity = 0.7
|
||||
preset.config.fog_color = Color(0.6, 0.62, 0.65)
|
||||
preset.config.fog_density = 0.03
|
||||
preset.config.sky_top_color = Color(0.45, 0.48, 0.55)
|
||||
preset.config.sky_horizon_color = Color(0.6, 0.62, 0.65)
|
||||
preset.config.sky_bottom_color = Color(0.5, 0.52, 0.55)
|
||||
preset.config.stars_visibility = 0.0
|
||||
preset.config.cloud_coverage = 0.9
|
||||
preset.config.cloud_opacity = 1.0
|
||||
preset.config.cloud_color = Color(0.7, 0.72, 0.75)
|
||||
preset.config.cloud_shadow_color = Color(0.4, 0.42, 0.45)
|
||||
preset.config.sun_glow_intensity = 0.1
|
||||
return preset
|
||||
|
||||
|
||||
static func create_storm() -> SkyPreset:
|
||||
var preset := SkyPreset.new()
|
||||
preset.preset_name = "Storm"
|
||||
preset.preset_type = PresetType.STORM
|
||||
preset.config = SkyConfig.new()
|
||||
preset.config.sun_color = Color(0.7, 0.7, 0.75)
|
||||
preset.config.sun_intensity = 0.2
|
||||
preset.config.moon_intensity = 0.0
|
||||
preset.config.ambient_color = Color(0.35, 0.38, 0.45)
|
||||
preset.config.ambient_intensity = 0.5
|
||||
preset.config.fog_color = Color(0.4, 0.42, 0.5)
|
||||
preset.config.fog_density = 0.05
|
||||
preset.config.sky_top_color = Color(0.25, 0.28, 0.35)
|
||||
preset.config.sky_horizon_color = Color(0.4, 0.42, 0.48)
|
||||
preset.config.sky_bottom_color = Color(0.35, 0.38, 0.42)
|
||||
preset.config.stars_visibility = 0.0
|
||||
preset.config.cloud_coverage = 1.0
|
||||
preset.config.cloud_opacity = 1.0
|
||||
preset.config.cloud_color = Color(0.4, 0.42, 0.5)
|
||||
preset.config.cloud_shadow_color = Color(0.2, 0.22, 0.28)
|
||||
preset.config.sun_glow_intensity = 0.0
|
||||
return preset
|
||||
|
||||
|
||||
static func create_dawn() -> SkyPreset:
|
||||
var preset := SkyPreset.new()
|
||||
preset.preset_name = "Dawn"
|
||||
preset.preset_type = PresetType.DAWN
|
||||
preset.config = SkyConfig.new()
|
||||
preset.config.sun_color = Color(1.0, 0.7, 0.5)
|
||||
preset.config.sun_intensity = 0.5
|
||||
preset.config.moon_intensity = 0.1
|
||||
preset.config.ambient_color = Color(0.4, 0.35, 0.45)
|
||||
preset.config.ambient_intensity = 0.35
|
||||
preset.config.fog_color = Color(0.6, 0.5, 0.55)
|
||||
preset.config.fog_density = 0.025
|
||||
preset.config.sky_top_color = Color(0.15, 0.2, 0.45)
|
||||
preset.config.sky_horizon_color = Color(0.9, 0.6, 0.5)
|
||||
preset.config.sky_bottom_color = Color(0.7, 0.45, 0.4)
|
||||
preset.config.stars_visibility = 0.3
|
||||
preset.config.cloud_coverage = 0.25
|
||||
preset.config.cloud_opacity = 0.8
|
||||
preset.config.cloud_color = Color(0.95, 0.7, 0.6)
|
||||
preset.config.cloud_shadow_color = Color(0.3, 0.25, 0.35)
|
||||
preset.config.sun_glow_intensity = 0.6
|
||||
preset.config.sun_glow_size = 0.35
|
||||
return preset
|
||||
1
dynamic-sky/resources/sky_preset.gd.uid
Normal file
1
dynamic-sky/resources/sky_preset.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c48eq2y4ikok8
|
||||
158
dynamic-sky/resources/weather_profile.gd
Normal file
158
dynamic-sky/resources/weather_profile.gd
Normal file
@@ -0,0 +1,158 @@
|
||||
@tool
|
||||
class_name WeatherProfile
|
||||
extends Resource
|
||||
|
||||
enum WeatherState {
|
||||
CLEAR,
|
||||
CLOUDY,
|
||||
OVERCAST,
|
||||
RAIN,
|
||||
STORM,
|
||||
SNOW
|
||||
}
|
||||
|
||||
@export var weather_state: WeatherState = WeatherState.CLEAR
|
||||
@export var display_name: String = "Clear"
|
||||
|
||||
@export_group("Clouds")
|
||||
@export var cloud_density: float = 0.2
|
||||
@export var cloud_opacity: float = 0.7
|
||||
@export var cloud_coverage: float = 0.3
|
||||
@export var cloud_color: Color = Color.WHITE
|
||||
@export var cloud_shadow_color: Color = Color(0.6, 0.6, 0.7)
|
||||
|
||||
@export_group("Wind")
|
||||
@export var wind_speed: float = 1.0
|
||||
@export var wind_direction: Vector2 = Vector2(1.0, 0.0)
|
||||
@export var wind_turbulence: float = 0.1
|
||||
|
||||
@export_group("Ambient Adjustments")
|
||||
@export var ambient_color_multiplier: Color = Color.WHITE
|
||||
@export var ambient_intensity_multiplier: float = 1.0
|
||||
|
||||
@export_group("Fog Adjustments")
|
||||
@export var fog_color_multiplier: Color = Color.WHITE
|
||||
@export var fog_density_multiplier: float = 1.0
|
||||
|
||||
@export_group("VFX")
|
||||
@export var rain_intensity: float = 0.0
|
||||
@export var snow_intensity: float = 0.0
|
||||
@export var lightning_enabled: bool = false
|
||||
@export var lightning_frequency: float = 0.1
|
||||
|
||||
@export_group("Transition")
|
||||
@export var default_transition_duration: float = 5.0
|
||||
|
||||
|
||||
static func create_clear() -> WeatherProfile:
|
||||
var profile := WeatherProfile.new()
|
||||
profile.weather_state = WeatherState.CLEAR
|
||||
profile.display_name = "Clear"
|
||||
profile.cloud_density = 0.1
|
||||
profile.cloud_opacity = 0.6
|
||||
profile.cloud_coverage = 0.2
|
||||
profile.wind_speed = 0.5
|
||||
return profile
|
||||
|
||||
|
||||
static func create_cloudy() -> WeatherProfile:
|
||||
var profile := WeatherProfile.new()
|
||||
profile.weather_state = WeatherState.CLOUDY
|
||||
profile.display_name = "Cloudy"
|
||||
profile.cloud_density = 0.5
|
||||
profile.cloud_opacity = 0.8
|
||||
profile.cloud_coverage = 0.5
|
||||
profile.wind_speed = 1.0
|
||||
profile.ambient_intensity_multiplier = 0.9
|
||||
return profile
|
||||
|
||||
|
||||
static func create_overcast() -> WeatherProfile:
|
||||
var profile := WeatherProfile.new()
|
||||
profile.weather_state = WeatherState.OVERCAST
|
||||
profile.display_name = "Overcast"
|
||||
profile.cloud_density = 0.9
|
||||
profile.cloud_opacity = 1.0
|
||||
profile.cloud_coverage = 0.9
|
||||
profile.cloud_color = Color(0.7, 0.72, 0.75)
|
||||
profile.wind_speed = 1.5
|
||||
profile.ambient_intensity_multiplier = 0.7
|
||||
profile.fog_density_multiplier = 1.5
|
||||
return profile
|
||||
|
||||
|
||||
static func create_rain() -> WeatherProfile:
|
||||
var profile := WeatherProfile.new()
|
||||
profile.weather_state = WeatherState.RAIN
|
||||
profile.display_name = "Rain"
|
||||
profile.cloud_density = 0.85
|
||||
profile.cloud_opacity = 0.95
|
||||
profile.cloud_coverage = 0.85
|
||||
profile.cloud_color = Color(0.55, 0.58, 0.65)
|
||||
profile.wind_speed = 2.0
|
||||
profile.ambient_intensity_multiplier = 0.6
|
||||
profile.fog_density_multiplier = 2.0
|
||||
profile.rain_intensity = 0.7
|
||||
profile.default_transition_duration = 8.0
|
||||
return profile
|
||||
|
||||
|
||||
static func create_storm() -> WeatherProfile:
|
||||
var profile := WeatherProfile.new()
|
||||
profile.weather_state = WeatherState.STORM
|
||||
profile.display_name = "Storm"
|
||||
profile.cloud_density = 1.0
|
||||
profile.cloud_opacity = 1.0
|
||||
profile.cloud_coverage = 1.0
|
||||
profile.cloud_color = Color(0.4, 0.42, 0.5)
|
||||
profile.cloud_shadow_color = Color(0.2, 0.22, 0.28)
|
||||
profile.wind_speed = 4.0
|
||||
profile.wind_turbulence = 0.4
|
||||
profile.ambient_intensity_multiplier = 0.4
|
||||
profile.fog_density_multiplier = 3.0
|
||||
profile.rain_intensity = 1.0
|
||||
profile.lightning_enabled = true
|
||||
profile.lightning_frequency = 0.15
|
||||
profile.default_transition_duration = 10.0
|
||||
return profile
|
||||
|
||||
|
||||
static func create_snow() -> WeatherProfile:
|
||||
var profile := WeatherProfile.new()
|
||||
profile.weather_state = WeatherState.SNOW
|
||||
profile.display_name = "Snow"
|
||||
profile.cloud_density = 0.8
|
||||
profile.cloud_opacity = 0.9
|
||||
profile.cloud_coverage = 0.8
|
||||
profile.cloud_color = Color(0.85, 0.87, 0.92)
|
||||
profile.wind_speed = 1.0
|
||||
profile.ambient_color_multiplier = Color(0.9, 0.92, 1.0)
|
||||
profile.ambient_intensity_multiplier = 0.75
|
||||
profile.fog_color_multiplier = Color(0.95, 0.97, 1.0)
|
||||
profile.fog_density_multiplier = 1.8
|
||||
profile.snow_intensity = 0.8
|
||||
profile.default_transition_duration = 12.0
|
||||
return profile
|
||||
|
||||
|
||||
func lerp_to(other: WeatherProfile, weight: float) -> WeatherProfile:
|
||||
var result := WeatherProfile.new()
|
||||
result.weather_state = weather_state if weight < 0.5 else other.weather_state
|
||||
result.display_name = display_name if weight < 0.5 else other.display_name
|
||||
result.cloud_density = lerpf(cloud_density, other.cloud_density, weight)
|
||||
result.cloud_opacity = lerpf(cloud_opacity, other.cloud_opacity, weight)
|
||||
result.cloud_coverage = lerpf(cloud_coverage, other.cloud_coverage, weight)
|
||||
result.cloud_color = cloud_color.lerp(other.cloud_color, weight)
|
||||
result.cloud_shadow_color = cloud_shadow_color.lerp(other.cloud_shadow_color, weight)
|
||||
result.wind_speed = lerpf(wind_speed, other.wind_speed, weight)
|
||||
result.wind_direction = wind_direction.lerp(other.wind_direction, weight).normalized()
|
||||
result.wind_turbulence = lerpf(wind_turbulence, other.wind_turbulence, weight)
|
||||
result.ambient_color_multiplier = ambient_color_multiplier.lerp(other.ambient_color_multiplier, weight)
|
||||
result.ambient_intensity_multiplier = lerpf(ambient_intensity_multiplier, other.ambient_intensity_multiplier, weight)
|
||||
result.fog_color_multiplier = fog_color_multiplier.lerp(other.fog_color_multiplier, weight)
|
||||
result.fog_density_multiplier = lerpf(fog_density_multiplier, other.fog_density_multiplier, weight)
|
||||
result.rain_intensity = lerpf(rain_intensity, other.rain_intensity, weight)
|
||||
result.snow_intensity = lerpf(snow_intensity, other.snow_intensity, weight)
|
||||
result.lightning_enabled = lightning_enabled or other.lightning_enabled
|
||||
result.lightning_frequency = lerpf(lightning_frequency, other.lightning_frequency, weight)
|
||||
return result
|
||||
1
dynamic-sky/resources/weather_profile.gd.uid
Normal file
1
dynamic-sky/resources/weather_profile.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d2srir3wsi045
|
||||
301
dynamic-sky/scripts/cloud_system_2d.gd
Normal file
301
dynamic-sky/scripts/cloud_system_2d.gd
Normal file
@@ -0,0 +1,301 @@
|
||||
@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()
|
||||
1
dynamic-sky/scripts/cloud_system_2d.gd.uid
Normal file
1
dynamic-sky/scripts/cloud_system_2d.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cwplvsarljsf
|
||||
313
dynamic-sky/scripts/day_night_controller.gd
Normal file
313
dynamic-sky/scripts/day_night_controller.gd
Normal file
@@ -0,0 +1,313 @@
|
||||
@tool
|
||||
class_name DayNightController
|
||||
extends Node
|
||||
|
||||
signal time_changed(time_of_day: float)
|
||||
signal hour_changed(hour: int)
|
||||
signal day_changed(day: int)
|
||||
signal sunrise_started()
|
||||
signal sunset_started()
|
||||
signal night_started()
|
||||
signal noon_reached()
|
||||
|
||||
const HOURS_PER_DAY := 24.0
|
||||
const SUNRISE_START := 0.2 # ~5 AM
|
||||
const SUNRISE_END := 0.3 # ~7 AM
|
||||
const SUNSET_START := 0.75 # ~6 PM
|
||||
const SUNSET_END := 0.85 # ~8 PM
|
||||
const NOON := 0.5
|
||||
|
||||
@export_group("Time Settings")
|
||||
@export var time_of_day: float = 0.25:
|
||||
set(value):
|
||||
var old_hour := get_current_hour()
|
||||
time_of_day = wrapf(value, 0.0, 1.0)
|
||||
var new_hour := get_current_hour()
|
||||
if old_hour != new_hour:
|
||||
hour_changed.emit(new_hour)
|
||||
time_changed.emit(time_of_day)
|
||||
_check_time_events(old_hour, new_hour)
|
||||
|
||||
@export var day_length_minutes: float = 30.0:
|
||||
set(value):
|
||||
day_length_minutes = clampf(value, 1.0, 1440.0)
|
||||
|
||||
@export var paused: bool = false
|
||||
@export var time_scale: float = 1.0
|
||||
@export var current_day: int = 1
|
||||
|
||||
@export_group("Sun Arc")
|
||||
@export var sun_arc_tilt: float = 23.5
|
||||
@export var sun_arc_offset: float = 0.0
|
||||
|
||||
@export_group("Moon Arc")
|
||||
@export var moon_arc_tilt: float = 15.0
|
||||
@export var moon_arc_offset: float = 180.0
|
||||
@export var moon_phase: float = 0.0
|
||||
|
||||
@export_group("Sun Curves")
|
||||
@export var sun_color_curve: Gradient
|
||||
@export var sun_intensity_curve: Curve
|
||||
|
||||
@export_group("Moon Curves")
|
||||
@export var moon_color_curve: Gradient
|
||||
@export var moon_intensity_curve: Curve
|
||||
|
||||
@export_group("Ambient Curves")
|
||||
@export var ambient_color_curve: Gradient
|
||||
@export var ambient_intensity_curve: Curve
|
||||
|
||||
@export_group("Fog Curves")
|
||||
@export var fog_color_curve: Gradient
|
||||
@export var fog_density_curve: Curve
|
||||
|
||||
@export_group("Stars")
|
||||
@export var stars_visibility_curve: Curve
|
||||
|
||||
var _last_hour: int = -1
|
||||
var _sunrise_emitted := false
|
||||
var _sunset_emitted := false
|
||||
var _night_emitted := false
|
||||
var _noon_emitted := false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_initialize_default_curves()
|
||||
_last_hour = get_current_hour()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if paused:
|
||||
return
|
||||
|
||||
var time_increment := delta / (day_length_minutes * 60.0) * time_scale
|
||||
var old_time := time_of_day
|
||||
time_of_day += time_increment
|
||||
|
||||
if time_of_day < old_time:
|
||||
current_day += 1
|
||||
day_changed.emit(current_day)
|
||||
_reset_time_events()
|
||||
|
||||
|
||||
func _initialize_default_curves() -> void:
|
||||
if sun_color_curve == null:
|
||||
sun_color_curve = Gradient.new()
|
||||
sun_color_curve.set_color(0, Color(1.0, 0.6, 0.4)) # Dawn
|
||||
sun_color_curve.add_point(0.25, Color(1.0, 0.95, 0.85)) # Morning
|
||||
sun_color_curve.add_point(0.5, Color(1.0, 1.0, 0.95)) # Noon
|
||||
sun_color_curve.add_point(0.75, Color(1.0, 0.7, 0.4)) # Sunset
|
||||
sun_color_curve.set_color(1, Color(1.0, 0.5, 0.3)) # Dusk
|
||||
|
||||
if sun_intensity_curve == null:
|
||||
sun_intensity_curve = Curve.new()
|
||||
sun_intensity_curve.add_point(Vector2(0.0, 0.0))
|
||||
sun_intensity_curve.add_point(Vector2(0.2, 0.0))
|
||||
sun_intensity_curve.add_point(Vector2(0.3, 0.8))
|
||||
sun_intensity_curve.add_point(Vector2(0.5, 1.0))
|
||||
sun_intensity_curve.add_point(Vector2(0.7, 0.8))
|
||||
sun_intensity_curve.add_point(Vector2(0.85, 0.0))
|
||||
sun_intensity_curve.add_point(Vector2(1.0, 0.0))
|
||||
|
||||
if moon_color_curve == null:
|
||||
moon_color_curve = Gradient.new()
|
||||
moon_color_curve.set_color(0, Color(0.7, 0.8, 1.0))
|
||||
moon_color_curve.set_color(1, Color(0.7, 0.8, 1.0))
|
||||
|
||||
if moon_intensity_curve == null:
|
||||
moon_intensity_curve = Curve.new()
|
||||
moon_intensity_curve.add_point(Vector2(0.0, 0.4))
|
||||
moon_intensity_curve.add_point(Vector2(0.2, 0.3))
|
||||
moon_intensity_curve.add_point(Vector2(0.25, 0.0))
|
||||
moon_intensity_curve.add_point(Vector2(0.75, 0.0))
|
||||
moon_intensity_curve.add_point(Vector2(0.85, 0.3))
|
||||
moon_intensity_curve.add_point(Vector2(1.0, 0.4))
|
||||
|
||||
if ambient_color_curve == null:
|
||||
ambient_color_curve = Gradient.new()
|
||||
ambient_color_curve.set_color(0, Color(0.15, 0.18, 0.3)) # Night
|
||||
ambient_color_curve.add_point(0.25, Color(0.5, 0.55, 0.65)) # Morning
|
||||
ambient_color_curve.add_point(0.5, Color(0.6, 0.65, 0.75)) # Noon
|
||||
ambient_color_curve.add_point(0.75, Color(0.55, 0.5, 0.55)) # Evening
|
||||
ambient_color_curve.set_color(1, Color(0.15, 0.18, 0.3)) # Night
|
||||
|
||||
if ambient_intensity_curve == null:
|
||||
ambient_intensity_curve = Curve.new()
|
||||
ambient_intensity_curve.add_point(Vector2(0.0, 0.2))
|
||||
ambient_intensity_curve.add_point(Vector2(0.25, 0.5))
|
||||
ambient_intensity_curve.add_point(Vector2(0.5, 0.7))
|
||||
ambient_intensity_curve.add_point(Vector2(0.75, 0.5))
|
||||
ambient_intensity_curve.add_point(Vector2(1.0, 0.2))
|
||||
|
||||
if fog_color_curve == null:
|
||||
fog_color_curve = Gradient.new()
|
||||
fog_color_curve.set_color(0, Color(0.1, 0.12, 0.2))
|
||||
fog_color_curve.add_point(0.25, Color(0.6, 0.65, 0.75))
|
||||
fog_color_curve.add_point(0.5, Color(0.7, 0.75, 0.85))
|
||||
fog_color_curve.add_point(0.75, Color(0.7, 0.55, 0.5))
|
||||
fog_color_curve.set_color(1, Color(0.1, 0.12, 0.2))
|
||||
|
||||
if fog_density_curve == null:
|
||||
fog_density_curve = Curve.new()
|
||||
fog_density_curve.add_point(Vector2(0.0, 0.02))
|
||||
fog_density_curve.add_point(Vector2(0.25, 0.015))
|
||||
fog_density_curve.add_point(Vector2(0.5, 0.01))
|
||||
fog_density_curve.add_point(Vector2(0.75, 0.015))
|
||||
fog_density_curve.add_point(Vector2(1.0, 0.02))
|
||||
|
||||
if stars_visibility_curve == null:
|
||||
stars_visibility_curve = Curve.new()
|
||||
stars_visibility_curve.add_point(Vector2(0.0, 1.0))
|
||||
stars_visibility_curve.add_point(Vector2(0.2, 0.8))
|
||||
stars_visibility_curve.add_point(Vector2(0.3, 0.0))
|
||||
stars_visibility_curve.add_point(Vector2(0.7, 0.0))
|
||||
stars_visibility_curve.add_point(Vector2(0.85, 0.8))
|
||||
stars_visibility_curve.add_point(Vector2(1.0, 1.0))
|
||||
|
||||
|
||||
func _check_time_events(old_hour: int, new_hour: int) -> void:
|
||||
if time_of_day >= SUNRISE_START and time_of_day < SUNRISE_END and not _sunrise_emitted:
|
||||
_sunrise_emitted = true
|
||||
sunrise_started.emit()
|
||||
|
||||
if time_of_day >= SUNSET_START and time_of_day < SUNSET_END and not _sunset_emitted:
|
||||
_sunset_emitted = true
|
||||
sunset_started.emit()
|
||||
|
||||
if time_of_day >= SUNSET_END and not _night_emitted:
|
||||
_night_emitted = true
|
||||
night_started.emit()
|
||||
|
||||
if time_of_day >= NOON - 0.01 and time_of_day < NOON + 0.01 and not _noon_emitted:
|
||||
_noon_emitted = true
|
||||
noon_reached.emit()
|
||||
|
||||
|
||||
func _reset_time_events() -> void:
|
||||
_sunrise_emitted = false
|
||||
_sunset_emitted = false
|
||||
_night_emitted = false
|
||||
_noon_emitted = false
|
||||
|
||||
|
||||
func get_current_hour() -> int:
|
||||
return int(time_of_day * HOURS_PER_DAY)
|
||||
|
||||
|
||||
func get_current_hour_float() -> float:
|
||||
return time_of_day * HOURS_PER_DAY
|
||||
|
||||
|
||||
func set_time_from_hours(hours: float) -> void:
|
||||
time_of_day = fmod(hours, HOURS_PER_DAY) / HOURS_PER_DAY
|
||||
|
||||
|
||||
func get_sun_direction() -> Vector3:
|
||||
var angle := (time_of_day - 0.25) * TAU
|
||||
var tilt_rad := deg_to_rad(sun_arc_tilt)
|
||||
var offset_rad := deg_to_rad(sun_arc_offset)
|
||||
|
||||
var direction := Vector3(
|
||||
cos(angle + offset_rad),
|
||||
sin(angle),
|
||||
sin(angle) * sin(tilt_rad)
|
||||
).normalized()
|
||||
|
||||
return direction
|
||||
|
||||
|
||||
func get_moon_direction() -> Vector3:
|
||||
var angle := (time_of_day + 0.25) * TAU
|
||||
var tilt_rad := deg_to_rad(moon_arc_tilt)
|
||||
var offset_rad := deg_to_rad(moon_arc_offset)
|
||||
|
||||
var direction := Vector3(
|
||||
cos(angle + offset_rad),
|
||||
sin(angle),
|
||||
sin(angle) * sin(tilt_rad)
|
||||
).normalized()
|
||||
|
||||
return direction
|
||||
|
||||
|
||||
func get_sun_color() -> Color:
|
||||
if sun_color_curve:
|
||||
return sun_color_curve.sample(time_of_day)
|
||||
return Color.WHITE
|
||||
|
||||
|
||||
func get_sun_intensity() -> float:
|
||||
if sun_intensity_curve:
|
||||
return sun_intensity_curve.sample(time_of_day)
|
||||
return 1.0
|
||||
|
||||
|
||||
func get_moon_color() -> Color:
|
||||
if moon_color_curve:
|
||||
return moon_color_curve.sample(time_of_day)
|
||||
return Color(0.7, 0.8, 1.0)
|
||||
|
||||
|
||||
func get_moon_intensity() -> float:
|
||||
var base_intensity := 1.0
|
||||
if moon_intensity_curve:
|
||||
base_intensity = moon_intensity_curve.sample(time_of_day)
|
||||
var phase_factor := 0.5 + 0.5 * cos(moon_phase * TAU)
|
||||
return base_intensity * phase_factor
|
||||
|
||||
|
||||
func get_ambient_color() -> Color:
|
||||
if ambient_color_curve:
|
||||
return ambient_color_curve.sample(time_of_day)
|
||||
return Color(0.5, 0.55, 0.65)
|
||||
|
||||
|
||||
func get_ambient_intensity() -> float:
|
||||
if ambient_intensity_curve:
|
||||
return ambient_intensity_curve.sample(time_of_day)
|
||||
return 0.5
|
||||
|
||||
|
||||
func get_fog_color() -> Color:
|
||||
if fog_color_curve:
|
||||
return fog_color_curve.sample(time_of_day)
|
||||
return Color(0.7, 0.75, 0.85)
|
||||
|
||||
|
||||
func get_fog_density() -> float:
|
||||
if fog_density_curve:
|
||||
return fog_density_curve.sample(time_of_day)
|
||||
return 0.01
|
||||
|
||||
|
||||
func get_stars_visibility() -> float:
|
||||
if stars_visibility_curve:
|
||||
return stars_visibility_curve.sample(time_of_day)
|
||||
return 0.0
|
||||
|
||||
|
||||
func is_daytime() -> bool:
|
||||
return time_of_day >= SUNRISE_END and time_of_day < SUNSET_START
|
||||
|
||||
|
||||
func is_nighttime() -> bool:
|
||||
return time_of_day < SUNRISE_START or time_of_day >= SUNSET_END
|
||||
|
||||
|
||||
func is_dawn() -> bool:
|
||||
return time_of_day >= SUNRISE_START and time_of_day < SUNRISE_END
|
||||
|
||||
|
||||
func is_dusk() -> bool:
|
||||
return time_of_day >= SUNSET_START and time_of_day < SUNSET_END
|
||||
|
||||
|
||||
func get_time_period() -> String:
|
||||
if is_dawn():
|
||||
return "dawn"
|
||||
elif is_dusk():
|
||||
return "dusk"
|
||||
elif is_daytime():
|
||||
return "day"
|
||||
else:
|
||||
return "night"
|
||||
1
dynamic-sky/scripts/day_night_controller.gd.uid
Normal file
1
dynamic-sky/scripts/day_night_controller.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://gypnoh5ikwjn
|
||||
399
dynamic-sky/scripts/dynamic_sky_root.gd
Normal file
399
dynamic-sky/scripts/dynamic_sky_root.gd
Normal file
@@ -0,0 +1,399 @@
|
||||
@tool
|
||||
class_name DynamicSkyRoot
|
||||
extends Node3D
|
||||
|
||||
signal time_of_day_changed(time: float)
|
||||
signal weather_changed(state: WeatherProfile.WeatherState)
|
||||
signal preset_changed(preset: SkyPreset)
|
||||
|
||||
@export_group("Controllers")
|
||||
@export var preset_library: SkyboxPresetLibrary
|
||||
@export var day_night_controller: DayNightController
|
||||
@export var weather_controller: WeatherController
|
||||
@export var cloud_system: CloudSystem2D
|
||||
@export var post_process_controller: PostProcessController
|
||||
@export var vfx_controller: VFXController
|
||||
@export var shadow_proxy_system: ShadowProxySystem
|
||||
|
||||
@export_group("Environment")
|
||||
@export var world_environment: WorldEnvironment
|
||||
@export var sun_light: DirectionalLight3D
|
||||
@export var moon_light: DirectionalLight3D
|
||||
|
||||
@export_group("Global Settings")
|
||||
@export var auto_create_controllers: bool = true
|
||||
@export var deterministic_seed: int = 0:
|
||||
set(value):
|
||||
deterministic_seed = value
|
||||
_apply_seed()
|
||||
|
||||
@export_group("Debug")
|
||||
@export var show_debug_panel: bool = false
|
||||
@export var debug_time_override: float = -1.0
|
||||
|
||||
var _current_config: SkyConfig
|
||||
var preset_name: String = ""
|
||||
var _updating_preset_name: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if auto_create_controllers:
|
||||
_ensure_controllers_exist()
|
||||
|
||||
_connect_signals()
|
||||
property_list_changed_notify()
|
||||
_sync_preset_name()
|
||||
_apply_seed()
|
||||
_sync_all_systems()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
_sync_all_systems()
|
||||
|
||||
if show_debug_panel:
|
||||
_update_debug_info()
|
||||
|
||||
|
||||
func _ensure_controllers_exist() -> void:
|
||||
if preset_library == null:
|
||||
preset_library = SkyboxPresetLibrary.new()
|
||||
preset_library.name = "PresetLibrary"
|
||||
add_child(preset_library)
|
||||
|
||||
if day_night_controller == null:
|
||||
day_night_controller = DayNightController.new()
|
||||
day_night_controller.name = "DayNightController"
|
||||
add_child(day_night_controller)
|
||||
|
||||
if weather_controller == null:
|
||||
weather_controller = WeatherController.new()
|
||||
weather_controller.name = "WeatherController"
|
||||
add_child(weather_controller)
|
||||
|
||||
if cloud_system == null:
|
||||
cloud_system = CloudSystem2D.new()
|
||||
cloud_system.name = "CloudSystem"
|
||||
add_child(cloud_system)
|
||||
|
||||
if post_process_controller == null:
|
||||
post_process_controller = PostProcessController.new()
|
||||
post_process_controller.name = "PostProcessController"
|
||||
add_child(post_process_controller)
|
||||
|
||||
if vfx_controller == null:
|
||||
vfx_controller = VFXController.new()
|
||||
vfx_controller.name = "VFXController"
|
||||
add_child(vfx_controller)
|
||||
|
||||
if shadow_proxy_system == null:
|
||||
shadow_proxy_system = ShadowProxySystem.new()
|
||||
shadow_proxy_system.name = "ShadowProxySystem"
|
||||
add_child(shadow_proxy_system)
|
||||
|
||||
if world_environment == null:
|
||||
_create_default_environment()
|
||||
|
||||
if sun_light == null:
|
||||
_create_sun_light()
|
||||
|
||||
if moon_light == null:
|
||||
_create_moon_light()
|
||||
|
||||
|
||||
func _create_default_environment() -> void:
|
||||
world_environment = WorldEnvironment.new()
|
||||
world_environment.name = "WorldEnvironment"
|
||||
world_environment.environment = Environment.new()
|
||||
world_environment.environment.background_mode = Environment.BG_SKY
|
||||
world_environment.environment.sky = Sky.new()
|
||||
add_child(world_environment)
|
||||
|
||||
if post_process_controller:
|
||||
post_process_controller.target_environment = world_environment.environment
|
||||
|
||||
|
||||
func _create_sun_light() -> void:
|
||||
sun_light = DirectionalLight3D.new()
|
||||
sun_light.name = "SunLight"
|
||||
sun_light.light_color = Color(1.0, 0.95, 0.85)
|
||||
sun_light.light_energy = 1.0
|
||||
sun_light.shadow_enabled = true
|
||||
add_child(sun_light)
|
||||
|
||||
|
||||
func _create_moon_light() -> void:
|
||||
moon_light = DirectionalLight3D.new()
|
||||
moon_light.name = "MoonLight"
|
||||
moon_light.light_color = Color(0.7, 0.8, 1.0)
|
||||
moon_light.light_energy = 0.3
|
||||
moon_light.shadow_enabled = false
|
||||
add_child(moon_light)
|
||||
|
||||
|
||||
func _connect_signals() -> void:
|
||||
if day_night_controller:
|
||||
day_night_controller.time_changed.connect(_on_time_changed)
|
||||
|
||||
if weather_controller:
|
||||
weather_controller.weather_state_changed.connect(_on_weather_state_changed)
|
||||
|
||||
if preset_library:
|
||||
preset_library.preset_changed.connect(_on_preset_changed)
|
||||
|
||||
|
||||
func _on_time_changed(time: float) -> void:
|
||||
time_of_day_changed.emit(time)
|
||||
|
||||
if vfx_controller:
|
||||
vfx_controller.set_night_mode(day_night_controller.is_nighttime())
|
||||
|
||||
|
||||
func _on_weather_state_changed(old_state: WeatherProfile.WeatherState, new_state: WeatherProfile.WeatherState) -> void:
|
||||
weather_changed.emit(new_state)
|
||||
|
||||
|
||||
func _on_preset_changed(preset: SkyPreset) -> void:
|
||||
preset_changed.emit(preset)
|
||||
if preset.config:
|
||||
_current_config = preset.config
|
||||
_sync_preset_name()
|
||||
|
||||
_apply_preset_assets(preset)
|
||||
|
||||
|
||||
func _apply_seed() -> void:
|
||||
if deterministic_seed != 0:
|
||||
seed(deterministic_seed)
|
||||
|
||||
|
||||
func _sync_all_systems() -> void:
|
||||
if day_night_controller == null:
|
||||
return
|
||||
|
||||
var time := day_night_controller.time_of_day
|
||||
if debug_time_override >= 0:
|
||||
time = debug_time_override
|
||||
|
||||
_sync_sun_moon()
|
||||
_sync_clouds()
|
||||
_sync_post_process()
|
||||
_sync_shadows()
|
||||
|
||||
|
||||
func _sync_sun_moon() -> void:
|
||||
if day_night_controller == null:
|
||||
return
|
||||
|
||||
var sun_dir := day_night_controller.get_sun_direction()
|
||||
var moon_dir := day_night_controller.get_moon_direction()
|
||||
|
||||
if sun_light:
|
||||
sun_light.look_at(global_position - sun_dir, Vector3.UP)
|
||||
sun_light.light_color = day_night_controller.get_sun_color()
|
||||
sun_light.light_energy = day_night_controller.get_sun_intensity()
|
||||
sun_light.visible = day_night_controller.get_sun_intensity() > 0.01
|
||||
|
||||
if moon_light:
|
||||
moon_light.look_at(global_position - moon_dir, Vector3.UP)
|
||||
moon_light.light_color = day_night_controller.get_moon_color()
|
||||
moon_light.light_energy = day_night_controller.get_moon_intensity()
|
||||
moon_light.visible = day_night_controller.get_moon_intensity() > 0.01
|
||||
|
||||
if world_environment and world_environment.environment:
|
||||
world_environment.environment.ambient_light_color = day_night_controller.get_ambient_color()
|
||||
world_environment.environment.ambient_light_energy = day_night_controller.get_ambient_intensity()
|
||||
|
||||
|
||||
func _sync_clouds() -> void:
|
||||
if cloud_system == null:
|
||||
return
|
||||
|
||||
if day_night_controller:
|
||||
cloud_system.set_sun_lighting(
|
||||
day_night_controller.get_sun_direction(),
|
||||
day_night_controller.get_sun_color(),
|
||||
day_night_controller.get_sun_intensity()
|
||||
)
|
||||
cloud_system.set_moon_lighting(
|
||||
day_night_controller.get_moon_direction(),
|
||||
day_night_controller.get_moon_color(),
|
||||
day_night_controller.get_moon_intensity()
|
||||
)
|
||||
|
||||
if weather_controller:
|
||||
cloud_system.global_coverage = weather_controller.get_cloud_coverage()
|
||||
cloud_system.set_global_wind(
|
||||
weather_controller.get_wind_direction(),
|
||||
weather_controller.get_wind_speed()
|
||||
)
|
||||
|
||||
|
||||
func _sync_post_process() -> void:
|
||||
if post_process_controller == null or day_night_controller == null:
|
||||
return
|
||||
|
||||
var sun_dir := day_night_controller.get_sun_direction()
|
||||
var sun_color := day_night_controller.get_sun_color()
|
||||
|
||||
post_process_controller.set_sun_glow(
|
||||
sun_dir,
|
||||
sun_color,
|
||||
day_night_controller.get_sun_intensity() * 0.5,
|
||||
0.2
|
||||
)
|
||||
|
||||
post_process_controller.set_fog_settings(
|
||||
day_night_controller.get_fog_color(),
|
||||
day_night_controller.get_fog_density()
|
||||
)
|
||||
|
||||
var dawn_dusk_blend := 0.0
|
||||
if day_night_controller.is_dawn() or day_night_controller.is_dusk():
|
||||
dawn_dusk_blend = 1.0
|
||||
post_process_controller.set_dawn_dusk_blend(dawn_dusk_blend)
|
||||
|
||||
post_process_controller.set_stars_visibility(
|
||||
day_night_controller.get_stars_visibility()
|
||||
)
|
||||
|
||||
if _current_config:
|
||||
post_process_controller.set_sky_colors(
|
||||
_current_config.sky_top_color,
|
||||
_current_config.sky_horizon_color,
|
||||
_current_config.sky_bottom_color
|
||||
)
|
||||
post_process_controller.gradient_intensity = _current_config.gradient_intensity
|
||||
post_process_controller.apply_config(_current_config)
|
||||
|
||||
|
||||
func _apply_preset_assets(preset: SkyPreset) -> void:
|
||||
if preset == null:
|
||||
return
|
||||
|
||||
if post_process_controller:
|
||||
post_process_controller.skybox_texture = preset.skybox_texture
|
||||
if preset.post_process_profile:
|
||||
post_process_controller.profile = preset.post_process_profile
|
||||
|
||||
if cloud_system and preset.cloud_layer_configs.size() > 0:
|
||||
cloud_system.layer_configs = preset.cloud_layer_configs
|
||||
cloud_system.rebuild_layers()
|
||||
|
||||
|
||||
func _get_property_list() -> Array:
|
||||
var properties := []
|
||||
var names: PackedStringArray = []
|
||||
if preset_library:
|
||||
names = preset_library.get_preset_names()
|
||||
|
||||
var hint_string := ""
|
||||
if names.size() > 0:
|
||||
hint_string = ",".join(names)
|
||||
|
||||
properties.append({
|
||||
"name": "preset_name",
|
||||
"type": TYPE_STRING,
|
||||
"usage": PROPERTY_USAGE_EDITOR,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": hint_string,
|
||||
})
|
||||
return properties
|
||||
|
||||
|
||||
func _set(property: StringName, value: Variant) -> bool:
|
||||
if property == "preset_name":
|
||||
if preset_name == value:
|
||||
return true
|
||||
preset_name = value
|
||||
if _updating_preset_name:
|
||||
return true
|
||||
if preset_library and String(preset_name) != "":
|
||||
preset_library.apply_preset(String(preset_name))
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _get(property: StringName) -> Variant:
|
||||
if property == "preset_name":
|
||||
return preset_name
|
||||
return null
|
||||
|
||||
|
||||
func _sync_preset_name() -> void:
|
||||
if preset_library == null:
|
||||
return
|
||||
var current := preset_library.get_current_preset()
|
||||
if current == null:
|
||||
return
|
||||
_updating_preset_name = true
|
||||
preset_name = current.preset_name
|
||||
_updating_preset_name = false
|
||||
|
||||
|
||||
func _sync_shadows() -> void:
|
||||
if shadow_proxy_system == null:
|
||||
return
|
||||
|
||||
if weather_controller:
|
||||
shadow_proxy_system.set_cloud_coverage(weather_controller.get_cloud_coverage())
|
||||
|
||||
if cloud_system:
|
||||
shadow_proxy_system.sync_with_cloud_system(cloud_system)
|
||||
|
||||
|
||||
func _update_debug_info() -> void:
|
||||
pass
|
||||
|
||||
|
||||
func set_time_of_day(time: float) -> void:
|
||||
if day_night_controller:
|
||||
day_night_controller.time_of_day = time
|
||||
|
||||
|
||||
func set_weather(state: WeatherProfile.WeatherState, transition_time: float = -1.0) -> void:
|
||||
if weather_controller:
|
||||
weather_controller.set_weather(state, transition_time)
|
||||
|
||||
|
||||
func apply_preset(preset_name: String) -> void:
|
||||
if preset_library:
|
||||
preset_library.apply_preset(preset_name)
|
||||
|
||||
|
||||
func pause_time() -> void:
|
||||
if day_night_controller:
|
||||
day_night_controller.paused = true
|
||||
|
||||
|
||||
func resume_time() -> void:
|
||||
if day_night_controller:
|
||||
day_night_controller.paused = false
|
||||
|
||||
|
||||
func trigger_meteor_shower(duration: float = 60.0) -> void:
|
||||
if vfx_controller:
|
||||
vfx_controller.start_meteor_shower(duration)
|
||||
|
||||
|
||||
func get_time_of_day() -> float:
|
||||
if day_night_controller:
|
||||
return day_night_controller.time_of_day
|
||||
return 0.0
|
||||
|
||||
|
||||
func get_current_weather() -> WeatherProfile.WeatherState:
|
||||
if weather_controller:
|
||||
return weather_controller.current_weather
|
||||
return WeatherProfile.WeatherState.CLEAR
|
||||
|
||||
|
||||
func is_daytime() -> bool:
|
||||
if day_night_controller:
|
||||
return day_night_controller.is_daytime()
|
||||
return true
|
||||
|
||||
|
||||
func is_nighttime() -> bool:
|
||||
if day_night_controller:
|
||||
return day_night_controller.is_nighttime()
|
||||
return false
|
||||
1
dynamic-sky/scripts/dynamic_sky_root.gd.uid
Normal file
1
dynamic-sky/scripts/dynamic_sky_root.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://8r3do6ow5nmn
|
||||
310
dynamic-sky/scripts/post_process_controller.gd
Normal file
310
dynamic-sky/scripts/post_process_controller.gd
Normal file
@@ -0,0 +1,310 @@
|
||||
@tool
|
||||
class_name PostProcessController
|
||||
extends Node
|
||||
|
||||
@export_group("Profile")
|
||||
@export var profile: PostProcessProfile:
|
||||
set(value):
|
||||
profile = value
|
||||
_apply_profile()
|
||||
|
||||
@export_group("Environment")
|
||||
@export var target_environment: Environment
|
||||
|
||||
@export_group("Sky Gradient")
|
||||
@export var sky_top_color: Color = Color(0.2, 0.4, 0.8)
|
||||
@export var sky_horizon_color: Color = Color(0.6, 0.7, 0.9)
|
||||
@export var sky_bottom_color: Color = Color(0.4, 0.5, 0.6)
|
||||
@export var gradient_intensity: float = 1.0
|
||||
|
||||
@export_group("Skybox")
|
||||
@export var skybox_texture: Texture2D:
|
||||
set(value):
|
||||
skybox_texture = value
|
||||
_update_sky_material()
|
||||
@export var skybox_blend: float = 1.0:
|
||||
set(value):
|
||||
skybox_blend = clampf(value, 0.0, 1.0)
|
||||
_update_sky_material()
|
||||
|
||||
@export_group("Fog Shaping")
|
||||
@export var fog_enabled: bool = true
|
||||
@export var fog_color: Color = Color(0.7, 0.8, 0.9)
|
||||
@export var fog_density: float = 0.01
|
||||
@export var height_fog_enabled: bool = true
|
||||
@export var height_fog_min: float = 0.0
|
||||
@export var height_fog_max: float = 100.0
|
||||
@export var height_fog_curve: float = 1.0
|
||||
|
||||
@export_group("Sun Glow")
|
||||
@export var sun_glow_enabled: bool = true
|
||||
@export var sun_glow_intensity: float = 0.5
|
||||
@export var sun_glow_size: float = 0.2
|
||||
@export var sun_glow_falloff: float = 2.0
|
||||
@export var sun_glow_color: Color = Color(1.0, 0.95, 0.8)
|
||||
@export var sun_direction: Vector3 = Vector3(0.0, 1.0, 0.0)
|
||||
|
||||
@export_group("Dawn/Dusk Enhancement")
|
||||
@export var dawn_dusk_enabled: bool = true
|
||||
@export var dawn_dusk_intensity: float = 0.5
|
||||
@export var dawn_dusk_blend: float = 0.0
|
||||
|
||||
@export_group("Performance")
|
||||
@export var effects_enabled: bool = true
|
||||
|
||||
var _sky_material: ShaderMaterial
|
||||
var _post_process_quad: MeshInstance3D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if profile == null:
|
||||
profile = PostProcessProfile.new()
|
||||
_apply_profile()
|
||||
_setup_environment()
|
||||
|
||||
|
||||
func _apply_profile() -> void:
|
||||
if profile == null:
|
||||
return
|
||||
|
||||
gradient_intensity = profile.gradient_intensity
|
||||
fog_enabled = profile.fog_enabled
|
||||
height_fog_enabled = profile.height_fog_enabled
|
||||
height_fog_min = profile.height_fog_start
|
||||
height_fog_max = profile.height_fog_end
|
||||
height_fog_curve = profile.height_fog_curve
|
||||
sun_glow_enabled = profile.sun_glow_enabled
|
||||
sun_glow_intensity = profile.sun_glow_intensity
|
||||
sun_glow_size = profile.sun_glow_size
|
||||
sun_glow_falloff = profile.sun_glow_falloff
|
||||
sun_glow_color = profile.sun_glow_color
|
||||
dawn_dusk_enabled = profile.dawn_dusk_enabled
|
||||
dawn_dusk_intensity = profile.dawn_dusk_intensity
|
||||
|
||||
|
||||
func _setup_environment() -> void:
|
||||
if target_environment == null:
|
||||
return
|
||||
|
||||
_update_fog()
|
||||
_update_sky()
|
||||
|
||||
|
||||
func _update_fog() -> void:
|
||||
if target_environment == null:
|
||||
return
|
||||
|
||||
target_environment.fog_enabled = fog_enabled and effects_enabled
|
||||
|
||||
if fog_enabled:
|
||||
target_environment.fog_light_color = fog_color
|
||||
target_environment.fog_density = fog_density
|
||||
|
||||
if height_fog_enabled:
|
||||
target_environment.fog_height = height_fog_min
|
||||
target_environment.fog_height_density = height_fog_curve * 0.1
|
||||
|
||||
|
||||
func _update_sky() -> void:
|
||||
if target_environment == null:
|
||||
return
|
||||
|
||||
if target_environment.sky == null:
|
||||
target_environment.sky = Sky.new()
|
||||
|
||||
if target_environment.sky.sky_material == null or not target_environment.sky.sky_material is ShaderMaterial:
|
||||
_create_sky_material()
|
||||
|
||||
_sky_material = target_environment.sky.sky_material as ShaderMaterial
|
||||
if _sky_material:
|
||||
_update_sky_material()
|
||||
|
||||
|
||||
func _create_sky_material() -> void:
|
||||
_sky_material = ShaderMaterial.new()
|
||||
_sky_material.shader = _get_sky_shader()
|
||||
|
||||
if target_environment and target_environment.sky:
|
||||
target_environment.sky.sky_material = _sky_material
|
||||
|
||||
|
||||
func _get_sky_shader() -> Shader:
|
||||
var shader := Shader.new()
|
||||
shader.code = """
|
||||
shader_type sky;
|
||||
|
||||
uniform sampler2D skybox_texture : source_color, filter_linear_mipmap, repeat_enable;
|
||||
uniform bool skybox_enabled = false;
|
||||
uniform float skybox_blend = 1.0;
|
||||
|
||||
uniform vec4 sky_top_color : source_color = vec4(0.2, 0.4, 0.8, 1.0);
|
||||
uniform vec4 sky_horizon_color : source_color = vec4(0.6, 0.7, 0.9, 1.0);
|
||||
uniform vec4 sky_bottom_color : source_color = vec4(0.4, 0.5, 0.6, 1.0);
|
||||
uniform float gradient_intensity = 1.0;
|
||||
uniform float horizon_blend = 0.5;
|
||||
|
||||
uniform bool sun_glow_enabled = true;
|
||||
uniform vec3 sun_direction = vec3(0.0, 1.0, 0.0);
|
||||
uniform vec4 sun_glow_color : source_color = vec4(1.0, 0.95, 0.8, 1.0);
|
||||
uniform float sun_glow_intensity = 0.5;
|
||||
uniform float sun_glow_size = 0.2;
|
||||
uniform float sun_glow_falloff = 2.0;
|
||||
|
||||
uniform bool dawn_dusk_enabled = true;
|
||||
uniform vec4 dawn_dusk_color : source_color = vec4(1.0, 0.6, 0.4, 1.0);
|
||||
uniform float dawn_dusk_intensity = 0.5;
|
||||
uniform float dawn_dusk_blend = 0.0;
|
||||
|
||||
uniform float stars_visibility = 0.0;
|
||||
uniform vec4 stars_tint : source_color = vec4(1.0);
|
||||
|
||||
float hash(vec2 p) {
|
||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
|
||||
}
|
||||
|
||||
float stars(vec3 dir) {
|
||||
vec2 uv = dir.xz / (abs(dir.y) + 0.001) * 50.0;
|
||||
vec2 cell = floor(uv);
|
||||
vec2 local = fract(uv) - 0.5;
|
||||
|
||||
float star = 0.0;
|
||||
float h = hash(cell);
|
||||
if (h > 0.97) {
|
||||
float dist = length(local);
|
||||
star = smoothstep(0.1, 0.0, dist) * (h - 0.97) / 0.03;
|
||||
star *= step(0.0, dir.y);
|
||||
}
|
||||
return star;
|
||||
}
|
||||
|
||||
vec2 pano_uv(vec3 dir) {
|
||||
dir = normalize(dir);
|
||||
float u = atan(dir.x, dir.z) / (2.0 * PI) + 0.5;
|
||||
float v = acos(clamp(dir.y, -1.0, 1.0)) / PI;
|
||||
return vec2(u, v);
|
||||
}
|
||||
|
||||
void sky() {
|
||||
vec3 dir = EYEDIR;
|
||||
float y = dir.y;
|
||||
|
||||
vec3 color;
|
||||
if (y > 0.0) {
|
||||
float t = pow(y, horizon_blend);
|
||||
color = mix(sky_horizon_color.rgb, sky_top_color.rgb, t);
|
||||
} else {
|
||||
float t = pow(-y, horizon_blend);
|
||||
color = mix(sky_horizon_color.rgb, sky_bottom_color.rgb, t);
|
||||
}
|
||||
|
||||
color = mix(vec3(0.5), color, gradient_intensity);
|
||||
|
||||
if (skybox_enabled) {
|
||||
vec3 pano_color = texture(skybox_texture, pano_uv(dir)).rgb;
|
||||
color = mix(color, pano_color, skybox_blend);
|
||||
}
|
||||
|
||||
if (sun_glow_enabled && sun_direction.y > -0.2) {
|
||||
float sun_dot = max(0.0, dot(dir, normalize(sun_direction)));
|
||||
float glow = pow(sun_dot, 1.0 / max(0.001, sun_glow_size));
|
||||
glow = pow(glow, sun_glow_falloff);
|
||||
color += sun_glow_color.rgb * glow * sun_glow_intensity;
|
||||
}
|
||||
|
||||
if (dawn_dusk_enabled && dawn_dusk_blend > 0.0) {
|
||||
float horizon_factor = 1.0 - abs(y);
|
||||
horizon_factor = pow(horizon_factor, 2.0);
|
||||
color = mix(color, dawn_dusk_color.rgb, horizon_factor * dawn_dusk_intensity * dawn_dusk_blend);
|
||||
}
|
||||
|
||||
if (stars_visibility > 0.0 && y > 0.0) {
|
||||
float star_value = stars(dir) * stars_visibility;
|
||||
color += stars_tint.rgb * star_value;
|
||||
}
|
||||
|
||||
COLOR = color;
|
||||
}
|
||||
"""
|
||||
return shader
|
||||
|
||||
|
||||
func _update_sky_material() -> void:
|
||||
if _sky_material == null:
|
||||
return
|
||||
|
||||
if skybox_texture:
|
||||
_sky_material.set_shader_parameter("skybox_enabled", true)
|
||||
_sky_material.set_shader_parameter("skybox_texture", skybox_texture)
|
||||
_sky_material.set_shader_parameter("skybox_blend", skybox_blend)
|
||||
else:
|
||||
_sky_material.set_shader_parameter("skybox_enabled", false)
|
||||
_sky_material.set_shader_parameter("skybox_blend", skybox_blend)
|
||||
|
||||
_sky_material.set_shader_parameter("sky_top_color", sky_top_color)
|
||||
_sky_material.set_shader_parameter("sky_horizon_color", sky_horizon_color)
|
||||
_sky_material.set_shader_parameter("sky_bottom_color", sky_bottom_color)
|
||||
_sky_material.set_shader_parameter("gradient_intensity", gradient_intensity)
|
||||
|
||||
_sky_material.set_shader_parameter("sun_glow_enabled", sun_glow_enabled and effects_enabled)
|
||||
_sky_material.set_shader_parameter("sun_direction", sun_direction)
|
||||
_sky_material.set_shader_parameter("sun_glow_color", sun_glow_color)
|
||||
_sky_material.set_shader_parameter("sun_glow_intensity", sun_glow_intensity)
|
||||
_sky_material.set_shader_parameter("sun_glow_size", sun_glow_size)
|
||||
_sky_material.set_shader_parameter("sun_glow_falloff", sun_glow_falloff)
|
||||
|
||||
_sky_material.set_shader_parameter("dawn_dusk_enabled", dawn_dusk_enabled and effects_enabled)
|
||||
_sky_material.set_shader_parameter("dawn_dusk_blend", dawn_dusk_blend)
|
||||
_sky_material.set_shader_parameter("dawn_dusk_intensity", dawn_dusk_intensity)
|
||||
|
||||
|
||||
func set_sky_colors(top: Color, horizon: Color, bottom: Color) -> void:
|
||||
sky_top_color = top
|
||||
sky_horizon_color = horizon
|
||||
sky_bottom_color = bottom
|
||||
_update_sky_material()
|
||||
|
||||
|
||||
func set_fog_settings(color: Color, density: float) -> void:
|
||||
fog_color = color
|
||||
fog_density = density
|
||||
_update_fog()
|
||||
|
||||
|
||||
func set_sun_glow(direction: Vector3, color: Color, intensity: float, size: float) -> void:
|
||||
sun_direction = direction.normalized()
|
||||
sun_glow_color = color
|
||||
sun_glow_intensity = intensity
|
||||
sun_glow_size = size
|
||||
_update_sky_material()
|
||||
|
||||
|
||||
func set_dawn_dusk_blend(blend: float) -> void:
|
||||
dawn_dusk_blend = clampf(blend, 0.0, 1.0)
|
||||
_update_sky_material()
|
||||
|
||||
|
||||
func set_stars_visibility(visibility: float, tint: Color = Color.WHITE) -> void:
|
||||
if _sky_material:
|
||||
_sky_material.set_shader_parameter("stars_visibility", visibility)
|
||||
_sky_material.set_shader_parameter("stars_tint", tint)
|
||||
|
||||
|
||||
func set_effects_enabled(enabled: bool) -> void:
|
||||
effects_enabled = enabled
|
||||
_update_fog()
|
||||
_update_sky_material()
|
||||
|
||||
|
||||
func apply_config(config: SkyConfig) -> void:
|
||||
sky_top_color = config.sky_top_color
|
||||
sky_horizon_color = config.sky_horizon_color
|
||||
sky_bottom_color = config.sky_bottom_color
|
||||
fog_color = config.fog_color
|
||||
fog_density = config.fog_density
|
||||
sun_glow_intensity = config.sun_glow_intensity
|
||||
sun_glow_size = config.sun_glow_size
|
||||
gradient_intensity = config.gradient_intensity
|
||||
|
||||
_update_fog()
|
||||
_update_sky_material()
|
||||
set_stars_visibility(config.stars_visibility, config.stars_tint)
|
||||
1
dynamic-sky/scripts/post_process_controller.gd.uid
Normal file
1
dynamic-sky/scripts/post_process_controller.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cwqg0bg2m2bme
|
||||
93
dynamic-sky/scripts/procedural_cloud_texture.gd
Normal file
93
dynamic-sky/scripts/procedural_cloud_texture.gd
Normal file
@@ -0,0 +1,93 @@
|
||||
@tool
|
||||
class_name ProceduralCloudTexture
|
||||
extends Resource
|
||||
|
||||
@export var size: int = 512
|
||||
@export var octaves: int = 6
|
||||
@export var persistence: float = 0.5
|
||||
@export var lacunarity: float = 2.0
|
||||
@export var scale: float = 4.0
|
||||
@export var seed_value: int = 0
|
||||
@export var cloud_threshold: float = 0.4
|
||||
@export var cloud_softness: float = 0.3
|
||||
|
||||
var _noise: FastNoiseLite
|
||||
var _texture: ImageTexture
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_setup_noise()
|
||||
|
||||
|
||||
func _setup_noise() -> void:
|
||||
_noise = FastNoiseLite.new()
|
||||
_noise.noise_type = FastNoiseLite.TYPE_SIMPLEX
|
||||
_noise.seed = seed_value
|
||||
_noise.frequency = 1.0 / (scale * 64.0)
|
||||
_noise.fractal_type = FastNoiseLite.FRACTAL_FBM
|
||||
_noise.fractal_octaves = octaves
|
||||
_noise.fractal_lacunarity = lacunarity
|
||||
_noise.fractal_gain = persistence
|
||||
|
||||
|
||||
func generate() -> ImageTexture:
|
||||
_setup_noise()
|
||||
|
||||
var image := Image.create(size, size, false, Image.FORMAT_RGBA8)
|
||||
|
||||
for y in size:
|
||||
for x in size:
|
||||
var noise_value := (_noise.get_noise_2d(float(x), float(y)) + 1.0) * 0.5
|
||||
|
||||
var cloud_value := smoothstep(cloud_threshold - cloud_softness, cloud_threshold + cloud_softness, noise_value)
|
||||
|
||||
var color := Color(cloud_value, cloud_value, cloud_value, cloud_value)
|
||||
image.set_pixel(x, y, color)
|
||||
|
||||
_texture = ImageTexture.create_from_image(image)
|
||||
return _texture
|
||||
|
||||
|
||||
func get_texture() -> ImageTexture:
|
||||
if _texture == null:
|
||||
return generate()
|
||||
return _texture
|
||||
|
||||
|
||||
static func create_default_cloud_texture(layer_type: int = 0) -> ImageTexture:
|
||||
var generator := ProceduralCloudTexture.new()
|
||||
generator.size = 512
|
||||
|
||||
match layer_type:
|
||||
0: # Low clouds - larger, denser
|
||||
generator.scale = 3.0
|
||||
generator.octaves = 4
|
||||
generator.cloud_threshold = 0.35
|
||||
generator.cloud_softness = 0.25
|
||||
1: # Mid clouds - medium
|
||||
generator.scale = 4.0
|
||||
generator.octaves = 5
|
||||
generator.cloud_threshold = 0.4
|
||||
generator.cloud_softness = 0.3
|
||||
2: # High clouds - wispy
|
||||
generator.scale = 6.0
|
||||
generator.octaves = 6
|
||||
generator.cloud_threshold = 0.5
|
||||
generator.cloud_softness = 0.35
|
||||
3: # Wisps - very thin
|
||||
generator.scale = 8.0
|
||||
generator.octaves = 7
|
||||
generator.cloud_threshold = 0.55
|
||||
generator.cloud_softness = 0.4
|
||||
|
||||
return generator.generate()
|
||||
|
||||
|
||||
static func create_shadow_texture() -> ImageTexture:
|
||||
var generator := ProceduralCloudTexture.new()
|
||||
generator.size = 256
|
||||
generator.scale = 4.0
|
||||
generator.octaves = 4
|
||||
generator.cloud_threshold = 0.3
|
||||
generator.cloud_softness = 0.4
|
||||
return generator.generate()
|
||||
1
dynamic-sky/scripts/procedural_cloud_texture.gd.uid
Normal file
1
dynamic-sky/scripts/procedural_cloud_texture.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dyjrdeqft0xlk
|
||||
220
dynamic-sky/scripts/shadow_proxy_system.gd
Normal file
220
dynamic-sky/scripts/shadow_proxy_system.gd
Normal file
@@ -0,0 +1,220 @@
|
||||
@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()
|
||||
1
dynamic-sky/scripts/shadow_proxy_system.gd.uid
Normal file
1
dynamic-sky/scripts/shadow_proxy_system.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://hvs2g6acqryq
|
||||
171
dynamic-sky/scripts/sky_debug_panel.gd
Normal file
171
dynamic-sky/scripts/sky_debug_panel.gd
Normal file
@@ -0,0 +1,171 @@
|
||||
@tool
|
||||
class_name SkyDebugPanel
|
||||
extends Control
|
||||
|
||||
@export var dynamic_sky: DynamicSkyRoot
|
||||
|
||||
var _panel: PanelContainer
|
||||
var _vbox: VBoxContainer
|
||||
var _time_label: Label
|
||||
var _weather_label: Label
|
||||
var _cloud_label: Label
|
||||
var _seed_label: Label
|
||||
var _time_slider: HSlider
|
||||
var _weather_dropdown: OptionButton
|
||||
var _pause_button: Button
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_create_ui()
|
||||
_connect_signals()
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if dynamic_sky == null:
|
||||
return
|
||||
_update_labels()
|
||||
|
||||
|
||||
func _create_ui() -> void:
|
||||
_panel = PanelContainer.new()
|
||||
_panel.name = "DebugPanel"
|
||||
_panel.custom_minimum_size = Vector2(300, 200)
|
||||
add_child(_panel)
|
||||
|
||||
var margin := MarginContainer.new()
|
||||
margin.add_theme_constant_override("margin_left", 10)
|
||||
margin.add_theme_constant_override("margin_right", 10)
|
||||
margin.add_theme_constant_override("margin_top", 10)
|
||||
margin.add_theme_constant_override("margin_bottom", 10)
|
||||
_panel.add_child(margin)
|
||||
|
||||
_vbox = VBoxContainer.new()
|
||||
_vbox.add_theme_constant_override("separation", 8)
|
||||
margin.add_child(_vbox)
|
||||
|
||||
var title := Label.new()
|
||||
title.text = "Dynamic Sky Debug"
|
||||
title.add_theme_font_size_override("font_size", 18)
|
||||
_vbox.add_child(title)
|
||||
|
||||
_vbox.add_child(HSeparator.new())
|
||||
|
||||
_time_label = Label.new()
|
||||
_time_label.text = "Time: 00:00 (0.00)"
|
||||
_vbox.add_child(_time_label)
|
||||
|
||||
var time_hbox := HBoxContainer.new()
|
||||
_vbox.add_child(time_hbox)
|
||||
|
||||
var time_label := Label.new()
|
||||
time_label.text = "Time:"
|
||||
time_label.custom_minimum_size.x = 60
|
||||
time_hbox.add_child(time_label)
|
||||
|
||||
_time_slider = HSlider.new()
|
||||
_time_slider.min_value = 0.0
|
||||
_time_slider.max_value = 1.0
|
||||
_time_slider.step = 0.001
|
||||
_time_slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
time_hbox.add_child(_time_slider)
|
||||
|
||||
_pause_button = Button.new()
|
||||
_pause_button.text = "Pause"
|
||||
_pause_button.toggle_mode = true
|
||||
_vbox.add_child(_pause_button)
|
||||
|
||||
_vbox.add_child(HSeparator.new())
|
||||
|
||||
_weather_label = Label.new()
|
||||
_weather_label.text = "Weather: Clear"
|
||||
_vbox.add_child(_weather_label)
|
||||
|
||||
var weather_hbox := HBoxContainer.new()
|
||||
_vbox.add_child(weather_hbox)
|
||||
|
||||
var weather_label := Label.new()
|
||||
weather_label.text = "Set:"
|
||||
weather_label.custom_minimum_size.x = 60
|
||||
weather_hbox.add_child(weather_label)
|
||||
|
||||
_weather_dropdown = OptionButton.new()
|
||||
_weather_dropdown.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
for state_name in WeatherProfile.WeatherState.keys():
|
||||
_weather_dropdown.add_item(state_name)
|
||||
weather_hbox.add_child(_weather_dropdown)
|
||||
|
||||
_vbox.add_child(HSeparator.new())
|
||||
|
||||
_cloud_label = Label.new()
|
||||
_cloud_label.text = "Clouds: 0%"
|
||||
_vbox.add_child(_cloud_label)
|
||||
|
||||
_seed_label = Label.new()
|
||||
_seed_label.text = "Seed: 0"
|
||||
_vbox.add_child(_seed_label)
|
||||
|
||||
var meteor_button := Button.new()
|
||||
meteor_button.text = "Trigger Meteor Shower"
|
||||
meteor_button.pressed.connect(_on_meteor_pressed)
|
||||
_vbox.add_child(meteor_button)
|
||||
|
||||
|
||||
func _connect_signals() -> void:
|
||||
_time_slider.value_changed.connect(_on_time_slider_changed)
|
||||
_pause_button.toggled.connect(_on_pause_toggled)
|
||||
_weather_dropdown.item_selected.connect(_on_weather_selected)
|
||||
|
||||
|
||||
func _update_labels() -> void:
|
||||
if dynamic_sky == null:
|
||||
return
|
||||
|
||||
var time := dynamic_sky.get_time_of_day()
|
||||
var hours := int(time * 24.0)
|
||||
var minutes := int(fmod(time * 24.0 * 60.0, 60.0))
|
||||
_time_label.text = "Time: %02d:%02d (%.3f)" % [hours, minutes, time]
|
||||
|
||||
if not _time_slider.has_focus():
|
||||
_time_slider.set_value_no_signal(time)
|
||||
|
||||
var weather := dynamic_sky.get_current_weather()
|
||||
var weather_name: String = WeatherProfile.WeatherState.keys()[weather]
|
||||
_weather_label.text = "Weather: %s" % weather_name
|
||||
|
||||
if dynamic_sky.weather_controller:
|
||||
var coverage := dynamic_sky.weather_controller.get_cloud_coverage()
|
||||
_cloud_label.text = "Clouds: %d%%" % int(coverage * 100)
|
||||
|
||||
if dynamic_sky.weather_controller.is_transitioning():
|
||||
var progress := dynamic_sky.weather_controller.get_transition_progress()
|
||||
_weather_label.text += " (transitioning %.0f%%)" % (progress * 100)
|
||||
|
||||
_seed_label.text = "Seed: %d" % dynamic_sky.deterministic_seed
|
||||
|
||||
if dynamic_sky.day_night_controller:
|
||||
_pause_button.set_pressed_no_signal(dynamic_sky.day_night_controller.paused)
|
||||
|
||||
|
||||
func _on_time_slider_changed(value: float) -> void:
|
||||
if dynamic_sky:
|
||||
dynamic_sky.set_time_of_day(value)
|
||||
|
||||
|
||||
func _on_pause_toggled(pressed: bool) -> void:
|
||||
if dynamic_sky:
|
||||
if pressed:
|
||||
dynamic_sky.pause_time()
|
||||
else:
|
||||
dynamic_sky.resume_time()
|
||||
_pause_button.text = "Resume" if pressed else "Pause"
|
||||
|
||||
|
||||
func _on_weather_selected(index: int) -> void:
|
||||
if dynamic_sky:
|
||||
var state: WeatherProfile.WeatherState = index as WeatherProfile.WeatherState
|
||||
dynamic_sky.set_weather(state)
|
||||
|
||||
|
||||
func _on_meteor_pressed() -> void:
|
||||
if dynamic_sky:
|
||||
dynamic_sky.trigger_meteor_shower(30.0)
|
||||
1
dynamic-sky/scripts/sky_debug_panel.gd.uid
Normal file
1
dynamic-sky/scripts/sky_debug_panel.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cimhpqq1qo80b
|
||||
131
dynamic-sky/scripts/skybox_preset_library.gd
Normal file
131
dynamic-sky/scripts/skybox_preset_library.gd
Normal file
@@ -0,0 +1,131 @@
|
||||
@tool
|
||||
class_name SkyboxPresetLibrary
|
||||
extends Node
|
||||
|
||||
signal preset_changed(preset: SkyPreset)
|
||||
signal preset_applied(preset_name: String)
|
||||
signal preset_reverted(preset_name: String)
|
||||
|
||||
@export var presets: Array[SkyPreset] = []
|
||||
@export var current_preset_index: int = 0:
|
||||
set(value):
|
||||
current_preset_index = clampi(value, 0, maxi(0, presets.size() - 1))
|
||||
if current_preset_index < presets.size():
|
||||
_on_preset_selected(presets[current_preset_index])
|
||||
|
||||
var _base_preset: SkyPreset
|
||||
var _runtime_overrides: SkyConfig
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if presets.is_empty():
|
||||
_initialize_default_presets()
|
||||
if current_preset_index < presets.size():
|
||||
_base_preset = presets[current_preset_index]
|
||||
|
||||
|
||||
func _initialize_default_presets() -> void:
|
||||
presets.clear()
|
||||
presets.append(SkyPreset.create_clear_day())
|
||||
presets.append(SkyPreset.create_sunset())
|
||||
presets.append(SkyPreset.create_night())
|
||||
presets.append(SkyPreset.create_overcast())
|
||||
presets.append(SkyPreset.create_storm())
|
||||
presets.append(SkyPreset.create_dawn())
|
||||
|
||||
|
||||
func get_preset_names() -> PackedStringArray:
|
||||
var names := PackedStringArray()
|
||||
for preset in presets:
|
||||
names.append(preset.preset_name)
|
||||
return names
|
||||
|
||||
|
||||
func get_current_preset() -> SkyPreset:
|
||||
if current_preset_index < presets.size():
|
||||
return presets[current_preset_index]
|
||||
return null
|
||||
|
||||
|
||||
func get_preset_by_name(preset_name: String) -> SkyPreset:
|
||||
for preset in presets:
|
||||
if preset.preset_name == preset_name:
|
||||
return preset
|
||||
return null
|
||||
|
||||
|
||||
func get_preset_by_type(preset_type: SkyPreset.PresetType) -> SkyPreset:
|
||||
for preset in presets:
|
||||
if preset.preset_type == preset_type:
|
||||
return preset
|
||||
return null
|
||||
|
||||
|
||||
func apply_preset(preset_name: String) -> bool:
|
||||
var preset := get_preset_by_name(preset_name)
|
||||
if preset == null:
|
||||
push_warning("SkyboxPresetLibrary: Preset '%s' not found" % preset_name)
|
||||
return false
|
||||
|
||||
for i in presets.size():
|
||||
if presets[i] == preset:
|
||||
current_preset_index = i
|
||||
break
|
||||
|
||||
_base_preset = preset
|
||||
_runtime_overrides = null
|
||||
preset_applied.emit(preset_name)
|
||||
preset_changed.emit(preset)
|
||||
return true
|
||||
|
||||
|
||||
func apply_preset_by_type(preset_type: SkyPreset.PresetType) -> bool:
|
||||
var preset := get_preset_by_type(preset_type)
|
||||
if preset == null:
|
||||
push_warning("SkyboxPresetLibrary: Preset type '%s' not found" % SkyPreset.PresetType.keys()[preset_type])
|
||||
return false
|
||||
return apply_preset(preset.preset_name)
|
||||
|
||||
|
||||
func revert_to_preset() -> void:
|
||||
_runtime_overrides = null
|
||||
if _base_preset:
|
||||
preset_reverted.emit(_base_preset.preset_name)
|
||||
preset_changed.emit(_base_preset)
|
||||
|
||||
|
||||
func set_runtime_override(config: SkyConfig) -> void:
|
||||
_runtime_overrides = config
|
||||
|
||||
|
||||
func get_effective_config() -> SkyConfig:
|
||||
if _runtime_overrides != null:
|
||||
return _runtime_overrides
|
||||
if _base_preset != null and _base_preset.config != null:
|
||||
return _base_preset.config
|
||||
return SkyConfig.new()
|
||||
|
||||
|
||||
func has_runtime_overrides() -> bool:
|
||||
return _runtime_overrides != null
|
||||
|
||||
|
||||
func add_preset(preset: SkyPreset) -> void:
|
||||
if preset not in presets:
|
||||
presets.append(preset)
|
||||
|
||||
|
||||
func remove_preset(preset_name: String) -> bool:
|
||||
for i in presets.size():
|
||||
if presets[i].preset_name == preset_name:
|
||||
presets.remove_at(i)
|
||||
if current_preset_index >= presets.size():
|
||||
current_preset_index = maxi(0, presets.size() - 1)
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _on_preset_selected(preset: SkyPreset) -> void:
|
||||
_base_preset = preset
|
||||
_runtime_overrides = null
|
||||
preset_changed.emit(preset)
|
||||
1
dynamic-sky/scripts/skybox_preset_library.gd.uid
Normal file
1
dynamic-sky/scripts/skybox_preset_library.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cb8c6ug44jx4t
|
||||
299
dynamic-sky/scripts/vfx_controller.gd
Normal file
299
dynamic-sky/scripts/vfx_controller.gd
Normal file
@@ -0,0 +1,299 @@
|
||||
@tool
|
||||
class_name VFXController
|
||||
extends Node3D
|
||||
|
||||
signal shooting_star_spawned(position: Vector3, direction: Vector3)
|
||||
signal meteor_shower_started()
|
||||
signal meteor_shower_ended()
|
||||
|
||||
@export_group("Shooting Stars")
|
||||
@export var shooting_stars_enabled: bool = true
|
||||
@export var shooting_star_frequency: float = 0.05
|
||||
@export var min_spawn_interval: float = 5.0
|
||||
@export var max_spawn_interval: float = 30.0
|
||||
|
||||
@export_group("Shooting Star Appearance")
|
||||
@export var trail_length: float = 2.0
|
||||
@export var trail_width: float = 0.05
|
||||
@export var star_brightness: float = 1.5
|
||||
@export var star_color: Color = Color(1.0, 1.0, 0.9)
|
||||
@export var trail_fade_time: float = 0.5
|
||||
|
||||
@export_group("Meteor Shower")
|
||||
@export var meteor_shower_active: bool = false
|
||||
@export var meteor_shower_intensity: float = 1.0
|
||||
@export var meteors_per_second: float = 3.0
|
||||
@export var meteor_shower_duration: float = 60.0
|
||||
|
||||
@export_group("Meteor Appearance")
|
||||
@export var meteor_trail_length: float = 4.0
|
||||
@export var meteor_brightness: float = 2.0
|
||||
@export var meteor_color: Color = Color(1.0, 0.8, 0.5)
|
||||
|
||||
@export_group("Spawn Area")
|
||||
@export var spawn_radius: float = 500.0
|
||||
@export var spawn_height_min: float = 100.0
|
||||
@export var spawn_height_max: float = 200.0
|
||||
@export var spawn_direction_variance: float = 30.0
|
||||
|
||||
@export_group("Audio")
|
||||
@export var meteor_shower_audio_event: String = ""
|
||||
|
||||
var _spawn_timer: float = 0.0
|
||||
var _next_spawn_time: float = 0.0
|
||||
var _meteor_timer: float = 0.0
|
||||
var _meteor_spawn_accumulator: float = 0.0
|
||||
var _meteor_shower_elapsed: float = 0.0
|
||||
var _active_stars: Array[Dictionary] = []
|
||||
var _star_mesh: MeshInstance3D
|
||||
var _star_material: ShaderMaterial
|
||||
var _is_night: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_setup_star_mesh()
|
||||
_schedule_next_shooting_star()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if not _is_night:
|
||||
return
|
||||
|
||||
if shooting_stars_enabled:
|
||||
_process_shooting_stars(delta)
|
||||
|
||||
if meteor_shower_active:
|
||||
_process_meteor_shower(delta)
|
||||
|
||||
_update_active_stars(delta)
|
||||
|
||||
|
||||
func _setup_star_mesh() -> void:
|
||||
_star_material = ShaderMaterial.new()
|
||||
_star_material.shader = _get_star_shader()
|
||||
_star_material.set_shader_parameter("color", star_color)
|
||||
_star_material.set_shader_parameter("brightness", star_brightness)
|
||||
|
||||
|
||||
func _get_star_shader() -> Shader:
|
||||
var shader := Shader.new()
|
||||
shader.code = """
|
||||
shader_type spatial;
|
||||
render_mode unshaded, depth_draw_never, cull_disabled, blend_add;
|
||||
|
||||
uniform vec4 color : source_color = vec4(1.0, 1.0, 0.9, 1.0);
|
||||
uniform float brightness = 1.5;
|
||||
uniform float fade = 1.0;
|
||||
uniform float trail_progress = 0.0;
|
||||
|
||||
void vertex() {
|
||||
VERTEX.y *= mix(0.1, 1.0, trail_progress);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
float gradient = UV.y;
|
||||
float alpha = gradient * fade;
|
||||
ALBEDO = color.rgb * brightness;
|
||||
ALPHA = alpha * color.a;
|
||||
}
|
||||
"""
|
||||
return shader
|
||||
|
||||
|
||||
func _schedule_next_shooting_star() -> void:
|
||||
_next_spawn_time = randf_range(min_spawn_interval, max_spawn_interval)
|
||||
_next_spawn_time /= maxf(0.01, shooting_star_frequency)
|
||||
_spawn_timer = 0.0
|
||||
|
||||
|
||||
func _process_shooting_stars(delta: float) -> void:
|
||||
_spawn_timer += delta
|
||||
|
||||
if _spawn_timer >= _next_spawn_time:
|
||||
_spawn_shooting_star()
|
||||
_schedule_next_shooting_star()
|
||||
|
||||
|
||||
func _process_meteor_shower(delta: float) -> void:
|
||||
_meteor_shower_elapsed += delta
|
||||
|
||||
if _meteor_shower_elapsed >= meteor_shower_duration:
|
||||
stop_meteor_shower()
|
||||
return
|
||||
|
||||
_meteor_spawn_accumulator += meteors_per_second * meteor_shower_intensity * delta
|
||||
|
||||
while _meteor_spawn_accumulator >= 1.0:
|
||||
_meteor_spawn_accumulator -= 1.0
|
||||
_spawn_meteor()
|
||||
|
||||
|
||||
func _spawn_shooting_star() -> void:
|
||||
var spawn_pos := _get_random_sky_position()
|
||||
var direction := _get_random_fall_direction()
|
||||
|
||||
var star_data := {
|
||||
"position": spawn_pos,
|
||||
"direction": direction,
|
||||
"speed": randf_range(50.0, 100.0),
|
||||
"lifetime": 0.0,
|
||||
"max_lifetime": trail_fade_time + randf_range(0.2, 0.5),
|
||||
"trail_length": trail_length * randf_range(0.8, 1.2),
|
||||
"brightness": star_brightness * randf_range(0.8, 1.2),
|
||||
"color": star_color,
|
||||
"mesh": _create_star_trail(spawn_pos, direction),
|
||||
"is_meteor": false
|
||||
}
|
||||
|
||||
_active_stars.append(star_data)
|
||||
shooting_star_spawned.emit(spawn_pos, direction)
|
||||
|
||||
|
||||
func _spawn_meteor() -> void:
|
||||
var spawn_pos := _get_random_sky_position()
|
||||
var direction := _get_random_fall_direction()
|
||||
|
||||
var star_data := {
|
||||
"position": spawn_pos,
|
||||
"direction": direction,
|
||||
"speed": randf_range(80.0, 150.0),
|
||||
"lifetime": 0.0,
|
||||
"max_lifetime": trail_fade_time + randf_range(0.3, 0.7),
|
||||
"trail_length": meteor_trail_length * randf_range(0.8, 1.2),
|
||||
"brightness": meteor_brightness * randf_range(0.8, 1.2),
|
||||
"color": meteor_color,
|
||||
"mesh": _create_star_trail(spawn_pos, direction, true),
|
||||
"is_meteor": true
|
||||
}
|
||||
|
||||
_active_stars.append(star_data)
|
||||
|
||||
|
||||
func _get_random_sky_position() -> Vector3:
|
||||
var angle := randf() * TAU
|
||||
var radius := randf_range(spawn_radius * 0.3, spawn_radius)
|
||||
var height := randf_range(spawn_height_min, spawn_height_max)
|
||||
|
||||
return Vector3(
|
||||
cos(angle) * radius,
|
||||
height,
|
||||
sin(angle) * radius
|
||||
)
|
||||
|
||||
|
||||
func _get_random_fall_direction() -> Vector3:
|
||||
var base_dir := Vector3(-0.5, -0.8, -0.3).normalized()
|
||||
|
||||
var variance := deg_to_rad(spawn_direction_variance)
|
||||
var rotation := Basis(Vector3.UP, randf() * TAU)
|
||||
rotation = rotation.rotated(Vector3.RIGHT, randf_range(-variance, variance))
|
||||
|
||||
return (rotation * base_dir).normalized()
|
||||
|
||||
|
||||
func _create_star_trail(position: Vector3, direction: Vector3, is_meteor: bool = false) -> MeshInstance3D:
|
||||
var mesh_instance := MeshInstance3D.new()
|
||||
|
||||
var length := meteor_trail_length if is_meteor else trail_length
|
||||
var width := trail_width * (1.5 if is_meteor else 1.0)
|
||||
|
||||
var quad := QuadMesh.new()
|
||||
quad.size = Vector2(width, length)
|
||||
mesh_instance.mesh = quad
|
||||
|
||||
var material := _star_material.duplicate() as ShaderMaterial
|
||||
material.set_shader_parameter("color", meteor_color if is_meteor else star_color)
|
||||
material.set_shader_parameter("brightness", meteor_brightness if is_meteor else star_brightness)
|
||||
mesh_instance.material_override = material
|
||||
|
||||
mesh_instance.position = position
|
||||
mesh_instance.look_at(position + direction, Vector3.UP)
|
||||
mesh_instance.rotate_object_local(Vector3.RIGHT, PI / 2.0)
|
||||
|
||||
add_child(mesh_instance)
|
||||
return mesh_instance
|
||||
|
||||
|
||||
func _update_active_stars(delta: float) -> void:
|
||||
var stars_to_remove: Array[int] = []
|
||||
|
||||
for i in _active_stars.size():
|
||||
var star: Dictionary = _active_stars[i]
|
||||
star["lifetime"] += delta
|
||||
|
||||
var progress: float = star["lifetime"] / star["max_lifetime"]
|
||||
|
||||
if progress >= 1.0:
|
||||
stars_to_remove.append(i)
|
||||
continue
|
||||
|
||||
var mesh: MeshInstance3D = star["mesh"]
|
||||
if is_instance_valid(mesh):
|
||||
star["position"] += star["direction"] * star["speed"] * delta
|
||||
mesh.position = star["position"]
|
||||
|
||||
var fade := 1.0 - smoothstep(0.5, 1.0, progress)
|
||||
var material := mesh.material_override as ShaderMaterial
|
||||
if material:
|
||||
material.set_shader_parameter("fade", fade)
|
||||
material.set_shader_parameter("trail_progress", minf(progress * 3.0, 1.0))
|
||||
|
||||
for i in range(stars_to_remove.size() - 1, -1, -1):
|
||||
var idx: int = stars_to_remove[i]
|
||||
var star: Dictionary = _active_stars[idx]
|
||||
var mesh: MeshInstance3D = star["mesh"]
|
||||
if is_instance_valid(mesh):
|
||||
mesh.queue_free()
|
||||
_active_stars.remove_at(idx)
|
||||
|
||||
|
||||
func set_night_mode(is_night: bool) -> void:
|
||||
_is_night = is_night
|
||||
|
||||
if not is_night:
|
||||
_clear_all_stars()
|
||||
|
||||
|
||||
func _clear_all_stars() -> void:
|
||||
for star in _active_stars:
|
||||
var mesh: MeshInstance3D = star["mesh"]
|
||||
if is_instance_valid(mesh):
|
||||
mesh.queue_free()
|
||||
_active_stars.clear()
|
||||
|
||||
|
||||
func start_meteor_shower(duration: float = -1.0) -> void:
|
||||
if duration > 0:
|
||||
meteor_shower_duration = duration
|
||||
|
||||
meteor_shower_active = true
|
||||
_meteor_shower_elapsed = 0.0
|
||||
_meteor_spawn_accumulator = 0.0
|
||||
|
||||
meteor_shower_started.emit()
|
||||
|
||||
if meteor_shower_audio_event != "":
|
||||
pass
|
||||
|
||||
|
||||
func stop_meteor_shower() -> void:
|
||||
meteor_shower_active = false
|
||||
_meteor_shower_elapsed = 0.0
|
||||
meteor_shower_ended.emit()
|
||||
|
||||
|
||||
func trigger_shooting_star() -> void:
|
||||
if _is_night:
|
||||
_spawn_shooting_star()
|
||||
|
||||
|
||||
func set_shooting_star_appearance(color: Color, brightness: float, length: float) -> void:
|
||||
star_color = color
|
||||
star_brightness = brightness
|
||||
trail_length = length
|
||||
|
||||
|
||||
func set_meteor_appearance(color: Color, brightness: float, length: float) -> void:
|
||||
meteor_color = color
|
||||
meteor_brightness = brightness
|
||||
meteor_trail_length = length
|
||||
1
dynamic-sky/scripts/vfx_controller.gd.uid
Normal file
1
dynamic-sky/scripts/vfx_controller.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cnvj3m5fpsdtj
|
||||
278
dynamic-sky/scripts/weather_controller.gd
Normal file
278
dynamic-sky/scripts/weather_controller.gd
Normal file
@@ -0,0 +1,278 @@
|
||||
@tool
|
||||
class_name WeatherController
|
||||
extends Node
|
||||
|
||||
signal weather_state_changed(old_state: WeatherProfile.WeatherState, new_state: WeatherProfile.WeatherState)
|
||||
signal weather_transition_started(from: WeatherProfile.WeatherState, to: WeatherProfile.WeatherState)
|
||||
signal weather_transition_completed(state: WeatherProfile.WeatherState)
|
||||
signal weather_state_entered(state: WeatherProfile.WeatherState)
|
||||
signal weather_state_exited(state: WeatherProfile.WeatherState)
|
||||
|
||||
@export_group("Weather Profiles")
|
||||
@export var weather_profiles: Array[WeatherProfile] = []
|
||||
@export var current_weather: WeatherProfile.WeatherState = WeatherProfile.WeatherState.CLEAR:
|
||||
set(value):
|
||||
if value != current_weather:
|
||||
_request_weather_change(value)
|
||||
|
||||
@export_group("Transition Settings")
|
||||
@export var default_transition_duration: float = 5.0
|
||||
@export var transition_curve: Curve
|
||||
|
||||
@export_group("Random Weather")
|
||||
@export var enable_random_weather: bool = false
|
||||
@export var min_weather_duration: float = 60.0
|
||||
@export var max_weather_duration: float = 300.0
|
||||
@export var snow_enabled: bool = true
|
||||
|
||||
@export_group("Scripted Timeline")
|
||||
@export var use_scripted_timeline: bool = false
|
||||
@export var weather_timeline: Array[Dictionary] = []
|
||||
|
||||
var _current_profile: WeatherProfile
|
||||
var _target_profile: WeatherProfile
|
||||
var _transition_weight: float = 1.0
|
||||
var _is_transitioning: bool = false
|
||||
var _transition_duration: float = 0.0
|
||||
var _transition_elapsed: float = 0.0
|
||||
var _random_weather_timer: float = 0.0
|
||||
var _next_weather_change: float = 0.0
|
||||
var _timeline_index: int = 0
|
||||
var _timeline_timer: float = 0.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if weather_profiles.is_empty():
|
||||
_initialize_default_profiles()
|
||||
|
||||
if transition_curve == null:
|
||||
transition_curve = Curve.new()
|
||||
transition_curve.add_point(Vector2(0.0, 0.0))
|
||||
transition_curve.add_point(Vector2(0.5, 0.5))
|
||||
transition_curve.add_point(Vector2(1.0, 1.0))
|
||||
|
||||
_current_profile = get_profile_for_state(current_weather)
|
||||
_target_profile = _current_profile
|
||||
_schedule_next_random_weather()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if _is_transitioning:
|
||||
_process_transition(delta)
|
||||
|
||||
if enable_random_weather and not use_scripted_timeline:
|
||||
_process_random_weather(delta)
|
||||
|
||||
if use_scripted_timeline:
|
||||
_process_scripted_timeline(delta)
|
||||
|
||||
|
||||
func _initialize_default_profiles() -> void:
|
||||
weather_profiles.clear()
|
||||
weather_profiles.append(WeatherProfile.create_clear())
|
||||
weather_profiles.append(WeatherProfile.create_cloudy())
|
||||
weather_profiles.append(WeatherProfile.create_overcast())
|
||||
weather_profiles.append(WeatherProfile.create_rain())
|
||||
weather_profiles.append(WeatherProfile.create_storm())
|
||||
weather_profiles.append(WeatherProfile.create_snow())
|
||||
|
||||
|
||||
func _process_transition(delta: float) -> void:
|
||||
_transition_elapsed += delta
|
||||
var t := clampf(_transition_elapsed / _transition_duration, 0.0, 1.0)
|
||||
|
||||
if transition_curve:
|
||||
_transition_weight = transition_curve.sample(t)
|
||||
else:
|
||||
_transition_weight = t
|
||||
|
||||
if t >= 1.0:
|
||||
_complete_transition()
|
||||
|
||||
|
||||
func _complete_transition() -> void:
|
||||
_is_transitioning = false
|
||||
_transition_weight = 1.0
|
||||
_current_profile = _target_profile
|
||||
var new_state := _current_profile.weather_state
|
||||
weather_transition_completed.emit(new_state)
|
||||
weather_state_entered.emit(new_state)
|
||||
|
||||
|
||||
func _process_random_weather(delta: float) -> void:
|
||||
_random_weather_timer += delta
|
||||
|
||||
if _random_weather_timer >= _next_weather_change:
|
||||
_random_weather_timer = 0.0
|
||||
_trigger_random_weather_change()
|
||||
_schedule_next_random_weather()
|
||||
|
||||
|
||||
func _schedule_next_random_weather() -> void:
|
||||
_next_weather_change = randf_range(min_weather_duration, max_weather_duration)
|
||||
|
||||
|
||||
func _trigger_random_weather_change() -> void:
|
||||
var available_states: Array[WeatherProfile.WeatherState] = []
|
||||
for state in WeatherProfile.WeatherState.values():
|
||||
if state == current_weather:
|
||||
continue
|
||||
if state == WeatherProfile.WeatherState.SNOW and not snow_enabled:
|
||||
continue
|
||||
available_states.append(state)
|
||||
|
||||
if available_states.is_empty():
|
||||
return
|
||||
|
||||
var new_state: WeatherProfile.WeatherState = available_states.pick_random()
|
||||
set_weather(new_state)
|
||||
|
||||
|
||||
func _process_scripted_timeline(delta: float) -> void:
|
||||
if weather_timeline.is_empty():
|
||||
return
|
||||
|
||||
_timeline_timer += delta
|
||||
|
||||
if _timeline_index < weather_timeline.size():
|
||||
var entry: Dictionary = weather_timeline[_timeline_index]
|
||||
var trigger_time: float = entry.get("time", 0.0)
|
||||
|
||||
if _timeline_timer >= trigger_time:
|
||||
var state_name: String = entry.get("state", "CLEAR")
|
||||
var duration: float = entry.get("duration", default_transition_duration)
|
||||
|
||||
if WeatherProfile.WeatherState.has(state_name):
|
||||
var state: WeatherProfile.WeatherState = WeatherProfile.WeatherState.get(state_name)
|
||||
set_weather(state, duration)
|
||||
|
||||
_timeline_index += 1
|
||||
|
||||
|
||||
func _request_weather_change(new_state: WeatherProfile.WeatherState) -> void:
|
||||
var profile := get_profile_for_state(new_state)
|
||||
if profile == null:
|
||||
push_warning("WeatherController: No profile for state %s" % WeatherProfile.WeatherState.keys()[new_state])
|
||||
return
|
||||
|
||||
set_weather(new_state, profile.default_transition_duration)
|
||||
|
||||
|
||||
func get_profile_for_state(state: WeatherProfile.WeatherState) -> WeatherProfile:
|
||||
for profile in weather_profiles:
|
||||
if profile.weather_state == state:
|
||||
return profile
|
||||
return null
|
||||
|
||||
|
||||
func set_weather(state: WeatherProfile.WeatherState, transition_duration: float = -1.0) -> void:
|
||||
var profile := get_profile_for_state(state)
|
||||
if profile == null:
|
||||
push_warning("WeatherController: No profile for state %s" % WeatherProfile.WeatherState.keys()[state])
|
||||
return
|
||||
|
||||
if _current_profile:
|
||||
weather_state_exited.emit(_current_profile.weather_state)
|
||||
|
||||
var old_state := current_weather
|
||||
current_weather = state
|
||||
_target_profile = profile
|
||||
|
||||
if transition_duration < 0:
|
||||
transition_duration = profile.default_transition_duration
|
||||
|
||||
_transition_duration = transition_duration
|
||||
_transition_elapsed = 0.0
|
||||
_is_transitioning = true
|
||||
_transition_weight = 0.0
|
||||
|
||||
weather_transition_started.emit(old_state, state)
|
||||
weather_state_changed.emit(old_state, state)
|
||||
|
||||
|
||||
func set_weather_immediate(state: WeatherProfile.WeatherState) -> void:
|
||||
var profile := get_profile_for_state(state)
|
||||
if profile == null:
|
||||
return
|
||||
|
||||
if _current_profile:
|
||||
weather_state_exited.emit(_current_profile.weather_state)
|
||||
|
||||
var old_state := current_weather
|
||||
current_weather = state
|
||||
_current_profile = profile
|
||||
_target_profile = profile
|
||||
_is_transitioning = false
|
||||
_transition_weight = 1.0
|
||||
|
||||
weather_state_changed.emit(old_state, state)
|
||||
weather_state_entered.emit(state)
|
||||
|
||||
|
||||
func get_current_profile() -> WeatherProfile:
|
||||
if _is_transitioning and _current_profile and _target_profile:
|
||||
return _current_profile.lerp_to(_target_profile, _transition_weight)
|
||||
return _current_profile
|
||||
|
||||
|
||||
func get_cloud_density() -> float:
|
||||
var profile := get_current_profile()
|
||||
return profile.cloud_density if profile else 0.2
|
||||
|
||||
|
||||
func get_cloud_opacity() -> float:
|
||||
var profile := get_current_profile()
|
||||
return profile.cloud_opacity if profile else 0.7
|
||||
|
||||
|
||||
func get_cloud_coverage() -> float:
|
||||
var profile := get_current_profile()
|
||||
return profile.cloud_coverage if profile else 0.3
|
||||
|
||||
|
||||
func get_cloud_color() -> Color:
|
||||
var profile := get_current_profile()
|
||||
return profile.cloud_color if profile else Color.WHITE
|
||||
|
||||
|
||||
func get_wind_speed() -> float:
|
||||
var profile := get_current_profile()
|
||||
return profile.wind_speed if profile else 1.0
|
||||
|
||||
|
||||
func get_wind_direction() -> Vector2:
|
||||
var profile := get_current_profile()
|
||||
return profile.wind_direction if profile else Vector2(1.0, 0.0)
|
||||
|
||||
|
||||
func get_rain_intensity() -> float:
|
||||
var profile := get_current_profile()
|
||||
return profile.rain_intensity if profile else 0.0
|
||||
|
||||
|
||||
func get_snow_intensity() -> float:
|
||||
var profile := get_current_profile()
|
||||
return profile.snow_intensity if profile else 0.0
|
||||
|
||||
|
||||
func is_lightning_active() -> bool:
|
||||
var profile := get_current_profile()
|
||||
return profile.lightning_enabled if profile else false
|
||||
|
||||
|
||||
func get_lightning_frequency() -> float:
|
||||
var profile := get_current_profile()
|
||||
return profile.lightning_frequency if profile else 0.0
|
||||
|
||||
|
||||
func get_transition_progress() -> float:
|
||||
return _transition_weight
|
||||
|
||||
|
||||
func is_transitioning() -> bool:
|
||||
return _is_transitioning
|
||||
|
||||
|
||||
func reset_timeline() -> void:
|
||||
_timeline_index = 0
|
||||
_timeline_timer = 0.0
|
||||
1
dynamic-sky/scripts/weather_controller.gd.uid
Normal file
1
dynamic-sky/scripts/weather_controller.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://blyx8v2gq8nlw
|
||||
277
dynamic-sky/scripts/weather_kill_volume.gd
Normal file
277
dynamic-sky/scripts/weather_kill_volume.gd
Normal file
@@ -0,0 +1,277 @@
|
||||
@tool
|
||||
class_name WeatherKillVolume
|
||||
extends Area3D
|
||||
|
||||
enum VolumeShape {
|
||||
BOX,
|
||||
SPHERE,
|
||||
CYLINDER
|
||||
}
|
||||
|
||||
@export_group("Volume Settings")
|
||||
@export var volume_shape: VolumeShape = VolumeShape.BOX:
|
||||
set(value):
|
||||
volume_shape = value
|
||||
_update_collision_shape()
|
||||
|
||||
@export var volume_size: Vector3 = Vector3(10.0, 5.0, 10.0):
|
||||
set(value):
|
||||
volume_size = value
|
||||
_update_collision_shape()
|
||||
|
||||
@export var volume_radius: float = 5.0:
|
||||
set(value):
|
||||
volume_radius = value
|
||||
_update_collision_shape()
|
||||
|
||||
@export_group("Kill Behavior")
|
||||
@export var enabled: bool = true
|
||||
@export var priority: int = 0
|
||||
@export var instant_kill: bool = true
|
||||
@export var fade_out_margin: float = 1.0
|
||||
@export var fade_out_duration: float = 0.3
|
||||
|
||||
@export_group("Particle Types")
|
||||
@export var block_rain: bool = true
|
||||
@export var block_snow: bool = true
|
||||
@export var block_all_precipitation: bool = true
|
||||
|
||||
@export_group("Debug")
|
||||
@export var show_debug_gizmo: bool = true
|
||||
@export var debug_color: Color = Color(0.2, 0.5, 1.0, 0.3)
|
||||
|
||||
var _collision_shape: CollisionShape3D
|
||||
var _particles_inside: Dictionary = {}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_setup_collision()
|
||||
|
||||
body_entered.connect(_on_body_entered)
|
||||
body_exited.connect(_on_body_exited)
|
||||
area_entered.connect(_on_area_entered)
|
||||
area_exited.connect(_on_area_exited)
|
||||
|
||||
|
||||
func _setup_collision() -> void:
|
||||
_collision_shape = CollisionShape3D.new()
|
||||
_collision_shape.name = "KillVolumeShape"
|
||||
add_child(_collision_shape)
|
||||
_update_collision_shape()
|
||||
|
||||
collision_layer = 0
|
||||
collision_mask = 1 << 20
|
||||
monitorable = true
|
||||
monitoring = true
|
||||
|
||||
|
||||
func _update_collision_shape() -> void:
|
||||
if _collision_shape == null:
|
||||
return
|
||||
|
||||
var shape: Shape3D
|
||||
|
||||
match volume_shape:
|
||||
VolumeShape.BOX:
|
||||
var box := BoxShape3D.new()
|
||||
box.size = volume_size
|
||||
shape = box
|
||||
|
||||
VolumeShape.SPHERE:
|
||||
var sphere := SphereShape3D.new()
|
||||
sphere.radius = volume_radius
|
||||
shape = sphere
|
||||
|
||||
VolumeShape.CYLINDER:
|
||||
var cylinder := CylinderShape3D.new()
|
||||
cylinder.radius = volume_radius
|
||||
cylinder.height = volume_size.y
|
||||
shape = cylinder
|
||||
|
||||
_collision_shape.shape = shape
|
||||
|
||||
|
||||
func _on_body_entered(body: Node3D) -> void:
|
||||
if not enabled:
|
||||
return
|
||||
|
||||
if _is_precipitation_particle(body):
|
||||
_handle_particle_enter(body)
|
||||
|
||||
|
||||
func _on_body_exited(body: Node3D) -> void:
|
||||
_handle_particle_exit(body)
|
||||
|
||||
|
||||
func _on_area_entered(area: Area3D) -> void:
|
||||
if not enabled:
|
||||
return
|
||||
|
||||
if area.has_meta("precipitation_particle"):
|
||||
_handle_particle_enter(area)
|
||||
|
||||
|
||||
func _on_area_exited(area: Area3D) -> void:
|
||||
_handle_particle_exit(area)
|
||||
|
||||
|
||||
func _is_precipitation_particle(node: Node3D) -> bool:
|
||||
if node.has_meta("particle_type"):
|
||||
var particle_type: String = node.get_meta("particle_type")
|
||||
|
||||
if block_all_precipitation:
|
||||
return particle_type in ["rain", "snow", "hail", "sleet"]
|
||||
|
||||
if block_rain and particle_type == "rain":
|
||||
return true
|
||||
|
||||
if block_snow and particle_type == "snow":
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func _handle_particle_enter(node: Node3D) -> void:
|
||||
if instant_kill:
|
||||
_kill_particle(node)
|
||||
else:
|
||||
_particles_inside[node.get_instance_id()] = {
|
||||
"node": node,
|
||||
"fade_time": 0.0
|
||||
}
|
||||
|
||||
|
||||
func _handle_particle_exit(node: Node3D) -> void:
|
||||
var id := node.get_instance_id()
|
||||
if _particles_inside.has(id):
|
||||
_particles_inside.erase(id)
|
||||
|
||||
|
||||
func _kill_particle(node: Node3D) -> void:
|
||||
if node.has_method("kill"):
|
||||
node.call("kill")
|
||||
elif node is GPUParticles3D:
|
||||
node.emitting = false
|
||||
elif node is CPUParticles3D:
|
||||
node.emitting = false
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if instant_kill or _particles_inside.is_empty():
|
||||
return
|
||||
|
||||
var to_remove: Array[int] = []
|
||||
|
||||
for id in _particles_inside:
|
||||
var data: Dictionary = _particles_inside[id]
|
||||
var node: Node3D = data["node"]
|
||||
|
||||
if not is_instance_valid(node):
|
||||
to_remove.append(id)
|
||||
continue
|
||||
|
||||
data["fade_time"] += delta
|
||||
var fade_progress: float = data["fade_time"] / fade_out_duration
|
||||
|
||||
if fade_progress >= 1.0:
|
||||
_kill_particle(node)
|
||||
to_remove.append(id)
|
||||
else:
|
||||
_apply_fade(node, 1.0 - fade_progress)
|
||||
|
||||
for id in to_remove:
|
||||
_particles_inside.erase(id)
|
||||
|
||||
|
||||
func _apply_fade(node: Node3D, alpha: float) -> void:
|
||||
if node.has_method("set_alpha"):
|
||||
node.call("set_alpha", alpha)
|
||||
elif node is GPUParticles3D or node is CPUParticles3D:
|
||||
pass
|
||||
|
||||
|
||||
func is_point_inside(point: Vector3) -> bool:
|
||||
var local_point := to_local(point)
|
||||
|
||||
match volume_shape:
|
||||
VolumeShape.BOX:
|
||||
var half_size := volume_size * 0.5
|
||||
return abs(local_point.x) <= half_size.x and \
|
||||
abs(local_point.y) <= half_size.y and \
|
||||
abs(local_point.z) <= half_size.z
|
||||
|
||||
VolumeShape.SPHERE:
|
||||
return local_point.length() <= volume_radius
|
||||
|
||||
VolumeShape.CYLINDER:
|
||||
var horizontal_dist := Vector2(local_point.x, local_point.z).length()
|
||||
var half_height := volume_size.y * 0.5
|
||||
return horizontal_dist <= volume_radius and abs(local_point.y) <= half_height
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func is_point_in_fade_margin(point: Vector3) -> bool:
|
||||
if fade_out_margin <= 0:
|
||||
return false
|
||||
|
||||
var local_point := to_local(point)
|
||||
|
||||
match volume_shape:
|
||||
VolumeShape.BOX:
|
||||
var half_size := volume_size * 0.5
|
||||
var inner_half := half_size - Vector3(fade_out_margin, fade_out_margin, fade_out_margin)
|
||||
|
||||
var in_outer := abs(local_point.x) <= half_size.x and \
|
||||
abs(local_point.y) <= half_size.y and \
|
||||
abs(local_point.z) <= half_size.z
|
||||
|
||||
var in_inner := abs(local_point.x) <= inner_half.x and \
|
||||
abs(local_point.y) <= inner_half.y and \
|
||||
abs(local_point.z) <= inner_half.z
|
||||
|
||||
return in_outer and not in_inner
|
||||
|
||||
VolumeShape.SPHERE:
|
||||
var dist := local_point.length()
|
||||
return dist <= volume_radius and dist >= volume_radius - fade_out_margin
|
||||
|
||||
VolumeShape.CYLINDER:
|
||||
var horizontal_dist := Vector2(local_point.x, local_point.z).length()
|
||||
var half_height := volume_size.y * 0.5
|
||||
|
||||
var in_outer := horizontal_dist <= volume_radius and abs(local_point.y) <= half_height
|
||||
var in_inner := horizontal_dist <= volume_radius - fade_out_margin and \
|
||||
abs(local_point.y) <= half_height - fade_out_margin
|
||||
|
||||
return in_outer and not in_inner
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func get_fade_factor(point: Vector3) -> float:
|
||||
if not is_point_inside(point):
|
||||
return 1.0
|
||||
|
||||
if not is_point_in_fade_margin(point):
|
||||
return 0.0
|
||||
|
||||
var local_point := to_local(point)
|
||||
var min_distance_to_edge := fade_out_margin
|
||||
|
||||
match volume_shape:
|
||||
VolumeShape.BOX:
|
||||
var half_size := volume_size * 0.5
|
||||
min_distance_to_edge = minf(min_distance_to_edge, half_size.x - abs(local_point.x))
|
||||
min_distance_to_edge = minf(min_distance_to_edge, half_size.y - abs(local_point.y))
|
||||
min_distance_to_edge = minf(min_distance_to_edge, half_size.z - abs(local_point.z))
|
||||
|
||||
VolumeShape.SPHERE:
|
||||
min_distance_to_edge = volume_radius - local_point.length()
|
||||
|
||||
VolumeShape.CYLINDER:
|
||||
var horizontal_dist := Vector2(local_point.x, local_point.z).length()
|
||||
var half_height := volume_size.y * 0.5
|
||||
min_distance_to_edge = minf(volume_radius - horizontal_dist, half_height - abs(local_point.y))
|
||||
|
||||
return clampf(min_distance_to_edge / fade_out_margin, 0.0, 1.0)
|
||||
1
dynamic-sky/scripts/weather_kill_volume.gd.uid
Normal file
1
dynamic-sky/scripts/weather_kill_volume.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://burw0wjl85d8a
|
||||
BIN
dynamic-sky/sky_04_2k.png
Normal file
BIN
dynamic-sky/sky_04_2k.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
BIN
dynamic-sky/sky_06_2k.png
Normal file
BIN
dynamic-sky/sky_06_2k.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
dynamic-sky/sky_11_2k.png
Normal file
BIN
dynamic-sky/sky_11_2k.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
dynamic-sky/sky_16_2k.png
Normal file
BIN
dynamic-sky/sky_16_2k.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
BIN
dynamic-sky/sky_25_2k.png
Normal file
BIN
dynamic-sky/sky_25_2k.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 966 KiB |
BIN
dynamic-sky/sky_44_2k.png
Normal file
BIN
dynamic-sky/sky_44_2k.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 862 KiB |
Reference in New Issue
Block a user