17 KiB
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_ONLYdefault,CARVING_ONLY,ANYWHERE).ANYWHEREis 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 16–32 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 ofinterval(free spin).min == max != 0— locked to that angle.- otherwise — multiple of
intervalclipped into[min, max]. interval: 0fully free only in free-spin case; in a clipped range always set non-zerointerval. 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[].nameis a packloot/key;vanillaLoot[].nameis a vanilla or datapack loot-table key.- Per chest:
exact: truefull block-data match beats material match beats nofilter. One table picked byweight. overrideGlobalLoot: truesuppresses 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: 0 → surfaceSupportDepth: 1 → requireSurfaceSupport: false → force: 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
- Wrong key —
placepaths are case-sensitive. Missing keys skip the placement attempt and log a loader warning. - Chance too low — prove wiring with
"chance": 1, "density": 4, then dial back. - Not actually in that biome — chunk-center sample decides.
- Wrong list —
CARVING_ONLYnever places on surface;SURFACE_ONLY(default) never places in caves. - Surface support rejection (section 7) — if it appears with
force: truebut not without, a guard is the cause. - Underwater — submerged surface placement needs
underwater/onwater. clamptoo tight — internal Y, not world Y.slopeConditiontoo strict —maximumSlopebelow ~1 excludes most terrain.- Native structure veto — objects never place into native structure pieces, even with
force. - 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 orVACUUM. - Buried →
MIN_HEIGHTor too much negativetranslate.y. - Floating in a cave →
ORGANIC_STILT(floor) orCEILING_HANG(roof) withcarvingSupport: "CARVING_ONLY". - Flat stilt disc →
VACUUM/VACUUM_ORGANIC/VACUUM_WAVY. - Rides on grass →
translate.y: -1orPAINT. - Interior filled with terrain →
boreorsmartBore.
Loot not filling
- Only storage chests receive placement loot.
- Console:
Couldn't find loot table. exact: truewith mismatched block data — drop toexact: 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 pastefirst. - Deep forensics: write
chunkX,chunkZ[,radius]intoplugins/Iris/goldendebug.txt— logs every attempt, pick, and rejection for those chunks. Extremely verbose; use radius 0.