mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/cities' into 'master'
Worldgen improvements See merge request veloren/veloren!245
This commit is contained in:
commit
6127fe61fc
Binary file not shown.
BIN
assets/world/structures/human/mage_tower.vox
Normal file
BIN
assets/world/structures/human/mage_tower.vox
Normal file
Binary file not shown.
BIN
assets/world/structures/human/town_hall.vox
Normal file
BIN
assets/world/structures/human/town_hall.vox
Normal file
Binary file not shown.
BIN
assets/world/structures/human/town_hall_spire.vox
Normal file
BIN
assets/world/structures/human/town_hall_spire.vox
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -20,11 +20,11 @@ use vek::*;
|
||||
|
||||
/// How much faster should an in-game day be compared to a real day?
|
||||
// TODO: Don't hard-code this.
|
||||
const DAY_CYCLE_FACTOR: f64 = 24.0 * 60.0;
|
||||
const DAY_CYCLE_FACTOR: f64 = 24.0 * 2.0;
|
||||
|
||||
/// A resource that stores the time of day.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct TimeOfDay(f64);
|
||||
pub struct TimeOfDay(pub f64);
|
||||
|
||||
/// A resource that stores the tick (i.e: physics) time.
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
||||
|
@ -28,7 +28,7 @@ impl Block {
|
||||
match self.kind {
|
||||
0 => None,
|
||||
1 => Some(0.85),
|
||||
_ => Some(1.0),
|
||||
_ => Some(3.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use common::{
|
||||
comp,
|
||||
msg::ServerMsg,
|
||||
npc::{get_npc_name, NpcKind},
|
||||
state::TimeOfDay,
|
||||
};
|
||||
use specs::{Builder, Entity as EcsEntity, Join};
|
||||
use vek::*;
|
||||
@ -59,37 +60,43 @@ lazy_static! {
|
||||
"jump",
|
||||
"{d} {d} {d}",
|
||||
"/jump <dx> <dy> <dz> : Offset your current position",
|
||||
handle_jump
|
||||
handle_jump,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"goto",
|
||||
"{d} {d} {d}",
|
||||
"/goto <x> <y> <z> : Teleport to a position",
|
||||
handle_goto
|
||||
handle_goto,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"alias",
|
||||
"{}",
|
||||
"/alias <name> : Change your alias",
|
||||
handle_alias
|
||||
handle_alias,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"tp",
|
||||
"{}",
|
||||
"/tp <alias> : Teleport to another player",
|
||||
handle_tp
|
||||
handle_tp,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"kill",
|
||||
"{}",
|
||||
"/kill : Kill yourself",
|
||||
handle_kill
|
||||
handle_kill,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"time",
|
||||
"{} {s}",
|
||||
"/time : Set the time of day",
|
||||
handle_time,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"spawn",
|
||||
"{} {} {d}",
|
||||
"/spawn <alignment> <entity> [amount] : Spawn a test entity",
|
||||
handle_spawn
|
||||
handle_spawn,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"help", "", "/help: Display this message", handle_help)
|
||||
@ -143,6 +150,33 @@ fn handle_kill(server: &mut Server, entity: EcsEntity, _args: String, _action: &
|
||||
.map(|s| s.hp.set_to(0, comp::HealthSource::Suicide));
|
||||
}
|
||||
|
||||
fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||
let time = scan_fmt!(&args, action.arg_fmt, String);
|
||||
server.state.ecs_mut().write_resource::<TimeOfDay>().0 = match time.as_ref().map(|s| s.as_str())
|
||||
{
|
||||
Some("day") => 12.0 * 3600.0,
|
||||
Some("night") => 24.0 * 3600.0,
|
||||
Some("dawn") => 5.0 * 3600.0,
|
||||
Some("dusk") => 17.0 * 3600.0,
|
||||
Some(n) => match n.parse() {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
server
|
||||
.clients
|
||||
.notify(entity, ServerMsg::Chat(format!("'{}' is not a time!", n)));
|
||||
return;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
server.clients.notify(
|
||||
entity,
|
||||
ServerMsg::Chat("You must specify a time!".to_string()),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||
let opt_alias = scan_fmt!(&args, action.arg_fmt, String);
|
||||
match opt_alias {
|
||||
|
@ -32,13 +32,8 @@ void main() {
|
||||
vec4(f_norm, 0.0)
|
||||
).xyz;
|
||||
|
||||
float ambient = 0.5;
|
||||
|
||||
vec3 sun_dir = normalize(vec3(1.3, 1.7, 1.1));
|
||||
|
||||
float sun_diffuse = dot(sun_dir, world_norm) * 0.5;
|
||||
|
||||
vec3 surf_color = model_col.rgb * f_col * (ambient + sun_diffuse);
|
||||
float light = get_sun_diffuse(world_norm, time_of_day.x);
|
||||
vec3 surf_color = model_col.rgb * f_col * 2.0 * light;
|
||||
|
||||
float fog_level = fog(f_pos.xy, focus_pos.xy);
|
||||
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x);
|
||||
|
@ -26,42 +26,184 @@ float noise(vec3 p){
|
||||
return o4.y * d.y + o4.x * (1.0 - d.y);
|
||||
}
|
||||
|
||||
vec3 get_sky_color(vec3 dir, float time_of_day) {
|
||||
bool objects = true;
|
||||
|
||||
vec3 get_sun_dir(float time_of_day) {
|
||||
const float TIME_FACTOR = (PI * 2.0) / (3600.0 * 24.0);
|
||||
|
||||
float sun_angle_rad = time_of_day * TIME_FACTOR;
|
||||
vec3 sun_dir = vec3(sin(sun_angle_rad), 0.0, cos(sun_angle_rad));
|
||||
|
||||
return sun_dir;
|
||||
}
|
||||
|
||||
float get_sun_brightness(vec3 sun_dir) {
|
||||
return max(-sun_dir.z + 0.6, 0.0) * 0.8;
|
||||
}
|
||||
|
||||
const float PERSISTENT_AMBIANCE = 0.015;
|
||||
|
||||
float get_sun_diffuse(vec3 norm, float time_of_day) {
|
||||
const float SUN_AMBIANCE = 0.2;
|
||||
|
||||
vec3 sun_dir = get_sun_dir(time_of_day);
|
||||
|
||||
float sun_light = get_sun_brightness(sun_dir);
|
||||
|
||||
return (SUN_AMBIANCE + max(dot(-norm, sun_dir), 0.0)) * sun_light + PERSISTENT_AMBIANCE;
|
||||
}
|
||||
|
||||
vec3 rand_offs(vec3 pos) {
|
||||
return sin(pos * vec3(1473.7 * pos.z + 472.3, 8891.1 * pos.x + 723.1, 3813.3 * pos.y + 982.5));
|
||||
}
|
||||
|
||||
vec3 get_sky_color(vec3 dir, float time_of_day) {
|
||||
|
||||
// Stars
|
||||
float star_scale = 30.0;
|
||||
float star = 0.0;
|
||||
for (int i = 0; i < 2; i ++) {
|
||||
for (int j = 0; j < 2; j ++) {
|
||||
for (int k = 0; k < 2; k ++) {
|
||||
// Star positions
|
||||
vec3 pos = (floor(dir * star_scale) + vec3(i, j, k) - vec3(0.5)) / star_scale;
|
||||
|
||||
// Noisy offsets
|
||||
pos += 3.0 * rand_offs(pos) / star_scale;
|
||||
|
||||
// Find distance to fragment
|
||||
float dist = length(normalize(pos) - dir);
|
||||
|
||||
// Star threshold
|
||||
if (dist < 0.0015) {
|
||||
star = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sky color
|
||||
|
||||
const vec3 SKY_DAY_TOP = vec3(0.2, 0.3, 0.9);
|
||||
const vec3 SKY_DAY_MID = vec3(0.1, 0.15, 0.7);
|
||||
const vec3 SKY_DAY_BOT = vec3(0.025, 0.15, 0.35);
|
||||
|
||||
const vec3 SKY_DUSK_TOP = vec3(0.1, 0.15, 0.3);
|
||||
const vec3 SKY_DUSK_MID = vec3(0.9, 0.3, 0.2);
|
||||
const vec3 SKY_DUSK_BOT = vec3(0.01, 0.05, 0.15);
|
||||
|
||||
const vec3 SKY_NIGHT_TOP = vec3(0.002, 0.002, 0.005);
|
||||
const vec3 SKY_NIGHT_MID = vec3(0.002, 0.01, 0.03);
|
||||
const vec3 SKY_NIGHT_BOT = vec3(0.002, 0.002, 0.005);
|
||||
|
||||
vec3 sun_dir = get_sun_dir(time_of_day);
|
||||
|
||||
vec3 sky_top = mix(
|
||||
mix(
|
||||
SKY_DUSK_TOP,
|
||||
SKY_NIGHT_TOP,
|
||||
clamp(sun_dir.z, 0, 1)
|
||||
) + star,
|
||||
SKY_DAY_TOP,
|
||||
clamp(-sun_dir.z, 0, 1)
|
||||
);
|
||||
|
||||
vec3 sky_mid = mix(
|
||||
mix(
|
||||
SKY_DUSK_MID,
|
||||
SKY_NIGHT_MID,
|
||||
clamp(sun_dir.z, 0, 1)
|
||||
),
|
||||
SKY_DAY_MID,
|
||||
clamp(-sun_dir.z, 0, 1)
|
||||
);
|
||||
|
||||
vec3 sky_bot = mix(
|
||||
mix(
|
||||
SKY_DUSK_BOT,
|
||||
SKY_NIGHT_BOT,
|
||||
clamp(sun_dir.z, 0, 1)
|
||||
),
|
||||
SKY_DAY_MID,
|
||||
clamp(-sun_dir.z, 0, 1)
|
||||
);
|
||||
|
||||
vec3 sky_color = mix(
|
||||
mix(
|
||||
sky_mid,
|
||||
sky_bot,
|
||||
pow(clamp(-dir.z, 0, 1), 0.4)
|
||||
),
|
||||
sky_top,
|
||||
pow(clamp(dir.z, 0, 1), 1.0)
|
||||
);
|
||||
|
||||
// Sun
|
||||
|
||||
const vec3 SUN_HALO_COLOR = vec3(1.0, 0.35, 0.1) * 0.3;
|
||||
const vec3 SUN_SURF_COLOR = vec3(1.0, 0.9, 0.35) * 200.0;
|
||||
|
||||
vec3 sun_halo = pow(max(dot(dir, -sun_dir) + 0.1, 0.0), 8.0) * SUN_HALO_COLOR;
|
||||
vec3 sun_surf = pow(max(dot(dir, -sun_dir) - 0.0045, 0.0), 1000.0) * SUN_SURF_COLOR;
|
||||
vec3 sun_light = (sun_halo + sun_surf) * clamp(dir.z * 10.0, 0, 1);
|
||||
|
||||
return sky_color + sun_light;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
bool objects = true;
|
||||
|
||||
vec2 pos2d = dir.xy / dir.z;
|
||||
|
||||
const vec3 SKY_TOP = vec3(0.2, 0.3, 0.9);
|
||||
const vec3 SKY_MIDDLE = vec3(0.1, 0.15, 0.7);
|
||||
const vec3 SKY_BOTTOM = vec3(0.025, 0.15, 0.35);
|
||||
|
||||
const vec3 SUN_HALO_COLOR = vec3(1.0, 0.7, 0.5) * 0.5;
|
||||
const vec3 SUN_HALO_COLOR = vec3(1.0, 0.4, 0.3) * 0.5;
|
||||
const vec3 SUN_SURF_COLOR = vec3(1.0, 0.9, 0.35) * 200.0;
|
||||
|
||||
float sun_angle_rad = time_of_day * TIME_FACTOR;
|
||||
vec3 sun_dir = vec3(sin(sun_angle_rad), 0.0, cos(sun_angle_rad));
|
||||
vec3 sun_dir = get_sun_dir(time_of_day);
|
||||
float sky_brightness = get_sun_brightness(sun_dir);
|
||||
|
||||
vec3 sun_halo = pow(max(dot(dir, sun_dir), 0.0), 8.0) * SUN_HALO_COLOR;
|
||||
vec3 sun_surf = pow(max(dot(dir, sun_dir) - 0.0045, 0.0), 1000.0) * SUN_SURF_COLOR;
|
||||
vec3 sun_halo = pow(max(dot(dir, -sun_dir) + 0.1, 0.0), 8.0) * SUN_HALO_COLOR;
|
||||
vec3 sun_surf = pow(max(dot(dir, -sun_dir) - 0.0045, 0.0), 1000.0) * SUN_SURF_COLOR;
|
||||
vec3 sun_light = sun_halo + sun_surf;
|
||||
|
||||
vec3 sky_top;
|
||||
float brightess = (sky_brightness + PERSISTENT_AMBIANCE);
|
||||
|
||||
vec3 sky_top = SKY_TOP * brightess;
|
||||
vec3 sky_middle = SKY_MIDDLE * brightess;
|
||||
if (objects) {
|
||||
vec3 p = vec3(pos2d + time_of_day * 0.0002, time_of_day * 0.0001);
|
||||
sky_top = mix(SKY_TOP, vec3(1), pow(noise(p) * 0.8 + noise(p * 3.0) * 0.2, 2.5) * 3.0);
|
||||
} else {
|
||||
sky_top = SKY_TOP;
|
||||
// Clouds
|
||||
// vec3 p = vec3(pos2d + time_of_day * 0.0002, time_of_day * 0.00003);
|
||||
// sky_top = mix(sky_top, vec3(1) * brightess, pow(noise(p) * 0.8 + noise(p * 3.0) * 0.2, 2.5) * 3.0);
|
||||
}
|
||||
|
||||
vec3 sky_color = mix(mix(SKY_MIDDLE, sky_top, clamp(dir.z, 0, 1)), SKY_BOTTOM, clamp(-dir.z * 5.0, 0, 1));
|
||||
|
||||
if (objects) {
|
||||
sky_color += sun_light;
|
||||
sky_top += sun_light;
|
||||
sky_middle += sun_light;
|
||||
}
|
||||
|
||||
vec3 sky_color = mix(
|
||||
mix(
|
||||
sky_middle,
|
||||
sky_top,
|
||||
clamp(dir.z * 1.0, 0, 1)
|
||||
),
|
||||
SKY_BOTTOM * brightess,
|
||||
clamp(-dir.z * 3.0, 0, 1)
|
||||
);
|
||||
|
||||
return sky_color;
|
||||
*/
|
||||
}
|
||||
|
||||
float fog(vec2 f_pos, vec2 focus_pos) {
|
||||
|
@ -164,6 +164,7 @@ void main() {
|
||||
vec2 uv = (f_pos + 1.0) * 0.5;
|
||||
|
||||
vec4 fxaa_color = fxaa_apply(src_color, uv * screen_res.xy, screen_res.xy);
|
||||
//vec4 fxaa_color = texture(src_color, uv);
|
||||
|
||||
vec4 hsva_color = vec4(rgb2hsv(fxaa_color.rgb), fxaa_color.a);
|
||||
hsva_color.y *= 1.3;
|
||||
|
@ -14,6 +14,7 @@ out vec3 f_pos;
|
||||
void main() {
|
||||
f_pos = v_pos;
|
||||
|
||||
// TODO: Make this position-independent to avoid rounding error jittering
|
||||
gl_Position =
|
||||
proj_mat *
|
||||
view_mat *
|
||||
|
@ -28,19 +28,7 @@ void main() {
|
||||
f_norm = vec3(0.0, 0.0, 1.0) * norm_dir;
|
||||
}
|
||||
|
||||
float glob_ambience = 0.0005;
|
||||
|
||||
float sun_ambience = 0.8;
|
||||
|
||||
vec3 sun_dir = normalize(vec3(0.7, 1.3, 2.1));
|
||||
|
||||
float sun_diffuse = dot(sun_dir, f_norm);
|
||||
float sun_light = sun_ambience + sun_diffuse;
|
||||
|
||||
float static_light = glob_ambience + min(sun_light, f_light);
|
||||
|
||||
vec3 light = vec3(static_light);
|
||||
|
||||
float light = get_sun_diffuse(f_norm, time_of_day.x) * f_light;
|
||||
vec3 surf_color = f_col * light;
|
||||
|
||||
float fog_level = fog(f_pos.xy, focus_pos.xy);
|
||||
|
@ -1,10 +1,13 @@
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle},
|
||||
widget_ids, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
widget::{self, Button, Image, Rectangle, Text},
|
||||
widget_ids, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
use super::{img_ids::Imgs, Fonts};
|
||||
use super::{img_ids::Imgs, Fonts, Show, TEXT_COLOR_2};
|
||||
use client::{self, Client};
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
@ -17,22 +20,28 @@ widget_ids! {
|
||||
map_frame_r,
|
||||
map_frame_bl,
|
||||
map_frame_br,
|
||||
location_name,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct Map<'a> {
|
||||
show: &'a Show,
|
||||
|
||||
client: &'a Client,
|
||||
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
|
||||
impl<'a> Map<'a> {
|
||||
pub fn new(imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
pub fn new(show: &'a Show, client: &'a Client, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
Self {
|
||||
show,
|
||||
imgs,
|
||||
client,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
@ -108,6 +117,21 @@ impl<'a> Widget for Map<'a> {
|
||||
return Some(Event::Close);
|
||||
}
|
||||
|
||||
// Location Name
|
||||
match self.client.current_chunk() {
|
||||
Some(chunk) => Text::new(chunk.meta().name())
|
||||
.mid_top_with_margin_on(state.ids.map_bg, 40.0)
|
||||
.font_size(40)
|
||||
.color(TEXT_COLOR_2)
|
||||
.parent(state.ids.map_frame_r)
|
||||
.set(state.ids.location_name, ui),
|
||||
None => Text::new(" ")
|
||||
.mid_top_with_margin_on(state.ids.map_bg, 3.0)
|
||||
.font_size(40)
|
||||
.color(TEXT_COLOR_2)
|
||||
.set(state.ids.location_name, ui),
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ use conrod_core::{
|
||||
};
|
||||
|
||||
use super::{img_ids::Imgs, Fonts, Show, TEXT_COLOR};
|
||||
use client::{self, Client};
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
@ -19,6 +22,8 @@ widget_ids! {
|
||||
pub struct MiniMap<'a> {
|
||||
show: &'a Show,
|
||||
|
||||
client: &'a Client,
|
||||
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
@ -27,9 +32,10 @@ pub struct MiniMap<'a> {
|
||||
}
|
||||
|
||||
impl<'a> MiniMap<'a> {
|
||||
pub fn new(show: &'a Show, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
pub fn new(show: &'a Show, client: &'a Client, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
Self {
|
||||
show,
|
||||
client,
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
@ -103,12 +109,18 @@ impl<'a> Widget for MiniMap<'a> {
|
||||
}
|
||||
|
||||
// Title
|
||||
// TODO: Make it display the actual location.
|
||||
Text::new("Uncanny Valley")
|
||||
.mid_top_with_margin_on(state.ids.mmap_frame, 3.0)
|
||||
.font_size(14)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.mmap_location, ui);
|
||||
match self.client.current_chunk() {
|
||||
Some(chunk) => Text::new(chunk.meta().name())
|
||||
.mid_top_with_margin_on(state.ids.mmap_frame, 3.0)
|
||||
.font_size(14)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.mmap_location, ui),
|
||||
None => Text::new(" ")
|
||||
.mid_top_with_margin_on(state.ids.mmap_frame, 3.0)
|
||||
.font_size(14)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.mmap_location, ui),
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -38,10 +38,12 @@ use conrod_core::{
|
||||
};
|
||||
use specs::Join;
|
||||
use std::collections::VecDeque;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use vek::*;
|
||||
|
||||
const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0);
|
||||
const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
||||
const TEXT_COLOR_2: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0);
|
||||
const HP_COLOR: Color = Color::Rgba(0.33, 0.63, 0.0, 1.0);
|
||||
const MANA_COLOR: Color = Color::Rgba(0.42, 0.41, 0.66, 1.0);
|
||||
|
||||
@ -522,7 +524,9 @@ impl Hud {
|
||||
}
|
||||
|
||||
// MiniMap
|
||||
match MiniMap::new(&self.show, &self.imgs, &self.fonts).set(self.ids.minimap, ui_widgets) {
|
||||
match MiniMap::new(&self.show, client, &self.imgs, &self.fonts)
|
||||
.set(self.ids.minimap, ui_widgets)
|
||||
{
|
||||
Some(minimap::Event::Toggle) => self.show.toggle_mini_map(),
|
||||
None => {}
|
||||
}
|
||||
@ -638,7 +642,9 @@ impl Hud {
|
||||
|
||||
// Map
|
||||
if self.show.map {
|
||||
match Map::new(&self.imgs, &self.fonts).set(self.ids.map, ui_widgets) {
|
||||
match Map::new(&self.show, client, &self.imgs, &self.fonts)
|
||||
.set(self.ids.map, ui_widgets)
|
||||
{
|
||||
Some(map::Event::Close) => {
|
||||
self.show.map(false);
|
||||
self.force_ungrab = true;
|
||||
@ -647,7 +653,6 @@ impl Hud {
|
||||
}
|
||||
}
|
||||
|
||||
// Esc-menu
|
||||
if self.show.esc_menu {
|
||||
match EscMenu::new(&self.imgs, &self.fonts).set(self.ids.esc_menu, ui_widgets) {
|
||||
Some(esc_menu::Event::OpenSettings(tab)) => {
|
||||
|
@ -65,7 +65,7 @@ impl Scene {
|
||||
backdrop_model: renderer
|
||||
.create_model(&FigureModelCache::load_mesh(
|
||||
"fixture/selection_bg.vox",
|
||||
Vec3::new(-55.0, -50.0, -1.0),
|
||||
Vec3::new(-55.0, -49.5, -2.0),
|
||||
))
|
||||
.unwrap(),
|
||||
backdrop_state: FigureState::new(renderer, FixtureSkeleton::new()),
|
||||
@ -84,7 +84,7 @@ impl Scene {
|
||||
.set_orientation(Vec3::new(client.state().get_time() as f32 * 0.0, 0.0, 0.0));
|
||||
|
||||
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client);
|
||||
|
||||
const CHAR_SELECT_TIME_OF_DAY: f32 = 80000.0; // 12*3600 seconds
|
||||
if let Err(err) = renderer.update_consts(
|
||||
&mut self.globals,
|
||||
&[Globals::new(
|
||||
@ -92,8 +92,8 @@ impl Scene {
|
||||
proj_mat,
|
||||
cam_pos,
|
||||
self.camera.get_focus_pos(),
|
||||
100.0,
|
||||
client.state().get_time_of_day(),
|
||||
CHAR_SELECT_TIME_OF_DAY,
|
||||
55800.0,
|
||||
client.state().get_time(),
|
||||
renderer.get_resolution(),
|
||||
)],
|
||||
|
@ -142,7 +142,6 @@ widget_ids! {
|
||||
image_ids! {
|
||||
struct Imgs {
|
||||
<VoxelGraphic>
|
||||
v_logo: "voxygen/element/v_logo.vox",
|
||||
button: "voxygen/element/buttons/button.vox",
|
||||
button_hover: "voxygen/element/buttons/button_hover.vox",
|
||||
button_press: "voxygen/element/buttons/button_press.vox",
|
||||
@ -171,7 +170,6 @@ image_ids! {
|
||||
skin_eyes_window: "voxygen/element/frames/skin_eyes.png",
|
||||
hair_window: "voxygen/element/frames/skin_eyes.png",
|
||||
accessories_window: "voxygen/element/frames/skin_eyes.png",
|
||||
color_picker_bg: "voxygen/element/misc_bg/color_picker_blank.png",
|
||||
slider_range: "voxygen/element/slider/track.png",
|
||||
slider_indicator: "voxygen/element/slider/indicator.png",
|
||||
window_frame_2: "voxygen/element/frames/window_2.png",
|
||||
|
@ -70,7 +70,9 @@ image_ids! {
|
||||
<ImageGraphic>
|
||||
bg: "voxygen/background/bg_main.png",
|
||||
error_frame: "voxygen/element/frames/window_2.png",
|
||||
nothing: "voxygen/element/nothing.png",
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
font_ids! {
|
||||
@ -137,6 +139,7 @@ impl MainMenuUi {
|
||||
let ref mut ui_widgets = self.ui.set_widgets();
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
||||
const TEXT_COLOR_2: Color = Color::Rgba(1.0, 1.0, 1.0, 0.2);
|
||||
// Background image, Veloren logo, Alpha-Version Label
|
||||
Image::new(self.imgs.bg)
|
||||
.middle_of(ui_widgets.window)
|
||||
@ -302,8 +305,8 @@ impl MainMenuUi {
|
||||
}
|
||||
if self.show_servers {
|
||||
Image::new(self.imgs.error_frame)
|
||||
.top_left_with_margins_on(ui_widgets.window, 3.0, 3.0)
|
||||
.w_h(400.0, 400.0)
|
||||
.mid_top_with_margin_on(self.ids.username_bg, -320.0)
|
||||
.w_h(400.0, 300.0)
|
||||
.set(self.ids.servers_frame, ui_widgets);
|
||||
|
||||
let net_settings = &global_state.settings.networking;
|
||||
@ -320,7 +323,7 @@ impl MainMenuUi {
|
||||
while let Some(item) = items.next(ui_widgets) {
|
||||
let mut text = "".to_string();
|
||||
if &net_settings.servers[item.i] == &self.server_address {
|
||||
text.push_str("* ")
|
||||
text.push_str("-> ")
|
||||
} else {
|
||||
text.push_str(" ")
|
||||
}
|
||||
@ -328,11 +331,11 @@ impl MainMenuUi {
|
||||
|
||||
if item
|
||||
.set(
|
||||
Button::image(self.imgs.button)
|
||||
.w_h(100.0, 53.0)
|
||||
.mid_bottom_with_margin_on(self.ids.servers_frame, 5.0)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
Button::image(self.imgs.nothing)
|
||||
.w_h(100.0, 50.0)
|
||||
.mid_top_with_margin_on(self.ids.servers_frame, 10.0)
|
||||
//.hover_image(self.imgs.button_hover)
|
||||
//.press_image(self.imgs.button_press)
|
||||
.label_y(Relative::Scalar(2.0))
|
||||
.label(&text)
|
||||
.label_font_size(20)
|
||||
@ -465,10 +468,10 @@ impl MainMenuUi {
|
||||
if Button::image(self.imgs.button)
|
||||
.w_h(190.0, 40.0)
|
||||
.up_from(self.ids.quit_button, 8.0)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
//.hover_image(self.imgs.button_hover)
|
||||
//.press_image(self.imgs.button_press)
|
||||
.label("Settings")
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_color(TEXT_COLOR_2)
|
||||
.label_font_size(20)
|
||||
.label_y(Relative::Scalar(3.0))
|
||||
.set(self.ids.settings_button, ui_widgets)
|
||||
|
@ -10,10 +10,6 @@ use vek::*;
|
||||
|
||||
type FigureVertex = <FigurePipeline as render::Pipeline>::Vertex;
|
||||
|
||||
fn create_vertex(origin: Vec3<f32>, norm: Vec3<f32>, col: Rgb<f32>) -> FigureVertex {
|
||||
FigureVertex::new(origin, norm, col, 0)
|
||||
}
|
||||
|
||||
impl Meshable for Segment {
|
||||
type Pipeline = FigurePipeline;
|
||||
type Supplement = Vec3<f32>;
|
||||
@ -31,8 +27,11 @@ impl Meshable for Segment {
|
||||
pos,
|
||||
offs + pos.map(|e| e as f32),
|
||||
col,
|
||||
create_vertex,
|
||||
|origin, norm, col, ao, light| {
|
||||
FigureVertex::new(origin, norm, col * ao * light, 0)
|
||||
},
|
||||
true,
|
||||
&[[[1.0; 3]; 3]; 3],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone> Meshable for
|
||||
|
||||
for x in range.min.x + 1..range.max.x - 1 {
|
||||
for y in range.min.y + 1..range.max.y - 1 {
|
||||
let mut neighbour_light = [[(1.0f32, 0.0); 3]; 3];
|
||||
let mut neighbour_light = [[[1.0f32; 3]; 3]; 3];
|
||||
|
||||
for z in (range.min.z..range.max.z).rev() {
|
||||
let pos = Vec3::new(x, y, z);
|
||||
@ -60,10 +60,12 @@ impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone> Meshable for
|
||||
if let Some(col) = self.get(pos).ok().and_then(|vox| vox.get_color()) {
|
||||
let avg_light = neighbour_light
|
||||
.iter()
|
||||
.map(|row| row.iter())
|
||||
.flatten()
|
||||
.map(|col| col.iter())
|
||||
.flatten()
|
||||
.fold(0.0, |a, (x, _)| a + x)
|
||||
/ 9.0;
|
||||
.fold(0.0, |a, x| a + x)
|
||||
/ 27.0;
|
||||
let light = avg_light;
|
||||
|
||||
let col = col.map(|e| e as f32 / 255.0);
|
||||
@ -77,30 +79,47 @@ impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone> Meshable for
|
||||
pos,
|
||||
offs,
|
||||
col,
|
||||
|pos, norm, col| TerrainVertex::new(pos, norm, col, light),
|
||||
|pos, norm, col, ao, light| {
|
||||
TerrainVertex::new(
|
||||
pos,
|
||||
norm,
|
||||
Lerp::lerp(Rgb::zero(), col, 1.0),
|
||||
light * ao,
|
||||
)
|
||||
},
|
||||
false,
|
||||
&neighbour_light,
|
||||
);
|
||||
}
|
||||
|
||||
// Shift lighting
|
||||
neighbour_light[2] = neighbour_light[1];
|
||||
neighbour_light[1] = neighbour_light[0];
|
||||
|
||||
// Accumulate shade under opaque blocks
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
let max_opacity = neighbour_light[i][j].1;
|
||||
neighbour_light[i][j] = if let Some(opacity) = self
|
||||
.get(pos + Vec3::new(i as i32 - 1, j as i32 - 1, 0))
|
||||
neighbour_light[0][i][j] = if let Some(opacity) = self
|
||||
.get(pos + Vec3::new(i as i32 - 1, j as i32 - 1, -1))
|
||||
.ok()
|
||||
.and_then(|vox| vox.get_opacity())
|
||||
{
|
||||
(
|
||||
(neighbour_light[i][j].0 * (1.0 - max_opacity * 0.3))
|
||||
.max(1.0 - max_opacity * 0.999),
|
||||
max_opacity.max(opacity),
|
||||
)
|
||||
(neighbour_light[0][i][j] * (1.0 - opacity * 0.1))
|
||||
.max(1.0 - opacity)
|
||||
} else {
|
||||
((neighbour_light[i][j].0 * 1.02).min(1.0), max_opacity)
|
||||
(neighbour_light[0][i][j] * 1.025).min(1.0)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Spread light
|
||||
neighbour_light[0] = [[neighbour_light[0]
|
||||
.iter()
|
||||
.map(|col| col.iter())
|
||||
.flatten()
|
||||
.copied()
|
||||
.fold(0.0, |a, x| a + x)
|
||||
/ 9.0; 3]; 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,72 +10,87 @@ use crate::render::{
|
||||
/// Given volume, position, and cardinal directions, compute each vertex's AO value.
|
||||
/// `dirs` should be a slice of length 5 so that the sliding window of size 2 over the slice
|
||||
/// yields each vertex' adjacent positions.
|
||||
fn get_ao_quad<V: ReadVol>(vol: &V, pos: Vec3<i32>, dirs: &[Vec3<i32>]) -> Vec4<f32> {
|
||||
fn get_ao_quad<V: ReadVol>(
|
||||
vol: &V,
|
||||
pos: Vec3<i32>,
|
||||
shift: Vec3<i32>,
|
||||
dirs: &[Vec3<i32>],
|
||||
corners: &[[usize; 3]; 4],
|
||||
darknesses: &[[[f32; 3]; 3]; 3],
|
||||
) -> Vec4<(f32, f32)> {
|
||||
dirs.windows(2)
|
||||
.map(|offs| {
|
||||
.enumerate()
|
||||
.map(|(i, offs)| {
|
||||
let (s1, s2) = (
|
||||
vol.get(pos + offs[0])
|
||||
vol.get(pos + shift + offs[0])
|
||||
.map(|v| !v.is_empty())
|
||||
.unwrap_or(false),
|
||||
vol.get(pos + offs[1])
|
||||
vol.get(pos + shift + offs[1])
|
||||
.map(|v| !v.is_empty())
|
||||
.unwrap_or(false),
|
||||
);
|
||||
|
||||
if s1 && s2 {
|
||||
0.0
|
||||
} else {
|
||||
let corner = vol
|
||||
.get(pos + offs[0] + offs[1])
|
||||
.map(|v| !v.is_empty())
|
||||
.unwrap_or(false);
|
||||
// Map both 1 and 2 neighbors to 0.5 occlusion.
|
||||
if s1 || s2 || corner {
|
||||
0.5
|
||||
let darkness = darknesses[corners[i][0]][corners[i][1]][corners[i][2]];
|
||||
|
||||
let darkness = darknesses
|
||||
.iter()
|
||||
.map(|x| x.iter().map(|y| y.iter()))
|
||||
.flatten()
|
||||
.flatten()
|
||||
.fold(0.0, |a: f32, x| a.max(*x));
|
||||
|
||||
(
|
||||
darkness,
|
||||
if s1 && s2 {
|
||||
0.0
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
let corner = vol
|
||||
.get(pos + shift + offs[0] + offs[1])
|
||||
.map(|v| !v.is_empty())
|
||||
.unwrap_or(false);
|
||||
// Map both 1 and 2 neighbors to 0.5 occlusion.
|
||||
if s1 || s2 || corner {
|
||||
0.5
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect::<Vec4<f32>>()
|
||||
.collect::<Vec4<(f32, f32)>>()
|
||||
}
|
||||
|
||||
// Utility function
|
||||
fn create_quad<P: Pipeline, F: Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>) -> P::Vertex>(
|
||||
fn create_quad<P: Pipeline, F: Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>, f32, f32) -> P::Vertex>(
|
||||
origin: Vec3<f32>,
|
||||
unit_x: Vec3<f32>,
|
||||
unit_y: Vec3<f32>,
|
||||
norm: Vec3<f32>,
|
||||
col: Rgb<f32>,
|
||||
ao: Vec4<f32>,
|
||||
darkness_ao: Vec4<(f32, f32)>,
|
||||
vcons: &F,
|
||||
) -> Quad<P> {
|
||||
let ao_scale = 0.95;
|
||||
let dark = col * (1.0 - ao_scale);
|
||||
|
||||
let ao_map = ao.map(|e| 0.15 + e.powf(2.0) * 0.85);
|
||||
let darkness = darkness_ao.map(|e| e.0);
|
||||
let ao = darkness_ao.map(|e| e.1);
|
||||
|
||||
let ao_map = ao.map(|e| 0.05 + e.powf(1.6) * 0.95);
|
||||
|
||||
if ao[0].min(ao[2]) < ao[1].min(ao[3]) {
|
||||
Quad::new(
|
||||
vcons(origin + unit_y, norm, Rgb::lerp(dark, col, ao_map[3])),
|
||||
vcons(origin, norm, Rgb::lerp(dark, col, ao_map[0])),
|
||||
vcons(origin + unit_x, norm, Rgb::lerp(dark, col, ao_map[1])),
|
||||
vcons(
|
||||
origin + unit_x + unit_y,
|
||||
norm,
|
||||
Rgb::lerp(dark, col, ao_map[2]),
|
||||
),
|
||||
vcons(origin + unit_y, norm, col, darkness[3], ao_map[3]),
|
||||
vcons(origin, norm, col, darkness[0], ao_map[0]),
|
||||
vcons(origin + unit_x, norm, col, darkness[1], ao_map[1]),
|
||||
vcons(origin + unit_x + unit_y, norm, col, darkness[2], ao_map[2]),
|
||||
)
|
||||
} else {
|
||||
Quad::new(
|
||||
vcons(origin, norm, Rgb::lerp(dark, col, ao_map[0])),
|
||||
vcons(origin + unit_x, norm, Rgb::lerp(dark, col, ao_map[1])),
|
||||
vcons(
|
||||
origin + unit_x + unit_y,
|
||||
norm,
|
||||
Rgb::lerp(dark, col, ao_map[2]),
|
||||
),
|
||||
vcons(origin + unit_y, norm, Rgb::lerp(dark, col, ao_map[3])),
|
||||
vcons(origin, norm, col, darkness[0], ao_map[0]),
|
||||
vcons(origin + unit_x, norm, col, darkness[1], ao_map[1]),
|
||||
vcons(origin + unit_x + unit_y, norm, col, darkness[2], ao_map[2]),
|
||||
vcons(origin + unit_y, norm, col, darkness[3], ao_map[3]),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -83,7 +98,7 @@ fn create_quad<P: Pipeline, F: Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>) -> P::Vertex>(
|
||||
pub fn push_vox_verts<
|
||||
V: ReadVol,
|
||||
P: Pipeline,
|
||||
F: Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>) -> P::Vertex,
|
||||
F: Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>, f32, f32) -> P::Vertex,
|
||||
>(
|
||||
mesh: &mut Mesh<P>,
|
||||
vol: &V,
|
||||
@ -92,6 +107,7 @@ pub fn push_vox_verts<
|
||||
col: Rgb<f32>,
|
||||
vcons: F,
|
||||
error_makes_face: bool,
|
||||
darknesses: &[[[f32; 3]; 3]; 3],
|
||||
) {
|
||||
let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z());
|
||||
|
||||
@ -107,7 +123,14 @@ pub fn push_vox_verts<
|
||||
Vec3::unit_y(),
|
||||
-Vec3::unit_x(),
|
||||
col,
|
||||
get_ao_quad(vol, pos - Vec3::unit_x(), &[-z, -y, z, y, -z]),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
-Vec3::unit_x(),
|
||||
&[-z, -y, z, y, -z],
|
||||
&[[0; 3]; 4],
|
||||
darknesses,
|
||||
),
|
||||
&vcons,
|
||||
));
|
||||
}
|
||||
@ -123,7 +146,14 @@ pub fn push_vox_verts<
|
||||
Vec3::unit_z(),
|
||||
Vec3::unit_x(),
|
||||
col,
|
||||
get_ao_quad(vol, pos + Vec3::unit_x(), &[-y, -z, y, z, -y]),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
Vec3::unit_x(),
|
||||
&[-y, -z, y, z, -y],
|
||||
&[[0; 3]; 4],
|
||||
darknesses,
|
||||
),
|
||||
&vcons,
|
||||
));
|
||||
}
|
||||
@ -139,7 +169,14 @@ pub fn push_vox_verts<
|
||||
Vec3::unit_z(),
|
||||
-Vec3::unit_y(),
|
||||
col,
|
||||
get_ao_quad(vol, pos - Vec3::unit_y(), &[-x, -z, x, z, -x]),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
-Vec3::unit_y(),
|
||||
&[-x, -z, x, z, -x],
|
||||
&[[0; 3]; 4],
|
||||
darknesses,
|
||||
),
|
||||
&vcons,
|
||||
));
|
||||
}
|
||||
@ -155,7 +192,14 @@ pub fn push_vox_verts<
|
||||
Vec3::unit_x(),
|
||||
Vec3::unit_y(),
|
||||
col,
|
||||
get_ao_quad(vol, pos + Vec3::unit_y(), &[-z, -x, z, x, -z]),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
Vec3::unit_y(),
|
||||
&[-z, -x, z, x, -z],
|
||||
&[[0; 3]; 4],
|
||||
darknesses,
|
||||
),
|
||||
&vcons,
|
||||
));
|
||||
}
|
||||
@ -171,7 +215,14 @@ pub fn push_vox_verts<
|
||||
Vec3::unit_x(),
|
||||
-Vec3::unit_z(),
|
||||
col,
|
||||
get_ao_quad(vol, pos - Vec3::unit_z(), &[-y, -x, y, x, -y]),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
-Vec3::unit_z(),
|
||||
&[-y, -x, y, x, -y],
|
||||
&[[0; 3]; 4],
|
||||
darknesses,
|
||||
),
|
||||
&vcons,
|
||||
));
|
||||
}
|
||||
@ -187,7 +238,14 @@ pub fn push_vox_verts<
|
||||
Vec3::unit_y(),
|
||||
Vec3::unit_z(),
|
||||
col,
|
||||
get_ao_quad(vol, pos + Vec3::unit_z(), &[-x, -y, x, y, -x]),
|
||||
get_ao_quad(
|
||||
vol,
|
||||
pos,
|
||||
Vec3::unit_z(),
|
||||
&[-x, -y, x, y, -x],
|
||||
&[[0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1]],
|
||||
darknesses,
|
||||
),
|
||||
&vcons,
|
||||
));
|
||||
}
|
||||
|
@ -757,8 +757,8 @@ impl<S: Skeleton> FigureState<S> {
|
||||
dt: f32,
|
||||
) {
|
||||
// Update interpolate pos
|
||||
self.pos = Lerp::lerp(self.pos, pos, (0.3f32).powf(1.0 / 60.0).powf(1.0 / dt));
|
||||
self.ori = Slerp::slerp(self.ori, ori, (0.15f32).powf(1.0 / 60.0).powf(1.0 / dt));
|
||||
self.pos = Lerp::lerp(self.pos, pos, (0.4f32).powf(60.0).powf(dt));
|
||||
self.ori = Slerp::slerp(self.ori, ori, (0.2f32).powf(60.0).powf(dt));
|
||||
|
||||
let mat = Mat4::<f32>::identity()
|
||||
* Mat4::translation_3d(self.pos)
|
||||
|
@ -57,14 +57,6 @@ impl SessionState {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Get rid of this when we're done with it (we're not yet)
|
||||
/*
|
||||
match self.client.borrow().current_chunk() {
|
||||
Some(chunk) => println!("Chunk location: {:?}", chunk.meta().name()),
|
||||
None => {}
|
||||
}
|
||||
*/
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
35
world/examples/city.rs
Normal file
35
world/examples/city.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use rand::thread_rng;
|
||||
use std::ops::{Add, Mul, Sub};
|
||||
use vek::*;
|
||||
use veloren_world::sim::Settlement;
|
||||
|
||||
const W: usize = 640;
|
||||
const H: usize = 480;
|
||||
|
||||
fn main() {
|
||||
let mut win =
|
||||
minifb::Window::new("City Viewer", W, H, minifb::WindowOptions::default()).unwrap();
|
||||
|
||||
let settlement = Settlement::generate(&mut thread_rng());
|
||||
|
||||
while win.is_open() {
|
||||
let mut buf = vec![0; W * H];
|
||||
|
||||
for i in 0..W {
|
||||
for j in 0..H {
|
||||
let pos = Vec2::new(i as f32, j as f32) * 0.002;
|
||||
|
||||
let seed = settlement.get_at(pos).map(|b| b.seed).unwrap_or(0);
|
||||
|
||||
buf[j * W + i] = u32::from_le_bytes([
|
||||
(seed >> 0) as u8,
|
||||
(seed >> 8) as u8,
|
||||
(seed >> 16) as u8,
|
||||
(seed >> 24) as u8,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
win.update_with_buffer(&buf).unwrap();
|
||||
}
|
||||
}
|
42
world/examples/turb.rs
Normal file
42
world/examples/turb.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use noise::{NoiseFn, Seedable, SuperSimplex};
|
||||
use rand::thread_rng;
|
||||
use std::ops::{Add, Mul, Sub};
|
||||
use vek::*;
|
||||
use veloren_world::sim::Settlement;
|
||||
|
||||
const W: usize = 640;
|
||||
const H: usize = 640;
|
||||
|
||||
fn main() {
|
||||
let mut win = minifb::Window::new("Turb", W, H, minifb::WindowOptions::default()).unwrap();
|
||||
|
||||
let nz_x = SuperSimplex::new().set_seed(0);
|
||||
let nz_y = SuperSimplex::new().set_seed(1);
|
||||
|
||||
let mut time = 0.0f64;
|
||||
while win.is_open() {
|
||||
let mut buf = vec![0; W * H];
|
||||
|
||||
for i in 0..W {
|
||||
for j in 0..H {
|
||||
let pos = Vec2::new(i as f64 / W as f64, j as f64 / H as f64) * 0.5 - 0.25;
|
||||
|
||||
let pos = pos * 10.0;
|
||||
|
||||
let pos = (0..10).fold(pos, |pos, _| pos.map(|e| e.powf(3.0) - 1.0));
|
||||
|
||||
let val = if pos.map(|e| e.abs() < 0.5).reduce_and() {
|
||||
1.0f32
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
buf[j * W + i] = u32::from_le_bytes([(val.max(0.0).min(1.0) * 255.0) as u8; 4]);
|
||||
}
|
||||
}
|
||||
|
||||
win.update_with_buffer(&buf).unwrap();
|
||||
|
||||
time += 1.0 / 60.0;
|
||||
}
|
||||
}
|
@ -15,35 +15,45 @@ fn main() {
|
||||
|
||||
let mut focus = Vec2::zero();
|
||||
let mut gain = 1.0;
|
||||
let mut scale = 4;
|
||||
|
||||
while win.is_open() {
|
||||
let mut buf = vec![0; W * H];
|
||||
|
||||
for i in 0..W {
|
||||
for j in 0..H {
|
||||
let pos = focus + Vec2::new(i as i32, j as i32) * 4;
|
||||
let pos = focus + Vec2::new(i as i32, j as i32) * scale;
|
||||
|
||||
let alt = sampler
|
||||
let (alt, location) = sampler
|
||||
.get(pos)
|
||||
.map(|sample| sample.alt.sub(64.0).add(gain).mul(0.7).max(0.0).min(255.0) as u8)
|
||||
.unwrap_or(0);
|
||||
.map(|sample| {
|
||||
(
|
||||
sample.alt.sub(64.0).add(gain).mul(0.7).max(0.0).min(255.0) as u8,
|
||||
sample.location,
|
||||
)
|
||||
})
|
||||
.unwrap_or((0, None));
|
||||
|
||||
buf[j * W + i] = u32::from_le_bytes([alt; 4]);
|
||||
let loc_color = location
|
||||
.map(|l| (l.loc_idx as u8 * 17, l.loc_idx as u8 * 13))
|
||||
.unwrap_or((0, 0));
|
||||
|
||||
buf[j * W + i] = u32::from_le_bytes([loc_color.0, loc_color.1, alt, alt]);
|
||||
}
|
||||
}
|
||||
|
||||
let spd = 32;
|
||||
if win.is_key_down(minifb::Key::W) {
|
||||
focus.y -= spd;
|
||||
focus.y -= spd * scale;
|
||||
}
|
||||
if win.is_key_down(minifb::Key::A) {
|
||||
focus.x -= spd;
|
||||
focus.x -= spd * scale;
|
||||
}
|
||||
if win.is_key_down(minifb::Key::S) {
|
||||
focus.y += spd;
|
||||
focus.y += spd * scale;
|
||||
}
|
||||
if win.is_key_down(minifb::Key::D) {
|
||||
focus.x += spd;
|
||||
focus.x += spd * scale;
|
||||
}
|
||||
if win.is_key_down(minifb::Key::Q) {
|
||||
gain += 10.0;
|
||||
@ -51,6 +61,12 @@ fn main() {
|
||||
if win.is_key_down(minifb::Key::E) {
|
||||
gain -= 10.0;
|
||||
}
|
||||
if win.is_key_down(minifb::Key::R) {
|
||||
scale += 6;
|
||||
}
|
||||
if win.is_key_down(minifb::Key::F) {
|
||||
scale -= 6;
|
||||
}
|
||||
|
||||
win.update_with_buffer(&buf).unwrap();
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use vek::*;
|
||||
|
||||
pub struct BlockGen<'a> {
|
||||
world: &'a World,
|
||||
column_cache: HashCache<Vec2<i32>, Option<ColumnSample>>,
|
||||
column_cache: HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
|
||||
column_gen: ColumnGen<'a>,
|
||||
}
|
||||
|
||||
@ -23,17 +23,57 @@ impl<'a> BlockGen<'a> {
|
||||
pub fn new(world: &'a World, column_gen: ColumnGen<'a>) -> Self {
|
||||
Self {
|
||||
world,
|
||||
column_cache: HashCache::with_capacity(1024),
|
||||
column_cache: HashCache::with_capacity(64),
|
||||
column_gen,
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_column(&mut self, wpos: Vec2<i32>) -> Option<ColumnSample> {
|
||||
let column_gen = &mut self.column_gen;
|
||||
self.column_cache
|
||||
fn sample_column(
|
||||
column_gen: &ColumnGen<'a>,
|
||||
cache: &mut HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
|
||||
wpos: Vec2<i32>,
|
||||
) -> Option<ColumnSample<'a>> {
|
||||
cache
|
||||
.get(Vec2::from(wpos), |wpos| column_gen.get(wpos))
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn get_cliff_height(
|
||||
column_gen: &ColumnGen<'a>,
|
||||
cache: &mut HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
|
||||
wpos: Vec2<f32>,
|
||||
close_cliffs: &[(Vec2<i32>, u32); 9],
|
||||
cliff_hill: f32,
|
||||
) -> f32 {
|
||||
close_cliffs.iter().fold(
|
||||
0.0f32,
|
||||
|max_height, (cliff_pos, seed)| match Self::sample_column(
|
||||
column_gen,
|
||||
cache,
|
||||
Vec2::from(*cliff_pos),
|
||||
) {
|
||||
Some(cliff_sample) if cliff_sample.cliffs && cliff_sample.spawn_rate > 0.5 => {
|
||||
let cliff_pos3d = Vec3::from(*cliff_pos);
|
||||
|
||||
let height = RandomField::new(seed + 1).get(cliff_pos3d) % 48;
|
||||
let radius = RandomField::new(seed + 2).get(cliff_pos3d) % 48 + 8;
|
||||
|
||||
max_height.max(
|
||||
if cliff_pos.map(|e| e as f32).distance_squared(wpos)
|
||||
< (radius * radius) as f32
|
||||
{
|
||||
cliff_sample.alt
|
||||
+ height as f32 * (1.0 - cliff_sample.chaos)
|
||||
+ cliff_hill
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
)
|
||||
}
|
||||
_ => max_height,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SamplerMut for BlockGen<'a> {
|
||||
@ -41,71 +81,80 @@ impl<'a> SamplerMut for BlockGen<'a> {
|
||||
type Sample = Option<Block>;
|
||||
|
||||
fn get(&mut self, wpos: Vec3<i32>) -> Option<Block> {
|
||||
let BlockGen {
|
||||
world,
|
||||
column_cache,
|
||||
column_gen,
|
||||
} = self;
|
||||
|
||||
let ColumnSample {
|
||||
alt,
|
||||
chaos,
|
||||
water_level,
|
||||
river,
|
||||
surface_color,
|
||||
sub_surface_color,
|
||||
tree_density,
|
||||
forest_kind,
|
||||
close_trees,
|
||||
cave_xy,
|
||||
cave_alt,
|
||||
rock,
|
||||
cliff,
|
||||
cliffs,
|
||||
cliff_hill,
|
||||
close_cliffs,
|
||||
temp,
|
||||
} = self.sample_column(Vec2::from(wpos))?;
|
||||
..
|
||||
} = Self::sample_column(column_gen, column_cache, Vec2::from(wpos))?;
|
||||
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
|
||||
// Apply warping
|
||||
|
||||
let warp = (self
|
||||
.world
|
||||
.sim()
|
||||
.gen_ctx
|
||||
.warp_nz
|
||||
.get((wposf.div(Vec3::new(150.0, 150.0, 150.0))).into_array())
|
||||
as f32)
|
||||
.mul((chaos - 0.1).max(0.0))
|
||||
.mul(115.0);
|
||||
|
||||
let is_cliff = if cliff > 0.0 {
|
||||
(self
|
||||
.world
|
||||
.sim()
|
||||
.gen_ctx
|
||||
.warp_nz
|
||||
.get((wposf.div(Vec3::new(300.0, 300.0, 1500.0))).into_array()) as f32)
|
||||
* cliff
|
||||
> 0.3
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let cliff = if is_cliff {
|
||||
(0.0 + (self
|
||||
.world
|
||||
.sim()
|
||||
.gen_ctx
|
||||
.warp_nz
|
||||
.get((wposf.div(Vec3::new(350.0, 350.0, 800.0))).into_array())
|
||||
as f32)
|
||||
* 0.8
|
||||
+ (self
|
||||
.world
|
||||
let (definitely_underground, height, water_height) =
|
||||
if (wposf.z as f32) < alt - 64.0 * chaos {
|
||||
// Shortcut warping
|
||||
(true, alt, water_level)
|
||||
} else {
|
||||
// Apply warping
|
||||
let warp = (world
|
||||
.sim()
|
||||
.gen_ctx
|
||||
.warp_nz
|
||||
.get((wposf.div(Vec3::new(100.0, 100.0, 70.0))).into_array())
|
||||
.get((wposf.div(Vec3::new(150.0, 150.0, 150.0))).into_array())
|
||||
as f32)
|
||||
* 0.3)
|
||||
.add(0.4)
|
||||
.mul(75.0)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
.mul((chaos - 0.1).max(0.0))
|
||||
.mul(115.0);
|
||||
|
||||
let height = alt + warp + cliff;
|
||||
let height = if (wposf.z as f32) < alt + warp - 10.0 {
|
||||
// Shortcut cliffs
|
||||
alt + warp
|
||||
} else {
|
||||
let turb = Vec2::new(
|
||||
world
|
||||
.sim()
|
||||
.gen_ctx
|
||||
.turb_x_nz
|
||||
.get((wposf.div(48.0)).into_array()) as f32,
|
||||
world
|
||||
.sim()
|
||||
.gen_ctx
|
||||
.turb_y_nz
|
||||
.get((wposf.div(48.0)).into_array()) as f32,
|
||||
) * 12.0;
|
||||
|
||||
let wpos_turb = Vec2::from(wpos).map(|e: i32| e as f32) + turb;
|
||||
let cliff_height = Self::get_cliff_height(
|
||||
column_gen,
|
||||
column_cache,
|
||||
wpos_turb,
|
||||
&close_cliffs,
|
||||
cliff_hill,
|
||||
);
|
||||
|
||||
(alt + warp).max(cliff_height)
|
||||
};
|
||||
|
||||
(false, height, water_level + warp)
|
||||
};
|
||||
|
||||
// Sample blocks
|
||||
|
||||
@ -120,8 +169,13 @@ impl<'a> SamplerMut for BlockGen<'a> {
|
||||
let water = Block::new(1, Rgb::new(100, 150, 255));
|
||||
let warm_stone = Block::new(1, Rgb::new(165, 165, 130));
|
||||
|
||||
let block = if (wposf.z as f32) < height - 3.0 {
|
||||
let col = Lerp::lerp(dirt_col, stone_col, (height - 4.0 - wposf.z as f32) * 0.15);
|
||||
let grass_depth = 2.0;
|
||||
let block = if (wposf.z as f32) < height - grass_depth {
|
||||
let col = Lerp::lerp(
|
||||
sub_surface_color.map(|e| (e * 255.0) as u8),
|
||||
stone_col,
|
||||
(height - grass_depth - wposf.z as f32) * 0.15,
|
||||
);
|
||||
|
||||
// Underground
|
||||
if (wposf.z as f32) > alt - 32.0 * chaos {
|
||||
@ -131,13 +185,15 @@ impl<'a> SamplerMut for BlockGen<'a> {
|
||||
}
|
||||
} else if (wposf.z as f32) < height {
|
||||
let col = Lerp::lerp(
|
||||
dirt_col.map(|e| e as f32 / 255.0),
|
||||
sub_surface_color,
|
||||
surface_color,
|
||||
(wposf.z as f32 - (height - 4.0)) * 0.25,
|
||||
(wposf.z as f32 - (height - grass_depth))
|
||||
.div(grass_depth)
|
||||
.powf(0.5),
|
||||
);
|
||||
// Surface
|
||||
Some(Block::new(1, col.map(|e| (e * 255.0) as u8)))
|
||||
} else if (wposf.z as f32) < CONFIG.sea_level {
|
||||
} else if (wposf.z as f32) < water_height {
|
||||
// Ocean
|
||||
Some(water)
|
||||
} else {
|
||||
@ -165,7 +221,19 @@ impl<'a> SamplerMut for BlockGen<'a> {
|
||||
// Rocks
|
||||
let block = block.or_else(|| {
|
||||
if (height + 2.5 - wposf.z as f32).div(7.5).abs().powf(2.0) < rock {
|
||||
Some(warm_stone)
|
||||
let field0 = RandomField::new(world.sim().seed + 0);
|
||||
let field1 = RandomField::new(world.sim().seed + 1);
|
||||
let field2 = RandomField::new(world.sim().seed + 2);
|
||||
|
||||
Some(Block::new(
|
||||
1,
|
||||
stone_col
|
||||
- Rgb::new(
|
||||
field0.get(wpos) as u8 % 32,
|
||||
field1.get(wpos) as u8 % 32,
|
||||
field2.get(wpos) as u8 % 32,
|
||||
),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -212,38 +280,60 @@ impl<'a> SamplerMut for BlockGen<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let block = match block {
|
||||
Some(block) => block,
|
||||
None => (&close_trees)
|
||||
.iter()
|
||||
.fold(air, |block, (tree_pos, tree_seed)| {
|
||||
match self.sample_column(Vec2::from(*tree_pos)) {
|
||||
Some(tree_sample)
|
||||
if tree_sample.tree_density
|
||||
> 0.5 + (*tree_seed as f32 / 1000.0).fract() * 0.2 =>
|
||||
{
|
||||
let tree_pos3d =
|
||||
Vec3::new(tree_pos.x, tree_pos.y, tree_sample.alt as i32);
|
||||
let rpos = wpos - tree_pos3d;
|
||||
let block = if definitely_underground {
|
||||
block.unwrap_or(Block::empty())
|
||||
} else {
|
||||
match block {
|
||||
Some(block) => block,
|
||||
None => (&close_trees)
|
||||
.iter()
|
||||
.fold(air, |block, (tree_pos, tree_seed)| {
|
||||
if !block.is_empty() {
|
||||
block
|
||||
} else {
|
||||
match Self::sample_column(
|
||||
column_gen,
|
||||
column_cache,
|
||||
Vec2::from(*tree_pos),
|
||||
) {
|
||||
Some(tree_sample)
|
||||
if tree_sample.tree_density
|
||||
> 0.5 + (*tree_seed as f32 / 1000.0).fract() * 0.2
|
||||
&& tree_sample.alt > tree_sample.water_level
|
||||
&& tree_sample.spawn_rate > 0.5 =>
|
||||
{
|
||||
let cliff_height = Self::get_cliff_height(
|
||||
column_gen,
|
||||
column_cache,
|
||||
tree_pos.map(|e| e as f32),
|
||||
&tree_sample.close_cliffs,
|
||||
cliff_hill,
|
||||
);
|
||||
let height = tree_sample.alt.max(cliff_height);
|
||||
let tree_pos3d =
|
||||
Vec3::new(tree_pos.x, tree_pos.y, height as i32);
|
||||
let rpos = wpos - tree_pos3d;
|
||||
|
||||
let trees = tree::kinds(tree_sample.forest_kind); // Choose tree kind
|
||||
let trees = tree::kinds(tree_sample.forest_kind); // Choose tree kind
|
||||
|
||||
block.or(trees[*tree_seed as usize % trees.len()]
|
||||
.get((rpos * 128) / 128) // Scaling
|
||||
.map(|b| {
|
||||
block_from_structure(
|
||||
*b,
|
||||
rpos,
|
||||
*tree_pos,
|
||||
*tree_seed,
|
||||
&tree_sample,
|
||||
)
|
||||
})
|
||||
.unwrap_or(Block::empty()))
|
||||
block.or(trees[*tree_seed as usize % trees.len()]
|
||||
.get((rpos * 128) / 128) // Scaling
|
||||
.map(|b| {
|
||||
block_from_structure(
|
||||
*b,
|
||||
rpos,
|
||||
*tree_pos,
|
||||
*tree_seed,
|
||||
&tree_sample,
|
||||
)
|
||||
})
|
||||
.unwrap_or(Block::empty()))
|
||||
}
|
||||
_ => block,
|
||||
}
|
||||
}
|
||||
_ => block,
|
||||
}
|
||||
}),
|
||||
}),
|
||||
}
|
||||
};
|
||||
|
||||
Some(block)
|
||||
|
@ -1,10 +1,19 @@
|
||||
use crate::{all::ForestKind, util::Sampler, World, CONFIG};
|
||||
use crate::{
|
||||
all::ForestKind,
|
||||
sim::{Location, LocationInfo},
|
||||
util::Sampler,
|
||||
World, CONFIG,
|
||||
};
|
||||
use common::{
|
||||
terrain::{Block, TerrainChunkSize},
|
||||
vol::{VolSize, Vox},
|
||||
};
|
||||
use noise::NoiseFn;
|
||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use std::{
|
||||
f32,
|
||||
ops::{Add, Div, Mul, Neg, Sub},
|
||||
sync::Arc,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
pub struct ColumnGen<'a> {
|
||||
@ -19,29 +28,62 @@ impl<'a> ColumnGen<'a> {
|
||||
|
||||
impl<'a> Sampler for ColumnGen<'a> {
|
||||
type Index = Vec2<i32>;
|
||||
type Sample = Option<ColumnSample>;
|
||||
type Sample = Option<ColumnSample<'a>>;
|
||||
|
||||
fn get(&self, wpos: Vec2<i32>) -> Option<ColumnSample> {
|
||||
fn get(&self, wpos: Vec2<i32>) -> Option<ColumnSample<'a>> {
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
let chunk_pos = wpos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| {
|
||||
e as u32 / sz
|
||||
e / sz as i32
|
||||
});
|
||||
|
||||
let sim = self.world.sim();
|
||||
|
||||
let turb = Vec2::new(
|
||||
sim.gen_ctx.turb_x_nz.get((wposf.div(48.0)).into_array()) as f32,
|
||||
sim.gen_ctx.turb_y_nz.get((wposf.div(48.0)).into_array()) as f32,
|
||||
) * 12.0;
|
||||
let wposf_turb = wposf + turb.map(|e| e as f64);
|
||||
|
||||
let alt_base = sim.get_interpolated(wpos, |chunk| chunk.alt_base)?;
|
||||
let chaos = sim.get_interpolated(wpos, |chunk| chunk.chaos)?;
|
||||
let temp = sim.get_interpolated(wpos, |chunk| chunk.temp)?;
|
||||
let dryness = sim.get_interpolated(wpos, |chunk| chunk.dryness)?;
|
||||
let rockiness = sim.get_interpolated(wpos, |chunk| chunk.rockiness)?;
|
||||
let cliffiness = sim.get_interpolated(wpos, |chunk| chunk.cliffiness)?;
|
||||
let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?;
|
||||
let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?;
|
||||
|
||||
let forest_kind = sim.get(chunk_pos)?.forest_kind;
|
||||
let sim_chunk = sim.get(chunk_pos)?;
|
||||
|
||||
let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?
|
||||
+ sim.gen_ctx.small_nz.get((wposf.div(256.0)).into_array()) as f32
|
||||
* chaos.max(0.2)
|
||||
* 64.0;
|
||||
const RIVER_PROPORTION: f32 = 0.025;
|
||||
|
||||
let river = dryness
|
||||
.abs()
|
||||
.neg()
|
||||
.add(RIVER_PROPORTION)
|
||||
.div(RIVER_PROPORTION)
|
||||
.max(0.0)
|
||||
.mul((1.0 - (chaos - 0.15) * 20.0).max(0.0).min(1.0));
|
||||
|
||||
let cliff_hill =
|
||||
(sim.gen_ctx.small_nz.get((wposf.div(128.0)).into_array()) as f32).mul(16.0);
|
||||
|
||||
let riverless_alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?
|
||||
+ (sim.gen_ctx.small_nz.get((wposf.div(256.0)).into_array()) as f32)
|
||||
.abs()
|
||||
.mul(chaos.max(0.2))
|
||||
.mul(64.0);
|
||||
|
||||
let cliffs = sim_chunk.cliffs;
|
||||
|
||||
let alt = riverless_alt
|
||||
- (1.0 - river)
|
||||
.mul(f32::consts::PI)
|
||||
.cos()
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.mul(24.0);
|
||||
|
||||
let water_level = (riverless_alt - 4.0 - 5.0 * chaos).max(CONFIG.sea_level);
|
||||
|
||||
let rock = (sim.gen_ctx.small_nz.get(
|
||||
Vec3::new(wposf.x, wposf.y, alt as f64)
|
||||
@ -55,15 +97,18 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
|
||||
let wposf3d = Vec3::new(wposf.x, wposf.y, alt as f64);
|
||||
|
||||
let marble = (0.0
|
||||
+ (sim.gen_ctx.hill_nz.get((wposf3d.div(48.0)).into_array()) as f32).mul(0.75)
|
||||
+ (sim.gen_ctx.hill_nz.get((wposf3d.div(3.0)).into_array()) as f32).mul(0.25))
|
||||
.add(1.0)
|
||||
.mul(0.5);
|
||||
let marble_small = (sim.gen_ctx.hill_nz.get((wposf3d.div(3.0)).into_array()) as f32)
|
||||
.add(1.0)
|
||||
.mul(0.5);
|
||||
let marble = (sim.gen_ctx.hill_nz.get((wposf3d.div(48.0)).into_array()) as f32)
|
||||
.mul(0.75)
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.add(marble_small.mul(0.25));
|
||||
|
||||
// Colours
|
||||
let cold_grass = Rgb::new(0.0, 0.3, 0.1);
|
||||
let warm_grass = Rgb::new(0.35, 1.0, 0.05);
|
||||
let cold_grass = Rgb::new(0.05, 0.2, 0.1);
|
||||
let warm_grass = Rgb::new(0.15, 0.65, 0.05);
|
||||
let cold_stone = Rgb::new(0.55, 0.7, 0.75);
|
||||
let warm_stone = Rgb::new(0.65, 0.65, 0.35);
|
||||
let beach_sand = Rgb::new(0.93, 0.84, 0.4);
|
||||
@ -74,10 +119,14 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
let sand = Rgb::lerp(beach_sand, desert_sand, marble);
|
||||
let cliff = Rgb::lerp(cold_stone, warm_stone, marble);
|
||||
|
||||
let dirt = Lerp::lerp(Rgb::new(0.2, 0.1, 0.05), Rgb::new(0.4, 0.25, 0.0), marble);
|
||||
|
||||
let turf = grass;
|
||||
|
||||
let ground = Rgb::lerp(
|
||||
Rgb::lerp(
|
||||
snow,
|
||||
grass,
|
||||
turf,
|
||||
temp.sub(CONFIG.snow_temp)
|
||||
.sub((marble - 0.5) * 0.05)
|
||||
.mul(256.0),
|
||||
@ -86,6 +135,66 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
temp.sub(CONFIG.desert_temp).mul(32.0),
|
||||
);
|
||||
|
||||
// Work out if we're on a path or near a town
|
||||
let dist_to_path = match &sim_chunk.location {
|
||||
Some(loc) => {
|
||||
let this_loc = &sim.locations[loc.loc_idx];
|
||||
this_loc
|
||||
.neighbours
|
||||
.iter()
|
||||
.map(|j| {
|
||||
let other_loc = &sim.locations[*j];
|
||||
|
||||
// Find the two location centers
|
||||
let near_0 = this_loc.center.map(|e| e as f32);
|
||||
let near_1 = other_loc.center.map(|e| e as f32);
|
||||
|
||||
// Calculate distance to path between them
|
||||
(0.0 + (near_1.y - near_0.y) * wposf_turb.x as f32
|
||||
- (near_1.x - near_0.x) * wposf_turb.y as f32
|
||||
+ near_1.x * near_0.y
|
||||
- near_0.x * near_1.y)
|
||||
.abs()
|
||||
.div(near_0.distance(near_1))
|
||||
})
|
||||
.filter(|x| x.is_finite())
|
||||
.min_by(|a, b| a.partial_cmp(b).unwrap())
|
||||
.unwrap_or(f32::INFINITY)
|
||||
}
|
||||
None => f32::INFINITY,
|
||||
};
|
||||
|
||||
let on_path = dist_to_path < 5.0 && !sim_chunk.near_cliffs; // || near_0.distance(wposf_turb.map(|e| e as f32)) < 150.0;
|
||||
|
||||
let (alt, ground) = if on_path {
|
||||
(alt - 1.0, dirt)
|
||||
} else {
|
||||
(alt, ground)
|
||||
};
|
||||
|
||||
// Cities
|
||||
// TODO: In a later MR
|
||||
/*
|
||||
let building = match &sim_chunk.location {
|
||||
Some(loc) => {
|
||||
let loc = &sim.locations[loc.loc_idx];
|
||||
let rpos = wposf.map2(loc.center, |a, b| a as f32 - b as f32) / 256.0 + 0.5;
|
||||
|
||||
if rpos.map(|e| e >= 0.0 && e < 1.0).reduce_and() {
|
||||
(loc.settlement
|
||||
.get_at(rpos)
|
||||
.map(|b| b.seed % 20 + 10)
|
||||
.unwrap_or(0)) as f32
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
None => 0.0,
|
||||
};
|
||||
|
||||
let alt = alt + building;
|
||||
*/
|
||||
|
||||
// Caves
|
||||
let cave_at = |wposf: Vec2<f64>| {
|
||||
(sim.gen_ctx.cave_0_nz.get(
|
||||
@ -99,7 +208,7 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
.mul((1.15 - chaos).min(1.0))
|
||||
};
|
||||
let cave_xy = cave_at(wposf);
|
||||
let cave_alt = alt - 32.0
|
||||
let cave_alt = alt - 24.0
|
||||
+ (sim
|
||||
.gen_ctx
|
||||
.cave_1_nz
|
||||
@ -108,15 +217,17 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
+ (sim
|
||||
.gen_ctx
|
||||
.cave_1_nz
|
||||
.get(Vec2::new(wposf.x, wposf.y).div(300.0).into_array()) as f32)
|
||||
.get(Vec2::new(wposf.x, wposf.y).div(500.0).into_array()) as f32)
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.powf(8.0)
|
||||
.mul(256.0);
|
||||
.powf(15.0)
|
||||
.mul(150.0);
|
||||
|
||||
Some(ColumnSample {
|
||||
alt,
|
||||
chaos,
|
||||
water_level,
|
||||
river,
|
||||
surface_color: Rgb::lerp(
|
||||
sand,
|
||||
// Land
|
||||
@ -133,34 +244,46 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
- marble * 24.0)
|
||||
/ 12.0,
|
||||
),
|
||||
(alt - CONFIG.sea_level - 0.2 * CONFIG.mountain_scale) / 180.0,
|
||||
(alt - CONFIG.sea_level - 0.3 * CONFIG.mountain_scale + marble * 128.0) / 100.0,
|
||||
),
|
||||
// Beach
|
||||
(alt - CONFIG.sea_level - 2.0) / 5.0,
|
||||
((alt - CONFIG.sea_level - 2.0) / 5.0).min(1.0 - river * 2.0),
|
||||
),
|
||||
sub_surface_color: dirt,
|
||||
tree_density,
|
||||
forest_kind,
|
||||
forest_kind: sim_chunk.forest_kind,
|
||||
close_trees: sim.gen_ctx.tree_gen.get(wpos),
|
||||
cave_xy,
|
||||
cave_alt,
|
||||
rock,
|
||||
cliff: cliffiness,
|
||||
cliffs,
|
||||
cliff_hill,
|
||||
close_cliffs: sim.gen_ctx.cliff_gen.get(wpos),
|
||||
temp,
|
||||
spawn_rate,
|
||||
location: sim_chunk.location.as_ref(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ColumnSample {
|
||||
pub struct ColumnSample<'a> {
|
||||
pub alt: f32,
|
||||
pub chaos: f32,
|
||||
pub water_level: f32,
|
||||
pub river: f32,
|
||||
pub surface_color: Rgb<f32>,
|
||||
pub sub_surface_color: Rgb<f32>,
|
||||
pub tree_density: f32,
|
||||
pub forest_kind: ForestKind,
|
||||
pub close_trees: [(Vec2<i32>, u32); 9],
|
||||
pub cave_xy: f32,
|
||||
pub cave_alt: f32,
|
||||
pub rock: f32,
|
||||
pub cliff: f32,
|
||||
pub cliffs: bool,
|
||||
pub cliff_hill: f32,
|
||||
pub close_cliffs: [(Vec2<i32>, u32); 9],
|
||||
pub temp: f32,
|
||||
pub spawn_rate: f32,
|
||||
pub location: Option<&'a LocationInfo>,
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
#![feature(euclidean_division, bind_by_move_pattern_guards)]
|
||||
#![feature(euclidean_division, bind_by_move_pattern_guards, option_flattening)]
|
||||
|
||||
mod all;
|
||||
mod block;
|
||||
mod column;
|
||||
pub mod config;
|
||||
mod sim;
|
||||
pub mod sim;
|
||||
pub mod util;
|
||||
|
||||
// Reexports
|
||||
@ -62,15 +62,21 @@ impl World {
|
||||
let water = Block::new(5, Rgb::new(100, 150, 255));
|
||||
|
||||
let chunk_size2d = Vec2::from(TerrainChunkSize::SIZE);
|
||||
let base_z = match self.sim.get_interpolated(
|
||||
chunk_pos.map2(chunk_size2d, |e, sz: u32| e * sz as i32 + sz as i32 / 2),
|
||||
|chunk| chunk.get_base_z(),
|
||||
) {
|
||||
Some(base_z) => base_z as i32,
|
||||
let (base_z, sim_chunk) = match self
|
||||
.sim
|
||||
.get_interpolated(
|
||||
chunk_pos.map2(chunk_size2d, |e, sz: u32| e * sz as i32 + sz as i32 / 2),
|
||||
|chunk| chunk.get_base_z(),
|
||||
)
|
||||
.and_then(|base_z| self.sim.get(chunk_pos).map(|sim_chunk| (base_z, sim_chunk)))
|
||||
{
|
||||
Some((base_z, sim_chunk)) => (base_z as i32, sim_chunk),
|
||||
None => return TerrainChunk::new(0, water, air, TerrainChunkMeta::void()),
|
||||
};
|
||||
|
||||
let mut chunk = TerrainChunk::new(base_z - 8, stone, air, TerrainChunkMeta::void());
|
||||
let meta = TerrainChunkMeta::new(sim_chunk.get_name(&self.sim), sim_chunk.get_biome());
|
||||
|
||||
let mut chunk = TerrainChunk::new(base_z - 8, stone, air, meta);
|
||||
|
||||
let mut sampler = self.sample_blocks();
|
||||
|
||||
|
@ -1,46 +1,68 @@
|
||||
use super::Settlement;
|
||||
use fxhash::FxHashSet;
|
||||
use rand::Rng;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum LocationKind {
|
||||
Settlement,
|
||||
Mountain,
|
||||
Forest,
|
||||
}
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Location {
|
||||
name: String,
|
||||
kind: LocationKind,
|
||||
kingdom: Kingdom,
|
||||
pub(crate) name: String,
|
||||
pub(crate) center: Vec2<i32>,
|
||||
pub(crate) kingdom: Option<Kingdom>,
|
||||
pub(crate) neighbours: FxHashSet<usize>,
|
||||
pub(crate) settlement: Settlement,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
pub fn generate(center: Vec2<i32>, rng: &mut impl Rng) -> Self {
|
||||
Self {
|
||||
name: generate_name(rng),
|
||||
center,
|
||||
kingdom: None,
|
||||
neighbours: FxHashSet::default(),
|
||||
settlement: Settlement::generate(rng),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kingdom(&self) -> Option<&Kingdom> {
|
||||
self.kingdom.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Kingdom {
|
||||
name: String,
|
||||
region_name: String,
|
||||
}
|
||||
|
||||
fn generate_name() -> String {
|
||||
let consts = [
|
||||
"st", "tr", "b", "n", "p", "ph", "cr", "g", "c", "d", "k", "kr", "kl", "gh", "sl", "st",
|
||||
"cr", "sp", "th", "dr", "pr", "dr", "gr", "br", "ryth", "rh", "sl", "f", "fr", "p", "pr",
|
||||
"qu", "s", "sh", "z", "k", "br", "wh", "tr", "h", "bl", "sl", "r", "kl", "sl", "w", "v",
|
||||
"vr", "kr",
|
||||
];
|
||||
let vowels = [
|
||||
"oo", "o", "oa", "au", "e", "ee", "ea", "ou", "u", "a", "i", "ie",
|
||||
fn generate_name<R: Rng>(rng: &mut R) -> String {
|
||||
let firstsyl = [
|
||||
"Eri", "Val", "Gla", "Wilde", "Cold", "Deep", "Dura", "Ester", "Fay", "Dark", "West",
|
||||
"East", "North", "South", "Ray", "Eri", "Dal", "Som", "Sommer", "Black", "Iron", "Grey",
|
||||
"Hel", "Gal", "Mor", "Lo", "Nil", "Bel", "Lor", "Gold", "Red", "Marble", "Mana", "Gar",
|
||||
"Mountain", "Red", "Cheo", "Far", "High",
|
||||
];
|
||||
let mid = ["ka", "se", "au", "da", "di"];
|
||||
let tails = [
|
||||
"er", "in", "o", "on", "an", "ar", "is", "oon", "er", "aru", "ab", "um", "id", "and",
|
||||
"eld", "ald", "oft", "aft", "ift", "ity", "ell", "oll", "ill", "all",
|
||||
/*"mill",*/ "ben", "sel", "dori", "theas", "dar", "bur", "to", "vis", "ten", "stone",
|
||||
"tiva", "id", "and", "or", "el", "ond", "ia", "eld", "ald", "aft", "ift", "ity", "well",
|
||||
"oll", "ill", "all", "wyn", "light", " Hill", "lin", "mont", "mor", "cliff", "rok", "den",
|
||||
"mi", "rock", "glenn", "rovi", "lea", "gate", "view", "ley", "wood", "ovia", "cliff",
|
||||
"marsh", "kor", "ice", /*"river",*/ "acre", "venn", "crest", "field", "vale",
|
||||
"spring", " Vale", "grasp", "fel", "fall", "grove", "wyn", "edge",
|
||||
];
|
||||
|
||||
let mut name = String::new();
|
||||
for i in 0..rand::random::<u32>() % 2 {
|
||||
name += rand::thread_rng().choose(&consts).unwrap();
|
||||
name += rand::thread_rng().choose(&vowels).unwrap();
|
||||
if rng.gen() {
|
||||
name += rng.choose(&firstsyl).unwrap();
|
||||
name += rng.choose(&mid).unwrap();
|
||||
name += rng.choose(&tails).unwrap();
|
||||
name
|
||||
} else {
|
||||
name += rng.choose(&firstsyl).unwrap();
|
||||
name += rng.choose(&tails).unwrap();
|
||||
name
|
||||
}
|
||||
name += rand::thread_rng().choose(&consts).unwrap();
|
||||
name += rand::thread_rng().choose(&tails).unwrap();
|
||||
|
||||
name
|
||||
}
|
||||
|
@ -1,9 +1,24 @@
|
||||
mod location;
|
||||
mod settlement;
|
||||
|
||||
use self::location::Location;
|
||||
use crate::{all::ForestKind, util::StructureGen2d, CONFIG};
|
||||
use common::{terrain::TerrainChunkSize, vol::VolSize};
|
||||
use noise::{BasicMulti, HybridMulti, MultiFractal, NoiseFn, RidgedMulti, Seedable, SuperSimplex};
|
||||
// Reexports
|
||||
pub use self::location::Location;
|
||||
pub use self::settlement::Settlement;
|
||||
|
||||
use crate::{
|
||||
all::ForestKind,
|
||||
util::{Sampler, StructureGen2d},
|
||||
CONFIG,
|
||||
};
|
||||
use common::{
|
||||
terrain::{BiomeKind, TerrainChunkSize},
|
||||
vol::VolSize,
|
||||
};
|
||||
use noise::{
|
||||
BasicMulti, HybridMulti, MultiFractal, NoiseFn, OpenSimplex, RidgedMulti, Seedable,
|
||||
SuperSimplex,
|
||||
};
|
||||
use rand::{prng::XorShiftRng, Rng, SeedableRng};
|
||||
use std::{
|
||||
ops::{Add, Div, Mul, Neg, Sub},
|
||||
sync::Arc,
|
||||
@ -13,12 +28,13 @@ use vek::*;
|
||||
pub const WORLD_SIZE: Vec2<usize> = Vec2 { x: 1024, y: 1024 };
|
||||
|
||||
pub(crate) struct GenCtx {
|
||||
pub turb_x_nz: BasicMulti,
|
||||
pub turb_y_nz: BasicMulti,
|
||||
pub turb_x_nz: SuperSimplex,
|
||||
pub turb_y_nz: SuperSimplex,
|
||||
pub chaos_nz: RidgedMulti,
|
||||
pub alt_nz: HybridMulti,
|
||||
pub hill_nz: SuperSimplex,
|
||||
pub temp_nz: SuperSimplex,
|
||||
pub dry_nz: BasicMulti,
|
||||
pub small_nz: BasicMulti,
|
||||
pub rock_nz: HybridMulti,
|
||||
pub cliff_nz: HybridMulti,
|
||||
@ -29,19 +45,23 @@ pub(crate) struct GenCtx {
|
||||
pub cave_1_nz: SuperSimplex,
|
||||
|
||||
pub tree_gen: StructureGen2d,
|
||||
pub cliff_gen: StructureGen2d,
|
||||
}
|
||||
|
||||
pub struct WorldSim {
|
||||
pub seed: u32,
|
||||
pub(crate) chunks: Vec<SimChunk>,
|
||||
pub(crate) locations: Vec<Location>,
|
||||
|
||||
pub(crate) gen_ctx: GenCtx,
|
||||
pub rng: XorShiftRng,
|
||||
}
|
||||
|
||||
impl WorldSim {
|
||||
pub fn generate(seed: u32) -> Self {
|
||||
let mut gen_ctx = GenCtx {
|
||||
turb_x_nz: BasicMulti::new().set_seed(seed + 0),
|
||||
turb_y_nz: BasicMulti::new().set_seed(seed + 1),
|
||||
turb_x_nz: SuperSimplex::new().set_seed(seed + 0),
|
||||
turb_y_nz: SuperSimplex::new().set_seed(seed + 1),
|
||||
chaos_nz: RidgedMulti::new().set_octaves(7).set_seed(seed + 2),
|
||||
hill_nz: SuperSimplex::new().set_seed(seed + 3),
|
||||
alt_nz: HybridMulti::new()
|
||||
@ -49,23 +69,25 @@ impl WorldSim {
|
||||
.set_persistence(0.1)
|
||||
.set_seed(seed + 4),
|
||||
temp_nz: SuperSimplex::new().set_seed(seed + 5),
|
||||
small_nz: BasicMulti::new().set_octaves(2).set_seed(seed + 6),
|
||||
rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(seed + 7),
|
||||
cliff_nz: HybridMulti::new().set_persistence(0.3).set_seed(seed + 7),
|
||||
warp_nz: BasicMulti::new().set_octaves(3).set_seed(seed + 8),
|
||||
dry_nz: BasicMulti::new().set_seed(seed + 6),
|
||||
small_nz: BasicMulti::new().set_octaves(2).set_seed(seed + 7),
|
||||
rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(seed + 8),
|
||||
cliff_nz: HybridMulti::new().set_persistence(0.3).set_seed(seed + 9),
|
||||
warp_nz: BasicMulti::new().set_octaves(3).set_seed(seed + 10),
|
||||
tree_nz: BasicMulti::new()
|
||||
.set_octaves(12)
|
||||
.set_persistence(0.75)
|
||||
.set_seed(seed + 9),
|
||||
cave_0_nz: SuperSimplex::new().set_seed(seed + 10),
|
||||
cave_1_nz: SuperSimplex::new().set_seed(seed + 11),
|
||||
.set_seed(seed + 12),
|
||||
cave_0_nz: SuperSimplex::new().set_seed(seed + 13),
|
||||
cave_1_nz: SuperSimplex::new().set_seed(seed + 14),
|
||||
|
||||
tree_gen: StructureGen2d::new(seed, 32, 24),
|
||||
cliff_gen: StructureGen2d::new(seed, 80, 56),
|
||||
};
|
||||
|
||||
let mut chunks = Vec::new();
|
||||
for x in 0..WORLD_SIZE.x as u32 {
|
||||
for y in 0..WORLD_SIZE.y as u32 {
|
||||
for x in 0..WORLD_SIZE.x as i32 {
|
||||
for y in 0..WORLD_SIZE.y as i32 {
|
||||
chunks.push(SimChunk::generate(Vec2::new(x, y), &mut gen_ctx));
|
||||
}
|
||||
}
|
||||
@ -73,21 +95,154 @@ impl WorldSim {
|
||||
let mut this = Self {
|
||||
seed,
|
||||
chunks,
|
||||
locations: Vec::new(),
|
||||
gen_ctx,
|
||||
rng: XorShiftRng::from_seed([
|
||||
(seed >> 0) as u8,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
(seed >> 8) as u8,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
(seed >> 16) as u8,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
(seed >> 24) as u8,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
};
|
||||
|
||||
this.simulate(100);
|
||||
this.seed_elements();
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
pub fn simulate(&mut self, cycles: usize) {
|
||||
// TODO
|
||||
/// Prepare the world for simulation
|
||||
pub fn seed_elements(&mut self) {
|
||||
let mut rng = self.rng.clone();
|
||||
|
||||
let cell_size = 32;
|
||||
let grid_size = WORLD_SIZE / cell_size;
|
||||
let loc_count = 250;
|
||||
|
||||
let mut loc_grid = vec![None; grid_size.product()];
|
||||
let mut locations = Vec::new();
|
||||
|
||||
// Seed the world with some locations
|
||||
for _ in 0..loc_count {
|
||||
let cell_pos = Vec2::new(
|
||||
self.rng.gen::<usize>() % grid_size.x,
|
||||
self.rng.gen::<usize>() % grid_size.y,
|
||||
);
|
||||
let wpos = (cell_pos * cell_size + cell_size / 2)
|
||||
.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| {
|
||||
e as i32 * sz as i32 + sz as i32 / 2
|
||||
});
|
||||
|
||||
locations.push(Location::generate(wpos, &mut rng));
|
||||
|
||||
loc_grid[cell_pos.y * grid_size.x + cell_pos.x] = Some(locations.len() - 1);
|
||||
}
|
||||
|
||||
// Find neighbours
|
||||
let mut loc_clone = locations
|
||||
.iter()
|
||||
.map(|l| l.center)
|
||||
.enumerate()
|
||||
.collect::<Vec<_>>();
|
||||
for i in 0..locations.len() {
|
||||
let pos = locations[i].center;
|
||||
|
||||
loc_clone.sort_by_key(|(_, l)| l.distance_squared(pos));
|
||||
|
||||
loc_clone.iter().skip(1).take(2).for_each(|(j, _)| {
|
||||
locations[i].neighbours.insert(*j);
|
||||
locations[*j].neighbours.insert(i);
|
||||
});
|
||||
}
|
||||
|
||||
// Simulate invasion!
|
||||
let invasion_cycles = 25;
|
||||
for _ in 0..invasion_cycles {
|
||||
for i in 0..grid_size.x {
|
||||
for j in 0..grid_size.y {
|
||||
if loc_grid[j * grid_size.x + i].is_none() {
|
||||
const R_COORDS: [i32; 5] = [-1, 0, 1, 0, -1];
|
||||
let idx = self.rng.gen::<usize>() % 4;
|
||||
let loc = Vec2::new(i as i32 + R_COORDS[idx], j as i32 + R_COORDS[idx + 1])
|
||||
.map(|e| e as usize);
|
||||
|
||||
loc_grid[j * grid_size.x + i] =
|
||||
loc_grid.get(loc.y * grid_size.x + loc.x).cloned().flatten();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Place the locations onto the world
|
||||
let gen = StructureGen2d::new(self.seed, cell_size as u32, cell_size as u32 / 2);
|
||||
for i in 0..WORLD_SIZE.x {
|
||||
for j in 0..WORLD_SIZE.y {
|
||||
let chunk_pos = Vec2::new(i as i32, j as i32);
|
||||
let block_pos = Vec2::new(
|
||||
chunk_pos.x * TerrainChunkSize::SIZE.x as i32,
|
||||
chunk_pos.y * TerrainChunkSize::SIZE.y as i32,
|
||||
);
|
||||
let cell_pos = Vec2::new(i / cell_size, j / cell_size);
|
||||
|
||||
// Find the distance to each region
|
||||
let near = gen.get(chunk_pos);
|
||||
let mut near = near
|
||||
.iter()
|
||||
.map(|(pos, seed)| RegionInfo {
|
||||
chunk_pos: *pos,
|
||||
block_pos: pos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| {
|
||||
e * sz as i32
|
||||
}),
|
||||
dist: (pos - chunk_pos).map(|e| e as f32).magnitude(),
|
||||
seed: *seed,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Sort regions based on distance
|
||||
near.sort_by(|a, b| a.dist.partial_cmp(&b.dist).unwrap());
|
||||
|
||||
let nearest_cell_pos = near[0].chunk_pos.map(|e| e as usize) / cell_size;
|
||||
self.get_mut(chunk_pos).unwrap().location = loc_grid
|
||||
.get(nearest_cell_pos.y * grid_size.x + nearest_cell_pos.x)
|
||||
.cloned()
|
||||
.unwrap_or(None)
|
||||
.map(|loc_idx| LocationInfo { loc_idx, near });
|
||||
|
||||
let town_size = 200;
|
||||
let in_town = self
|
||||
.get(chunk_pos)
|
||||
.unwrap()
|
||||
.location
|
||||
.as_ref()
|
||||
.map(|l| {
|
||||
locations[l.loc_idx].center.distance_squared(block_pos)
|
||||
< town_size * town_size
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if in_town {
|
||||
self.get_mut(chunk_pos).unwrap().spawn_rate = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.rng = rng;
|
||||
self.locations = locations;
|
||||
}
|
||||
|
||||
pub fn get(&self, chunk_pos: Vec2<u32>) -> Option<&SimChunk> {
|
||||
pub fn get(&self, chunk_pos: Vec2<i32>) -> Option<&SimChunk> {
|
||||
if chunk_pos
|
||||
.map2(WORLD_SIZE, |e, sz| e < sz as u32)
|
||||
.map2(WORLD_SIZE, |e, sz| e >= 0 && e < sz as i32)
|
||||
.reduce_and()
|
||||
{
|
||||
Some(&self.chunks[chunk_pos.y as usize * WORLD_SIZE.x + chunk_pos.x as usize])
|
||||
@ -96,7 +251,18 @@ impl WorldSim {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_base_z(&self, chunk_pos: Vec2<u32>) -> Option<f32> {
|
||||
pub fn get_mut(&mut self, chunk_pos: Vec2<i32>) -> Option<&mut SimChunk> {
|
||||
if chunk_pos
|
||||
.map2(WORLD_SIZE, |e, sz| e >= 0 && e < sz as i32)
|
||||
.reduce_and()
|
||||
{
|
||||
Some(&mut self.chunks[chunk_pos.y as usize * WORLD_SIZE.x + chunk_pos.x as usize])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_base_z(&self, chunk_pos: Vec2<i32>) -> Option<f32> {
|
||||
self.get(chunk_pos).and_then(|_| {
|
||||
(0..2)
|
||||
.map(|i| (0..2).map(move |j| (i, j)))
|
||||
@ -134,11 +300,10 @@ impl WorldSim {
|
||||
let mut x = [T::default(); 4];
|
||||
|
||||
for (x_idx, j) in (-1..3).enumerate() {
|
||||
let y0 =
|
||||
f(self.get(pos.map2(Vec2::new(j, -1), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
let y1 = f(self.get(pos.map2(Vec2::new(j, 0), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
let y2 = f(self.get(pos.map2(Vec2::new(j, 1), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
let y3 = f(self.get(pos.map2(Vec2::new(j, 2), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
let y0 = f(self.get(pos.map2(Vec2::new(j, -1), |e, q| e.max(0.0) as i32 + q))?);
|
||||
let y1 = f(self.get(pos.map2(Vec2::new(j, 0), |e, q| e.max(0.0) as i32 + q))?);
|
||||
let y2 = f(self.get(pos.map2(Vec2::new(j, 1), |e, q| e.max(0.0) as i32 + q))?);
|
||||
let y3 = f(self.get(pos.map2(Vec2::new(j, 2), |e, q| e.max(0.0) as i32 + q))?);
|
||||
|
||||
x[x_idx] = cubic(y0, y1, y2, y3, pos.y.fract() as f32);
|
||||
}
|
||||
@ -147,23 +312,40 @@ impl WorldSim {
|
||||
}
|
||||
}
|
||||
|
||||
const Z_TOLERANCE: (f32, f32) = (128.0, 96.0);
|
||||
const Z_TOLERANCE: (f32, f32) = (100.0, 128.0);
|
||||
|
||||
pub struct SimChunk {
|
||||
pub chaos: f32,
|
||||
pub alt_base: f32,
|
||||
pub alt: f32,
|
||||
pub temp: f32,
|
||||
pub dryness: f32,
|
||||
pub rockiness: f32,
|
||||
pub cliffiness: f32,
|
||||
pub cliffs: bool,
|
||||
pub near_cliffs: bool,
|
||||
pub tree_density: f32,
|
||||
pub forest_kind: ForestKind,
|
||||
pub location: Option<Arc<Location>>,
|
||||
pub spawn_rate: f32,
|
||||
pub location: Option<LocationInfo>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct RegionInfo {
|
||||
pub chunk_pos: Vec2<i32>,
|
||||
pub block_pos: Vec2<i32>,
|
||||
pub dist: f32,
|
||||
pub seed: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LocationInfo {
|
||||
pub loc_idx: usize,
|
||||
pub near: Vec<RegionInfo>,
|
||||
}
|
||||
|
||||
impl SimChunk {
|
||||
fn generate(pos: Vec2<u32>, gen_ctx: &mut GenCtx) -> Self {
|
||||
let wposf = (pos * Vec2::from(TerrainChunkSize::SIZE)).map(|e| e as f64);
|
||||
fn generate(pos: Vec2<i32>, gen_ctx: &mut GenCtx) -> Self {
|
||||
let wposf = (pos * TerrainChunkSize::SIZE.map(|e| e as i32)).map(|e| e as f64);
|
||||
|
||||
let hill = (0.0
|
||||
+ gen_ctx
|
||||
@ -177,10 +359,29 @@ impl SimChunk {
|
||||
.add(0.3)
|
||||
.max(0.0);
|
||||
|
||||
let dryness = (gen_ctx.dry_nz.get(
|
||||
(wposf
|
||||
.add(Vec2::new(
|
||||
gen_ctx
|
||||
.dry_nz
|
||||
.get((wposf.add(10000.0).div(500.0)).into_array())
|
||||
* 150.0,
|
||||
gen_ctx.dry_nz.get((wposf.add(0.0).div(500.0)).into_array()) * 150.0,
|
||||
))
|
||||
.div(2_000.0))
|
||||
.into_array(),
|
||||
) as f32);
|
||||
|
||||
let chaos = (gen_ctx.chaos_nz.get((wposf.div(4_000.0)).into_array()) as f32)
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.powf(1.4)
|
||||
.mul(
|
||||
(gen_ctx.chaos_nz.get((wposf.div(6_000.0)).into_array()) as f32)
|
||||
.powf(2.0)
|
||||
.add(0.5)
|
||||
.min(1.0),
|
||||
)
|
||||
.powf(1.5)
|
||||
.add(0.1 * hill);
|
||||
|
||||
let chaos = chaos + chaos.mul(16.0).sin().mul(0.02);
|
||||
@ -191,9 +392,9 @@ impl SimChunk {
|
||||
.add(alt_base.mul(128.0).sin().mul(0.005))
|
||||
.mul(400.0);
|
||||
|
||||
let alt_main = (gen_ctx.alt_nz.get((wposf.div(1_000.0)).into_array()) as f32)
|
||||
let alt_main = (gen_ctx.alt_nz.get((wposf.div(2_000.0)).into_array()) as f32)
|
||||
.abs()
|
||||
.powf(1.7);
|
||||
.powf(1.8);
|
||||
|
||||
let alt = CONFIG.sea_level
|
||||
+ alt_base
|
||||
@ -210,25 +411,27 @@ impl SimChunk {
|
||||
|
||||
let temp = (gen_ctx.temp_nz.get((wposf.div(8192.0)).into_array()) as f32);
|
||||
|
||||
let cliff = gen_ctx.cliff_nz.get((wposf.div(2048.0)).into_array()) as f32 + chaos * 0.2;
|
||||
|
||||
Self {
|
||||
chaos,
|
||||
alt_base,
|
||||
alt,
|
||||
temp,
|
||||
dryness,
|
||||
rockiness: (gen_ctx.rock_nz.get((wposf.div(1024.0)).into_array()) as f32)
|
||||
.sub(0.1)
|
||||
.mul(1.3)
|
||||
.max(0.0),
|
||||
cliffiness: (gen_ctx.cliff_nz.get((wposf.div(2048.0)).into_array()) as f32)
|
||||
.sub(0.15)
|
||||
.mul(3.0)
|
||||
.mul(1.1 - chaos)
|
||||
.max(0.0)
|
||||
.min(1.0),
|
||||
cliffs: cliff > 0.5
|
||||
&& dryness > 0.05
|
||||
&& alt > CONFIG.sea_level + 5.0
|
||||
&& dryness.abs() > 0.075,
|
||||
near_cliffs: cliff > 0.4,
|
||||
tree_density: (gen_ctx.tree_nz.get((wposf.div(1024.0)).into_array()) as f32)
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.mul(1.2 - chaos * 0.85)
|
||||
.mul(1.2 - chaos * 0.95)
|
||||
.add(0.1)
|
||||
.mul(if alt > CONFIG.sea_level + 5.0 {
|
||||
1.0
|
||||
@ -248,6 +451,7 @@ impl SimChunk {
|
||||
ForestKind::SnowPine
|
||||
}
|
||||
},
|
||||
spawn_rate: 1.0,
|
||||
location: None,
|
||||
}
|
||||
}
|
||||
@ -257,10 +461,35 @@ impl SimChunk {
|
||||
}
|
||||
|
||||
pub fn get_min_z(&self) -> f32 {
|
||||
self.alt - Z_TOLERANCE.0 * (self.chaos + 0.3)
|
||||
self.alt - Z_TOLERANCE.0 * (self.chaos * 1.2 + 0.3)
|
||||
}
|
||||
|
||||
pub fn get_max_z(&self) -> f32 {
|
||||
(self.alt + Z_TOLERANCE.1).max(CONFIG.sea_level + 1.0)
|
||||
(self.alt + Z_TOLERANCE.1 * if self.near_cliffs { 1.0 } else { 0.5 })
|
||||
.max(CONFIG.sea_level + 2.0)
|
||||
}
|
||||
|
||||
pub fn get_name(&self, world: &WorldSim) -> Option<String> {
|
||||
if let Some(loc) = &self.location {
|
||||
Some(world.locations[loc.loc_idx].name().to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_biome(&self) -> BiomeKind {
|
||||
if self.alt < CONFIG.sea_level {
|
||||
BiomeKind::Ocean
|
||||
} else if self.chaos > 0.6 {
|
||||
BiomeKind::Mountain
|
||||
} else if self.temp > CONFIG.desert_temp {
|
||||
BiomeKind::Desert
|
||||
} else if self.temp < CONFIG.snow_temp {
|
||||
BiomeKind::Snowlands
|
||||
} else if self.tree_density > 0.65 {
|
||||
BiomeKind::Forest
|
||||
} else {
|
||||
BiomeKind::Grassland
|
||||
}
|
||||
}
|
||||
}
|
||||
|
88
world/src/sim/settlement.rs
Normal file
88
world/src/sim/settlement.rs
Normal file
@ -0,0 +1,88 @@
|
||||
use rand::Rng;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Settlement {
|
||||
lot: Lot,
|
||||
}
|
||||
|
||||
impl Settlement {
|
||||
pub fn generate(rng: &mut impl Rng) -> Self {
|
||||
Self {
|
||||
lot: Lot::generate(0, 32.0, 1.0, rng),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_at(&self, pos: Vec2<f32>) -> Option<&Building> {
|
||||
self.lot.get_at(pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Building {
|
||||
pub seed: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum Lot {
|
||||
None,
|
||||
One(Building),
|
||||
Many { split_x: bool, lots: Vec<Lot> },
|
||||
}
|
||||
|
||||
impl Lot {
|
||||
pub fn generate(deep: usize, depth: f32, aspect: f32, rng: &mut impl Rng) -> Self {
|
||||
let depth = if deep < 3 { 8.0 } else { depth };
|
||||
|
||||
if (depth < 1.0 || deep > 6) && !(deep < 3 || deep % 2 == 1) {
|
||||
if rng.gen::<f32>() < 0.5 {
|
||||
Lot::One(Building { seed: rng.gen() })
|
||||
} else {
|
||||
Lot::None
|
||||
}
|
||||
} else {
|
||||
Lot::Many {
|
||||
split_x: aspect > 1.0,
|
||||
lots: {
|
||||
let pow2 = 1 + rng.gen::<usize>() % 1;
|
||||
let n = 1 << pow2;
|
||||
|
||||
let new_aspect = if aspect > 1.0 {
|
||||
aspect / n as f32
|
||||
} else {
|
||||
aspect * n as f32
|
||||
};
|
||||
|
||||
let vari = (rng.gen::<f32>() - 0.35) * 2.8;
|
||||
let new_depth = depth * 0.5 * (1.0 + vari);
|
||||
|
||||
(0..n)
|
||||
.map(|_| Lot::generate(deep + 1, new_depth, new_aspect, rng))
|
||||
.collect()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_at(&self, pos: Vec2<f32>) -> Option<&Building> {
|
||||
match self {
|
||||
Lot::None => None,
|
||||
Lot::One(building) => {
|
||||
if pos.map(|e| e > 0.1 && e < 0.9).reduce_and() {
|
||||
Some(building)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Lot::Many { split_x, lots } => {
|
||||
let split_dim = if *split_x { pos.x } else { pos.y };
|
||||
let idx = (split_dim * lots.len() as f32).floor() as usize;
|
||||
lots[idx.min(lots.len() - 1)].get_at(if *split_x {
|
||||
Vec2::new((pos.x * lots.len() as f32).fract(), pos.y)
|
||||
} else {
|
||||
Vec2::new(pos.x, (pos.y * lots.len() as f32).fract())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,24 +18,20 @@ impl Sampler for RandomField {
|
||||
fn get(&self, pos: Self::Index) -> Self::Sample {
|
||||
let pos = pos.map(|e| (e * 13 + (1 << 31)) as u32);
|
||||
|
||||
let next = self.seed.wrapping_mul(0x168E3D1F).wrapping_add(0xDEADBEAD);
|
||||
let next = next
|
||||
.rotate_left(13)
|
||||
.wrapping_mul(133227)
|
||||
.wrapping_add(pos.x);
|
||||
let next = next.rotate_left(13).wrapping_mul(318912) ^ 0x42133742;
|
||||
let next = next
|
||||
.rotate_left(13)
|
||||
.wrapping_mul(938219)
|
||||
.wrapping_add(pos.y);
|
||||
let next = next.rotate_left(13).wrapping_mul(318912) ^ 0x23341753;
|
||||
let next = next
|
||||
.rotate_left(13)
|
||||
.wrapping_mul(938219)
|
||||
.wrapping_add(pos.z);
|
||||
let next = next.rotate_left(13).wrapping_mul(313322) ^ 0xDEADBEEF;
|
||||
let next = next.rotate_left(13).wrapping_mul(929009) ^ 0xFF329DE3;
|
||||
let next = next.rotate_left(13).wrapping_mul(422671) ^ 0x42892942;
|
||||
next
|
||||
let mut a = self.seed;
|
||||
a = (a ^ 61) ^ (a >> 16);
|
||||
a = a + (a << 3);
|
||||
a = a ^ pos.x;
|
||||
a = a ^ (a >> 4);
|
||||
a = a * 0x27d4eb2d;
|
||||
a = a ^ (a >> 15);
|
||||
a = a ^ pos.y;
|
||||
a = (a ^ 61) ^ (a >> 16);
|
||||
a = a + (a << 3);
|
||||
a = a ^ (a >> 4);
|
||||
a = a ^ pos.z;
|
||||
a = a * 0x27d4eb2d;
|
||||
a = a ^ (a >> 15);
|
||||
a
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user