Moving brush entities
A door, a lift or a secret wall is brushwork that moves. While it stands where it was baked, a lightmap lights it best: the shadow of the frame, the lamp across the hall. The moment it moves, that lightmap is a picture of somewhere it no longer is. So a brush entity hands its faces over to the light probes as it starts to move, and - if it comes back exactly where it was - hands them back.
1. Is it in the bake?
By default a brush entity's brushwork is not lightmapped: the bake treats it as something that is somewhere else as often as not, and does not let it cast shadows it will not be there to cast. A door or a lift should stay that way, and be lit by the probes from the start.
Something that is flush with the world nearly all the time - a secret door in a panelled wall - wants the lightmap while it is shut. Say so on the type, and every brush of every such entity is baked:
HammeriteEntityTypeRegistry.register_brush(&"secret_door", {
&"classname": &"SecretDoor",
&"bake_static": true,
})
An author can say the same of one brush, with Bake into the world (lighting/bake_static) in
its Lighting properties. get_lighting().is_baked_static() answers which it is.
2. Hand over as it starts moving
Every HammeriteBrushEntity* base class carries the handover. Call hand_over_to_probes() as the
movement starts, so the quarter-second blend from lightmap to probes happens under the motion rather
than as a pop once it has moved:
class_name SecretDoor
extends HammeriteBrushEntityStaticBody3D
const SLIDE: Vector3 = Vector3(0, 0, 64)
func open() -> void:
hand_over_to_probes()
create_tween().tween_property(self, ^"position", SLIDE, 1.5)
func set_editor_active(_active: bool) -> void:
pass
func apply_entity_property(_prop_name: StringName, _value: Variant) -> void:
pass
The node of a brush entity stands at the map's origin with its brushwork in map space, so moving the
node by SLIDE moves the brushwork by SLIDE.
From then on, each frame, the entity finds the probes around the middle of its brushwork - again as it moves far enough - and writes them into its faces' materials, following every lamp that is switched. Nothing needs driving: the base class's lighting runs itself while there is anything to blend.
Call it for an entity that is not baked too. It has no lightmap to leave, but probes light it far better than the flat ambient it otherwise wears.
3. Hand back only when it is flush again
func close() -> void:
var slide := create_tween()
slide.tween_property(self, ^"position", Vector3.ZERO, 1.5)
slide.finished.connect(hand_back_to_lightmap)
hand_back_to_lightmap() blends back to the bake, and the entity stops costing anything per frame.
Only call it when the brushwork is back exactly where it was baked: a door left ajar is not where its
lightmap says it is, and should stay on the probes.
4. In the editor
The handover belongs to the running world: put it in the movement, which runs while the world is
simulating, and reset_simulation() puts the entity back where the map built it - flush, so hand back
there too:
func reset_simulation() -> void:
position = Vector3.ZERO
hand_back_to_lightmap()
5. Driving it yourself
get_lighting() returns the entity's HammeriteBrushEntityLighting,
made the first time it is asked for. get_blend() says how far over it is - 0.0 all lightmap,
1.0 all probes. After a rebake replaces the brushwork's meshes, the lighting finds the new materials
itself.
See also
- Probe lighting - what the faces are lit by once handed over.
- Writing an entity class - brushwork entities, and the rest of the interface.
HammeriteBrushEntityStaticBody3D,HammeriteBrushEntityNode3D.