Skip to main content

Arena 3D Renderer

The RODENT arena renderer converts the experiment arena definition into a live 3D Godot scene. The renderer is data-driven: arena regions, walls, doors, structures, stimuli, materials, and camera behaviour are created from the current experiment snapshot rather than being hard-coded into a single arena layout.

Renderer Architecture

The arena is displayed through a Godot SubViewport embedded inside the arena panel.

ArenaPanel


SubViewportContainer


SubViewport


Arena3D

├── Floor regions
├── Floor labels
├── Outer walls
├── Inner walls
├── Doors
├── Structures
├── Stimuli
├── Treatment chamber
└── Camera

The SubViewportContainer is responsible for displaying the viewport, while Arena3D contains the generated 3D scene.

The relevant implementation files are:

src/app/panels/arena_panel.tscn
src/app/panels/arena_panel.gd
src/app/arena_3d.gd

Snapshot-Driven Rendering

The renderer is rebuilt using:

setup_from_snapshot(snapshot)

The snapshot contains the current arena configuration and simulation state.

When the snapshot is applied, the renderer rebuilds the relevant scene objects:

  1. Floor regions
  2. Region labels
  3. Outer walls
  4. Inner walls
  5. Doors
  6. DCR roof
  7. Structures
  8. Stimuli
  9. Treatment chamber
  10. Camera state

Previously generated arena objects are removed before rebuilding. Generated nodes use prefixes such as:

region_
outer_wall_
corner_post_
inner_wall_
jpost_
collision_
door_
ring_
dcr_roof_
bridge_
structure_
label_
stim_
light_source_
treatment_chamber

This prevents stale geometry from remaining after an experiment configuration changes.

Coordinate Mapping

Preset coordinates use a two-dimensional arena coordinate system.

JSON Godot

x ───────────────────► X
y ───────────────────► Z
elevation ► Y

The renderer uses:

_scale := 0.1

Therefore:

Godot X = JSON x × 0.1
Godot Z = JSON y × 0.1
Godot Y = elevation × 0.1

For example, a JSON position of:

[50, 25]

is rendered at approximately:

Godot X = 5.0
Godot Z = 2.5

This allows the JSON arena coordinates to remain independent of the physical scale used by the Godot scene.

Floor Regions

Arena regions are created from their rect definition.

A rectangular region uses:

"rect": [x, y, width, height]

The renderer calculates the centre of the region and creates a mesh using its dimensions.

Regions can also specify a circular shape.

For rectangular regions:

rect
├── x
├── y
├── width
└── height

For circular regions, the renderer uses the configured radius.

Region elevation is supported, allowing regions to represent raised sections of an arena.

The renderer also supports region rotation through:

"rotation_y": 90

Region Materials

Each region can reference a material through:

"material_id": "solid_floor"

The renderer resolves the material against the material definitions in:

arena.materials

The legacy surface value can also be used as a fallback when resolving a region material.

Outer Walls

Outer walls are controlled by:

"use_outer_walls": true

The wall height is configured using:

"outer_wall_height": 28

The material is selected using:

"outer_wall_material_id": "clear_acrylic"

Four outer walls are generated:

North
South
West
East

Four corner posts are also generated.

Outer walls use collision geometry so that they act as boundaries for the arena.

Inner Walls

Inner walls are defined using a rectangular geometry, height, material and enabled state.

Example:

{
"id": "divider",
"rect": [48, 20, 4, 60],
"height": 20,
"material_id": "painted_steel",
"enabled": true
}

The renderer determines whether the wall is primarily horizontal or vertical from its dimensions.

Walls may also specify:

"rotation_y": 45

The visual mesh and collision body use the same rotation.

This ensures that rotated walls remain physically aligned with their visible geometry.

Door Rendering

Doors reference an existing inner wall:

"wall": "divider"

The renderer uses this relationship to determine where the opening must be placed.

When a wall contains one or more doors, the wall is segmented around the door openings rather than rendering a solid wall through the doorway.

Door orientation is also rotation-aware.

Doors can have the following states:

open
closed
locked
transitioning

Open

An open door:

  • disables its collision shape
  • sets collision layer and mask to zero
  • displays a translucent visual
  • rotates the visual door panel open

Closed

A closed door:

  • keeps collision enabled
  • displays the closed door material

Locked

A locked door:

  • keeps collision enabled
  • displays a red-emission visual state

Transitioning

A transitioning door:

  • keeps collision enabled
  • displays an amber-emission visual state
  • partially rotates the visual panel

The renderer therefore keeps the visual representation and collision state synchronized.

Collision Geometry

The renderer creates collision geometry using Godot physics nodes.

Box-based arena objects use:

StaticBody3D

└── CollisionShape3D

└── BoxShape3D

Walls and doors therefore have physical collision geometry in addition to their visible meshes.

Collision geometry copies the position and rotation of the associated visual object.

Rotated Wall Collision

Rotated walls require special handling because a door opening must remain aligned with the wall's local coordinate system.

The renderer transforms door positions into wall-local coordinates before calculating the required gap.

Conceptually:

World position


Wall-local coordinate system


Door gap calculation


Segmented wall

This prevents a rotated wall from producing incorrectly positioned door openings.

Structures

The renderer supports additional arena structures, including:

bridge
ramp

Bridge

A bridge can reference:

region_id
entry_region_id
exit_region_id
elevation

The bridge renderer creates:

  • an elevated bridge base
  • an entry ramp
  • an exit ramp

The bridge can therefore represent a raised section connecting different regions.

The current bridge implementation focuses on rendering the elevated structure and its ramps. The bridge base and ramps are not treated as generic wall collision geometry by the renderer.

Ramp

Ramps are created as thin box meshes rotated according to the required slope.

This allows elevated and sloped regions to be visually represented without requiring a separate terrain system.

DCR Roof

Regions marked:

"covered": true

can receive a DCR roof.

The renderer creates the roof at approximately the configured wall height and darkens the corresponding floor material.

This provides visual separation between covered and uncovered regions.

Stimuli

Stimuli are represented visually using:

  • a sphere marker
  • a radius indicator
  • a label

Light stimuli additionally create a Godot light source.

The renderer therefore provides both a visible stimulus location and an indication of the stimulus's effective radius.

Treatment Chamber

An optional treatment chamber can be rendered from the arena configuration.

The chamber can specify:

id
label
position
size
rotation_y
material_id
enabled
collidable

This allows treatment-related arena geometry to be represented directly in the 3D scene.

Material System

Materials are resolved using the material definitions maintained by the experiment system.

The renderer uses the material's visual properties, including:

albedo colour
roughness
metallic
transparency

The supported default material IDs are:

MaterialPurpose
solid_floorStandard arena floor
rubberRubber-like surface
wire_meshTransparent mesh-like surface
wood_chipNatural bedding surface
painted_steelMetal wall and structure surface
clear_acrylicTransparent enclosure surface

Unknown material IDs use the renderer's fallback appearance instead of causing the entire arena render to fail.

Transparency Handling

Transparent materials require explicit Godot transparency configuration.

The material renderer checks whether the configured transparency is greater than zero.

For transparent materials it enables:

BaseMaterial3D.TRANSPARENCY_ALPHA

and calculates the displayed alpha from the configured transparency value.

Conceptually:

transparency = 0.55

alpha = 1.0 - 0.55
= 0.45

This ensures materials such as clear_acrylic and wire_mesh are rendered as transparent rather than appearing as opaque surfaces.

Camera System

The arena supports three camera modes.

Orbit View

The orbit camera provides a conventional inspection view around the arena.

Controls:

Left drag → rotate
Right drag → pan
Wheel → zoom

The orbit pitch is constrained to prevent the camera from flipping.

The orbit distance is constrained between a minimum and maximum range.

Zooming towards the mouse position also adjusts the orbit target so that the view remains focused on the area being inspected.

Free Fly

Free-fly mode provides first-person style arena navigation.

Controls:

W/A/S/D → movement
Q/E → vertical movement
Shift → faster movement
Left drag → look around

The free camera uses configurable mouse sensitivity and horizontal/vertical inversion.

Follow Rat

Follow Rat mode keeps the camera positioned relative to the simulated rat.

The camera follows the rat using smoothing rather than snapping directly to every position update.

The follow camera supports:

Left drag → look around the rat
Wheel → change follow distance

The follow distance and pitch are constrained to sensible ranges.

Camera Collision

The camera performs a raycast from its target towards its desired position.

If an obstacle is detected, the camera is moved towards the collision point and offset slightly along the collision normal.

Conceptually:

Target

│ desired camera path

Obstacle


Collision point


Camera moved before entering obstacle

This prevents the camera from passing directly through arena geometry.

Camera Reset

Resetting the camera restores the default orbit configuration.

The default orbit configuration includes:

Distance: 8.8
Pitch: -55°
Yaw: 0°

The target is reset to the centre of the arena.

Renderer Responsibilities

The renderer is responsible for:

  • converting arena data into 3D geometry
  • creating floor regions
  • rendering labels
  • generating outer walls
  • generating inner walls
  • creating door openings
  • updating door visuals
  • enabling and disabling door collision
  • rendering structures
  • rendering stimuli
  • rendering treatment chambers
  • applying material properties
  • rendering transparent materials
  • providing camera controls
  • preventing camera geometry penetration
  • rebuilding the scene when the experiment snapshot changes

The renderer does not own the experiment configuration itself. It visualizes the configuration supplied by the application.

src/app/arena_3d.gd
src/app/arena_view.gd
src/app/panels/arena_panel.gd
src/app/panels/arena_panel.tscn
src/app/services/experiment_migrator.gd

The arena renderer therefore acts as the visual and interactive 3D layer between the experiment data model and the user interface.