Skip to content

Grid Placement v6.0

3D Object Placement

Click-to-place on a GridMap, snap families, CELL/EDGE/FACE mounts, and ground snapping.

Status
Draft
Version
v6.0
Updated
Development docs generated from GDScript source

This is unreleased documentation in active development. APIs, class names, and behavior may change before the final release.

Grid Placement 6.0 supports 3D object placement on GridMap in two coordinate modes:

  • GRID — cell- and mount-aligned placement for structures and other grid-aligned objects.
  • SMOOTH — free world-space placement with optional world-space sockets.

3D terrain editing/painting remains outside the supported public addon contract.

Choose the placement mode first

Use GRID for floors, walls, fences, windows, modular structures, or anything else that must align to cells or structural mounts.

Use SMOOTH for clutter, organic props, furniture, or object-to-object joins that should not be tied to GridMap cells.

See Grid vs Smooth Placement for the detailed tradeoff.

GRID placement lifecycle

A normal GRID placement follows this path:

  1. Select a ScenePlacementEntry.
  2. Target a GridMap cell or mount.
  3. Show the placement preview and validation feedback.
  4. Rotate yaw when the entry allows it.
  5. Confirm placement.
  6. Record occupancy, mount data, transform, and stable placement identity.
  7. Move, remove, save, or restore the object through the same placement world.

Preview and commit use the same placement result. If the world changes before confirmation, the final validation result decides whether placement succeeds.

CELL, EDGE, FACE, CORNER, and TOP

PlacementSnapProfile3D.SnapMode controls where a GRID entry attaches.

Mode Meaning Typical use
CELL Occupies a GridMap cell or footprint floor, foundation, crate
EDGE Occupies a canonical edge between neighboring cells wall, fence panel, door frame
FACE Mounts to a supported provider face window, shutter, wall decoration
CORNER Occupies a canonical XZ grid intersection structural corner post
TOP Occupies the top of a host EDGE, or the cell-centered ridge roof plane, gable, ridge cap

EDGE/FACE occupancy is canonical from either neighboring cell, so the same physical edge cannot be double-booked by addressing it from the other side. TOP wall-tops use that same canonical EDGE key in a separate occupancy table, so a wall and its roof coexist. Ridge uses edge (0, 0, 0) and requires opposite roof tops on the cell.

CORNER uses the shared grid intersection. A CORNER entry needs two perpendicular hosts at that intersection. Their saved yaw directions must differ by an odd number of quarter turns, so one host runs along X and the other along Z. One host or two parallel hosts is invalid.

TOP is keyed to a host wall edge — the same canonical EDGE key, held in a separate occupancy table — or to the cell-centered ridge. A TOP placement is rejected when that edge holds no host wall, so TOP seats a roof or an upper wall on a wall, not a small item on the top face of an arbitrary prop. In 2D the surface equivalent is the separate opt-in surface contract described in Placement Rules, which takes a declared host entry; the 2D preview/commit path then resolves sockets automatically.

Cursor picking for EDGE, FACE, and TOP

PlacementInputHandler3D turns a cursor over a cell into the mount the ghost should show. It keeps the picked wall line steady against cursor tremor, mounts an aimed wall face on its own slot, hosts a room's boundary wall on the floored side, continues an existing same-family run, and resolves an occupied slot to the piece already standing there so replacements preview in place. TOP picks hold the chosen roof slope or ridge the same way.

Configure it once with the GridMap, the ObjectPlacementService3D, and optionally the GridPositioner3D whose physics hit it prefers. Each frame, pass the cell and the cursor's point on the cell's surface plane (Vector3.INF when there is no usable ray):

var handler:= PlacementInputHandler3D.new()
handler.configure(grid_map, object_service, positioner)
var mount:= handler.resolve_edge_mount(cell, plane_hit)# [mount_cell, edge]
object_service.update_preview_edge(mount[0], mount[1])

Input events, rotate keys, and the commit stay in your game code. Set wall_axis_lock (or call toggle_wall_axis) from your rotate keys, and call reset() when a gesture is cancelled or the cursor leaves the grid. The 3D demo uses it this way.

FACE providers

A provider must explicitly expose mountable faces. The consumer's snap profile must accept the provider family. FACE save data preserves the provider, mount identity, and orientation so restore can rebuild the same relationship.

Preview object vs mount indicator

The placement preview (sometimes called the ghost) shows where the object will be placed.

The indicator shows what is being validated:

  • CELL — the evaluated cell or footprint support;
  • EDGE — the claimed edge at its structural height;
  • FACE — the provider face;
  • CORNER — the claimed grid intersection;
  • TOP — the host EDGE or cell-centered ridge.

Both come from the same placement result. An elevated mount should therefore be highlighted at its actual height instead of pointing at unrelated ground below it.

Use the shipped 3D demo as the reference for floor → CELL wall → FACE-mounted piece placement and for House Corner placement at wall intersections.

Optional blocked patterns

Color-only previews remain the default. In the 3D demo, enable Blocked preview stripes to add diagonal bands to the invalid-color tint. Valid previews keep their normal tint. Turn it off to return to flat colors.

For your own integration, set ObjectPlacementSettings3D.blocked_feedback to BlockedFeedback.STRIPES (or FLAT_COLOR). The same settings resource controls GRID ghosts, mount/footprint indicators, and host-owned SMOOTH ghosts. You can assign blocked_pattern_texture to replace the built-in grayscale pattern; the invalid color still tints it. Standalone SMOOTH services use attachment_settings for this resource.

Patterns follow the existing validity result, including when valid and invalid colors are identical. They do not change placement rules. Keep text feedback for failure reasons and check contrast against your own scenes.

Snap families

PlacementSnapProfile3D lets entries describe what they provide and what they can attach to.

Field Purpose
family Category published by the placed object.
snaps_with Provider families this entry may attach to.
attachment_requirement INHERIT (default, uses the settings default), OPTIONAL (free placement), or REQUIRED (must satisfy the mount's normal compatible-attachment rule).
corner_host_topology CORNER-only: INHERIT (default), ANY_ONE (one compatible host), or TWO_ORTHOGONAL (strict two-host contract).
edge_dressing EDGE-only: DEFAULT keeps along-edge yaw for symmetric pieces; OUTWARD and INWARD orient asymmetric pieces' dressed (+Z) face away from or toward the host side.
edge_outward_facing Legacy compatibility alias. When edge_dressing remains DEFAULT, true resolves to OUTWARD.
require_edge_span_floor_support EDGE-only, default false. When enabled, every host-side tile touched by the authored EDGE span must publish the floor family.

The consumer decides what it accepts. You can keep names broad (wall, floor) or use project-specific families without changing plugin code.

Attachment strictness is one shared policy across CELL/EDGE/FACE/CORNER/TOP and SMOOTH socket consumption: mount identity (where the piece sits) never changes with the policy — only whether a compatible provider must be present. The legacy require_adjacent_snap boolean is deprecated but still resolves identically (false → OPTIONAL, true → REQUIRED); set attachment_requirement explicitly on new profiles.

Setting up a placeable 3D object

A basic GRID placeable needs three pieces of setup:

  1. Packed scene — the object scene used when placement commits. Use the shipped demos/3d/objects/ scenes as component-layout references.
  2. ScenePlacementEntry — points to the packed scene and defines its placement data.
  3. Catalog registration — add the entry to the PlacementCatalog used by the session so it can be selected.

For the ScenePlacementEntry, configure the fields that apply to the object:

  • footprint_3d — occupied cells for each yaw step;
  • snap_profile_3d — family, snaps_with, attachment_requirement, and snap_mode;
  • seam_bounds_3d — only when the snap profile enables seam_close_to_partner and the asset needs mesh-aware seam closure.

Choose the mount mode from the object's actual placement behavior: CELL for cell/footprint pieces, EDGE for edge panels, FACE for provider-mounted pieces, CORNER for intersection posts, and TOP for roof planes, gables, or ridge caps.

The shipped demo provides copyable references in demos/3d/placement/placeables/*.tres, demos/3d/config/3d_catalog.tres, and demos/3d/demo_3d.tscn. When adapting one, replace the scene and update its footprint, snap profile, and seam bounds for your own asset instead of only swapping the mesh.

In the demo, selecting an entry shows the preview and mount-aware indicator. You can then rotate, place, move, demolish, save, and restore the committed object through the normal placement workflow.

Footprints and rotation

ScenePlacementEntry.footprint_3d defines the CELL GRID footprint. CELL occupancy and support validation apply to the complete rotated footprint. EDGE pieces instead use footprint_3d_world for their span; set require_edge_span_floor_support only on structural entries that must have floor under every host-side tile they cover.

GRID structures use discrete yaw rotation. Structural pieces remain world-up by default; terrain slope does not automatically tilt walls or foundations.

Ground, slope, and partial support

GRID placement can validate the support below a footprint. Different placeables can use different support rules.

Typical controls include:

  • maximum allowed ground slope;
  • support, height, and planarity tolerance;
  • minimum support coverage;
  • optional center or edge support requirements;
  • stricter physics-required support when the project needs it.

For example, a small prop can allow some overhang while a building foundation requires stronger coverage. Configure that policy on the relevant entry instead of weakening support rules globally.

See 3D Surface & Slope Support.

Foundations, and placing rooms on top of them

Surface support answers "is there ground here". A different question is "is there a placed piece underneath me" — the foundation-under-room rule. That is require_column_support on the snap profile, and it is not part of snaps_with.

Why snaps_with is not enough

snaps_with under CELL attachment checks the four cardinal neighbours in the same layer. A foundation one cell to the north satisfies it. So a room configured only with snaps_with will happily attach to a floor beside it and stand on bare ground:

.foundation .floor .room     <- the room is attached, but nothing is under it

require_column_support checks the same column instead. Every footprint cell needs a COLUMN provider directly underneath it — that column's own occupant, or any overlay stacked on it — publishing a family in snaps_with. A neighbouring foundation does not count.

Setting it up

Provider (the foundation):

Field Value
family foundation
snaps_with empty
occupy_columns true

Consumer (the room):

Field Value
family room
snaps_with ["foundation", "floor"]
attachment_requirement REQUIRED
require_column_support true
occupy_columns false

occupy_columns = false on the room matters. A foundation that occupies its columns already owns them, so a room with occupy_columns = true is refused CELL_OCCUPIED before support is ever asked. The room becomes an overlay stacked on the foundation's column — which is how the shipped floors and roofs already work. Either channel counts as the provider, so an occupying foundation and a non-occupying floor deck are interchangeable.

A 2x2 foundation carries all four of its cells, because each cell of the provider's own footprint counts.

What the player sees

Situation Preview Commit
No foundation under the cell invalid refused
Foundation under the cell valid accepted
Foundation in the next cell only invalid refused
Supported cell already holding a room invalid refused

The refusal names the rule, and the reason bit is PlacementValidation3D.Reason.NO_COLUMN_SUPPORT:

'Room' (family 'room') needs a foundation underneath every covered tile: the column below each cell must hold ["foundation", "floor"].

Preview and commit read the same validator, so a green preview is a commit that will succeed.

Removing or moving a foundation a room still needs is refused rather than leaving the room stranded. Clear or move the room first.

The 3D demo ships this as a worked example: the Room entry (demos/3d/placement/placeables/placeable_room.tres) drops onto the Wagon foundation.

Stacked floors

Upper floors use a different mount. A TOP piece needs a host published by an already-committed piece, and there is no anchor-only TOP socket — so an upper floor over open ground is refused with TOP_REQUIRES_EDGE. A cell-centered floor deck needs an opposite EDGE wall pair (east+west, or north+south), and the walls themselves mount on a cell publishing floor. Once the deck is committed the slot is exclusive.

require_column_support applies to CELL pieces; TOP sockets resolve their hosts through the TOP rules above.

Fences and modular structures

GRID CELL/EDGE/FACE/CORNER/TOP is the supported path for cell-aligned modular structures.

A structure can combine:

  • CELL floor/foundation pieces;
  • EDGE walls/fences/door pieces;
  • walls meeting at 90-degree EDGE corners;
  • FACE-mounted pieces such as windows/shutters;
  • CORNER-mounted structural posts where the kit uses them;
  • TOP-mounted roof planes, gables, and a cell-centered ridge;
  • removal and re-placement;
  • save → clear → restore → continue.

TOP occupancy is keyed by the host EDGE (separate table), so a wall and its roof can coexist. Ridge uses edge (0,0,0) and requires opposite roof tops. Snap-family names still do not create a new mount type.

The shipped 3D demo's modular house kit uses these placement paths for its floor deck, walls, door, shuttered windows, and corner posts.

Sized pieces and wall runs

Piece sizes are authored, not resized at runtime: there are no scale handles. To offer a longer wall, ship a bigger catalog variant (the demo kit has 1 m, 2x1, and 2x2 wall entries) and let players chain runs with the EDGE line drag, which mounts one panel per authored span width along the dragged run.

EDGE mounting also validates spans: collinear penetration and true pass-through crossings are rejected, while end-to-end joins, 90-degree corners, and T-junctions remain legal. Same-anchor replacement is unaffected. Span size comes from the entry's footprint_3d_world (x = span along the edge, z = thickness across it), positioned at the measured mesh. Structural entries can set require_edge_span_floor_support to require floor under every host-side tile touched by that span; empty-yard fences and wall chaining remain anchor-only unless they opt in.

Line and rectangle fills

ObjectFillSpan3D turns one drag between two grid cells into the pieces a LINE, RECTANGLE_FILL, or RECTANGLE_OUTLINE brush places, and commits them through ObjectPlacementService3D in order. CELL entries place one piece per span cell; EDGE entries mount one piece per authored span width and keep the pressed wall's rotation along the run. Your game supplies the cursor: set the drag corners, the pressed EDGE mount if any, and optional cursor_edge / cursor_mount callables for drags with no direction.

var fill:= ObjectFillSpan3D.new(service)
fill.brush_shape= PlacementEnums.ObjectTool.RECTANGLE_FILL
fill.start_cell= press_cell
fill.end_cell= hover_cell
fill.max_cells= 64
var cells:= fill.cells()# empty when the drag exceeds max_cells
var verdicts:= fill.cell_verdicts(fill.cells_3d(cells))# preview, in commit order
var result:= fill.commit(cells)# on release
print("placed%d of%d" % [result.committed, result.attempted])

cell_verdicts judges each anchor the way the commit will, so a 2x2 piece dragged in a line previews the anchors its own footprint covers as blocked. For EDGE entries, preview_edges(cells) tints the service's span ghosts. The 3D demo drives its fill drags through this class.

Roofs and gables

TOP is the supported roof and gable mount in the 6.2.0 contract. Roof and gable entries attach to a host EDGE, while the ridge is cell-centered and requires the opposite roof tops described above.

Do not use an arbitrary snap-family name to imply a new mount type.

SMOOTH world-space sockets

SMOOTH can optionally publish and consume world-space sockets for joins that should not use GRID CELL/EDGE/FACE/CORNER/TOP keys.

Common socket roles include ends, perimeter points, faces, and upward points. A consumer still uses family and snaps_with to choose compatible providers.

The default SMOOTH behavior remains free placement. Socket snapping is opt-in through the entry/profile configuration.

GRID mounts and SMOOTH sockets use different occupancy identities:

  • GRID mounts use canonical cell, edge, face, corner, or TOP keys.
  • SMOOTH sockets use provider identity plus world-space socket identity.

See Smooth Placement.

Validation failures

A 3D placement can reject for reasons such as:

  • missing or invalid active entry;
  • occupied footprint or mount;
  • unhosted or occupied corner intersection;
  • incompatible snap family or provider;
  • missing required ground or support;
  • slope/support policy failure;
  • collision or overlap conflict;
  • missing physics evidence when a physics-required policy is selected.

Use the report-producing placement APIs when your UI or game needs typed failure information. Keep surface/support failures distinct from occupancy failures so the player receives the correct feedback.

When a placement is rejected as "no ground", run the evidence-side checklist before touching policy: confirm the GridMap scan range covers the terrain's Y layers, that the footprint columns actually hold support cells, and that the support mode matches your world's evidence (GridMap-authored worlds usually want a GridMap-capable mode, not PHYSICS_REQUIRED). The Core validation by workflow table names the policy side of the same check.

Persistence

Supported 3D placements keep stable placement identity plus the transform, occupancy, and mount data needed to restore the same world state. GRID, SMOOTH, CELL/EDGE/FACE/CORNER/TOP mounts, sockets, and shared occupancy are restored together.

See Save and Load.

Supported boundaries

  • 3D object placement: supported.
  • GRID + SMOOTH: supported.
  • Generic GRID roof/top mount: supported as SnapMode.TOP (wall-top and ridge). SMOOTH UP sockets remain the free-placement path.
  • CELL / EDGE / FACE / CORNER / TOP structure workflows: supported.
  • Configurable GRID slope/support validation: supported.
  • Automatic structural GRID pitch/roll to follow terrain: not supported by default.
  • 3D terrain editing/painting: supported (paint/erase, LINE/rectangle brushes, game-authored rule evaluation; FLOOD_FILL not supported in 3D).

Related guides