extends Node3D
## Test scene: Sky3DExtended (atmosphere/sun/moon/stars) + Nubis hero cloud volumes.
## Sky3D Extended provides the atmosphere — nubis_clouds adds volumetric clouds via CompositorEffect.
##
## Controls:
## T / Shift+T — advance / rewind time by 1 hour
## F — fast forward (1min cycle)
## G — normal speed (15min cycle)
## P — pause / resume time
## F1 — cycle cloud coverage
## F12 — screenshot
var _sky3d: Sky3DExtended
func _ready() -> void:
_sky3d = $Sky3DExtended as Sky3DExtended
# Sky3DExtended keeps its own atmosphere shader — no override needed.
# Set up NubisCompositor first, THEN add CloudVolumes
# (CloudVolumes search for NubisCompositor in _ready)
call_deferred("_setup_clouds")
if _sky3d:
if _sky3d.sky:
_sky3d.sky.moon_phase = 0.5
_sky3d.sky.moon_light_energy = 1.5
print("[Sky3D+Nubis] Time: %.1f (%s) | Moon: full" % [
_sky3d.current_time, "day" if _sky3d.is_day() else "night"])
print("[Sky3D+Nubis] T/Shift+T: ±1hr, F: fast, G: slow, P: pause, F1: coverage, F12: screenshot")
func _setup_clouds() -> void:
var we := $Sky3DExtended as WorldEnvironment
if not we:
push_error("[Sky3D+Nubis] Sky3DExtended not found")
return
# Ground cloud shadows
var pw_tex = load("res://addons/nubis_clouds/textures/perlworlnoise.png")
var weather_tex = load("res://addons/nubis_clouds/textures/weather.webp")
var shadow_shader := load("res://addons/nubis_clouds/shaders/cloud_shadow_ground.gdshader") as Shader
if shadow_shader and pw_tex and weather_tex:
var ground_mat := ShaderMaterial.new()
ground_mat.shader = shadow_shader
ground_mat.set_shader_parameter("perlworlnoise", pw_tex)
ground_mat.set_shader_parameter("weather_map", weather_tex)
ground_mat.set_shader_parameter("cloud_coverage", 0.35)
ground_mat.set_shader_parameter("shadow_strength", 0.5)
var ground := get_node_or_null("Ground") as MeshInstance3D
if ground:
ground.material_override = ground_mat
var compositor := Compositor.new()
var nubis_comp := NubisCompositor.new()
nubis_comp.density_scale = 80.0
nubis_comp.noise_freq = 0.00005
nubis_comp.erosion_strength = 0.9
compositor.compositor_effects = [nubis_comp]
we.compositor = compositor
# Schneider SDF — same as addon's test_cloud_hero.gd
var shared := _make_schneider_sdf(32, 42, 0.5)
var configs := [
{"pos": Vector3(0, 300, -800), "size": Vector3(1500, 800, 1500), "density": 80.0},
{"pos": Vector3(1000, 350, -400), "size": Vector3(1200, 600, 1200), "density": 75.0},
{"pos": Vector3(-900, 280, 500), "size": Vector3(1400, 700, 1300), "density": 85.0},
{"pos": Vector3(600, 250, 900), "size": Vector3(1800, 400, 1600), "density": 70.0},
{"pos": Vector3(-1200, 300, -600), "size": Vector3(1600, 500, 1400), "density": 75.0},
{"pos": Vector3(2000, 400, -1500), "size": Vector3(1000, 900, 1000), "density": 90.0},
{"pos": Vector3(-1800, 350, -1800), "size": Vector3(1200, 700, 1100), "density": 80.0},
{"pos": Vector3(0, 320, 2000), "size": Vector3(1500, 600, 1500), "density": 70.0},
{"pos": Vector3(500, 150, -200), "size": Vector3(2000, 200, 2000), "density": 60.0},
{"pos": Vector3(-800, 180, 1200), "size": Vector3(1800, 250, 1800), "density": 55.0},
]
for cfg in configs:
var c := CloudVolume.new()
c.shared_sdf = shared
c.position = cfg["pos"]
c.cloud_size = cfg["size"]
c.density_scale = cfg["density"]
c.wind_speed = 0.02
add_child(c)
print("[Sky3D+Nubis] Sky3DExtended atmosphere + %d Nubis hero volumes active" % configs.size())
func _make_schneider_sdf(res: int, seed_val: int, coverage: float) -> ImageTexture3D:
var perlin := FastNoiseLite.new()
perlin.noise_type = FastNoiseLite.TYPE_PERLIN
perlin.frequency = 0.08
perlin.fractal_octaves = 4
perlin.fractal_gain = 0.5
perlin.seed = seed_val
var worley := FastNoiseLite.new()
worley.noise_type = FastNoiseLite.TYPE_CELLULAR
worley.cellular_return_type = FastNoiseLite.RETURN_DISTANCE
worley.frequency = 0.12
worley.fractal_octaves = 3
worley.seed = seed_val + 10
var images: Array[Image] = []
for z in res:
var img := Image.create(res, res, false, Image.FORMAT_RF)
for y in res:
for x in res:
var uvw := Vector3(float(x) / float(res), 1.0 - float(y) / float(res), float(z) / float(res))
var sp := Vector3(float(x), float(y), float(z))
var p_val := perlin.get_noise_3d(sp.x, sp.y, sp.z) * 0.5 + 0.5
var w_val := 1.0 - (worley.get_noise_3d(sp.x, sp.y, sp.z) * 0.5 + 0.5)
var lo := -(1.0 - w_val)
var base_cloud := clampf((p_val - lo) / (1.0 - lo), 0.0, 1.0)
var hf := uvw.y
var gradient := smoothstep(0.01, 0.15, hf) * (1.0 - smoothstep(0.6, 1.0, hf))
base_cloud *= gradient
var threshold := 1.0 - coverage
var shaped := clampf((base_cloud - threshold) / maxf(1.0 - threshold, 0.001), 0.0, 1.0)
shaped *= smoothstep(threshold - 0.05, threshold + 0.05, base_cloud)
var ex := smoothstep(0.0, 0.08, uvw.x) * smoothstep(0.0, 0.08, 1.0 - uvw.x)
var ey := smoothstep(0.0, 0.08, 1.0 - uvw.y)
var ez := smoothstep(0.0, 0.08, uvw.z) * smoothstep(0.0, 0.08, 1.0 - uvw.z)
shaped *= ex * ey * ez
var sdf := pow(clampf(shaped, 0.0, 1.0), (1.0 - hf) * 0.8 + 0.5)
img.set_pixel(x, y, Color(sdf, 0, 0, 1))
images.append(img)
var tex := ImageTexture3D.new()
tex.create(Image.FORMAT_RF, res, res, res, false, images)
return tex
func smoothstep(edge0: float, edge1: float, x: float) -> float:
var t := clampf((x - edge0) / (edge1 - edge0), 0.0, 1.0)
return t * t * (3.0 - 2.0 * t)
func _unhandled_input(event: InputEvent) -> void:
if not event is InputEventKey or not event.pressed:
return
match event.keycode:
KEY_T:
if _sky3d:
var delta := -1.0 if event.shift_pressed else 1.0
_sky3d.current_time = fmod(_sky3d.current_time + delta + 24.0, 24.0)
print("[Sky3D+Nubis] Time: %.1f (%s)" % [
_sky3d.current_time, "day" if _sky3d.is_day() else "night"])
KEY_F:
if _sky3d:
_sky3d.minutes_per_day = 1.0
print("[Sky3D+Nubis] Fast forward (1min cycle)")
KEY_G:
if _sky3d:
_sky3d.minutes_per_day = 15.0
print("[Sky3D+Nubis] Normal speed (15min cycle)")
KEY_P:
if _sky3d:
if _sky3d.game_time_enabled:
_sky3d.pause()
print("[Sky3D+Nubis] Paused at %.1f" % _sky3d.current_time)
else:
_sky3d.resume()
print("[Sky3D+Nubis] Resumed")
KEY_F12:
var img := get_viewport().get_texture().get_image()
var ts := Time.get_datetime_string_from_system().replace(":", "-")
var dir := "/home/fleeky/Desktop/hyper-tps"
DirAccess.make_dir_recursive_absolute(dir)
var path := "%s/sky3d_nubis_%s.png" % [dir, ts]
img.save_png(path)
print("[Sky3D+Nubis] Screenshot: %s" % path)