Layers

Imagine walking into a digital workshop. At the entrance sits a Project, a sturdy cabinet filled with blueprints, reference notes, and the settings needed to reassemble an entire 3D world. Each drawer in that cabinet holds a Layer — a focused blueprint describing a single part of that world: the terrain, the buildings, the trees, the imagery above them.
When you open a drawer (load a layer), a craftsman — the LayerGameObject — takes the blueprint and builds its
physical form on the workbench: a GameObject hierarchy, meshes, textures, and whatever else is needed to make it
visible.
Beside the bench sits a control panel — the Layer Panel — letting you switch layers on or off, reorder them, and
tweak their properties while watching the world change.
This is the Layer System of Netherlands3D: a clean separation between data (the blueprint), runtime behaviour (the craftsman), and presentation (the instruments).
Quick cheat sheet
- Purpose: Provide a bridge between serialized project data and live, interactive scene content.
- Audience: Developers and designers extending or integrating new layer types.
- Mental shortcut: Layers follow a define → build → interact cycle. LayerData defines the layer, LayerGameObject builds and synchronizes it, and the Layer Panel and scene view let users interact with it — all staying in sync through the model-view-presenter pattern.
1. Mental model at a glance
| Term | Think of it as | Where it lives |
|---|---|---|
| Project | The workshop that stores all layer blueprints and settings; opening it reconstructs the scene from data. | Project / .nl3d |
| LayerData | A blueprint node in the project hierarchy — pure data (id, name, parent/children, visibility, properties). No in-world GameObjects. | Assets/_Application/Layers/LayerData.cs |
| ReferencedLayerData | A blueprint with a handle to runtime content (e.g., prefabId). Owns/attaches a presenter and may resolve external assets/streams. |
Assets/_Application/Layers/LayerTypes/ReferencedLayerData.cs |
| LayerGameObject | The presenter/visualizer created and managed by ReferencedLayerData; builds and syncs the in-world representation and the layer’s UI entry. |
Assets/_Application/Layers/LayerGameObject.cs |
| Layer UI (LayerUIManager / LayerUI) | The panel view for inspecting, toggling, renaming, and reordering layers; UI only (no scene orchestration). | Assets/_Application/Layers/UI/HierarchyInspector/LayerUIManager.cs, LayerUI.cs |
| Property Sections | Modular editors for layer settings; write to LayerData and, when a presenter exists, trigger re-render via the presenter. |
Assets/_Application/Layers/Properties/Properties.cs, PropertySectionWithLayerGameObject.cs, LayerGameObjectPropertySectionInstantiator.cs |
| Builder / Spawner / Layers service | The foremen that instantiate presenters for referenced blueprints and keep hierarchy/order in sync with data. | Assets/_Application/Layers/LayerBuilder.cs, LayerSpawner.cs, Services/Layers.cs |
Together they form the Layer System: data-defined blueprints (LayerData) inside a Project; only referenced
blueprints (ReferencedLayerData) create a presenter (LayerGameObject) that drives the UI panel and *
in-world representation*, with property edits flowing through the model and being reflected live.
2. A day in the life of a layer
-
A project is opened, and its stored
.nl3dfile is read. The project loader reconstructs everyLayerDataentry — a set of blueprints describing the layers that make up the scene. At this point, nothing is visible yet; the data simply defines what should exist. -
Each blueprint finds its craftsman. For every
LayerData, aLayerGameObjectpresenter is instantiated. This presenter interprets the stored data into live scene content — spawning GameObjects, loading prefabs, or connecting to external datasets such as WMS or 3D Tiles. -
The layer’s two views come online.
- The scene view is created, building the actual 3D representation (meshes, sprites, or tile hierarchies).
- The UI view appears in the Layer Panel, mirroring the model’s name, order, and visibility.
-
Everything stays in sync. When the user toggles visibility, renames a layer, or adjusts a property in the UI, the presenter updates the
LayerData. The change is reflected immediately in the scene. Likewise, when project data is modified programmatically, the presenter propagates those updates back to both views. -
The layer tidies up when it’s removed or replaced. The presenter detaches from its model, destroys associated GameObjects, and ensures the updated state is stored in the project. When reopened, the same data can rebuild the same scene, ensuring reproducibility between sessions.
If you prefer a concrete example, picture the “Buildings” layer with a referenced dataset and a few basic properties:
| Action | What happens behind the scenes |
|---|---|
| Layer is loaded | LayerData specifies a ReferencedLayerData with a prefab ID pointing to building models. |
| Presenter created | LayerGameObject resolves the prefab, spawns the models, and registers itself in the hierarchy. |
| User toggles visibility | The UI updates the visible flag in LayerData; the presenter calls SetActive(false) on the scene objects. |
| User edits color | The property section writes the change to the model; the presenter reconfigures the material. |
| Layer deleted | The presenter cleans up, detaching its references and removing the model from the project. |
Every step follows the same cycle: data model defines → presenter interprets → views reflect → data model updates.
3. Understanding Layers
3.1. The workshop: Projects as containers

A Project in Netherlands3D acts as the workspace of the system. It stores not only project-wide configuration and
metadata but also an ordered collection of Layer blueprints (LayerData objects).
Each blueprint defines:
- What the layer is (
type) - How it should appear (
visible,style,properties) - How it connects to others (
parent,children)
When you open a project (.nl3d file), the system rebuilds your workspace:
- It reads all stored
LayerDataobjects. - It instantiates a LayerGameObject for each one that has a visualisation, or presenter.
- Those presenters rebuild their corresponding scene content and user interface entries.
The result: your saved digital world — exactly as it was when you left it.
3.2. The blueprint: LayerData
LayerData is the model — pure, serializable information.
It doesn’t render, animate, or run code. It describes what exists, not how it behaves.
Each LayerData instance defines:
- A unique identifier (
id) - Its type (determines which presenter will interpret it)
- Relationships (parent/child layers)
- Visibility, name, and metadata
- Optional serialized property blocks (through the properties system)
Because LayerData is stored as JSON (using Newtonsoft), it’s platform-agnostic and editable outside Unity if needed.
3.3. The craftsman: LayerGameObject
Once the project loads, each blueprint is handed to a craftsman — a LayerGameObject.
This class acts as the Presenter in the MVP pattern: it binds the pure data from LayerData to two forms of
presentation:
- The Scene View — a tangible GameObject hierarchy with renderers, meshes, or other runtime elements.
- The UI View — a panel entry for toggling, reordering, and editing.
Its duties include:
- Creating and maintaining the scene object tree.
- Reacting to model updates (visibility, order, style changes).
- Forwarding UI edits back to the model.
- Managing lifecycle events such as instantiation, destruction, and rehydration after serialization.
3.4. The instruments: Layer panel and in-world representation
The craftsman doesn’t work alone — it interacts with instruments that let both developers and users see and control its results.
3.4.1 UI View — the Layer Panel
The Layer Panel conceptually mirrors Unity’s hierarchy but with more context.
It’s managed by LayerUIManager and composed of LayerUI elements — one per layer.
Through this interface you can:
- Toggle visibility
- Reorder layers
- Select layers for editing
- Delete layers
- Open property sections for fine-tuning
Unlike the classic Unity hierarchy, the panel is not just a visual list — it’s fully synchronized with the LayerData
hierarchy.
3.4.2 Scene View — the 3D result
Each layer’s LayerGameObject builds its own subtree of GameObjects, representing what that layer contributes to the
scene.
Depending on the layer type, this might be:
- A mesh or tile (e.g., Cartesian tiles)
- A set of sprites or annotations
- A streamed dataset (e.g., OGC 3D Tiles or WMS imagery)
- A custom prefab loaded via
ReferencedLayerData
3.5. Types of blueprints: Basic vs Referenced layers
| Type | What it describes | Common use |
|---|---|---|
| Basic Layer | Self-contained; data and visual are built entirely from the model. | Folders, polygon overlays, feature masks. |
| Referenced Layer | Contains a pointer to external content (prefab, library ID, streamed source). | 3D Tiles, WMS imagery, CityJSON models. |
Referenced layers are defined by ReferencedLayerData, which tells the presenter how to resolve the external
resource.
Their LayerGameObject might live outside the main hierarchy, but they still behave like native layers in the UI panel.
3.6. Fine-tuning the result: Properties and Property Sections
Once a layer is built, its look and behaviour can be adjusted through Property Sections — modular UI components bound to its model and presenter.
The properties system includes:
Properties.cs— the backbone for registering and reading property blocks.PropertySectionWithLayerGameObject.cs— a base for UI sections that interact with presenters.LayerGameObjectPropertySectionInstantiator.cs— a factory for spawning the right sections per layer type.
Example property sections:
FillColorPropertySection.cs— controls polygon fill color.VerticalDatumPropertySection.cs— adjusts reference elevation.ToggleScatterPropertySection.cs— toggles scattered objects in polygon layers.CredentialsInputPropertySection.cs— manages authentication for remote layers.WMSPropertySection.cs— configures WMS source parameters.
Each property section updates the model, not the scene directly. The presenter observes model changes and reflects them into the scene view — keeping data authoritative.
3.7. The ecosystem of creation and updates
Several utilities keep the system coherent:
| Role | Responsibility | Typical Files |
|---|---|---|
| Layers Service | Facade that coordinates everything: building, destroying, and syncing. | Services/Layers.cs |
| LayerBuilder | A Builder for constructing new models, which can be added using the Layers Service. | LayerBuilder.cs |
| LayerSpawner | Factory for constructing, or spawning, the LayerGameObjects -or presenters-. | LayerSpawner.cs |
| LayerUIManager | UI hierarchy and visibility control. | LayerUIManager.cs |
Together, they ensure that adding a new layer or reloading a project follows the same predictable pattern.
3.8. Hierarchy, order, and visibility
The hierarchy of LayerData mirrors Unity’s transform hierarchy:
- Parenting maps to
Transform.parent. - Reordering maps to
SetSiblingIndex. - Visibility maps to
GameObject.SetActive.
Referenced layers maintain the same semantics even when their roots live elsewhere.
4. Crafting Layers
5. Where Layers live and how they travel
6. Tips & best practices
- Let data lead.
LayerDatais the source of truth; presenters interpret it. - Keep presenters idempotent. They should rebuild safely when data changes or reloads.
- Minimize deep nesting. Each transform level adds cost; prefer logical grouping.
- Clean teardown. Deregister and destroy referenced content carefully.
- Stay data-driven. Avoid hardcoded child references; rely on serialized data and builders.
- Respect Functionality toggles. Integrate with the
FunctionalityListenersystem.
8. Recap
- The Project is your workshop — a collection of layer blueprints (
LayerData). - Each Layer is a blueprint that comes to life through its Presenter (
LayerGameObject). - The Presenter visualizes the in-world scene, and the UI panel visualizes the UI view.
- Properties let users fine-tune layer data; the presenter reflects those changes live.
- The LayerUIManager handles panel interaction, while LayerSpawner and Layers handle creation logic.
- Everything stays serializable, modular, and data-driven — ensuring reproducibility across sessions and machines.