mirror of
https://github.com/louis-e/arnis.git
synced 2026-04-20 05:59:52 -04:00
Implement tunnel=building_passage support
Render ground-level archway openings through buildings where highway ways tagged tunnel=building_passage pass through. A pre-scan phase collects passage highway coordinates into a CoordinateBitmap, which is threaded into building generation to: - Cut wall blocks below passage height in build_wall_ring() - Skip ground floors and place passage ceiling lintels - Add corridor side walls where interior meets the passage - Skip foundation pillars, corner quoins, wall depth features, window decorations, and special doors at passage coordinates - Suppress interior furniture placement inside the passage zone
This commit is contained in:
@@ -67,6 +67,11 @@ pub fn generate_world_with_options(
|
||||
// Uses a memory-efficient bitmap (~1 bit per coordinate) instead of a HashSet (~24 bytes per coordinate)
|
||||
let building_footprints = flood_fill_cache.collect_building_footprints(&elements, &xzbbox);
|
||||
|
||||
// Collect coordinates covered by tunnel=building_passage highways so that
|
||||
// building generation can cut ground-level openings through walls and floors.
|
||||
let building_passages =
|
||||
highways::collect_building_passage_coords(&elements, &xzbbox, args.scale);
|
||||
|
||||
// Process all elements (no longer need to partition boundaries)
|
||||
let elements_count: usize = elements.len();
|
||||
let process_pb: ProgressBar = ProgressBar::new(elements_count as u64);
|
||||
@@ -138,6 +143,7 @@ pub fn generate_world_with_options(
|
||||
None,
|
||||
None,
|
||||
&flood_fill_cache,
|
||||
&building_passages,
|
||||
);
|
||||
}
|
||||
} else if way.tags.contains_key("highway") {
|
||||
@@ -256,6 +262,7 @@ pub fn generate_world_with_options(
|
||||
args,
|
||||
&flood_fill_cache,
|
||||
&xzbbox,
|
||||
&building_passages,
|
||||
);
|
||||
} else if rel.tags.contains_key("water")
|
||||
|| rel
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::coordinate_system::cartesian::XZPoint;
|
||||
use crate::deterministic_rng::{coord_rng, element_rng};
|
||||
use crate::element_processing::historic;
|
||||
use crate::element_processing::subprocessor::buildings_interior::generate_building_interior;
|
||||
use crate::floodfill_cache::FloodFillCache;
|
||||
use crate::floodfill_cache::{CoordinateBitmap, FloodFillCache};
|
||||
use crate::osm_parser::{ProcessedMemberRole, ProcessedNode, ProcessedRelation, ProcessedWay};
|
||||
use crate::world_editor::WorldEditor;
|
||||
use fastnbt::Value;
|
||||
@@ -52,6 +52,11 @@ pub(crate) struct HolePolygon {
|
||||
// Building Style System
|
||||
// ============================================================================
|
||||
|
||||
/// Height (in blocks above ground floor) of a building-passage archway.
|
||||
/// Walls and floors below this height are removed at tunnel=building_passage
|
||||
/// highway coordinates, creating a ground-level opening through the building.
|
||||
const BUILDING_PASSAGE_HEIGHT: i32 = 4;
|
||||
|
||||
/// Accent block options for building decoration
|
||||
const ACCENT_BLOCK_OPTIONS: [Block; 6] = [
|
||||
POLISHED_ANDESITE,
|
||||
@@ -1602,11 +1607,14 @@ fn build_wall_ring(
|
||||
config: &BuildingConfig,
|
||||
args: &Args,
|
||||
has_sloped_roof: bool,
|
||||
building_passages: &CoordinateBitmap,
|
||||
) -> (Vec<(i32, i32)>, (i32, i32, i32)) {
|
||||
let mut previous_node: Option<(i32, i32)> = None;
|
||||
let mut corner_addup: (i32, i32, i32) = (0, 0, 0);
|
||||
let mut current_building: Vec<(i32, i32)> = Vec::new();
|
||||
|
||||
let passage_height = BUILDING_PASSAGE_HEIGHT.min(config.building_height);
|
||||
|
||||
for node in nodes {
|
||||
let x = node.x;
|
||||
let z = node.z;
|
||||
@@ -1622,8 +1630,11 @@ fn build_wall_ring(
|
||||
);
|
||||
|
||||
for (bx, _, bz) in bresenham_points {
|
||||
let is_passage = building_passages.contains(bx, bz);
|
||||
|
||||
// Create foundation pillars when using terrain
|
||||
if args.terrain && config.is_ground_level {
|
||||
// Skip in passage zones so the road can pass through.
|
||||
if args.terrain && config.is_ground_level && !is_passage {
|
||||
let local_ground_level = if let Some(ground) = editor.get_ground() {
|
||||
ground.level(XZPoint::new(
|
||||
bx - editor.get_min_coords().0,
|
||||
@@ -1645,10 +1656,17 @@ fn build_wall_ring(
|
||||
}
|
||||
}
|
||||
|
||||
// Generate wall blocks with windows
|
||||
for h in
|
||||
(config.start_y_offset + 1)..=(config.start_y_offset + config.building_height)
|
||||
{
|
||||
// Generate wall blocks with windows.
|
||||
// In passage zones, skip below passage ceiling so the road
|
||||
// can pass through; place a floor-block lintel at the top of
|
||||
// the opening and continue the wall above.
|
||||
let wall_start = if is_passage {
|
||||
config.start_y_offset + passage_height + 1
|
||||
} else {
|
||||
config.start_y_offset + 1
|
||||
};
|
||||
|
||||
for h in wall_start..=(config.start_y_offset + config.building_height) {
|
||||
let block = determine_wall_block_at_position(bx, h, bz, config);
|
||||
editor.set_block_absolute(
|
||||
block,
|
||||
@@ -1660,6 +1678,18 @@ fn build_wall_ring(
|
||||
);
|
||||
}
|
||||
|
||||
// Place passage ceiling lintel
|
||||
if is_passage && passage_height < config.building_height {
|
||||
editor.set_block_absolute(
|
||||
config.floor_block,
|
||||
bx,
|
||||
config.start_y_offset + passage_height + config.abs_terrain_offset,
|
||||
bz,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
// Add roof line only for flat roofs, sloped roofs will cover this area
|
||||
if !has_sloped_roof {
|
||||
let roof_line_block = if config.use_accent_roof_line {
|
||||
@@ -1697,6 +1727,7 @@ fn generate_special_doors(
|
||||
element: &ProcessedWay,
|
||||
config: &BuildingConfig,
|
||||
wall_outline: &[(i32, i32)],
|
||||
building_passages: &CoordinateBitmap,
|
||||
) {
|
||||
if wall_outline.is_empty() {
|
||||
return;
|
||||
@@ -1738,6 +1769,13 @@ fn generate_special_doors(
|
||||
(mid_x, mid_z, mid_x, mid_z + 1)
|
||||
};
|
||||
|
||||
// Skip placing doors inside a building passage
|
||||
if building_passages.contains(door1_x, door1_z)
|
||||
|| building_passages.contains(door2_x, door2_z)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Place the double door (lower and upper parts)
|
||||
// Use empty blacklist to overwrite existing wall blocks
|
||||
editor.set_block_absolute(
|
||||
@@ -1783,9 +1821,19 @@ fn generate_special_doors(
|
||||
let door_idx = rng.random_range(0..wall_outline.len());
|
||||
let (door_x, door_z) = wall_outline[door_idx];
|
||||
|
||||
// Place single oak door (empty blacklist to overwrite wall blocks)
|
||||
editor.set_block_absolute(OAK_DOOR, door_x, door_y, door_z, None, Some(&[]));
|
||||
editor.set_block_absolute(OAK_DOOR_UPPER, door_x, door_y + 1, door_z, None, Some(&[]));
|
||||
// Skip placing a door inside a building passage
|
||||
if !building_passages.contains(door_x, door_z) {
|
||||
// Place single oak door (empty blacklist to overwrite wall blocks)
|
||||
editor.set_block_absolute(OAK_DOOR, door_x, door_y, door_z, None, Some(&[]));
|
||||
editor.set_block_absolute(
|
||||
OAK_DOOR_UPPER,
|
||||
door_x,
|
||||
door_y + 1,
|
||||
door_z,
|
||||
None,
|
||||
Some(&[]),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2017,6 +2065,7 @@ fn generate_residential_window_decorations(
|
||||
editor: &mut WorldEditor,
|
||||
element: &ProcessedWay,
|
||||
config: &BuildingConfig,
|
||||
building_passages: &CoordinateBitmap,
|
||||
) {
|
||||
// Only non-tall residential / house buildings get decorations.
|
||||
if config.is_tall_building {
|
||||
@@ -2074,6 +2123,11 @@ fn generate_residential_window_decorations(
|
||||
let bx = *bx;
|
||||
let bz = *bz;
|
||||
|
||||
// Skip decorations at passage openings
|
||||
if building_passages.contains(bx, bz) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mod6 = ((bx + bz) % 6 + 6) % 6; // always 0..5
|
||||
|
||||
// --- Shutters ---
|
||||
@@ -2350,6 +2404,7 @@ fn generate_corner_quoins(
|
||||
editor: &mut WorldEditor,
|
||||
element: &ProcessedWay,
|
||||
config: &BuildingConfig,
|
||||
building_passages: &CoordinateBitmap,
|
||||
) {
|
||||
// Skip if wall and accent are the same block (nothing visible)
|
||||
if config.wall_block == config.accent_block {
|
||||
@@ -2380,9 +2435,16 @@ fn generate_corner_quoins(
|
||||
|
||||
let quoin_block = config.accent_block;
|
||||
let top_h = config.start_y_offset + config.building_height;
|
||||
let passage_h = config.start_y_offset + BUILDING_PASSAGE_HEIGHT.min(config.building_height);
|
||||
|
||||
for &(cx, cz) in &corners {
|
||||
for h in (config.start_y_offset + 1)..=top_h {
|
||||
let is_passage = building_passages.contains(cx, cz);
|
||||
let start_h = if is_passage {
|
||||
passage_h + 1
|
||||
} else {
|
||||
config.start_y_offset + 1
|
||||
};
|
||||
for h in start_h..=top_h {
|
||||
editor.set_block_absolute(
|
||||
quoin_block,
|
||||
cx,
|
||||
@@ -2407,6 +2469,7 @@ fn generate_wall_depth_features(
|
||||
element: &ProcessedWay,
|
||||
config: &BuildingConfig,
|
||||
has_sloped_roof: bool,
|
||||
building_passages: &CoordinateBitmap,
|
||||
) {
|
||||
if config.wall_depth_style == WallDepthStyle::None {
|
||||
return;
|
||||
@@ -2479,6 +2542,12 @@ fn generate_wall_depth_features(
|
||||
let bx = *bx;
|
||||
let bz = *bz;
|
||||
|
||||
// Skip decorative features at passage openings — the road
|
||||
// passes through here so no pilasters/buttresses/etc.
|
||||
if building_passages.contains(bx, bz) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mod6 = ((bx + bz) % 6 + 6) % 6;
|
||||
|
||||
match config.wall_depth_style {
|
||||
@@ -3108,6 +3177,7 @@ fn generate_floors_and_ceilings(
|
||||
config: &BuildingConfig,
|
||||
args: &Args,
|
||||
generate_non_flat_roof: bool,
|
||||
building_passages: &CoordinateBitmap,
|
||||
) -> HashSet<(i32, i32)> {
|
||||
let mut processed_points: HashSet<(i32, i32)> = HashSet::new();
|
||||
let ceiling_light_block = if config.is_abandoned_building {
|
||||
@@ -3116,26 +3186,38 @@ fn generate_floors_and_ceilings(
|
||||
GLOWSTONE
|
||||
};
|
||||
|
||||
let passage_height = BUILDING_PASSAGE_HEIGHT.min(config.building_height);
|
||||
|
||||
for &(x, z) in cached_floor_area {
|
||||
if !processed_points.insert((x, z)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set ground floor
|
||||
editor.set_block_absolute(
|
||||
config.floor_block,
|
||||
x,
|
||||
config.start_y_offset + config.abs_terrain_offset,
|
||||
z,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let is_passage = building_passages.contains(x, z);
|
||||
|
||||
// Set ground floor — skip in passage zones (the road surface is placed
|
||||
// by the highway processor instead).
|
||||
if !is_passage {
|
||||
editor.set_block_absolute(
|
||||
config.floor_block,
|
||||
x,
|
||||
config.start_y_offset + config.abs_terrain_offset,
|
||||
z,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
// Set intermediate ceilings with light fixtures
|
||||
if config.building_height > 4 {
|
||||
for h in (config.start_y_offset + 2 + 4..config.start_y_offset + config.building_height)
|
||||
.step_by(4)
|
||||
{
|
||||
// Skip intermediate ceilings below passage opening
|
||||
if is_passage && h <= config.start_y_offset + passage_height {
|
||||
continue;
|
||||
}
|
||||
|
||||
let block = if x % 5 == 0 && z % 5 == 0 {
|
||||
ceiling_light_block
|
||||
} else {
|
||||
@@ -3143,8 +3225,8 @@ fn generate_floors_and_ceilings(
|
||||
};
|
||||
editor.set_block_absolute(block, x, h + config.abs_terrain_offset, z, None, None);
|
||||
}
|
||||
} else if x % 5 == 0 && z % 5 == 0 {
|
||||
// Single floor building with ceiling light
|
||||
} else if x % 5 == 0 && z % 5 == 0 && !is_passage {
|
||||
// Single floor building with ceiling light (skip in passage)
|
||||
editor.set_block_absolute(
|
||||
ceiling_light_block,
|
||||
x,
|
||||
@@ -3155,6 +3237,18 @@ fn generate_floors_and_ceilings(
|
||||
);
|
||||
}
|
||||
|
||||
// Place passage ceiling lintel at the top of the archway
|
||||
if is_passage && passage_height < config.building_height {
|
||||
editor.set_block_absolute(
|
||||
config.floor_block,
|
||||
x,
|
||||
config.start_y_offset + passage_height + config.abs_terrain_offset,
|
||||
z,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
// Set top ceiling (only if flat roof or no roof generation)
|
||||
// Use the resolved style flag, not just the OSM tag, since auto-gabled roofs
|
||||
// may be generated for residential buildings without a roof:shape tag
|
||||
@@ -3233,6 +3327,7 @@ pub fn generate_buildings(
|
||||
relation_levels: Option<i32>,
|
||||
hole_polygons: Option<&[HolePolygon]>,
|
||||
flood_fill_cache: &FloodFillCache,
|
||||
building_passages: &CoordinateBitmap,
|
||||
) {
|
||||
// Early return for underground buildings
|
||||
if should_skip_underground_building(element) {
|
||||
@@ -3443,35 +3538,48 @@ pub fn generate_buildings(
|
||||
|
||||
// Generate walls, pass whether this building will have a sloped roof
|
||||
let has_sloped_roof = args.roof && style.generate_roof && style.roof_type != RoofType::Flat;
|
||||
let (wall_outline, corner_addup) =
|
||||
build_wall_ring(editor, &element.nodes, &config, args, has_sloped_roof);
|
||||
let (wall_outline, corner_addup) = build_wall_ring(
|
||||
editor,
|
||||
&element.nodes,
|
||||
&config,
|
||||
args,
|
||||
has_sloped_roof,
|
||||
building_passages,
|
||||
);
|
||||
|
||||
if let Some(holes) = hole_polygons {
|
||||
for hole in holes {
|
||||
if hole.add_walls {
|
||||
let _ = build_wall_ring(editor, &hole.way.nodes, &config, args, has_sloped_roof);
|
||||
let _ = build_wall_ring(
|
||||
editor,
|
||||
&hole.way.nodes,
|
||||
&config,
|
||||
args,
|
||||
has_sloped_roof,
|
||||
building_passages,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate special doors (garage doors, shed doors)
|
||||
if config.has_garage_door || config.has_single_door {
|
||||
generate_special_doors(editor, element, &config, &wall_outline);
|
||||
generate_special_doors(editor, element, &config, &wall_outline, building_passages);
|
||||
}
|
||||
|
||||
// Add shutters and window boxes to small residential buildings
|
||||
generate_residential_window_decorations(editor, element, &config);
|
||||
generate_residential_window_decorations(editor, element, &config, building_passages);
|
||||
|
||||
// Add wall depth features (pilasters, columns, ledges, cornices, buttresses)
|
||||
// Only for standalone buildings, not building:part sub-sections (parts adjoin
|
||||
// other parts and outward protrusions would collide with neighbours).
|
||||
if !element.tags.contains_key("building:part") {
|
||||
generate_wall_depth_features(editor, element, &config, has_sloped_roof);
|
||||
generate_wall_depth_features(editor, element, &config, has_sloped_roof, building_passages);
|
||||
}
|
||||
|
||||
// Add corner quoins (accent-block columns at building corners)
|
||||
if !element.tags.contains_key("building:part") {
|
||||
generate_corner_quoins(editor, element, &config);
|
||||
generate_corner_quoins(editor, element, &config, building_passages);
|
||||
}
|
||||
|
||||
// Create roof area = floor area + wall outline (so roof covers the walls too)
|
||||
@@ -3492,8 +3600,42 @@ pub fn generate_buildings(
|
||||
&config,
|
||||
args,
|
||||
style.generate_roof,
|
||||
building_passages,
|
||||
);
|
||||
|
||||
// Build tunnel side walls: for each interior coordinate that borders a
|
||||
// passage coordinate, place a wall column from ground to passage ceiling.
|
||||
// This creates the left/right corridor walls inside the archway.
|
||||
if !building_passages.is_empty() {
|
||||
let passage_height =
|
||||
BUILDING_PASSAGE_HEIGHT.min(config.building_height);
|
||||
let abs = config.abs_terrain_offset;
|
||||
for &(x, z) in &cached_floor_area {
|
||||
if building_passages.contains(x, z) {
|
||||
continue; // this is road, not a wall
|
||||
}
|
||||
// Check 4-connected neighbours for passage adjacency
|
||||
let adjacent_to_passage = building_passages.contains(x - 1, z)
|
||||
|| building_passages.contains(x + 1, z)
|
||||
|| building_passages.contains(x, z - 1)
|
||||
|| building_passages.contains(x, z + 1);
|
||||
if adjacent_to_passage {
|
||||
for y in (config.start_y_offset + 1)
|
||||
..=(config.start_y_offset + passage_height)
|
||||
{
|
||||
editor.set_block_absolute(
|
||||
config.wall_block,
|
||||
x,
|
||||
y + abs,
|
||||
z,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate interior features
|
||||
if args.interior {
|
||||
let skip_interior = matches!(
|
||||
@@ -3518,6 +3660,7 @@ pub fn generate_buildings(
|
||||
element,
|
||||
abs_terrain_offset,
|
||||
is_abandoned_building,
|
||||
building_passages,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5217,6 +5360,7 @@ pub fn generate_building_from_relation(
|
||||
args: &Args,
|
||||
flood_fill_cache: &FloodFillCache,
|
||||
xzbbox: &crate::coordinate_system::cartesian::XZBBox,
|
||||
building_passages: &CoordinateBitmap,
|
||||
) {
|
||||
// Skip underground buildings/building parts
|
||||
// Check layer tag
|
||||
@@ -5399,6 +5543,7 @@ pub fn generate_building_from_relation(
|
||||
Some(relation_levels),
|
||||
hole_polygons.as_deref(),
|
||||
flood_fill_cache,
|
||||
building_passages,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::args::Args;
|
||||
use crate::block_definitions::*;
|
||||
use crate::bresenham::bresenham_line;
|
||||
use crate::coordinate_system::cartesian::XZPoint;
|
||||
use crate::floodfill_cache::FloodFillCache;
|
||||
use crate::coordinate_system::cartesian::{XZBBox, XZPoint};
|
||||
use crate::floodfill_cache::{CoordinateBitmap, FloodFillCache};
|
||||
use crate::osm_parser::{ProcessedElement, ProcessedWay};
|
||||
use crate::world_editor::WorldEditor;
|
||||
use std::collections::HashMap;
|
||||
@@ -936,3 +936,92 @@ pub fn generate_aeroway(editor: &mut WorldEditor, way: &ProcessedWay, args: &Arg
|
||||
previous_node = Some((node.x, node.z));
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the half-width (block_range) for a highway type.
|
||||
///
|
||||
/// This extracts the same logic used inside `generate_highways_internal` so
|
||||
/// that pre-scan passes (e.g. building-passage collection) can determine road
|
||||
/// width without generating any blocks.
|
||||
pub(crate) fn highway_block_range(
|
||||
highway_type: &str,
|
||||
tags: &HashMap<String, String>,
|
||||
scale: f64,
|
||||
) -> i32 {
|
||||
let mut block_range: i32 = match highway_type {
|
||||
"footway" | "pedestrian" => 1,
|
||||
"path" => 1,
|
||||
"motorway" | "primary" | "trunk" => 5,
|
||||
"secondary" => 4,
|
||||
"tertiary" => 2,
|
||||
"track" => 1,
|
||||
"service" => 2,
|
||||
"secondary_link" | "tertiary_link" => 1,
|
||||
"escape" => 1,
|
||||
"steps" => 1,
|
||||
_ => {
|
||||
if let Some(lanes) = tags.get("lanes") {
|
||||
if lanes == "2" {
|
||||
3
|
||||
} else if lanes != "1" {
|
||||
4
|
||||
} else {
|
||||
2
|
||||
}
|
||||
} else {
|
||||
2
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if scale < 1.0 {
|
||||
block_range = ((block_range as f64) * scale).floor() as i32;
|
||||
}
|
||||
|
||||
block_range
|
||||
}
|
||||
|
||||
/// Collect all (x, z) coordinates covered by highways tagged
|
||||
/// `tunnel=building_passage`. The returned bitmap can be passed into building
|
||||
/// generation to cut ground-level openings through walls and floors.
|
||||
pub fn collect_building_passage_coords(
|
||||
elements: &[ProcessedElement],
|
||||
xzbbox: &XZBBox,
|
||||
scale: f64,
|
||||
) -> CoordinateBitmap {
|
||||
let mut bitmap = CoordinateBitmap::new(xzbbox);
|
||||
|
||||
for element in elements {
|
||||
let ProcessedElement::Way(way) = element else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Must be tunnel=building_passage
|
||||
if way.tags.get("tunnel").map(|v| v.as_str()) != Some("building_passage") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Must have a highway tag so we know the road width
|
||||
let Some(highway_type) = way.tags.get("highway") else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let block_range = highway_block_range(highway_type, &way.tags, scale);
|
||||
|
||||
for i in 1..way.nodes.len() {
|
||||
let prev = way.nodes[i - 1].xz();
|
||||
let cur = way.nodes[i].xz();
|
||||
|
||||
let points = bresenham_line(prev.x, 0, prev.z, cur.x, 0, cur.z);
|
||||
|
||||
for (bx, _, bz) in &points {
|
||||
for dx in -block_range..=block_range {
|
||||
for dz in -block_range..=block_range {
|
||||
bitmap.set(bx + dx, bz + dz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitmap
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::block_definitions::*;
|
||||
use crate::floodfill_cache::CoordinateBitmap;
|
||||
use crate::world_editor::WorldEditor;
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -291,6 +292,7 @@ pub fn generate_building_interior(
|
||||
element: &crate::osm_parser::ProcessedWay,
|
||||
abs_terrain_offset: i32,
|
||||
is_abandoned_building: bool,
|
||||
building_passages: &CoordinateBitmap,
|
||||
) {
|
||||
// Skip interior generation for very small buildings
|
||||
let width = max_x - min_x + 1;
|
||||
@@ -364,6 +366,14 @@ pub fn generate_building_interior(
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip interior blocks in building-passage zones on floors
|
||||
// that fall within the archway opening.
|
||||
if building_passages.contains(x, z)
|
||||
&& floor_y < start_y_offset + 4.min(building_height)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Map the world coordinates to pattern coordinates using modulo
|
||||
// This creates a seamless tiling effect across the entire building
|
||||
// Add floor_index offset to create variation between floors
|
||||
|
||||
Reference in New Issue
Block a user