· 4 min read
When the Lights Come On
Day and night on a model railway is not a lighting change. It is a second copy of the world that only exists after dusk, and keeping the two copies honest is most of the work.
The layout has a clock. It runs faster than real time, the light in the room shifts from morning through afternoon into dusk, and at some point the windows come on.
That last part is the interesting one, and it is not implemented the way people assume.
Lit windows are not lights
The obvious approach is to put a light source inside each building. It is also completely impractical: a town has hundreds of windows, and hundreds of dynamic lights is not a budget any real-time renderer will accept.
So a lit window is not lit. It is a second, unlit mesh — a glow mesh — drawn in a colour that reads as light, made visible when the clock passes dusk and hidden again at dawn. It emits nothing and affects nothing around it. It is a picture of a lit window, and at the distance you view the layout from, a picture of a lit window is indistinguishable from one.
The same trick runs the street lamps, the station platform lights, the signal aspects and the strings of bulbs on the fairground rides.
Two meshes, one model
Every built model therefore has the potential to produce two pieces of geometry: the ordinary lit-by-the-scene mesh, and the glow mesh. Both come out of the same draw call in the model definition, which receives two builders and decides what goes into each.
This is where it gets easy to make a mistake, because the two meshes are drawn from the same code and it is not always obvious which one a given piece of geometry ended up in.
The window helper puts the glass box into the ordinary mesh and the tint into the glow mesh. That is correct for a window: by day you see near-black glass, by night you see a warm rectangle.
It is wrong for anything meant to read as clear glazing. A bus shelter drawn with it is a solid dark box all day and a glowing one all night, neither of which is what a bus shelter does. Those want a pale solid, and the fact that this is not obvious from the function name is a genuine wart.
Animated parts cannot glow
There is a harder limitation, and it shapes what several models look like.
An animated part — a rotating wheel, a swinging boom, a turning carousel — is built with a single builder and no second one. There is nowhere for its glow geometry to go.
So the ferris wheel's rim bulbs are plain caps rather than lamps. They do not light up at night, because they physically cannot. The lit strings on that ride live on the fixed A-frames instead, which is why the frames are strung with bulbs and the wheel itself is not, and why it reads slightly differently from a real fairground wheel.
There is an escape hatch: a part can be marked as glowing outright, which makes the whole part the glow material. That works for a beacon or a lit sign. It does not work for a wheel with bulbs on it, because you would get a glowing wheel rather than a wheel with glowing bulbs.
The proper fix is to give animated parts a second builder. It is not hard. It has not been done because the ferris wheel is the only model that has wanted it so far, and one model is not yet a pattern.
The bug that took two days
When the glTF vehicle assets arrived, the code-drawn railcar was superseded: the Kenney model loads, the code model is hidden, the better-looking one is what you see.
Except at night, when the code railcar's windows came back on. Not the whole model — just its window glow, floating in the air at its own spacing and count, on top of the Kenney body which has its own windows in different places.
The cause is a two-line story. Attaching an asset hides the code model's glow mesh. The night loop then walks every registered glow mesh and sets its visibility according to the clock. The night loop won, because it ran second and did not know the difference between "hidden because it is daytime" and "hidden because this model no longer exists".
The fix is a flag on the mesh saying it has been superseded, which the night loop checks before touching anything. The general lesson is duller and more useful: a system that sets a property on everything in a list must know why each thing is in the state it is in. "Hidden" is not one condition, and code that treats it as one will eventually un-hide something that was hidden for a different reason.
What dusk actually does
Four things move together, and they move on a curve rather than a switch, because a hard cut reads as a bug.
The room's directional light dims and shifts warm, then cold. The ambient level drops. The sky visible through the window changes colour. And the glow meshes fade up.
The order matters. The lights come on slightly before it is properly dark, which is what happens in reality — street lights are on a photocell that trips while there is still light in the sky — and getting that wrong made the whole transition feel artificial in a way that was hard to diagnose until someone pointed out that the lamps were late.
- engine
- lighting
- night
