mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Send client 3D rendered map.
Also shares configurable rendering between map generator and server.
This commit is contained in:
parent
9ee0cd82d0
commit
ebe0d14eab
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1572,6 +1572,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ pub struct Tick(u64);
|
|||||||
pub struct Server {
|
pub struct Server {
|
||||||
state: State,
|
state: State,
|
||||||
world: Arc<World>,
|
world: Arc<World>,
|
||||||
|
map: Vec<u32>,
|
||||||
|
|
||||||
postoffice: PostOffice<ServerMsg, ClientMsg>,
|
postoffice: PostOffice<ServerMsg, ClientMsg>,
|
||||||
|
|
||||||
@ -119,6 +120,7 @@ impl Server {
|
|||||||
..WorldOpts::default()
|
..WorldOpts::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
let map = world.sim().get_map();
|
||||||
|
|
||||||
let spawn_point = {
|
let spawn_point = {
|
||||||
// NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops,
|
// NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops,
|
||||||
@ -178,6 +180,7 @@ impl Server {
|
|||||||
let this = Self {
|
let this = Self {
|
||||||
state,
|
state,
|
||||||
world: Arc::new(world),
|
world: Arc::new(world),
|
||||||
|
map,
|
||||||
|
|
||||||
postoffice: PostOffice::bind(settings.gameserver_address)?,
|
postoffice: PostOffice::bind(settings.gameserver_address)?,
|
||||||
|
|
||||||
@ -1084,7 +1087,7 @@ impl Server {
|
|||||||
.create_entity_package(entity),
|
.create_entity_package(entity),
|
||||||
server_info: self.server_info.clone(),
|
server_info: self.server_info.clone(),
|
||||||
time_of_day: *self.state.ecs().read_resource(),
|
time_of_day: *self.state.ecs().read_resource(),
|
||||||
world_map: (WORLD_SIZE.map(|e| e as u32), self.world.sim().get_map()),
|
world_map: (WORLD_SIZE.map(|e| e as u32), self.map.clone()),
|
||||||
});
|
});
|
||||||
log::debug!("Done initial sync with client.");
|
log::debug!("Done initial sync with client.");
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ use common::{terrain::TerrainChunkSize, vol::RectVolSize};
|
|||||||
use std::{f32, f64, path::PathBuf};
|
use std::{f32, f64, path::PathBuf};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use veloren_world::{
|
use veloren_world::{
|
||||||
sim::{self, RiverKind, WorldOpts, WORLD_SIZE},
|
sim::{self, MapConfig, MapDebug, RiverKind, WorldOpts, WORLD_SIZE},
|
||||||
util::Sampler,
|
util::Sampler,
|
||||||
World, CONFIG,
|
World, CONFIG,
|
||||||
};
|
};
|
||||||
@ -46,11 +46,11 @@ fn main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let sampler = world.sim();
|
|
||||||
|
|
||||||
let mut win =
|
let mut win =
|
||||||
minifb::Window::new("World Viewer", W, H, minifb::WindowOptions::default()).unwrap();
|
minifb::Window::new("World Viewer", W, H, minifb::WindowOptions::default()).unwrap();
|
||||||
|
|
||||||
|
let sampler = world.sim();
|
||||||
|
|
||||||
let mut focus = Vec3::new(0.0, 0.0, CONFIG.sea_level as f64);
|
let mut focus = Vec3::new(0.0, 0.0, CONFIG.sea_level as f64);
|
||||||
// Altitude is divided by gain and clamped to [0, 1]; thus, decreasing gain makes
|
// Altitude is divided by gain and clamped to [0, 1]; thus, decreasing gain makes
|
||||||
// smaller differences in altitude appear larger.
|
// smaller differences in altitude appear larger.
|
||||||
@ -67,7 +67,6 @@ fn main() {
|
|||||||
//
|
//
|
||||||
// "In world space the x-axis will be pointing east, the y-axis up and the z-axis will be pointing south"
|
// "In world space the x-axis will be pointing east, the y-axis up and the z-axis will be pointing south"
|
||||||
let mut light_direction = Vec3::new(-0.8, -1.0, 0.3);
|
let mut light_direction = Vec3::new(-0.8, -1.0, 0.3);
|
||||||
let light_res = 3;
|
|
||||||
|
|
||||||
let mut is_basement = false;
|
let mut is_basement = false;
|
||||||
let mut is_water = true;
|
let mut is_water = true;
|
||||||
@ -76,199 +75,33 @@ fn main() {
|
|||||||
let mut is_humidity = true;
|
let mut is_humidity = true;
|
||||||
|
|
||||||
while win.is_open() {
|
while win.is_open() {
|
||||||
let light = light_direction.normalized();
|
let config = MapConfig {
|
||||||
let mut buf = vec![0; W * H];
|
dimensions: Vec2::new(W, H),
|
||||||
const QUADRANTS: usize = 4;
|
focus,
|
||||||
let mut quads = [[0u32; QUADRANTS]; QUADRANTS];
|
gain,
|
||||||
let mut rivers = 0u32;
|
lgain,
|
||||||
let mut lakes = 0u32;
|
scale,
|
||||||
let mut oceans = 0u32;
|
light_direction,
|
||||||
|
|
||||||
// let water_light = (light_direction.z + 1.0) / 2.0 * 0.8 + 0.2;
|
is_basement,
|
||||||
let focus_rect = Vec2::from(focus);
|
is_water,
|
||||||
let true_sea_level = (CONFIG.sea_level as f64 - focus.z) / gain as f64;
|
is_shaded,
|
||||||
|
is_temperature,
|
||||||
for i in 0..W {
|
is_humidity,
|
||||||
for j in 0..H {
|
is_debug: true,
|
||||||
let pos =
|
|
||||||
(focus_rect + Vec2::new(i as f64, j as f64) * scale).map(|e: f64| e as i32);
|
|
||||||
/* let top_left = pos;
|
|
||||||
let top_right = focus + Vec2::new(i as i32 + light_res, j as i32) * scale;
|
|
||||||
let bottom_left = focus + Vec2::new(i as i32, j as i32 + light_res) * scale; */
|
|
||||||
|
|
||||||
let (alt, basement, water_alt, humidity, temperature, downhill, river_kind) =
|
|
||||||
sampler
|
|
||||||
.get(pos)
|
|
||||||
.map(|sample| {
|
|
||||||
(
|
|
||||||
sample.alt,
|
|
||||||
sample.basement,
|
|
||||||
sample.water_alt,
|
|
||||||
sample.humidity,
|
|
||||||
sample.temp,
|
|
||||||
sample.downhill,
|
|
||||||
sample.river.river_kind,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or((
|
|
||||||
CONFIG.sea_level,
|
|
||||||
CONFIG.sea_level,
|
|
||||||
CONFIG.sea_level,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
let humidity = humidity.min(1.0).max(0.0);
|
|
||||||
let temperature = temperature.min(1.0).max(-1.0) * 0.5 + 0.5;
|
|
||||||
let pos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
|
|
||||||
let downhill_pos = (downhill
|
|
||||||
.map(|downhill_pos| downhill_pos/*.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32)*/)
|
|
||||||
.unwrap_or(pos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
|
||||||
- pos)/* * scale*/
|
|
||||||
+ pos;
|
|
||||||
let downhill_alt = sampler
|
|
||||||
.get_wpos(downhill_pos)
|
|
||||||
.map(|s| if is_basement { s.basement } else { s.alt })
|
|
||||||
.unwrap_or(CONFIG.sea_level);
|
|
||||||
let alt = if is_basement { basement } else { alt };
|
|
||||||
/* let alt_tl = sampler.get(top_left).map(|s| s.alt)
|
|
||||||
.unwrap_or(CONFIG.sea_level);
|
|
||||||
let alt_tr = sampler.get(top_right).map(|s| s.alt)
|
|
||||||
.unwrap_or(CONFIG.sea_level);
|
|
||||||
let alt_bl = sampler.get(bottom_left).map(|s| s.alt)
|
|
||||||
.unwrap_or(CONFIG.sea_level); */
|
|
||||||
let cross_pos = pos
|
|
||||||
+ ((downhill_pos - pos)
|
|
||||||
.map(|e| e as f32)
|
|
||||||
.rotated_z(f32::consts::FRAC_PI_2)
|
|
||||||
.map(|e| e as i32));
|
|
||||||
let cross_alt = sampler
|
|
||||||
.get_wpos(cross_pos)
|
|
||||||
.map(|s| if is_basement { s.basement } else { s.alt })
|
|
||||||
.unwrap_or(CONFIG.sea_level);
|
|
||||||
// Pointing downhill, forward
|
|
||||||
// (index--note that (0,0,1) is backward right-handed)
|
|
||||||
let forward_vec = Vec3::new(
|
|
||||||
(downhill_pos.x - pos.x) as f64,
|
|
||||||
(downhill_alt - alt) as f64 * lgain,
|
|
||||||
(downhill_pos.y - pos.y) as f64,
|
|
||||||
);
|
|
||||||
// Pointing 90 degrees left (in horizontal xy) of downhill, up
|
|
||||||
// (middle--note that (1,0,0), 90 degrees CCW backward, is right right-handed)
|
|
||||||
let up_vec = Vec3::new(
|
|
||||||
(cross_pos.x - pos.x) as f64,
|
|
||||||
(cross_alt - alt) as f64 * lgain,
|
|
||||||
(cross_pos.y - pos.y) as f64,
|
|
||||||
);
|
|
||||||
// Then cross points "to the right" (upwards) on a right-handed coordinate system.
|
|
||||||
// (right-handed coordinate system means (0, 0, 1.0) is "forward" into the screen).
|
|
||||||
let surface_normal = forward_vec.cross(up_vec).normalized();
|
|
||||||
// f = (0, alt_bl - alt_tl, 1) [backward right-handed = (0,0,1)]
|
|
||||||
// u = (1, alt_tr - alt_tl, 0) [right (90 degrees CCW backward) = (1,0,0)]
|
|
||||||
// (f × u in right-handed coordinate system: pointing up)
|
|
||||||
//
|
|
||||||
// f × u =
|
|
||||||
// (a.y*b.z - a.z*b.y,
|
|
||||||
// a.z*b.x - a.x*b.z,
|
|
||||||
// a.x*b.y - a.y*b.x,
|
|
||||||
// )
|
|
||||||
// =
|
|
||||||
// (-(alt_tr - alt_tl),
|
|
||||||
// 1,
|
|
||||||
// -(alt_bl - alt_tl),
|
|
||||||
// )
|
|
||||||
// =
|
|
||||||
// (alt_tl - alt_tr,
|
|
||||||
// 1,
|
|
||||||
// alt_tl - alt_bl,
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// let surface_normal = Vec3::new((alt_tl - alt_tr) as f64, 1.0, (alt_tl - alt_bl) as f64).normalized();
|
|
||||||
let light = (surface_normal.dot(light) + 1.0) / 2.0;
|
|
||||||
let light = (light * 0.9) + 0.1;
|
|
||||||
|
|
||||||
let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64;
|
|
||||||
let true_alt = (alt as f64 - focus.z) / gain as f64;
|
|
||||||
let water_depth = (true_water_alt - true_alt).min(1.0).max(0.0);
|
|
||||||
let water_alt = true_water_alt.min(1.0).max(0.0);
|
|
||||||
let alt = true_alt.min(1.0).max(0.0);
|
|
||||||
let quad =
|
|
||||||
|x: f32| ((x as f64 * QUADRANTS as f64).floor() as usize).min(QUADRANTS - 1);
|
|
||||||
if river_kind.is_none() || humidity != 0.0 {
|
|
||||||
quads[quad(humidity)][quad(temperature)] += 1;
|
|
||||||
}
|
|
||||||
match river_kind {
|
|
||||||
Some(RiverKind::River { .. }) => {
|
|
||||||
rivers += 1;
|
|
||||||
}
|
|
||||||
Some(RiverKind::Lake { .. }) => {
|
|
||||||
lakes += 1;
|
|
||||||
}
|
|
||||||
Some(RiverKind::Ocean { .. }) => {
|
|
||||||
oceans += 1;
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[j * W + i] = match (river_kind, (is_water, true_alt >= true_sea_level)) {
|
|
||||||
(_, (false, _)) | (None, (_, true)) => {
|
|
||||||
let (r, g, b) = (
|
|
||||||
(if is_shaded { alt } else { alt }
|
|
||||||
* if is_temperature {
|
|
||||||
temperature as f64
|
|
||||||
} else if is_shaded {
|
|
||||||
alt
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
})
|
|
||||||
.sqrt(),
|
|
||||||
if is_shaded { 0.2 + (alt * 0.8) } else { alt },
|
|
||||||
(if is_shaded { alt } else { alt }
|
|
||||||
* if is_humidity {
|
|
||||||
humidity as f64
|
|
||||||
} else if is_shaded {
|
|
||||||
alt
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
})
|
|
||||||
.sqrt(),
|
|
||||||
);
|
|
||||||
let light = if is_shaded { light } else { 1.0 };
|
|
||||||
u32::from_le_bytes([
|
|
||||||
(b * light * 255.0) as u8,
|
|
||||||
(g * light * 255.0) as u8,
|
|
||||||
(r * light * 255.0) as u8,
|
|
||||||
255,
|
|
||||||
])
|
|
||||||
/* u32::from_le_bytes([
|
|
||||||
(/*alt * *//*(1.0 - humidity)*/(alt * humidity).sqrt()/*temperature*/ * 255.0) as u8,
|
|
||||||
(/*alt*//*alt*//* * humidity*//*alt * 255.0*//*humidity*/alt * 255.0) as u8,
|
|
||||||
(/*alt*//*alt * *//*(1.0 - humidity)*/(alt * temperature).sqrt() * 255.0) as u8,
|
|
||||||
255,
|
|
||||||
]) */
|
|
||||||
}
|
|
||||||
(Some(RiverKind::Ocean), _) => u32::from_le_bytes([
|
|
||||||
((64.0 - water_depth * 64.0) * 1.0) as u8,
|
|
||||||
((32.0 - water_depth * 32.0) * 1.0) as u8,
|
|
||||||
0,
|
|
||||||
255,
|
|
||||||
]),
|
|
||||||
(Some(RiverKind::River { .. }), _) => u32::from_le_bytes([
|
|
||||||
64 + (alt * 191.0) as u8,
|
|
||||||
32 + (alt * 95.0) as u8,
|
|
||||||
0,
|
|
||||||
255,
|
|
||||||
]),
|
|
||||||
(None, _) | (Some(RiverKind::Lake { .. }), _) => u32::from_le_bytes([
|
|
||||||
(((64.0 + water_alt * 191.0) + (-water_depth * 64.0)) * 1.0) as u8,
|
|
||||||
(((32.0 + water_alt * 95.0) + (-water_depth * 32.0)) * 1.0) as u8,
|
|
||||||
0,
|
|
||||||
255,
|
|
||||||
]),
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
let mut buf = vec![0; W * H];
|
||||||
|
let MapDebug {
|
||||||
|
rivers,
|
||||||
|
lakes,
|
||||||
|
oceans,
|
||||||
|
quads,
|
||||||
|
} = config.generate(sampler, |pos, (r, g, b, a)| {
|
||||||
|
let i = pos.x;
|
||||||
|
let j = pos.y;
|
||||||
|
buf[j * W + i] = u32::from_le_bytes([b, g, r, a]);
|
||||||
|
});
|
||||||
|
|
||||||
let spd = 32.0;
|
let spd = 32.0;
|
||||||
let lspd = 0.1;
|
let lspd = 0.1;
|
||||||
@ -299,8 +132,8 @@ fn main() {
|
|||||||
}
|
}
|
||||||
if win.get_mouse_down(minifb::MouseButton::Left) {
|
if win.get_mouse_down(minifb::MouseButton::Left) {
|
||||||
if let Some((mx, my)) = win.get_mouse_pos(minifb::MouseMode::Clamp) {
|
if let Some((mx, my)) = win.get_mouse_pos(minifb::MouseMode::Clamp) {
|
||||||
let pos =
|
let pos = (Vec2::<f64>::from(focus) + (Vec2::new(mx as f64, my as f64) * scale))
|
||||||
(focus_rect + (Vec2::new(mx as f64, my as f64) * scale)).map(|e| e as i32);
|
.map(|e| e as i32);
|
||||||
println!(
|
println!(
|
||||||
"Chunk position: {:?}",
|
"Chunk position: {:?}",
|
||||||
pos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e * f as i32)
|
pos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e * f as i32)
|
||||||
|
315
world/src/sim/map.rs
Normal file
315
world/src/sim/map.rs
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
use crate::{
|
||||||
|
sim::{self, uniform_idx_as_vec2, RiverKind, WorldSim, WORLD_SIZE},
|
||||||
|
util::Sampler,
|
||||||
|
CONFIG,
|
||||||
|
};
|
||||||
|
use common::{terrain::TerrainChunkSize, vol::RectVolSize};
|
||||||
|
use std::{f32, f64};
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
pub struct MapConfig {
|
||||||
|
/// Dimensions of the window being written to. Defaults to WORLD_SIZE.
|
||||||
|
pub dimensions: Vec2<usize>,
|
||||||
|
/// x, y, and z of top left of map (defaults to (0.0, 0.0, CONFIG.sea_level)).
|
||||||
|
pub focus: Vec3<f64>,
|
||||||
|
/// Altitude is divided by gain and clamped to [0, 1]; thus, decreasing gain makes
|
||||||
|
/// smaller differences in altitude appear larger.
|
||||||
|
///
|
||||||
|
/// Defaults to CONFIG.mountain_scale.
|
||||||
|
pub gain: f32,
|
||||||
|
/// lgain is used for shading purposes and refers to how much impact a change in the z
|
||||||
|
/// direction has on the perceived slope relative to the same change in x and y.
|
||||||
|
///
|
||||||
|
/// Defaults to TerrainChunkSize::RECT_SIZE.x.
|
||||||
|
pub lgain: f64,
|
||||||
|
/// Scale is like gain, but for x and y rather than z.
|
||||||
|
///
|
||||||
|
/// Defaults to WORLD_SIZE.x / dimensions.x (NOTE: fractional, not integer, division!).
|
||||||
|
pub scale: f64,
|
||||||
|
/// Vector that indicates which direction light is coming from, if shading is turned on.
|
||||||
|
///
|
||||||
|
/// Right-handed coordinate system: light is going left, down, and "backwards" (i.e. on the
|
||||||
|
/// map, where we translate the y coordinate on the world map to z in the coordinate system,
|
||||||
|
/// the light comes from -y on the map and points towards +y on the map). In a right
|
||||||
|
/// handed coordinate system, the "camera" points towards -z, so positive z is backwards
|
||||||
|
/// "into" the camera.
|
||||||
|
///
|
||||||
|
/// "In world space the x-axis will be pointing east, the y-axis up and the z-axis will be pointing south"
|
||||||
|
///
|
||||||
|
/// Defaults to (-0.8, -1.0, 0.3).
|
||||||
|
pub light_direction: Vec3<f64>,
|
||||||
|
/// If true, only the basement (bedrock) is used for altitude; otherwise, the surface is used.
|
||||||
|
///
|
||||||
|
/// Defaults to false.
|
||||||
|
pub is_basement: bool,
|
||||||
|
/// If true, water is rendered; otherwise, the surface without water is rendered, even if it
|
||||||
|
/// is underwater.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
pub is_water: bool,
|
||||||
|
/// If true, 3D lighting and shading are turned on. Otherwise, a plain altitude map is used.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
pub is_shaded: bool,
|
||||||
|
/// If true, the red component of the image is also used for temperature (redder is hotter).
|
||||||
|
/// Defaults to false.
|
||||||
|
pub is_temperature: bool,
|
||||||
|
/// If true, the blue component of the image is also used for humidity (bluer is wetter).
|
||||||
|
///
|
||||||
|
/// Defaults to false.
|
||||||
|
pub is_humidity: bool,
|
||||||
|
/// Record debug information.
|
||||||
|
///
|
||||||
|
/// Defaults to false.
|
||||||
|
pub is_debug: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const QUADRANTS: usize = 4;
|
||||||
|
|
||||||
|
pub struct MapDebug {
|
||||||
|
pub quads: [[u32; QUADRANTS]; QUADRANTS],
|
||||||
|
pub rivers: u32,
|
||||||
|
pub lakes: u32,
|
||||||
|
pub oceans: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MapConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
let dimensions = WORLD_SIZE;
|
||||||
|
Self {
|
||||||
|
dimensions,
|
||||||
|
focus: Vec3::new(0.0, 0.0, CONFIG.sea_level as f64),
|
||||||
|
gain: CONFIG.mountain_scale,
|
||||||
|
lgain: TerrainChunkSize::RECT_SIZE.x as f64,
|
||||||
|
scale: WORLD_SIZE.x as f64 / dimensions.x as f64,
|
||||||
|
light_direction: Vec3::new(-0.8, -1.0, 0.3),
|
||||||
|
|
||||||
|
is_basement: false,
|
||||||
|
is_water: true,
|
||||||
|
is_shaded: true,
|
||||||
|
is_temperature: false,
|
||||||
|
is_humidity: false,
|
||||||
|
is_debug: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapConfig {
|
||||||
|
/// Generates a map image using the specified settings. Note that it will write from left to
|
||||||
|
/// write from (0, 0) to dimensions - 1, inclusive, with 4 1-byte color components provided
|
||||||
|
/// as (r, g, b, a). It is up to the caller to provide a function that translates this
|
||||||
|
/// information into the correct format for a buffer and writes to it.
|
||||||
|
pub fn generate(
|
||||||
|
&self,
|
||||||
|
sampler: &WorldSim,
|
||||||
|
mut write_pixel: impl FnMut(Vec2<usize>, (u8, u8, u8, u8)),
|
||||||
|
) -> MapDebug {
|
||||||
|
let MapConfig {
|
||||||
|
dimensions,
|
||||||
|
focus,
|
||||||
|
gain,
|
||||||
|
lgain,
|
||||||
|
scale,
|
||||||
|
light_direction,
|
||||||
|
|
||||||
|
is_basement,
|
||||||
|
is_water,
|
||||||
|
is_shaded,
|
||||||
|
is_temperature,
|
||||||
|
is_humidity,
|
||||||
|
is_debug,
|
||||||
|
} = *self;
|
||||||
|
|
||||||
|
let light = light_direction.normalized();
|
||||||
|
let mut quads = [[0u32; QUADRANTS]; QUADRANTS];
|
||||||
|
let mut rivers = 0u32;
|
||||||
|
let mut lakes = 0u32;
|
||||||
|
let mut oceans = 0u32;
|
||||||
|
|
||||||
|
// let water_light = (light_direction.z + 1.0) / 2.0 * 0.8 + 0.2;
|
||||||
|
let focus_rect = Vec2::from(focus);
|
||||||
|
let true_sea_level = (CONFIG.sea_level as f64 - focus.z) / gain as f64;
|
||||||
|
|
||||||
|
(0..dimensions.y * dimensions.x)
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|chunk_idx| {
|
||||||
|
let pos = uniform_idx_as_vec2(chunk_idx);
|
||||||
|
let i = pos.x as usize;
|
||||||
|
let j = pos.y as usize;
|
||||||
|
|
||||||
|
let pos =
|
||||||
|
(focus_rect + Vec2::new(i as f64, j as f64) * scale).map(|e: f64| e as i32);
|
||||||
|
|
||||||
|
let (alt, basement, water_alt, humidity, temperature, downhill, river_kind) =
|
||||||
|
sampler
|
||||||
|
.get(pos)
|
||||||
|
.map(|sample| {
|
||||||
|
(
|
||||||
|
sample.alt,
|
||||||
|
sample.basement,
|
||||||
|
sample.water_alt,
|
||||||
|
sample.humidity,
|
||||||
|
sample.temp,
|
||||||
|
sample.downhill,
|
||||||
|
sample.river.river_kind,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or((
|
||||||
|
CONFIG.sea_level,
|
||||||
|
CONFIG.sea_level,
|
||||||
|
CONFIG.sea_level,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
let humidity = humidity.min(1.0).max(0.0);
|
||||||
|
let temperature = temperature.min(1.0).max(-1.0) * 0.5 + 0.5;
|
||||||
|
let pos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
|
||||||
|
let downhill_pos = (downhill
|
||||||
|
.map(|downhill_pos| downhill_pos/*.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32)*/)
|
||||||
|
.unwrap_or(pos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
||||||
|
- pos)/* * scale*/
|
||||||
|
+ pos;
|
||||||
|
let downhill_alt = sampler
|
||||||
|
.get_wpos(downhill_pos)
|
||||||
|
.map(|s| if is_basement { s.basement } else { s.alt })
|
||||||
|
.unwrap_or(CONFIG.sea_level);
|
||||||
|
let alt = if is_basement { basement } else { alt };
|
||||||
|
let cross_pos = pos
|
||||||
|
+ ((downhill_pos - pos)
|
||||||
|
.map(|e| e as f32)
|
||||||
|
.rotated_z(f32::consts::FRAC_PI_2)
|
||||||
|
.map(|e| e as i32));
|
||||||
|
let cross_alt = sampler
|
||||||
|
.get_wpos(cross_pos)
|
||||||
|
.map(|s| if is_basement { s.basement } else { s.alt })
|
||||||
|
.unwrap_or(CONFIG.sea_level);
|
||||||
|
// Pointing downhill, forward
|
||||||
|
// (index--note that (0,0,1) is backward right-handed)
|
||||||
|
let forward_vec = Vec3::new(
|
||||||
|
(downhill_pos.x - pos.x) as f64,
|
||||||
|
(downhill_alt - alt) as f64 * lgain,
|
||||||
|
(downhill_pos.y - pos.y) as f64,
|
||||||
|
);
|
||||||
|
// Pointing 90 degrees left (in horizontal xy) of downhill, up
|
||||||
|
// (middle--note that (1,0,0), 90 degrees CCW backward, is right right-handed)
|
||||||
|
let up_vec = Vec3::new(
|
||||||
|
(cross_pos.x - pos.x) as f64,
|
||||||
|
(cross_alt - alt) as f64 * lgain,
|
||||||
|
(cross_pos.y - pos.y) as f64,
|
||||||
|
);
|
||||||
|
// Then cross points "to the right" (upwards) on a right-handed coordinate system.
|
||||||
|
// (right-handed coordinate system means (0, 0, 1.0) is "forward" into the screen).
|
||||||
|
let surface_normal = forward_vec.cross(up_vec).normalized();
|
||||||
|
// f = (0, alt_bl - alt_tl, 1) [backward right-handed = (0,0,1)]
|
||||||
|
// u = (1, alt_tr - alt_tl, 0) [right (90 degrees CCW backward) = (1,0,0)]
|
||||||
|
// (f × u in right-handed coordinate system: pointing up)
|
||||||
|
//
|
||||||
|
// f × u =
|
||||||
|
// (a.y*b.z - a.z*b.y,
|
||||||
|
// a.z*b.x - a.x*b.z,
|
||||||
|
// a.x*b.y - a.y*b.x,
|
||||||
|
// )
|
||||||
|
// =
|
||||||
|
// (-(alt_tr - alt_tl),
|
||||||
|
// 1,
|
||||||
|
// -(alt_bl - alt_tl),
|
||||||
|
// )
|
||||||
|
// =
|
||||||
|
// (alt_tl - alt_tr,
|
||||||
|
// 1,
|
||||||
|
// alt_tl - alt_bl,
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// let surface_normal = Vec3::new((alt_tl - alt_tr) as f64, 1.0, (alt_tl - alt_bl) as f64).normalized();
|
||||||
|
let light = (surface_normal.dot(light) + 1.0) / 2.0;
|
||||||
|
let light = (light * 0.9) + 0.1;
|
||||||
|
|
||||||
|
let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64;
|
||||||
|
let true_alt = (alt as f64 - focus.z) / gain as f64;
|
||||||
|
let water_depth = (true_water_alt - true_alt).min(1.0).max(0.0);
|
||||||
|
let water_alt = true_water_alt.min(1.0).max(0.0);
|
||||||
|
let alt = true_alt.min(1.0).max(0.0);
|
||||||
|
if is_debug {
|
||||||
|
let quad =
|
||||||
|
|x: f32| ((x as f64 * QUADRANTS as f64).floor() as usize).min(QUADRANTS - 1);
|
||||||
|
if river_kind.is_none() || humidity != 0.0 {
|
||||||
|
quads[quad(humidity)][quad(temperature)] += 1;
|
||||||
|
}
|
||||||
|
match river_kind {
|
||||||
|
Some(RiverKind::River { .. }) => {
|
||||||
|
rivers += 1;
|
||||||
|
}
|
||||||
|
Some(RiverKind::Lake { .. }) => {
|
||||||
|
lakes += 1;
|
||||||
|
}
|
||||||
|
Some(RiverKind::Ocean { .. }) => {
|
||||||
|
oceans += 1;
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rgba = match (river_kind, (is_water, true_alt >= true_sea_level)) {
|
||||||
|
(_, (false, _)) | (None, (_, true)) => {
|
||||||
|
let (r, g, b) = (
|
||||||
|
(if is_shaded { alt } else { alt }
|
||||||
|
* if is_temperature {
|
||||||
|
temperature as f64
|
||||||
|
} else if is_shaded {
|
||||||
|
alt
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
})
|
||||||
|
.sqrt(),
|
||||||
|
if is_shaded { 0.2 + (alt * 0.8) } else { alt },
|
||||||
|
(if is_shaded { alt } else { alt }
|
||||||
|
* if is_humidity {
|
||||||
|
humidity as f64
|
||||||
|
} else if is_shaded {
|
||||||
|
alt
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
})
|
||||||
|
.sqrt(),
|
||||||
|
);
|
||||||
|
let light = if is_shaded { light } else { 1.0 };
|
||||||
|
(
|
||||||
|
(r * light * 255.0) as u8,
|
||||||
|
(g * light * 255.0) as u8,
|
||||||
|
(b * light * 255.0) as u8,
|
||||||
|
255,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(Some(RiverKind::Ocean), _) => (
|
||||||
|
0,
|
||||||
|
((32.0 - water_depth * 32.0) * 1.0) as u8,
|
||||||
|
((64.0 - water_depth * 64.0) * 1.0) as u8,
|
||||||
|
255,
|
||||||
|
),
|
||||||
|
(Some(RiverKind::River { .. }), _) => (
|
||||||
|
0,
|
||||||
|
32 + (alt * 95.0) as u8,
|
||||||
|
64 + (alt * 191.0) as u8,
|
||||||
|
255,
|
||||||
|
),
|
||||||
|
(None, _) | (Some(RiverKind::Lake { .. }), _) => (
|
||||||
|
0,
|
||||||
|
(((32.0 + water_alt * 95.0) + (-water_depth * 32.0)) * 1.0) as u8,
|
||||||
|
(((64.0 + water_alt * 191.0) + (-water_depth * 64.0)) * 1.0) as u8,
|
||||||
|
255,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
write_pixel(Vec2::new(i, j), rgba);
|
||||||
|
});
|
||||||
|
|
||||||
|
MapDebug {
|
||||||
|
quads,
|
||||||
|
rivers,
|
||||||
|
lakes,
|
||||||
|
oceans,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
mod diffusion;
|
mod diffusion;
|
||||||
mod erosion;
|
mod erosion;
|
||||||
mod location;
|
mod location;
|
||||||
|
mod map;
|
||||||
mod settlement;
|
mod settlement;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ pub use self::erosion::{
|
|||||||
mrec_downhill, Alt, RiverData, RiverKind,
|
mrec_downhill, Alt, RiverData, RiverKind,
|
||||||
};
|
};
|
||||||
pub use self::location::Location;
|
pub use self::location::Location;
|
||||||
|
pub use self::map::{MapConfig, MapDebug};
|
||||||
pub use self::settlement::Settlement;
|
pub use self::settlement::Settlement;
|
||||||
pub use self::util::{
|
pub use self::util::{
|
||||||
cdf_irwin_hall, downhill, get_oceans, local_cells, map_edge_factor, neighbors,
|
cdf_irwin_hall, downhill, get_oceans, local_cells, map_edge_factor, neighbors,
|
||||||
@ -1651,39 +1653,12 @@ impl WorldSim {
|
|||||||
|
|
||||||
/// Draw a map of the world based on chunk information. Returns a buffer of u32s.
|
/// Draw a map of the world based on chunk information. Returns a buffer of u32s.
|
||||||
pub fn get_map(&self) -> Vec<u32> {
|
pub fn get_map(&self) -> Vec<u32> {
|
||||||
(0..WORLD_SIZE.x * WORLD_SIZE.y)
|
let mut v = vec![0u32; WORLD_SIZE.x * WORLD_SIZE.y];
|
||||||
.into_par_iter()
|
// TODO: Parallelize again.
|
||||||
.map(|chunk_idx| {
|
MapConfig::default().generate(&self, |pos, (r, g, b, a)| {
|
||||||
let pos = uniform_idx_as_vec2(chunk_idx);
|
v[pos.y * WORLD_SIZE.x + pos.x] = u32::from_le_bytes([r, g, b, a]);
|
||||||
|
});
|
||||||
let (alt, water_alt, river_kind) = self
|
v
|
||||||
.get(pos)
|
|
||||||
.map(|sample| (sample.alt, sample.water_alt, sample.river.river_kind))
|
|
||||||
.unwrap_or((CONFIG.sea_level, CONFIG.sea_level, None));
|
|
||||||
let alt = ((alt - CONFIG.sea_level) / CONFIG.mountain_scale)
|
|
||||||
.min(1.0)
|
|
||||||
.max(0.0);
|
|
||||||
let water_alt = ((alt.max(water_alt) - CONFIG.sea_level) / CONFIG.mountain_scale)
|
|
||||||
.min(1.0)
|
|
||||||
.max(0.0);
|
|
||||||
match river_kind {
|
|
||||||
Some(RiverKind::Ocean) => u32::from_le_bytes([0, 32, 64, 255]),
|
|
||||||
Some(RiverKind::Lake { .. }) => u32::from_le_bytes([
|
|
||||||
0,
|
|
||||||
32 + (water_alt * 95.0) as u8,
|
|
||||||
64 + (water_alt * 191.0) as u8,
|
|
||||||
255,
|
|
||||||
]),
|
|
||||||
Some(RiverKind::River { .. }) => u32::from_le_bytes([
|
|
||||||
0,
|
|
||||||
32 + (alt * 95.0) as u8,
|
|
||||||
64 + (alt * 191.0) as u8,
|
|
||||||
255,
|
|
||||||
]),
|
|
||||||
None => u32::from_le_bytes([0, (alt * 255.0) as u8, 0, 255]),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepare the world for simulation
|
/// Prepare the world for simulation
|
||||||
@ -1825,7 +1800,7 @@ impl WorldSim {
|
|||||||
chunk_idx_center(WORLD_SIZE.map(|e| e as i32)),
|
chunk_idx_center(WORLD_SIZE.map(|e| e as i32)),
|
||||||
)
|
)
|
||||||
.map_init(
|
.map_init(
|
||||||
|| BlockGen::new(ColumnGen::new(self)),
|
|| Box::new(BlockGen::new(ColumnGen::new(self))),
|
||||||
|mut block_gen, (pos, seed)| {
|
|mut block_gen, (pos, seed)| {
|
||||||
let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
|
let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
|
||||||
// println!("Town: {:?}", town);
|
// println!("Town: {:?}", town);
|
||||||
|
Loading…
Reference in New Issue
Block a user