From 3a9850f9047516bac7a0ecd4f8d98fcffed6f00a Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Sat, 21 Oct 2023 20:42:31 -0700 Subject: [PATCH] content: Add ability to place exhibits underground. This is not yet used due to various missing details: * Decide what wall surfaces underground exhibits should have. (Probably the enclosure should not have air but walls.) * Add exhibit fields to define what kind of entrance doorway they want. * Generate appropriate entranceway blocks rather than Road. * Decide how to handle when the exhibit is tall enough to stick out. * Add access stairs-or-something down. --- all-is-cubes-content/src/city.rs | 44 +++++++++++++++++++++------- all-is-cubes-content/src/exhibits.rs | 25 +++++++++++++++- all-is-cubes-content/src/lib.rs | 2 +- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/all-is-cubes-content/src/city.rs b/all-is-cubes-content/src/city.rs index 4a126cdb8..b58d2fea3 100644 --- a/all-is-cubes-content/src/city.rs +++ b/all-is-cubes-content/src/city.rs @@ -70,7 +70,6 @@ pub(crate) async fn demo_city( let lamp_spacing = 20; let sky_height = 30; let ground_depth = 30; // TODO: wavy_landscape is forcing us to have extra symmetry here - let underground_floor_y = -5; let space_size = params.size.unwrap_or(GridVector::new(160, 60, 160)); let bounds = GridAab::from_lower_upper( [-space_size.x / 2, -ground_depth, -space_size.z / 2], @@ -192,7 +191,7 @@ pub(crate) async fn demo_city( // Dig underground passages // TODO: They need a connection to the surface for p in -road_radius..=road_radius { - for y in underground_floor_y..0 { + for y in CityPlanner::UNDERGROUND_FLOOR_Y..0 { space.set( step.cube_ahead() + perpendicular * p + GridVector::new(0, y, 0), &AIR, @@ -354,12 +353,11 @@ async fn place_exhibits_in_city( }; exhibit_progress.progress(0.33).await; - // Now that we know the size of the exhibit, find a place for it. + // Now that we know the size of the exhibit, find a place for it that fits its bounds. let exhibit_footprint = exhibit_space.bounds(); - let enclosure_footprint = exhibit_footprint.expand(FaceMap::repeat(1)); - let Some(plot_transform) = planner.find_plot(enclosure_footprint) else { + let Some(plot_transform) = planner.find_plot(enclosure_footprint, exhibit.placement) else { log::error!("Out of city space!"); break 'exhibit; }; @@ -372,7 +370,9 @@ async fn place_exhibits_in_city( enclosure_at_plot.lower_bounds(), [ enclosure_at_plot.upper_bounds().x, - 1.max(plot.lower_bounds().y), // handles case where plot is floating + // max()ing handles the case where the plot is floating but should still + // have enclosure floor + exhibit.placement.floor().max(plot.lower_bounds().y), enclosure_at_plot.upper_bounds().z, ], ); @@ -591,10 +591,27 @@ fn place_lamppost( pub(crate) struct Exhibit { pub name: &'static str, pub subtitle: &'static str, + pub placement: Placement, pub factory: for<'a> fn(&'a Exhibit, &'a mut Universe) -> BoxFuture<'a, Result>, } +#[derive(Clone, Copy, Debug)] +pub(crate) enum Placement { + Surface, + #[allow(unused)] // TODO: polish this and then use it + Underground, +} + +impl Placement { + fn floor(self) -> GridCoordinate { + match self { + Placement::Surface => CityPlanner::SURFACE_Y, + Placement::Underground => CityPlanner::UNDERGROUND_FLOOR_Y, + } + } +} + /// Generate a Space containing text voxels to put on the signboard for an exhibit. /// /// The space's bounds extend upward from [0, 0, 0]. @@ -660,13 +677,20 @@ impl CityPlanner { const PLOT_FRONT_RADIUS: GridCoordinate = Self::LAMP_POSITION_RADIUS; const GAP_BETWEEN_PLOTS: GridCoordinate = 1; + const SURFACE_Y: GridCoordinate = 1; + const UNDERGROUND_FLOOR_Y: GridCoordinate = -10; + pub fn new(space_bounds: GridAab) -> Self { let city_radius = space_bounds.upper_bounds().x; // TODO: compare everything and take the max let mut occupied_plots = Vec::new(); let road = GridAab::from_lower_upper( - [-Self::ROAD_RADIUS, 0, -city_radius], - [Self::ROAD_RADIUS + 1, 2, city_radius + 1], + [ + -Self::ROAD_RADIUS, + Self::UNDERGROUND_FLOOR_Y - 1, + -city_radius, + ], + [Self::ROAD_RADIUS + 1, Self::SURFACE_Y + 2, city_radius + 1], ); occupied_plots.push(road); occupied_plots.push(road.transform(GridRotation::CLOCKWISE.into()).unwrap()); @@ -677,7 +701,7 @@ impl CityPlanner { } } - pub fn find_plot(&mut self, plot_shape: GridAab) -> Option { + pub fn find_plot(&mut self, plot_shape: GridAab, placement: Placement) -> Option { // TODO: We'd like to resume the search from _when we left off_, but that's tricky since a // smaller plot might fit where a large one didn't. So, quadratic search it is for now. for d in 0..=self.city_radius { @@ -692,7 +716,7 @@ impl CityPlanner { let mut transform = Gridgid::from_translation(GridVector::new( d, - 1, + placement.floor(), if left_side { -Self::PLOT_FRONT_RADIUS - plot_shape.upper_bounds().z } else { diff --git a/all-is-cubes-content/src/exhibits.rs b/all-is-cubes-content/src/exhibits.rs index e26b87b5b..7bdeaf7ec 100644 --- a/all-is-cubes-content/src/exhibits.rs +++ b/all-is-cubes-content/src/exhibits.rs @@ -47,9 +47,10 @@ use all_is_cubes::{ use all_is_cubes::{include_image, rgb_const, rgba_const}; use crate::alg::{four_walls, voronoi_pattern}; +use crate::city::{Exhibit, Placement}; use crate::{ make_slab, make_some_blocks, make_some_voxel_blocks, palette, tree, AnimatedVoxels, DemoBlocks, - Exhibit, Fire, LandscapeBlocks, + Fire, LandscapeBlocks, }; /// All exhibits which will show up in [`crate::UniverseTemplate::DemoCity`]. @@ -100,6 +101,7 @@ macro_rules! exhibit { subtitle: "Test depth sorting and blending.\n\ Lighting of volumes still needs work.", + placement: Placement::Surface, )] async fn TRANSPARENCY_LARGE(_: &Exhibit, _universe: &mut Universe) { let mut space = Space::empty(GridAab::from_lower_size([-3, 0, -3], [7, 5, 7])); @@ -135,6 +137,7 @@ async fn TRANSPARENCY_LARGE(_: &Exhibit, _universe: &mut Universe) { subtitle: "Transparency in complex blocks is not correctly implemented.\n\ We also need something for surface properties.", + placement: Placement::Surface, )] async fn TRANSPARENCY_SMALL(_: &Exhibit, universe: &mut Universe) { let footprint = GridAab::from_lower_size([0, 0, 0], [7, 4, 7]); @@ -217,6 +220,7 @@ async fn TRANSPARENCY_SMALL(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Knot", subtitle: "Complex voxel shape", + placement: Placement::Surface, )] async fn KNOT(this: &Exhibit, universe: &mut Universe) { let footprint = GridAab::from_lower_size([-2, -2, -1], [5, 5, 3]); @@ -291,6 +295,7 @@ async fn KNOT(this: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Text", subtitle: "", + placement: Placement::Surface, )] async fn TEXT(_: &Exhibit, universe: &mut Universe) { let space = draw_to_blocks( @@ -313,6 +318,7 @@ async fn TEXT(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Animation", subtitle: "Blocks whose definition is animated", + placement: Placement::Surface, )] async fn ANIMATION(_: &Exhibit, universe: &mut Universe) { let demo_blocks = BlockProvider::::using(universe)?; @@ -384,6 +390,7 @@ async fn ANIMATION(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Collision", subtitle: "Test cases for character/world collision", + placement: Placement::Surface, )] async fn COLLISION(_: &Exhibit, universe: &mut Universe) { let half_block = make_slab(universe, 2, R4); @@ -433,6 +440,7 @@ async fn COLLISION(_: &Exhibit, universe: &mut Universe) { subtitle: "Voxel blocks can be subdivided into\n\ powers of 2 from 2 to 256.", + placement: Placement::Surface, )] async fn RESOLUTIONS(_: &Exhibit, universe: &mut Universe) { let footprint = GridAab::from_lower_size([0, 0, 0], [5, 3, 3]); @@ -496,6 +504,7 @@ async fn RESOLUTIONS(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "World's Smallest Voxel", subtitle: "1/128th the length of a standard block", + placement: Placement::Surface, )] async fn SMALLEST(_: &Exhibit, universe: &mut Universe) { let demo_blocks = BlockProvider::::using(universe)?; @@ -528,6 +537,7 @@ async fn SMALLEST(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Rotations", subtitle: "Rotated blocks and GridRotation::from_to()", + placement: Placement::Surface, )] async fn ROTATIONS(_: &Exhibit, universe: &mut Universe) { let demo_blocks = BlockProvider::::using(universe)?; @@ -577,6 +587,7 @@ async fn ROTATIONS(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Modifier::Zoom", subtitle: "", + placement: Placement::Surface, )] async fn ZOOM(_: &Exhibit, universe: &mut Universe) { let demo_blocks = BlockProvider::::using(universe)?; @@ -613,6 +624,7 @@ async fn ZOOM(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Modifier::Composite", subtitle: "", + placement: Placement::Surface, )] async fn COMPOSITE(_: &Exhibit, universe: &mut Universe) { let demo_blocks = BlockProvider::::using(universe)?; @@ -695,6 +707,7 @@ async fn COMPOSITE(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Modifier::Move", subtitle: "Stationary but not animated cases.", + placement: Placement::Surface, )] async fn MOVED_BLOCKS(_: &Exhibit, universe: &mut Universe) { let mut space = Space::empty(GridAab::from_lower_upper([0, 0, -3], [16, 2, 3])); @@ -729,6 +742,7 @@ async fn MOVED_BLOCKS(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Colors", subtitle: "RGB cube of 5 linear color steps", + placement: Placement::Surface, )] async fn COLORS(_: &Exhibit, universe: &mut Universe) { let gradient_resolution = 5; @@ -801,6 +815,7 @@ async fn COLORS(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Colored Lights", subtitle: "RGBCMY lights in an enclosed room", + placement: Placement::Surface, )] async fn COLOR_LIGHTS(_: &Exhibit, universe: &mut Universe) { let room_width = 11; @@ -962,6 +977,7 @@ async fn COLOR_LIGHTS(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "ChunkChart", subtitle: "Volume of world chunks in view at a distance of 4.99", + placement: Placement::Surface, )] async fn CHUNK_CHART(_: &Exhibit, _: &mut Universe) { use all_is_cubes::chunking::ChunkChart; @@ -975,6 +991,7 @@ async fn CHUNK_CHART(_: &Exhibit, _: &mut Universe) { #[exhibit( name: "make_some_blocks()", subtitle: "", + placement: Placement::Surface, )] async fn MAKE_SOME_BLOCKS(_: &Exhibit, universe: &mut Universe) { const ROWS: GridCoordinate = 5; @@ -1005,6 +1022,7 @@ async fn MAKE_SOME_BLOCKS(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Dashed outline boxes", subtitle: "", + placement: Placement::Surface, )] async fn DASHED_BOXES(_: &Exhibit, universe: &mut Universe) { let color = Rgb::new(1.0, 0.5, 0.5); @@ -1054,6 +1072,7 @@ async fn DASHED_BOXES(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Swimming Pool", subtitle: "Transparent blocks that can be passed through", + placement: Placement::Surface, )] async fn SWIMMING_POOL(_: &Exhibit, _: &mut Universe) { let width = 6; @@ -1075,6 +1094,7 @@ async fn SWIMMING_POOL(_: &Exhibit, _: &mut Universe) { #[exhibit( name: "space_from_image()", subtitle: "Using rotations XYZ, XyZ, XZY, xYZ", + placement: Placement::Surface, )] async fn IMAGES(_: &Exhibit, universe: &mut Universe) { // TODO: it would be nice if this exhibit visualized the generated bounding box somehow @@ -1119,6 +1139,7 @@ async fn IMAGES(_: &Exhibit, universe: &mut Universe) { name: "UI Blocks", subtitle: "Blocks from the UI system (inactive)", + placement: Placement::Surface, )] async fn UI_BLOCKS(_: &Exhibit, universe: &mut Universe) { // TODO: This was designed for a render test and is still shaped for that rather than @@ -1183,6 +1204,7 @@ async fn UI_BLOCKS(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Trees", subtitle: "", + placement: Placement::Surface, )] async fn TREES(_: &Exhibit, universe: &mut Universe) { let landscape_blocks = BlockProvider::::using(universe)?; @@ -1239,6 +1261,7 @@ async fn TREES(_: &Exhibit, universe: &mut Universe) { #[exhibit( name: "Block Destruction", subtitle: "Animation prototype", + placement: Placement::Surface, )] async fn DESTRUCTION(_: &Exhibit, universe: &mut Universe) { let width = 7; diff --git a/all-is-cubes-content/src/lib.rs b/all-is-cubes-content/src/lib.rs index 231e9250e..ea51b57df 100644 --- a/all-is-cubes-content/src/lib.rs +++ b/all-is-cubes-content/src/lib.rs @@ -59,7 +59,7 @@ mod atrium; mod blocks; pub use blocks::*; mod city; -pub(crate) use city::*; +pub(crate) use city::demo_city; mod clouds; mod template; pub use template::*;