mirror of
https://github.com/louis-e/arnis.git
synced 2026-04-18 04:59:40 -04:00
Speed up the bench rotation search by generating a mask of roads and paths, and searching that instead of the actual blocks.
This commit is contained in:
@@ -72,6 +72,14 @@ pub fn generate_world_with_options(
|
||||
let building_passages =
|
||||
highways::collect_building_passage_coords(&elements, &xzbbox, args.scale);
|
||||
|
||||
// Pre-build a bitmap of every (x, z) block coordinate covered by a rendered
|
||||
// road or path surface. Uses the same Bresenham + block_range geometry as
|
||||
// generate_highways_internal, so the bitmap is a 1:1 match of what gets placed.
|
||||
// Amenity processors use this for O(1) nearest-road-block lookups.
|
||||
/// TODO Use this data to create overhanging traffic signals.
|
||||
let road_mask =
|
||||
highways::collect_road_surface_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);
|
||||
@@ -171,7 +179,7 @@ pub fn generate_world_with_options(
|
||||
&building_footprints,
|
||||
);
|
||||
} else if way.tags.contains_key("amenity") {
|
||||
amenities::generate_amenities(&mut editor, &element, args, &flood_fill_cache);
|
||||
amenities::generate_amenities(&mut editor, &element, args, &flood_fill_cache, &road_mask);
|
||||
} else if way.tags.contains_key("leisure") {
|
||||
leisure::generate_leisure(
|
||||
&mut editor,
|
||||
@@ -226,7 +234,7 @@ pub fn generate_world_with_options(
|
||||
&building_footprints,
|
||||
);
|
||||
} else if node.tags.contains_key("amenity") {
|
||||
amenities::generate_amenities(&mut editor, &element, args, &flood_fill_cache);
|
||||
amenities::generate_amenities(&mut editor, &element, args, &flood_fill_cache, &road_mask);
|
||||
} else if node.tags.contains_key("barrier") {
|
||||
barriers::generate_barrier_nodes(&mut editor, node);
|
||||
} else if node.tags.contains_key("highway") {
|
||||
@@ -312,6 +320,7 @@ pub fn generate_world_with_options(
|
||||
// Drop remaining caches
|
||||
drop(highway_connectivity);
|
||||
drop(flood_fill_cache);
|
||||
drop(road_mask);
|
||||
|
||||
// Generate ground layer (surface blocks, vegetation, shorelines, underground fill)
|
||||
ground_generation::generate_ground_layer(
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::block_definitions::*;
|
||||
use crate::bresenham::bresenham_line;
|
||||
use crate::coordinate_system::cartesian::XZPoint;
|
||||
use crate::deterministic_rng::element_rng;
|
||||
use crate::floodfill_cache::FloodFillCache;
|
||||
use crate::floodfill_cache::{FloodFillCache, RoadMaskBitmap};
|
||||
use crate::osm_parser::ProcessedElement;
|
||||
use crate::world_editor::WorldEditor;
|
||||
use fastnbt::Value;
|
||||
@@ -17,26 +17,22 @@ use std::collections::{HashMap, HashSet};
|
||||
/// up to max_radius blocks away, and returns the (x, z) position of
|
||||
/// the nearest road node found.
|
||||
///
|
||||
|
||||
/// Returns None if no road node exists within range.
|
||||
/// Callers can use the returned position to derive a facing direction,
|
||||
/// compute a distance, or do anything else they need.
|
||||
fn nearest_road(x: i32, z: i32, max_radius: i32, editor: &WorldEditor) -> Option<(i32, i32)> {
|
||||
let road_blocks = [
|
||||
GRAY_CONCRETE_POWDER,
|
||||
CYAN_TERRACOTTA,
|
||||
DIRT_PATH,
|
||||
GRAY_CONCRETE,
|
||||
BLACK_CONCRETE,
|
||||
STONE_BRICKS,
|
||||
];
|
||||
fn get_nearest_road_block(
|
||||
x: i32,
|
||||
z: i32,
|
||||
max_radius: i32,
|
||||
road_mask: &RoadMaskBitmap,
|
||||
) -> Option<(i32, i32)> {
|
||||
for dist in 1..=max_radius {
|
||||
// Cross pattern: North, South, West, East
|
||||
let candidates = [(x, z - dist), (x, z + dist), (x - dist, z), (x + dist, z)];
|
||||
|
||||
for (cx, cz) in candidates {
|
||||
let surface_y = editor.get_ground_level(cx, cz);
|
||||
|
||||
if editor.check_for_block_absolute(cx, surface_y, cz, Some(&road_blocks), None) {
|
||||
if road_mask.contains(cx, cz) {
|
||||
return Some((cx, cz));
|
||||
}
|
||||
}
|
||||
@@ -50,6 +46,7 @@ pub fn generate_amenities(
|
||||
element: &ProcessedElement,
|
||||
args: &Args,
|
||||
flood_fill_cache: &FloodFillCache,
|
||||
road_mask: &RoadMaskBitmap,
|
||||
) {
|
||||
// Skip if 'layer' or 'level' is negative in the tags
|
||||
if let Some(layer) = element.tags().get("layer") {
|
||||
@@ -164,7 +161,7 @@ pub fn generate_amenities(
|
||||
// Place a bench
|
||||
if let Some(pt) = first_node {
|
||||
let mut rng = element_rng(element.id());
|
||||
let road_pos = nearest_road(pt.x, pt.z, 5, editor);
|
||||
let road_pos = get_nearest_road_block(pt.x, pt.z, 4, road_mask);
|
||||
|
||||
let use_east_west = if let Some((rx, rz)) = road_pos {
|
||||
let dx = (rx - pt.x).abs();
|
||||
|
||||
@@ -1010,6 +1010,83 @@ pub(crate) fn highway_block_range(
|
||||
block_range
|
||||
}
|
||||
|
||||
/// Collect all (x, z) coordinates that are covered by any rendered road or path
|
||||
/// surface. The returned bitmap has 1 for every block that the highway renderer
|
||||
/// places as a road/path surface and 0 everywhere else.
|
||||
///
|
||||
/// Geometry is computed identically to `generate_highways_internal`:
|
||||
/// - Bresenham line between each consecutive pair of OSM nodes
|
||||
/// - Expanded by `block_range` in both axes (same value as the renderer uses)
|
||||
/// - `area=yes` ways, indoor ways, negative-level ways, and pure node types
|
||||
/// (street_lamp, crossing, bus_stop) are excluded, matching the renderer's
|
||||
/// early-return guards.
|
||||
///
|
||||
/// This lets `find_nearest_road_block` in `amenities.rs` or other processors do a single O(1) bitmap lookup
|
||||
/// instead of live `get_ground_level` + `check_for_block_absolute` world scans.
|
||||
pub fn collect_road_surface_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;
|
||||
};
|
||||
|
||||
let Some(highway_type) = way.tags.get("highway") else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Exclude non-surface node-only highway types
|
||||
match highway_type.as_str() {
|
||||
"street_lamp" | "crossing" | "bus_stop" => continue,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Exclude area highways (pedestrian plazas etc.) — flood-filled separately
|
||||
if way.tags.get("area").is_some_and(|v| v == "yes") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Exclude indoor ways (same guard as generate_highways_internal)
|
||||
if way.tags.get("indoor").is_some_and(|v| v == "yes") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Exclude negative-level ways (indoor mapping)
|
||||
if way
|
||||
.tags
|
||||
.get("level")
|
||||
.and_then(|l| l.parse::<i32>().ok())
|
||||
.is_some_and(|l| l < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use the same block_range the renderer uses for this highway type
|
||||
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
|
||||
}
|
||||
|
||||
/// 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.
|
||||
|
||||
@@ -237,6 +237,12 @@ impl CoordinateBitmap {
|
||||
/// Type alias for building footprint bitmap (for backwards compatibility).
|
||||
pub type BuildingFootprintBitmap = CoordinateBitmap;
|
||||
|
||||
/// Type alias for the road surface bitmap used by amenity processors.
|
||||
/// Built by `highways::collect_road_surface_coords` using the same Bresenham +
|
||||
/// block_range geometry as the renderer, so every placed road/path block coordinate
|
||||
/// is marked as 1 and everything else is 0.
|
||||
pub type RoadMaskBitmap = CoordinateBitmap;
|
||||
|
||||
/// A cache of pre-computed flood fill results, keyed by element ID.
|
||||
pub struct FloodFillCache {
|
||||
/// Cached results: element_id -> filled coordinates
|
||||
|
||||
Reference in New Issue
Block a user