mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/rtsim' into 'master'
Beehives, Fireflies, and minor worldsim/lighting adjustments See merge request veloren/veloren!1357
This commit is contained in:
commit
49c713a071
@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Point and directional lights now cast realistic shadows, using shadow mapping.
|
||||
- Added leaf and chimney particles
|
||||
- Some more combat sound effects
|
||||
- Beehives and bees
|
||||
- Fireflies
|
||||
|
||||
### Changed
|
||||
|
||||
|
738
Cargo.lock
generated
738
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -5,8 +5,8 @@ Item(
|
||||
(
|
||||
kind: "Black0",
|
||||
color: (r: 255, g: 128, b: 26),
|
||||
strength_thousandths: 3000,
|
||||
flicker_thousandths: 300,
|
||||
strength_thousandths: 5000,
|
||||
flicker_thousandths: 150,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -5,7 +5,7 @@ Item(
|
||||
(
|
||||
kind: "Blue0",
|
||||
color: (r: 255, g: 128, b: 26),
|
||||
strength_thousandths: 4000,
|
||||
strength_thousandths: 6000,
|
||||
flicker_thousandths: 250,
|
||||
),
|
||||
),
|
||||
|
@ -5,8 +5,8 @@ Item(
|
||||
(
|
||||
kind: "Green0",
|
||||
color: (r: 255, g: 128, b: 26),
|
||||
strength_thousandths: 4000,
|
||||
flicker_thousandths: 500,
|
||||
strength_thousandths: 6000,
|
||||
flicker_thousandths: 250,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -5,8 +5,8 @@ Item(
|
||||
(
|
||||
kind: "Red0",
|
||||
color: (r: 255, g: 128, b: 26),
|
||||
strength_thousandths: 3500,
|
||||
flicker_thousandths: 1000,
|
||||
strength_thousandths: 5000,
|
||||
flicker_thousandths: 250,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -140,9 +140,8 @@ float lights_at(vec3 wpos, vec3 wnorm, vec3 /*cam_to_frag*/view_dir, vec3 mu, ve
|
||||
float distance_2 = dot(difference, difference);
|
||||
|
||||
// float strength = attenuation_strength(difference);// pow(attenuation_strength(difference), 0.6);
|
||||
// // NOTE: This normalizes strength to 1.0 at the center of the point source.
|
||||
// float strength = 1.0 / (1.0 + distance_2);
|
||||
float strength = 1.0 / distance_2;
|
||||
// NOTE: This normalizes strength to 0.25 at the center of the point source.
|
||||
float strength = 1.0 / (4 + distance_2);
|
||||
|
||||
// Multiply the vec3 only once
|
||||
const float PI = 3.1415926535897932384626433832795;
|
||||
|
@ -71,6 +71,10 @@ void main() {
|
||||
|
||||
max_light += lights_at(f_pos, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light);
|
||||
|
||||
// Allow particles to glow at night
|
||||
// TODO: Not this
|
||||
emitted_light += max(f_col.rgb - 1.0, vec3(0));
|
||||
|
||||
surf_color = illuminate(max_light, view_dir, surf_color * emitted_light, surf_color * reflected_light);
|
||||
|
||||
#if (CLOUD_MODE == CLOUD_MODE_REGULAR)
|
||||
|
@ -45,6 +45,8 @@ const int FIREWORK_PURPLE = 6;
|
||||
const int FIREWORK_RED = 7;
|
||||
const int FIREWORK_YELLOW = 8;
|
||||
const int LEAF = 9;
|
||||
const int FIREFLY = 10;
|
||||
const int BEE = 11;
|
||||
|
||||
// meters per second squared (acceleration)
|
||||
const float earth_gravity = 9.807;
|
||||
@ -214,6 +216,30 @@ void main() {
|
||||
vec4(vec3(0.2 + rand7 * 0.2, 0.2 + (0.5 + rand6 * 0.5) * 0.6, 0), 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5)
|
||||
);
|
||||
} else if (inst_mode == FIREFLY) {
|
||||
float raise = pow(sin(3.1416 * lifetime / inst_lifespan), 0.2);
|
||||
attr = Attr(
|
||||
vec3(0, 0, raise * 5.0) + vec3(
|
||||
sin(lifetime * 1.0 + rand0) + sin(lifetime * 7.0 + rand3) * 0.3,
|
||||
sin(lifetime * 3.0 + rand1) + sin(lifetime * 8.0 + rand4) * 0.3,
|
||||
sin(lifetime * 2.0 + rand2) + sin(lifetime * 9.0 + rand5) * 0.3
|
||||
),
|
||||
raise,
|
||||
vec4(vec3(5, 5, 1.1), 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5)
|
||||
);
|
||||
} else if (inst_mode == BEE) {
|
||||
float lower = pow(sin(3.1416 * lifetime / inst_lifespan), 0.2);
|
||||
attr = Attr(
|
||||
vec3(0, 0, lower * -0.5) + vec3(
|
||||
sin(lifetime * 2.0 + rand0) + sin(lifetime * 9.0 + rand3) * 0.3,
|
||||
sin(lifetime * 3.0 + rand1) + sin(lifetime * 10.0 + rand4) * 0.3,
|
||||
sin(lifetime * 4.0 + rand2) + sin(lifetime * 11.0 + rand5) * 0.3
|
||||
) * 0.5,
|
||||
lower,
|
||||
vec4(vec3(1, 0.7, 0), 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5)
|
||||
);
|
||||
} else {
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
|
BIN
assets/voxygen/voxel/sprite/beehive/beehive.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/beehive/beehive.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/reed/reed-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/reed/reed-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/reed/reed-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/reed/reed-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/reed/reed-2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/reed/reed-2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/reed/reed-3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/reed/reed-3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/reed/reed-4.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/reed/reed-4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -1947,4 +1947,48 @@ GrassSnow: Some((
|
||||
],
|
||||
wind_sway: 0.2,
|
||||
)),
|
||||
|
||||
// Reed
|
||||
Reed: Some((
|
||||
variations: [
|
||||
(
|
||||
model: "voxygen.voxel.sprite.reed.reed-0",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.5),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.reed.reed-1",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.5),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.reed.reed-2",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.5),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.reed.reed-3",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.5),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.reed.reed-4",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.5),
|
||||
),
|
||||
],
|
||||
wind_sway: 0.3,
|
||||
)),
|
||||
|
||||
// Beehive
|
||||
Beehive: Some((
|
||||
variations: [
|
||||
(
|
||||
model: "voxygen.voxel.sprite.beehive.beehive",
|
||||
offset: (-5.5, -5.5, 0.0),
|
||||
lod_axes: (1.0, 1.0, 1.0),
|
||||
),
|
||||
],
|
||||
wind_sway: 0.1,
|
||||
)),
|
||||
)
|
||||
|
@ -21,6 +21,7 @@ use serde::{Deserialize, Serialize};
|
||||
use specs::{Component, FlaggedStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
use std::{fs::File, io::BufReader};
|
||||
use vek::*;
|
||||
|
||||
make_case_elim!(
|
||||
body,
|
||||
@ -505,6 +506,14 @@ impl Body {
|
||||
Body::QuadrupedLow(_) => 4.5,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_light_offset(&self) -> Vec3<f32> {
|
||||
// TODO: Make this a manifest
|
||||
match self {
|
||||
Body::Object(_) => Vec3::unit_z() * 0.5,
|
||||
_ => Vec3::unit_z(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Body {
|
||||
|
@ -42,6 +42,7 @@ pub mod store;
|
||||
pub mod sync;
|
||||
pub mod sys;
|
||||
pub mod terrain;
|
||||
pub mod time;
|
||||
pub mod typed;
|
||||
pub mod util;
|
||||
pub mod vol;
|
||||
|
@ -5,6 +5,7 @@ use crate::{
|
||||
sync::WorldSyncExt,
|
||||
sys,
|
||||
terrain::{Block, TerrainChunk, TerrainGrid},
|
||||
time::DayPeriod,
|
||||
vol::WriteVol,
|
||||
};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
@ -230,6 +231,22 @@ impl State {
|
||||
/// localised timings.
|
||||
pub fn get_time_of_day(&self) -> f64 { self.ecs.read_resource::<TimeOfDay>().0 }
|
||||
|
||||
/// Get the current in-game day period (period of the day/night cycle)
|
||||
pub fn get_day_period(&self) -> DayPeriod {
|
||||
let tod = self.get_time_of_day().rem_euclid(60.0 * 60.0 * 24.0);
|
||||
if tod < 60.0 * 60.0 * 4.0 {
|
||||
DayPeriod::Night
|
||||
} else if tod < 60.0 * 60.0 * 10.0 {
|
||||
DayPeriod::Morning
|
||||
} else if tod < 60.0 * 60.0 * 16.0 {
|
||||
DayPeriod::Noon
|
||||
} else if tod < 60.0 * 60.0 * 20.0 {
|
||||
DayPeriod::Evening
|
||||
} else {
|
||||
DayPeriod::Night
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current in-game time.
|
||||
///
|
||||
/// Note that this does not correspond to the time of day.
|
||||
|
@ -156,9 +156,7 @@ impl<'a> System<'a> for Sys {
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(dir) = velocities
|
||||
} else if let Some(dir) = velocities
|
||||
.get(entity)
|
||||
.and_then(|vel| vel.0.try_normalized())
|
||||
{
|
||||
|
@ -93,6 +93,8 @@ make_case_elim!(
|
||||
DropGate = 0x50,
|
||||
DropGateBottom = 0x51,
|
||||
GrassSnow = 0x52,
|
||||
Reed = 0x53,
|
||||
Beehive = 0x54,
|
||||
}
|
||||
);
|
||||
|
||||
@ -200,6 +202,8 @@ impl BlockKind {
|
||||
BlockKind::DropGate => false,
|
||||
BlockKind::DropGateBottom => false,
|
||||
BlockKind::GrassSnow => true,
|
||||
BlockKind::Reed => true,
|
||||
BlockKind::Beehive => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -296,6 +300,8 @@ impl BlockKind {
|
||||
BlockKind::DropGate => false,
|
||||
BlockKind::DropGateBottom => false,
|
||||
BlockKind::GrassSnow => false,
|
||||
BlockKind::Reed => false,
|
||||
BlockKind::Beehive => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
@ -373,6 +379,7 @@ impl BlockKind {
|
||||
BlockKind::DropGate => true,
|
||||
BlockKind::DropGateBottom => false,
|
||||
BlockKind::GrassSnow => false,
|
||||
BlockKind::Reed => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
@ -502,7 +509,8 @@ impl Block {
|
||||
| BlockKind::Chest
|
||||
| BlockKind::DropGate
|
||||
| BlockKind::DropGateBottom
|
||||
| BlockKind::Door => Some(self.color[0] & 0b111),
|
||||
| BlockKind::Door
|
||||
| BlockKind::Beehive => Some(self.color[0] & 0b111),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
13
common/src/time.rs
Normal file
13
common/src/time.rs
Normal file
@ -0,0 +1,13 @@
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum DayPeriod {
|
||||
Night,
|
||||
Morning,
|
||||
Noon,
|
||||
Evening,
|
||||
}
|
||||
|
||||
impl DayPeriod {
|
||||
pub fn is_dark(&self) -> bool { *self == DayPeriod::Night }
|
||||
|
||||
pub fn is_light(&self) -> bool { !self.is_dark() }
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
use crate::{sys, Server, StateExt};
|
||||
use common::{
|
||||
comp::{
|
||||
self, Agent, Alignment, Body, Gravity, Item, ItemDrop, LightAnimation, LightEmitter,
|
||||
Loadout, Pos, Projectile, Scale, Stats, Vel, WaypointArea,
|
||||
self, Agent, Alignment, Body, Gravity, Item, ItemDrop, LightEmitter, Loadout, Pos,
|
||||
Projectile, Scale, Stats, Vel, WaypointArea,
|
||||
},
|
||||
outcome::Outcome,
|
||||
util::Dir,
|
||||
@ -119,16 +119,12 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
|
||||
.state
|
||||
.create_object(Pos(pos), comp::object::Body::CampfireLit)
|
||||
.with(LightEmitter {
|
||||
col: Rgb::new(1.0, 0.8, 0.0),
|
||||
strength: 8.0,
|
||||
col: Rgb::new(1.0, 0.3, 0.1),
|
||||
strength: 5.0,
|
||||
flicker: 1.0,
|
||||
animated: true,
|
||||
})
|
||||
.with(LightAnimation {
|
||||
offset: Vec3::new(0.0, 0.0, 2.0),
|
||||
col: Rgb::new(1.0, 0.8, 0.0),
|
||||
strength: 8.0,
|
||||
})
|
||||
.with(WaypointArea::default())
|
||||
.with(comp::Mass(100000.0))
|
||||
.build();
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ pub fn handle_lantern(server: &mut Server, entity: EcsEntity) {
|
||||
.insert(entity, comp::LightEmitter {
|
||||
col: lantern.color(),
|
||||
strength: lantern.strength(),
|
||||
flicker: 1.0,
|
||||
flicker: 0.35,
|
||||
animated: true,
|
||||
});
|
||||
}
|
||||
|
@ -358,6 +358,9 @@ impl Server {
|
||||
/// Get a reference to the server's world.
|
||||
pub fn world(&self) -> &World { &self.world }
|
||||
|
||||
/// Get a reference to the server's world map.
|
||||
pub fn map(&self) -> &WorldMapMsg { &self.map }
|
||||
|
||||
/// Execute a single server tick, handle input and update the game state by
|
||||
/// the given duration.
|
||||
pub fn tick(&mut self, _input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
|
||||
|
@ -103,6 +103,8 @@ pub enum ParticleMode {
|
||||
FireworkRed = 7,
|
||||
FireworkYellow = 8,
|
||||
Leaf = 9,
|
||||
Firefly = 10,
|
||||
Bee = 11,
|
||||
}
|
||||
|
||||
impl ParticleMode {
|
||||
|
@ -43,6 +43,7 @@ use guillotiere::AtlasAllocator;
|
||||
use hashbrown::HashMap;
|
||||
use specs::{Entity as EcsEntity, Join, LazyUpdate, WorldExt};
|
||||
use treeculler::{BVol, BoundingSphere};
|
||||
use vek::*;
|
||||
|
||||
const DAMAGE_FADE_COEFFICIENT: f64 = 5.0;
|
||||
const MOVING_THRESHOLD: f32 = 0.7;
|
||||
@ -363,13 +364,20 @@ impl FigureMgr {
|
||||
// TODO: Pending review in #587
|
||||
pub fn update_lighting(&mut self, scene_data: &SceneData) {
|
||||
let ecs = scene_data.state.ecs();
|
||||
for (entity, light_emitter) in (&ecs.entities(), &ecs.read_storage::<LightEmitter>()).join()
|
||||
for (entity, body, light_emitter) in (
|
||||
&ecs.entities(),
|
||||
ecs.read_storage::<common::comp::Body>().maybe(),
|
||||
&ecs.read_storage::<LightEmitter>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
// Add LightAnimation for objects with a LightEmitter
|
||||
let mut anim_storage = ecs.write_storage::<LightAnimation>();
|
||||
if let None = anim_storage.get_mut(entity) {
|
||||
let anim = LightAnimation {
|
||||
offset: vek::Vec3::zero(),
|
||||
offset: body
|
||||
.map(|b| b.default_light_offset())
|
||||
.unwrap_or_else(Vec3::zero),
|
||||
col: light_emitter.col,
|
||||
strength: 0.0,
|
||||
};
|
||||
|
@ -282,32 +282,85 @@ impl ParticleMgr {
|
||||
(e.floor() as i32).div_euclid(sz as i32)
|
||||
});
|
||||
|
||||
type BoiFn<'a> = fn(&'a BlocksOfInterest) -> &'a [Vec3<i32>];
|
||||
// blocks, chunk range, emission density, lifetime, particle mode
|
||||
//
|
||||
// - blocks: the function to select the blocks of interest that we should emit
|
||||
// from
|
||||
// - chunk range: the range, in chunks, that the particles should be generated
|
||||
// in from the player
|
||||
// - emission density: the density, per block per second, of the generated
|
||||
// particles
|
||||
// - lifetime: the number of seconds that each particle should live for
|
||||
// - particle mode: the visual mode of the generated particle
|
||||
let particles: &[(BoiFn, usize, f32, f32, ParticleMode)] = &[
|
||||
(|boi| &boi.leaves, 4, 0.001, 30.0, ParticleMode::Leaf),
|
||||
(|boi| &boi.embers, 2, 20.0, 0.25, ParticleMode::CampfireFire),
|
||||
(|boi| &boi.embers, 8, 3.0, 40.0, ParticleMode::CampfireSmoke),
|
||||
struct BlockParticles<'a> {
|
||||
// The function to select the blocks of interest that we should emit from
|
||||
blocks: fn(&'a BlocksOfInterest) -> &'a [Vec3<i32>],
|
||||
// The range, in chunks, that the particles should be generated in from the player
|
||||
range: usize,
|
||||
// The emission rate, per block per second, of the generated particles
|
||||
rate: f32,
|
||||
// The number of seconds that each particle should live for
|
||||
lifetime: f32,
|
||||
// The visual mode of the generated particle
|
||||
mode: ParticleMode,
|
||||
// Condition that must be true
|
||||
cond: fn(&SceneData) -> bool,
|
||||
}
|
||||
|
||||
let particles: &[BlockParticles] = &[
|
||||
BlockParticles {
|
||||
blocks: |boi| &boi.leaves,
|
||||
range: 4,
|
||||
rate: 0.001,
|
||||
lifetime: 30.0,
|
||||
mode: ParticleMode::Leaf,
|
||||
cond: |_| true,
|
||||
},
|
||||
BlockParticles {
|
||||
blocks: |boi| &boi.embers,
|
||||
range: 2,
|
||||
rate: 20.0,
|
||||
lifetime: 0.25,
|
||||
mode: ParticleMode::CampfireFire,
|
||||
cond: |_| true,
|
||||
},
|
||||
BlockParticles {
|
||||
blocks: |boi| &boi.embers,
|
||||
range: 8,
|
||||
rate: 3.0,
|
||||
lifetime: 40.0,
|
||||
mode: ParticleMode::CampfireSmoke,
|
||||
cond: |_| true,
|
||||
},
|
||||
BlockParticles {
|
||||
blocks: |boi| &boi.reeds,
|
||||
range: 6,
|
||||
rate: 0.004,
|
||||
lifetime: 40.0,
|
||||
mode: ParticleMode::Firefly,
|
||||
cond: |sd| sd.state.get_day_period().is_dark(),
|
||||
},
|
||||
BlockParticles {
|
||||
blocks: |boi| &boi.flowers,
|
||||
range: 5,
|
||||
rate: 0.002,
|
||||
lifetime: 40.0,
|
||||
mode: ParticleMode::Firefly,
|
||||
cond: |sd| sd.state.get_day_period().is_dark(),
|
||||
},
|
||||
BlockParticles {
|
||||
blocks: |boi| &boi.beehives,
|
||||
range: 3,
|
||||
rate: 0.5,
|
||||
lifetime: 30.0,
|
||||
mode: ParticleMode::Bee,
|
||||
cond: |sd| sd.state.get_day_period().is_light(),
|
||||
},
|
||||
];
|
||||
|
||||
let mut rng = thread_rng();
|
||||
for (get_blocks, range, rate, dur, mode) in particles.iter() {
|
||||
for offset in Spiral2d::new().take((*range * 2 + 1).pow(2)) {
|
||||
for particles in particles.iter() {
|
||||
if !(particles.cond)(scene_data) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for offset in Spiral2d::new().take((particles.range * 2 + 1).pow(2)) {
|
||||
let chunk_pos = player_chunk + offset;
|
||||
|
||||
terrain.get(chunk_pos).map(|chunk_data| {
|
||||
let blocks = get_blocks(&chunk_data.blocks_of_interest);
|
||||
let blocks = (particles.blocks)(&chunk_data.blocks_of_interest);
|
||||
|
||||
let avg_particles = dt * blocks.len() as f32 * *rate;
|
||||
let avg_particles = dt * blocks.len() as f32 * particles.rate;
|
||||
let particle_count = avg_particles.trunc() as usize
|
||||
+ (rng.gen::<f32>() < avg_particles.fract()) as usize;
|
||||
|
||||
@ -318,9 +371,9 @@ impl ParticleMgr {
|
||||
+ blocks.choose(&mut rng).copied().unwrap(); // Can't fail
|
||||
|
||||
Particle::new(
|
||||
Duration::from_secs_f32(*dur),
|
||||
Duration::from_secs_f32(particles.lifetime),
|
||||
time,
|
||||
*mode,
|
||||
particles.mode,
|
||||
block_pos.map(|e: i32| e as f32 + rng.gen::<f32>()),
|
||||
)
|
||||
})
|
||||
|
@ -7,13 +7,21 @@ use vek::*;
|
||||
|
||||
pub struct BlocksOfInterest {
|
||||
pub leaves: Vec<Vec3<i32>>,
|
||||
pub grass: Vec<Vec3<i32>>,
|
||||
pub embers: Vec<Vec3<i32>>,
|
||||
pub beehives: Vec<Vec3<i32>>,
|
||||
pub reeds: Vec<Vec3<i32>>,
|
||||
pub flowers: Vec<Vec3<i32>>,
|
||||
}
|
||||
|
||||
impl BlocksOfInterest {
|
||||
pub fn from_chunk(chunk: &TerrainChunk) -> Self {
|
||||
let mut leaves = Vec::new();
|
||||
let mut grass = Vec::new();
|
||||
let mut embers = Vec::new();
|
||||
let mut beehives = Vec::new();
|
||||
let mut reeds = Vec::new();
|
||||
let mut flowers = Vec::new();
|
||||
|
||||
chunk
|
||||
.vol_iter(
|
||||
@ -24,14 +32,36 @@ impl BlocksOfInterest {
|
||||
chunk.get_max_z(),
|
||||
),
|
||||
)
|
||||
.for_each(|(pos, block)| {
|
||||
if block.kind() == BlockKind::Leaves && thread_rng().gen_range(0, 16) == 0 {
|
||||
leaves.push(pos);
|
||||
} else if block.kind() == BlockKind::Ember {
|
||||
embers.push(pos);
|
||||
}
|
||||
.for_each(|(pos, block)| match block.kind() {
|
||||
BlockKind::Leaves => {
|
||||
if thread_rng().gen_range(0, 16) == 0 {
|
||||
leaves.push(pos)
|
||||
}
|
||||
},
|
||||
BlockKind::Grass => {
|
||||
if thread_rng().gen_range(0, 16) == 0 {
|
||||
grass.push(pos)
|
||||
}
|
||||
},
|
||||
BlockKind::Ember => embers.push(pos),
|
||||
BlockKind::Beehive => beehives.push(pos),
|
||||
BlockKind::Reed => reeds.push(pos),
|
||||
BlockKind::PinkFlower => flowers.push(pos),
|
||||
BlockKind::PurpleFlower => flowers.push(pos),
|
||||
BlockKind::RedFlower => flowers.push(pos),
|
||||
BlockKind::WhiteFlower => flowers.push(pos),
|
||||
BlockKind::YellowFlower => flowers.push(pos),
|
||||
BlockKind::Sunflower => flowers.push(pos),
|
||||
_ => {},
|
||||
});
|
||||
|
||||
Self { leaves, embers }
|
||||
Self {
|
||||
leaves,
|
||||
grass,
|
||||
embers,
|
||||
beehives,
|
||||
reeds,
|
||||
flowers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -574,10 +574,12 @@ pub fn block_from_structure(
|
||||
// None of these BlockKinds has an orientation, so we just use zero for the other color
|
||||
// bits.
|
||||
StructureBlock::Liana => Some(Block::new(BlockKind::Liana, Rgb::zero())),
|
||||
StructureBlock::Fruit => Some(if field.get(pos + structure_pos) % 3 > 0 {
|
||||
Block::empty()
|
||||
} else {
|
||||
StructureBlock::Fruit => Some(if field.get(pos + structure_pos) % 24 == 0 {
|
||||
Block::new(BlockKind::Beehive, Rgb::zero())
|
||||
} else if field.get(pos + structure_pos + 1) % 3 == 0 {
|
||||
Block::new(BlockKind::Apple, Rgb::zero())
|
||||
} else {
|
||||
Block::empty()
|
||||
}),
|
||||
StructureBlock::Coconut => Some(if field.get(pos + structure_pos) % 3 > 0 {
|
||||
Block::empty()
|
||||
|
@ -774,6 +774,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
.div(100.0)
|
||||
.into_array(),
|
||||
) as f32)
|
||||
//.mul(water_dist.map(|wd| (wd / 2.0).clamped(0.0, 1.0).powf(0.5)).unwrap_or(1.0))
|
||||
.mul(rockiness)
|
||||
.sub(0.4)
|
||||
.max(0.0)
|
||||
@ -996,6 +997,11 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
(alt, ground, sub_surface_color)
|
||||
};
|
||||
|
||||
// Make river banks not have grass
|
||||
let ground = water_dist
|
||||
.map(|wd| Lerp::lerp(sub_surface_color, ground, (wd / 3.0).clamped(0.0, 1.0)))
|
||||
.unwrap_or(ground);
|
||||
|
||||
let near_ocean = max_river.and_then(|(_, _, river_data, _)| {
|
||||
if (river_data.is_lake() || river_data.river_kind == Some(RiverKind::Ocean))
|
||||
&& ((alt <= water_level.max(CONFIG.sea_level + 5.0) && !is_cliffs) || !near_cliffs)
|
||||
|
@ -1,8 +1,11 @@
|
||||
pub mod scatter;
|
||||
|
||||
pub use self::scatter::apply_scatter_to;
|
||||
|
||||
use crate::{
|
||||
column::ColumnSample,
|
||||
sim::SimChunk,
|
||||
util::{RandomField, Sampler},
|
||||
IndexRef, CONFIG,
|
||||
IndexRef,
|
||||
};
|
||||
use common::{
|
||||
assets::Asset,
|
||||
@ -27,320 +30,6 @@ pub struct Colors {
|
||||
pub stalagtite: (u8, u8, u8),
|
||||
}
|
||||
|
||||
fn close(x: f32, tgt: f32, falloff: f32) -> f32 {
|
||||
(1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.5)
|
||||
}
|
||||
const MUSH_FACT: f32 = 1.0e-4; // To balance everything around the mushroom spawning rate
|
||||
pub fn apply_scatter_to<'a>(
|
||||
wpos2d: Vec2<i32>,
|
||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||
index: IndexRef,
|
||||
chunk: &SimChunk,
|
||||
) {
|
||||
use BlockKind::*;
|
||||
#[allow(clippy::type_complexity)]
|
||||
// TODO: Add back all sprites we had before
|
||||
let scatter: &[(_, bool, fn(&SimChunk) -> (f32, Option<(f32, f32)>))] = &[
|
||||
// (density, Option<(wavelen, threshold)>)
|
||||
// Flowers
|
||||
(BlueFlower, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.1, 0.2).min(close(c.humidity, CONFIG.forest_hum, 0.35))
|
||||
* MUSH_FACT
|
||||
* 0.01,
|
||||
Some((48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
(PinkFlower, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.2, 0.2).min(close(c.humidity, CONFIG.forest_hum, 0.35))
|
||||
* MUSH_FACT
|
||||
* 0.01,
|
||||
Some((48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
(PurpleFlower, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.2).min(close(c.humidity, CONFIG.forest_hum, 0.35))
|
||||
* MUSH_FACT
|
||||
* 0.01,
|
||||
Some((48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
(RedFlower, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.5, 0.2).min(close(c.humidity, CONFIG.forest_hum, 0.35))
|
||||
* MUSH_FACT
|
||||
* 0.01,
|
||||
Some((48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
(WhiteFlower, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.0, 0.3).min(close(c.humidity, CONFIG.forest_hum, 0.35))
|
||||
* MUSH_FACT
|
||||
* 0.01,
|
||||
Some((48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
(YellowFlower, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.2).min(close(c.humidity, CONFIG.forest_hum, 0.35))
|
||||
* MUSH_FACT
|
||||
* 0.01,
|
||||
Some((48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
// Herbs and Spices
|
||||
(LingonBerry, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.forest_hum, 0.35))
|
||||
* MUSH_FACT
|
||||
* 0.5,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(LeafyPlant, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.forest_hum, 0.35))
|
||||
* MUSH_FACT
|
||||
* 0.5,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(Fern, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.forest_hum, 0.35))
|
||||
* MUSH_FACT
|
||||
* 0.5,
|
||||
Some((48.0, 0.4)),
|
||||
)
|
||||
}),
|
||||
(Blueberry, false, |c| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.5).min(close(
|
||||
c.humidity,
|
||||
CONFIG.forest_hum,
|
||||
0.35,
|
||||
)) * MUSH_FACT
|
||||
* 0.3,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
// Collectable Objects
|
||||
// Only spawn twigs in temperate forests
|
||||
(Twigs, false, |c| {
|
||||
((c.tree_density - 0.5).max(0.0) * 1.0e-3, None)
|
||||
}),
|
||||
(Stones, false, |c| {
|
||||
((c.rockiness - 0.5).max(0.0) * 1.0e-3, None)
|
||||
}),
|
||||
// Don't spawn Mushrooms in snowy regions
|
||||
(Mushroom, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.forest_hum, 0.35)) * MUSH_FACT,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
// Grass
|
||||
(ShortGrass, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, 0.6, 0.35)) * 0.05,
|
||||
Some((48.0, 0.4)),
|
||||
)
|
||||
}),
|
||||
(MediumGrass, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.0, 0.6).min(close(c.humidity, 0.6, 0.35)) * 0.05,
|
||||
Some((48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
(LongGrass, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.4, 0.4).min(close(c.humidity, 0.8, 0.2)) * 0.08,
|
||||
Some((48.0, 0.1)),
|
||||
)
|
||||
}),
|
||||
// Jungle Sprites
|
||||
(LongGrass, false, |c| {
|
||||
(
|
||||
close(c.temp, CONFIG.tropical_temp, 0.4).min(close(
|
||||
c.humidity,
|
||||
CONFIG.jungle_hum,
|
||||
0.6,
|
||||
)) * 0.08,
|
||||
Some((60.0, 5.0)),
|
||||
)
|
||||
}),
|
||||
/*(WheatGreen, false, |c| {
|
||||
(
|
||||
close(c.temp, 0.4, 0.2).min(close(c.humidity, CONFIG.forest_hum, 0.1))
|
||||
* MUSH_FACT
|
||||
* 0.001,
|
||||
None,
|
||||
)
|
||||
}),*/
|
||||
(GrassSnow, false, |c| {
|
||||
(
|
||||
close(c.temp, CONFIG.snow_temp - 0.2, 0.4).min(close(
|
||||
c.humidity,
|
||||
CONFIG.forest_hum,
|
||||
0.5,
|
||||
)) * 0.01,
|
||||
Some((48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
// Desert Plants
|
||||
(DeadBush, false, |c| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.3,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(LargeCactus, false, |c| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
/*(BarrelCactus, false, |c| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(RoundCactus, false, |c| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(ShortCactus, false, |c| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(MedFlatCactus, false, |c| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(ShortFlatCactus, false, |c| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),*/
|
||||
];
|
||||
|
||||
for y in 0..vol.size_xy().y as i32 {
|
||||
for x in 0..vol.size_xy().x as i32 {
|
||||
let offs = Vec2::new(x, y);
|
||||
|
||||
let wpos2d = wpos2d + offs;
|
||||
|
||||
// Sample terrain
|
||||
let col_sample = if let Some(col_sample) = get_column(offs) {
|
||||
col_sample
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let underwater = col_sample.water_level > col_sample.alt;
|
||||
|
||||
let bk = scatter
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(i, (bk, is_underwater, f))| {
|
||||
let (density, patch) = f(chunk);
|
||||
let is_patch = patch
|
||||
.map(|(wavelen, threshold)| {
|
||||
index.noise.scatter_nz.get(
|
||||
wpos2d
|
||||
.map(|e| e as f64 / wavelen as f64 + i as f64 * 43.0)
|
||||
.into_array(),
|
||||
) < threshold as f64
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if density <= 0.0
|
||||
|| is_patch
|
||||
|| !RandomField::new(i as u32)
|
||||
.chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
|
||||
|| underwater != *is_underwater
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(*bk)
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(bk) = bk {
|
||||
let alt = col_sample.alt as i32;
|
||||
|
||||
// Find the intersection between ground and air, if there is one near the
|
||||
// surface
|
||||
if let Some(solid_end) = (-4..8)
|
||||
.find(|z| {
|
||||
vol.get(Vec3::new(offs.x, offs.y, alt + z))
|
||||
.map(|b| b.is_solid())
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.and_then(|solid_start| {
|
||||
(1..8).map(|z| solid_start + z).find(|z| {
|
||||
vol.get(Vec3::new(offs.x, offs.y, alt + z))
|
||||
.map(|b| !b.is_solid())
|
||||
.unwrap_or(true)
|
||||
})
|
||||
})
|
||||
{
|
||||
let _ = vol.set(
|
||||
Vec3::new(offs.x, offs.y, alt + solid_end),
|
||||
Block::new(bk, Rgb::broadcast(0)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_paths_to<'a>(
|
||||
wpos2d: Vec2<i32>,
|
||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
|
360
world/src/layer/scatter.rs
Normal file
360
world/src/layer/scatter.rs
Normal file
@ -0,0 +1,360 @@
|
||||
use crate::{column::ColumnSample, sim::SimChunk, util::RandomField, IndexRef, CONFIG};
|
||||
use common::{
|
||||
terrain::{Block, BlockKind},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
||||
};
|
||||
use noise::NoiseFn;
|
||||
use std::f32;
|
||||
use vek::*;
|
||||
|
||||
fn close(x: f32, tgt: f32, falloff: f32) -> f32 {
|
||||
(1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.125)
|
||||
}
|
||||
const MUSH_FACT: f32 = 1.0e-4; // To balance everything around the mushroom spawning rate
|
||||
pub fn apply_scatter_to<'a>(
|
||||
wpos2d: Vec2<i32>,
|
||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||
index: IndexRef,
|
||||
chunk: &SimChunk,
|
||||
) {
|
||||
use BlockKind::*;
|
||||
#[allow(clippy::type_complexity)]
|
||||
// TODO: Add back all sprites we had before
|
||||
let scatter: &[(
|
||||
_,
|
||||
bool,
|
||||
fn(&SimChunk, &ColumnSample) -> (f32, Option<(f32, f32)>),
|
||||
)] = &[
|
||||
// (density, Option<(wavelen, threshold)>)
|
||||
// Flowers
|
||||
(BlueFlower, false, |c, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.7).min(close(
|
||||
c.humidity,
|
||||
CONFIG.jungle_hum,
|
||||
0.4,
|
||||
)) * col.tree_density
|
||||
* MUSH_FACT
|
||||
* 256.0,
|
||||
Some((256.0, 0.25)),
|
||||
)
|
||||
}),
|
||||
(PinkFlower, false, |c, col| {
|
||||
(
|
||||
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
|
||||
* col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.1)),
|
||||
)
|
||||
}),
|
||||
(PurpleFlower, false, |c, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.7).min(close(
|
||||
c.humidity,
|
||||
CONFIG.jungle_hum,
|
||||
0.4,
|
||||
)) * col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.1)),
|
||||
)
|
||||
}),
|
||||
(RedFlower, false, |c, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.tropical_temp, 0.6).min(close(
|
||||
c.humidity,
|
||||
CONFIG.jungle_hum,
|
||||
0.3,
|
||||
)) * col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.05)),
|
||||
)
|
||||
}),
|
||||
(WhiteFlower, false, |c, col| {
|
||||
(
|
||||
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
|
||||
* col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.1)),
|
||||
)
|
||||
}),
|
||||
(YellowFlower, false, |c, col| {
|
||||
(
|
||||
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
|
||||
* col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.1)),
|
||||
)
|
||||
}),
|
||||
(Sunflower, false, |c, col| {
|
||||
(
|
||||
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
|
||||
* col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.15)),
|
||||
)
|
||||
}),
|
||||
// Herbs and Spices
|
||||
(LingonBerry, false, |c, _| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.jungle_hum, 0.5))
|
||||
* MUSH_FACT
|
||||
* 2.5,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(LeafyPlant, false, |c, _| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.jungle_hum, 0.3))
|
||||
* MUSH_FACT
|
||||
* 4.0,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(Fern, false, |c, _| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.forest_hum, 0.5))
|
||||
* MUSH_FACT
|
||||
* 0.25,
|
||||
Some((64.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
(Blueberry, false, |c, _| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.5).min(close(
|
||||
c.humidity,
|
||||
CONFIG.forest_hum,
|
||||
0.5,
|
||||
)) * MUSH_FACT
|
||||
* 0.3,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
// Collectable Objects
|
||||
// Only spawn twigs in temperate forests
|
||||
(Twigs, false, |c, _| {
|
||||
((c.tree_density - 0.5).max(0.0) * 1.0e-3, None)
|
||||
}),
|
||||
(Stones, false, |c, _| {
|
||||
((c.rockiness - 0.5).max(0.0) * 1.0e-3, None)
|
||||
}),
|
||||
// Don't spawn Mushrooms in snowy regions
|
||||
(Mushroom, false, |c, _| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.forest_hum, 0.35)) * MUSH_FACT,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
// Grass
|
||||
(ShortGrass, false, |c, _| {
|
||||
(
|
||||
close(c.temp, 0.2, 0.65).min(close(c.humidity, CONFIG.jungle_hum, 0.4)) * 0.015,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(MediumGrass, false, |c, _| {
|
||||
(
|
||||
close(c.temp, 0.2, 0.6).min(close(c.humidity, CONFIG.jungle_hum, 0.4)) * 0.012,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(LongGrass, false, |c, _| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.35).min(close(c.humidity, CONFIG.jungle_hum, 0.3)) * 0.15,
|
||||
Some((48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
// Jungle Sprites
|
||||
// (LongGrass, false, |c, col| {
|
||||
// (
|
||||
// close(c.temp, CONFIG.tropical_temp, 0.4).min(close(
|
||||
// c.humidity,
|
||||
// CONFIG.jungle_hum,
|
||||
// 0.6,
|
||||
// )) * 0.08,
|
||||
// Some((60.0, 5.0)),
|
||||
// )
|
||||
// }),
|
||||
/*(WheatGreen, false, |c, col| {
|
||||
(
|
||||
close(c.temp, 0.4, 0.2).min(close(c.humidity, CONFIG.forest_hum, 0.1))
|
||||
* MUSH_FACT
|
||||
* 0.001,
|
||||
None,
|
||||
)
|
||||
}),*/
|
||||
(GrassSnow, false, |c, _| {
|
||||
(
|
||||
close(c.temp, CONFIG.snow_temp - 0.2, 0.4).min(close(
|
||||
c.humidity,
|
||||
CONFIG.forest_hum,
|
||||
0.5,
|
||||
)) * 0.01,
|
||||
Some((48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
// Desert Plants
|
||||
(DeadBush, false, |c, _| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.95).min(close(c.humidity, 0.0, 0.3)) * MUSH_FACT * 15.0,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(LargeCactus, false, |c, _| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
/*(BarrelCactus, false, |c, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(RoundCactus, false, |c, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(ShortCactus, false, |c, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(MedFlatCactus, false, |c, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(ShortFlatCactus, false, |c, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.desert_temp + 0.2, 0.3).min(close(
|
||||
c.humidity,
|
||||
CONFIG.desert_hum,
|
||||
0.2,
|
||||
)) * MUSH_FACT
|
||||
* 0.1,
|
||||
None,
|
||||
)
|
||||
}),*/
|
||||
(Reed, false, |c, col| {
|
||||
(
|
||||
close(c.humidity, CONFIG.jungle_hum, 0.7)
|
||||
* col
|
||||
.water_dist
|
||||
.map(|wd| Lerp::lerp(0.2, 0.0, (wd / 8.0).clamped(0.0, 1.0)))
|
||||
.unwrap_or(0.0),
|
||||
Some((128.0, 0.5)),
|
||||
)
|
||||
}),
|
||||
];
|
||||
|
||||
for y in 0..vol.size_xy().y as i32 {
|
||||
for x in 0..vol.size_xy().x as i32 {
|
||||
let offs = Vec2::new(x, y);
|
||||
|
||||
let wpos2d = wpos2d + offs;
|
||||
|
||||
// Sample terrain
|
||||
let col_sample = if let Some(col_sample) = get_column(offs) {
|
||||
col_sample
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let underwater = col_sample.water_level > col_sample.alt;
|
||||
|
||||
let bk = scatter
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(i, (bk, is_underwater, f))| {
|
||||
let (density, patch) = f(chunk, col_sample);
|
||||
let is_patch = patch
|
||||
.map(|(wavelen, threshold)| {
|
||||
index
|
||||
.noise
|
||||
.scatter_nz
|
||||
.get(
|
||||
wpos2d
|
||||
.map(|e| e as f64 / wavelen as f64 + i as f64 * 43.0)
|
||||
.into_array(),
|
||||
)
|
||||
.abs()
|
||||
> 1.0 - threshold as f64
|
||||
})
|
||||
.unwrap_or(true);
|
||||
if density > 0.0
|
||||
&& is_patch
|
||||
&& RandomField::new(i as u32)
|
||||
.chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
|
||||
&& underwater == *is_underwater
|
||||
{
|
||||
Some(*bk)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(bk) = bk {
|
||||
let alt = col_sample.alt as i32;
|
||||
|
||||
// Find the intersection between ground and air, if there is one near the
|
||||
// surface
|
||||
if let Some(solid_end) = (-4..8)
|
||||
.find(|z| {
|
||||
vol.get(Vec3::new(offs.x, offs.y, alt + z))
|
||||
.map(|b| b.is_solid())
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.and_then(|solid_start| {
|
||||
(1..8).map(|z| solid_start + z).find(|z| {
|
||||
vol.get(Vec3::new(offs.x, offs.y, alt + z))
|
||||
.map(|b| !b.is_solid())
|
||||
.unwrap_or(true)
|
||||
})
|
||||
})
|
||||
{
|
||||
let _ = vol.set(
|
||||
Vec3::new(offs.x, offs.y, alt + solid_end),
|
||||
Block::new(bk, Rgb::broadcast(0)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ mod column;
|
||||
pub mod config;
|
||||
pub mod index;
|
||||
pub mod layer;
|
||||
pub mod rtsim;
|
||||
pub mod sim;
|
||||
pub mod sim2;
|
||||
pub mod site;
|
||||
|
1
world/src/rtsim/mod.rs
Normal file
1
world/src/rtsim/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
|
@ -1657,7 +1657,7 @@ impl WorldSim {
|
||||
|
||||
let new_chunk = this.get(downhill_pos)?;
|
||||
const SLIDE_THRESHOLD: f32 = 5.0;
|
||||
if new_chunk.is_underwater() || new_chunk.alt + SLIDE_THRESHOLD < chunk.alt {
|
||||
if new_chunk.river.near_water() || new_chunk.alt + SLIDE_THRESHOLD < chunk.alt {
|
||||
break;
|
||||
} else {
|
||||
chunk = new_chunk;
|
||||
@ -2184,6 +2184,7 @@ impl SimChunk {
|
||||
humidity,
|
||||
rockiness: if true {
|
||||
(gen_ctx.rock_nz.get((wposf.div(1024.0)).into_array()) as f32)
|
||||
//.add(if river.near_river() { 20.0 } else { 0.0 })
|
||||
.sub(0.1)
|
||||
.mul(1.3)
|
||||
.max(0.0)
|
||||
|
@ -421,9 +421,7 @@ impl Floor {
|
||||
let offs = Vec2::new(rng.gen_range(-1.0, 1.0), rng.gen_range(-1.0, 1.0))
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::unit_y)
|
||||
* FLOOR_SIZE.x as f32
|
||||
/ 2.0
|
||||
- 8.0;
|
||||
* (TILE_SIZE as f32 / 2.0 - 4.0);
|
||||
if !self.final_level {
|
||||
supplement.add_entity(
|
||||
EntityInfo::at((origin + stair_rcenter).map(|e| e as f32) + Vec3::from(offs))
|
||||
|
Loading…
Reference in New Issue
Block a user