2019-11-11 17:14:58 +00:00
|
|
|
use common::{terrain::TerrainChunkSize, vol::RectVolSize};
|
2020-02-21 13:52:17 +00:00
|
|
|
use rayon::prelude::*;
|
2020-01-20 20:17:55 +00:00
|
|
|
use std::{f64, io::Write, path::PathBuf, time::SystemTime};
|
2019-10-16 11:39:41 +00:00
|
|
|
use vek::*;
|
|
|
|
use veloren_world::{
|
2020-02-21 13:52:17 +00:00
|
|
|
sim::{
|
|
|
|
self, get_shadows, uniform_idx_as_vec2, Alt, MapConfig, MapDebug, WorldOpts, WORLD_SIZE,
|
|
|
|
},
|
|
|
|
util::Sampler,
|
2019-10-16 11:39:41 +00:00
|
|
|
World, CONFIG,
|
|
|
|
};
|
|
|
|
|
|
|
|
const W: usize = 1024;
|
|
|
|
const H: usize = 1024;
|
|
|
|
|
|
|
|
fn main() {
|
2019-11-11 17:14:58 +00:00
|
|
|
pretty_env_logger::init();
|
|
|
|
|
2020-02-01 20:39:39 +00:00
|
|
|
// To load a map file of your choice, replace map_file with the name of your map
|
|
|
|
// (stored locally in the map directory of your Veloren root), and swap the
|
|
|
|
// sim::FileOpts::Save line below for the sim::FileOpts::Load one.
|
2019-12-11 09:14:50 +00:00
|
|
|
let map_file =
|
|
|
|
// "map_1575990726223.bin";
|
|
|
|
// "map_1575987666972.bin";
|
2020-02-21 13:52:17 +00:00
|
|
|
// "map_1576046079066.bin";
|
|
|
|
"map_1579539133272.bin";
|
2019-12-11 09:14:50 +00:00
|
|
|
let mut _map_file = PathBuf::from("./maps");
|
|
|
|
_map_file.push(map_file);
|
|
|
|
|
2020-02-01 20:39:39 +00:00
|
|
|
let world = World::generate(5284, WorldOpts {
|
|
|
|
seed_elements: false,
|
2020-02-21 13:52:17 +00:00
|
|
|
// world_file: sim::FileOpts::LoadAsset(veloren_world::sim::DEFAULT_WORLD_MAP.into()),
|
|
|
|
world_file: sim::FileOpts::Load(_map_file),
|
|
|
|
// world_file: sim::FileOpts::Save,
|
2020-02-01 20:39:39 +00:00
|
|
|
..WorldOpts::default()
|
|
|
|
});
|
2020-02-21 13:52:17 +00:00
|
|
|
log::info!("Sampling data...");
|
|
|
|
let sampler = world.sim();
|
|
|
|
|
|
|
|
let samples_data = {
|
|
|
|
let column_sample = world.sample_columns();
|
|
|
|
(0..WORLD_SIZE.product())
|
|
|
|
.into_par_iter()
|
|
|
|
.map(|posi| {
|
|
|
|
column_sample
|
|
|
|
.get(uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.into_boxed_slice()
|
|
|
|
};
|
|
|
|
|
|
|
|
let refresh_shadows = |light_direction: Vec3<f64>, lgain, scale, is_basement, is_water| {
|
|
|
|
get_shadows(
|
|
|
|
//Vec3::new(-0.8, 0.3, /*-1.0*/-(1.0 / TerrainChunkSize::RECT_SIZE.x as Alt)),
|
|
|
|
Vec3::new(light_direction.x, light_direction.z, light_direction.y/* / lgain*/),
|
|
|
|
lgain,
|
|
|
|
TerrainChunkSize::RECT_SIZE.x as f64/* * scale*/,
|
|
|
|
TerrainChunkSize::RECT_SIZE.y as f64/* * scale*/,
|
|
|
|
Aabr {
|
|
|
|
min: Vec2::new(0.0, 0.0), // focus.into(),
|
|
|
|
max: WORLD_SIZE.map(|e| e as f64) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64)/* * scale*//* + focus.into() */,
|
|
|
|
},
|
|
|
|
CONFIG.sea_level as f64, // focus.z,
|
|
|
|
(CONFIG.sea_level + sampler.max_height) as f64, // (focus.z + self.max_height) as Alt,
|
|
|
|
|posi| {
|
|
|
|
let sample = sampler.get(uniform_idx_as_vec2(posi)).unwrap();
|
|
|
|
if is_basement {
|
|
|
|
sample.alt as f64
|
|
|
|
} else {
|
|
|
|
sample.basement as f64
|
|
|
|
}.max(if is_water { sample.water_alt as f64 } else { -f64::INFINITY })
|
|
|
|
},
|
|
|
|
).ok()
|
|
|
|
};
|
2019-10-16 11:39:41 +00:00
|
|
|
|
|
|
|
let mut win =
|
|
|
|
minifb::Window::new("World Viewer", W, H, minifb::WindowOptions::default()).unwrap();
|
|
|
|
|
2019-12-03 01:07:44 +00:00
|
|
|
let mut focus = Vec3::new(0.0, 0.0, CONFIG.sea_level as f64);
|
2020-02-01 20:39:39 +00:00
|
|
|
// Altitude is divided by gain and clamped to [0, 1]; thus, decreasing gain
|
|
|
|
// makes smaller differences in altitude appear larger.
|
2020-02-21 13:52:17 +00:00
|
|
|
let mut gain = /*CONFIG.mountain_scale*/sampler.max_height;
|
2019-12-03 01:07:44 +00:00
|
|
|
// The Z component during normal calculations is multiplied by gain; thus,
|
|
|
|
let mut lgain = 1.0;
|
2020-01-20 18:19:06 +00:00
|
|
|
let mut scale = WORLD_SIZE.x as f64 / W as f64;
|
2019-10-16 11:39:41 +00:00
|
|
|
|
2020-02-01 20:39:39 +00:00
|
|
|
// 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.
|
2019-12-03 01:07:44 +00:00
|
|
|
//
|
2020-02-01 20:39:39 +00:00
|
|
|
// "In world space the x-axis will be pointing east, the y-axis up and the
|
|
|
|
// z-axis will be pointing south"
|
2020-02-21 13:52:17 +00:00
|
|
|
let mut light_direction = Vec3::new(-/*0.8*/1.3, -1.0, 0.3);
|
2019-11-11 17:14:58 +00:00
|
|
|
|
2019-11-19 18:34:52 +00:00
|
|
|
let mut is_basement = false;
|
2019-12-03 01:07:44 +00:00
|
|
|
let mut is_water = true;
|
2019-11-20 03:39:17 +00:00
|
|
|
let mut is_shaded = true;
|
|
|
|
let mut is_temperature = true;
|
|
|
|
let mut is_humidity = true;
|
2019-11-19 18:34:52 +00:00
|
|
|
|
2020-02-21 13:52:17 +00:00
|
|
|
let mut shadows = None; //refresh_shadows(light_direction, lgain, scale, is_basement, is_water);
|
|
|
|
let mut samples = None;
|
|
|
|
|
2019-10-16 11:39:41 +00:00
|
|
|
while win.is_open() {
|
2020-01-13 07:10:38 +00:00
|
|
|
let config = MapConfig {
|
|
|
|
dimensions: Vec2::new(W, H),
|
|
|
|
focus,
|
|
|
|
gain,
|
|
|
|
lgain,
|
|
|
|
scale,
|
|
|
|
light_direction,
|
2020-02-21 13:52:17 +00:00
|
|
|
shadows: shadows.as_deref(),
|
|
|
|
samples,
|
2020-01-13 07:10:38 +00:00
|
|
|
|
|
|
|
is_basement,
|
|
|
|
is_water,
|
|
|
|
is_shaded,
|
|
|
|
is_temperature,
|
|
|
|
is_humidity,
|
2020-02-21 13:52:17 +00:00
|
|
|
// is_sampled,
|
2020-01-13 07:10:38 +00:00
|
|
|
is_debug: true,
|
|
|
|
};
|
2019-10-16 11:39:41 +00:00
|
|
|
|
2020-01-13 07:10:38 +00:00
|
|
|
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]);
|
|
|
|
});
|
2019-10-16 11:39:41 +00:00
|
|
|
|
2020-01-20 20:17:55 +00:00
|
|
|
if win.is_key_down(minifb::Key::F4) {
|
|
|
|
if let Some(len) = (W * H)
|
|
|
|
.checked_mul(scale as usize)
|
|
|
|
.and_then(|acc| acc.checked_mul(scale as usize))
|
|
|
|
{
|
2020-01-22 09:41:10 +00:00
|
|
|
let x = (W as f64 * scale) as usize;
|
|
|
|
let y = (H as f64 * scale) as usize;
|
2020-01-20 20:17:55 +00:00
|
|
|
let config = sim::MapConfig {
|
|
|
|
dimensions: Vec2::new(x, y),
|
|
|
|
scale: 1.0,
|
|
|
|
..config
|
|
|
|
};
|
|
|
|
let mut buf = vec![0u8; 4 * len];
|
|
|
|
config.generate(sampler, |pos, (r, g, b, a)| {
|
|
|
|
let i = pos.x;
|
|
|
|
let j = pos.y;
|
|
|
|
(&mut buf[(j * x + i) * 4..]).write(&[r, g, b, a]).unwrap();
|
|
|
|
});
|
2020-01-22 13:35:13 +00:00
|
|
|
// TODO: Justify fits in u32.
|
2020-01-20 20:17:55 +00:00
|
|
|
let world_map = image::RgbaImage::from_raw(x as u32, y as u32, buf)
|
|
|
|
.expect("Image dimensions must be valid");
|
|
|
|
let mut path = PathBuf::from("./screenshots");
|
|
|
|
if !path.exists() {
|
|
|
|
if let Err(err) = std::fs::create_dir(&path) {
|
|
|
|
log::warn!("Couldn't create folder for screenshot: {:?}", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
path.push(format!(
|
|
|
|
"worldmap_{}.png",
|
|
|
|
SystemTime::now()
|
|
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
|
|
.map(|d| d.as_millis())
|
|
|
|
.unwrap_or(0)
|
|
|
|
));
|
|
|
|
if let Err(err) = world_map.save(&path) {
|
|
|
|
log::warn!("Couldn't save screenshot: {:?}", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-03 01:07:44 +00:00
|
|
|
let spd = 32.0;
|
|
|
|
let lspd = 0.1;
|
2019-11-07 20:25:30 +00:00
|
|
|
if win.is_key_down(minifb::Key::P) {
|
2019-11-11 17:14:58 +00:00
|
|
|
println!(
|
|
|
|
"\
|
2020-02-01 20:39:39 +00:00
|
|
|
Gain / Shade gain: {:?} / {:?}\nScale / Focus: {:?} / {:?}\nLight: {:?}
|
|
|
|
Land(adjacent): (X = temp, Y = humidity): {:?}\nRivers: {:?}\nLakes: \
|
|
|
|
{:?}\nOceans: {:?}\nTotal water: {:?}\nTotal land(adjacent): {:?}",
|
2019-11-22 02:55:19 +00:00
|
|
|
gain,
|
2019-12-03 01:07:44 +00:00
|
|
|
lgain,
|
2019-11-22 02:55:19 +00:00
|
|
|
scale,
|
2019-12-03 01:07:44 +00:00
|
|
|
focus,
|
|
|
|
light_direction,
|
2019-11-11 17:14:58 +00:00
|
|
|
quads,
|
|
|
|
rivers,
|
|
|
|
lakes,
|
|
|
|
oceans,
|
2019-11-07 20:25:30 +00:00
|
|
|
rivers + lakes + oceans,
|
2019-11-11 17:14:58 +00:00
|
|
|
quads.iter().map(|x| x.iter().sum::<u32>()).sum::<u32>()
|
2019-11-07 20:25:30 +00:00
|
|
|
);
|
|
|
|
}
|
2019-11-11 17:14:58 +00:00
|
|
|
if win.get_mouse_down(minifb::MouseButton::Left) {
|
|
|
|
if let Some((mx, my)) = win.get_mouse_pos(minifb::MouseMode::Clamp) {
|
2020-01-13 07:10:38 +00:00
|
|
|
let pos = (Vec2::<f64>::from(focus) + (Vec2::new(mx as f64, my as f64) * scale))
|
|
|
|
.map(|e| e as i32);
|
2019-11-11 17:14:58 +00:00
|
|
|
println!(
|
|
|
|
"Chunk position: {:?}",
|
|
|
|
pos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e * f as i32)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-12-03 01:07:44 +00:00
|
|
|
let is_camera = win.is_key_down(minifb::Key::C);
|
2019-11-19 18:34:52 +00:00
|
|
|
if win.is_key_down(minifb::Key::B) {
|
|
|
|
is_basement ^= true;
|
2020-02-21 13:52:17 +00:00
|
|
|
shadows = shadows.and_then(|_| {
|
|
|
|
refresh_shadows(light_direction, lgain, scale, is_basement, is_water)
|
|
|
|
});
|
2019-11-19 18:34:52 +00:00
|
|
|
}
|
2019-11-20 03:39:17 +00:00
|
|
|
if win.is_key_down(minifb::Key::H) {
|
|
|
|
is_humidity ^= true;
|
|
|
|
}
|
|
|
|
if win.is_key_down(minifb::Key::T) {
|
|
|
|
is_temperature ^= true;
|
|
|
|
}
|
2019-12-03 01:07:44 +00:00
|
|
|
if win.is_key_down(minifb::Key::O) {
|
|
|
|
is_water ^= true;
|
|
|
|
}
|
2019-11-20 03:39:17 +00:00
|
|
|
if win.is_key_down(minifb::Key::L) {
|
2020-02-21 13:52:17 +00:00
|
|
|
if is_camera {
|
|
|
|
shadows = match shadows {
|
|
|
|
Some(_) => None,
|
|
|
|
None => refresh_shadows(light_direction, lgain, scale, is_basement, is_water),
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
is_shaded ^= true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if win.is_key_down(minifb::Key::M) {
|
|
|
|
samples = samples.xor(Some(&*samples_data));
|
|
|
|
// is_sampled ^= true;
|
2019-11-20 03:39:17 +00:00
|
|
|
}
|
2019-10-16 11:39:41 +00:00
|
|
|
if win.is_key_down(minifb::Key::W) {
|
2019-12-03 01:07:44 +00:00
|
|
|
if is_camera {
|
|
|
|
light_direction.z -= lspd;
|
2020-02-21 13:52:17 +00:00
|
|
|
shadows = shadows.and_then(|_| {
|
|
|
|
refresh_shadows(light_direction, lgain, scale, is_basement, is_water)
|
|
|
|
});
|
2019-12-03 01:07:44 +00:00
|
|
|
} else {
|
|
|
|
focus.y -= spd * scale;
|
|
|
|
}
|
2019-10-16 11:39:41 +00:00
|
|
|
}
|
|
|
|
if win.is_key_down(minifb::Key::A) {
|
2019-12-03 01:07:44 +00:00
|
|
|
if is_camera {
|
|
|
|
light_direction.x -= lspd;
|
2020-02-21 13:52:17 +00:00
|
|
|
shadows = shadows.and_then(|_| {
|
|
|
|
refresh_shadows(light_direction, lgain, scale, is_basement, is_water)
|
|
|
|
});
|
2019-12-03 01:07:44 +00:00
|
|
|
} else {
|
|
|
|
focus.x -= spd * scale;
|
|
|
|
}
|
2019-10-16 11:39:41 +00:00
|
|
|
}
|
|
|
|
if win.is_key_down(minifb::Key::S) {
|
2019-12-03 01:07:44 +00:00
|
|
|
if is_camera {
|
|
|
|
light_direction.z += lspd;
|
2020-02-21 13:52:17 +00:00
|
|
|
shadows = shadows.and_then(|_| {
|
|
|
|
refresh_shadows(light_direction, lgain, scale, is_basement, is_water)
|
|
|
|
});
|
2019-12-03 01:07:44 +00:00
|
|
|
} else {
|
|
|
|
focus.y += spd * scale;
|
|
|
|
}
|
2019-10-16 11:39:41 +00:00
|
|
|
}
|
|
|
|
if win.is_key_down(minifb::Key::D) {
|
2019-12-03 01:07:44 +00:00
|
|
|
if is_camera {
|
|
|
|
light_direction.x += lspd;
|
2020-02-21 13:52:17 +00:00
|
|
|
shadows = shadows.and_then(|_| {
|
|
|
|
refresh_shadows(light_direction, lgain, scale, is_basement, is_water)
|
|
|
|
});
|
2019-12-03 01:07:44 +00:00
|
|
|
} else {
|
|
|
|
focus.x += spd * scale;
|
|
|
|
}
|
2019-10-16 11:39:41 +00:00
|
|
|
}
|
|
|
|
if win.is_key_down(minifb::Key::Q) {
|
2019-12-03 01:07:44 +00:00
|
|
|
if is_camera {
|
|
|
|
if (lgain * 2.0).is_normal() {
|
|
|
|
lgain *= 2.0;
|
2020-02-21 13:52:17 +00:00
|
|
|
shadows = shadows.and_then(|_| {
|
|
|
|
refresh_shadows(light_direction, lgain, scale, is_basement, is_water)
|
|
|
|
});
|
2019-12-03 01:07:44 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
gain += 64.0;
|
|
|
|
}
|
2019-10-16 11:39:41 +00:00
|
|
|
}
|
|
|
|
if win.is_key_down(minifb::Key::E) {
|
2019-12-03 01:07:44 +00:00
|
|
|
if is_camera {
|
|
|
|
if (lgain / 2.0).is_normal() {
|
|
|
|
lgain /= 2.0;
|
2020-02-21 13:52:17 +00:00
|
|
|
shadows = shadows.and_then(|_| {
|
|
|
|
refresh_shadows(light_direction, lgain, scale, is_basement, is_water)
|
|
|
|
});
|
2019-12-03 01:07:44 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
gain = (gain - 64.0).max(64.0);
|
|
|
|
}
|
2019-10-16 11:39:41 +00:00
|
|
|
}
|
|
|
|
if win.is_key_down(minifb::Key::R) {
|
2019-12-03 01:07:44 +00:00
|
|
|
if is_camera {
|
|
|
|
focus.z += spd * scale;
|
|
|
|
} else {
|
|
|
|
if (scale * 2.0).is_normal() {
|
|
|
|
scale *= 2.0;
|
2020-02-21 13:52:17 +00:00
|
|
|
// shadows = refresh_shadows(light_direction, lgain, scale,
|
|
|
|
// is_basement);
|
2019-12-03 01:07:44 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-16 11:39:41 +00:00
|
|
|
}
|
|
|
|
if win.is_key_down(minifb::Key::F) {
|
2019-12-03 01:07:44 +00:00
|
|
|
if is_camera {
|
|
|
|
focus.z -= spd * scale;
|
|
|
|
} else {
|
|
|
|
if (scale / 2.0).is_normal() {
|
|
|
|
scale /= 2.0;
|
2020-02-21 13:52:17 +00:00
|
|
|
// shadows = refresh_shadows(light_direction, lgain, scale,
|
|
|
|
// is_basement);
|
2019-12-03 01:07:44 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-16 11:39:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
win.update_with_buffer(&buf).unwrap();
|
|
|
|
}
|
|
|
|
}
|