HammeriteSkybox
Core: the map. Inherits Resource
What is outside a map: a scale model of the world beyond it, and the sky over that.
The idea is Half-Life 2's, and it is worth stating plainly because the geometry is otherwise baffling: the city outside a level is built SMALL, and looked at from a camera that stands inside the model rather than where the player stands. Move the player and the sky does not shift, because it is not where they are - it is where camera_position is, at model_scale. Which is how a hundred metres of rooftop can sit behind a window in a room eight metres across, and hold still while you walk past it. A map has one (HammeriteMap.skybox). Which part of the city a map is in is then a matter of moving the virtual camera about inside the same model, rather than building a second one - so a dozen maps in one city share a model and differ by a position. What is authored here is the OUTSIDE. Everything about where it shows through - which walls are sky - is the map's, and already decided: a face whose texture is named sky_* is an aperture, and it is those the sky is drawn on. Hammerite draws a gradient; a game draws its own sky. What is here is what lights the world - the gradient, the sun or moon, their light - and dome_material is what the sky is drawn with: Hammerite's own is the gradient and nothing in it. A game's clouds, its sun's disc, its weather are properties it declares (HammeriteSkyPropertyRegistry) and uniforms its shader reads: each skybox field and each declared property reaches the dome as the uniform named after it, a property clouds/cover as clouds_cover.
HammeriteSkybox.dome_material = preload("res://materials/our_sky.tres") # shader_type skyEvery field and property emits Resource.changed when it is set, and the sky that is up follows: a game changing its weather, or horizon_color at dusk, sees it on the next frame.
Properties
| Type | Name | Default |
|---|---|---|
ShaderMaterial | dome_material | <Object> |
PackedScene | model | null |
Vector3 | camera_position | Vector3(0, 0, 0) |
float | model_scale | 16.0 |
Color | horizon_color | Color(0.62, 0.66, 0.72, 1) |
Color | zenith_color | Color(0.24, 0.4, 0.62, 1) |
int | body | 0 |
Vector3 | body_direction | Vector3(0.35, -0.8, 0.45) |
Color | body_color | Color(1, 0.94, 0.8, 1) |
float | body_energy | 2000.0 |
float | light_energy | 100.0 |
bool | light_is_dynamic | false |
StringName | light_group | &"" |
Dictionary | properties | {} |
Methods
| Returns | Method |
|---|---|
bool | is_moon() |
Color | ambient_color() |
Variant | get_property(key: StringName, fallback = null) |
void | set_property(key: StringName, value) |
Enumerations
enum Body
| Value | Name | Description |
|---|---|---|
0 | SUN | A sun: its light is what body_energy says. |
1 | MOON | A moon: the sky lights the world a hundredth as brightly (ambient_color()). |
Constants
STOCK_DOME_MATERIAL
const STOCK_DOME_MATERIAL = <Object>
What the sky dome is drawn with until a game says otherwise: the gradient.
Property descriptions
dome_material
var dome_material: ShaderMaterial = <Object>
What every sky's dome is drawn with: a sky shader, given horizon_color, zenith_color, body_color, body_energy, light_energy and each declared property, by the names of the uniforms it declares, and two worked out: body_direction, the way TOWARDS the body (the field negated and normalized), and body_is_moon. One for every sky in the process, every map and every renderer: set it once, before a map is built. Null draws STOCK_DOME_MATERIAL.
model
var model: PackedScene = null
The scale model of what is out there: rooftops, hills, a distant cathedral. Anything Godot can instance. Null is a perfectly good answer - the sky itself is drawn either way, and an early map wants a sky long before it wants a city.
camera_position
var camera_position: Vector3 = Vector3(0, 0, 0)
Where the sky is looked at FROM, inside that model. This is the parameter that puts a map somewhere: the same model seen from the docks and from the cathedral roof is two skies.
model_scale
var model_scale: float = 16.0
How much smaller the model is than the world it stands for. 16 is the Half-Life 2 number and a good default: everything in the model is a sixteenth of life size, so it reads as distant.
horizon_color
var horizon_color: Color = Color(0.62, 0.66, 0.72, 1)
The colour of the sky dome at the horizon, blended towards zenith_color overhead.
zenith_color
var zenith_color: Color = Color(0.24, 0.4, 0.62, 1)
The colour of the sky straight overhead.
body
var body: int = 0
A sun or a moon.
body_direction
var body_direction: Vector3 = Vector3(0.35, -0.8, 0.45)
The direction its light travels, towards the ground (so a high sun points down), in world space; need not be normalized. The same direction a HammeriteDirectionalLight3D should point.
body_color
var body_color: Color = Color(1, 0.94, 0.8, 1)
The colour of the disc and of the light body_energy describes.
body_energy
var body_energy: float = 2000.0
What its light is worth on the ground, in the units a light's energy is in: a directional light that follows the sky shines with this and body_color. A sun of a couple of thousand is a clear day; a moon is a few dozen.
light_energy
var light_energy: float = 100.0
How brightly this sky lights what is open to it, in the units a light's energy is in: 100 is roughly a bright overcast day against a sun of a couple of thousand. Its colour is ambient_color()'s. A noon sky and a dusk sky differ in this as much as in their colour, and both are the sky's to say rather than the bake's. Baked: an edit shows after the next bake.
light_is_dynamic
var light_is_dynamic: bool = false
Whether the light this sky casts is switchable at runtime, or baked into the surface for good. Static by default, and that is not only compatibility. Daylight through a hole is the one light in a level that genuinely never changes for most maps, and a static one costs no slot: a face has four, and spending one on a sky that nothing will ever modulate is a slot a lamp wanted. Turn it on for a map whose weather moves. The sky's light then claims a slot like any other light and a HammeriteBakedLight drives it, so cloud crossing the sun dims the ground it falls on - a game dimming only its sun would leave an overcast level with bright sky-lit floors.
light_group
var light_group: StringName = &""
Which slot group the sky's light joins, when it is dynamic (light_is_dynamic). Naming the group a directional light already uses is the usual thing: the sun and the daylight around it are one sky and should dim together, and one handle drives both. Empty gives the sky a group of its own, which is right when the sky is the only thing that moves. Ignored entirely while the light is static, since a static light claims no slot to group.
properties
var properties: Dictionary = {}
What games and addons have put on this sky (HammeriteSkyPropertyRegistry) - a game's weather. Sparse: only what an author changed from its registered default. Read through get_property(), and write through set_property(): an edit of the dictionary in place is not heard.
Method descriptions
is_moon()
func is_moon() -> bool
Is this a moon rather than a sun? What it changes is the light: see ambient_color().
ambient_color()
func ambient_color() -> Color
What colour this sky lights the world with - the average of what a surface open to it can see, which is mostly its zenith. An overcast sky is a grey one. This is what a sky face should EMIT (sky faces are apertures, and an aperture with nothing behind it lights nothing, which is why a room open to the sky came out black at the bottom).
get_property()
func get_property(key: StringName, fallback = null) -> Variant
The value of registered sky property key: this sky's own, or the registered default.
set_property()
func set_property(key: StringName, value) -> void
Author sky property key. Setting it back to its registered default erases the override, as a brush property's does, so the sparse invariant holds however the author gets there.