sky3d
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
class_name OrbitalElements
|
||||
|
||||
# Handles Orbital elements for planetary system
|
||||
|
||||
var N: float # Longitude of the ascending node.
|
||||
var i: float # The Inclination to the ecliptic.
|
||||
var w: float # Argument of perihelion.
|
||||
var a: float # Semi-major axis, or mean distance from sun.
|
||||
var e: float # Eccentricity.
|
||||
var M: float # Mean anomaly.
|
||||
|
||||
|
||||
func get_orbital_elements(index: int, timeScale: float) -> void:
|
||||
if index == 0: # Sun
|
||||
N = 0.0
|
||||
i = 0.0
|
||||
w = 282.9404 + 4.70935e-5 * timeScale
|
||||
a = 0.0
|
||||
e = 0.016709 - 1.151e-9 * timeScale
|
||||
M = 356.0470 + 0.9856002585 * timeScale
|
||||
else: # Moon
|
||||
N = 125.1228 - 0.0529538083 * timeScale
|
||||
i = 5.1454
|
||||
w = 318.0634 + 0.1643573223 * timeScale
|
||||
a = 60.2666
|
||||
e = 0.054900
|
||||
M = 115.3654 + 13.0649929509 * timeScale
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://4ka6iw74pja3
|
||||
@@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
const __sky3d_script: String = "res://addons/sky_3d/src/Sky3D.gd"
|
||||
const __sky3d_icon: String = "res://addons/sky_3d/assets/textures/SkyIcon.png"
|
||||
const __skydome_script: String = "res://addons/sky_3d/src/SkyDome.gd"
|
||||
const __skydome_icon: String = "res://addons/sky_3d/assets/textures/SkyIcon.png"
|
||||
const __time_of_day_script: String = "res://addons/sky_3d/src/TimeOfDay.gd"
|
||||
const __time_of_day_icon: String = "res://addons/sky_3d/assets/textures/SkyIcon.png"
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
add_custom_type("Sky3D", "Node", load(__sky3d_script), load(__sky3d_icon))
|
||||
add_custom_type("SkyDome", "Node", load(__skydome_script), load(__skydome_icon))
|
||||
add_custom_type("TimeOfDay", "Node", load(__time_of_day_script), load(__time_of_day_icon))
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
remove_custom_type("Sky3D")
|
||||
remove_custom_type("SkyDome")
|
||||
remove_custom_type("TimeOfDay")
|
||||
@@ -0,0 +1 @@
|
||||
uid://ri5mkwojuh33
|
||||
@@ -0,0 +1,50 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
class_name ScatterLib
|
||||
|
||||
# Atmospheric Scattering Lib.
|
||||
# References:
|
||||
# - Preetham and Hoffman Paper:
|
||||
# See: https://developer.amd.com/wordpress/media/2012/10/ATI-LightScattering.pdf
|
||||
|
||||
const n: float = 1.0003 # Index of the air refraction
|
||||
const n2: float = 1.00060009 # Index of the air refraction ˆ 2
|
||||
const N: float = 2.545e25 # Molecular Density
|
||||
const pn: float = 0.035 # Depolatization factor for standard air.
|
||||
|
||||
|
||||
static func compute_wavelenghts_lambda(value: Vector3) -> Vector3:
|
||||
return value * 1e-9
|
||||
|
||||
|
||||
static func compute_wavelenghts(lambda: Vector3) -> Vector3:
|
||||
var k = 4.0
|
||||
var ret: Vector3 = lambda
|
||||
ret.x = pow(lambda.x, k)
|
||||
ret.y = pow(lambda.y, k)
|
||||
ret.z = pow(lambda.z, k)
|
||||
return ret
|
||||
|
||||
|
||||
static func compute_beta_ray(wavelenghts: Vector3) -> Vector3:
|
||||
var kr: float = (8.0 * pow(PI, 3.0) * pow(n2 - 1.0, 2.0) * (6.0 + 3.0 * pn))
|
||||
var ret: Vector3 = 3.0 * N * wavelenghts * (6.0 - 7.0 * pn)
|
||||
ret.x = kr / ret.x
|
||||
ret.y = kr / ret.y
|
||||
ret.z = kr / ret.z
|
||||
return ret
|
||||
|
||||
|
||||
static func compute_beta_mie(mie: float, turbidity: float) -> Vector3:
|
||||
var k: float = 434e-6
|
||||
return Vector3.ONE * mie * turbidity * k
|
||||
|
||||
|
||||
static func get_partial_mie_phase(g: float) -> Vector3:
|
||||
var g2: float = g * g
|
||||
var ret: Vector3
|
||||
ret.x = 1.0 - g2
|
||||
ret.y = 1.0 + g2
|
||||
ret.z = 2.0 * g
|
||||
return ret
|
||||
@@ -0,0 +1 @@
|
||||
uid://dc70wk0t64fwe
|
||||
@@ -0,0 +1,549 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
## Sky3D is an Atmosphereic Day/Night Cycle for Godot 4.
|
||||
##
|
||||
## This plugin manages time, moving the sun, moon, and stars, and consolidates environmental lighting controls.
|
||||
## To use it, remove any WorldEnvironment node from you scene, then add a new Sky3D node.
|
||||
## Explore and configure the settings in the Sky3D, SunLight, MoonLight, [SkyDome], and [TimeOfDay] nodes.
|
||||
|
||||
@tool
|
||||
class_name Sky3D
|
||||
extends WorldEnvironment
|
||||
|
||||
## Emitted when the environment has changed to a new resource.
|
||||
signal environment_changed
|
||||
|
||||
const SKY_SHADER: String = "res://addons/sky_3d/shaders/SkyMaterial.gdshader"
|
||||
|
||||
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
|
||||
var version: String = "2.1"
|
||||
|
||||
## The Sun DirectionalLight.
|
||||
var sun: DirectionalLight3D
|
||||
## The Moon DirectionalLight.
|
||||
var moon: DirectionalLight3D
|
||||
## The TimeOfDay node.
|
||||
var tod: TimeOfDay
|
||||
## The SkyDome node.
|
||||
var sky: SkyDome
|
||||
## The Sky shader.
|
||||
var sky_material: ShaderMaterial
|
||||
|
||||
## Enables all rendering and time tracking.
|
||||
@export var sky3d_enabled: bool = true :
|
||||
set(value):
|
||||
sky3d_enabled = value
|
||||
if value:
|
||||
show_sky()
|
||||
resume()
|
||||
else:
|
||||
hide_sky()
|
||||
pause()
|
||||
|
||||
|
||||
#####################
|
||||
## Visibility
|
||||
#####################
|
||||
|
||||
@export_group("Visibility")
|
||||
|
||||
|
||||
## Enables the sky shader. Disable sky, lights, fog for a black sky or call [method hide_sky].
|
||||
@export var sky_enabled: bool = true :
|
||||
set(value):
|
||||
sky_enabled = value
|
||||
if sky and sky_material:
|
||||
sky_material.set_shader_parameter("sky_visible", value)
|
||||
sky.cumulus_visible = clouds_enabled and value
|
||||
sky.cirrus_visible = clouds_enabled and value
|
||||
|
||||
|
||||
## Enables both 2D and cumulus cloud layers.
|
||||
@export var clouds_enabled: bool = true :
|
||||
set(value):
|
||||
clouds_enabled = value
|
||||
if sky:
|
||||
sky.cumulus_visible = value
|
||||
sky.cirrus_visible = value
|
||||
|
||||
|
||||
## Enables the Sun and Moon [DirectionalLight3D]s.
|
||||
@export var lights_enabled: bool = true :
|
||||
set(value):
|
||||
lights_enabled = value
|
||||
if sky:
|
||||
sky.sun_light_enabled = value
|
||||
sky.moon_light_enabled = value
|
||||
|
||||
|
||||
## Enables the screen space fog shader. Sky3D also works with the other two fog methods built into Godot.
|
||||
@export var fog_enabled: bool = true :
|
||||
set(value):
|
||||
fog_enabled = value
|
||||
if sky:
|
||||
sky.fog_visible = value
|
||||
|
||||
|
||||
## Disables rendering of sky, fog, and lights.
|
||||
func hide_sky() -> void:
|
||||
sky_enabled = false
|
||||
lights_enabled = false
|
||||
fog_enabled = false
|
||||
clouds_enabled = false
|
||||
|
||||
|
||||
## Enables rendering of sky, fog, and lights.
|
||||
func show_sky() -> void:
|
||||
sky_enabled = true
|
||||
lights_enabled = true
|
||||
fog_enabled = true
|
||||
clouds_enabled = true
|
||||
|
||||
|
||||
#####################
|
||||
## Time
|
||||
#####################
|
||||
|
||||
@export_group("Time")
|
||||
|
||||
|
||||
## Allows time to progress in the editor. Alias for [member TimeOfDay.editor_time_enabled].
|
||||
@export var editor_time_enabled: bool = true :
|
||||
set(value):
|
||||
if tod:
|
||||
tod.editor_time_enabled = value
|
||||
get:
|
||||
return tod.editor_time_enabled if tod else editor_time_enabled
|
||||
|
||||
|
||||
## Allows time to progress in game. Alias for [member TimeOfDay.game_time_enabled].
|
||||
@export var game_time_enabled: bool = true :
|
||||
set(value):
|
||||
if tod:
|
||||
tod.game_time_enabled = value
|
||||
get:
|
||||
return tod.game_time_enabled if tod else game_time_enabled
|
||||
|
||||
|
||||
## A readable game date string, eg. '2025-01-01'. Alias for [member TimeOfDay.game_date].
|
||||
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
|
||||
var game_date: String = "" :
|
||||
get:
|
||||
return tod.game_date if tod else game_date
|
||||
|
||||
|
||||
## A readable game time string, e.g. '08:00:00'. Alias for [member TimeOfDay.game_time].
|
||||
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
|
||||
var game_time: String = "" :
|
||||
get:
|
||||
return tod.game_time if tod else game_time
|
||||
|
||||
|
||||
## The current in-game time in hours from 0.0 to 23.99. Smaller or larger values than the range will wrap.
|
||||
## Alias for [member TimeOfDay.current_time].
|
||||
@export_range(0.0, 23.9998, 0.01) var current_time: float = 8.0 :
|
||||
set(value):
|
||||
if tod:
|
||||
tod.current_time = value
|
||||
get:
|
||||
return tod.current_time if tod else current_time
|
||||
|
||||
|
||||
## The total length of time for a complete day and night cycle in real world minutes. Setting this to
|
||||
## [param 15] means a full in-game day takes 15 real-world minutes. [member game_time_enabled] must be
|
||||
## enabled for this to work. Negative values moves time backwards. The Witcher 3 uses a 96 minute cycle.
|
||||
## Adjust [member update_interval] to match. Shorter days needs more updates. Longer days need less.
|
||||
## Alias for [member TimeOfDay.minutes_per_day].
|
||||
@export_range(-1440, 1440, 0.1) var minutes_per_day: float = 15.0 :
|
||||
set(value):
|
||||
if tod:
|
||||
tod.minutes_per_day = value
|
||||
get:
|
||||
return tod.minutes_per_day if tod else minutes_per_day
|
||||
|
||||
|
||||
## Frequency of sky updates, per second. The smaller the number, the more frequent the updates and
|
||||
## the smoother the animation. Set to [param 0.016] for 60fps, for example.[br][br]
|
||||
## [b]Note:[/b] Setting this value too small may cause unwanted behavior. See [member Timer.wait_time].
|
||||
## Alias for [member TimeOfDay.update_interval].
|
||||
@export_range(0.016, 10.0) var update_interval: float = 0.016 :
|
||||
set(value):
|
||||
if tod:
|
||||
tod.update_interval = value
|
||||
get:
|
||||
return tod.update_interval if tod else update_interval
|
||||
|
||||
|
||||
## Returns true if the sun is above the horizon.
|
||||
func is_day() -> bool:
|
||||
return sky and sky.is_day()
|
||||
|
||||
|
||||
## Returns true if the sun is below the horizon.
|
||||
func is_night() -> bool:
|
||||
return sky and not sky.is_day()
|
||||
|
||||
|
||||
## Pauses time calculation. Alias for [method TimeOfDay.pause].
|
||||
func pause() -> void:
|
||||
if tod:
|
||||
tod.pause()
|
||||
|
||||
|
||||
## Resumes time calculation. Alias for [method TimeOfDay.resume].
|
||||
func resume() -> void:
|
||||
if tod:
|
||||
tod.resume()
|
||||
|
||||
|
||||
var _contrib_tween: Tween
|
||||
|
||||
# Adjusts sky contribution if transitioning to day or night.
|
||||
func _start_sky_contrib_tween(daytime: bool = is_day()) -> void:
|
||||
if not (sky and environment and is_inside_tree()):
|
||||
return
|
||||
|
||||
if _contrib_tween:
|
||||
_contrib_tween.kill()
|
||||
_contrib_tween = get_tree().create_tween()
|
||||
_contrib_tween.set_parallel(true)
|
||||
|
||||
if daytime:
|
||||
_contrib_tween.tween_property(environment, "ambient_light_sky_contribution", sky_contribution, contribution_tween_time)
|
||||
else:
|
||||
var night_contrib: float = minf(night_sky_contribution, sky_contribution) if night_ambient_boost else sky_contribution
|
||||
_contrib_tween.tween_property(environment, "ambient_light_sky_contribution", night_contrib, contribution_tween_time)
|
||||
|
||||
|
||||
#####################
|
||||
## Lighting
|
||||
#####################
|
||||
|
||||
@export_group("Lighting")
|
||||
|
||||
|
||||
## Light intensity scaled before the tonemapper. Softer highlights.
|
||||
## Alias for [member Environment.camera_attributes].
|
||||
## Connect this same resource to your [member Camera3D.attributes].
|
||||
@export_range(0, 16, 0.005) var camera_exposure: float = 1.0 :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.exposure_multiplier = value
|
||||
get:
|
||||
return camera_attributes.exposure_multiplier if camera_attributes else camera_exposure
|
||||
|
||||
|
||||
## Light intensity scaled in post processing. Hotter highlights.
|
||||
## Alias for [member Environment.tonemap_exposure].
|
||||
## Connect this same resource to your [member Camera3D.environment].
|
||||
@export_range(0, 16, 0.005) var tonemap_exposure: float = 1.0 :
|
||||
set(value):
|
||||
if environment:
|
||||
environment.tonemap_exposure = value
|
||||
get:
|
||||
return environment.tonemap_exposure if environment else tonemap_exposure
|
||||
|
||||
|
||||
## Light energy coming from the sky shader. Alias for [member SkyDome.exposure].
|
||||
@export_range(0, 16, 0.005) var skydome_energy: float = 1.0 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.exposure = value
|
||||
get:
|
||||
return sky.exposure if sky else skydome_energy
|
||||
|
||||
|
||||
## Brightness of and light energy coming from the clouds. Alias for [member SkyDome.cumulus_intensity].
|
||||
@export_range(0, 16, 0.005) var cloud_intensity: float = 0.6 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.cumulus_intensity = value
|
||||
get:
|
||||
return sky.cumulus_intensity if sky else cloud_intensity
|
||||
|
||||
|
||||
## Maximum brightness of the Sun DirectionalLight, visible during the day.
|
||||
## Alias for [member SkyDome.sun_light_energy].
|
||||
@export_range(0, 16, 0.005) var sun_energy: float = 1.0 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.sun_light_energy = value
|
||||
get:
|
||||
return sky.sun_light_energy if sky else sun_energy
|
||||
|
||||
|
||||
## Opacity of Sun DirectionalLight shadow. Alias for [member DirectionalLight3D.shadow_opacity].
|
||||
@export_range(0, 1, 0.005) var sun_shadow_opacity: float = 1.0 :
|
||||
set(value):
|
||||
if sun:
|
||||
sun.shadow_opacity = value
|
||||
get:
|
||||
return sun.shadow_opacity if sun else sun_shadow_opacity
|
||||
|
||||
|
||||
## Ratio of ambient light to sky light. Works when there are no Reflection Probes or GI.
|
||||
## Sets the target for [member Environment.ambient_light_sky_contribution], which may change at night
|
||||
## depending on [member night_ambient_boost] and [member night_sky_contribution].
|
||||
@export_range(0, 1, 0.005) var sky_contribution: float = 1.0 :
|
||||
set(value):
|
||||
if environment:
|
||||
sky_contribution = value
|
||||
environment.ambient_light_sky_contribution = value
|
||||
_start_sky_contrib_tween()
|
||||
|
||||
|
||||
## Strength of ambient light. Works when there are no Reflection Probes or GI, and
|
||||
## [member sky_contribution] < 1. Alias for [member Environment.ambient_light_energy].
|
||||
@export_range(0, 16, 0.005) var ambient_energy: float = 1.0 :
|
||||
set(value):
|
||||
environment.ambient_light_energy = value
|
||||
_start_sky_contrib_tween()
|
||||
get:
|
||||
return environment.ambient_light_energy if environment else ambient_energy
|
||||
|
||||
|
||||
@export_subgroup("Night")
|
||||
|
||||
|
||||
## Maximum strength of Moon DirectionalLight, visible at night. Alias for [member SkyDome.moon_light_energy].
|
||||
@export_range(0, 16, 0.005) var moon_energy: float = 0.3 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.moon_light_energy = value
|
||||
get:
|
||||
return sky.moon_light_energy if sky else moon_energy
|
||||
|
||||
|
||||
## Opacity of Moon DirectionalLight shadow. Alias for [member DirectionalLight3D.shadow_opacity].
|
||||
@export_range(0, 1, 0.005) var moon_shadow_opacity: float = 1.0 :
|
||||
set(value):
|
||||
if moon:
|
||||
moon.shadow_opacity = value
|
||||
get:
|
||||
return moon.shadow_opacity if moon else moon_shadow_opacity
|
||||
|
||||
|
||||
## Enables a lower sky_contribution at night, which allows more ambient energy to show.
|
||||
## To use, ensure there are no ReflectionProbes or GI. Set [member ambient_energy] > 0.
|
||||
## Set [member night_sky_contribution] < [member sky_contribution].
|
||||
## Then at night, [member Environment.ambient_light_sky_contribution] will be set lower, which
|
||||
## will show more [member ambient_energy].
|
||||
@export var night_ambient_boost: bool = true :
|
||||
set(value):
|
||||
night_ambient_boost = value
|
||||
_start_sky_contrib_tween()
|
||||
|
||||
|
||||
## Sets [member Environment.ambient_light_sky_contribution] at night if [member night_ambient_boost] is enabled.
|
||||
## See [member night_ambient_boost] and [member sky_contribution].
|
||||
@export_range(0, 1, 0.005) var night_sky_contribution: float = 0.7 :
|
||||
set(value):
|
||||
night_sky_contribution = value
|
||||
if night_ambient_boost:
|
||||
_start_sky_contrib_tween()
|
||||
|
||||
|
||||
## Transition time for changing sky contribution when shifting between day and night.
|
||||
@export_range(0, 30, 0.05) var contribution_tween_time: float = 3.0
|
||||
|
||||
|
||||
@export_subgroup("Auto Exposure")
|
||||
|
||||
|
||||
## Alias for [member CameraAttributes.auto_exposure_enabled].
|
||||
@export var auto_exposure: bool = false :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.auto_exposure_enabled = value
|
||||
get:
|
||||
return camera_attributes.auto_exposure_enabled if camera_attributes else auto_exposure
|
||||
|
||||
|
||||
## Alias for [member CameraAttributes.auto_exposure_scale].
|
||||
@export_range(0.01, 16, 0.005) var auto_exposure_scale: float = 0.4 :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.auto_exposure_scale = value
|
||||
get:
|
||||
return camera_attributes.auto_exposure_scale if camera_attributes else auto_exposure_scale
|
||||
|
||||
|
||||
## Alias for [member CameraAttributesPractical.auto_exposure_min_sensitivity].
|
||||
@export_range(0, 1600, 0.5) var auto_exposure_min: float = 0.0 :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.auto_exposure_min_sensitivity = value
|
||||
if value > auto_exposure_max:
|
||||
auto_exposure_max = value
|
||||
get:
|
||||
return camera_attributes.auto_exposure_min_sensitivity if camera_attributes else auto_exposure_min
|
||||
|
||||
|
||||
## Alias for [member CameraAttributesPractical.auto_exposure_max_sensitivity].
|
||||
@export_range(30, 64000, 0.5) var auto_exposure_max: float = 800.0 :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.auto_exposure_max_sensitivity = value
|
||||
if value < auto_exposure_min:
|
||||
auto_exposure_min = value
|
||||
get:
|
||||
return camera_attributes.auto_exposure_max_sensitivity if camera_attributes else auto_exposure_max
|
||||
|
||||
|
||||
## Alias for [member CameraAttributes.auto_exposure_speed].
|
||||
@export_range(0.1, 64, 0.1) var auto_exposure_speed: float = 0.5 :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.auto_exposure_speed = value
|
||||
get:
|
||||
return camera_attributes.auto_exposure_speed if camera_attributes else auto_exposure_speed
|
||||
|
||||
|
||||
#####################
|
||||
## Weather
|
||||
#####################
|
||||
|
||||
@export_group("Weather")
|
||||
|
||||
## Sets the wind speed. Alias for [member SkyDome.wind_speed].
|
||||
@export_custom(PROPERTY_HINT_RANGE, "0,120,0.1,or_greater,or_less,suffix:m/s") var wind_speed: float = 1.0 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.wind_speed = value
|
||||
get:
|
||||
return sky.wind_speed if sky else wind_speed
|
||||
|
||||
## Sets the wind direction. Zero means the wind is coming from the north, 90 from the east,
|
||||
## 180 from the south and 270 (or -90) from the west. Alias for [member SkyDome.wind_direction].
|
||||
@export_custom(PROPERTY_HINT_RANGE, "-180,180,0.1,radians_as_degrees") var wind_direction: float = 0.0 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.wind_direction = value
|
||||
get:
|
||||
return sky.wind_direction if sky else wind_direction
|
||||
|
||||
|
||||
#####################
|
||||
## Overlays
|
||||
#####################
|
||||
|
||||
@export_group("Overlays")
|
||||
|
||||
|
||||
## Overlays a grid aligned to the horizon and the sky zenith.
|
||||
## Change color in SkyDome. Alias for [member SkyDome.show_azimuthal_grid].
|
||||
@export var show_azimuthal_grid: bool = false :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.show_azimuthal_grid = value
|
||||
get:
|
||||
return sky.show_azimuthal_grid if sky else show_azimuthal_grid
|
||||
|
||||
|
||||
## Overlays a grid aligned to the celestial equator and the north celestial pole (near Polaris).
|
||||
## Change color in SkyDome. Alias for [member SkyDome.show_equatorial_grid].
|
||||
@export var show_equatorial_grid: bool = false :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.show_equatorial_grid = value
|
||||
get:
|
||||
return sky.show_equatorial_grid if sky else show_equatorial_grid
|
||||
|
||||
|
||||
#####################
|
||||
## Setup
|
||||
#####################
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
# Must be after _init and before _enter_tree to properly set vars like 'sky' for setters
|
||||
if what in [ NOTIFICATION_SCENE_INSTANTIATED, NOTIFICATION_ENTER_TREE ]:
|
||||
_initialize()
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
# Create default environment
|
||||
if environment == null:
|
||||
environment = Environment.new()
|
||||
environment.background_mode = Environment.BG_SKY
|
||||
environment.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
|
||||
environment.ambient_light_sky_contribution = 0.7
|
||||
environment.ambient_light_energy = 1.0
|
||||
environment.reflected_light_source = Environment.REFLECTION_SOURCE_SKY
|
||||
environment.tonemap_mode = Environment.TONE_MAPPER_ACES
|
||||
environment.tonemap_white = 6
|
||||
emit_signal("environment_changed", environment)
|
||||
|
||||
# Setup Sky material & Upgrade old
|
||||
if environment.sky == null or environment.sky.sky_material is PhysicalSkyMaterial:
|
||||
environment.sky = Sky.new()
|
||||
environment.sky.sky_material = ShaderMaterial.new()
|
||||
environment.sky.sky_material.shader = load(SKY_SHADER)
|
||||
|
||||
# Set a reference to the sky material for easy access.
|
||||
sky_material = environment.sky.sky_material
|
||||
|
||||
# Create default camera attributes
|
||||
if camera_attributes == null:
|
||||
camera_attributes = CameraAttributesPractical.new()
|
||||
|
||||
# Assign children nodes
|
||||
|
||||
if has_node("SunLight"):
|
||||
sun = $SunLight
|
||||
elif is_inside_tree():
|
||||
sun = DirectionalLight3D.new()
|
||||
sun.name = "SunLight"
|
||||
add_child(sun, true)
|
||||
sun.owner = get_tree().edited_scene_root
|
||||
sun.shadow_enabled = true
|
||||
|
||||
if has_node("MoonLight"):
|
||||
moon = $MoonLight
|
||||
elif is_inside_tree():
|
||||
moon = DirectionalLight3D.new()
|
||||
moon.name = "MoonLight"
|
||||
add_child(moon, true)
|
||||
moon.owner = get_tree().edited_scene_root
|
||||
moon.shadow_enabled = true
|
||||
|
||||
# DEPRECATED - Remove 2.2
|
||||
if has_node("Skydome"):
|
||||
$Skydome.name = "SkyDome"
|
||||
if has_node("SkyDome"):
|
||||
sky = $SkyDome
|
||||
sky.environment = environment
|
||||
elif is_inside_tree():
|
||||
sky = SkyDome.new()
|
||||
sky.name = "SkyDome"
|
||||
add_child(sky, true)
|
||||
sky.owner = get_tree().edited_scene_root
|
||||
sky.sun_light_path = "../SunLight"
|
||||
sky.moon_light_path = "../MoonLight"
|
||||
sky.environment = environment
|
||||
|
||||
if has_node("TimeOfDay"):
|
||||
tod = $TimeOfDay
|
||||
elif is_inside_tree():
|
||||
tod = TimeOfDay.new()
|
||||
tod.name = "TimeOfDay"
|
||||
add_child(tod, true)
|
||||
tod.owner = get_tree().edited_scene_root
|
||||
tod.dome_path = "../SkyDome"
|
||||
if sky and not sky.day_night_changed.is_connected(_start_sky_contrib_tween):
|
||||
sky.day_night_changed.connect(_start_sky_contrib_tween)
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
_start_sky_contrib_tween()
|
||||
|
||||
|
||||
func _set(property: StringName, value: Variant) -> bool:
|
||||
match property:
|
||||
"environment":
|
||||
sky.environment = value
|
||||
environment = value
|
||||
emit_signal("environment_changed", environment)
|
||||
return true
|
||||
return false
|
||||
@@ -0,0 +1 @@
|
||||
uid://deo0gxkmk2ahd
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
uid://b4tkplvh7fq40
|
||||
@@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
class_name TOD_Math
|
||||
|
||||
|
||||
static func rev(val: float) -> float:
|
||||
return val - int(floor(val / 360.0)) * 360.0
|
||||
|
||||
|
||||
static func spherical_to_cartesian(theta: float, azimuth: float, radius: float = 1.0) -> Vector3:
|
||||
var ret: Vector3
|
||||
var sinTheta: float = sin(theta)
|
||||
ret.x = sinTheta * sin(azimuth)
|
||||
ret.y = cos(theta)
|
||||
ret.z = sinTheta * cos(azimuth)
|
||||
return ret * radius
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://couf46uxtsrhg
|
||||
@@ -0,0 +1,593 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
## TimeOfDay is a component of [Sky3D].
|
||||
##
|
||||
## This class tracks the progress of time, as well as the planetary calculations. See [Sky3D].
|
||||
|
||||
@tool
|
||||
class_name TimeOfDay
|
||||
extends Node
|
||||
|
||||
|
||||
signal time_changed(value)
|
||||
signal minute_changed(value)
|
||||
signal hour_changed(value)
|
||||
signal day_changed(value)
|
||||
signal month_changed(value)
|
||||
signal year_changed(value)
|
||||
|
||||
var _previous_minute: int = -1
|
||||
var _previous_hour: int = -1
|
||||
|
||||
const HOURS_PER_DAY: int = 24
|
||||
const RADIANS_PER_HOUR: float = PI / 12.0
|
||||
const HALFPI: float = PI / 2.0
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
dome_path = dome_path
|
||||
|
||||
_previous_minute = floori(fmod(current_time, 1.0) * 60.0)
|
||||
_previous_hour = floori(current_time)
|
||||
|
||||
_update_timer = Timer.new()
|
||||
_update_timer.name = "Timer"
|
||||
add_child(_update_timer)
|
||||
_update_timer.timeout.connect(_on_timeout)
|
||||
_update_timer.wait_time = update_interval
|
||||
resume()
|
||||
|
||||
|
||||
func _on_timeout() -> void:
|
||||
if system_sync:
|
||||
_update_time_from_os()
|
||||
else:
|
||||
var delta: float = 0.001 * (Time.get_ticks_msec() - _last_update)
|
||||
_progress_time(delta)
|
||||
_update_celestial_coords()
|
||||
_last_update = Time.get_ticks_msec()
|
||||
|
||||
|
||||
## Halts the automatic progression of in-game time until resumed.
|
||||
func pause() -> void:
|
||||
if is_instance_valid(_update_timer):
|
||||
_update_timer.stop()
|
||||
|
||||
|
||||
## Restarts the automatic progression of in-game time.
|
||||
func resume() -> void:
|
||||
if is_instance_valid(_update_timer):
|
||||
# Assume resuming from a pause, so timer only gets one tick
|
||||
_last_update = Time.get_ticks_msec() - update_interval
|
||||
if (Engine.is_editor_hint() and editor_time_enabled) or \
|
||||
(not Engine.is_editor_hint() and game_time_enabled):
|
||||
_update_timer.start()
|
||||
|
||||
|
||||
#####################
|
||||
## General
|
||||
#####################
|
||||
|
||||
var _update_timer: Timer
|
||||
var _last_update: int = 0
|
||||
var _sky_dome: SkyDome
|
||||
|
||||
@export_group("General")
|
||||
|
||||
## Allows time to progress in the editor.
|
||||
@export var editor_time_enabled: bool = true :
|
||||
set(value):
|
||||
editor_time_enabled = value
|
||||
if Engine.is_editor_hint():
|
||||
if editor_time_enabled:
|
||||
resume()
|
||||
else:
|
||||
pause()
|
||||
|
||||
|
||||
## Allows time to progress in game.
|
||||
@export var game_time_enabled: bool = true :
|
||||
set(value):
|
||||
game_time_enabled = value
|
||||
if not Engine.is_editor_hint():
|
||||
if game_time_enabled:
|
||||
resume()
|
||||
else:
|
||||
pause()
|
||||
|
||||
|
||||
## NodePath to SkyDome
|
||||
@export var dome_path: NodePath :
|
||||
set(value):
|
||||
dome_path = value
|
||||
if dome_path:
|
||||
# DEPRECATED - Remove 2.2
|
||||
if dome_path == NodePath("../Skydome"):
|
||||
dome_path = NodePath("../SkyDome")
|
||||
_sky_dome = get_node_or_null(dome_path)
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## The total length of time for a complete day and night cycle in real world minutes. Setting this to
|
||||
## [param 15] means a full in-game day takes 15 real-world minutes. [member game_time_enabled] must be
|
||||
## enabled for this to work. Negative values moves time backwards. The Witcher 3 uses a 96 minute cycle.
|
||||
## Adjust [member update_interval] to match. Shorter days needs more updates. Longer days need less.
|
||||
@export var minutes_per_day: float = 15.0
|
||||
|
||||
|
||||
## Celestial coordinates are updated based upon a timer, which continuously fires based on
|
||||
## this interval: [0.016, 10s]. Set to the lowest, 0.016 (60fps) if your [member minutes_per_day] is short,
|
||||
## such as less than 15 minutes. The Witcher 3 uses a 96 minute day cycle, so 0.1 (10fps) is adequate.
|
||||
@export_range(0.016, 10) var update_interval: float = 0.016 :
|
||||
set(value):
|
||||
update_interval = clamp(value, .016, 10)
|
||||
if is_instance_valid(_update_timer):
|
||||
_update_timer.wait_time = update_interval
|
||||
resume()
|
||||
|
||||
|
||||
#####################
|
||||
## DateTime
|
||||
#####################
|
||||
|
||||
@export_group("Current Time")
|
||||
|
||||
## Syncronize all of Sky3D with your system clock for a realtime sky, time, and date.
|
||||
@export var system_sync: bool = false
|
||||
|
||||
## A readable game date string, eg. '2025-01-01'. Alias for [member TimeOfDay.game_date].
|
||||
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
|
||||
var game_date: String = "" :
|
||||
get():
|
||||
return "%04d-%02d-%02d" % [ year, month, day ]
|
||||
|
||||
|
||||
## A readable game time string, e.g. '08:00:00'. Alias for [member TimeOfDay.game_time].
|
||||
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
|
||||
var game_time: String = "" :
|
||||
get():
|
||||
return "%02d:%02d:%02d" % [ floor(current_time), floor(fmod(current_time, 1.0) * 60.0),
|
||||
floor(fmod(current_time * 60.0, 1.0) * 60.0) ]
|
||||
|
||||
|
||||
## The current in-game time in hours, in the range [0.0 .. 23.9998]
|
||||
@export_range(0.,23.9998) var current_time: float = 8.0 :
|
||||
set(value):
|
||||
if current_time != value:
|
||||
current_time = value
|
||||
while current_time > 23.9999:
|
||||
current_time -= 24
|
||||
day += 1
|
||||
while current_time < 0.0000:
|
||||
current_time += 24
|
||||
day -= 1
|
||||
emit_signal("time_changed", current_time)
|
||||
|
||||
var new_hour: int = floori(current_time)
|
||||
var new_minute: int = floori(fmod(current_time, 1.0) * 60.0)
|
||||
|
||||
if new_hour != _previous_hour:
|
||||
_previous_hour = new_hour
|
||||
emit_signal("hour_changed", new_hour)
|
||||
|
||||
if new_minute != _previous_minute:
|
||||
_previous_minute = new_minute
|
||||
emit_signal("minute_changed", new_minute)
|
||||
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## The current day of the month
|
||||
@export_range(0,31) var day: int = 1 :
|
||||
set(value):
|
||||
if day != value:
|
||||
day = value
|
||||
while day > max_days_per_month():
|
||||
day -= max_days_per_month()
|
||||
month += 1
|
||||
while day < 1:
|
||||
month -= 1
|
||||
day += max_days_per_month()
|
||||
emit_signal("day_changed", day)
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## The current month of the year
|
||||
@export_range(0,12) var month: int = 1 :
|
||||
set(value):
|
||||
if month != value:
|
||||
month = value
|
||||
while month > 12:
|
||||
month -= 12
|
||||
year += 1
|
||||
while month < 1:
|
||||
month += 12
|
||||
year -= 1
|
||||
emit_signal("month_changed", month)
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## The current year
|
||||
@export_range(-9999,9999) var year: int = 2025 :
|
||||
set(value):
|
||||
if year != value:
|
||||
year = value
|
||||
emit_signal("year_changed", year)
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## Determines whether the current year has 366 days.
|
||||
func is_leap_year() -> bool:
|
||||
return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)
|
||||
|
||||
|
||||
## Calculates the number of days in the current month, including adjustments for February during leap years.
|
||||
func max_days_per_month() -> int:
|
||||
match month:
|
||||
1, 3, 5, 7, 8, 10, 12:
|
||||
return 31
|
||||
2:
|
||||
return 29 if is_leap_year() else 28
|
||||
return 30
|
||||
|
||||
|
||||
## Sets [member current_time] from parameters.
|
||||
func set_time(hour: int, minute: int, second: int) -> void:
|
||||
current_time = float(hour) + float(minute) / 60.0 + float(second) / 3600.0
|
||||
|
||||
|
||||
## Sets the in-game date and time from a dictionary.
|
||||
func set_from_datetime_dict(datetime_dict: Dictionary) -> void:
|
||||
year = datetime_dict.year
|
||||
month = datetime_dict.month
|
||||
day = datetime_dict.day
|
||||
set_time(datetime_dict.hour, datetime_dict.minute, datetime_dict.second)
|
||||
|
||||
|
||||
## Returns the current date and time as a Godot Time format compatible dictionary.
|
||||
func get_datetime_dict() -> Dictionary:
|
||||
var datetime_dict: Dictionary = {
|
||||
"year": year,
|
||||
"month": month,
|
||||
"day": day,
|
||||
"hour": floor(current_time),
|
||||
"minute": floor(fmod(current_time, 1.0) * 60.0),
|
||||
"second": floor(fmod(current_time * 60.0, 1.0) * 60.0)
|
||||
}
|
||||
return datetime_dict
|
||||
|
||||
|
||||
## Initializes datetime from Unix timestamp, used by users' save/load extentions.
|
||||
func set_from_unix_timestamp(timestamp: int) -> void:
|
||||
set_from_datetime_dict(Time.get_datetime_dict_from_unix_time(timestamp))
|
||||
|
||||
|
||||
## Converts current datetime to Unix timestamp, used by users' save/load extentions.
|
||||
func get_unix_timestamp() -> int:
|
||||
return Time.get_unix_time_from_datetime_dict(get_datetime_dict())
|
||||
|
||||
|
||||
func _progress_time(delta: float) -> void:
|
||||
var hours_per_real_second: float = 0. if is_zero_approx(minutes_per_day) else HOURS_PER_DAY / (minutes_per_day * 60.0)
|
||||
current_time += delta * hours_per_real_second
|
||||
|
||||
|
||||
func _update_time_from_os() -> void:
|
||||
var date_time_os: Dictionary = Time.get_datetime_dict_from_system()
|
||||
set_time(date_time_os.hour, date_time_os.minute, date_time_os.second)
|
||||
day = date_time_os.day
|
||||
month = date_time_os.month
|
||||
year = date_time_os.year
|
||||
|
||||
|
||||
#####################
|
||||
## Planetary
|
||||
#####################
|
||||
|
||||
@export_group("Planetary And Location")
|
||||
|
||||
var _sun_coords: Vector2 = Vector2.ZERO
|
||||
var _moon_coords: Vector2 = Vector2.ZERO
|
||||
var _sun_distance: float
|
||||
var _mean_sun_longitude: float
|
||||
var _sideral_time: float
|
||||
var _local_sideral_time: float
|
||||
var _sun_orbital_elements := OrbitalElements.new()
|
||||
var _moon_orbital_elements := OrbitalElements.new()
|
||||
|
||||
|
||||
enum CelestialMode { SIMPLE, REALISTIC }
|
||||
## Selects between simple trigonometric approximations or full orbital mechanics for computing sun and moon positions.
|
||||
@export var celestials_calculations := CelestialMode.REALISTIC:
|
||||
set(value):
|
||||
celestials_calculations = value
|
||||
_update_celestial_coords()
|
||||
notify_property_list_changed()
|
||||
|
||||
|
||||
## The observer's position on Earth, north or south of the equator (0°=equator, +90°=North Pole, -90°=South Pole).
|
||||
@export_range(-90, 90, 0.00001, "radians_as_degrees") var latitude: float = deg_to_rad(16.) :
|
||||
set(value):
|
||||
latitude = value
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## The observer's position on Earth, east or west of the prime meridian (0°=Greenwich, +180°=International Date Line).
|
||||
@export_range(-180, 180, 0.00001, "radians_as_degrees") var longitude: float = deg_to_rad(108.) :
|
||||
set(value):
|
||||
longitude = value
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## Specifies the time zone offset from UTC in hours to adjust local time calculations.
|
||||
@export_range(-12,14,.25) var utc: float = 7.0 :
|
||||
set(value):
|
||||
utc = value
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## Calculate moon orbital position. Disable for performance if moon lighting not needed.
|
||||
@export var compute_moon_coords: bool = true :
|
||||
set(value):
|
||||
compute_moon_coords = value
|
||||
_update_celestial_coords()
|
||||
notify_property_list_changed()
|
||||
|
||||
|
||||
## Toggles the calculation of sidereal time and star field rotation for realistic deep space alignments.
|
||||
@export var compute_deep_space_coords: bool = true :
|
||||
set(value):
|
||||
compute_deep_space_coords = value
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## Offset the moon azimuth and altitude. Simple celestial calculation mode only.
|
||||
@export var moon_coords_offset: Vector2 = Vector2(0.0, 0.0) :
|
||||
set(value):
|
||||
moon_coords_offset = value
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## Returns the current time at UTC 0
|
||||
func get_current_time_utc0() -> float:
|
||||
return current_time - utc
|
||||
|
||||
|
||||
func _get_time_scale() -> float:
|
||||
# All divisions must be integer divisions, see: https://stjarnhimlen.se/comp/ppcomp.html#3
|
||||
var leap_term: int = int(7 * int((year + int((month + 9) / 12))) / 4)
|
||||
var century_term: int = int(3 * int((int((year + int((month - 9) / 7))) / 100) + 1) / 4)
|
||||
var month_term: int = int(275 * month / 9)
|
||||
var time_scale: int = 367 * year - leap_term - century_term + month_term + day - 730515
|
||||
return time_scale + current_time / 24.0
|
||||
|
||||
|
||||
func _get_oblecl() -> float:
|
||||
# From: https://stjarnhimlen.se/comp/ppcomp.html#4
|
||||
# Another quantity we will need is ecl, the obliquity of the ecliptic,
|
||||
# i.e. the "tilt" of the Earth's axis of rotation (currently 23.4 degrees
|
||||
# and slowly decreasing). First, compute the "d" of the moment of interest
|
||||
# (section 3). Then, compute the obliquity of the ecliptic:
|
||||
return deg_to_rad(23.4393 - 3.563e-7 * _get_time_scale())
|
||||
|
||||
|
||||
func _update_celestial_coords() -> void:
|
||||
if not _sky_dome:
|
||||
return
|
||||
|
||||
match celestials_calculations:
|
||||
CelestialMode.SIMPLE:
|
||||
_compute_simple_sun_coords()
|
||||
_sky_dome.sun_altitude = _sun_coords.y
|
||||
_sky_dome.sun_azimuth = _sun_coords.x
|
||||
if compute_moon_coords:
|
||||
_compute_simple_moon_coords()
|
||||
_sky_dome.moon_altitude = _moon_coords.y
|
||||
_sky_dome.moon_azimuth = _moon_coords.x
|
||||
# Celestial North Pole (SIMPLE convention: +Z = north)
|
||||
_sky_dome.celestial_north_pole = Vector3(0.0, sin(latitude), cos(latitude))
|
||||
|
||||
if compute_deep_space_coords:
|
||||
if _sky_dome.is_scene_built:
|
||||
_sky_dome.sky_material.set_shader_parameter("star_tilt", HALFPI - latitude)
|
||||
|
||||
CelestialMode.REALISTIC:
|
||||
_compute_realistic_sun_coords()
|
||||
_sky_dome.sun_altitude = -_sun_coords.y
|
||||
_sky_dome.sun_azimuth = -_sun_coords.x
|
||||
if compute_moon_coords:
|
||||
_compute_realistic_moon_coords()
|
||||
_sky_dome.moon_altitude = -_moon_coords.y
|
||||
_sky_dome.moon_azimuth = -_moon_coords.x
|
||||
# Celestial North Pole (REALISTIC convention: -Z = north)
|
||||
_sky_dome.celestial_north_pole = Vector3(0.0, sin(latitude), -cos(latitude))
|
||||
|
||||
if compute_deep_space_coords:
|
||||
if _sky_dome.is_scene_built:
|
||||
_sky_dome.sky_material.set_shader_parameter("star_tilt", latitude - HALFPI)
|
||||
_sky_dome.sky_material.set_shader_parameter("star_rotation", -_local_sideral_time)
|
||||
_sky_dome.update_moon_coords()
|
||||
|
||||
|
||||
func _compute_simple_sun_coords() -> void:
|
||||
# PI/12.0 radians = 15 degrees => 1 hour is 15 degrees of rotation
|
||||
var altitude: float = (get_current_time_utc0() * RADIANS_PER_HOUR) + longitude
|
||||
_sun_coords.y = PI - altitude
|
||||
_sun_coords.x = latitude
|
||||
|
||||
|
||||
func _compute_simple_moon_coords() -> void:
|
||||
_moon_coords.y = (PI - _sun_coords.y) + moon_coords_offset.y
|
||||
_moon_coords.x = (PI + _sun_coords.x) + moon_coords_offset.x
|
||||
|
||||
|
||||
func _compute_realistic_sun_coords() -> void:
|
||||
# Orbital Elements
|
||||
_sun_orbital_elements.get_orbital_elements(0, _get_time_scale())
|
||||
_sun_orbital_elements.M = TOD_Math.rev(_sun_orbital_elements.M)
|
||||
|
||||
# Mean anomaly in radians
|
||||
var MRad: float = deg_to_rad(_sun_orbital_elements.M)
|
||||
|
||||
# Eccentric Anomaly
|
||||
var E: float = _sun_orbital_elements.M + rad_to_deg(_sun_orbital_elements.e * sin(MRad) * (1 + _sun_orbital_elements.e * cos(MRad)))
|
||||
|
||||
var ERad: float = deg_to_rad(E)
|
||||
|
||||
# Rectangular coordinates of the sun in the plane of the ecliptic
|
||||
var xv: float = cos(ERad) - _sun_orbital_elements.e
|
||||
var yv: float = sin(ERad) * sqrt(1 - _sun_orbital_elements.e * _sun_orbital_elements.e)
|
||||
|
||||
# Distance and true anomaly
|
||||
# Convert to distance and true anomaly(r = radians, v = degrees)
|
||||
var r: float = sqrt(xv * xv + yv * yv)
|
||||
|
||||
var v: float = atan2(yv, xv)
|
||||
_sun_distance = r
|
||||
|
||||
# True longitude
|
||||
var lonSun: float = v + deg_to_rad(_sun_orbital_elements.w)
|
||||
lonSun = fmod(lonSun + TAU, TAU) # Wrap to [0, 2π)
|
||||
|
||||
## Ecliptic and ecuatorial coords
|
||||
|
||||
# Ecliptic rectangular coords
|
||||
var xs: float = r * cos(lonSun)
|
||||
var ys: float = r * sin(lonSun)
|
||||
|
||||
# Ecliptic rectangular coordinates rotate these to equatorial coordinates
|
||||
var obleclCos: float = cos(_get_oblecl())
|
||||
var obleclSin: float = sin(_get_oblecl())
|
||||
|
||||
var xe: float = xs
|
||||
var ye: float = ys * obleclCos - 0.0 * obleclSin
|
||||
var ze: float = ys * obleclSin + 0.0 * obleclCos
|
||||
|
||||
# Ascencion and declination
|
||||
var RA: float = atan2(ye, xe) # right ascension.
|
||||
var decl: float = atan2(ze, sqrt(xe * xe + ye * ye)) # declination
|
||||
|
||||
# Mean longitude
|
||||
var L: float = deg_to_rad(_sun_orbital_elements.w + _sun_orbital_elements.M)
|
||||
L = fmod(L + TAU, TAU) # Wrap to [0, 2π)
|
||||
_mean_sun_longitude = L
|
||||
var GMST0: float = L + PI
|
||||
|
||||
# Sidereal, see: https://stjarnhimlen.se/comp/ppcomp.html#5b
|
||||
# Local Sidereal Time = GMST0 + UT + longitude
|
||||
_sideral_time = GMST0 + get_current_time_utc0() * RADIANS_PER_HOUR + longitude
|
||||
_local_sideral_time = _sideral_time
|
||||
|
||||
# Hour Angle = Sidereal Time - Right Ascension
|
||||
# See: https://stjarnhimlen.se/comp/ppcomp.html#12b
|
||||
var HA: float = _sideral_time - RA
|
||||
|
||||
# Hour angle and declination in rectangular coords
|
||||
var declCos: float = cos(decl)
|
||||
var x: float = cos(HA) * declCos # X Axis points to the celestial equator in the south
|
||||
var y: float = sin(HA) * declCos # Y axis points to the horizon in the west
|
||||
var z: float = sin(decl) # Z axis points to the north celestial pole
|
||||
|
||||
# Rotate the rectangualar coordinates system along of the Y axis
|
||||
var sinLat: float = sin(latitude)
|
||||
var cosLat: float = cos(latitude)
|
||||
var xhor: float = x * sinLat - z * cosLat
|
||||
var yhor: float = y
|
||||
var zhor: float = x * cosLat + z * sinLat
|
||||
|
||||
# Azimuth and altitude
|
||||
_sun_coords.x = atan2(yhor, xhor) + PI
|
||||
_sun_coords.y = (PI * 0.5) - asin(zhor)
|
||||
|
||||
|
||||
func _compute_realistic_moon_coords() -> void:
|
||||
# Orbital Elements
|
||||
_moon_orbital_elements.get_orbital_elements(1, _get_time_scale())
|
||||
_moon_orbital_elements.N = TOD_Math.rev(_moon_orbital_elements.N)
|
||||
_moon_orbital_elements.w = TOD_Math.rev(_moon_orbital_elements.w)
|
||||
_moon_orbital_elements.M = TOD_Math.rev(_moon_orbital_elements.M)
|
||||
|
||||
var NRad: float = deg_to_rad(_moon_orbital_elements.N)
|
||||
var IRad: float = deg_to_rad(_moon_orbital_elements.i)
|
||||
var MRad: float = deg_to_rad(_moon_orbital_elements.M)
|
||||
|
||||
# Eccentric anomaly
|
||||
var E: float = _moon_orbital_elements.M + rad_to_deg(_moon_orbital_elements.e * sin(MRad) * (1 + _moon_orbital_elements.e * cos(MRad)))
|
||||
var ERad: float = deg_to_rad(E)
|
||||
|
||||
# See: https://stjarnhimlen.se/comp/ppcomp.html#6
|
||||
var xv: float = _moon_orbital_elements.a * (cos(ERad) - _moon_orbital_elements.e)
|
||||
var yv: float = _moon_orbital_elements.a * sqrt(1 - _moon_orbital_elements.e * _moon_orbital_elements.e) * sin(ERad)
|
||||
|
||||
# Convert to distance and true anomaly(r = radians, v = degrees)
|
||||
var r: float = sqrt(xv * xv + yv * yv)
|
||||
var v: float = atan2(yv, xv)
|
||||
var l: float = v + deg_to_rad(_moon_orbital_elements.w)
|
||||
|
||||
var cosL: float = cos(l)
|
||||
var sinL: float = sin(l)
|
||||
var cosNRad: float = cos(NRad)
|
||||
var sinNRad: float = sin(NRad)
|
||||
var cosIRad: float = cos(IRad)
|
||||
|
||||
var xeclip: float = r * (cosNRad * cosL - sinNRad * sinL * cosIRad)
|
||||
var yeclip: float = r * (sinNRad * cosL + cosNRad * sinL * cosIRad)
|
||||
var zeclip: float = r * (sinL * sin(IRad))
|
||||
|
||||
# Position in space: https://stjarnhimlen.se/comp/ppcomp.html#7
|
||||
# Geocentric position for the moon and Heliocentric position for the planets
|
||||
var lonecl: float = atan2(yeclip, xeclip) # Ecliptic longitude
|
||||
var latecl: float = atan2(zeclip, sqrt(xeclip * xeclip + yeclip * yeclip)) # Ecliptic latitude
|
||||
|
||||
var nr: float = 1.0
|
||||
var xh: float = nr * cos(lonecl) * cos(latecl)
|
||||
var yh: float = nr * sin(lonecl) * cos(latecl)
|
||||
var zh: float = nr * sin(latecl)
|
||||
|
||||
# Geocentric coords
|
||||
var xs: float = 0.0
|
||||
var ys: float = 0.0
|
||||
|
||||
# Convert the geocentric position to heliocentric position
|
||||
var xg: float = xh + xs
|
||||
var yg: float = yh + ys
|
||||
var zg: float = zh
|
||||
|
||||
# Ecuatorial coords
|
||||
# Convert xg, yg to equatorial coords
|
||||
var obleclCos: float = cos(_get_oblecl())
|
||||
var obleclSin: float = sin(_get_oblecl())
|
||||
|
||||
var xe: float = xg
|
||||
var ye: float = yg * obleclCos - zg * obleclSin
|
||||
var ze: float = yg * obleclSin + zg * obleclCos
|
||||
|
||||
# Right ascention
|
||||
var RA: float = atan2(ye, xe)
|
||||
var decl: float = atan2(ze, sqrt(xe * xe + ye * ye))
|
||||
|
||||
# Sideral time and hour angle
|
||||
var HA: float = ((_sideral_time) - RA)
|
||||
|
||||
# HA y Decl in rectangular coordinates
|
||||
var declCos: float = cos(decl)
|
||||
var xr: float = cos(HA) * declCos
|
||||
var yr: float = sin(HA) * declCos
|
||||
var zr: float = sin(decl)
|
||||
|
||||
# Rotate the rectangualar coordinates system along of the Y axis(radians)
|
||||
var sinLat: float = sin(latitude)
|
||||
var cosLat: float = cos(latitude)
|
||||
|
||||
var xhor: float = xr * sinLat - zr * cosLat
|
||||
var yhor: float = yr
|
||||
var zhor: float = xr * cosLat + zr * sinLat
|
||||
|
||||
# Azimuth and altitude
|
||||
_moon_coords.x = atan2(yhor, xhor) + PI
|
||||
_moon_coords.y = (PI *0.5) - atan2(zhor, sqrt(xhor * xhor + yhor * yhor)) # Mathf.Asin(zhor)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cjmkcj8hwm7ts
|
||||
Reference in New Issue
Block a user