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 = [
|
||||
"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)",
|
||||
"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)",
|
||||
]
|
||||
|
||||
|
@ -76,6 +76,7 @@ pub struct Tick(u64);
|
||||
pub struct Server {
|
||||
state: State,
|
||||
world: Arc<World>,
|
||||
map: Vec<u32>,
|
||||
|
||||
postoffice: PostOffice<ServerMsg, ClientMsg>,
|
||||
|
||||
@ -119,6 +120,7 @@ impl Server {
|
||||
..WorldOpts::default()
|
||||
},
|
||||
);
|
||||
let map = world.sim().get_map();
|
||||
|
||||
let spawn_point = {
|
||||
// NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops,
|
||||
@ -178,6 +180,7 @@ impl Server {
|
||||
let this = Self {
|
||||
state,
|
||||
world: Arc::new(world),
|
||||
map,
|
||||
|
||||
postoffice: PostOffice::bind(settings.gameserver_address)?,
|
||||
|
||||
@ -1084,7 +1087,7 @@ impl Server {
|
||||
.create_entity_package(entity),
|
||||
server_info: self.server_info.clone(),
|
||||
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.");
|
||||
|
||||
|
@ -3,7 +3,7 @@ use common::{terrain::TerrainChunkSize, vol::RectVolSize};
|
||||
use std::{f32, f64, path::PathBuf};
|
||||
use vek::*;
|
||||
use veloren_world::{
|
||||
sim::{self, RiverKind, WorldOpts, WORLD_SIZE},
|
||||
sim::{self, MapConfig, MapDebug, RiverKind, WorldOpts, WORLD_SIZE},
|
||||
util::Sampler,
|
||||
World, CONFIG,
|
||||
};
|
||||
@ -46,11 +46,11 @@ fn main() {
|
||||
},
|
||||
);
|
||||
|
||||
let sampler = world.sim();
|
||||
|
||||
let mut win =
|
||||
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);
|
||||
// Altitude is divided by gain and clamped to [0, 1]; thus, decreasing gain makes
|
||||
// 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"
|
||||
let mut light_direction = Vec3::new(-0.8, -1.0, 0.3);
|
||||
let light_res = 3;
|
||||
|
||||
let mut is_basement = false;
|
||||
let mut is_water = true;
|
||||
@ -76,199 +75,33 @@ fn main() {
|
||||
let mut is_humidity = true;
|
||||
|
||||
while win.is_open() {
|
||||
let light = light_direction.normalized();
|
||||
let config = MapConfig {
|
||||
dimensions: Vec2::new(W, H),
|
||||
focus,
|
||||
gain,
|
||||
lgain,
|
||||
scale,
|
||||
light_direction,
|
||||
|
||||
is_basement,
|
||||
is_water,
|
||||
is_shaded,
|
||||
is_temperature,
|
||||
is_humidity,
|
||||
is_debug: true,
|
||||
};
|
||||
|
||||
let mut buf = vec![0; W * H];
|
||||
const QUADRANTS: usize = 4;
|
||||
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;
|
||||
|
||||
for i in 0..W {
|
||||
for j in 0..H {
|
||||
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 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 lspd = 0.1;
|
||||
@ -299,8 +132,8 @@ fn main() {
|
||||
}
|
||||
if win.get_mouse_down(minifb::MouseButton::Left) {
|
||||
if let Some((mx, my)) = win.get_mouse_pos(minifb::MouseMode::Clamp) {
|
||||
let pos =
|
||||
(focus_rect + (Vec2::new(mx as f64, my as f64) * scale)).map(|e| e as i32);
|
||||
let pos = (Vec2::<f64>::from(focus) + (Vec2::new(mx as f64, my as f64) * scale))
|
||||
.map(|e| e as i32);
|
||||
println!(
|
||||
"Chunk position: {:?}",
|
||||
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 erosion;
|
||||
mod location;
|
||||
mod map;
|
||||
mod settlement;
|
||||
mod util;
|
||||
|
||||
@ -12,6 +13,7 @@ pub use self::erosion::{
|
||||
mrec_downhill, Alt, RiverData, RiverKind,
|
||||
};
|
||||
pub use self::location::Location;
|
||||
pub use self::map::{MapConfig, MapDebug};
|
||||
pub use self::settlement::Settlement;
|
||||
pub use self::util::{
|
||||
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.
|
||||
pub fn get_map(&self) -> Vec<u32> {
|
||||
(0..WORLD_SIZE.x * WORLD_SIZE.y)
|
||||
.into_par_iter()
|
||||
.map(|chunk_idx| {
|
||||
let pos = uniform_idx_as_vec2(chunk_idx);
|
||||
|
||||
let (alt, water_alt, river_kind) = self
|
||||
.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()
|
||||
let mut v = vec![0u32; WORLD_SIZE.x * WORLD_SIZE.y];
|
||||
// TODO: Parallelize again.
|
||||
MapConfig::default().generate(&self, |pos, (r, g, b, a)| {
|
||||
v[pos.y * WORLD_SIZE.x + pos.x] = u32::from_le_bytes([r, g, b, a]);
|
||||
});
|
||||
v
|
||||
}
|
||||
|
||||
/// Prepare the world for simulation
|
||||
@ -1825,7 +1800,7 @@ impl WorldSim {
|
||||
chunk_idx_center(WORLD_SIZE.map(|e| e as i32)),
|
||||
)
|
||||
.map_init(
|
||||
|| BlockGen::new(ColumnGen::new(self)),
|
||||
|| Box::new(BlockGen::new(ColumnGen::new(self))),
|
||||
|mut block_gen, (pos, seed)| {
|
||||
let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
|
||||
// println!("Town: {:?}", town);
|
||||
|
Loading…
Reference in New Issue
Block a user