mirror of
https://github.com/louis-e/arnis.git
synced 2026-01-18 02:57:50 -05:00
Compare commits
12 Commits
v2.1.0
...
v2.1.0-men
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7401036675 | ||
|
|
46cf1bca63 | ||
|
|
ae3a91970f | ||
|
|
b44b7995d1 | ||
|
|
f3ae449f02 | ||
|
|
2e319d5ea0 | ||
|
|
1a259d6dfc | ||
|
|
036e54807e | ||
|
|
a75cffd94b | ||
|
|
135e15077a | ||
|
|
a284f77545 | ||
|
|
5cb749ba8f |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
gui-src/** linguist-vendored
|
||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
buy_me_a_coffee: louisdev
|
||||
@@ -21,6 +21,7 @@ dirs = "4.0.0"
|
||||
fastanvil = "0.31.0"
|
||||
fastnbt = "2.5.0"
|
||||
fnv = "1.0.7"
|
||||
fs2 = "0.4"
|
||||
geo = "0.28.0"
|
||||
indicatif = "0.17.8"
|
||||
itertools = "0.13.0"
|
||||
|
||||
22
README.md
22
README.md
@@ -60,23 +60,21 @@ The project is named after the smallest city in Germany, Arnis[^2]. The city's s
|
||||
|
||||
## :memo: ToDo and Known Bugs
|
||||
Feel free to choose an item from the To-Do or Known Bugs list, or bring your own idea to the table. Bug reports shall be raised as a Github issue. Contributions are highly welcome and appreciated!
|
||||
- [ ] Memory optimization
|
||||
- [ ] Mapping real coordinates to Minecraft coordinates (https://github.com/louis-e/arnis/issues/29)
|
||||
- [ ] Evaluate and implement elevation (https://github.com/louis-e/arnis/issues/66)
|
||||
- [ ] Fix Github Action Workflow for releasing Linux & MacOS Binary
|
||||
- [ ] Evaluate and implement multithreaded region saving
|
||||
- [ ] Better code documentation
|
||||
- [ ] Implement house roof types
|
||||
- [ ] Refactor railway implementation
|
||||
- [ ] Refactor bridges implementation
|
||||
- [ ] Refactor fountain structure implementation
|
||||
- [ ] Evaluate and implement faster region saving
|
||||
- [ ] Automatic new world creation instead of using an existing world
|
||||
- [ ] Tool for mapping real coordinates to Minecraft coordinates
|
||||
- [ ] Setup fork of [https://github.com/aaronr/bboxfinder.com](https://github.com/aaronr/bboxfinder.com) for easy bbox picking
|
||||
- [ ] Implement house roof types
|
||||
- [ ] Refactor bridges implementation
|
||||
- [ ] Refactor railway implementation
|
||||
- [ ] Better code documentation
|
||||
- [ ] Refactor fountain structure implementation
|
||||
- [ ] Add interior to buildings
|
||||
- [ ] Evaluate and implement elevation
|
||||
- [ ] Generate a few big cities using high performance hardware and make them available to download
|
||||
- [ ] Implement memory mapped storing of chunks to reduce memory usage
|
||||
- [x] Memory optimization
|
||||
- [x] Design and implement a GUI
|
||||
- [x] Fix faulty empty chunks ([https://github.com/owengage/fastnbt/issues/120](https://github.com/owengage/fastnbt/issues/120)) (workaround found)
|
||||
- [x] Setup fork of [https://github.com/aaronr/bboxfinder.com](https://github.com/aaronr/bboxfinder.com) for easy bbox picking
|
||||
|
||||
## :trophy: Open Source
|
||||
#### Key objectives of this project
|
||||
|
||||
129
gui-src/css/styles.css
vendored
129
gui-src/css/styles.css
vendored
@@ -55,7 +55,7 @@ a:hover {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
justify-content: center;
|
||||
align-items: stretch; /* Ensures both sections take full height */
|
||||
align-items: stretch;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@ a:hover {
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
/* No display: flex here, so buttons and content aren't stretched */
|
||||
}
|
||||
|
||||
.controls-content {
|
||||
@@ -83,7 +82,7 @@ a:hover {
|
||||
}
|
||||
|
||||
.controls-box .progress-section {
|
||||
margin-top: auto; /* Keeps the progress section at the bottom */
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.map-container {
|
||||
@@ -112,11 +111,11 @@ button {
|
||||
transition: border-color 0.25s;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
|
||||
margin-top: 10px;
|
||||
width: auto; /* Ensures buttons don’t stretch */
|
||||
width: auto;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-color: #396cd8;
|
||||
border-color: #656565;
|
||||
}
|
||||
|
||||
#selected-directory {
|
||||
@@ -238,4 +237,122 @@ button:hover {
|
||||
|
||||
.controls-box button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Customization Settings */
|
||||
.modal {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #797979;
|
||||
padding: 20px;
|
||||
border: 1px solid #797979;
|
||||
border-radius: 10px;
|
||||
width: 400px;
|
||||
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.close-button {
|
||||
color: #e9e9e9;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#winter-toggle {
|
||||
accent-color: #fecc44;
|
||||
}
|
||||
|
||||
.winter-toggle-container, .scale-slider-container {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.scale-slider-container label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#scale-value-slider {
|
||||
accent-color: #fecc44;
|
||||
}
|
||||
|
||||
#slider-value {
|
||||
margin-left: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bbox-input-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.bbox-input-container label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#bbox-coords {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #fecc44;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#bbox-coords:focus {
|
||||
outline: none;
|
||||
border-color: #fecc44;
|
||||
box-shadow: 0 0 5px #fecc44;
|
||||
}
|
||||
|
||||
|
||||
.button-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.start-button {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.start-button:hover {
|
||||
background-color: #4caf50;
|
||||
}
|
||||
|
||||
.settings-button {
|
||||
width: 40px !important;
|
||||
height: 38px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s, border-color 0.3s;
|
||||
}
|
||||
|
||||
.settings-button .gear-icon::before {
|
||||
content: "⚙️";
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
50
gui-src/index.html
vendored
50
gui-src/index.html
vendored
@@ -34,7 +34,7 @@
|
||||
<section class="section controls-box">
|
||||
<div class="controls-content">
|
||||
<h2>Select World</h2>
|
||||
|
||||
|
||||
<!-- Updated Tooltip Structure -->
|
||||
<div class="tooltip" style="width: 100%;">
|
||||
<button type="button" onclick="pickDirectory()" style="padding: 10px; line-height: 1.2; width: 100%;">
|
||||
@@ -48,12 +48,15 @@
|
||||
Please select a Minecraft world that can be overwritten, as the generation process will replace existing structures in the chosen world!
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<button type="button" onclick="startGeneration()">Start Generation</button>
|
||||
|
||||
<div class="button-container">
|
||||
<button type="button" id="start-button" class="start-button" onclick="startGeneration()">Start Generation</button>
|
||||
<button type="button" class="settings-button" onclick="openSettings()">
|
||||
<i class="gear-icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
|
||||
<div class="progress-section">
|
||||
<h2>Progress</h2>
|
||||
<div class="progress-bar-container">
|
||||
@@ -68,6 +71,39 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Settings Modal -->
|
||||
<div id="settings-modal" class="modal" style="display: none;">
|
||||
<div class="modal-content">
|
||||
<span class="close-button" onclick="closeSettings()">×</span>
|
||||
<h2>Customization Settings</h2>
|
||||
|
||||
<!-- Winter Mode Toggle Button -->
|
||||
<div class="winter-toggle-container">
|
||||
<label for="winter-toggle">Winter:</label>
|
||||
<input type="checkbox" id="winter-toggle" name="winter-toggle">
|
||||
</div>
|
||||
|
||||
<!-- World Scale Slider -->
|
||||
<div class="scale-slider-container">
|
||||
<label for="scale-value-slider">World Scale:</label>
|
||||
<input type="range" id="scale-value-slider" name="scale-value-slider" min="0.50" max="2.5" step="0.25" value="1">
|
||||
<span id="slider-value">1.00</span>
|
||||
</div>
|
||||
|
||||
<!-- Bounding Box Input -->
|
||||
<div class="bbox-input-container">
|
||||
<label for="bbox-coords">Custom Bounding Box:</label>
|
||||
<input type="text" id="bbox-coords" name="bbox-coords" maxlength="55" style="width: 280px;" autocomplete="one-time-code" placeholder="Format: lat,lng,lat,lng">
|
||||
</div>
|
||||
|
||||
<!-- Floodfill Timeout Input -->
|
||||
<div class="timeout-input-container">
|
||||
<label for="floodfill-timeout">Floodfill Timeout (sec):</label>
|
||||
<input type="number" id="floodfill-timeout" name="floodfill-timeout" min="0" step="1" value="20" style="width: 100px;" placeholder="Seconds">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer">
|
||||
<a href="https://github.com/louis-e/arnis" target="_blank" class="footer-link">
|
||||
@@ -77,4 +113,4 @@
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
148
gui-src/js/main.js
vendored
148
gui-src/js/main.js
vendored
@@ -2,10 +2,21 @@ const { invoke } = window.__TAURI__.core;
|
||||
|
||||
// Initialize elements and start the demo progress
|
||||
window.addEventListener("DOMContentLoaded", async () => {
|
||||
initFooter();
|
||||
await checkForUpdates();
|
||||
registerMessageEvent();
|
||||
window.pickDirectory = pickDirectory;
|
||||
window.startGeneration = startGeneration;
|
||||
setupProgressListener();
|
||||
initSettings();
|
||||
handleBboxInput();
|
||||
});
|
||||
|
||||
// Function to initialize the footer with the current year and version
|
||||
async function initFooter() {
|
||||
const currentYear = new Date().getFullYear();
|
||||
document.getElementById("current-year").textContent = currentYear;
|
||||
|
||||
// Update displayed version
|
||||
try {
|
||||
const version = await invoke('gui_get_version');
|
||||
const footerLink = document.querySelector(".footer-link");
|
||||
@@ -13,8 +24,10 @@ window.addEventListener("DOMContentLoaded", async () => {
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch version:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for updates
|
||||
// Function to check for updates and display a notification if available
|
||||
async function checkForUpdates() {
|
||||
try {
|
||||
const isUpdateAvailable = await invoke('gui_check_for_updates');
|
||||
if (isUpdateAvailable) {
|
||||
@@ -35,8 +48,10 @@ window.addEventListener("DOMContentLoaded", async () => {
|
||||
} catch (error) {
|
||||
console.error("Failed to check for updates: ", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Register bbox update event for iframe map
|
||||
// Function to register the event listener for bbox updates from iframe
|
||||
function registerMessageEvent() {
|
||||
window.addEventListener('message', function (event) {
|
||||
const bboxText = event.data.bboxText;
|
||||
|
||||
@@ -45,15 +60,14 @@ window.addEventListener("DOMContentLoaded", async () => {
|
||||
displayBboxInfoText(bboxText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.pickDirectory = pickDirectory;
|
||||
window.startGeneration = startGeneration;
|
||||
|
||||
// Function to set up the progress bar listener
|
||||
function setupProgressListener() {
|
||||
const progressBar = document.getElementById("progress-bar");
|
||||
const progressMessage = document.getElementById("progress-message");
|
||||
const progressDetail = document.getElementById("progress-detail");
|
||||
|
||||
// Listen for progress-update events
|
||||
window.__TAURI__.event.listen("progress-update", (event) => {
|
||||
const { progress, message } = event.payload;
|
||||
|
||||
@@ -64,15 +78,102 @@ window.addEventListener("DOMContentLoaded", async () => {
|
||||
|
||||
if (message != "") {
|
||||
progressMessage.textContent = message;
|
||||
|
||||
|
||||
if (message.startsWith("Error!")) {
|
||||
progressMessage.style.color = "#fa7878";
|
||||
generationButtonEnabled = true;
|
||||
} else if (message.startsWith("Done!")) {
|
||||
progressMessage.style.color = "#7bd864";
|
||||
generationButtonEnabled = true;
|
||||
} else {
|
||||
progressMessage.style.color = "";
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initSettings() {
|
||||
// Settings
|
||||
const settingsModal = document.getElementById("settings-modal");
|
||||
const slider = document.getElementById("scale-value-slider");
|
||||
const sliderValue = document.getElementById("slider-value");
|
||||
|
||||
// Open settings modal
|
||||
function openSettings() {
|
||||
settingsModal.style.display = "flex";
|
||||
settingsModal.style.justifyContent = "center";
|
||||
settingsModal.style.alignItems = "center";
|
||||
}
|
||||
|
||||
// Close settings modal
|
||||
function closeSettings() {
|
||||
settingsModal.style.display = "none";
|
||||
}
|
||||
|
||||
window.openSettings = openSettings;
|
||||
window.closeSettings = closeSettings;
|
||||
|
||||
// Update slider value display
|
||||
slider.addEventListener("input", () => {
|
||||
sliderValue.textContent = parseFloat(slider.value).toFixed(2);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to validate and handle bbox input
|
||||
function handleBboxInput() {
|
||||
const inputBox = document.getElementById("bbox-coords");
|
||||
const bboxInfo = document.getElementById("bbox-info");
|
||||
|
||||
inputBox.addEventListener("input", function () {
|
||||
const input = inputBox.value.trim();
|
||||
|
||||
if (input === "") {
|
||||
bboxInfo.textContent = "";
|
||||
bboxInfo.style.color = "";
|
||||
selectedBBox = "";
|
||||
return;
|
||||
}
|
||||
|
||||
// Regular expression to validate bbox input (supports both comma and space-separated formats)
|
||||
const bboxPattern = /^(-?\d+(\.\d+)?)[,\s](-?\d+(\.\d+)?)[,\s](-?\d+(\.\d+)?)[,\s](-?\d+(\.\d+)?)$/;
|
||||
|
||||
if (bboxPattern.test(input)) {
|
||||
const matches = input.match(bboxPattern);
|
||||
|
||||
// Extract coordinates (Lat / Lng order expected)
|
||||
const lat1 = parseFloat(matches[1]);
|
||||
const lng1 = parseFloat(matches[3]);
|
||||
const lat2 = parseFloat(matches[5]);
|
||||
const lng2 = parseFloat(matches[7]);
|
||||
|
||||
// Validate latitude and longitude ranges in the expected Lat / Lng order
|
||||
if (
|
||||
lat1 >= -90 && lat1 <= 90 &&
|
||||
lng1 >= -180 && lng1 <= 180 &&
|
||||
lat2 >= -90 && lat2 <= 90 &&
|
||||
lng2 >= -180 && lng2 <= 180
|
||||
) {
|
||||
// Input is valid; trigger the event
|
||||
const bboxText = `${lat1} ${lng1} ${lat2} ${lng2}`;
|
||||
window.dispatchEvent(new MessageEvent('message', { data: { bboxText } }));
|
||||
|
||||
// Update the info text
|
||||
bboxInfo.textContent = "Custom selection confirmed!";
|
||||
bboxInfo.style.color = "#7bd864";
|
||||
} else {
|
||||
// Valid numbers but invalid order or range
|
||||
bboxInfo.textContent = "Error: Coordinates are out of range or incorrectly ordered (Lat before Lng required).";
|
||||
bboxInfo.style.color = "#fecc44";
|
||||
selectedBBox = "";
|
||||
}
|
||||
} else {
|
||||
// Input doesn't match the required format
|
||||
bboxInfo.textContent = "Invalid format. Please use 'lat,lng,lat,lng' or 'lat lng lat lng'.";
|
||||
bboxInfo.style.color = "#fecc44";
|
||||
selectedBBox = "";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Function to calculate the bounding box "size" in square meters based on latitude and longitude
|
||||
function calculateBBoxSize(lng1, lat1, lng2, lat2) {
|
||||
@@ -145,26 +246,39 @@ async function pickDirectory() {
|
||||
}
|
||||
}
|
||||
|
||||
let generationButtonEnabled = true;
|
||||
async function startGeneration() {
|
||||
try {
|
||||
if (worldPath === "No world selected" || worldPath == "Invalid Minecraft world" || worldPath === "") {
|
||||
if (generationButtonEnabled === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedBBox || selectedBBox == "0.000000 0.000000 0.000000 0.000000") {
|
||||
document.getElementById('bbox-info').textContent = "Select a location first!";
|
||||
document.getElementById('bbox-info').style.color = "#fa7878";
|
||||
return;
|
||||
}
|
||||
|
||||
if (worldPath === "No world selected" || worldPath == "Invalid Minecraft world" || worldPath == "The selected world is currently in use" || worldPath === "") {
|
||||
document.getElementById('selected-world').textContent = "Select a Minecraft world first!";
|
||||
document.getElementById('selected-world').style.color = "#fa7878";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedBBox || selectedBBox == "0.000000 0.000000 0.000000 0.000000") {
|
||||
document.getElementById('bbox-info').textContent = "Select a location firsta using the rectangle tool!";
|
||||
document.getElementById('bbox-info').style.color = "#fa7878";
|
||||
return;
|
||||
}
|
||||
var winter_mode = document.getElementById("winter-toggle").checked;
|
||||
var scale = parseFloat(document.getElementById("scale-value-slider").value);
|
||||
var floodfill_timeout = parseInt(document.getElementById("floodfill-timeout").value, 10);
|
||||
|
||||
// Validate the floodfill timeout
|
||||
floodfill_timeout = isNaN(floodfill_timeout) || floodfill_timeout < 0 ? 20 : floodfill_timeout;
|
||||
|
||||
// Pass the bounding box and selected world to the Rust backend
|
||||
await invoke("gui_start_generation", { bboxText: selectedBBox, selectedWorld: worldPath });
|
||||
await invoke("gui_start_generation", { bboxText: selectedBBox, selectedWorld: worldPath, worldScale: scale, winterMode: winter_mode, floodfillTimeout: floodfill_timeout });
|
||||
|
||||
// Update the UI or show a loading/progress message if needed
|
||||
console.log("Generation process started.");
|
||||
generationButtonEnabled = false;
|
||||
} catch (error) {
|
||||
console.error("Error starting generation:", error);
|
||||
generationButtonEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,11 +33,15 @@ pub struct Args {
|
||||
#[arg(long, default_value = "1.0")]
|
||||
pub scale: f64,
|
||||
|
||||
/// Enable winter mode (default: false)
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub winter: bool,
|
||||
|
||||
/// Enable debug mode (optional)
|
||||
#[arg(long, default_value_t = false, action = clap::ArgAction::SetTrue)]
|
||||
pub debug: bool,
|
||||
|
||||
/// Set floodfill timeout (seconds) (optional) // TODO
|
||||
/// Set floodfill timeout (seconds) (optional)
|
||||
#[arg(long, value_parser = parse_duration)]
|
||||
pub timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
@@ -138,6 +138,8 @@ impl Block {
|
||||
108 => "potatoes",
|
||||
109 => "wheat",
|
||||
110 => "bedrock",
|
||||
111 => "snow_block",
|
||||
112 => "snow",
|
||||
_ => panic!("Invalid id"),
|
||||
}
|
||||
}
|
||||
@@ -285,6 +287,8 @@ pub const MAGENTA_CONCRETE: Block = Block::new(101);
|
||||
pub const BROWN_WOOL: Block = Block::new(102);
|
||||
pub const OXIDIZED_COPPER: Block = Block::new(103);
|
||||
pub const YELLOW_TERRACOTTA: Block = Block::new(104);
|
||||
pub const SNOW_BLOCK: Block = Block::new(111);
|
||||
pub const SNOW_LAYER: Block = Block::new(112);
|
||||
|
||||
pub const CARROTS: Block = Block::new(105);
|
||||
pub const DARK_OAK_DOOR_LOWER: Block = Block::new(106);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::args::Args;
|
||||
use crate::block_definitions::{DIRT, GRASS_BLOCK};
|
||||
use crate::block_definitions::{DIRT, GRASS_BLOCK, SNOW_BLOCK};
|
||||
use crate::element_processing::*;
|
||||
use crate::osm_parser::ProcessedElement;
|
||||
use crate::progress::emit_gui_progress_update;
|
||||
@@ -54,47 +54,17 @@ pub fn generate_world(
|
||||
match element {
|
||||
ProcessedElement::Way(way) => {
|
||||
if way.tags.contains_key("building") || way.tags.contains_key("building:part") {
|
||||
buildings::generate_buildings(
|
||||
&mut editor,
|
||||
way,
|
||||
GROUND_LEVEL,
|
||||
args.timeout.as_ref(),
|
||||
);
|
||||
buildings::generate_buildings(&mut editor, way, GROUND_LEVEL, args);
|
||||
} else if way.tags.contains_key("highway") {
|
||||
highways::generate_highways(
|
||||
&mut editor,
|
||||
element,
|
||||
GROUND_LEVEL,
|
||||
args.timeout.as_ref(),
|
||||
);
|
||||
highways::generate_highways(&mut editor, element, GROUND_LEVEL, args);
|
||||
} else if way.tags.contains_key("landuse") {
|
||||
landuse::generate_landuse(
|
||||
&mut editor,
|
||||
way,
|
||||
GROUND_LEVEL,
|
||||
args.timeout.as_ref(),
|
||||
);
|
||||
landuse::generate_landuse(&mut editor, way, GROUND_LEVEL, args);
|
||||
} else if way.tags.contains_key("natural") {
|
||||
natural::generate_natural(
|
||||
&mut editor,
|
||||
element,
|
||||
GROUND_LEVEL,
|
||||
args.timeout.as_ref(),
|
||||
);
|
||||
natural::generate_natural(&mut editor, element, GROUND_LEVEL, args);
|
||||
} else if way.tags.contains_key("amenity") {
|
||||
amenities::generate_amenities(
|
||||
&mut editor,
|
||||
element,
|
||||
GROUND_LEVEL,
|
||||
args.timeout.as_ref(),
|
||||
);
|
||||
amenities::generate_amenities(&mut editor, element, GROUND_LEVEL, args);
|
||||
} else if way.tags.contains_key("leisure") {
|
||||
leisure::generate_leisure(
|
||||
&mut editor,
|
||||
way,
|
||||
GROUND_LEVEL,
|
||||
args.timeout.as_ref(),
|
||||
);
|
||||
leisure::generate_leisure(&mut editor, way, GROUND_LEVEL, args);
|
||||
} else if way.tags.contains_key("barrier") {
|
||||
barriers::generate_barriers(&mut editor, element, GROUND_LEVEL);
|
||||
} else if way.tags.contains_key("waterway") {
|
||||
@@ -113,28 +83,13 @@ pub fn generate_world(
|
||||
} else if node.tags.contains_key("natural")
|
||||
&& node.tags.get("natural") == Some(&"tree".to_string())
|
||||
{
|
||||
natural::generate_natural(
|
||||
&mut editor,
|
||||
element,
|
||||
GROUND_LEVEL,
|
||||
args.timeout.as_ref(),
|
||||
);
|
||||
natural::generate_natural(&mut editor, element, GROUND_LEVEL, args);
|
||||
} else if node.tags.contains_key("amenity") {
|
||||
amenities::generate_amenities(
|
||||
&mut editor,
|
||||
element,
|
||||
GROUND_LEVEL,
|
||||
args.timeout.as_ref(),
|
||||
);
|
||||
amenities::generate_amenities(&mut editor, element, GROUND_LEVEL, args);
|
||||
} else if node.tags.contains_key("barrier") {
|
||||
barriers::generate_barriers(&mut editor, element, GROUND_LEVEL);
|
||||
} else if node.tags.contains_key("highway") {
|
||||
highways::generate_highways(
|
||||
&mut editor,
|
||||
element,
|
||||
GROUND_LEVEL,
|
||||
args.timeout.as_ref(),
|
||||
);
|
||||
highways::generate_highways(&mut editor, element, GROUND_LEVEL, args);
|
||||
} else if node.tags.contains_key("tourism") {
|
||||
tourisms::generate_tourisms(&mut editor, node, GROUND_LEVEL);
|
||||
}
|
||||
@@ -172,9 +127,11 @@ pub fn generate_world(
|
||||
let total_iterations_grnd: f64 = (scale_factor_x + 1.0) * (scale_factor_z + 1.0);
|
||||
let progress_increment_grnd: f64 = 30.0 / total_iterations_grnd;
|
||||
|
||||
let groundlayer_block = if args.winter { SNOW_BLOCK } else { GRASS_BLOCK };
|
||||
|
||||
for x in 0..=(scale_factor_x as i32) {
|
||||
for z in 0..=(scale_factor_z as i32) {
|
||||
editor.set_block(GRASS_BLOCK, x, GROUND_LEVEL, z, None, None);
|
||||
editor.set_block(groundlayer_block, x, GROUND_LEVEL, z, None, None);
|
||||
editor.set_block(DIRT, x, GROUND_LEVEL - 1, z, None, None);
|
||||
|
||||
block_counter += 1;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::args::Args;
|
||||
use crate::block_definitions::*;
|
||||
use crate::bresenham::bresenham_line;
|
||||
use crate::floodfill::flood_fill_area;
|
||||
@@ -10,7 +9,7 @@ pub fn generate_amenities(
|
||||
editor: &mut WorldEditor,
|
||||
element: &ProcessedElement,
|
||||
ground_level: i32,
|
||||
floodfill_timeout: Option<&Duration>,
|
||||
args: &Args,
|
||||
) {
|
||||
// Skip if 'layer' or 'level' is negative in the tags
|
||||
if let Some(layer) = element.tags().get("layer") {
|
||||
@@ -52,7 +51,7 @@ pub fn generate_amenities(
|
||||
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
|
||||
.collect();
|
||||
let floor_area: Vec<(i32, i32)> =
|
||||
flood_fill_area(&polygon_coords, floodfill_timeout);
|
||||
flood_fill_area(&polygon_coords, args.timeout.as_ref());
|
||||
|
||||
// Fill the floor area
|
||||
for (x, z) in floor_area.iter() {
|
||||
@@ -152,7 +151,7 @@ pub fn generate_amenities(
|
||||
if corner_addup.2 > 0 {
|
||||
let polygon_coords: Vec<(i32, i32)> = current_amenity.to_vec();
|
||||
let flood_area: Vec<(i32, i32)> =
|
||||
flood_fill_area(&polygon_coords, floodfill_timeout);
|
||||
flood_fill_area(&polygon_coords, args.timeout.as_ref());
|
||||
|
||||
for (x, z) in flood_area {
|
||||
editor.set_block(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::args::Args;
|
||||
use crate::block_definitions::*;
|
||||
use crate::bresenham::bresenham_line;
|
||||
use crate::colors::{color_text_to_rgb_tuple, rgb_distance, RGBTuple};
|
||||
@@ -12,7 +13,7 @@ pub fn generate_buildings(
|
||||
editor: &mut WorldEditor,
|
||||
element: &ProcessedWay,
|
||||
ground_level: i32,
|
||||
floodfill_timeout: Option<&Duration>,
|
||||
args: &Args,
|
||||
) {
|
||||
let mut previous_node: Option<(i32, i32)> = None;
|
||||
let mut corner_addup: (i32, i32, i32) = (0, 0, 0);
|
||||
@@ -95,7 +96,7 @@ pub fn generate_buildings(
|
||||
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
|
||||
.collect();
|
||||
let floor_area: Vec<(i32, i32)> =
|
||||
flood_fill_area(&polygon_coords, floodfill_timeout);
|
||||
flood_fill_area(&polygon_coords, args.timeout.as_ref());
|
||||
|
||||
// Fill the floor area
|
||||
for (x, z) in floor_area.iter() {
|
||||
@@ -152,7 +153,8 @@ pub fn generate_buildings(
|
||||
.iter()
|
||||
.map(|node: &crate::osm_parser::ProcessedNode| (node.x, node.z))
|
||||
.collect();
|
||||
let roof_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, floodfill_timeout); // Use flood-fill to determine the area
|
||||
let roof_area: Vec<(i32, i32)> =
|
||||
flood_fill_area(&polygon_coords, args.timeout.as_ref()); // Use flood-fill to determine the area
|
||||
|
||||
// Fill the interior of the roof with STONE_BRICK_SLAB
|
||||
for (x, z) in roof_area.iter() {
|
||||
@@ -172,7 +174,7 @@ pub fn generate_buildings(
|
||||
building_height = 23
|
||||
}
|
||||
} else if building_type == "bridge" {
|
||||
generate_bridge(editor, element, ground_level, floodfill_timeout);
|
||||
generate_bridge(editor, element, ground_level, args.timeout.as_ref());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -201,6 +203,7 @@ pub fn generate_buildings(
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ceiling cobblestone
|
||||
editor.set_block(
|
||||
COBBLESTONE,
|
||||
bx,
|
||||
@@ -208,7 +211,19 @@ pub fn generate_buildings(
|
||||
bz,
|
||||
None,
|
||||
None,
|
||||
); // Ceiling cobblestone
|
||||
);
|
||||
|
||||
if args.winter {
|
||||
editor.set_block(
|
||||
SNOW_LAYER,
|
||||
x,
|
||||
ground_level + building_height + 2,
|
||||
z,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
current_building.push((bx, bz));
|
||||
corner_addup = (corner_addup.0 + bx, corner_addup.1 + bz, corner_addup.2 + 1);
|
||||
}
|
||||
@@ -224,7 +239,7 @@ pub fn generate_buildings(
|
||||
.iter()
|
||||
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
|
||||
.collect();
|
||||
let floor_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, floodfill_timeout);
|
||||
let floor_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, args.timeout.as_ref());
|
||||
|
||||
for (x, z) in floor_area {
|
||||
if processed_points.insert((x, z)) {
|
||||
@@ -253,6 +268,17 @@ pub fn generate_buildings(
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
if args.winter {
|
||||
editor.set_block(
|
||||
SNOW_LAYER,
|
||||
x,
|
||||
ground_level + building_height + 2,
|
||||
z,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::args::Args;
|
||||
use crate::block_definitions::*;
|
||||
use crate::bresenham::bresenham_line;
|
||||
use crate::floodfill::flood_fill_area;
|
||||
use crate::osm_parser::{ProcessedElement, ProcessedWay};
|
||||
use crate::world_editor::WorldEditor; // Assuming you have a flood fill function for area filling
|
||||
use crate::world_editor::WorldEditor;
|
||||
|
||||
pub fn generate_highways(
|
||||
editor: &mut WorldEditor,
|
||||
element: &ProcessedElement,
|
||||
ground_level: i32,
|
||||
floodfill_timeout: Option<&Duration>,
|
||||
args: &Args,
|
||||
) {
|
||||
if let Some(highway_type) = element.tags().get("highway") {
|
||||
if highway_type == "street_lamp" {
|
||||
@@ -37,6 +36,10 @@ pub fn generate_highways(
|
||||
editor.set_block(GREEN_WOOL, x, ground_level + 4, z, None, None);
|
||||
editor.set_block(YELLOW_WOOL, x, ground_level + 5, z, None, None);
|
||||
editor.set_block(RED_WOOL, x, ground_level + 6, z, None, None);
|
||||
|
||||
if args.winter {
|
||||
editor.set_block(SNOW_LAYER, x, ground_level + 7, z, None, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,7 +75,13 @@ pub fn generate_highways(
|
||||
"wood" => OAK_PLANKS,
|
||||
"asphalt" => BLACK_CONCRETE,
|
||||
"gravel" | "fine_gravel" => GRAVEL,
|
||||
"grass" => GRASS_BLOCK,
|
||||
"grass" => {
|
||||
if args.winter {
|
||||
SNOW_BLOCK
|
||||
} else {
|
||||
GRASS_BLOCK
|
||||
}
|
||||
}
|
||||
"dirt" => DIRT,
|
||||
"sand" => SAND,
|
||||
"concrete" => LIGHT_GRAY_CONCRETE,
|
||||
@@ -86,7 +95,8 @@ pub fn generate_highways(
|
||||
.iter()
|
||||
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
|
||||
.collect();
|
||||
let filled_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, floodfill_timeout);
|
||||
let filled_area: Vec<(i32, i32)> =
|
||||
flood_fill_area(&polygon_coords, args.timeout.as_ref());
|
||||
|
||||
for (x, z) in filled_area {
|
||||
editor.set_block(surface_block, x, ground_level, z, None, None);
|
||||
@@ -95,7 +105,7 @@ pub fn generate_highways(
|
||||
let mut previous_node: Option<(i32, i32)> = None;
|
||||
let mut block_type = BLACK_CONCRETE;
|
||||
let mut block_range: i32 = 2;
|
||||
let mut add_stripe = false; // Flag for adding stripes
|
||||
let mut add_stripe = false;
|
||||
|
||||
// Skip if 'layer' or 'level' is negative in the tags
|
||||
if let Some(layer) = element.tags().get("layer") {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::args::Args;
|
||||
use crate::block_definitions::*;
|
||||
use crate::bresenham::bresenham_line;
|
||||
use crate::element_processing::tree::create_tree;
|
||||
@@ -12,7 +11,7 @@ pub fn generate_landuse(
|
||||
editor: &mut WorldEditor,
|
||||
element: &ProcessedWay,
|
||||
ground_level: i32,
|
||||
floodfill_timeout: Option<&Duration>,
|
||||
args: &Args,
|
||||
) {
|
||||
let mut previous_node: Option<(i32, i32)> = None;
|
||||
let mut corner_addup: (i32, i32, i32) = (0, 0, 0);
|
||||
@@ -23,9 +22,21 @@ pub fn generate_landuse(
|
||||
let landuse_tag: &String = element.tags.get("landuse").unwrap_or(&binding);
|
||||
|
||||
let block_type = match landuse_tag.as_str() {
|
||||
"greenfield" | "meadow" | "grass" => GRASS_BLOCK,
|
||||
"greenfield" | "meadow" | "grass" => {
|
||||
if args.winter {
|
||||
SNOW_BLOCK
|
||||
} else {
|
||||
GRASS_BLOCK
|
||||
}
|
||||
}
|
||||
"farmland" => FARMLAND,
|
||||
"forest" => GRASS_BLOCK,
|
||||
"forest" => {
|
||||
if args.winter {
|
||||
SNOW_BLOCK
|
||||
} else {
|
||||
GRASS_BLOCK
|
||||
}
|
||||
}
|
||||
"cemetery" => PODZOL,
|
||||
"beach" => SAND,
|
||||
"construction" => DIRT,
|
||||
@@ -36,9 +47,17 @@ pub fn generate_landuse(
|
||||
"industrial" => COBBLESTONE,
|
||||
"military" => GRAY_CONCRETE,
|
||||
"railway" => GRAVEL,
|
||||
_ => GRASS_BLOCK,
|
||||
_ => {
|
||||
if args.winter {
|
||||
SNOW_BLOCK
|
||||
} else {
|
||||
GRASS_BLOCK
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let bresenham_block: Block = if args.winter { SNOW_BLOCK } else { GRASS_BLOCK };
|
||||
|
||||
// Process landuse nodes to fill the area
|
||||
for node in &element.nodes {
|
||||
let x: i32 = node.x;
|
||||
@@ -49,7 +68,7 @@ pub fn generate_landuse(
|
||||
let bresenham_points: Vec<(i32, i32, i32)> =
|
||||
bresenham_line(prev.0, ground_level, prev.1, x, ground_level, z);
|
||||
for (bx, _, bz) in bresenham_points {
|
||||
editor.set_block(GRASS_BLOCK, bx, ground_level, bz, None, None);
|
||||
editor.set_block(bresenham_block, bx, ground_level, bz, None, None);
|
||||
}
|
||||
|
||||
current_landuse.push((x, z));
|
||||
@@ -62,7 +81,7 @@ pub fn generate_landuse(
|
||||
// If there are landuse nodes, flood-fill the area
|
||||
if !current_landuse.is_empty() {
|
||||
let polygon_coords: Vec<(i32, i32)> = element.nodes.iter().map(|n| (n.x, n.z)).collect();
|
||||
let floor_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, floodfill_timeout);
|
||||
let floor_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, args.timeout.as_ref());
|
||||
|
||||
let mut rng: rand::prelude::ThreadRng = rand::thread_rng();
|
||||
|
||||
@@ -156,7 +175,14 @@ pub fn generate_landuse(
|
||||
editor.set_block(RED_FLOWER, x, ground_level + 1, z, None, None);
|
||||
}
|
||||
} else if random_choice < 33 {
|
||||
create_tree(editor, x, ground_level + 1, z, rng.gen_range(1..=3));
|
||||
create_tree(
|
||||
editor,
|
||||
x,
|
||||
ground_level + 1,
|
||||
z,
|
||||
rng.gen_range(1..=3),
|
||||
args.winter,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +190,14 @@ pub fn generate_landuse(
|
||||
if !editor.check_for_block(x, ground_level, z, None, Some(&[WATER])) {
|
||||
let random_choice: i32 = rng.gen_range(0..21);
|
||||
if random_choice == 20 {
|
||||
create_tree(editor, x, ground_level + 1, z, rng.gen_range(1..=3));
|
||||
create_tree(
|
||||
editor,
|
||||
x,
|
||||
ground_level + 1,
|
||||
z,
|
||||
rng.gen_range(1..=3),
|
||||
args.winter,
|
||||
);
|
||||
} else if random_choice == 2 {
|
||||
let flower_block: Block = match rng.gen_range(1..=4) {
|
||||
1 => RED_FLOWER,
|
||||
@@ -206,6 +239,7 @@ pub fn generate_landuse(
|
||||
ground_level + 1,
|
||||
z,
|
||||
rng.gen_range(1..=3),
|
||||
args.winter,
|
||||
);
|
||||
} else if special_choice <= 6 {
|
||||
editor.set_block(HAY_BALE, x, ground_level + 1, z, None, None);
|
||||
@@ -319,7 +353,14 @@ pub fn generate_landuse(
|
||||
if !editor.check_for_block(x, ground_level, z, None, Some(&[WATER])) {
|
||||
let random_choice: i32 = rng.gen_range(0..1001);
|
||||
if random_choice < 5 {
|
||||
create_tree(editor, x, ground_level + 1, z, rng.gen_range(1..=3));
|
||||
create_tree(
|
||||
editor,
|
||||
x,
|
||||
ground_level + 1,
|
||||
z,
|
||||
rng.gen_range(1..=3),
|
||||
args.winter,
|
||||
);
|
||||
} else if random_choice < 800 {
|
||||
editor.set_block(GRASS, x, ground_level + 1, z, None, None);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::args::Args;
|
||||
use crate::block_definitions::*;
|
||||
use crate::bresenham::bresenham_line;
|
||||
use crate::element_processing::tree::create_tree;
|
||||
@@ -12,7 +11,7 @@ pub fn generate_leisure(
|
||||
editor: &mut WorldEditor,
|
||||
element: &ProcessedWay,
|
||||
ground_level: i32,
|
||||
floodfill_timeout: Option<&Duration>,
|
||||
args: &Args,
|
||||
) {
|
||||
if let Some(leisure_type) = element.tags.get("leisure") {
|
||||
let mut previous_node: Option<(i32, i32)> = None;
|
||||
@@ -21,7 +20,13 @@ pub fn generate_leisure(
|
||||
|
||||
// Determine block type based on leisure type
|
||||
let block_type: Block = match leisure_type.as_str() {
|
||||
"park" => GRASS_BLOCK,
|
||||
"park" => {
|
||||
if args.winter {
|
||||
SNOW_BLOCK
|
||||
} else {
|
||||
GRASS_BLOCK
|
||||
}
|
||||
}
|
||||
"playground" | "recreation_ground" | "pitch" => {
|
||||
if let Some(surface) = element.tags.get("surface") {
|
||||
match surface.as_str() {
|
||||
@@ -34,9 +39,21 @@ pub fn generate_leisure(
|
||||
GREEN_STAINED_HARDENED_CLAY
|
||||
}
|
||||
}
|
||||
"garden" => GRASS_BLOCK,
|
||||
"garden" => {
|
||||
if args.winter {
|
||||
SNOW_BLOCK
|
||||
} else {
|
||||
GRASS_BLOCK
|
||||
}
|
||||
}
|
||||
"swimming_pool" => WATER,
|
||||
_ => GRASS_BLOCK,
|
||||
_ => {
|
||||
if args.winter {
|
||||
SNOW_BLOCK
|
||||
} else {
|
||||
GRASS_BLOCK
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Process leisure area nodes
|
||||
@@ -78,7 +95,8 @@ pub fn generate_leisure(
|
||||
.iter()
|
||||
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
|
||||
.collect();
|
||||
let filled_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, floodfill_timeout);
|
||||
let filled_area: Vec<(i32, i32)> =
|
||||
flood_fill_area(&polygon_coords, args.timeout.as_ref());
|
||||
|
||||
for (x, z) in filled_area {
|
||||
editor.set_block(block_type, x, ground_level, z, Some(&[GRASS_BLOCK]), None);
|
||||
@@ -113,7 +131,14 @@ pub fn generate_leisure(
|
||||
}
|
||||
71..=80 => {
|
||||
// Tree
|
||||
create_tree(editor, x, ground_level + 1, z, rng.gen_range(1..=3));
|
||||
create_tree(
|
||||
editor,
|
||||
x,
|
||||
ground_level + 1,
|
||||
z,
|
||||
rng.gen_range(1..=3),
|
||||
args.winter,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::args::Args;
|
||||
use crate::block_definitions::*;
|
||||
use crate::bresenham::bresenham_line;
|
||||
use crate::element_processing::tree::create_tree;
|
||||
@@ -12,7 +11,7 @@ pub fn generate_natural(
|
||||
editor: &mut WorldEditor,
|
||||
element: &ProcessedElement,
|
||||
ground_level: i32,
|
||||
floodfill_timeout: Option<&Duration>,
|
||||
args: &Args,
|
||||
) {
|
||||
if let Some(natural_type) = element.tags().get("natural") {
|
||||
if natural_type == "tree" {
|
||||
@@ -21,7 +20,14 @@ pub fn generate_natural(
|
||||
let z: i32 = node.z;
|
||||
|
||||
let mut rng: rand::prelude::ThreadRng = rand::thread_rng();
|
||||
create_tree(editor, x, ground_level + 1, z, rng.gen_range(1..=3));
|
||||
create_tree(
|
||||
editor,
|
||||
x,
|
||||
ground_level + 1,
|
||||
z,
|
||||
rng.gen_range(1..=3),
|
||||
args.winter,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let mut previous_node: Option<(i32, i32)> = None;
|
||||
@@ -30,11 +36,29 @@ pub fn generate_natural(
|
||||
|
||||
// Determine block type based on natural tag
|
||||
let block_type: Block = match natural_type.as_str() {
|
||||
"scrub" | "grassland" | "wood" => GRASS_BLOCK,
|
||||
"scrub" | "grassland" | "wood" => {
|
||||
if args.winter {
|
||||
SNOW_BLOCK
|
||||
} else {
|
||||
GRASS_BLOCK
|
||||
}
|
||||
}
|
||||
"beach" | "sand" => SAND,
|
||||
"tree_row" => GRASS_BLOCK,
|
||||
"tree_row" => {
|
||||
if args.winter {
|
||||
SNOW_BLOCK
|
||||
} else {
|
||||
GRASS_BLOCK
|
||||
}
|
||||
}
|
||||
"wetland" | "water" => WATER,
|
||||
_ => GRASS_BLOCK,
|
||||
_ => {
|
||||
if args.winter {
|
||||
SNOW_BLOCK
|
||||
} else {
|
||||
GRASS_BLOCK
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let ProcessedElement::Way(way) = element else {
|
||||
@@ -69,7 +93,7 @@ pub fn generate_natural(
|
||||
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
|
||||
.collect();
|
||||
let filled_area: Vec<(i32, i32)> =
|
||||
flood_fill_area(&polygon_coords, floodfill_timeout);
|
||||
flood_fill_area(&polygon_coords, args.timeout.as_ref());
|
||||
|
||||
let mut rng: rand::prelude::ThreadRng = rand::thread_rng();
|
||||
|
||||
@@ -84,7 +108,14 @@ pub fn generate_natural(
|
||||
|
||||
let random_choice: i32 = rng.gen_range(0..26);
|
||||
if random_choice == 25 {
|
||||
create_tree(editor, x, ground_level + 1, z, rng.gen_range(1..=3));
|
||||
create_tree(
|
||||
editor,
|
||||
x,
|
||||
ground_level + 1,
|
||||
z,
|
||||
rng.gen_range(1..=3),
|
||||
args.winter,
|
||||
);
|
||||
} else if random_choice == 2 {
|
||||
let flower_block = match rng.gen_range(1..=4) {
|
||||
1 => RED_FLOWER,
|
||||
|
||||
@@ -46,7 +46,7 @@ fn round3(editor: &mut WorldEditor, material: Block, x: i32, y: i32, z: i32) {
|
||||
}
|
||||
|
||||
/// Function to create different types of trees.
|
||||
pub fn create_tree(editor: &mut WorldEditor, x: i32, y: i32, z: i32, typetree: u8) {
|
||||
pub fn create_tree(editor: &mut WorldEditor, x: i32, y: i32, z: i32, typetree: u8, snow: bool) {
|
||||
let mut blacklist: Vec<Block> = Vec::new();
|
||||
blacklist.extend(building_corner_variations());
|
||||
blacklist.extend(building_wall_variations());
|
||||
@@ -60,7 +60,6 @@ pub fn create_tree(editor: &mut WorldEditor, x: i32, y: i32, z: i32, typetree: u
|
||||
match typetree {
|
||||
1 => {
|
||||
// Oak tree
|
||||
|
||||
editor.fill_blocks(OAK_LOG, x, y, z, x, y + 8, z, None, None);
|
||||
editor.fill_blocks(OAK_LEAVES, x - 1, y + 3, z, x - 1, y + 9, z, None, None);
|
||||
editor.fill_blocks(OAK_LEAVES, x + 1, y + 3, z, x + 1, y + 9, z, None, None);
|
||||
@@ -79,6 +78,24 @@ pub fn create_tree(editor: &mut WorldEditor, x: i32, y: i32, z: i32, typetree: u
|
||||
round2(editor, OAK_LEAVES, x, y + 4, z);
|
||||
round3(editor, OAK_LEAVES, x, y + 6, z);
|
||||
round3(editor, OAK_LEAVES, x, y + 5, z);
|
||||
|
||||
if snow {
|
||||
editor.set_block(SNOW_LAYER, x, y + 11, z, None, None);
|
||||
editor.set_block(SNOW_LAYER, x + 1, y + 10, z, None, None);
|
||||
editor.set_block(SNOW_LAYER, x - 1, y + 10, z, None, None);
|
||||
editor.set_block(SNOW_LAYER, x, y + 10, z - 1, None, None);
|
||||
editor.set_block(SNOW_LAYER, x, y + 10, z + 1, None, None);
|
||||
round1(editor, SNOW_LAYER, x, y + 9, z);
|
||||
round1(editor, SNOW_LAYER, x, y + 8, z);
|
||||
round1(editor, SNOW_LAYER, x, y + 7, z);
|
||||
round1(editor, SNOW_LAYER, x, y + 6, z);
|
||||
round2(editor, SNOW_LAYER, x, y + 8, z);
|
||||
round2(editor, SNOW_LAYER, x, y + 7, z);
|
||||
round2(editor, SNOW_LAYER, x, y + 6, z);
|
||||
round2(editor, SNOW_LAYER, x, y + 5, z);
|
||||
round3(editor, SNOW_LAYER, x, y + 7, z);
|
||||
round3(editor, SNOW_LAYER, x, y + 6, z);
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
// Spruce tree
|
||||
@@ -95,6 +112,21 @@ pub fn create_tree(editor: &mut WorldEditor, x: i32, y: i32, z: i32, typetree: u
|
||||
round1(editor, BIRCH_LEAVES, x, y + 3, z);
|
||||
round2(editor, BIRCH_LEAVES, x, y + 6, z);
|
||||
round2(editor, BIRCH_LEAVES, x, y + 3, z);
|
||||
|
||||
if snow {
|
||||
editor.set_block(SNOW_LAYER, x, y + 11, z, None, None);
|
||||
editor.set_block(SNOW_LAYER, x + 1, y + 11, z, None, None);
|
||||
editor.set_block(SNOW_LAYER, x - 1, y + 11, z, None, None);
|
||||
editor.set_block(SNOW_LAYER, x, y + 11, z - 1, None, None);
|
||||
editor.set_block(SNOW_LAYER, x, y + 11, z + 1, None, None);
|
||||
round1(editor, SNOW_LAYER, x, y + 10, z);
|
||||
round1(editor, SNOW_LAYER, x, y + 8, z);
|
||||
round1(editor, SNOW_LAYER, x, y + 7, z);
|
||||
round1(editor, SNOW_LAYER, x, y + 5, z);
|
||||
round1(editor, SNOW_LAYER, x, y + 4, z);
|
||||
round2(editor, SNOW_LAYER, x, y + 7, z);
|
||||
round2(editor, SNOW_LAYER, x, y + 4, z);
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
// Birch tree
|
||||
@@ -112,6 +144,22 @@ pub fn create_tree(editor: &mut WorldEditor, x: i32, y: i32, z: i32, typetree: u
|
||||
round2(editor, BIRCH_LEAVES, x, y + 2, z);
|
||||
round2(editor, BIRCH_LEAVES, x, y + 3, z);
|
||||
round2(editor, BIRCH_LEAVES, x, y + 4, z);
|
||||
|
||||
if snow {
|
||||
editor.set_block(SNOW_LAYER, x, y + 9, z, None, None);
|
||||
editor.set_block(SNOW_LAYER, x + 1, y + 8, z, None, None);
|
||||
editor.set_block(SNOW_LAYER, x - 1, y + 8, z, None, None);
|
||||
editor.set_block(SNOW_LAYER, x, y + 8, z - 1, None, None);
|
||||
editor.set_block(SNOW_LAYER, x, y + 8, z + 1, None, None);
|
||||
round1(editor, SNOW_LAYER, x, y + 7, z);
|
||||
round1(editor, SNOW_LAYER, x, y + 6, z);
|
||||
round1(editor, SNOW_LAYER, x, y + 5, z);
|
||||
round1(editor, SNOW_LAYER, x, y + 4, z);
|
||||
round1(editor, SNOW_LAYER, x, y + 3, z);
|
||||
round2(editor, SNOW_LAYER, x, y + 3, z);
|
||||
round2(editor, SNOW_LAYER, x, y + 4, z);
|
||||
round2(editor, SNOW_LAYER, x, y + 5, z);
|
||||
}
|
||||
}
|
||||
_ => {} // Do nothing if typetree is not recognized
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ pub fn flood_fill_area(
|
||||
while let Some((start_x, start_z)) = candidate_points.pop_front() {
|
||||
if let Some(timeout) = timeout {
|
||||
if &start_time.elapsed() > timeout {
|
||||
eprintln!("Floodfill timeout"); // TODO only print when debug arg is set?
|
||||
eprintln!("Floodfill timeout");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ pub fn flood_fill_area(
|
||||
while let Some((x, z)) = queue.pop_front() {
|
||||
if let Some(timeout) = timeout {
|
||||
if &start_time.elapsed() > timeout {
|
||||
eprintln!("Floodfill timeout"); // TODO only print when debug arg is set?
|
||||
eprintln!("Floodfill timeout");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
28
src/main.rs
28
src/main.rs
@@ -16,6 +16,7 @@ mod world_editor;
|
||||
use args::Args;
|
||||
use clap::Parser;
|
||||
use colored::*;
|
||||
use fs2::FileExt;
|
||||
use rfd::FileDialog;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
@@ -168,6 +169,20 @@ fn gui_pick_directory() -> Result<String, String> {
|
||||
|
||||
// Check if the "region" folder exists within the selected directory
|
||||
if path.join("region").exists() {
|
||||
// Check the 'session.lock' file
|
||||
let session_lock_path = path.join("session.lock");
|
||||
if session_lock_path.exists() {
|
||||
// Try to acquire a lock on the session.lock file
|
||||
if let Ok(file) = File::open(&session_lock_path) {
|
||||
if file.try_lock_shared().is_err() {
|
||||
return Err("The selected world is currently in use".to_string());
|
||||
} else {
|
||||
// Release the lock immediately
|
||||
let _ = file.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(path.display().to_string());
|
||||
} else {
|
||||
// Notify the frontend that no valid Minecraft world was found
|
||||
@@ -193,7 +208,13 @@ fn gui_check_for_updates() -> Result<bool, String> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn gui_start_generation(bbox_text: String, selected_world: String) -> Result<(), String> {
|
||||
fn gui_start_generation(
|
||||
bbox_text: String,
|
||||
selected_world: String,
|
||||
world_scale: f64,
|
||||
winter_mode: bool,
|
||||
floodfill_timeout: u64,
|
||||
) -> Result<(), String> {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
if let Err(e) = tokio::task::spawn_blocking(move || {
|
||||
// Utility function to reorder bounding box coordinates
|
||||
@@ -217,9 +238,10 @@ fn gui_start_generation(bbox_text: String, selected_world: String) -> Result<(),
|
||||
file: None,
|
||||
path: selected_world,
|
||||
downloader: "requests".to_string(),
|
||||
scale: 1.0,
|
||||
scale: world_scale,
|
||||
winter: winter_mode,
|
||||
debug: false,
|
||||
timeout: None,
|
||||
timeout: Some(std::time::Duration::from_secs(floodfill_timeout)),
|
||||
};
|
||||
|
||||
// Reorder bounding box coordinates for further processing
|
||||
|
||||
@@ -389,7 +389,7 @@ impl<'a> WorldEditor<'a> {
|
||||
.progress_chars("█▓░"),
|
||||
);
|
||||
|
||||
let total_steps: f64 = 10.0;
|
||||
let total_steps: f64 = 9.0;
|
||||
let progress_increment_save: f64 = total_steps / total_regions as f64;
|
||||
let mut current_progress_save: f64 = 90.0;
|
||||
let mut last_emitted_progress: f64 = current_progress_save;
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"height": 650,
|
||||
"resizable": false,
|
||||
"transparent": true,
|
||||
"center": true
|
||||
"center": true,
|
||||
"theme": "Dark"
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
|
||||
Reference in New Issue
Block a user