DMX Sources - towneh/vrsl-urp GitHub Wiki

DMX Sources

How the desk's channel values reach the fixtures. Two routes exist, a scene can carry both, and the manager's DMX Input chooses which one the lights read. The DMX Monitor tells you which one the fixtures read at any moment. The full reference is in DMX Channel Sources. It includes the maths for framing a grid, and how to write a source of your own.

Route one: a video grid

A frame of video encodes channel values as coloured cells, the package decodes the frame on the GPU, and the fixtures read the result. This is the original VRSL route, and it exists because a video frame was once the only way to get DMX into a world.

Anything that ends up as a texture will feed it, whether a screen-capture sender in the editor or a video player showing a stream that carries the grid. Add Light Manager to Active Scene sets the manager up with the package's decode textures.

Grid layouts

VRSL lays channels out on the grid in one of three ways: Horizontal, Vertical or Legacy. The desk or encoder sends one of them, and the manager's DMX Input has to name it: Horizontal grid, Vertical grid or Legacy grid. The wrong one lights the wrong fixtures, or none. Every fixture follows the manager, so one fixture prefab works on any layout.

A world can change layout while it runs, with SetDmxInput (below) or by changing DMX Input in the inspector in Play mode. The new layout lights within a few frames, as its decode textures catch up, and the old one stops decoding. Each layout's decode textures read their own grid from the video, so whatever puts the video into a grid has to switch to the new layout's grid as well, or the lights read a grid nothing is writing. The Basis video output does that with Follow Manager Layout on.

What to know about it:

  • The grid is lossy by construction. Values pass through video compression and back. The fixtures read a smoothed copy one frame behind.
  • A 1080p frame carries three universes in horizontal mode. The strip costs a fifth of the picture.
  • Where the grid is part of a larger picture, set the corner UVs on the video output so the strip lands on the decode texture. VRSL → URP → DMX Config → Add Basis DMX Video Output (Horizontal) sets them for a Basis media player. It frames a horizontal grid across the bottom of a 1080p picture as VRSL's own reader camera does, including the quarter turn horizontal mode needs, which cannot be worked out by eye.

Route two: bytes

Where the values already arrive as data, a channel source hands them to the manager with no encoding or decoding in between, and the fixtures read the exact bytes the desk sent.

Truss carries a desk's Art-Net inside a live H.264 stream. It stamps the data into each frame as the video passes through a relay. The lighting data arrives wherever the video arrives and stays locked to the picture. A Basis media player that shows that stream raises the data as it shows each frame.

To use it:

  1. Have a BasisMediaPlayer showing the stream, which needs com.basis.mediaplayer in the project. The player also has to pass on the stream's user data as it plays. Basis's Rust media player does; the older C player does not, and the source says so in the console once and stays idle.
  2. Select an object and run VRSL → URP → DMX Config → Add Basis DMX Record Source (SEI). It adds the source component and points it at the player on that object or above it.
  3. Set minimumUniverses to the size of the show, so the first cue does not have to grow the buffers.
  4. Patch fixtures as usual, bearing in mind that the stream's universe 0 is VRSL's universe 1.
  5. Set the manager's DMX Input to Stream data. Until then the source waits on standby. A scene saved with 0.2.0-rc.6 with a source in it, or a manager added beside one, comes up on Stream data by itself.

Under Stream data the lights read the source from the moment it is enabled, whether or not data has arrived. If the stream reaches you through a service that re-encodes video, the data is stripped in transit and the fixtures go dark; nothing falls back to the picture, because the input is the event's choice. The component's RecordsDecoded and LastResult fields say which of those you have, and the DMX Monitor shows a registered source that is hearing nothing.

Choosing

Bytes Video grid
Fidelity Exact Lossy, and a frame behind
Survives a service that re-encodes video No, silently Yes
Needs the player to surface stream data Yes No
Integrity check Every record carries a CRC. The package drops damaged records and counts them None
Capacity Whatever the bitrate allows Three universes per 1080p frame
Cost to the picture None A strip of the frame

Use bytes wherever the player supports it. Keep the grid where the video path is not yours to control.

Choosing the input, and syncing it

The manager's DMX Input says which route the lights read: Horizontal grid, Vertical grid, Legacy grid or Stream data. A channel source in the scene waits on standby under a grid input, bound and kept current, and is read the moment Stream data is chosen. Under Stream data no grid is decoded at all.

Nothing chooses for you. A stream can carry a grid and records at once, so which one a show uses is the event's call, and the package never switches by itself: not when records start or stop arriving, and not when a source is enabled or disabled. Under Stream data with nothing sending, the lights are dark and the manager says so in the Console after a few seconds; choose a grid if the stream carries no records.

SetDmxInput on the manager takes the choice as a number: 0 Horizontal grid, 1 Vertical grid, 2 Legacy grid, 3 Stream data. A UI Dropdown with the four options in that order can call it from On Value Changed (Int32) directly, which is enough for a world with one person in it.

With more than one person, every client has to read the same input, including anyone who joins later. Hold the number in the world's own networked script and apply it on every client: when it changes, and once on join with the current value. Feed the dropdown into that script rather than straight into the manager. Who may change it, the event's operator or the world's owner, is the world's rule. An outline for a BasisNetworkBehaviour, to check against the Basis version you build with:

public class DmxInputSync : BasisNetworkBehaviour
{
    int input;

    // The dropdown's On Value Changed (Int32).
    public void Choose(int value)
    {
        if (!IsLocalOwner()) return;   // the world's rule for who may change it
        input = value;
        Apply();
        SendCustomNetworkEvent(new[] { (byte)value }, DeliveryMethod.ReliableOrdered);
    }

    public override void OnNetworkMessage(ushort playerId, byte[] buffer, DeliveryMethod method)
    {
        if (buffer == null || buffer.Length < 1) return;
        input = buffer[0];
        Apply();
    }

    // Tell someone who has just arrived what is live.
    public override void OnPlayerJoined(BasisNetworkPlayer player)
    {
        if (IsLocalOwner())
            SendCustomNetworkEvent(new[] { (byte)input }, DeliveryMethod.ReliableOrdered,
                                   new[] { player.playerId });
    }

    void Apply()
    {
        var manager = VRSL_DMXURPLightManager.Instance;
        if (manager != null) manager.SetDmxInput(input);
    }
}

SetDmxInput is safe to repeat and to call early: applying a number twice, before the source has come up, or before the manager has taken over ends in the same state as applying it once. An interpreted world script does the same through a BasisNetworkShim from SafeUtil.MakeNetworkable(this), whose NetworkMessageReceived and PlayerJoined take the place of the overrides; it can only call SetDmxInput once Basis allows VRSL's manager in the sandbox, which it doesn't yet.

If the video output frames more than one layout's grid, turn on its Follow Manager Layout and set the corners for each layout, so switching between grids switches what it writes. It writes nothing under Stream data.

Writing your own source

A component that implements IVRSLDMXChannelSource hands the manager blocks of channel values by universe and start address. Once assigned to VRSL_DMXURPLightManager.Instance.ChannelSource, the manager asks it for blocks every frame until it is cleared, and the lights read them under Stream data. The reference covers the interface, the timing rules for movement smoothing, and the menu that reads the buffer back through the shader.