use crate::args::Args; use crate::block_definitions::{BEDROCK, DIRT, GRASS_BLOCK, STONE}; use crate::coordinate_system::cartesian::XZBBox; use crate::element_processing::*; use crate::ground::Ground; use crate::osm_parser::ProcessedElement; use crate::progress::emit_gui_progress_update; use crate::world_editor::WorldEditor; use colored::Colorize; use indicatif::{ProgressBar, ProgressStyle}; pub const MIN_Y: i32 = -64; pub fn generate_world( elements: Vec, xzbbox: XZBBox, ground: Ground, args: &Args, ) -> Result<(), String> { let mut editor: WorldEditor = WorldEditor::new(args.path.clone(), &xzbbox); println!("{} Processing data...", "[4/7]".bold()); // Set ground reference in the editor to enable elevation-aware block placement editor.set_ground(&ground); println!("{} Processing terrain...", "[5/7]".bold()); emit_gui_progress_update(25.0, "Processing terrain..."); // Process data let elements_count: usize = elements.len(); let process_pb: ProgressBar = ProgressBar::new(elements_count as u64); process_pb.set_style(ProgressStyle::default_bar() .template("{spinner:.green} [{elapsed_precise}] [{bar:45.white/black}] {pos}/{len} elements ({eta}) {msg}") .unwrap() .progress_chars("█▓░")); let progress_increment_prcs: f64 = 45.0 / elements_count as f64; let mut current_progress_prcs: f64 = 25.0; let mut last_emitted_progress: f64 = current_progress_prcs; for element in &elements { process_pb.inc(1); current_progress_prcs += progress_increment_prcs; if (current_progress_prcs - last_emitted_progress).abs() > 0.25 { emit_gui_progress_update(current_progress_prcs, ""); last_emitted_progress = current_progress_prcs; } if args.debug { process_pb.set_message(format!( "(Element ID: {} / Type: {})", element.id(), element.kind() )); } else { process_pb.set_message(""); } match element { ProcessedElement::Way(way) => { if way.tags.contains_key("building") || way.tags.contains_key("building:part") { buildings::generate_buildings(&mut editor, way, args, None); } else if way.tags.contains_key("highway") { highways::generate_highways(&mut editor, element, args); } else if way.tags.contains_key("landuse") { landuse::generate_landuse(&mut editor, way, args); } else if way.tags.contains_key("natural") { natural::generate_natural(&mut editor, element, args); } else if way.tags.contains_key("amenity") { amenities::generate_amenities(&mut editor, element, args); } else if way.tags.contains_key("leisure") { leisure::generate_leisure(&mut editor, way, args); } else if way.tags.contains_key("barrier") { barriers::generate_barriers(&mut editor, element); } else if way.tags.contains_key("waterway") { waterways::generate_waterways(&mut editor, way); } else if way.tags.contains_key("bridge") { //bridges::generate_bridges(&mut editor, way, ground_level); // TODO FIX } else if way.tags.contains_key("railway") { railways::generate_railways(&mut editor, way); } else if way.tags.contains_key("aeroway") || way.tags.contains_key("area:aeroway") { highways::generate_aeroway(&mut editor, way, args); } else if way.tags.get("service") == Some(&"siding".to_string()) { highways::generate_siding(&mut editor, way); } else if way.tags.contains_key("man_made") { man_made::generate_man_made(&mut editor, element, args); } } ProcessedElement::Node(node) => { if node.tags.contains_key("door") || node.tags.contains_key("entrance") { doors::generate_doors(&mut editor, node); } else if node.tags.contains_key("natural") && node.tags.get("natural") == Some(&"tree".to_string()) { natural::generate_natural(&mut editor, element, args); } else if node.tags.contains_key("amenity") { amenities::generate_amenities(&mut editor, element, args); } else if node.tags.contains_key("barrier") { barriers::generate_barrier_nodes(&mut editor, node); } else if node.tags.contains_key("highway") { highways::generate_highways(&mut editor, element, args); } else if node.tags.contains_key("tourism") { tourisms::generate_tourisms(&mut editor, node); } else if node.tags.contains_key("man_made") { man_made::generate_man_made_nodes(&mut editor, node); } } ProcessedElement::Relation(rel) => { if rel.tags.contains_key("building") || rel.tags.contains_key("building:part") { buildings::generate_building_from_relation(&mut editor, rel, args); } else if rel.tags.contains_key("water") || rel.tags.get("natural") == Some(&"water".to_string()) { water_areas::generate_water_areas(&mut editor, rel); } else if rel.tags.contains_key("natural") { natural::generate_natural_from_relation(&mut editor, rel, args); } else if rel.tags.contains_key("landuse") { landuse::generate_landuse_from_relation(&mut editor, rel, args); } else if rel.tags.get("leisure") == Some(&"park".to_string()) { leisure::generate_leisure_from_relation(&mut editor, rel, args); } else if rel.tags.contains_key("man_made") { man_made::generate_man_made( &mut editor, &ProcessedElement::Relation(rel.clone()), args, ); } } } } process_pb.finish(); // Generate ground layer let total_blocks: u64 = xzbbox.bounding_rect().total_blocks(); let desired_updates: u64 = 1500; let batch_size: u64 = (total_blocks / desired_updates).max(1); let mut block_counter: u64 = 0; println!("{} Generating ground...", "[6/7]".bold()); emit_gui_progress_update(70.0, "Generating ground..."); let ground_pb: ProgressBar = ProgressBar::new(total_blocks); ground_pb.set_style( ProgressStyle::default_bar() .template("{spinner:.green} [{elapsed_precise}] [{bar:45}] {pos}/{len} blocks ({eta})") .unwrap() .progress_chars("█▓░"), ); let mut gui_progress_grnd: f64 = 70.0; let mut last_emitted_progress: f64 = gui_progress_grnd; let total_iterations_grnd: f64 = total_blocks as f64; let progress_increment_grnd: f64 = 20.0 / total_iterations_grnd; let groundlayer_block = GRASS_BLOCK; for x in xzbbox.min_x()..=xzbbox.max_x() { for z in xzbbox.min_z()..=xzbbox.max_z() { // Add default dirt and grass layer if there isn't a stone layer already if !editor.check_for_block(x, 0, z, Some(&[STONE])) { editor.set_block(groundlayer_block, x, 0, z, None, None); editor.set_block(DIRT, x, -1, z, None, None); editor.set_block(DIRT, x, -2, z, None, None); } // Fill underground with stone if args.fillground { // Fill from bedrock+1 to 3 blocks below ground with stone editor.fill_blocks_absolute( STONE, x, MIN_Y + 1, z, x, editor.get_absolute_y(x, -3, z), z, None, None, ); } // Generate a bedrock level at MIN_Y editor.set_block_absolute(BEDROCK, x, MIN_Y, z, None, Some(&[BEDROCK])); block_counter += 1; if block_counter % batch_size == 0 { ground_pb.inc(batch_size); } gui_progress_grnd += progress_increment_grnd; if (gui_progress_grnd - last_emitted_progress).abs() > 0.25 { emit_gui_progress_update(gui_progress_grnd, ""); last_emitted_progress = gui_progress_grnd; } } } // Set sign for player orientation /*editor.set_sign( "↑".to_string(), "Generated World".to_string(), "This direction".to_string(), "".to_string(), 9, -61, 9, 6, );*/ ground_pb.inc(block_counter % batch_size); ground_pb.finish(); // Save world editor.save(); // Update player spawn Y coordinate based on terrain height after generation #[cfg(feature = "gui")] if let Some(spawn_coords) = &args.spawn_point { use crate::gui::update_player_spawn_y_after_generation; let bbox_string = format!( "{},{},{},{}", args.bbox.min().lng(), args.bbox.min().lat(), args.bbox.max().lng(), args.bbox.max().lat() ); if let Err(e) = update_player_spawn_y_after_generation( &args.path, Some(*spawn_coords), bbox_string, args.scale, &ground, ) { eprintln!("Warning: Failed to update spawn point Y coordinate: {e}"); } } emit_gui_progress_update(100.0, "Done! World generation completed."); println!("{}", "Done! World generation completed.".green().bold()); Ok(()) }