diff --git a/assets/voxygen/voxel/fixture/selection_bg.vox b/assets/voxygen/voxel/fixture/selection_bg.vox index 66dc4a639e..0c8ce0e625 100644 --- a/assets/voxygen/voxel/fixture/selection_bg.vox +++ b/assets/voxygen/voxel/fixture/selection_bg.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d0c91d3e411f26f9fb0675ddbd00f02120d9396cc40c6f6e39cbf2be962d640 -size 594518 +oid sha256:8604501bac76da9bd609a2c1aa0ea8162c6c1b92884017451b8d781d92a4eff9 +size 594480 diff --git a/assets/world/structures/human/mage_tower.vox b/assets/world/structures/human/mage_tower.vox new file mode 100644 index 0000000000..7df09363c0 --- /dev/null +++ b/assets/world/structures/human/mage_tower.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6ed2f574af13b79f6cb6df5450068cc637372a24a739a892d55d4b1ff4f287b +size 125468 diff --git a/assets/world/structures/human/town_hall.vox b/assets/world/structures/human/town_hall.vox new file mode 100644 index 0000000000..8befbaa750 --- /dev/null +++ b/assets/world/structures/human/town_hall.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42595711180fbecf049f52b4e4c39a8d34d732d4aae841e7e32bd317a39c49e2 +size 80900 diff --git a/assets/world/structures/human/town_hall_spire.vox b/assets/world/structures/human/town_hall_spire.vox new file mode 100644 index 0000000000..f23eb7e8dc --- /dev/null +++ b/assets/world/structures/human/town_hall_spire.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81bed37162a2179b1e3cc9cf2b1a78fe2498acc9bbbbbca521884c87f0babe85 +size 81773 diff --git a/assets/world/tree/oak_green/1.vox b/assets/world/tree/oak_green/1.vox index 7127afa54f..c2227fc623 100644 --- a/assets/world/tree/oak_green/1.vox +++ b/assets/world/tree/oak_green/1.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92988d46dde3a0c9c7af42a4042b13de2eebf2f904a0b3679e792fc917b24dea -size 80836 +oid sha256:90cb2531d3a7bbf5a856b93abed2b4fa6c99038d2b1c119c01319e9bc6b092bd +size 80540 diff --git a/assets/world/tree/oak_green/2.vox b/assets/world/tree/oak_green/2.vox index 6000db082d..1ab9fe3e60 100644 --- a/assets/world/tree/oak_green/2.vox +++ b/assets/world/tree/oak_green/2.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f7c62ea16914827fc4e6c3030161e0b910ecbdb7d0db3f55bbe8a251e859fb5 -size 89252 +oid sha256:8cbe46a82f64ec25e3dba71606d2215e601b2bc643b7422fb50a8f1d253fb531 +size 88800 diff --git a/assets/world/tree/oak_green/4.vox b/assets/world/tree/oak_green/4.vox index 80872fb4da..7bcd66bd37 100644 --- a/assets/world/tree/oak_green/4.vox +++ b/assets/world/tree/oak_green/4.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a204b1300be9fe810f56c0f483f2fe3cacc73d35b57d0bf68d93bef5295ef44 -size 89944 +oid sha256:5862b4a6bf51df3a0659e1f007cf408714a4db659fda78370b5fad732c08eaa9 +size 89452 diff --git a/assets/world/tree/oak_green/5.vox b/assets/world/tree/oak_green/5.vox index 149167f780..bb1700709a 100644 --- a/assets/world/tree/oak_green/5.vox +++ b/assets/world/tree/oak_green/5.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db82d0f9c734ca5316be1965676bd49eea6c894e38033144b8f6e6430edac8cd -size 95628 +oid sha256:ee602e8454202dda569c4d03a2b52024f16b0b546c6b363f128d823f107d1e4f +size 95176 diff --git a/assets/world/tree/oak_green/6.vox b/assets/world/tree/oak_green/6.vox index b3875dafee..44a55fa64e 100644 --- a/assets/world/tree/oak_green/6.vox +++ b/assets/world/tree/oak_green/6.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8dba4cac13e74efefdaf60d4e3e4d11caa092f85edb7364cee8ef9a990ae441c -size 114484 +oid sha256:257fbc96e69cf00c6b0dd93df953dea53ee3cf628e296ba04259f887e83d7d1b +size 112496 diff --git a/assets/world/tree/oak_green/7.vox b/assets/world/tree/oak_green/7.vox index 2d77ba69cd..4809311ae6 100644 --- a/assets/world/tree/oak_green/7.vox +++ b/assets/world/tree/oak_green/7.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c662098c6d0dac131d718d4734f2eb6f4fe1ee0934eb193c52dea8015e7c8cd7 -size 124884 +oid sha256:6623f234e6a591a4f73934f192351eec1280a0c92208cd6e227e4c368a7e2e14 +size 123856 diff --git a/assets/world/tree/oak_green/8.vox b/assets/world/tree/oak_green/8.vox index f58e824b3b..76bcb46efc 100644 --- a/assets/world/tree/oak_green/8.vox +++ b/assets/world/tree/oak_green/8.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7a6925cf4085bfe315fc31052f82d958fd3df33492c98a165722ea794fff484 -size 117340 +oid sha256:c44033a6b7996f42022f79cb18323431f06dae59273713f3b943cad2a4cb6f2e +size 116232 diff --git a/assets/world/tree/oak_green/9.vox b/assets/world/tree/oak_green/9.vox index fb408054fe..159806222c 100644 --- a/assets/world/tree/oak_green/9.vox +++ b/assets/world/tree/oak_green/9.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1cf00092594f397180875ae4ad1f2fc4b220a526ac5782c3158f5c91f1bda08e -size 188286 +oid sha256:bddbc5e61b2f376653af7a567b946a1f8d7ede17c2ae8c3bc7053db5fa9575f8 +size 124118 diff --git a/common/src/state.rs b/common/src/state.rs index 56d476b4f4..656ff40a51 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -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)] diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index ef3b5d2ffb..fce23c4a57 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -28,7 +28,7 @@ impl Block { match self.kind { 0 => None, 1 => Some(0.85), - _ => Some(1.0), + _ => Some(3.0), } } } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 510a171ff9..262c6a3bba 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -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 : Offset your current position", - handle_jump + handle_jump, ), ChatCommand::new( "goto", "{d} {d} {d}", "/goto : Teleport to a position", - handle_goto + handle_goto, ), ChatCommand::new( "alias", "{}", "/alias : Change your alias", - handle_alias + handle_alias, ), ChatCommand::new( "tp", "{}", "/tp : 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 [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::().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 { diff --git a/voxygen/shaders/figure.frag b/voxygen/shaders/figure.frag index 36dd375a00..d5b8648ebd 100644 --- a/voxygen/shaders/figure.frag +++ b/voxygen/shaders/figure.frag @@ -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); diff --git a/voxygen/shaders/include/sky.glsl b/voxygen/shaders/include/sky.glsl index 4c917ca47a..9ae84af4b7 100644 --- a/voxygen/shaders/include/sky.glsl +++ b/voxygen/shaders/include/sky.glsl @@ -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) { diff --git a/voxygen/shaders/postprocess.frag b/voxygen/shaders/postprocess.frag index f1f644b4e6..4e1adf4040 100644 --- a/voxygen/shaders/postprocess.frag +++ b/voxygen/shaders/postprocess.frag @@ -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; diff --git a/voxygen/shaders/skybox.vert b/voxygen/shaders/skybox.vert index 4c7322fb3f..c1816d4127 100644 --- a/voxygen/shaders/skybox.vert +++ b/voxygen/shaders/skybox.vert @@ -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 * diff --git a/voxygen/shaders/terrain.frag b/voxygen/shaders/terrain.frag index 3678ec803d..9620347fe2 100644 --- a/voxygen/shaders/terrain.frag +++ b/voxygen/shaders/terrain.frag @@ -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); diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index a21a5f2b7f..f0c851c2e0 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -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 } } diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index d7eee449a5..f1a9755562 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -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 } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 9dfaf90800..9e9e9c4d8b 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -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)) => { diff --git a/voxygen/src/menu/char_selection/scene.rs b/voxygen/src/menu/char_selection/scene.rs index 9ea9a24756..f18c191fc1 100644 --- a/voxygen/src/menu/char_selection/scene.rs +++ b/voxygen/src/menu/char_selection/scene.rs @@ -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(), )], diff --git a/voxygen/src/menu/char_selection/ui.rs b/voxygen/src/menu/char_selection/ui.rs index 23a83f76a5..3c0bdbe6e8 100644 --- a/voxygen/src/menu/char_selection/ui.rs +++ b/voxygen/src/menu/char_selection/ui.rs @@ -142,7 +142,6 @@ widget_ids! { image_ids! { struct Imgs { - 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", diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index 380666ac99..99f8b6a7e5 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -70,7 +70,9 @@ image_ids! { 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) diff --git a/voxygen/src/mesh/segment.rs b/voxygen/src/mesh/segment.rs index ad01b2f054..7ab8555f2c 100644 --- a/voxygen/src/mesh/segment.rs +++ b/voxygen/src/mesh/segment.rs @@ -10,10 +10,6 @@ use vek::*; type FigureVertex = ::Vertex; -fn create_vertex(origin: Vec3, norm: Vec3, col: Rgb) -> FigureVertex { - FigureVertex::new(origin, norm, col, 0) -} - impl Meshable for Segment { type Pipeline = FigurePipeline; type Supplement = Vec3; @@ -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], ); } } diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index 868a2d9498..839092caa4 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -51,7 +51,7 @@ impl + 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 + 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 + 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]; } } } diff --git a/voxygen/src/mesh/vol.rs b/voxygen/src/mesh/vol.rs index 7223a46af7..563a520e15 100644 --- a/voxygen/src/mesh/vol.rs +++ b/voxygen/src/mesh/vol.rs @@ -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(vol: &V, pos: Vec3, dirs: &[Vec3]) -> Vec4 { +fn get_ao_quad( + vol: &V, + pos: Vec3, + shift: Vec3, + dirs: &[Vec3], + 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::>() + .collect::>() } // Utility function -fn create_quad, Vec3, Rgb) -> P::Vertex>( +fn create_quad, Vec3, Rgb, f32, f32) -> P::Vertex>( origin: Vec3, unit_x: Vec3, unit_y: Vec3, norm: Vec3, col: Rgb, - ao: Vec4, + darkness_ao: Vec4<(f32, f32)>, vcons: &F, ) -> Quad

{ 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, Vec3, Rgb) -> P::Vertex>( pub fn push_vox_verts< V: ReadVol, P: Pipeline, - F: Fn(Vec3, Vec3, Rgb) -> P::Vertex, + F: Fn(Vec3, Vec3, Rgb, f32, f32) -> P::Vertex, >( mesh: &mut Mesh

, vol: &V, @@ -92,6 +107,7 @@ pub fn push_vox_verts< col: Rgb, 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, )); } diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index fb35f50999..9409bd9658 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -757,8 +757,8 @@ impl FigureState { 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::::identity() * Mat4::translation_3d(self.pos) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 1c46ddd2a1..bd80a71f51 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -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(()) } diff --git a/world/examples/city.rs b/world/examples/city.rs new file mode 100644 index 0000000000..8774b3daf9 --- /dev/null +++ b/world/examples/city.rs @@ -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(); + } +} diff --git a/world/examples/turb.rs b/world/examples/turb.rs new file mode 100644 index 0000000000..6fa2f94a4b --- /dev/null +++ b/world/examples/turb.rs @@ -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; + } +} diff --git a/world/examples/view.rs b/world/examples/view.rs index e358924625..c1f52ec8e6 100644 --- a/world/examples/view.rs +++ b/world/examples/view.rs @@ -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(); } diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index c09efbd5c9..02e12d5ff5 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -15,7 +15,7 @@ use vek::*; pub struct BlockGen<'a> { world: &'a World, - column_cache: HashCache, Option>, + column_cache: HashCache, Option>>, 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) -> Option { - let column_gen = &mut self.column_gen; - self.column_cache + fn sample_column( + column_gen: &ColumnGen<'a>, + cache: &mut HashCache, Option>>, + wpos: Vec2, + ) -> Option> { + cache .get(Vec2::from(wpos), |wpos| column_gen.get(wpos)) .clone() } + + fn get_cliff_height( + column_gen: &ColumnGen<'a>, + cache: &mut HashCache, Option>>, + wpos: Vec2, + close_cliffs: &[(Vec2, 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; fn get(&mut self, wpos: Vec3) -> Option { + 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) diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 0783aa4dd5..05a27e6534 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -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; - type Sample = Option; + type Sample = Option>; - fn get(&self, wpos: Vec2) -> Option { + fn get(&self, wpos: Vec2) -> Option> { 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| { (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, + pub sub_surface_color: Rgb, pub tree_density: f32, pub forest_kind: ForestKind, pub close_trees: [(Vec2, 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, u32); 9], pub temp: f32, + pub spawn_rate: f32, + pub location: Option<&'a LocationInfo>, } diff --git a/world/src/lib.rs b/world/src/lib.rs index e3528616c4..515ffd13da 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -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(); diff --git a/world/src/sim/location.rs b/world/src/sim/location.rs index 892e8e62a8..240ddd03bf 100644 --- a/world/src/sim/location.rs +++ b/world/src/sim/location.rs @@ -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, + pub(crate) kingdom: Option, + pub(crate) neighbours: FxHashSet, + pub(crate) settlement: Settlement, +} + +impl Location { + pub fn generate(center: Vec2, 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(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::() % 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 } diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 501e95f5dd..158a2d6aa8 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -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 = 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, + pub(crate) locations: Vec, + 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::() % grid_size.x, + self.rng.gen::() % 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::>(); + 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::() % 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::>(); + + // 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) -> Option<&SimChunk> { + pub fn get(&self, chunk_pos: Vec2) -> 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) -> Option { + pub fn get_mut(&mut self, chunk_pos: Vec2) -> 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) -> Option { 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>, + pub spawn_rate: f32, + pub location: Option, +} + +#[derive(Copy, Clone)] +pub struct RegionInfo { + pub chunk_pos: Vec2, + pub block_pos: Vec2, + pub dist: f32, + pub seed: u32, +} + +#[derive(Clone)] +pub struct LocationInfo { + pub loc_idx: usize, + pub near: Vec, } impl SimChunk { - fn generate(pos: Vec2, gen_ctx: &mut GenCtx) -> Self { - let wposf = (pos * Vec2::from(TerrainChunkSize::SIZE)).map(|e| e as f64); + fn generate(pos: Vec2, 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 { + 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 + } } } diff --git a/world/src/sim/settlement.rs b/world/src/sim/settlement.rs new file mode 100644 index 0000000000..aadae70693 --- /dev/null +++ b/world/src/sim/settlement.rs @@ -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) -> 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 }, +} + +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::() < 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::() % 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::() - 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) -> 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()) + }) + } + } + } +} diff --git a/world/src/util/random.rs b/world/src/util/random.rs index f7ec6696c2..6e30b635bb 100644 --- a/world/src/util/random.rs +++ b/world/src/util/random.rs @@ -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 } }