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
e3cf9cdfdc
BIN
assets/voxygen/voxel/fixture/selection_bg.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/fixture/selection_bg.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/structures/human/mage_tower.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/structures/human/mage_tower.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/structures/human/town_hall.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/structures/human/town_hall.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/structures/human/town_hall_spire.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/structures/human/town_hall_spire.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/oak_green/1.vox
(Stored with Git LFS)
BIN
assets/world/tree/oak_green/1.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/tree/oak_green/2.vox
(Stored with Git LFS)
BIN
assets/world/tree/oak_green/2.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/tree/oak_green/4.vox
(Stored with Git LFS)
BIN
assets/world/tree/oak_green/4.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/tree/oak_green/5.vox
(Stored with Git LFS)
BIN
assets/world/tree/oak_green/5.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/tree/oak_green/6.vox
(Stored with Git LFS)
BIN
assets/world/tree/oak_green/6.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/tree/oak_green/7.vox
(Stored with Git LFS)
BIN
assets/world/tree/oak_green/7.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/tree/oak_green/8.vox
(Stored with Git LFS)
BIN
assets/world/tree/oak_green/8.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/tree/oak_green/9.vox
(Stored with Git LFS)
BIN
assets/world/tree/oak_green/9.vox
(Stored with Git LFS)
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…
Reference in New Issue
Block a user