mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
World colors are all hotloadable.
They live in assets/world/style/colors.ron. Only a small handful of hardcoed colors remain in World; they are either part of the map, or difficult to disentangle from the rest of the computation. Comments are made where appropriate.
This commit is contained in:
parent
5b1625f99d
commit
e2f5162e4f
@ -1,3 +1,5 @@
|
|||||||
|
#![enable(unwrap_newtypes)]
|
||||||
|
|
||||||
(
|
(
|
||||||
// NOTE: You can't change the legnths of these arrays without updating num_hair_colors() in
|
// NOTE: You can't change the legnths of these arrays without updating num_hair_colors() in
|
||||||
// common/src/comp/body/humanoid.rs. That's because this is a hack; we should really use enum
|
// common/src/comp/body/humanoid.rs. That's because this is a hack; we should really use enum
|
||||||
|
152
assets/world/style/colors.ron
Normal file
152
assets/world/style/colors.ron
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
#![enable(unwrap_newtypes)]
|
||||||
|
#![enable(implicit_some)]
|
||||||
|
|
||||||
|
// NOTE: Many of these colors are not used directly, but are modified in various ways (e.g. via
|
||||||
|
// lerping). So don't be too frustrated if a color change seems to have a different effect in
|
||||||
|
// different places; just follow the trends.
|
||||||
|
(
|
||||||
|
block: (
|
||||||
|
pyramid: (203, 170, 146),
|
||||||
|
|
||||||
|
// These are all ranges from low to high.
|
||||||
|
structure_blocks: (
|
||||||
|
None: None,
|
||||||
|
// Samples the surface color.
|
||||||
|
Grass: None,
|
||||||
|
// Water blocks ignore color information, and even if they didn't would not be lerped.
|
||||||
|
Water: None,
|
||||||
|
GreenSludge: None,
|
||||||
|
// Leaves all actually get interpolated.
|
||||||
|
TemperateLeaves: (start: (0, 132, 94), end: (142, 181, 0)),
|
||||||
|
PineLeaves: (start: (0, 60, 50), end: (30, 100, 10)),
|
||||||
|
PalmLeavesInner: (start: (61, 166, 43), end: (29, 130, 32)),
|
||||||
|
PalmLeavesOuter: (start: (62, 171, 38), end: (45, 171, 65)),
|
||||||
|
Acacia: (start: (15, 126, 50), end: (30, 180, 10)),
|
||||||
|
Liana: (start: (0, 125, 107), end: (0, 155, 129)),
|
||||||
|
Mangrove: (start: (32, 56, 22), end: (57, 69, 27)),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Water blocks ignore color now so this isn't used, but just in case this color was worth
|
||||||
|
// remembering here it is.
|
||||||
|
// green_sludge: (30.0, 126.0, 23.0)
|
||||||
|
),
|
||||||
|
column: (
|
||||||
|
cold_grass: (0.0, 0.5, 0.25),
|
||||||
|
warm_grass: (0.4, 0.8, 0.0),
|
||||||
|
dark_grass: (0.15, 0.4, 0.1),
|
||||||
|
wet_grass: (0.1, 0.8, 0.2),
|
||||||
|
cold_stone: (0.57, 0.67, 0.8),
|
||||||
|
hot_stone: (0.07, 0.07, 0.06),
|
||||||
|
warm_stone: (0.77, 0.77, 0.64),
|
||||||
|
beach_sand: (0.8, 0.75, 0.5),
|
||||||
|
desert_sand: (0.7, 0.4, 0.25),
|
||||||
|
snow: (0.8, 0.85, 1.0),
|
||||||
|
|
||||||
|
stone_col: (195, 187, 201),
|
||||||
|
|
||||||
|
dirt_low: (0.075, 0.07, 0.3),
|
||||||
|
dirt_high: (0.75, 0.55, 0.1),
|
||||||
|
|
||||||
|
snow_high: (0.01, 0.3, 0.0),
|
||||||
|
warm_stone_high: (0.3, 0.12, 0.2),
|
||||||
|
|
||||||
|
grass_high: (0.15, 0.2, 0.15),
|
||||||
|
tropical_high: (0.87, 0.62, 0.56),
|
||||||
|
),
|
||||||
|
// NOTE: I think (but am not sure) that this is the color of stuff below the bottom-most
|
||||||
|
// ground. I'm not sure how easy it is to see.
|
||||||
|
deep_stone_color: (125, 120, 130),
|
||||||
|
layer: (
|
||||||
|
bridge: (80, 80, 100),
|
||||||
|
stalagtite: (200, 200, 200),
|
||||||
|
),
|
||||||
|
site: (
|
||||||
|
castle: (),
|
||||||
|
dungeon: (
|
||||||
|
stone: (150, 150, 175),
|
||||||
|
),
|
||||||
|
settlement: (
|
||||||
|
building: (
|
||||||
|
archetype: (
|
||||||
|
keep: (
|
||||||
|
brick_base: (80, 80, 80),
|
||||||
|
floor_base: (80, 60, 10),
|
||||||
|
pole: (90, 70, 50),
|
||||||
|
flag: (
|
||||||
|
Evil: (80, 10, 130),
|
||||||
|
Good: (200, 80, 40),
|
||||||
|
),
|
||||||
|
stone: (
|
||||||
|
Evil: (65, 60, 55),
|
||||||
|
Good: (100, 100, 110),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
house: (
|
||||||
|
foundation: (100, 100, 100),
|
||||||
|
floor: (100, 75, 50),
|
||||||
|
roof: (
|
||||||
|
Roof1: (0x99, 0x5E, 0x54),
|
||||||
|
Roof2: (0x43, 0x63, 0x64),
|
||||||
|
Roof3: (0x76, 0x6D, 0x68),
|
||||||
|
Roof4: (0x7B, 0x41, 0x61),
|
||||||
|
Roof5: (0x52, 0x20, 0x20),
|
||||||
|
Roof6: (0x1A, 0x4A, 0x59),
|
||||||
|
Roof7: (0xCC, 0x76, 0x4E),
|
||||||
|
// (0x1D, 0x4D, 0x45),
|
||||||
|
// (0xB3, 0x7D, 0x60),
|
||||||
|
// (0xAC, 0x5D, 0x26),
|
||||||
|
// (0x32, 0x46, 0x6B),
|
||||||
|
// (0x2B, 0x19, 0x0F),
|
||||||
|
// (0x93, 0x78, 0x51),
|
||||||
|
// (0x92, 0x57, 0x24),
|
||||||
|
// (0x4A, 0x4E, 0x4E),
|
||||||
|
// (0x2F, 0x32, 0x47),
|
||||||
|
// (0x8F, 0x35, 0x43),
|
||||||
|
// (0x6D, 0x1E, 0x3A),
|
||||||
|
// (0x6D, 0xA7, 0x80),
|
||||||
|
// (0x4F, 0xA0, 0x95),
|
||||||
|
// (0xE2, 0xB9, 0x99),
|
||||||
|
// (0x7A, 0x30, 0x22),
|
||||||
|
// (0x4A, 0x06, 0x08),
|
||||||
|
// (0x8E, 0xB4, 0x57),
|
||||||
|
),
|
||||||
|
wall: (
|
||||||
|
Wall1: (200, 180, 150),
|
||||||
|
Wall2: (0xB8, 0xB4, 0xA4),
|
||||||
|
Wall3: (0x76, 0x6D, 0x68),
|
||||||
|
Wall4: (0xF3, 0xC9, 0x8F),
|
||||||
|
Wall5: (0xD3, 0xB7, 0x99),
|
||||||
|
Wall6: (0xE1, 0xAB, 0x91),
|
||||||
|
Wall7: (0x82, 0x57, 0x4C),
|
||||||
|
Wall8: (0xB9, 0x96, 0x77),
|
||||||
|
Wall9: (0xAE, 0x8D, 0x9C),
|
||||||
|
),
|
||||||
|
support: (
|
||||||
|
Support1: (60, 45, 30),
|
||||||
|
Support2: (0x65, 0x55, 0x56),
|
||||||
|
Support3: (0x53, 0x33, 0x13),
|
||||||
|
Support4: (0x58, 0x42, 0x33),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
plot_town_path: (100, 95, 65),
|
||||||
|
|
||||||
|
plot_field_dirt: (80, 55, 35),
|
||||||
|
plot_field_mound: (70, 80, 30),
|
||||||
|
|
||||||
|
wall_low: (130, 100, 0),
|
||||||
|
wall_high :(90, 70, 50),
|
||||||
|
|
||||||
|
tower_color: (50, 50, 50),
|
||||||
|
|
||||||
|
// NOTE: Ideally these would be part of a make_case_elim, but we can't use it beacuse
|
||||||
|
// it doesn't support struct variants yet.
|
||||||
|
plot_dirt: (90, 70, 50),
|
||||||
|
plot_grass: (100, 200, 0),
|
||||||
|
plot_water: (100, 150, 250),
|
||||||
|
plot_town: (150, 110, 60),
|
||||||
|
// TODO: Add field furrow stuff.
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
@ -170,5 +170,13 @@ impl ReloadIndicator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the watched file was changed
|
// Returns true if the watched file was changed
|
||||||
pub fn reloaded(&self) -> bool { self.reloaded.swap(false, Ordering::Acquire) }
|
pub fn reloaded(&self) -> bool {
|
||||||
|
// Optimize for the common case by performing an initial relaxed read, avoiding
|
||||||
|
// the atomic write.
|
||||||
|
if self.reloaded.load(Ordering::Relaxed) {
|
||||||
|
self.reloaded.swap(false, Ordering::Acquire)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#![feature(
|
#![feature(
|
||||||
arbitrary_enum_discriminant,
|
arbitrary_enum_discriminant,
|
||||||
const_checked_int_methods,
|
const_checked_int_methods,
|
||||||
|
fundamental,
|
||||||
option_unwrap_none,
|
option_unwrap_none,
|
||||||
bool_to_option,
|
bool_to_option,
|
||||||
label_break_value,
|
label_break_value,
|
||||||
@ -38,6 +39,7 @@ pub mod store;
|
|||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod sys;
|
pub mod sys;
|
||||||
pub mod terrain;
|
pub mod terrain;
|
||||||
|
pub mod typed;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod vol;
|
pub mod vol;
|
||||||
pub mod volumes;
|
pub mod volumes;
|
||||||
|
@ -453,10 +453,10 @@ pub struct Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Block {
|
impl Block {
|
||||||
pub fn new(kind: BlockKind, color: Rgb<u8>) -> Self {
|
pub const fn new(kind: BlockKind, color: Rgb<u8>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind,
|
kind,
|
||||||
color: color.into_array(),
|
color: [color.r, color.g, color.b],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use super::BlockKind;
|
use super::BlockKind;
|
||||||
use crate::{
|
use crate::{
|
||||||
assets::{self, Asset},
|
assets::{self, Asset},
|
||||||
|
make_case_elim,
|
||||||
vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol},
|
vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol},
|
||||||
volumes::dyna::{Dyna, DynaError},
|
volumes::dyna::{Dyna, DynaError},
|
||||||
};
|
};
|
||||||
@ -9,25 +10,29 @@ use serde::Deserialize;
|
|||||||
use std::{fs::File, io::BufReader, sync::Arc};
|
use std::{fs::File, io::BufReader, sync::Arc};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
make_case_elim!(
|
||||||
pub enum StructureBlock {
|
structure_block,
|
||||||
None,
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
Grass,
|
#[repr(u32)]
|
||||||
TemperateLeaves,
|
pub enum StructureBlock {
|
||||||
PineLeaves,
|
None = 0,
|
||||||
Acacia,
|
Grass = 1,
|
||||||
Mangrove,
|
TemperateLeaves = 2,
|
||||||
PalmLeavesInner,
|
PineLeaves = 3,
|
||||||
PalmLeavesOuter,
|
Acacia = 4,
|
||||||
Water,
|
Mangrove = 5,
|
||||||
GreenSludge,
|
PalmLeavesInner = 6,
|
||||||
Fruit,
|
PalmLeavesOuter = 7,
|
||||||
Coconut,
|
Water = 8,
|
||||||
Chest,
|
GreenSludge = 9,
|
||||||
Hollow,
|
Fruit = 10,
|
||||||
Liana,
|
Coconut = 11,
|
||||||
Normal(Rgb<u8>),
|
Chest = 12,
|
||||||
}
|
Hollow = 13,
|
||||||
|
Liana = 14,
|
||||||
|
Normal(color: Rgb<u8>) = 15,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
impl Vox for StructureBlock {
|
impl Vox for StructureBlock {
|
||||||
fn empty() -> Self { StructureBlock::None }
|
fn empty() -> Self { StructureBlock::None }
|
||||||
|
@ -22,6 +22,7 @@ impl<Context: SubContext<S>, T, S> Typed<Context, Pure<T>, S> for T {
|
|||||||
fn reduce(self, context: Context) -> (Pure<T>, S) { (Pure(self), context.sub_context()) }
|
fn reduce(self, context: Context) -> (Pure<T>, S) { (Pure(self), context.sub_context()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[fundamental]
|
||||||
pub struct ElimCase<Expr, Cases, Type> {
|
pub struct ElimCase<Expr, Cases, Type> {
|
||||||
pub expr: Expr,
|
pub expr: Expr,
|
||||||
pub cases: Cases,
|
pub cases: Cases,
|
||||||
@ -38,7 +39,7 @@ macro_rules! as_item {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! make_case_elim {
|
macro_rules! make_case_elim {
|
||||||
($mod:ident, $( #[$ty_attr:meta] )* $vis:vis enum $ty:ident {
|
($mod:ident, $( #[$ty_attr:meta] )* $vis:vis enum $ty:ident {
|
||||||
$( $constr:ident $( ( $( $arg_name:ident : $arg_ty:ty ),* ) )? = $index:tt ),* $(,)?
|
$( $constr:ident $( ( $( $arg_name:ident : $arg_ty:ty ),* ) )? = $index:expr ),* $(,)?
|
||||||
}) => {
|
}) => {
|
||||||
$crate::as_item! {
|
$crate::as_item! {
|
||||||
$( #[$ty_attr] )*
|
$( #[$ty_attr] )*
|
||||||
@ -48,9 +49,14 @@ macro_rules! make_case_elim {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(dead_code)]
|
||||||
$vis mod $mod {
|
$vis mod $mod {
|
||||||
use ::serde::{Deserialize, Serialize};
|
use ::serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub const NUM_VARIANTS: usize = 0 $( + { let _ = $index; 1 } )*;
|
||||||
|
|
||||||
|
pub const ALL_INDICES: [u32; NUM_VARIANTS] = [ $( $index, )* ];
|
||||||
|
|
||||||
pub trait PackedElim {
|
pub trait PackedElim {
|
||||||
$( type $constr; )*
|
$( type $constr; )*
|
||||||
}
|
}
|
||||||
@ -60,25 +66,25 @@ macro_rules! make_case_elim {
|
|||||||
$( pub $constr : Elim::$constr, )*
|
$( pub $constr : Elim::$constr, )*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> PackedElim for $crate::util::Pure<T> {
|
impl<T> PackedElim for $crate::typed::Pure<T> {
|
||||||
$( type $constr = T; )*
|
$( type $constr = T; )*
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PureCases<Elim> = Cases<$crate::util::Pure<Elim>>;
|
pub type PureCases<Elim> = Cases<$crate::typed::Pure<Elim>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_parens)]
|
#[allow(unused_parens)]
|
||||||
impl<'a, Elim: $mod::PackedElim, Context, Type, S>
|
impl<'a, Elim: $mod::PackedElim, Context, Type, S>
|
||||||
$crate::util::Typed<Context, Type, S> for $crate::util::ElimCase<&'a $ty, &'a $mod::Cases<Elim>, Type>
|
$crate::typed::Typed<Context, Type, S> for $crate::typed::ElimCase<&'a $ty, &'a $mod::Cases<Elim>, Type>
|
||||||
where
|
where
|
||||||
$( &'a Elim::$constr: $crate::util::Typed<($( ($( &'a $arg_ty, )*), )? Context), Type, S>, )*
|
$( &'a Elim::$constr: $crate::typed::Typed<($( ($( &'a $arg_ty, )*), )? Context), Type, S>, )*
|
||||||
{
|
{
|
||||||
fn reduce(self, context: Context) -> (Type, S)
|
fn reduce(self, context: Context) -> (Type, S)
|
||||||
{
|
{
|
||||||
let Self { expr, cases, .. } = self;
|
let Self { expr, cases, .. } = self;
|
||||||
match expr {
|
match expr {
|
||||||
$( $ty::$constr $( ($( $arg_name, )*) )? =>
|
$( $ty::$constr $( ($( $arg_name, )*) )? =>
|
||||||
<_ as $crate::util::Typed<_, Type, _>>::reduce(
|
<_ as $crate::typed::Typed<_, Type, _>>::reduce(
|
||||||
&cases.$constr,
|
&cases.$constr,
|
||||||
($( ($( $arg_name, )*), )? context),
|
($( ($( $arg_name, )*), )? context),
|
||||||
),
|
),
|
||||||
@ -91,11 +97,11 @@ macro_rules! make_case_elim {
|
|||||||
pub fn elim_case<'a, Elim: $mod::PackedElim, Context, S, Type>(&'a self, cases: &'a $mod::Cases<Elim>, context: Context) ->
|
pub fn elim_case<'a, Elim: $mod::PackedElim, Context, S, Type>(&'a self, cases: &'a $mod::Cases<Elim>, context: Context) ->
|
||||||
(Type, S)
|
(Type, S)
|
||||||
where
|
where
|
||||||
$crate::util::ElimCase<&'a $ty, &'a $mod::Cases<Elim>, Type> : $crate::util::Typed<Context, Type, S>,
|
$crate::typed::ElimCase<&'a $ty, &'a $mod::Cases<Elim>, Type> : $crate::typed::Typed<Context, Type, S>,
|
||||||
{
|
{
|
||||||
use $crate::util::Typed;
|
use $crate::typed::Typed;
|
||||||
|
|
||||||
let case = $crate::util::ElimCase {
|
let case = $crate::typed::ElimCase {
|
||||||
expr: self,
|
expr: self,
|
||||||
cases,
|
cases,
|
||||||
ty: ::core::marker::PhantomData,
|
ty: ::core::marker::PhantomData,
|
||||||
@ -105,7 +111,7 @@ macro_rules! make_case_elim {
|
|||||||
|
|
||||||
pub fn elim_case_pure<'a, Type>(&'a self, cases: &'a $mod::PureCases<Type>) -> &'a Type
|
pub fn elim_case_pure<'a, Type>(&'a self, cases: &'a $mod::PureCases<Type>) -> &'a Type
|
||||||
{
|
{
|
||||||
let ($crate::util::Pure(expr), ()) = self.elim_case(cases, ());
|
let ($crate::typed::Pure(expr), ()) = self.elim_case(cases, ());
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
mod color;
|
mod color;
|
||||||
mod dir;
|
mod dir;
|
||||||
mod typed;
|
|
||||||
|
|
||||||
pub const GIT_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/githash"));
|
pub const GIT_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/githash"));
|
||||||
|
|
||||||
@ -11,4 +10,3 @@ lazy_static::lazy_static! {
|
|||||||
|
|
||||||
pub use color::*;
|
pub use color::*;
|
||||||
pub use dir::*;
|
pub use dir::*;
|
||||||
pub use typed::*;
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
use crate::test_world::World;
|
use crate::test_world::{IndexOwned, World};
|
||||||
use common::{generation::ChunkSupplement, terrain::TerrainChunk};
|
use common::{generation::ChunkSupplement, terrain::TerrainChunk};
|
||||||
use crossbeam::channel;
|
use crossbeam::channel;
|
||||||
use hashbrown::{hash_map::Entry, HashMap};
|
use hashbrown::{hash_map::Entry, HashMap};
|
||||||
@ -9,11 +9,12 @@ use std::sync::{
|
|||||||
Arc,
|
Arc,
|
||||||
};
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
#[cfg(feature = "worldgen")] use world::World;
|
#[cfg(feature = "worldgen")]
|
||||||
|
use world::{IndexOwned, World};
|
||||||
|
|
||||||
type ChunkGenResult = (
|
type ChunkGenResult = (
|
||||||
Vec2<i32>,
|
Vec2<i32>,
|
||||||
Result<(TerrainChunk, ChunkSupplement), EcsEntity>,
|
Result<(TerrainChunk, ChunkSupplement), Option<EcsEntity>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
pub struct ChunkGenerator {
|
pub struct ChunkGenerator {
|
||||||
@ -34,10 +35,11 @@ impl ChunkGenerator {
|
|||||||
|
|
||||||
pub fn generate_chunk(
|
pub fn generate_chunk(
|
||||||
&mut self,
|
&mut self,
|
||||||
entity: EcsEntity,
|
entity: Option<EcsEntity>,
|
||||||
key: Vec2<i32>,
|
key: Vec2<i32>,
|
||||||
thread_pool: &mut uvth::ThreadPool,
|
thread_pool: &mut uvth::ThreadPool,
|
||||||
world: Arc<World>,
|
world: Arc<World>,
|
||||||
|
index: IndexOwned,
|
||||||
) {
|
) {
|
||||||
let v = if let Entry::Vacant(v) = self.pending_chunks.entry(key) {
|
let v = if let Entry::Vacant(v) = self.pending_chunks.entry(key) {
|
||||||
v
|
v
|
||||||
@ -48,8 +50,9 @@ impl ChunkGenerator {
|
|||||||
v.insert(Arc::clone(&cancel));
|
v.insert(Arc::clone(&cancel));
|
||||||
let chunk_tx = self.chunk_tx.clone();
|
let chunk_tx = self.chunk_tx.clone();
|
||||||
thread_pool.execute(move || {
|
thread_pool.execute(move || {
|
||||||
|
let index = index.as_index_ref();
|
||||||
let payload = world
|
let payload = world
|
||||||
.generate_chunk(key, || cancel.load(Ordering::Relaxed))
|
.generate_chunk(index, key, || cancel.load(Ordering::Relaxed))
|
||||||
.map_err(|_| entity);
|
.map_err(|_| entity);
|
||||||
let _ = chunk_tx.send((key, payload));
|
let _ = chunk_tx.send((key, payload));
|
||||||
});
|
});
|
||||||
@ -74,4 +77,10 @@ impl ChunkGenerator {
|
|||||||
cancel.store(true, Ordering::Relaxed);
|
cancel.store(true, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cancel_all(&mut self) {
|
||||||
|
self.pending_chunks.drain().for_each(|(_, cancel)| {
|
||||||
|
cancel.store(true, Ordering::Relaxed);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1473,7 +1473,7 @@ fn handle_debug_column(
|
|||||||
let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?;
|
let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?;
|
||||||
let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
|
let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
|
||||||
let chunk = sim.get(chunk_pos)?;
|
let chunk = sim.get(chunk_pos)?;
|
||||||
let col = sampler.get((wpos, server.world.index()))?;
|
let col = sampler.get((wpos, server.index.as_index_ref()))?;
|
||||||
let downhill = chunk.downhill;
|
let downhill = chunk.downhill;
|
||||||
let river = &chunk.river;
|
let river = &chunk.river;
|
||||||
let flux = chunk.flux;
|
let flux = chunk.flux;
|
||||||
|
@ -55,7 +55,7 @@ use std::{
|
|||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
use test_world::World;
|
use test_world::{IndexOwned, World};
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
use uvth::{ThreadPool, ThreadPoolBuilder};
|
use uvth::{ThreadPool, ThreadPoolBuilder};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -63,7 +63,7 @@ use vek::*;
|
|||||||
use world::{
|
use world::{
|
||||||
civ::SiteKind,
|
civ::SiteKind,
|
||||||
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP},
|
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP},
|
||||||
World,
|
IndexOwned, World,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[macro_use] extern crate diesel;
|
#[macro_use] extern crate diesel;
|
||||||
@ -82,6 +82,7 @@ pub struct Tick(u64);
|
|||||||
pub struct Server {
|
pub struct Server {
|
||||||
state: State,
|
state: State,
|
||||||
world: Arc<World>,
|
world: Arc<World>,
|
||||||
|
index: IndexOwned,
|
||||||
map: WorldMapMsg,
|
map: WorldMapMsg,
|
||||||
|
|
||||||
network: Network,
|
network: Network,
|
||||||
@ -173,7 +174,7 @@ impl Server {
|
|||||||
state.ecs_mut().insert(AliasValidator::new(banned_words));
|
state.ecs_mut().insert(AliasValidator::new(banned_words));
|
||||||
|
|
||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
let world = World::generate(settings.world_seed, WorldOpts {
|
let (world, index) = World::generate(settings.world_seed, WorldOpts {
|
||||||
seed_elements: true,
|
seed_elements: true,
|
||||||
world_file: if let Some(ref opts) = settings.map_file {
|
world_file: if let Some(ref opts) = settings.map_file {
|
||||||
opts.clone()
|
opts.clone()
|
||||||
@ -184,10 +185,10 @@ impl Server {
|
|||||||
..WorldOpts::default()
|
..WorldOpts::default()
|
||||||
});
|
});
|
||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
let map = world.get_map_data();
|
let map = world.get_map_data(index.as_index_ref());
|
||||||
|
|
||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
let world = World::generate(settings.world_seed);
|
let (world, index) = World::generate(settings.world_seed);
|
||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
let map = WorldMapMsg {
|
let map = WorldMapMsg {
|
||||||
dimensions: Vec2::new(1, 1),
|
dimensions: Vec2::new(1, 1),
|
||||||
@ -198,6 +199,7 @@ impl Server {
|
|||||||
|
|
||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
let spawn_point = {
|
let spawn_point = {
|
||||||
|
let index = index.as_index_ref();
|
||||||
// NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops,
|
// NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops,
|
||||||
// but are needed to be explicit about casting (and to make the compiler stop
|
// but are needed to be explicit about casting (and to make the compiler stop
|
||||||
// complaining)
|
// complaining)
|
||||||
@ -224,11 +226,11 @@ impl Server {
|
|||||||
// get a z cache for the collumn in which we want to spawn
|
// get a z cache for the collumn in which we want to spawn
|
||||||
let mut block_sampler = world.sample_blocks();
|
let mut block_sampler = world.sample_blocks();
|
||||||
let z_cache = block_sampler
|
let z_cache = block_sampler
|
||||||
.get_z_cache(spawn_location, world.index())
|
.get_z_cache(spawn_location, index)
|
||||||
.expect(&format!("no z_cache found for chunk: {}", spawn_chunk));
|
.expect(&format!("no z_cache found for chunk: {}", spawn_chunk));
|
||||||
|
|
||||||
// get the minimum and maximum z values at which there could be soild blocks
|
// get the minimum and maximum z values at which there could be soild blocks
|
||||||
let (min_z, _, max_z) = z_cache.get_z_limits(&mut block_sampler, world.index());
|
let (min_z, _, max_z) = z_cache.get_z_limits(&mut block_sampler, index);
|
||||||
// round range outwards, so no potential air block is missed
|
// round range outwards, so no potential air block is missed
|
||||||
let min_z = min_z.floor() as i32;
|
let min_z = min_z.floor() as i32;
|
||||||
let max_z = max_z.ceil() as i32;
|
let max_z = max_z.ceil() as i32;
|
||||||
@ -244,7 +246,7 @@ impl Server {
|
|||||||
Vec3::new(spawn_location.x, spawn_location.y, *z),
|
Vec3::new(spawn_location.x, spawn_location.y, *z),
|
||||||
Some(&z_cache),
|
Some(&z_cache),
|
||||||
false,
|
false,
|
||||||
world.index(),
|
index,
|
||||||
)
|
)
|
||||||
.map(|b| b.is_air())
|
.map(|b| b.is_air())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
@ -288,6 +290,7 @@ impl Server {
|
|||||||
let this = Self {
|
let this = Self {
|
||||||
state,
|
state,
|
||||||
world: Arc::new(world),
|
world: Arc::new(world),
|
||||||
|
index,
|
||||||
map,
|
map,
|
||||||
|
|
||||||
network,
|
network,
|
||||||
@ -494,6 +497,42 @@ impl Server {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
// Check for new chunks; cancel and regenerate all chunks if the asset has been
|
||||||
|
// reloaded. Note that all of these assignments are no-ops, so the
|
||||||
|
// only work we do here on the fast path is perform a relaxed read on an atomic.
|
||||||
|
// boolean.
|
||||||
|
let index = &mut self.index;
|
||||||
|
let thread_pool = &mut self.thread_pool;
|
||||||
|
let world = &mut self.world;
|
||||||
|
let ecs = self.state.ecs_mut();
|
||||||
|
|
||||||
|
index.reload_colors_if_changed(|index| {
|
||||||
|
let mut chunk_generator = ecs.write_resource::<ChunkGenerator>();
|
||||||
|
let client = ecs.read_storage::<Client>();
|
||||||
|
let mut terrain = ecs.write_resource::<common::terrain::TerrainGrid>();
|
||||||
|
|
||||||
|
// Cancel all pending chunks.
|
||||||
|
chunk_generator.cancel_all();
|
||||||
|
|
||||||
|
if client.is_empty() {
|
||||||
|
// No cilents, so just clear all terrain.
|
||||||
|
terrain.clear();
|
||||||
|
} else {
|
||||||
|
// There's at leasat one client, so regenerate all chunks.
|
||||||
|
terrain.iter().for_each(|(pos, _)| {
|
||||||
|
chunk_generator.generate_chunk(
|
||||||
|
None,
|
||||||
|
pos,
|
||||||
|
thread_pool,
|
||||||
|
world.clone(),
|
||||||
|
index.clone(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let end_of_server_tick = Instant::now();
|
let end_of_server_tick = Instant::now();
|
||||||
|
|
||||||
// 8) Update Metrics
|
// 8) Update Metrics
|
||||||
@ -728,7 +767,13 @@ impl Server {
|
|||||||
self.state
|
self.state
|
||||||
.ecs()
|
.ecs()
|
||||||
.write_resource::<ChunkGenerator>()
|
.write_resource::<ChunkGenerator>()
|
||||||
.generate_chunk(entity, key, &mut self.thread_pool, self.world.clone());
|
.generate_chunk(
|
||||||
|
Some(entity),
|
||||||
|
key,
|
||||||
|
&mut self.thread_pool,
|
||||||
|
self.world.clone(),
|
||||||
|
self.index.clone(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_chat_cmd(&mut self, entity: EcsEntity, cmd: String) {
|
fn process_chat_cmd(&mut self, entity: EcsEntity, cmd: String) {
|
||||||
|
@ -60,7 +60,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
'insert_terrain_chunks: while let Some((key, res)) = chunk_generator.recv_new_chunk() {
|
'insert_terrain_chunks: while let Some((key, res)) = chunk_generator.recv_new_chunk() {
|
||||||
let (chunk, supplement) = match res {
|
let (chunk, supplement) = match res {
|
||||||
Ok((chunk, supplement)) => (chunk, supplement),
|
Ok((chunk, supplement)) => (chunk, supplement),
|
||||||
Err(entity) => {
|
Err(Some(entity)) => {
|
||||||
if let Some(client) = clients.get_mut(entity) {
|
if let Some(client) = clients.get_mut(entity) {
|
||||||
client.notify(ServerMsg::TerrainChunkUpdate {
|
client.notify(ServerMsg::TerrainChunkUpdate {
|
||||||
key,
|
key,
|
||||||
@ -69,6 +69,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
continue 'insert_terrain_chunks;
|
continue 'insert_terrain_chunks;
|
||||||
},
|
},
|
||||||
|
Err(None) => {
|
||||||
|
continue 'insert_terrain_chunks;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
// Send the chunk to all nearby players.
|
// Send the chunk to all nearby players.
|
||||||
for (view_distance, pos, client) in (&players, &positions, &mut clients)
|
for (view_distance, pos, client) in (&players, &positions, &mut clients)
|
||||||
|
@ -16,8 +16,25 @@ const DEFAULT_WORLD_CHUNKS_LG: MapSizeLg =
|
|||||||
|
|
||||||
pub struct World;
|
pub struct World;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct IndexOwned;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct IndexRef<'a>(&'a IndexOwned);
|
||||||
|
|
||||||
|
impl IndexOwned {
|
||||||
|
pub fn reload_colors_if_changed<R>(
|
||||||
|
&mut self,
|
||||||
|
_reload: impl FnOnce(&mut Self) -> R,
|
||||||
|
) -> Option<R> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_index_ref(&self) -> IndexRef { IndexRef(self) }
|
||||||
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn generate(_seed: u32) -> Self { Self }
|
pub fn generate(_seed: u32) -> (Self, IndexOwned) { (Self, IndexOwned) }
|
||||||
|
|
||||||
pub fn tick(&self, dt: Duration) {}
|
pub fn tick(&self, dt: Duration) {}
|
||||||
|
|
||||||
@ -26,6 +43,7 @@ impl World {
|
|||||||
|
|
||||||
pub fn generate_chunk(
|
pub fn generate_chunk(
|
||||||
&self,
|
&self,
|
||||||
|
_index: IndexRef,
|
||||||
chunk_pos: Vec2<i32>,
|
chunk_pos: Vec2<i32>,
|
||||||
_should_continue: impl FnMut() -> bool,
|
_should_continue: impl FnMut() -> bool,
|
||||||
) -> Result<(TerrainChunk, ChunkSupplement), ()> {
|
) -> Result<(TerrainChunk, ChunkSupplement), ()> {
|
||||||
|
@ -36,13 +36,14 @@ fn main() {
|
|||||||
let mut _map_file = PathBuf::from("./maps");
|
let mut _map_file = PathBuf::from("./maps");
|
||||||
_map_file.push(map_file);
|
_map_file.push(map_file);
|
||||||
|
|
||||||
let world = World::generate(5284, WorldOpts {
|
let (world, index) = World::generate(5284, WorldOpts {
|
||||||
seed_elements: false,
|
seed_elements: false,
|
||||||
world_file: sim::FileOpts::LoadAsset(veloren_world::sim::DEFAULT_WORLD_MAP.into()),
|
world_file: sim::FileOpts::LoadAsset(veloren_world::sim::DEFAULT_WORLD_MAP.into()),
|
||||||
// world_file: sim::FileOpts::Load(_map_file),
|
// world_file: sim::FileOpts::Load(_map_file),
|
||||||
// world_file: sim::FileOpts::Save,
|
// world_file: sim::FileOpts::Save,
|
||||||
..WorldOpts::default()
|
..WorldOpts::default()
|
||||||
});
|
});
|
||||||
|
let index = index.as_index_ref();
|
||||||
tracing::info!("Sampling data...");
|
tracing::info!("Sampling data...");
|
||||||
let sampler = world.sim();
|
let sampler = world.sim();
|
||||||
let map_size_lg = sampler.map_size_lg();
|
let map_size_lg = sampler.map_size_lg();
|
||||||
@ -55,7 +56,7 @@ fn main() {
|
|||||||
column_sample.get((
|
column_sample.get((
|
||||||
uniform_idx_as_vec2(map_size_lg, posi)
|
uniform_idx_as_vec2(map_size_lg, posi)
|
||||||
* TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
* TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||||
world.index(),
|
index,
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
@ -68,7 +69,7 @@ fn main() {
|
|||||||
sample_pos(
|
sample_pos(
|
||||||
config,
|
config,
|
||||||
sampler,
|
sampler,
|
||||||
world.index(),
|
index,
|
||||||
samples,
|
samples,
|
||||||
uniform_idx_as_vec2(map_size_lg, posi),
|
uniform_idx_as_vec2(map_size_lg, posi),
|
||||||
)
|
)
|
||||||
|
@ -3,15 +3,28 @@ mod natural;
|
|||||||
use crate::{
|
use crate::{
|
||||||
column::{ColumnGen, ColumnSample},
|
column::{ColumnGen, ColumnSample},
|
||||||
util::{RandomField, Sampler, SmallCache},
|
util::{RandomField, Sampler, SmallCache},
|
||||||
Index,
|
IndexRef,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
terrain::{structure::StructureBlock, Block, BlockKind, Structure},
|
terrain::{
|
||||||
|
structure::{self, StructureBlock},
|
||||||
|
Block, BlockKind, Structure,
|
||||||
|
},
|
||||||
vol::{ReadVol, Vox},
|
vol::{ReadVol, Vox},
|
||||||
};
|
};
|
||||||
use std::ops::{Div, Mul};
|
use core::ops::{Div, Mul, Range};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Colors {
|
||||||
|
pub pyramid: (u8, u8, u8),
|
||||||
|
// TODO(@Sharp): After the merge, construct enough infrastructure to make it convenient to
|
||||||
|
// define mapping functions over the input; i.e. we should be able to interpret some fields as
|
||||||
|
// defining App<Abs<Fun, Type>, Arg>, where Fun : (Context, Arg) → (S, Type).
|
||||||
|
pub structure_blocks: structure::structure_block::PureCases<Option<Range<(u8, u8, u8)>>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct BlockGen<'a> {
|
pub struct BlockGen<'a> {
|
||||||
pub column_cache: SmallCache<Option<ColumnSample<'a>>>,
|
pub column_cache: SmallCache<Option<ColumnSample<'a>>>,
|
||||||
pub column_gen: ColumnGen<'a>,
|
pub column_gen: ColumnGen<'a>,
|
||||||
@ -29,7 +42,7 @@ impl<'a> BlockGen<'a> {
|
|||||||
column_gen: &ColumnGen<'a>,
|
column_gen: &ColumnGen<'a>,
|
||||||
cache: &'b mut SmallCache<Option<ColumnSample<'a>>>,
|
cache: &'b mut SmallCache<Option<ColumnSample<'a>>>,
|
||||||
wpos: Vec2<i32>,
|
wpos: Vec2<i32>,
|
||||||
index: &'a Index,
|
index: IndexRef<'a>,
|
||||||
) -> Option<&'b ColumnSample<'a>> {
|
) -> Option<&'b ColumnSample<'a>> {
|
||||||
cache
|
cache
|
||||||
.get(wpos, |wpos| column_gen.get((wpos, index)))
|
.get(wpos, |wpos| column_gen.get((wpos, index)))
|
||||||
@ -43,7 +56,7 @@ impl<'a> BlockGen<'a> {
|
|||||||
close_cliffs: &[(Vec2<i32>, u32); 9],
|
close_cliffs: &[(Vec2<i32>, u32); 9],
|
||||||
cliff_hill: f32,
|
cliff_hill: f32,
|
||||||
tolerance: f32,
|
tolerance: f32,
|
||||||
index: &'a Index,
|
index: IndexRef<'a>,
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
close_cliffs.iter().fold(
|
close_cliffs.iter().fold(
|
||||||
0.0f32,
|
0.0f32,
|
||||||
@ -89,7 +102,7 @@ impl<'a> BlockGen<'a> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_z_cache(&mut self, wpos: Vec2<i32>, index: &'a Index) -> Option<ZCache<'a>> {
|
pub fn get_z_cache(&mut self, wpos: Vec2<i32>, index: IndexRef<'a>) -> Option<ZCache<'a>> {
|
||||||
let BlockGen {
|
let BlockGen {
|
||||||
column_cache,
|
column_cache,
|
||||||
column_gen,
|
column_gen,
|
||||||
@ -143,7 +156,7 @@ impl<'a> BlockGen<'a> {
|
|||||||
wpos: Vec3<i32>,
|
wpos: Vec3<i32>,
|
||||||
z_cache: Option<&ZCache>,
|
z_cache: Option<&ZCache>,
|
||||||
only_structures: bool,
|
only_structures: bool,
|
||||||
index: &'a Index,
|
index: IndexRef<'a>,
|
||||||
) -> Option<Block> {
|
) -> Option<Block> {
|
||||||
let BlockGen {
|
let BlockGen {
|
||||||
column_cache,
|
column_cache,
|
||||||
@ -237,18 +250,7 @@ impl<'a> BlockGen<'a> {
|
|||||||
|
|
||||||
// Sample blocks
|
// Sample blocks
|
||||||
|
|
||||||
// let stone_col = Rgb::new(195, 187, 201);
|
let water = Block::new(BlockKind::Water, Rgb::zero());
|
||||||
|
|
||||||
// let dirt_col = Rgb::new(79, 67, 60);
|
|
||||||
|
|
||||||
let _air = Block::empty();
|
|
||||||
// let stone = Block::new(2, stone_col);
|
|
||||||
// let surface_stone = Block::new(1, Rgb::new(200, 220, 255));
|
|
||||||
// let dirt = Block::new(1, dirt_col);
|
|
||||||
// let sand = Block::new(1, Rgb::new(180, 150, 50));
|
|
||||||
// let warm_stone = Block::new(1, Rgb::new(165, 165, 130));
|
|
||||||
|
|
||||||
let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190));
|
|
||||||
|
|
||||||
let grass_depth = (1.5 + 2.0 * chaos).min(height - basement_height);
|
let grass_depth = (1.5 + 2.0 * chaos).min(height - basement_height);
|
||||||
let block = if (wposf.z as f32) < height - grass_depth {
|
let block = if (wposf.z as f32) < height - grass_depth {
|
||||||
@ -389,7 +391,7 @@ impl<'a> BlockGen<'a> {
|
|||||||
.iter()
|
.iter()
|
||||||
.find_map(|st| {
|
.find_map(|st| {
|
||||||
let (st, st_sample) = st.as_ref()?;
|
let (st, st_sample) = st.as_ref()?;
|
||||||
st.get(wpos, st_sample)
|
st.get(index, wpos, st_sample)
|
||||||
})
|
})
|
||||||
.or(block);
|
.or(block);
|
||||||
|
|
||||||
@ -407,7 +409,7 @@ impl<'a> ZCache<'a> {
|
|||||||
pub fn get_z_limits<'b>(
|
pub fn get_z_limits<'b>(
|
||||||
&self,
|
&self,
|
||||||
block_gen: &mut BlockGen<'b>,
|
block_gen: &mut BlockGen<'b>,
|
||||||
index: &'b Index,
|
index: IndexRef<'b>,
|
||||||
) -> (f32, f32, f32) {
|
) -> (f32, f32, f32) {
|
||||||
let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0);
|
let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0);
|
||||||
let min = min - 4.0;
|
let min = min - 4.0;
|
||||||
@ -496,7 +498,7 @@ impl StructureInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, wpos: Vec3<i32>, sample: &ColumnSample) -> Option<Block> {
|
fn get(&self, index: IndexRef, wpos: Vec3<i32>, sample: &ColumnSample) -> Option<Block> {
|
||||||
match self.meta {
|
match self.meta {
|
||||||
StructureMeta::Pyramid { height } => {
|
StructureMeta::Pyramid { height } => {
|
||||||
if wpos.z - self.pos.z
|
if wpos.z - self.pos.z
|
||||||
@ -505,7 +507,10 @@ impl StructureInfo {
|
|||||||
.map(|e: i32| (e.abs() / 2) * 2)
|
.map(|e: i32| (e.abs() / 2) * 2)
|
||||||
.reduce_max()
|
.reduce_max()
|
||||||
{
|
{
|
||||||
Some(Block::new(BlockKind::Dense, Rgb::new(203, 170, 146)))
|
Some(Block::new(
|
||||||
|
BlockKind::Dense,
|
||||||
|
index.colors.block.pyramid.into(),
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -521,6 +526,7 @@ impl StructureInfo {
|
|||||||
.ok()
|
.ok()
|
||||||
.and_then(|b| {
|
.and_then(|b| {
|
||||||
block_from_structure(
|
block_from_structure(
|
||||||
|
index,
|
||||||
*b,
|
*b,
|
||||||
block_pos,
|
block_pos,
|
||||||
self.pos.into(),
|
self.pos.into(),
|
||||||
@ -534,6 +540,7 @@ impl StructureInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn block_from_structure(
|
pub fn block_from_structure(
|
||||||
|
index: IndexRef,
|
||||||
sblock: StructureBlock,
|
sblock: StructureBlock,
|
||||||
pos: Vec3<i32>,
|
pos: Vec3<i32>,
|
||||||
structure_pos: Vec2<i32>,
|
structure_pos: Vec2<i32>,
|
||||||
@ -545,119 +552,60 @@ pub fn block_from_structure(
|
|||||||
let lerp = ((field.get(Vec3::from(structure_pos)).rem_euclid(256)) as f32 / 255.0) * 0.85
|
let lerp = ((field.get(Vec3::from(structure_pos)).rem_euclid(256)) as f32 / 255.0) * 0.85
|
||||||
+ ((field.get(pos + std::i32::MAX / 2).rem_euclid(256)) as f32 / 255.0) * 0.15;
|
+ ((field.get(pos + std::i32::MAX / 2).rem_euclid(256)) as f32 / 255.0) * 0.15;
|
||||||
|
|
||||||
let saturate_leaves = |col: Rgb<f32>| {
|
|
||||||
// /*saturate_srgb(col / 255.0, 0.65)*/
|
|
||||||
/* let rgb = srgb_to_linear(col / 255.0);
|
|
||||||
/* let mut xyy = rgb_to_xyy(rgb);
|
|
||||||
xyy.x *= xyy.x;
|
|
||||||
xyy.y *= xyy.y;
|
|
||||||
linear_to_srgb(xyy_to_rgb(xyy).map(|e| e.min(1.0).max(0.0))).map(|e| e * 255.0) */
|
|
||||||
/* let xyz = rgb_to_xyz(rgb);
|
|
||||||
let col_adjusted = if xyz.y == 0.0 {
|
|
||||||
Rgb::zero()
|
|
||||||
} else {
|
|
||||||
rgb / xyz.y
|
|
||||||
};
|
|
||||||
let col = col_adjusted * col_adjusted * xyz.y;
|
|
||||||
linear_to_srgb(col).map(|e| e * 255.0) */
|
|
||||||
/* let mut hsv = rgb_to_hsv(rgb);
|
|
||||||
hsv.y *= hsv.y;
|
|
||||||
linear_to_srgb(hsv_to_rgb(hsv).map(|e| e.min(1.0).max(0.0))).map(|e| e * 255.0) */
|
|
||||||
linear_to_srgb(rgb * rgb).map(|e| e * 255.0) */
|
|
||||||
col
|
|
||||||
};
|
|
||||||
|
|
||||||
match sblock {
|
match sblock {
|
||||||
StructureBlock::None => None,
|
StructureBlock::None => None,
|
||||||
|
StructureBlock::Hollow => Some(Block::empty()),
|
||||||
StructureBlock::Grass => Some(Block::new(
|
StructureBlock::Grass => Some(Block::new(
|
||||||
BlockKind::Normal,
|
BlockKind::Normal,
|
||||||
sample.surface_color.map(|e| (e * 255.0) as u8),
|
sample.surface_color.map(|e| (e * 255.0) as u8),
|
||||||
)),
|
)),
|
||||||
StructureBlock::TemperateLeaves => Some(Block::new(
|
StructureBlock::Normal(color) => {
|
||||||
BlockKind::Leaves,
|
Some(Block::new(BlockKind::Normal, color)).filter(|block| !block.is_empty())
|
||||||
Lerp::lerp(
|
},
|
||||||
saturate_leaves(Rgb::new(0.0, 132.0, 94.0)),
|
// Water / sludge throw away their color bits currently, so we don't set anyway.
|
||||||
saturate_leaves(Rgb::new(142.0, 181.0, 0.0)),
|
StructureBlock::Water => Some(Block::new(BlockKind::Water, Rgb::zero())),
|
||||||
lerp,
|
|
||||||
)
|
|
||||||
.map(|e| e as u8),
|
|
||||||
)),
|
|
||||||
StructureBlock::PineLeaves => Some(Block::new(
|
|
||||||
BlockKind::Leaves,
|
|
||||||
Lerp::lerp(Rgb::new(0.0, 60.0, 50.0), Rgb::new(30.0, 100.0, 10.0), lerp)
|
|
||||||
.map(|e| e as u8),
|
|
||||||
)),
|
|
||||||
StructureBlock::PalmLeavesInner => Some(Block::new(
|
|
||||||
BlockKind::Leaves,
|
|
||||||
Lerp::lerp(
|
|
||||||
Rgb::new(61.0, 166.0, 43.0),
|
|
||||||
Rgb::new(29.0, 130.0, 32.0),
|
|
||||||
lerp,
|
|
||||||
)
|
|
||||||
.map(|e| e as u8),
|
|
||||||
)),
|
|
||||||
StructureBlock::PalmLeavesOuter => Some(Block::new(
|
|
||||||
BlockKind::Leaves,
|
|
||||||
Lerp::lerp(
|
|
||||||
Rgb::new(62.0, 171.0, 38.0),
|
|
||||||
Rgb::new(45.0, 171.0, 65.0),
|
|
||||||
lerp,
|
|
||||||
)
|
|
||||||
.map(|e| e as u8),
|
|
||||||
)),
|
|
||||||
StructureBlock::Water => Some(Block::new(
|
|
||||||
BlockKind::Water,
|
|
||||||
saturate_leaves(Rgb::new(100.0, 150.0, 255.0)).map(|e| e as u8),
|
|
||||||
)),
|
|
||||||
StructureBlock::GreenSludge => Some(Block::new(
|
StructureBlock::GreenSludge => Some(Block::new(
|
||||||
BlockKind::Water,
|
BlockKind::Water,
|
||||||
saturate_leaves(Rgb::new(30.0, 126.0, 23.0)).map(|e| e as u8),
|
// TODO: If/when liquid supports other colors again, revisit this.
|
||||||
)),
|
Rgb::zero(),
|
||||||
StructureBlock::Acacia => Some(Block::new(
|
|
||||||
BlockKind::Leaves,
|
|
||||||
Lerp::lerp(
|
|
||||||
Rgb::new(15.0, 126.0, 50.0),
|
|
||||||
Rgb::new(30.0, 180.0, 10.0),
|
|
||||||
lerp,
|
|
||||||
)
|
|
||||||
.map(|e| e as u8),
|
|
||||||
)),
|
)),
|
||||||
|
// None of these BlockKinds has an orientation, so we just use zero for the other color
|
||||||
|
// bits.
|
||||||
StructureBlock::Fruit => Some(if field.get(pos + structure_pos) % 3 > 0 {
|
StructureBlock::Fruit => Some(if field.get(pos + structure_pos) % 3 > 0 {
|
||||||
Block::empty()
|
Block::empty()
|
||||||
} else {
|
} else {
|
||||||
Block::new(BlockKind::Apple, Rgb::new(1, 1, 1))
|
Block::new(BlockKind::Apple, Rgb::zero())
|
||||||
}),
|
}),
|
||||||
StructureBlock::Coconut => Some(if field.get(pos + structure_pos) % 3 > 0 {
|
StructureBlock::Coconut => Some(if field.get(pos + structure_pos) % 3 > 0 {
|
||||||
Block::empty()
|
Block::empty()
|
||||||
} else {
|
} else {
|
||||||
Block::new(BlockKind::Coconut, Rgb::new(1, 1, 1))
|
Block::new(BlockKind::Coconut, Rgb::zero())
|
||||||
}),
|
}),
|
||||||
StructureBlock::Chest => Some(if structure_seed % 10 < 7 {
|
StructureBlock::Chest => Some(if structure_seed % 10 < 7 {
|
||||||
Block::empty()
|
Block::empty()
|
||||||
} else {
|
} else {
|
||||||
Block::new(BlockKind::Chest, Rgb::new(1, 1, 1))
|
Block::new(BlockKind::Chest, Rgb::zero())
|
||||||
}),
|
}),
|
||||||
StructureBlock::Liana => Some(Block::new(
|
// We interpolate all these BlockKinds as needed.
|
||||||
BlockKind::Liana,
|
StructureBlock::TemperateLeaves
|
||||||
Lerp::lerp(
|
| StructureBlock::PineLeaves
|
||||||
saturate_leaves(Rgb::new(0.0, 125.0, 107.0)),
|
| StructureBlock::PalmLeavesInner
|
||||||
saturate_leaves(Rgb::new(0.0, 155.0, 129.0)),
|
| StructureBlock::PalmLeavesOuter
|
||||||
lerp,
|
| StructureBlock::Acacia
|
||||||
)
|
| StructureBlock::Liana
|
||||||
.map(|e| e as u8),
|
| StructureBlock::Mangrove => sblock
|
||||||
)),
|
.elim_case_pure(&index.colors.block.structure_blocks)
|
||||||
StructureBlock::Mangrove => Some(Block::new(
|
.as_ref()
|
||||||
BlockKind::Leaves,
|
.map(|range| {
|
||||||
Lerp::lerp(
|
Block::new(
|
||||||
saturate_leaves(Rgb::new(32.0, 56.0, 22.0)),
|
BlockKind::Leaves,
|
||||||
saturate_leaves(Rgb::new(57.0, 69.0, 27.0)),
|
Rgb::<f32>::lerp(
|
||||||
lerp,
|
Rgb::<u8>::from(range.start).map(f32::from),
|
||||||
)
|
Rgb::<u8>::from(range.end).map(f32::from),
|
||||||
.map(|e| e as u8),
|
lerp,
|
||||||
)),
|
)
|
||||||
StructureBlock::Hollow => Some(Block::empty()),
|
.map(|e| e as u8),
|
||||||
StructureBlock::Normal(color) => {
|
)
|
||||||
Some(Block::new(BlockKind::Normal, color)).filter(|block| !block.is_empty())
|
}),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
all::ForestKind,
|
all::ForestKind,
|
||||||
column::{ColumnGen, ColumnSample},
|
column::{ColumnGen, ColumnSample},
|
||||||
util::{RandomPerm, Sampler, SmallCache, UnitChooser},
|
util::{RandomPerm, Sampler, SmallCache, UnitChooser},
|
||||||
Index, CONFIG,
|
IndexRef, CONFIG,
|
||||||
};
|
};
|
||||||
use common::terrain::Structure;
|
use common::terrain::Structure;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
@ -20,7 +20,7 @@ pub fn structure_gen<'a>(
|
|||||||
st_pos: Vec2<i32>,
|
st_pos: Vec2<i32>,
|
||||||
st_seed: u32,
|
st_seed: u32,
|
||||||
st_sample: &ColumnSample,
|
st_sample: &ColumnSample,
|
||||||
index: &'a Index,
|
index: IndexRef<'a>,
|
||||||
) -> Option<StructureInfo> {
|
) -> Option<StructureInfo> {
|
||||||
// Assuming it's a tree... figure out when it SHOULDN'T spawn
|
// Assuming it's a tree... figure out when it SHOULDN'T spawn
|
||||||
let random_seed = (st_seed as f64) / (u32::MAX as f64);
|
let random_seed = (st_seed as f64) / (u32::MAX as f64);
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
block::StructureMeta,
|
block::StructureMeta,
|
||||||
sim::{local_cells, Cave, Path, RiverKind, SimChunk, WorldSim},
|
sim::{local_cells, Cave, Path, RiverKind, SimChunk, WorldSim},
|
||||||
util::Sampler,
|
util::Sampler,
|
||||||
Index, CONFIG,
|
IndexRef, CONFIG,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
terrain::{
|
terrain::{
|
||||||
@ -13,6 +13,7 @@ use common::{
|
|||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
};
|
};
|
||||||
use noise::NoiseFn;
|
use noise::NoiseFn;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Reverse,
|
cmp::Reverse,
|
||||||
f32, f64,
|
f32, f64,
|
||||||
@ -25,6 +26,31 @@ pub struct ColumnGen<'a> {
|
|||||||
pub sim: &'a WorldSim,
|
pub sim: &'a WorldSim,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Colors {
|
||||||
|
pub cold_grass: (f32, f32, f32),
|
||||||
|
pub warm_grass: (f32, f32, f32),
|
||||||
|
pub dark_grass: (f32, f32, f32),
|
||||||
|
pub wet_grass: (f32, f32, f32),
|
||||||
|
pub cold_stone: (f32, f32, f32),
|
||||||
|
pub hot_stone: (f32, f32, f32),
|
||||||
|
pub warm_stone: (f32, f32, f32),
|
||||||
|
pub beach_sand: (f32, f32, f32),
|
||||||
|
pub desert_sand: (f32, f32, f32),
|
||||||
|
pub snow: (f32, f32, f32),
|
||||||
|
|
||||||
|
pub stone_col: (u8, u8, u8),
|
||||||
|
|
||||||
|
pub dirt_low: (f32, f32, f32),
|
||||||
|
pub dirt_high: (f32, f32, f32),
|
||||||
|
|
||||||
|
pub snow_high: (f32, f32, f32),
|
||||||
|
pub warm_stone_high: (f32, f32, f32),
|
||||||
|
|
||||||
|
pub grass_high: (f32, f32, f32),
|
||||||
|
pub tropical_high: (f32, f32, f32),
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> ColumnGen<'a> {
|
impl<'a> ColumnGen<'a> {
|
||||||
pub fn new(sim: &'a WorldSim) -> Self { Self { sim } }
|
pub fn new(sim: &'a WorldSim) -> Self { Self { sim } }
|
||||||
|
|
||||||
@ -80,7 +106,7 @@ impl<'a> ColumnGen<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Sampler<'a> for ColumnGen<'a> {
|
impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||||
type Index = (Vec2<i32>, &'a Index);
|
type Index = (Vec2<i32>, IndexRef<'a>);
|
||||||
type Sample = Option<ColumnSample<'a>>;
|
type Sample = Option<ColumnSample<'a>>;
|
||||||
|
|
||||||
#[allow(clippy::float_cmp)] // TODO: Pending review in #587
|
#[allow(clippy::float_cmp)] // TODO: Pending review in #587
|
||||||
@ -765,25 +791,47 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
.add(marble_small.sub(0.5).mul(0.25));
|
.add(marble_small.sub(0.5).mul(0.25));
|
||||||
|
|
||||||
// Colours
|
// Colours
|
||||||
let cold_grass = Rgb::new(0.0, 0.5, 0.25);
|
let Colors {
|
||||||
let warm_grass = Rgb::new(0.4, 0.8, 0.0);
|
cold_grass,
|
||||||
let dark_grass = Rgb::new(0.15, 0.4, 0.1);
|
warm_grass,
|
||||||
let wet_grass = Rgb::new(0.1, 0.8, 0.2);
|
dark_grass,
|
||||||
let cold_stone = Rgb::new(0.57, 0.67, 0.8);
|
wet_grass,
|
||||||
let hot_stone = Rgb::new(0.07, 0.07, 0.06);
|
cold_stone,
|
||||||
let warm_stone = Rgb::new(0.77, 0.77, 0.64);
|
hot_stone,
|
||||||
let beach_sand = Rgb::new(0.8, 0.75, 0.5);
|
warm_stone,
|
||||||
let desert_sand = Rgb::new(0.7, 0.4, 0.25);
|
beach_sand,
|
||||||
let snow = Rgb::new(0.8, 0.85, 1.0);
|
desert_sand,
|
||||||
|
snow,
|
||||||
|
stone_col,
|
||||||
|
dirt_low,
|
||||||
|
dirt_high,
|
||||||
|
snow_high,
|
||||||
|
warm_stone_high,
|
||||||
|
grass_high,
|
||||||
|
tropical_high,
|
||||||
|
} = index.colors.column;
|
||||||
|
|
||||||
let stone_col = Rgb::new(195, 187, 201);
|
let cold_grass = cold_grass.into();
|
||||||
let dirt = Lerp::lerp(
|
let warm_grass = warm_grass.into();
|
||||||
Rgb::new(0.075, 0.07, 0.3),
|
let dark_grass = dark_grass.into();
|
||||||
Rgb::new(0.75, 0.55, 0.1),
|
let wet_grass = wet_grass.into();
|
||||||
marble,
|
let cold_stone = cold_stone.into();
|
||||||
);
|
let hot_stone = hot_stone.into();
|
||||||
let tundra = Lerp::lerp(snow, Rgb::new(0.01, 0.3, 0.0), 0.4 + marble * 0.6);
|
let warm_stone: Rgb<f32> = warm_stone.into();
|
||||||
let dead_tundra = Lerp::lerp(warm_stone, Rgb::new(0.3, 0.12, 0.2), marble);
|
let beach_sand = beach_sand.into();
|
||||||
|
let desert_sand = desert_sand.into();
|
||||||
|
let snow = snow.into();
|
||||||
|
let stone_col = stone_col.into();
|
||||||
|
let dirt_low: Rgb<f32> = dirt_low.into();
|
||||||
|
let dirt_high = dirt_high.into();
|
||||||
|
let snow_high = snow_high.into();
|
||||||
|
let warm_stone_high = warm_stone_high.into();
|
||||||
|
let grass_high = grass_high.into();
|
||||||
|
let tropical_high = tropical_high.into();
|
||||||
|
|
||||||
|
let dirt = Lerp::lerp(dirt_low, dirt_high, marble);
|
||||||
|
let tundra = Lerp::lerp(snow, snow_high, 0.4 + marble * 0.6);
|
||||||
|
let dead_tundra = Lerp::lerp(warm_stone, warm_stone_high, marble);
|
||||||
let cliff = Rgb::lerp(cold_stone, hot_stone, marble);
|
let cliff = Rgb::lerp(cold_stone, hot_stone, marble);
|
||||||
|
|
||||||
let grass = Rgb::lerp(
|
let grass = Rgb::lerp(
|
||||||
@ -799,14 +847,14 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
let tropical = Rgb::lerp(
|
let tropical = Rgb::lerp(
|
||||||
Rgb::lerp(
|
Rgb::lerp(
|
||||||
grass,
|
grass,
|
||||||
Rgb::new(0.15, 0.2, 0.15),
|
grass_high,
|
||||||
marble_small
|
marble_small
|
||||||
.sub(0.5)
|
.sub(0.5)
|
||||||
.mul(0.2)
|
.mul(0.2)
|
||||||
.add(0.75.mul(1.0.sub(humidity)))
|
.add(0.75.mul(1.0.sub(humidity)))
|
||||||
.powf(0.667),
|
.powf(0.667),
|
||||||
),
|
),
|
||||||
Rgb::new(0.87, 0.62, 0.56),
|
tropical_high,
|
||||||
marble.powf(1.5).sub(0.5).mul(4.0),
|
marble.powf(1.5).sub(0.5).mul(4.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,21 +1,104 @@
|
|||||||
use crate::site::Site;
|
use crate::{site::Site, Colors};
|
||||||
use common::store::Store;
|
use common::{
|
||||||
|
assets::{self, watch::ReloadIndicator},
|
||||||
|
store::Store,
|
||||||
|
};
|
||||||
|
use core::ops::Deref;
|
||||||
use noise::{Seedable, SuperSimplex};
|
use noise::{Seedable, SuperSimplex};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
const WORLD_COLORS_MANIFEST: &str = "world.style.colors";
|
||||||
|
|
||||||
pub struct Index {
|
pub struct Index {
|
||||||
pub seed: u32,
|
pub seed: u32,
|
||||||
pub time: f32,
|
pub time: f32,
|
||||||
pub noise: Noise,
|
pub noise: Noise,
|
||||||
pub sites: Store<Site>,
|
pub sites: Store<Site>,
|
||||||
|
indicator: ReloadIndicator,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An owned reference to indexed data.
|
||||||
|
///
|
||||||
|
/// The data are split out so that we can replace the colors without disturbing
|
||||||
|
/// the rest of the index, while also keeping all the adta within a single
|
||||||
|
/// indirection (though possibly not contiguous).
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct IndexOwned {
|
||||||
|
colors: Arc<Colors>,
|
||||||
|
index: Arc<Index>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for IndexOwned {
|
||||||
|
type Target = Index;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.index }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A shared reference to indexed data.
|
||||||
|
///
|
||||||
|
/// This is copyable and can be used from either style of index.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct IndexRef<'a> {
|
||||||
|
pub colors: &'a Colors,
|
||||||
|
pub index: &'a Index,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for IndexRef<'a> {
|
||||||
|
type Target = Index;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.index }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index {
|
impl Index {
|
||||||
pub fn new(seed: u32) -> Self {
|
/// NOTE: Panics if the color manifest cannot be loaded.
|
||||||
|
pub fn new(seed: u32) -> (Self, Arc<Colors>) {
|
||||||
|
let mut indicator = ReloadIndicator::new();
|
||||||
|
let colors = assets::load_watched::<Colors>(WORLD_COLORS_MANIFEST, &mut indicator)
|
||||||
|
.expect("Could not load world colors!");
|
||||||
|
|
||||||
|
(
|
||||||
|
Self {
|
||||||
|
seed,
|
||||||
|
time: 0.0,
|
||||||
|
noise: Noise::new(seed),
|
||||||
|
sites: Store::default(),
|
||||||
|
indicator,
|
||||||
|
},
|
||||||
|
colors,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexOwned {
|
||||||
|
pub fn new(index: Index, colors: Arc<Colors>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
seed,
|
index: Arc::new(index),
|
||||||
time: 0.0,
|
colors,
|
||||||
noise: Noise::new(seed),
|
}
|
||||||
sites: Store::default(),
|
}
|
||||||
|
|
||||||
|
/// NOTE: Callback is called only when colors actually have to be reloaded.
|
||||||
|
/// The server is responsible for making sure that all affected chunks are
|
||||||
|
/// reloaded; a naive approach will just regenerate every chunk on the
|
||||||
|
/// server, but it is possible that eventually we can find a better
|
||||||
|
/// solution.
|
||||||
|
///
|
||||||
|
/// Ideally, this should be called about once per tick.
|
||||||
|
pub fn reload_colors_if_changed<R>(
|
||||||
|
&mut self,
|
||||||
|
reload: impl FnOnce(&mut Self) -> R,
|
||||||
|
) -> Option<R> {
|
||||||
|
self.indicator.reloaded().then(move || {
|
||||||
|
// We know the asset was loaded before, so load_expect should be fine.
|
||||||
|
self.colors = assets::load_expect::<Colors>(WORLD_COLORS_MANIFEST);
|
||||||
|
reload(self)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_index_ref(&self) -> IndexRef {
|
||||||
|
IndexRef {
|
||||||
|
colors: &self.colors,
|
||||||
|
index: &self.index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
column::ColumnSample,
|
column::ColumnSample,
|
||||||
sim::SimChunk,
|
sim::SimChunk,
|
||||||
util::{RandomField, Sampler},
|
util::{RandomField, Sampler},
|
||||||
Index,
|
IndexRef,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets, comp,
|
assets, comp,
|
||||||
@ -13,12 +13,19 @@ use common::{
|
|||||||
};
|
};
|
||||||
use noise::NoiseFn;
|
use noise::NoiseFn;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
f32,
|
f32,
|
||||||
ops::{Mul, Sub},
|
ops::{Mul, Sub},
|
||||||
};
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Colors {
|
||||||
|
pub bridge: (u8, u8, u8),
|
||||||
|
pub stalagtite: (u8, u8, u8),
|
||||||
|
}
|
||||||
|
|
||||||
fn close(x: f32, tgt: f32, falloff: f32) -> f32 {
|
fn close(x: f32, tgt: f32, falloff: f32) -> f32 {
|
||||||
(1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.5)
|
(1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.5)
|
||||||
}
|
}
|
||||||
@ -27,7 +34,7 @@ pub fn apply_scatter_to<'a>(
|
|||||||
wpos2d: Vec2<i32>,
|
wpos2d: Vec2<i32>,
|
||||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||||
index: &Index,
|
index: IndexRef,
|
||||||
chunk: &SimChunk,
|
chunk: &SimChunk,
|
||||||
) {
|
) {
|
||||||
use BlockKind::*;
|
use BlockKind::*;
|
||||||
@ -150,7 +157,7 @@ pub fn apply_paths_to<'a>(
|
|||||||
wpos2d: Vec2<i32>,
|
wpos2d: Vec2<i32>,
|
||||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||||
_index: &Index,
|
index: IndexRef,
|
||||||
) {
|
) {
|
||||||
for y in 0..vol.size_xy().y as i32 {
|
for y in 0..vol.size_xy().y as i32 {
|
||||||
for x in 0..vol.size_xy().x as i32 {
|
for x in 0..vol.size_xy().x as i32 {
|
||||||
@ -213,7 +220,10 @@ pub fn apply_paths_to<'a>(
|
|||||||
let _ = vol.set(
|
let _ = vol.set(
|
||||||
Vec3::new(offs.x, offs.y, surface_z + z),
|
Vec3::new(offs.x, offs.y, surface_z + z),
|
||||||
if bridge_offset >= 2.0 && path_dist >= 3.0 || z < inset - 1 {
|
if bridge_offset >= 2.0 && path_dist >= 3.0 || z < inset - 1 {
|
||||||
Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8))
|
Block::new(
|
||||||
|
BlockKind::Normal,
|
||||||
|
noisy_color(index.colors.layer.bridge.into(), 8),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let path_color = path.surface_color(
|
let path_color = path.surface_color(
|
||||||
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
|
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
|
||||||
@ -238,7 +248,7 @@ pub fn apply_caves_to<'a>(
|
|||||||
wpos2d: Vec2<i32>,
|
wpos2d: Vec2<i32>,
|
||||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||||
index: &Index,
|
index: IndexRef,
|
||||||
) {
|
) {
|
||||||
for y in 0..vol.size_xy().y as i32 {
|
for y in 0..vol.size_xy().y as i32 {
|
||||||
for x in 0..vol.size_xy().x as i32 {
|
for x in 0..vol.size_xy().x as i32 {
|
||||||
@ -297,7 +307,7 @@ pub fn apply_caves_to<'a>(
|
|||||||
for z in cave_roof - stalagtites..cave_roof {
|
for z in cave_roof - stalagtites..cave_roof {
|
||||||
let _ = vol.set(
|
let _ = vol.set(
|
||||||
Vec3::new(offs.x, offs.y, z),
|
Vec3::new(offs.x, offs.y, z),
|
||||||
Block::new(BlockKind::Rock, Rgb::broadcast(200)),
|
Block::new(BlockKind::Rock, index.colors.layer.stalagtite.into()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,7 +335,7 @@ pub fn apply_caves_supplement<'a>(
|
|||||||
wpos2d: Vec2<i32>,
|
wpos2d: Vec2<i32>,
|
||||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||||
vol: &(impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
vol: &(impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||||
index: &Index,
|
index: IndexRef,
|
||||||
supplement: &mut ChunkSupplement,
|
supplement: &mut ChunkSupplement,
|
||||||
) {
|
) {
|
||||||
for y in 0..vol.size_xy().y as i32 {
|
for y in 0..vol.size_xy().y as i32 {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#![allow(clippy::option_map_unit_fn)]
|
#![allow(clippy::option_map_unit_fn)]
|
||||||
#![feature(
|
#![feature(
|
||||||
arbitrary_enum_discriminant,
|
arbitrary_enum_discriminant,
|
||||||
|
bool_to_option,
|
||||||
const_generics,
|
const_generics,
|
||||||
const_panic,
|
const_panic,
|
||||||
label_break_value,
|
label_break_value,
|
||||||
@ -25,6 +26,7 @@ pub mod util;
|
|||||||
pub use crate::config::CONFIG;
|
pub use crate::config::CONFIG;
|
||||||
pub use block::BlockGen;
|
pub use block::BlockGen;
|
||||||
pub use column::ColumnSample;
|
pub use column::ColumnSample;
|
||||||
|
pub use index::{IndexOwned, IndexRef};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
column::ColumnGen,
|
column::ColumnGen,
|
||||||
@ -32,6 +34,7 @@ use crate::{
|
|||||||
util::{Grid, Sampler},
|
util::{Grid, Sampler},
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
|
assets::{self, Asset},
|
||||||
comp::{self, bird_medium, critter, quadruped_low, quadruped_medium, quadruped_small},
|
comp::{self, bird_medium, critter, quadruped_low, quadruped_medium, quadruped_small},
|
||||||
generation::{ChunkSupplement, EntityInfo},
|
generation::{ChunkSupplement, EntityInfo},
|
||||||
msg::server::WorldMapMsg,
|
msg::server::WorldMapMsg,
|
||||||
@ -39,7 +42,8 @@ use common::{
|
|||||||
vol::{ReadVol, RectVolSize, Vox, WriteVol},
|
vol::{ReadVol, RectVolSize, Vox, WriteVol},
|
||||||
};
|
};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::time::Duration;
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{fs::File, io::BufReader, time::Duration};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -50,35 +54,51 @@ pub enum Error {
|
|||||||
pub struct World {
|
pub struct World {
|
||||||
sim: sim::WorldSim,
|
sim: sim::WorldSim,
|
||||||
civs: civ::Civs,
|
civs: civ::Civs,
|
||||||
index: Index,
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Colors {
|
||||||
|
pub deep_stone_color: (u8, u8, u8),
|
||||||
|
pub block: block::Colors,
|
||||||
|
pub column: column::Colors,
|
||||||
|
pub layer: layer::Colors,
|
||||||
|
pub site: site::Colors,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Asset for Colors {
|
||||||
|
const ENDINGS: &'static [&'static str] = &["ron"];
|
||||||
|
|
||||||
|
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
|
||||||
|
ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn generate(seed: u32, opts: sim::WorldOpts) -> Self {
|
pub fn generate(seed: u32, opts: sim::WorldOpts) -> (Self, IndexOwned) {
|
||||||
|
// NOTE: Generating index first in order to quickly fail if the color manifest
|
||||||
|
// is broken.
|
||||||
|
let (mut index, colors) = Index::new(seed);
|
||||||
let mut sim = sim::WorldSim::generate(seed, opts);
|
let mut sim = sim::WorldSim::generate(seed, opts);
|
||||||
let mut index = Index::new(seed);
|
|
||||||
let civs = civ::Civs::generate(seed, &mut sim, &mut index);
|
let civs = civ::Civs::generate(seed, &mut sim, &mut index);
|
||||||
|
|
||||||
sim2::simulate(&mut index, &mut sim);
|
sim2::simulate(&mut index, &mut sim);
|
||||||
|
|
||||||
Self { sim, civs, index }
|
(Self { sim, civs }, IndexOwned::new(index, colors))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sim(&self) -> &sim::WorldSim { &self.sim }
|
pub fn sim(&self) -> &sim::WorldSim { &self.sim }
|
||||||
|
|
||||||
pub fn civs(&self) -> &civ::Civs { &self.civs }
|
pub fn civs(&self) -> &civ::Civs { &self.civs }
|
||||||
|
|
||||||
pub fn index(&self) -> &Index { &self.index }
|
|
||||||
|
|
||||||
pub fn tick(&self, _dt: Duration) {
|
pub fn tick(&self, _dt: Duration) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_map_data(&self) -> WorldMapMsg { self.sim.get_map(&self.index) }
|
pub fn get_map_data(&self, index: IndexRef) -> WorldMapMsg { self.sim.get_map(index) }
|
||||||
|
|
||||||
pub fn sample_columns(
|
pub fn sample_columns(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Sampler<Index = (Vec2<i32>, &Index), Sample = Option<ColumnSample>> + '_ {
|
) -> impl Sampler<Index = (Vec2<i32>, IndexRef), Sample = Option<ColumnSample>> + '_ {
|
||||||
ColumnGen::new(&self.sim)
|
ColumnGen::new(&self.sim)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +107,7 @@ impl World {
|
|||||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||||
pub fn generate_chunk(
|
pub fn generate_chunk(
|
||||||
&self,
|
&self,
|
||||||
|
index: IndexRef,
|
||||||
chunk_pos: Vec2<i32>,
|
chunk_pos: Vec2<i32>,
|
||||||
// TODO: misleading name
|
// TODO: misleading name
|
||||||
mut should_continue: impl FnMut() -> bool,
|
mut should_continue: impl FnMut() -> bool,
|
||||||
@ -97,7 +118,7 @@ impl World {
|
|||||||
let grid_border = 4;
|
let grid_border = 4;
|
||||||
let zcache_grid = Grid::populate_from(
|
let zcache_grid = Grid::populate_from(
|
||||||
TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2,
|
TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2,
|
||||||
|offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, &self.index),
|
|offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, index),
|
||||||
);
|
);
|
||||||
|
|
||||||
let air = Block::empty();
|
let air = Block::empty();
|
||||||
@ -107,9 +128,9 @@ impl World {
|
|||||||
.get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2)
|
.get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2)
|
||||||
.and_then(|zcache| zcache.as_ref())
|
.and_then(|zcache| zcache.as_ref())
|
||||||
.map(|zcache| zcache.sample.stone_col)
|
.map(|zcache| zcache.sample.stone_col)
|
||||||
.unwrap_or(Rgb::new(125, 120, 130)),
|
.unwrap_or(index.colors.deep_stone_color.into()),
|
||||||
);
|
);
|
||||||
let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190));
|
let water = Block::new(BlockKind::Water, Rgb::zero());
|
||||||
|
|
||||||
let _chunk_size2d = TerrainChunkSize::RECT_SIZE;
|
let _chunk_size2d = TerrainChunkSize::RECT_SIZE;
|
||||||
let (base_z, sim_chunk) = match self
|
let (base_z, sim_chunk) = match self
|
||||||
@ -153,7 +174,7 @@ impl World {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (min_z, only_structures_min_z, max_z) =
|
let (min_z, only_structures_min_z, max_z) =
|
||||||
z_cache.get_z_limits(&mut sampler, &self.index);
|
z_cache.get_z_limits(&mut sampler, index);
|
||||||
|
|
||||||
(base_z..min_z as i32).for_each(|z| {
|
(base_z..min_z as i32).for_each(|z| {
|
||||||
let _ = chunk.set(Vec3::new(x, y, z), stone);
|
let _ = chunk.set(Vec3::new(x, y, z), stone);
|
||||||
@ -165,7 +186,7 @@ impl World {
|
|||||||
let only_structures = lpos.z >= only_structures_min_z as i32;
|
let only_structures = lpos.z >= only_structures_min_z as i32;
|
||||||
|
|
||||||
if let Some(block) =
|
if let Some(block) =
|
||||||
sampler.get_with_z_cache(wpos, Some(&z_cache), only_structures, &self.index)
|
sampler.get_with_z_cache(wpos, Some(&z_cache), only_structures, index)
|
||||||
{
|
{
|
||||||
let _ = chunk.set(lpos, block);
|
let _ = chunk.set(lpos, block);
|
||||||
}
|
}
|
||||||
@ -184,13 +205,13 @@ impl World {
|
|||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
// Apply layers (paths, caves, etc.)
|
// Apply layers (paths, caves, etc.)
|
||||||
layer::apply_scatter_to(chunk_wpos2d, sample_get, &mut chunk, &self.index, sim_chunk);
|
layer::apply_scatter_to(chunk_wpos2d, sample_get, &mut chunk, index, sim_chunk);
|
||||||
layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk, &self.index);
|
layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk, index);
|
||||||
layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk, &self.index);
|
layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk, index);
|
||||||
|
|
||||||
// Apply site generation
|
// Apply site generation
|
||||||
sim_chunk.sites.iter().for_each(|site| {
|
sim_chunk.sites.iter().for_each(|site| {
|
||||||
self.index.sites[*site].apply_to(chunk_wpos2d, sample_get, &mut chunk)
|
index.sites[*site].apply_to(index, chunk_wpos2d, sample_get, &mut chunk)
|
||||||
});
|
});
|
||||||
|
|
||||||
let gen_entity_pos = || {
|
let gen_entity_pos = || {
|
||||||
@ -243,18 +264,13 @@ impl World {
|
|||||||
chunk_wpos2d,
|
chunk_wpos2d,
|
||||||
sample_get,
|
sample_get,
|
||||||
&chunk,
|
&chunk,
|
||||||
&self.index,
|
index,
|
||||||
&mut supplement,
|
&mut supplement,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Apply site supplementary information
|
// Apply site supplementary information
|
||||||
sim_chunk.sites.iter().for_each(|site| {
|
sim_chunk.sites.iter().for_each(|site| {
|
||||||
self.index.sites[*site].apply_supplement(
|
index.sites[*site].apply_supplement(&mut rng, chunk_wpos2d, sample_get, &mut supplement)
|
||||||
&mut rng,
|
|
||||||
chunk_wpos2d,
|
|
||||||
sample_get,
|
|
||||||
&mut supplement,
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok((chunk, supplement))
|
Ok((chunk, supplement))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
column::ColumnSample,
|
column::ColumnSample,
|
||||||
sim::{RiverKind, WorldSim},
|
sim::{RiverKind, WorldSim},
|
||||||
Index, CONFIG,
|
IndexRef, CONFIG,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
terrain::{
|
terrain::{
|
||||||
@ -63,10 +63,15 @@ pub fn sample_wpos(config: &MapConfig, sampler: &WorldSim, wpos: Vec2<i32>) -> f
|
|||||||
/// are to be used for some reason, one should pass a custom function to
|
/// are to be used for some reason, one should pass a custom function to
|
||||||
/// generate instead (e.g. one that just looks up the color in a cached
|
/// generate instead (e.g. one that just looks up the color in a cached
|
||||||
/// array).
|
/// array).
|
||||||
|
// NOTE: Deliberately not putting Rgb colors here in the config file; they
|
||||||
|
// aren't hot reloaded anyway, and for various reasons they're probably not a
|
||||||
|
// good idea to update in that way (for example, we currently want water colors
|
||||||
|
// to match voxygen's). Eventually we'll fix these sorts of issues in some
|
||||||
|
// other way.
|
||||||
pub fn sample_pos(
|
pub fn sample_pos(
|
||||||
config: &MapConfig,
|
config: &MapConfig,
|
||||||
sampler: &WorldSim,
|
sampler: &WorldSim,
|
||||||
index: &Index,
|
index: IndexRef,
|
||||||
samples: Option<&[Option<ColumnSample>]>,
|
samples: Option<&[Option<ColumnSample>]>,
|
||||||
pos: Vec2<i32>,
|
pos: Vec2<i32>,
|
||||||
) -> MapSample {
|
) -> MapSample {
|
||||||
|
@ -29,7 +29,7 @@ use crate::{
|
|||||||
column::ColumnGen,
|
column::ColumnGen,
|
||||||
site::Site,
|
site::Site,
|
||||||
util::{seed_expan, FastNoise, RandomField, Sampler, StructureGen2d, LOCALITY, NEIGHBORS},
|
util::{seed_expan, FastNoise, RandomField, Sampler, StructureGen2d, LOCALITY, NEIGHBORS},
|
||||||
Index, CONFIG,
|
IndexRef, CONFIG,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets,
|
assets,
|
||||||
@ -1408,7 +1408,7 @@ impl WorldSim {
|
|||||||
|
|
||||||
/// Draw a map of the world based on chunk information. Returns a buffer of
|
/// Draw a map of the world based on chunk information. Returns a buffer of
|
||||||
/// u32s.
|
/// u32s.
|
||||||
pub fn get_map(&self, index: &Index) -> WorldMapMsg {
|
pub fn get_map(&self, index: IndexRef) -> WorldMapMsg {
|
||||||
let mut map_config = MapConfig::orthographic(
|
let mut map_config = MapConfig::orthographic(
|
||||||
self.map_size_lg(),
|
self.map_size_lg(),
|
||||||
core::ops::RangeInclusive::new(CONFIG.sea_level, CONFIG.sea_level + self.max_height),
|
core::ops::RangeInclusive::new(CONFIG.sea_level, CONFIG.sea_level + self.max_height),
|
||||||
|
@ -3,9 +3,10 @@ use crate::{
|
|||||||
column::ColumnSample,
|
column::ColumnSample,
|
||||||
sim::WorldSim,
|
sim::WorldSim,
|
||||||
site::settlement::building::{
|
site::settlement::building::{
|
||||||
archetype::keep::{Attr, Keep as KeepArchetype},
|
archetype::keep::{Attr, FlagColor, Keep as KeepArchetype, StoneColor},
|
||||||
Archetype, Ori,
|
Archetype, Ori,
|
||||||
},
|
},
|
||||||
|
IndexRef,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
generation::ChunkSupplement,
|
generation::ChunkSupplement,
|
||||||
@ -14,6 +15,7 @@ use common::{
|
|||||||
};
|
};
|
||||||
use core::f32;
|
use core::f32;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
struct Keep {
|
struct Keep {
|
||||||
@ -47,6 +49,9 @@ pub struct GenCtx<'a, R: Rng> {
|
|||||||
rng: &'a mut R,
|
rng: &'a mut R,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Colors;
|
||||||
|
|
||||||
impl Castle {
|
impl Castle {
|
||||||
#[allow(clippy::let_and_return)] // TODO: Pending review in #587
|
#[allow(clippy::let_and_return)] // TODO: Pending review in #587
|
||||||
pub fn generate(wpos: Vec2<i32>, sim: Option<&mut WorldSim>, rng: &mut impl Rng) -> Self {
|
pub fn generate(wpos: Vec2<i32>, sim: Option<&mut WorldSim>, rng: &mut impl Rng) -> Self {
|
||||||
@ -167,6 +172,7 @@ impl Castle {
|
|||||||
|
|
||||||
pub fn apply_to<'a>(
|
pub fn apply_to<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
index: IndexRef,
|
||||||
wpos2d: Vec2<i32>,
|
wpos2d: Vec2<i32>,
|
||||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||||
@ -273,14 +279,14 @@ impl Castle {
|
|||||||
|
|
||||||
let keep_archetype = KeepArchetype {
|
let keep_archetype = KeepArchetype {
|
||||||
flag_color: if self.evil {
|
flag_color: if self.evil {
|
||||||
Rgb::new(80, 10, 130)
|
FlagColor::Evil
|
||||||
} else {
|
} else {
|
||||||
Rgb::new(200, 80, 40)
|
FlagColor::Good
|
||||||
},
|
},
|
||||||
stone_color: if self.evil {
|
stone_color: if self.evil {
|
||||||
Rgb::new(65, 60, 55)
|
StoneColor::Evil
|
||||||
} else {
|
} else {
|
||||||
Rgb::new(100, 100, 110)
|
StoneColor::Good
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -294,6 +300,7 @@ impl Castle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut mask = keep_archetype.draw(
|
let mut mask = keep_archetype.draw(
|
||||||
|
index,
|
||||||
Vec3::from(wall_rpos) + Vec3::unit_z() * wpos.z - wall_alt,
|
Vec3::from(wall_rpos) + Vec3::unit_z() * wpos.z - wall_alt,
|
||||||
wall_dist,
|
wall_dist,
|
||||||
border_pos,
|
border_pos,
|
||||||
@ -323,6 +330,7 @@ impl Castle {
|
|||||||
|
|
||||||
let border_pos = (tower_wpos - wpos).xy().map(|e| e.abs());
|
let border_pos = (tower_wpos - wpos).xy().map(|e| e.abs());
|
||||||
mask = mask.resolve_with(keep_archetype.draw(
|
mask = mask.resolve_with(keep_archetype.draw(
|
||||||
|
index,
|
||||||
if (tower_wpos.x - wpos.x).abs() < (tower_wpos.y - wpos.y).abs() {
|
if (tower_wpos.x - wpos.x).abs() < (tower_wpos.y - wpos.y).abs() {
|
||||||
wpos - tower_wpos
|
wpos - tower_wpos
|
||||||
} else {
|
} else {
|
||||||
@ -364,6 +372,7 @@ impl Castle {
|
|||||||
|
|
||||||
let border_pos = (keep_wpos - wpos).xy().map(|e| e.abs());
|
let border_pos = (keep_wpos - wpos).xy().map(|e| e.abs());
|
||||||
mask = mask.resolve_with(keep_archetype.draw(
|
mask = mask.resolve_with(keep_archetype.draw(
|
||||||
|
index,
|
||||||
if (keep_wpos.x - wpos.x).abs() < (keep_wpos.y - wpos.y).abs() {
|
if (keep_wpos.x - wpos.x).abs() < (keep_wpos.y - wpos.y).abs() {
|
||||||
wpos - keep_wpos
|
wpos - keep_wpos
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,6 +5,7 @@ use crate::{
|
|||||||
sim::WorldSim,
|
sim::WorldSim,
|
||||||
site::BlockMask,
|
site::BlockMask,
|
||||||
util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS},
|
util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS},
|
||||||
|
IndexRef,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets,
|
assets,
|
||||||
@ -20,6 +21,7 @@ use core::{f32, hash::BuildHasherDefault};
|
|||||||
use fxhash::FxHasher64;
|
use fxhash::FxHasher64;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -37,6 +39,11 @@ pub struct GenCtx<'a, R: Rng> {
|
|||||||
rng: &'a mut R,
|
rng: &'a mut R,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Colors {
|
||||||
|
pub stone: (u8, u8, u8),
|
||||||
|
}
|
||||||
|
|
||||||
const ALT_OFFSET: i32 = -2;
|
const ALT_OFFSET: i32 = -2;
|
||||||
|
|
||||||
const LEVELS: usize = 5;
|
const LEVELS: usize = 5;
|
||||||
@ -80,6 +87,7 @@ impl Dungeon {
|
|||||||
|
|
||||||
pub fn apply_to<'a>(
|
pub fn apply_to<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
index: IndexRef,
|
||||||
wpos2d: Vec2<i32>,
|
wpos2d: Vec2<i32>,
|
||||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||||
@ -112,7 +120,14 @@ impl Dungeon {
|
|||||||
.ok()
|
.ok()
|
||||||
.copied()
|
.copied()
|
||||||
.map(|sb| {
|
.map(|sb| {
|
||||||
block_from_structure(sb, spos, self.origin, self.seed, col_sample)
|
block_from_structure(
|
||||||
|
index,
|
||||||
|
sb,
|
||||||
|
spos,
|
||||||
|
self.origin,
|
||||||
|
self.seed,
|
||||||
|
col_sample,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or(None)
|
.unwrap_or(None)
|
||||||
{
|
{
|
||||||
@ -125,7 +140,7 @@ impl Dungeon {
|
|||||||
for floor in &self.floors {
|
for floor in &self.floors {
|
||||||
z -= floor.total_depth();
|
z -= floor.total_depth();
|
||||||
|
|
||||||
let mut sampler = floor.col_sampler(rpos, z);
|
let mut sampler = floor.col_sampler(index, rpos, z);
|
||||||
|
|
||||||
for rz in 0..floor.total_depth() {
|
for rz in 0..floor.total_depth() {
|
||||||
if let Some(block) = sampler(rz).finish() {
|
if let Some(block) = sampler(rz).finish() {
|
||||||
@ -574,16 +589,29 @@ impl Floor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587
|
#[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587
|
||||||
pub fn col_sampler(&self, pos: Vec2<i32>, floor_z: i32) -> impl FnMut(i32) -> BlockMask + '_ {
|
pub fn col_sampler<'a>(
|
||||||
|
&'a self,
|
||||||
|
index: IndexRef<'a>,
|
||||||
|
pos: Vec2<i32>,
|
||||||
|
floor_z: i32,
|
||||||
|
) -> impl FnMut(i32) -> BlockMask + 'a {
|
||||||
let rpos = pos - self.tile_offset * TILE_SIZE;
|
let rpos = pos - self.tile_offset * TILE_SIZE;
|
||||||
let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE));
|
let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE));
|
||||||
let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2;
|
let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2;
|
||||||
let rtile_pos = rpos - tile_center;
|
let rtile_pos = rpos - tile_center;
|
||||||
|
|
||||||
|
let colors = &index.colors.site.dungeon;
|
||||||
|
|
||||||
let empty = BlockMask::new(Block::empty(), 1);
|
let empty = BlockMask::new(Block::empty(), 1);
|
||||||
|
|
||||||
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
||||||
let stone = BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(150, 150, 175)), 5);
|
let stone = BlockMask::new(
|
||||||
|
Block::new(
|
||||||
|
BlockKind::Normal,
|
||||||
|
/* Rgb::new(150, 150, 175) */ colors.stone.into(),
|
||||||
|
),
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
|
||||||
if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) {
|
if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) {
|
||||||
stone
|
stone
|
||||||
|
@ -10,15 +10,23 @@ pub use self::{
|
|||||||
settlement::Settlement,
|
settlement::Settlement,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::column::ColumnSample;
|
use crate::{column::ColumnSample, IndexRef};
|
||||||
use common::{
|
use common::{
|
||||||
generation::ChunkSupplement,
|
generation::ChunkSupplement,
|
||||||
terrain::Block,
|
terrain::Block,
|
||||||
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
||||||
};
|
};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Colors {
|
||||||
|
pub castle: castle::Colors,
|
||||||
|
pub dungeon: dungeon::Colors,
|
||||||
|
pub settlement: settlement::Colors,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SpawnRules {
|
pub struct SpawnRules {
|
||||||
pub trees: bool,
|
pub trees: bool,
|
||||||
}
|
}
|
||||||
@ -86,14 +94,15 @@ impl Site {
|
|||||||
|
|
||||||
pub fn apply_to<'a>(
|
pub fn apply_to<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
index: IndexRef,
|
||||||
wpos2d: Vec2<i32>,
|
wpos2d: Vec2<i32>,
|
||||||
get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||||
) {
|
) {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
SiteKind::Settlement(s) => s.apply_to(wpos2d, get_column, vol),
|
SiteKind::Settlement(s) => s.apply_to(index, wpos2d, get_column, vol),
|
||||||
SiteKind::Dungeon(d) => d.apply_to(wpos2d, get_column, vol),
|
SiteKind::Dungeon(d) => d.apply_to(index, wpos2d, get_column, vol),
|
||||||
SiteKind::Castle(c) => c.apply_to(wpos2d, get_column, vol),
|
SiteKind::Castle(c) => c.apply_to(index, wpos2d, get_column, vol),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,64 +4,103 @@ use super::{super::skeleton::*, Archetype};
|
|||||||
use crate::{
|
use crate::{
|
||||||
site::BlockMask,
|
site::BlockMask,
|
||||||
util::{RandomField, Sampler},
|
util::{RandomField, Sampler},
|
||||||
|
IndexRef,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
|
make_case_elim,
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind},
|
||||||
vol::Vox,
|
vol::Vox,
|
||||||
};
|
};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub struct ColorTheme {
|
#[derive(Deserialize, Serialize)]
|
||||||
roof: Rgb<u8>,
|
pub struct Colors {
|
||||||
wall: Rgb<u8>,
|
pub foundation: (u8, u8, u8),
|
||||||
support: Rgb<u8>,
|
pub floor: (u8, u8, u8),
|
||||||
|
pub roof: roof_color::PureCases<(u8, u8, u8)>,
|
||||||
|
pub wall: wall_color::PureCases<(u8, u8, u8)>,
|
||||||
|
pub support: support_color::PureCases<(u8, u8, u8)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ROOF_COLORS: &[Rgb<u8>] = &[
|
pub struct ColorTheme {
|
||||||
// Rgb::new(0x1D, 0x4D, 0x45),
|
roof: RoofColor,
|
||||||
// Rgb::new(0xB3, 0x7D, 0x60),
|
wall: WallColor,
|
||||||
// Rgb::new(0xAC, 0x5D, 0x26),
|
support: SupportColor,
|
||||||
// Rgb::new(0x32, 0x46, 0x6B),
|
}
|
||||||
// Rgb::new(0x2B, 0x19, 0x0F),
|
|
||||||
// Rgb::new(0x93, 0x78, 0x51),
|
make_case_elim!(
|
||||||
// Rgb::new(0x92, 0x57, 0x24),
|
roof_color,
|
||||||
// Rgb::new(0x4A, 0x4E, 0x4E),
|
#[repr(u32)]
|
||||||
// Rgb::new(0x2F, 0x32, 0x47),
|
#[derive(Clone, Copy)]
|
||||||
// Rgb::new(0x8F, 0x35, 0x43),
|
pub enum RoofColor {
|
||||||
// Rgb::new(0x6D, 0x1E, 0x3A),
|
Roof1 = 0,
|
||||||
// Rgb::new(0x6D, 0xA7, 0x80),
|
Roof2 = 1,
|
||||||
// Rgb::new(0x4F, 0xA0, 0x95),
|
Roof3 = 2,
|
||||||
// Rgb::new(0xE2, 0xB9, 0x99),
|
Roof4 = 3,
|
||||||
// Rgb::new(0x7A, 0x30, 0x22),
|
Roof5 = 4,
|
||||||
// Rgb::new(0x4A, 0x06, 0x08),
|
Roof6 = 5,
|
||||||
// Rgb::new(0x8E, 0xB4, 0x57),
|
Roof7 = 6,
|
||||||
Rgb::new(0x99, 0x5E, 0x54),
|
}
|
||||||
Rgb::new(0x43, 0x63, 0x64),
|
);
|
||||||
Rgb::new(0x76, 0x6D, 0x68),
|
|
||||||
Rgb::new(0x7B, 0x41, 0x61),
|
make_case_elim!(
|
||||||
Rgb::new(0x52, 0x20, 0x20),
|
wall_color,
|
||||||
Rgb::new(0x1A, 0x4A, 0x59),
|
#[repr(u32)]
|
||||||
Rgb::new(0xCC, 0x76, 0x4E),
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum WallColor {
|
||||||
|
Wall1 = 0,
|
||||||
|
Wall2 = 1,
|
||||||
|
Wall3 = 2,
|
||||||
|
Wall4 = 3,
|
||||||
|
Wall5 = 4,
|
||||||
|
Wall6 = 5,
|
||||||
|
Wall7 = 6,
|
||||||
|
Wall8 = 7,
|
||||||
|
Wall9 = 8,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
make_case_elim!(
|
||||||
|
support_color,
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum SupportColor {
|
||||||
|
Support1 = 0,
|
||||||
|
Support2 = 1,
|
||||||
|
Support3 = 2,
|
||||||
|
Support4 = 3,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const ROOF_COLORS: [RoofColor; roof_color::NUM_VARIANTS] = [
|
||||||
|
RoofColor::Roof1,
|
||||||
|
RoofColor::Roof2,
|
||||||
|
RoofColor::Roof3,
|
||||||
|
RoofColor::Roof4,
|
||||||
|
RoofColor::Roof4,
|
||||||
|
RoofColor::Roof6,
|
||||||
|
RoofColor::Roof7,
|
||||||
];
|
];
|
||||||
|
|
||||||
const WALL_COLORS: &[Rgb<u8>] = &[
|
const WALL_COLORS: [WallColor; wall_color::NUM_VARIANTS] = [
|
||||||
Rgb::new(200, 180, 150),
|
WallColor::Wall1,
|
||||||
Rgb::new(0xB8, 0xB4, 0xA4),
|
WallColor::Wall2,
|
||||||
Rgb::new(0x76, 0x6D, 0x68),
|
WallColor::Wall3,
|
||||||
Rgb::new(0xF3, 0xC9, 0x8F),
|
WallColor::Wall4,
|
||||||
Rgb::new(0xD3, 0xB7, 0x99),
|
WallColor::Wall5,
|
||||||
Rgb::new(0xE1, 0xAB, 0x91),
|
WallColor::Wall6,
|
||||||
Rgb::new(0x82, 0x57, 0x4C),
|
WallColor::Wall7,
|
||||||
Rgb::new(0xB9, 0x96, 0x77),
|
WallColor::Wall8,
|
||||||
Rgb::new(0xAE, 0x8D, 0x9C),
|
WallColor::Wall9,
|
||||||
];
|
];
|
||||||
|
|
||||||
const SUPPORT_COLORS: &[Rgb<u8>] = &[
|
const SUPPORT_COLORS: [SupportColor; support_color::NUM_VARIANTS] = [
|
||||||
Rgb::new(60, 45, 30),
|
SupportColor::Support1,
|
||||||
Rgb::new(0x65, 0x55, 0x56),
|
SupportColor::Support2,
|
||||||
Rgb::new(0x53, 0x33, 0x13),
|
SupportColor::Support3,
|
||||||
Rgb::new(0x58, 0x42, 0x33),
|
SupportColor::Support4,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub struct House {
|
pub struct House {
|
||||||
@ -210,6 +249,7 @@ impl Archetype for House {
|
|||||||
#[allow(clippy::int_plus_one)] // TODO: Pending review in #587
|
#[allow(clippy::int_plus_one)] // TODO: Pending review in #587
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
|
index: IndexRef,
|
||||||
_pos: Vec3<i32>,
|
_pos: Vec3<i32>,
|
||||||
dist: i32,
|
dist: i32,
|
||||||
bound_offset: Vec2<i32>,
|
bound_offset: Vec2<i32>,
|
||||||
@ -220,6 +260,11 @@ impl Archetype for House {
|
|||||||
_len: i32,
|
_len: i32,
|
||||||
attr: &Self::Attr,
|
attr: &Self::Attr,
|
||||||
) -> BlockMask {
|
) -> BlockMask {
|
||||||
|
let colors = &index.colors.site.settlement.building.archetype.house;
|
||||||
|
let roof_color = *self.colors.roof.elim_case_pure(&colors.roof);
|
||||||
|
let wall_color = *self.colors.wall.elim_case_pure(&colors.wall);
|
||||||
|
let support_color = *self.colors.support.elim_case_pure(&colors.support);
|
||||||
|
|
||||||
let profile = Vec2::new(bound_offset.x, z);
|
let profile = Vec2::new(bound_offset.x, z);
|
||||||
|
|
||||||
let make_meta = |ori| {
|
let make_meta = |ori| {
|
||||||
@ -240,6 +285,7 @@ impl Archetype for House {
|
|||||||
BlockMask::new(
|
BlockMask::new(
|
||||||
Block::new(
|
Block::new(
|
||||||
BlockKind::Normal,
|
BlockKind::Normal,
|
||||||
|
// TODO: Clarify exactly how this affects the color.
|
||||||
Rgb::new(r, g, b)
|
Rgb::new(r, g, b)
|
||||||
.map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8)),
|
.map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8)),
|
||||||
),
|
),
|
||||||
@ -253,11 +299,11 @@ impl Archetype for House {
|
|||||||
let foundation_layer = internal_layer + 1;
|
let foundation_layer = internal_layer + 1;
|
||||||
let floor_layer = foundation_layer + 1;
|
let floor_layer = foundation_layer + 1;
|
||||||
|
|
||||||
let foundation = make_block((100, 100, 100)).with_priority(foundation_layer);
|
let foundation = make_block(colors.foundation).with_priority(foundation_layer);
|
||||||
let log = make_block(self.colors.support.into_tuple());
|
let log = make_block(support_color);
|
||||||
let floor = make_block((100, 75, 50));
|
let floor = make_block(colors.floor);
|
||||||
let wall = make_block(self.colors.wall.into_tuple()).with_priority(facade_layer);
|
let wall = make_block(wall_color).with_priority(facade_layer);
|
||||||
let roof = make_block(self.colors.roof.into_tuple()).with_priority(facade_layer - 1);
|
let roof = make_block(roof_color).with_priority(facade_layer - 1);
|
||||||
let empty = BlockMask::nothing();
|
let empty = BlockMask::nothing();
|
||||||
let internal = BlockMask::new(Block::empty(), internal_layer);
|
let internal = BlockMask::new(Block::empty(), internal_layer);
|
||||||
let end_window = BlockMask::new(
|
let end_window = BlockMask::new(
|
||||||
|
@ -2,17 +2,29 @@ use super::{super::skeleton::*, Archetype};
|
|||||||
use crate::{
|
use crate::{
|
||||||
site::BlockMask,
|
site::BlockMask,
|
||||||
util::{RandomField, Sampler},
|
util::{RandomField, Sampler},
|
||||||
|
IndexRef,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
|
make_case_elim,
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind},
|
||||||
vol::Vox,
|
vol::Vox,
|
||||||
};
|
};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Colors {
|
||||||
|
pub brick_base: (u8, u8, u8),
|
||||||
|
pub floor_base: (u8, u8, u8),
|
||||||
|
pub pole: (u8, u8, u8),
|
||||||
|
pub flag: flag_color::PureCases<(u8, u8, u8)>,
|
||||||
|
pub stone: stone_color::PureCases<(u8, u8, u8)>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Keep {
|
pub struct Keep {
|
||||||
pub flag_color: Rgb<u8>,
|
pub flag_color: FlagColor,
|
||||||
pub stone_color: Rgb<u8>,
|
pub stone_color: StoneColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Attr {
|
pub struct Attr {
|
||||||
@ -24,6 +36,24 @@ pub struct Attr {
|
|||||||
pub has_doors: bool,
|
pub has_doors: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
make_case_elim!(
|
||||||
|
flag_color,
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum FlagColor {
|
||||||
|
Good = 0,
|
||||||
|
Evil = 1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
make_case_elim!(
|
||||||
|
stone_color,
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum StoneColor {
|
||||||
|
Good = 0,
|
||||||
|
Evil = 1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
impl Archetype for Keep {
|
impl Archetype for Keep {
|
||||||
type Attr = Attr;
|
type Attr = Attr;
|
||||||
|
|
||||||
@ -71,8 +101,8 @@ impl Archetype for Keep {
|
|||||||
|
|
||||||
(
|
(
|
||||||
Self {
|
Self {
|
||||||
flag_color: Rgb::new(200, 80, 40),
|
flag_color: FlagColor::Good,
|
||||||
stone_color: Rgb::new(100, 100, 110),
|
stone_color: StoneColor::Good,
|
||||||
},
|
},
|
||||||
skel,
|
skel,
|
||||||
)
|
)
|
||||||
@ -81,6 +111,7 @@ impl Archetype for Keep {
|
|||||||
#[allow(clippy::if_same_then_else)] // TODO: Pending review in #587
|
#[allow(clippy::if_same_then_else)] // TODO: Pending review in #587
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
|
index: IndexRef,
|
||||||
pos: Vec3<i32>,
|
pos: Vec3<i32>,
|
||||||
_dist: i32,
|
_dist: i32,
|
||||||
bound_offset: Vec2<i32>,
|
bound_offset: Vec2<i32>,
|
||||||
@ -91,6 +122,11 @@ impl Archetype for Keep {
|
|||||||
_len: i32,
|
_len: i32,
|
||||||
attr: &Self::Attr,
|
attr: &Self::Attr,
|
||||||
) -> BlockMask {
|
) -> BlockMask {
|
||||||
|
let dungeon_stone = index.colors.site.dungeon.stone;
|
||||||
|
let colors = &index.colors.site.settlement.building.archetype.keep;
|
||||||
|
let flag_color = self.flag_color.elim_case_pure(&colors.flag);
|
||||||
|
let stone_color = self.stone_color.elim_case_pure(&colors.stone);
|
||||||
|
|
||||||
let profile = Vec2::new(bound_offset.x, z);
|
let profile = Vec2::new(bound_offset.x, z);
|
||||||
|
|
||||||
let weak_layer = 1;
|
let weak_layer = 1;
|
||||||
@ -118,30 +154,35 @@ impl Archetype for Keep {
|
|||||||
|
|
||||||
let brick_tex_pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1);
|
let brick_tex_pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1);
|
||||||
let brick_tex = RandomField::new(0).get(brick_tex_pos) as u8 % 24;
|
let brick_tex = RandomField::new(0).get(brick_tex_pos) as u8 % 24;
|
||||||
let foundation = make_block(80 + brick_tex, 80 + brick_tex, 80 + brick_tex);
|
let foundation = make_block(
|
||||||
|
colors.brick_base.0 + brick_tex,
|
||||||
|
colors.brick_base.1 + brick_tex,
|
||||||
|
colors.brick_base.2 + brick_tex,
|
||||||
|
);
|
||||||
let wall = make_block(
|
let wall = make_block(
|
||||||
self.stone_color.r + brick_tex,
|
stone_color.0 + brick_tex,
|
||||||
self.stone_color.g + brick_tex,
|
stone_color.1 + brick_tex,
|
||||||
self.stone_color.b + brick_tex,
|
stone_color.2 + brick_tex,
|
||||||
);
|
);
|
||||||
let window = BlockMask::new(
|
let window = BlockMask::new(
|
||||||
Block::new(BlockKind::Window1, make_meta(ori.flip())),
|
Block::new(BlockKind::Window1, make_meta(ori.flip())),
|
||||||
normal_layer,
|
normal_layer,
|
||||||
);
|
);
|
||||||
let floor = make_block(
|
let floor = make_block(
|
||||||
80 + (pos.y.abs() % 2) as u8 * 15,
|
colors.floor_base.0 + (pos.y.abs() % 2) as u8 * 15,
|
||||||
60 + (pos.y.abs() % 2) as u8 * 15,
|
colors.floor_base.1 + (pos.y.abs() % 2) as u8 * 15,
|
||||||
10 + (pos.y.abs() % 2) as u8 * 15,
|
colors.floor_base.2 + (pos.y.abs() % 2) as u8 * 15,
|
||||||
)
|
)
|
||||||
.with_priority(important_layer);
|
.with_priority(important_layer);
|
||||||
let pole = make_block(90, 70, 50).with_priority(important_layer);
|
let pole =
|
||||||
let flag = make_block(self.flag_color.r, self.flag_color.g, self.flag_color.b)
|
make_block(colors.pole.0, colors.pole.1, colors.pole.2).with_priority(important_layer);
|
||||||
.with_priority(important_layer);
|
let flag =
|
||||||
|
make_block(flag_color.0, flag_color.1, flag_color.2).with_priority(important_layer);
|
||||||
let internal = BlockMask::new(Block::empty(), internal_layer);
|
let internal = BlockMask::new(Block::empty(), internal_layer);
|
||||||
let empty = BlockMask::nothing();
|
let empty = BlockMask::nothing();
|
||||||
|
|
||||||
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
||||||
let stone = BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(150, 150, 175)), 5);
|
let stone = BlockMask::new(Block::new(BlockKind::Normal, dungeon_stone.into()), 5);
|
||||||
|
|
||||||
if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) {
|
if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) {
|
||||||
stone
|
stone
|
||||||
|
@ -2,10 +2,17 @@ pub mod house;
|
|||||||
pub mod keep;
|
pub mod keep;
|
||||||
|
|
||||||
use super::skeleton::*;
|
use super::skeleton::*;
|
||||||
use crate::site::BlockMask;
|
use crate::{site::BlockMask, IndexRef};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Colors {
|
||||||
|
pub house: house::Colors,
|
||||||
|
pub keep: keep::Colors,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Archetype {
|
pub trait Archetype {
|
||||||
type Attr;
|
type Attr;
|
||||||
|
|
||||||
@ -16,6 +23,7 @@ pub trait Archetype {
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
|
index: IndexRef,
|
||||||
pos: Vec3<i32>,
|
pos: Vec3<i32>,
|
||||||
dist: i32,
|
dist: i32,
|
||||||
bound_offset: Vec2<i32>,
|
bound_offset: Vec2<i32>,
|
||||||
|
@ -7,10 +7,17 @@ pub use self::{
|
|||||||
skeleton::*,
|
skeleton::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::IndexRef;
|
||||||
use common::terrain::Block;
|
use common::terrain::Block;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Colors {
|
||||||
|
pub archetype: archetype::Colors,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Building<A: Archetype> {
|
pub struct Building<A: Archetype> {
|
||||||
skel: Skeleton<A::Attr>,
|
skel: Skeleton<A::Attr>,
|
||||||
archetype: A,
|
archetype: A,
|
||||||
@ -46,13 +53,14 @@ impl<A: Archetype> Building<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample(&self, pos: Vec3<i32>) -> Option<Block> {
|
pub fn sample(&self, index: IndexRef, pos: Vec3<i32>) -> Option<Block> {
|
||||||
let rpos = pos - self.origin;
|
let rpos = pos - self.origin;
|
||||||
self.skel
|
self.skel
|
||||||
.sample_closest(
|
.sample_closest(
|
||||||
rpos,
|
rpos,
|
||||||
|pos, dist, bound_offset, center_offset, ori, branch| {
|
|pos, dist, bound_offset, center_offset, ori, branch| {
|
||||||
self.archetype.draw(
|
self.archetype.draw(
|
||||||
|
index,
|
||||||
pos,
|
pos,
|
||||||
dist,
|
dist,
|
||||||
bound_offset,
|
bound_offset,
|
||||||
|
@ -10,6 +10,7 @@ use crate::{
|
|||||||
column::ColumnSample,
|
column::ColumnSample,
|
||||||
sim::WorldSim,
|
sim::WorldSim,
|
||||||
util::{RandomField, Sampler, StructureGen2d},
|
util::{RandomField, Sampler, StructureGen2d},
|
||||||
|
IndexRef,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets,
|
assets,
|
||||||
@ -25,9 +26,30 @@ use common::{
|
|||||||
use fxhash::FxHasher64;
|
use fxhash::FxHasher64;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::VecDeque, f32, hash::BuildHasherDefault};
|
use std::{collections::VecDeque, f32, hash::BuildHasherDefault};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Colors {
|
||||||
|
pub building: building::Colors,
|
||||||
|
|
||||||
|
pub plot_town_path: (u8, u8, u8),
|
||||||
|
|
||||||
|
pub plot_field_dirt: (u8, u8, u8),
|
||||||
|
pub plot_field_mound: (u8, u8, u8),
|
||||||
|
|
||||||
|
pub wall_low: (u8, u8, u8),
|
||||||
|
pub wall_high: (u8, u8, u8),
|
||||||
|
|
||||||
|
pub tower_color: (u8, u8, u8),
|
||||||
|
|
||||||
|
pub plot_dirt: (u8, u8, u8),
|
||||||
|
pub plot_grass: (u8, u8, u8),
|
||||||
|
pub plot_water: (u8, u8, u8),
|
||||||
|
pub plot_town: (u8, u8, u8),
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn gradient(line: [Vec2<f32>; 2]) -> f32 {
|
pub fn gradient(line: [Vec2<f32>; 2]) -> f32 {
|
||||||
let r = (line[0].y - line[1].y) / (line[0].x - line[1].x);
|
let r = (line[0].y - line[1].y) / (line[0].x - line[1].x);
|
||||||
@ -109,10 +131,10 @@ impl Structure {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample(&self, rpos: Vec3<i32>) -> Option<Block> {
|
pub fn sample(&self, index: IndexRef, rpos: Vec3<i32>) -> Option<Block> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
StructureKind::House(house) => house.sample(rpos),
|
StructureKind::House(house) => house.sample(index, rpos),
|
||||||
StructureKind::Keep(keep) => keep.sample(rpos),
|
StructureKind::Keep(keep) => keep.sample(index, rpos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -527,10 +549,13 @@ impl Settlement {
|
|||||||
#[allow(clippy::modulo_one)] // TODO: Pending review in #587
|
#[allow(clippy::modulo_one)] // TODO: Pending review in #587
|
||||||
pub fn apply_to<'a>(
|
pub fn apply_to<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
index: IndexRef,
|
||||||
wpos2d: Vec2<i32>,
|
wpos2d: Vec2<i32>,
|
||||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||||
) {
|
) {
|
||||||
|
let colors = &index.colors.site.settlement;
|
||||||
|
|
||||||
for y in 0..vol.size_xy().y as i32 {
|
for y in 0..vol.size_xy().y as i32 {
|
||||||
for x in 0..vol.size_xy().x as i32 {
|
for x in 0..vol.size_xy().x as i32 {
|
||||||
let offs = Vec2::new(x, y);
|
let offs = Vec2::new(x, y);
|
||||||
@ -586,47 +611,6 @@ impl Settlement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paths
|
|
||||||
// if let Some((WayKind::Path, dist, nearest)) = sample.way {
|
|
||||||
// let inset = -1;
|
|
||||||
|
|
||||||
// // Try to use the column at the centre of the path for sampling to make
|
|
||||||
// them // flatter
|
|
||||||
// let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos))
|
|
||||||
// .unwrap_or(col_sample);
|
|
||||||
// let (bridge_offset, depth) = if let Some(water_dist) = col.water_dist {
|
|
||||||
// (
|
|
||||||
// ((water_dist.max(0.0) * 0.2).min(f32::consts::PI).cos() + 1.0) *
|
|
||||||
// 5.0, ((1.0 - ((water_dist + 2.0) *
|
|
||||||
// 0.3).min(0.0).cos().abs())
|
|
||||||
// * (col.riverless_alt + 5.0 - col.alt).max(0.0)
|
|
||||||
// * 1.75
|
|
||||||
// + 3.0) as i32,
|
|
||||||
// )
|
|
||||||
// } else {
|
|
||||||
// (0.0, 3)
|
|
||||||
// };
|
|
||||||
// let surface_z = (col.riverless_alt + bridge_offset).floor() as i32;
|
|
||||||
|
|
||||||
// for z in inset - depth..inset {
|
|
||||||
// let _ = vol.set(
|
|
||||||
// Vec3::new(offs.x, offs.y, surface_z + z),
|
|
||||||
// if bridge_offset >= 2.0 && dist >= 3.0 || z < inset - 1 {
|
|
||||||
// Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80,
|
|
||||||
// 100), 8)) } else {
|
|
||||||
// Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 50,
|
|
||||||
// 30), 8)) },
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// let head_space = (8 - (dist * 0.25).powf(6.0).round() as i32).max(1);
|
|
||||||
// for z in inset..inset + head_space {
|
|
||||||
// let pos = Vec3::new(offs.x, offs.y, surface_z + z);
|
|
||||||
// if vol.get(pos).unwrap().kind() != BlockKind::Water {
|
|
||||||
// let _ = vol.set(pos, Block::empty());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // Ground colour
|
|
||||||
// } else
|
|
||||||
{
|
{
|
||||||
let mut surface_block = None;
|
let mut surface_block = None;
|
||||||
|
|
||||||
@ -634,9 +618,9 @@ impl Settlement {
|
|||||||
|seed, n| self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, seed * 5)) % n;
|
|seed, n| self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, seed * 5)) % n;
|
||||||
|
|
||||||
let color = match sample.plot {
|
let color = match sample.plot {
|
||||||
Some(Plot::Dirt) => Some(Rgb::new(90, 70, 50)),
|
Some(Plot::Dirt) => Some(colors.plot_dirt.into()),
|
||||||
Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)),
|
Some(Plot::Grass) => Some(colors.plot_grass.into()),
|
||||||
Some(Plot::Water) => Some(Rgb::new(100, 150, 250)),
|
Some(Plot::Water) => Some(colors.plot_water.into()),
|
||||||
//Some(Plot::Town { district }) => None,
|
//Some(Plot::Town { district }) => None,
|
||||||
Some(Plot::Town { .. }) => {
|
Some(Plot::Town { .. }) => {
|
||||||
if let Some((_, path_nearest, _, _)) = col_sample.path {
|
if let Some((_, path_nearest, _, _)) = col_sample.path {
|
||||||
@ -659,13 +643,16 @@ impl Settlement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Rgb::new(100, 95, 65).map2(Rgb::iota(), |e: u8, i: i32| {
|
Some(Rgb::from(colors.plot_town_path).map2(
|
||||||
e.saturating_add(
|
Rgb::iota(),
|
||||||
(self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 1)
|
|e: u8, i: i32| {
|
||||||
as u8,
|
e.saturating_add(
|
||||||
)
|
(self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 1)
|
||||||
.saturating_sub(8)
|
as u8,
|
||||||
}))
|
)
|
||||||
|
.saturating_sub(8)
|
||||||
|
},
|
||||||
|
))
|
||||||
},
|
},
|
||||||
Some(Plot::Field { seed, crop, .. }) => {
|
Some(Plot::Field { seed, crop, .. }) => {
|
||||||
let furrow_dirs = [
|
let furrow_dirs = [
|
||||||
@ -677,12 +664,13 @@ impl Settlement {
|
|||||||
let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()];
|
let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()];
|
||||||
let in_furrow = (wpos2d * furrow_dir).sum().rem_euclid(5) < 2;
|
let in_furrow = (wpos2d * furrow_dir).sum().rem_euclid(5) < 2;
|
||||||
|
|
||||||
let dirt = Rgb::new(80, 55, 35).map(|e| {
|
let dirt = Rgb::<u8>::from(colors.plot_field_dirt).map(|e| {
|
||||||
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) % 32)
|
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) % 32)
|
||||||
as u8
|
as u8
|
||||||
});
|
});
|
||||||
let mound =
|
let mound = Rgb::<u8>::from(colors.plot_field_mound)
|
||||||
Rgb::new(70, 80, 30).map(|e| e + roll(0, 8) as u8).map(|e| {
|
.map(|e| e + roll(0, 8) as u8)
|
||||||
|
.map(|e| {
|
||||||
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 1) as i32))
|
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 1) as i32))
|
||||||
% 32) as u8
|
% 32) as u8
|
||||||
});
|
});
|
||||||
@ -769,8 +757,8 @@ impl Settlement {
|
|||||||
// Walls
|
// Walls
|
||||||
if let Some((WayKind::Wall, dist, _)) = sample.way {
|
if let Some((WayKind::Wall, dist, _)) = sample.way {
|
||||||
let color = Lerp::lerp(
|
let color = Lerp::lerp(
|
||||||
Rgb::new(130i32, 100, 0),
|
Rgb::<u8>::from(colors.wall_low).map(i32::from),
|
||||||
Rgb::new(90, 70, 50),
|
Rgb::<u8>::from(colors.wall_high).map(i32::from),
|
||||||
(RandomField::new(0).get(wpos2d.into()) % 256) as f32 / 256.0,
|
(RandomField::new(0).get(wpos2d.into()) % 256) as f32 / 256.0,
|
||||||
)
|
)
|
||||||
.map(|e| (e % 256) as u8);
|
.map(|e| (e % 256) as u8);
|
||||||
@ -797,7 +785,7 @@ impl Settlement {
|
|||||||
for z in -2..16 {
|
for z in -2..16 {
|
||||||
let _ = vol.set(
|
let _ = vol.set(
|
||||||
Vec3::new(offs.x, offs.y, surface_z + z),
|
Vec3::new(offs.x, offs.y, surface_z + z),
|
||||||
Block::new(BlockKind::Normal, Rgb::new(50, 50, 50)),
|
Block::new(BlockKind::Normal, colors.tower_color.into()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -832,7 +820,7 @@ impl Settlement {
|
|||||||
let wpos = Vec3::from(self.origin) + rpos;
|
let wpos = Vec3::from(self.origin) + rpos;
|
||||||
let coffs = wpos - Vec3::from(wpos2d);
|
let coffs = wpos - Vec3::from(wpos2d);
|
||||||
|
|
||||||
if let Some(block) = structure.sample(rpos) {
|
if let Some(block) = structure.sample(index, rpos) {
|
||||||
let _ = vol.set(coffs, block);
|
let _ = vol.set(coffs, block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -938,30 +926,24 @@ impl Settlement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_color(&self, pos: Vec2<i32>) -> Option<Rgb<u8>> {
|
pub fn get_color(&self, index: IndexRef, pos: Vec2<i32>) -> Option<Rgb<u8>> {
|
||||||
|
let colors = &index.colors.site.settlement;
|
||||||
|
|
||||||
let sample = self.land.get_at_block(pos);
|
let sample = self.land.get_at_block(pos);
|
||||||
|
|
||||||
// match sample.tower {
|
|
||||||
// Some((Tower::Wall, _)) => return Some(Rgb::new(50, 50, 50)),
|
|
||||||
// _ => {},
|
|
||||||
// }
|
|
||||||
|
|
||||||
// match sample.way {
|
|
||||||
// Some((WayKind::Path, _, _)) => return Some(Rgb::new(90, 70, 50)),
|
|
||||||
// Some((WayKind::Hedge, _, _)) => return Some(Rgb::new(0, 150, 0)),
|
|
||||||
// Some((WayKind::Wall, _, _)) => return Some(Rgb::new(60, 60, 60)),
|
|
||||||
// _ => {},
|
|
||||||
// }
|
|
||||||
|
|
||||||
match sample.plot {
|
match sample.plot {
|
||||||
Some(Plot::Dirt) => return Some(Rgb::new(90, 70, 50)),
|
Some(Plot::Dirt) => return Some(colors.plot_dirt.into()),
|
||||||
Some(Plot::Grass) => return Some(Rgb::new(100, 200, 0)),
|
Some(Plot::Grass) => return Some(colors.plot_grass.into()),
|
||||||
Some(Plot::Water) => return Some(Rgb::new(100, 150, 250)),
|
Some(Plot::Water) => return Some(colors.plot_water.into()),
|
||||||
Some(Plot::Town { .. }) => {
|
Some(Plot::Town { .. }) => {
|
||||||
return Some(Rgb::new(150, 110, 60).map2(Rgb::iota(), |e: u8, i: i32| {
|
return Some(
|
||||||
e.saturating_add((self.noise.get(Vec3::new(pos.x, pos.y, i * 5)) % 16) as u8)
|
Rgb::from(colors.plot_town).map2(Rgb::iota(), |e: u8, i: i32| {
|
||||||
|
e.saturating_add(
|
||||||
|
(self.noise.get(Vec3::new(pos.x, pos.y, i * 5)) % 16) as u8,
|
||||||
|
)
|
||||||
.saturating_sub(8)
|
.saturating_sub(8)
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
Some(Plot::Field { seed, .. }) => {
|
Some(Plot::Field { seed, .. }) => {
|
||||||
let furrow_dirs = [
|
let furrow_dirs = [
|
||||||
@ -972,6 +954,12 @@ impl Settlement {
|
|||||||
];
|
];
|
||||||
let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()];
|
let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()];
|
||||||
let furrow = (pos * furrow_dir).sum().rem_euclid(6) < 3;
|
let furrow = (pos * furrow_dir).sum().rem_euclid(6) < 3;
|
||||||
|
// NOTE: Very hard to understand how to make this dynamically configurable. The
|
||||||
|
// base values can easily cause the others to go out of range, and there's some
|
||||||
|
// weird scaling going on. For now, we just let these remain hardcoded.
|
||||||
|
//
|
||||||
|
// FIXME: Rewrite this so that validity is not so heavily dependent on the exact
|
||||||
|
// color values.
|
||||||
return Some(Rgb::new(
|
return Some(Rgb::new(
|
||||||
if furrow {
|
if furrow {
|
||||||
100
|
100
|
||||||
@ -1003,6 +991,8 @@ pub enum Crop {
|
|||||||
Sunflower,
|
Sunflower,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: No support for struct variants in make_case_elim yet, unfortunately, so
|
||||||
|
// we can't use it.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum Plot {
|
pub enum Plot {
|
||||||
Hazard,
|
Hazard,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// pub mod boruvka;
|
|
||||||
pub mod fast_noise;
|
pub mod fast_noise;
|
||||||
pub mod grid;
|
pub mod grid;
|
||||||
pub mod map_vec;
|
pub mod map_vec;
|
||||||
|
Loading…
Reference in New Issue
Block a user