Merge branch 'zesterer/cities' into 'master'

Worldgen improvements

See merge request veloren/veloren!245
This commit is contained in:
Forest Anderson 2019-06-26 20:17:44 +00:00
commit e3cf9cdfdc
41 changed files with 1319 additions and 392 deletions

Binary file not shown.

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

Binary file not shown.

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)

Binary file not shown.

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)

Binary file not shown.

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)

Binary file not shown.

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)

Binary file not shown.

BIN
assets/world/tree/oak_green/9.vox (Stored with Git LFS)

Binary file not shown.

View File

@ -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)]

View File

@ -28,7 +28,7 @@ impl Block {
match self.kind {
0 => None,
1 => Some(0.85),
_ => Some(1.0),
_ => Some(3.0),
}
}
}

View File

@ -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 {

View File

@ -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);

View File

@ -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) {

View File

@ -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;

View File

@ -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 *

View File

@ -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);

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)) => {

View File

@ -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(),
)],

View File

@ -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",

View File

@ -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)

View File

@ -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],
);
}
}

View File

@ -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];
}
}
}

View File

@ -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,
));
}

View File

@ -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)

View File

@ -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
View 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
View 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;
}
}

View File

@ -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();
}

View File

@ -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)

View File

@ -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>,
}

View File

@ -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();

View File

@ -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
}

View File

@ -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
}
}
}

View 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())
})
}
}
}
}

View File

@ -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
}
}