# How Arnis Building Interior Generation Works: Rooms, Layouts, and Doorways

> Discover how Arnis building interior generation creates rooms, layouts, and doorways using tiled character maps. Learn about door and wall block placement.

- Repository: [Louis Erbkamm/arnis](https://github.com/louis-e/arnis)
- Tags: internals
- Published: 2026-03-20

---

**The Arnis building interior generation module creates deterministic room layouts by tiling 23×23 character pattern maps across each floor, mapping characters like `'D'` to door blocks and `'W'` to interior walls, then extending walls and doorframes vertically to the ceiling.**

Arnis is an open-source tool that transforms real-world geospatial data into detailed Minecraft worlds. Its building interior generation system, implemented in [`src/element_processing/subprocessor/buildings_interior.rs`](https://github.com/louis-e/arnis/blob/main/src/element_processing/subprocessor/buildings_interior.rs), converts empty building shells into furnished structures with realistic room divisions, doorways, and multi-floor layouts.

## Preparation and Floor Selection

Before generating rooms, the system validates that a building can support an interior. In `generate_building_interior` (lines 99-101), the code immediately returns for any structure smaller than 8×8 blocks:

```rust
if width < 8 || depth < 8 {
    return;
}

```

For valid buildings, the system converts the floor footprint into a `HashSet<(i32, i32)>` for O(1) membership tests (lines 103-105). It then applies a **2-block buffer** from the outer walls to ensure furniture never touches the perimeter, calculating interior bounds as `interior_min_x = min_x + buffer` and `interior_max_x = max_x - buffer` (lines 107-113).

## Pattern-Based Tiling System

The core room generation uses **23×23 character pattern maps** that are tiled across each floor. Each floor is processed using two layers: one for the base room layout and a second for upper elements like door tops and tall furniture.

### Floor-Specific Pattern Selection

The system selects different patterns based on floor level and building condition (lines 138-152):

- **Ground floor**: Uses `INTERIOR1_LAYER1` and `INTERIOR1_LAYER2`
- **Upper floors**: Uses `INTERIOR2_LAYER1` and `INTERIOR2_LAYER2`
- **Abandoned buildings**: Uses `ABANDONED_LAYER1` and `ABANDONED_LAYER2`

### Seamless Tiling with Modulo Arithmetic

To cover arbitrarily large rooms without seams, the code uses modulo arithmetic with the floor index as an offset (lines 170-176):

```rust
let pattern_x = ((x - interior_min_x + floor_index as i32) % pattern_width
                + pattern_width) % pattern_width;
let pattern_z = ((z - interior_min_z + floor_index as i32) % pattern_height
                + pattern_height) % pattern_height;

```

This ensures that the 23×23 motif repeats deterministically across the interior space while shifting slightly per floor to create variety.

### Character-to-Block Mapping

Each character in the pattern maps to a specific Minecraft block via `get_interior_block` (lines 30-74):

- **`'W'`** → The building's wall block (interior walls match exterior material)
- **`'D'`** → **Dark oak doors**; layer 1 creates `DARK_OAK_DOOR_LOWER` and layer 2 creates `DARK_OAK_DOOR_UPPER` (lines 53-60)
- **`'U'`** → Oak fence (often used for table legs or railings)
- **`'S'`** → Oak stairs (furniture or seating)
- **`'B'`** → Bookshelf
- **`'C'`** → Chest
- **`' '`** (space) → Air (empty floor space)

Blocks are placed using `editor.set_block_absolute` at `floor_y + y_offset` for layer 1 and `floor_y + 1` for layer 2 (lines 82-92).

## Doorway and Wall Extension Logic

After tiling the floor pattern, the system ensures walls and doorways extend vertically to the ceiling. During the first layer placement, the code records positions:

- **`wall_positions`** stores coordinates where `'W'` was placed (lines 92-99)
- **`door_positions`** stores coordinates where `'D'` was placed

After processing both layers, a vertical extension loop stacks wall blocks from the floor up to the ceiling (lines 166-170):

```rust
for (x, z) in &wall_positions {
    for y in (floor_y + y_offset + 2)..=current_floor_ceiling {
        editor.set_block_absolute(wall_block, *x, y + abs_terrain_offset, *z, None, None);
    }
}

```

The same logic applies to door positions, ensuring doorframes create clean vertical openings through the ceiling. This guarantees that interior walls properly partition rooms and doorways connect them realistically across multiple floors.

## Integration with the Building Pipeline

The interior generator is invoked from [`src/element_processing/buildings.rs`](https://github.com/louis-e/arnis/blob/main/src/element_processing/buildings.rs) when processing complete building shells. Around line 2534, the pipeline checks user arguments and building size before calling `generate_building_interior`:

```rust
if args.interior && !skip_interior && cached_floor_area.len() > 100 {
    let floor_levels = calculate_floor_levels(start_y_offset, building_height);
    generate_building_interior(
        editor,
        &cached_floor_area,
        bounds.min_x,
        bounds.min_z,
        bounds.max_x,
        bounds.max_z,
        start_y_offset,
        building_height,
        style.wall_block,
        &floor_levels,
        args,
        element,
        abs_terrain_offset,
        is_abandoned_building,
    );
}

```

This integration ensures that only sufficiently large buildings (over 100 blocks in floor area) with the `interior` flag enabled receive generated interiors, while preserving the building's external architectural style through the `wall_block` parameter.

## Summary

- **Size Filtering**: Interiors are only generated for buildings larger than 8×8 blocks with sufficient floor area, enforced by early returns in `generate_building_interior`.
- **Pattern Tiling**: Room layouts are created by tiling 23×23 character patterns using modulo arithmetic, with different patterns for ground floors, upper floors, and abandoned buildings.
- **Block Mapping**: Characters map to specific blocks via `get_interior_block`, with `'D'` creating dark oak door halves and `'W'` creating interior walls matching the exterior material.
- **Vertical Extension**: Walls and doorframes are extended from the floor tiles up to the ceiling using recorded positions from the tiling phase, ensuring solid room partitions.
- **Pipeline Integration**: The system is called from [`buildings.rs`](https://github.com/louis-e/arnis/blob/main/buildings.rs) when the `interior` argument is enabled and the building meets size thresholds.

## Frequently Asked Questions

### What is the minimum building size for interior generation?

Buildings must be at least 8 blocks wide and 8 blocks deep to receive interiors. The `generate_building_interior` function in [`src/element_processing/subprocessor/buildings_interior.rs`](https://github.com/louis-e/arnis/blob/main/src/element_processing/subprocessor/buildings_interior.rs) immediately returns if `width < 8 || depth < 8`, as smaller structures are too cramped for meaningful room layouts.

### How does Arnis ensure interior walls match the building's exterior material?

The `get_interior_block` function maps the character `'W'` to the `wall_block` parameter passed from the building generator. This parameter contains the block type used for the building's exterior walls (such as brick or concrete), ensuring that interior partition walls visually match the external architecture for a cohesive aesthetic.

### Why does the interior generation use a 2-block buffer from the outer walls?

The generator subtracts a 2-block buffer from the building perimeter to create `interior_min_x`, `interior_max_x`, etc. This ensures that furniture and interior walls never touch the outer building walls, preventing visual clipping and maintaining proper clearance for windows and structural elements on the building exterior.

### Can custom interior patterns be added to Arnis?

Yes, the pattern system uses constant 23×23 character arrays defined in [`buildings_interior.rs`](https://github.com/louis-e/arnis/blob/main/buildings_interior.rs). Developers can define new `INTERIOR1_LAYER1`, `INTERIOR2_LAYER1`, or `ABANDONED_LAYER1` arrays with custom room layouts, or add new character mappings in `get_interior_block` to introduce new furniture types and architectural features.