Files
Iris/docs/20 - Object Placement.md
T
2026-08-08 00:29:48 -06:00

17 KiB
Raw Blame History

20 - Object Placement

Object placements wire a saved object (objects/<key>.iob) into biome or region JSON so the generator stamps it. Creating objects is 19 - Objects.md; multi-piece assemblies are 21 - Jigsaw Structures.md.

1. Where placements go

objects[] exists on exactly two resource types:

  • Biome (biomes/**.json) — where that biome generates.
  • Region (regions/**.json) — every biome in the region.

Dimensions do not have objects[]. An objects array on a dimension is ignored at generation time. Dimensions carry surface-support knobs (requireObjectSurfaceSupport, objectSurfaceSupportBuffer, section 7) and upper-dimension object controls (upperDimensionObjects, upperObjectsForcePlace), but no placements of their own.

Scope and frequency

Per chunk, biome and region are sampled once at the chunk center (block 8,8), then every placement entry in the biome list, then the region list, rolls independently.

  • Biome vs region differ only in breadth; chance/density math is identical; both can fire in the same chunk.
  • A biome that only occupies a corner contributes nothing to that chunk; the center biome's objects can spill slightly into neighbors.
  • Each entry belongs to the surface list, the cave list, or both via carvingSupport (SURFACE_ONLY default, CARVING_ONLY, ANYWHERE). ANYWHERE is in both lists and rolls chance twice per chunk. Cave placements probe for a cave biome below the surface and fall back to the surface biome.

Minimal entry

{
  "objects": [
    {
      "place": ["clutter/boulder1"],
      "chance": 0.05
    }
  ]
}

place entries are object keys (path under objects/ without .iob). A whole placement can also be a snippet reference ("snippet/object-placer/<name>").

2. Frequency: chance, density, densityStyle

Field Default Meaning
chance 1 Rolled once per chunk per entry (0..1). Surface entries add ±0.005 jitter, so use omission — not chance: 0 — to disable a surface entry. Cave entries use the raw chance (no jitter).
density 1 Placement attempts in the chunk once chance passes. Each attempt picks a random object from place and a random column. Attempts can still be vetoed — density is not a guarantee.
densityStyle unset Noise-driven range that replaces density when present. Class defaults are min 16 / max 32, so an empty densityStyle: {} means 1632 attempts per chunk.

Expected objects per chunk ≈ chance × density, before rejections. chance: 0.002 is roughly one per 500 chunks.

place has no weights — uniform pick. To bias one object, list its key multiple times or split entries. An unresolvable key skips the attempt; ObjectResourceLoader logs a warning for each failed lookup.

3. Placement modes

mode (default CENTER_HEIGHT) decides how the object meets terrain. All 21 values:

Height sampling

Mode Meaning
CENTER_HEIGHT One height sample at the center; whole object uses it. Default.
MAX_HEIGHT / FAST_MAX_HEIGHT Highest sample across footprint (FAST = 4 samples). Never buried; floats off cliffs.
MIN_HEIGHT / FAST_MIN_HEIGHT Lowest sample. Never overhangs; buries into slopes.
PAINT Every column pushed to terrain surface — melts the object over terrain. Common for ground clutter.

Stilts (extend bottom blocks down; tuned by stiltSettings)

Mode Meaning
STILT / FAST_STILT MAX_HEIGHT + stilt columns (FAST cheaper, less accurate — combine with overStilt).
MIN_STILT / FAST_MIN_STILT MIN_HEIGHT + stilts.
CENTER_STILT CENTER_HEIGHT + stilts. Cheapest generally useful stilt mode.
ERODE_STILT Cone-tapered stilts: center deepest, edges drop off, lower parts randomly broken.
ORGANIC_STILT Scans down to first solid (cave floor or terrain); fills with object's bottom blocks, noise-varied. For cave floor connection. Tune with organicMaxScan, organicJitter, organicScratch.
CEILING_HANG Flips object and anchors to cave ceiling with organic stilt into the roof.

Terrain shaping (modify terrain; tuned by vacuumSettings)

Mode Meaning
VACUUM Anchors like CENTER_HEIGHT, bends surrounding terrain flush with base (default radius 12, parabolic).
VACUUM_HIGH Radius 20, finest blend. More expensive.
VACUUM_FAST Radius 8, coarser sampling.
VACUUM_ORGANIC Falloff radius perturbed per column.
VACUUM_WAVY Bend modulated by simplex noise (waveAmplitude, waveScale).

Special

Mode Meaning
FLOATING Pure-air placement: Y from rotated object center height plus translate.y (+ yRandom). Skips terrain, water, and cave checks. Floating islands, sky structures.
STRUCTURE_PIECE Raw stamp at caller coordinates. Internal for structure pieces. Do not author in objects[].

4. Field reference

Anchoring and water

Field Default Meaning
carvingSupport SURFACE_ONLY SURFACE_ONLY / CARVING_ONLY / ANYWHERE.
caveAnchorMode PROFILE_DEFAULT Cave: FLOOR, CEILING, CENTER, ANY, or carving profile default.
heightmap unset Noise generator used as a virtual heightmap instead of terrain.
bottom false With explicit Y (cave/structure/tree paths), place from ground up. Incompatible with X/Z rotation.
fromBottom false Place from world bottom up. Incomplete path — avoid.
underwater false Place on terrain height, ignoring water surface. Without it (or onwater), submerged surface placements are rejected.
onwater false Place on fluid surface (boats).
waterloggable false Waterlog placed blocks that sit in water.
isDolphinTarget false With underwater, marks placed storage chests as buried-treasure POI for dolphins.

Terrain interaction

Field Default Meaning
bore false Clear bounding cuboid to air before placing. boreExtendMaxY / boreExtendMinY expand the box.
smartBore false Raytraced interior fill — clears rooms/pockets. Slows object loading, not warmed-up generation.
meld false Place only where blocks already exist. Expensive.
warp flat Generator style warping placement coordinates per block.
snow 0 0..1 — snow layers on top of placed columns.
edit [] Find-and-replace at placement: find[], replace (palette), exact, chance.

Collisions and force

Field Default Meaning
forbiddenCollisions [] Object keys this object may not intersect; match inside bounding box rejects (unless also in allowedCollisions).
allowedCollisions [] Exceptions to the above.
forcePlace false JSON also accepts "force". Bypasses slope, surface-support, underwater, clamp, bedrock, and collision guards. Does not bypass native-structure veto (objects never overwrite native structure pieces).

Slope

Field Default Meaning
slopeCondition {minimumSlope: 0, maximumSlope: 10} Slope gate, 3-block radius. Defaults mean no condition; tighten maximumSlope (e.g. 2) to keep buildings off hills.
rotateTowardsSlope false Adds downhill direction (rounded to 90°) to Y rotation.

rotation

Default already enables Y free 90° steps — objects get random cardinal rotation with no config. Common pack form:

{
"rotation": { "enabled": true, "yAxis": { "enabled": true, "min": 0, "max": 270, "interval": 90 } }
}

Per-axis (xAxis / yAxis / zAxis, each {enabled, min, max, interval}):

  • min == max == 0 — any multiple of interval (free spin).
  • min == max != 0 — locked to that angle.
  • otherwise — multiple of interval clipped into [min, max].
  • interval: 0 fully free only in free-spin case; in a clipped range always set non-zero interval. Non-90° intervals usually look bad at block resolution.

Disable with "rotation": {"enabled": false}. X/Z rotation is incompatible with bottom: true.

translate and scale

{
"translate": { "x": 0, "y": -1, "z": 0, "yRandom": 0 }
}

translate.y: -1 seats clutter into the ground. Translate rotates with the object. yRandom adds rand(0..yRandom) height per placement.

{
"scale": { "size": 1, "minimumScale": 0.75, "maximumScale": 1.25, "variations": 7, "interpolation": "TRILINEAR" }
}

Defaults: size, minimumScale, maximumScale all 1; interpolation NONE — inert until you set size or widen min/max. size != 1 is a fixed multiplier and overrides the range; at size: 1 a random scale is picked from the range, quantized into variations (default 7) cached variants. Upscale interpolators: NONE, TRILINEAR, TRICUBIC, TRIHERMITE.

clamp

{
"clamp": { "minimumHeight": 40, "maximumHeight": 225 }
}

Rejects placements whose resolved bottom/top fall outside the band. Field defaults -2048 / 2048. Heights are engine-internal Y (0..dimension height), not necessarily world Y.

stiltSettings (all *_STILT modes and CEILING_HANG)

Field Default Meaning
yMax 0 Max stilt height before overstilt/random range.
yRand 0 Extra random stilt depth.
overStilt 0 Extra depth into ground (useful with FAST_STILT).
palette unset Column palette; default repeats object's bottom block (grass-family bottoms substitute dirt). Stops at first fluid.
organicMaxScan 48 ORGANIC_STILT / CEILING_HANG max scan.
organicJitter 3 Random per-column shortening.
organicScratch 0.55 Fraction of deepest part randomly broken.

vacuumSettings (all VACUUM* modes)

Field Default Meaning
radius 0 (auto: 12 / 20 high / 8 fast) Deformation extent past footprint.
falloff 2.0 Easing: 1 = cone, 2 = parabolic, higher = flatter near object.
organicJitter 4 VACUUM_ORGANIC radius perturbation.
waveAmplitude 3 VACUUM_WAVY wave height.
waveScale 5.0 VACUUM_WAVY frequency (~100/waveScale blocks wavelength).

5. Loot

Two arrays, matched against container blocks inside the placed object. Loot injects lazily when a chest is first opened, from the placement recorded in the mantle — only storage chests receive it.

{
"loot": [
  { "name": "global-treasure", "filter": [{ "block": "minecraft:chest" }], "exact": false, "weight": 3 },
  { "name": "global-tools", "weight": 1 }
],
"vanillaLoot": [
  { "name": "minecraft:chests/simple_dungeon", "weight": 1 }
],
"overrideGlobalLoot": false
}
  • loot[].name is a pack loot/ key; vanillaLoot[].name is a vanilla or datapack loot-table key.
  • Per chest: exact: true full block-data match beats material match beats no filter. One table picked by weight.
  • overrideGlobalLoot: true suppresses dimension/region/biome loot for containers this placement matched.
  • Unresolvable loot name logs Couldn't find loot table <name>.

Independent of this, a chest saved into the .iob with a vanilla loot table already on it keeps that table (19 - Objects.md).

6. Markers: entity spawns on placed objects

Placements have no direct entity field. markers[] tags matching blocks with a marker resource; the marker (markers/) carries spawners[]:

{
"markers": [
  { "mark": [{ "block": "minecraft:mossy_cobblestone" }], "marker": "camp-spawns", "maximumMarkers": 4, "exact": false }
]
}

markers/camp-spawns.json references spawner resources; emptyAbove (default true) requires two air blocks above the marked block.

7. Surface support

Iris refuses surface objects that roof over, bridge, or overhang a carved opening. It rasterizes the object's lowest solid layer, dilates by surfaceSupportBuffer, and requires every column in that stencil to have surfaceSupportDepth blocks of un-carved, surface-solid ground. Failure rejects with no log line — common cause of "object never appears" near caves and canyon rims.

Field Where Default Meaning
requireSurfaceSupport placement true Guard off for this placement.
surfaceSupportBuffer placement 2 (0..16) Extra solid ring around footprint.
surfaceSupportDepth placement 2 (1..16) Required un-carved ground thickness.
requireObjectSurfaceSupport dimension true false disables guard pack-wide.
objectSurfaceSupportBuffer dimension 2 Floor for every placement's buffer — can widen, never narrow.

Skipped for: force, fromBottom, mode: FLOATING, STRUCTURE_PIECE, underwater, onwater, cave-anchored placements, and requireSurfaceSupport: false.

Escape hatches: surfaceSupportBuffer: 0surfaceSupportDepth: 1requireSurfaceSupport: falseforce: true → dimension-wide off.

A related guard rejects surface placements resolving to y ≤ 1 in bedrock dimensions and does log (throttled): Implausible object placement rejected.

8. Worked examples

Rare surface camp:

{
  "place": ["clutter/camp1"],
  "chance": 0.00175,
  "rotation": { "enabled": true, "yAxis": { "enabled": true, "min": 0, "max": 270, "interval": 90 } }
}

Trees with slope gate, warp, snow:

{
  "place": ["trees/spruce/pine1", "trees/spruce/pine2", "trees/spruce/pine3"],
  "chance": 0.6,
  "density": 1,
  "slopeCondition": { "maximumSlope": 2 },
  "rotation": { "enabled": true, "yAxis": { "enabled": true, "interval": 90, "min": 0, "max": 270 } },
  "warp": { "style": "IRIS_DOUBLE", "exponent": 1.2, "zoom": 0.4 },
  "snow": 0.1
}

Ruins on stilts with loot:

{
  "place": ["structures/ruin-small-a", "structures/ruin-small-b"],
  "chance": 0.08,
  "density": 2,
  "mode": "CENTER_STILT",
  "stiltSettings": { "yMax": 4, "yRand": 1, "overStilt": 1 },
  "translate": { "y": -1 },
  "slopeCondition": { "maximumSlope": 3 },
  "loot": [
    { "name": "global-treasure", "filter": [{ "block": "minecraft:chest" }], "weight": 2 },
    { "name": "global-clutter", "weight": 1 }
  ]
}

Cave floor clutter:

{
  "chance": 0.21,
  "density": 6,
  "carvingSupport": "CARVING_ONLY",
  "caveAnchorMode": "PROFILE_DEFAULT",
  "translate": { "y": -1 },
  "place": ["clutter/stoneclutt4", "clutter/stoneclutt5"],
  "bottom": true,
  "force": true,
  "scale": { "size": 0.75 }
}

Ground-hugging carpet:

{
  "chance": 0.15,
  "density": 2,
  "mode": "PAINT",
  "translate": { "y": -1 },
  "rotation": { "enabled": true, "yAxis": { "enabled": true, "min": 0, "max": 270, "interval": 90 } },
  "place": ["trees/mushroom/mushclut1", "trees/mushroom/mushclut2"]
}

9. Trees

When a sapling grows, Iris scans the biome's (then region's) objects[] for placements whose trees[] matches the grown tree type and sapling square size, picks one, and stamps it. A placement can serve both generation and sapling override. Procedural tree generation (proceduralObjects) is separate.

10. Troubleshooting

Never appears

  1. Wrong key — place paths are case-sensitive. Missing keys skip the placement attempt and log a loader warning.
  2. Chance too low — prove wiring with "chance": 1, "density": 4, then dial back.
  3. Not actually in that biome — chunk-center sample decides.
  4. Wrong list — CARVING_ONLY never places on surface; SURFACE_ONLY (default) never places in caves.
  5. Surface support rejection (section 7) — if it appears with force: true but not without, a guard is the cause.
  6. Underwater — submerged surface placement needs underwater / onwater.
  7. clamp too tight — internal Y, not world Y.
  8. slopeCondition too strict — maximumSlope below ~1 excludes most terrain.
  9. Native structure veto — objects never place into native structure pieces, even with force.
  10. Console: Implausible object placement rejected (bedrock y≤1); loot failures: Couldn't find loot table.

Floats or clips

  • Floats off cliffs → MAX_HEIGHT; use a stilt mode or VACUUM.
  • Buried → MIN_HEIGHT or too much negative translate.y.
  • Floating in a cave → ORGANIC_STILT (floor) or CEILING_HANG (roof) with carvingSupport: "CARVING_ONLY".
  • Flat stilt disc → VACUUM / VACUUM_ORGANIC / VACUUM_WAVY.
  • Rides on grass → translate.y: -1 or PAINT.
  • Interior filled with terrain → bore or smartBore.

Loot not filling

  • Only storage chests receive placement loot.
  • Console: Couldn't find loot table.
  • exact: true with mismatched block data — drop to exact: false.

Iterating quickly

  • Studio worlds hotload pack edits (JSON and .iob) within about a second — newly generated chunks only.
  • Non-studio worlds do not hotload.
  • Separate object vs placement: /iris object paste first.
  • Deep forensics: write chunkX,chunkZ[,radius] into plugins/Iris/goldendebug.txt — logs every attempt, pick, and rejection for those chunks. Extremely verbose; use radius 0.