mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/worldgen' into 'master'
Better mesh scheduling, block picking, worldgen speedup See merge request veloren/veloren!298
This commit is contained in:
commit
9e90c89844
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2665,6 +2665,7 @@ dependencies = [
|
|||||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"frustum_query 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"frustum_query 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gfx 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gfx 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gfx_device_gl 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gfx_device_gl 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gfx_window_glutin 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gfx_window_glutin 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -55,3 +55,4 @@ rand = "0.5"
|
|||||||
frustum_query = "0.1.2"
|
frustum_query = "0.1.2"
|
||||||
rodio = { git = "https://github.com/desttinghim/rodio.git", rev = "dd93f905c1afefaac03c496a666ecab27d3e391b" }
|
rodio = { git = "https://github.com/desttinghim/rodio.git", rev = "dd93f905c1afefaac03c496a666ecab27d3e391b" }
|
||||||
crossbeam = "^0.7.1"
|
crossbeam = "^0.7.1"
|
||||||
|
fxhash = "0.2"
|
||||||
|
@ -3,7 +3,7 @@ const float PI = 3.141592;
|
|||||||
const vec3 SKY_DAY_TOP = vec3(0.2, 0.3, 0.9);
|
const vec3 SKY_DAY_TOP = vec3(0.2, 0.3, 0.9);
|
||||||
const vec3 SKY_DAY_MID = vec3(0.15, 0.2, 0.8);
|
const vec3 SKY_DAY_MID = vec3(0.15, 0.2, 0.8);
|
||||||
const vec3 SKY_DAY_BOT = vec3(0.02, 0.1, 0.3);
|
const vec3 SKY_DAY_BOT = vec3(0.02, 0.1, 0.3);
|
||||||
const vec3 DAY_LIGHT = vec3(0.5, 0.5, 0.8);
|
const vec3 DAY_LIGHT = vec3(0.5, 0.5, 1.0);
|
||||||
|
|
||||||
const vec3 SKY_DUSK_TOP = vec3(0.1, 0.15, 0.3);
|
const vec3 SKY_DUSK_TOP = vec3(0.1, 0.15, 0.3);
|
||||||
const vec3 SKY_DUSK_MID = vec3(0.9, 0.3, 0.2);
|
const vec3 SKY_DUSK_MID = vec3(0.9, 0.3, 0.2);
|
||||||
@ -25,13 +25,13 @@ vec3 get_sun_dir(float time_of_day) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float get_sun_brightness(vec3 sun_dir) {
|
float get_sun_brightness(vec3 sun_dir) {
|
||||||
return max(-sun_dir.z + 0.6, 0.0) * 0.8;
|
return max(-sun_dir.z + 0.6, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const float PERSISTENT_AMBIANCE = 0.008;
|
const float PERSISTENT_AMBIANCE = 0.008;
|
||||||
|
|
||||||
vec3 get_sun_diffuse(vec3 norm, float time_of_day) {
|
vec3 get_sun_diffuse(vec3 norm, float time_of_day) {
|
||||||
const float SUN_AMBIANCE = 0.2;
|
const float SUN_AMBIANCE = 0.1;
|
||||||
|
|
||||||
vec3 sun_dir = get_sun_dir(time_of_day);
|
vec3 sun_dir = get_sun_dir(time_of_day);
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@ use common::{
|
|||||||
volumes::vol_map_2d::VolMap2dErr,
|
volumes::vol_map_2d::VolMap2dErr,
|
||||||
};
|
};
|
||||||
use frustum_query::frustum::Frustum;
|
use frustum_query::frustum::Frustum;
|
||||||
use std::{collections::HashMap, i32, ops::Mul, sync::mpsc, time::Duration};
|
use fxhash::FxHashMap;
|
||||||
|
use std::{i32, ops::Mul, sync::mpsc, time::Duration};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
struct TerrainChunk {
|
struct TerrainChunk {
|
||||||
@ -23,7 +24,7 @@ struct TerrainChunk {
|
|||||||
struct ChunkMeshState {
|
struct ChunkMeshState {
|
||||||
pos: Vec2<i32>,
|
pos: Vec2<i32>,
|
||||||
started_tick: u64,
|
started_tick: u64,
|
||||||
active_worker: bool,
|
active_worker: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type produced by mesh worker threads corresponding to the position and mesh of a chunk.
|
/// A type produced by mesh worker threads corresponding to the position and mesh of a chunk.
|
||||||
@ -51,13 +52,13 @@ fn mesh_worker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Terrain {
|
pub struct Terrain {
|
||||||
chunks: HashMap<Vec2<i32>, TerrainChunk>,
|
chunks: FxHashMap<Vec2<i32>, TerrainChunk>,
|
||||||
|
|
||||||
// The mpsc sender and receiver used for talking to meshing worker threads.
|
// The mpsc sender and receiver used for talking to meshing worker threads.
|
||||||
// We keep the sender component for no reason other than to clone it and send it to new workers.
|
// We keep the sender component for no reason other than to clone it and send it to new workers.
|
||||||
mesh_send_tmp: mpsc::Sender<MeshWorkerResponse>,
|
mesh_send_tmp: mpsc::Sender<MeshWorkerResponse>,
|
||||||
mesh_recv: mpsc::Receiver<MeshWorkerResponse>,
|
mesh_recv: mpsc::Receiver<MeshWorkerResponse>,
|
||||||
mesh_todo: HashMap<Vec2<i32>, ChunkMeshState>,
|
mesh_todo: FxHashMap<Vec2<i32>, ChunkMeshState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Terrain {
|
impl Terrain {
|
||||||
@ -67,11 +68,10 @@ impl Terrain {
|
|||||||
let (send, recv) = mpsc::channel();
|
let (send, recv) = mpsc::channel();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
chunks: HashMap::new(),
|
chunks: FxHashMap::default(),
|
||||||
|
|
||||||
mesh_send_tmp: send,
|
mesh_send_tmp: send,
|
||||||
mesh_recv: recv,
|
mesh_recv: recv,
|
||||||
mesh_todo: HashMap::new(),
|
mesh_todo: FxHashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ impl Terrain {
|
|||||||
ChunkMeshState {
|
ChunkMeshState {
|
||||||
pos,
|
pos,
|
||||||
started_tick: current_tick,
|
started_tick: current_tick,
|
||||||
active_worker: false,
|
active_worker: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -146,9 +146,17 @@ impl Terrain {
|
|||||||
for todo in self
|
for todo in self
|
||||||
.mesh_todo
|
.mesh_todo
|
||||||
.values_mut()
|
.values_mut()
|
||||||
// Only spawn workers for meshing jobs without an active worker already.
|
.filter(|todo| {
|
||||||
.filter(|todo| !todo.active_worker)
|
todo.active_worker
|
||||||
|
.map(|worker_tick| worker_tick < todo.started_tick)
|
||||||
|
.unwrap_or(true)
|
||||||
|
})
|
||||||
|
.min_by_key(|todo| todo.active_worker.unwrap_or(todo.started_tick))
|
||||||
{
|
{
|
||||||
|
if client.thread_pool().queued_count() > 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Find the area of the terrain we want. Because meshing needs to compute things like
|
// Find the area of the terrain we want. Because meshing needs to compute things like
|
||||||
// ambient occlusion and edge elision, we also need the borders of the chunk's
|
// ambient occlusion and edge elision, we also need the borders of the chunk's
|
||||||
// neighbours too (hence the `- 1` and `+ 1`).
|
// neighbours too (hence the `- 1` and `+ 1`).
|
||||||
@ -189,16 +197,17 @@ impl Terrain {
|
|||||||
let pos = todo.pos;
|
let pos = todo.pos;
|
||||||
|
|
||||||
// Queue the worker thread.
|
// Queue the worker thread.
|
||||||
|
let started_tick = todo.started_tick;
|
||||||
client.thread_pool().execute(move || {
|
client.thread_pool().execute(move || {
|
||||||
let _ = send.send(mesh_worker(
|
let _ = send.send(mesh_worker(
|
||||||
pos,
|
pos,
|
||||||
(min_z as f32, max_z as f32),
|
(min_z as f32, max_z as f32),
|
||||||
current_tick,
|
started_tick,
|
||||||
volume,
|
volume,
|
||||||
aabb,
|
aabb,
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
todo.active_worker = true;
|
todo.active_worker = Some(todo.started_tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receive a chunk mesh from a worker thread and upload it to the GPU, then store it.
|
// Receive a chunk mesh from a worker thread and upload it to the GPU, then store it.
|
||||||
@ -208,7 +217,7 @@ impl Terrain {
|
|||||||
match self.mesh_todo.get(&response.pos) {
|
match self.mesh_todo.get(&response.pos) {
|
||||||
// It's the mesh we want, insert the newly finished model into the terrain model
|
// It's the mesh we want, insert the newly finished model into the terrain model
|
||||||
// data structure (convert the mesh to a model first of course).
|
// data structure (convert the mesh to a model first of course).
|
||||||
Some(todo) if response.started_tick == todo.started_tick => {
|
Some(todo) if response.started_tick <= todo.started_tick => {
|
||||||
self.chunks.insert(
|
self.chunks.insert(
|
||||||
response.pos,
|
response.pos,
|
||||||
TerrainChunk {
|
TerrainChunk {
|
||||||
@ -230,7 +239,9 @@ impl Terrain {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
self.mesh_todo.remove(&response.pos);
|
if response.started_tick == todo.started_tick {
|
||||||
|
self.mesh_todo.remove(&response.pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Chunk must have been removed, or it was spawned on an old tick. Drop the mesh
|
// Chunk must have been removed, or it was spawned on an old tick. Drop the mesh
|
||||||
// since it's either out of date or no longer needed.
|
// since it's either out of date or no longer needed.
|
||||||
|
@ -8,9 +8,7 @@ use crate::{
|
|||||||
Direction, Error, GlobalState, PlayState, PlayStateResult,
|
Direction, Error, GlobalState, PlayState, PlayStateResult,
|
||||||
};
|
};
|
||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use common::{
|
use common::{clock::Clock, comp, comp::Pos, msg::ClientState, terrain::Block, vol::ReadVol};
|
||||||
clock::Clock, comp, comp::Pos, msg::ClientState, terrain::block::Block, vol::ReadVol,
|
|
||||||
};
|
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use std::{cell::RefCell, rc::Rc, time::Duration};
|
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -21,6 +19,7 @@ pub struct SessionState {
|
|||||||
hud: Hud,
|
hud: Hud,
|
||||||
key_state: KeyState,
|
key_state: KeyState,
|
||||||
controller: comp::Controller,
|
controller: comp::Controller,
|
||||||
|
selected_block: Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an active game session (i.e., the one being played).
|
/// Represents an active game session (i.e., the one being played).
|
||||||
@ -35,6 +34,7 @@ impl SessionState {
|
|||||||
key_state: KeyState::new(),
|
key_state: KeyState::new(),
|
||||||
controller: comp::Controller::default(),
|
controller: comp::Controller::default(),
|
||||||
hud: Hud::new(window),
|
hud: Hud::new(window),
|
||||||
|
selected_block: Block::new(1, Rgb::broadcast(255)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,7 +127,7 @@ impl PlayState for SessionState {
|
|||||||
if b {
|
if b {
|
||||||
let pos =
|
let pos =
|
||||||
(cam_pos + cam_dir * (d - 0.01)).map(|e| e.floor() as i32);
|
(cam_pos + cam_dir * (d - 0.01)).map(|e| e.floor() as i32);
|
||||||
client.place_block(pos, Block::new(1, Rgb::broadcast(255))); // TODO: Handle block color with a command
|
client.place_block(pos, self.selected_block); // TODO: Handle block color with a command
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.controller.attack = state
|
self.controller.attack = state
|
||||||
@ -164,7 +164,39 @@ impl PlayState for SessionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::InputUpdate(GameInput::Roll, state) => {
|
Event::InputUpdate(GameInput::Roll, state) => {
|
||||||
self.controller.roll = state;
|
let client = self.client.borrow();
|
||||||
|
if client
|
||||||
|
.state()
|
||||||
|
.read_storage::<comp::CanBuild>()
|
||||||
|
.get(client.entity())
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
if state {
|
||||||
|
let cam_pos = self.scene.camera().compute_dependents(&client).2;
|
||||||
|
let cam_dir =
|
||||||
|
(self.scene.camera().get_focus_pos() - cam_pos).normalized();
|
||||||
|
|
||||||
|
let (d, block) = {
|
||||||
|
let terrain = client.state().terrain();
|
||||||
|
let ray =
|
||||||
|
terrain.ray(cam_pos, cam_pos + cam_dir * 100.0).cast();
|
||||||
|
(
|
||||||
|
ray.0,
|
||||||
|
if let Ok(Some(b)) = ray.1 {
|
||||||
|
Some(*b)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(block) = block {
|
||||||
|
self.selected_block = block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.controller.roll = state;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Event::InputUpdate(GameInput::Jump, state) => {
|
Event::InputUpdate(GameInput::Jump, state) => {
|
||||||
self.controller.jump = state;
|
self.controller.jump = state;
|
||||||
|
@ -74,20 +74,41 @@ impl<'a> BlockGen<'a> {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> SamplerMut for BlockGen<'a> {
|
pub fn get_z_cache(&mut self, wpos: Vec2<i32>) -> Option<ZCache<'a>> {
|
||||||
type Index = Vec3<i32>;
|
|
||||||
type Sample = Option<Block>;
|
|
||||||
|
|
||||||
fn get(&mut self, wpos: Vec3<i32>) -> Option<Block> {
|
|
||||||
let BlockGen {
|
let BlockGen {
|
||||||
world,
|
world,
|
||||||
column_cache,
|
column_cache,
|
||||||
column_gen,
|
column_gen,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let ColumnSample {
|
// Main sample
|
||||||
|
let sample = Self::sample_column(column_gen, column_cache, wpos)?;
|
||||||
|
|
||||||
|
// Tree samples
|
||||||
|
let mut tree_samples = [None, None, None, None, None, None, None, None, None];
|
||||||
|
for i in 0..tree_samples.len() {
|
||||||
|
tree_samples[i] = Self::sample_column(
|
||||||
|
column_gen,
|
||||||
|
column_cache,
|
||||||
|
Vec2::from(sample.close_trees[i].0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(ZCache {
|
||||||
|
sample,
|
||||||
|
tree_samples,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_with_z_cache(&mut self, wpos: Vec3<i32>, z_cache: Option<&ZCache>) -> Option<Block> {
|
||||||
|
let BlockGen {
|
||||||
|
world,
|
||||||
|
column_cache,
|
||||||
|
column_gen,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let &ColumnSample {
|
||||||
alt,
|
alt,
|
||||||
chaos,
|
chaos,
|
||||||
water_level,
|
water_level,
|
||||||
@ -105,8 +126,11 @@ impl<'a> SamplerMut for BlockGen<'a> {
|
|||||||
close_cliffs,
|
close_cliffs,
|
||||||
//temp,
|
//temp,
|
||||||
..
|
..
|
||||||
} = Self::sample_column(column_gen, column_cache, Vec2::from(wpos))?;
|
} = &z_cache?.sample;
|
||||||
|
|
||||||
|
let tree_samples = &z_cache?.tree_samples;
|
||||||
|
|
||||||
|
let wpos2d = Vec2::from(wpos);
|
||||||
let wposf = wpos.map(|e| e as f64);
|
let wposf = wpos.map(|e| e as f64);
|
||||||
|
|
||||||
let (definitely_underground, height, water_height) =
|
let (definitely_underground, height, water_height) =
|
||||||
@ -285,22 +309,19 @@ impl<'a> SamplerMut for BlockGen<'a> {
|
|||||||
} else {
|
} else {
|
||||||
match block {
|
match block {
|
||||||
Some(block) => block,
|
Some(block) => block,
|
||||||
None => (&close_trees)
|
None => (&close_trees).iter().enumerate().fold(
|
||||||
.iter()
|
air,
|
||||||
.fold(air, |block, (tree_pos, tree_seed)| {
|
|block, (tree_idx, (tree_pos, tree_seed))| {
|
||||||
if !block.is_empty() {
|
if !block.is_empty() {
|
||||||
block
|
block
|
||||||
} else {
|
} else {
|
||||||
match Self::sample_column(
|
match &tree_samples[tree_idx] {
|
||||||
column_gen,
|
|
||||||
column_cache,
|
|
||||||
Vec2::from(*tree_pos),
|
|
||||||
) {
|
|
||||||
Some(tree_sample)
|
Some(tree_sample)
|
||||||
if tree_sample.tree_density
|
if tree_sample.tree_density
|
||||||
> 0.5 + (*tree_seed as f32 / 1000.0).fract() * 0.2
|
> 0.5 + (*tree_seed as f32 / 1000.0).fract() * 0.2
|
||||||
&& tree_sample.alt > tree_sample.water_level
|
&& tree_sample.alt > tree_sample.water_level
|
||||||
&& tree_sample.spawn_rate > 0.5 =>
|
&& tree_sample.spawn_rate > 0.5
|
||||||
|
&& wpos2d.distance_squared(*tree_pos) < 20 * 20 =>
|
||||||
{
|
{
|
||||||
let cliff_height = Self::get_cliff_height(
|
let cliff_height = Self::get_cliff_height(
|
||||||
column_gen,
|
column_gen,
|
||||||
@ -332,10 +353,26 @@ impl<'a> SamplerMut for BlockGen<'a> {
|
|||||||
_ => block,
|
_ => block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(block)
|
Some(block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ZCache<'a> {
|
||||||
|
sample: ColumnSample<'a>,
|
||||||
|
tree_samples: [Option<ColumnSample<'a>>; 9],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SamplerMut for BlockGen<'a> {
|
||||||
|
type Index = Vec3<i32>;
|
||||||
|
type Sample = Option<Block>;
|
||||||
|
|
||||||
|
fn get(&mut self, wpos: Vec3<i32>) -> Option<Block> {
|
||||||
|
let z_cache = self.get_z_cache(wpos.into());
|
||||||
|
self.get_with_z_cache(wpos, z_cache.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -95,11 +95,11 @@ impl<'a> Sampler for ColumnGen<'a> {
|
|||||||
.mul(0.75)
|
.mul(0.75)
|
||||||
.add(1.0)
|
.add(1.0)
|
||||||
.mul(0.5)
|
.mul(0.5)
|
||||||
.add(marble_small.mul(0.25));
|
.add(marble_small.sub(0.5).mul(0.25));
|
||||||
|
|
||||||
// Colours
|
// Colours
|
||||||
let cold_grass = Rgb::new(0.05, 0.2, 0.1);
|
let cold_grass = Rgb::new(0.0, 0.25, 0.13);
|
||||||
let warm_grass = Rgb::new(0.15, 0.65, 0.05);
|
let warm_grass = Rgb::new(0.18, 0.65, 0.0);
|
||||||
let cold_stone = Rgb::new(0.55, 0.7, 0.75);
|
let cold_stone = Rgb::new(0.55, 0.7, 0.75);
|
||||||
let warm_stone = Rgb::new(0.65, 0.65, 0.35);
|
let warm_stone = Rgb::new(0.65, 0.65, 0.35);
|
||||||
let beach_sand = Rgb::new(0.93, 0.84, 0.4);
|
let beach_sand = Rgb::new(0.93, 0.84, 0.4);
|
||||||
@ -165,7 +165,6 @@ impl<'a> Sampler for ColumnGen<'a> {
|
|||||||
|
|
||||||
// Cities
|
// Cities
|
||||||
// TODO: In a later MR
|
// TODO: In a later MR
|
||||||
/*
|
|
||||||
let building = match &sim_chunk.location {
|
let building = match &sim_chunk.location {
|
||||||
Some(loc) => {
|
Some(loc) => {
|
||||||
let loc = &sim.locations[loc.loc_idx];
|
let loc = &sim.locations[loc.loc_idx];
|
||||||
@ -184,7 +183,6 @@ impl<'a> Sampler for ColumnGen<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let alt = alt + building;
|
let alt = alt + building;
|
||||||
*/
|
|
||||||
|
|
||||||
// Caves
|
// Caves
|
||||||
let cave_at = |wposf: Vec2<f64>| {
|
let cave_at = |wposf: Vec2<f64>| {
|
||||||
|
@ -52,7 +52,7 @@ impl World {
|
|||||||
ColumnGen::new(self)
|
ColumnGen::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample_blocks(&self) -> impl SamplerMut<Index = Vec3<i32>, Sample = Option<Block>> + '_ {
|
pub fn sample_blocks(&self) -> BlockGen {
|
||||||
BlockGen::new(self, ColumnGen::new(self))
|
BlockGen::new(self, ColumnGen::new(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,12 +96,14 @@ impl World {
|
|||||||
.get_interpolated(wpos2d, |chunk| chunk.get_max_z())
|
.get_interpolated(wpos2d, |chunk| chunk.get_max_z())
|
||||||
.unwrap_or(0.0) as i32;
|
.unwrap_or(0.0) as i32;
|
||||||
|
|
||||||
|
let z_cache = sampler.get_z_cache(wpos2d);
|
||||||
|
|
||||||
for z in min_z..max_z {
|
for z in min_z..max_z {
|
||||||
let lpos = Vec3::new(x, y, z);
|
let lpos = Vec3::new(x, y, z);
|
||||||
let wpos =
|
let wpos =
|
||||||
lpos + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32);
|
lpos + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32);
|
||||||
|
|
||||||
if let Some(block) = sampler.get(wpos) {
|
if let Some(block) = sampler.get_with_z_cache(wpos, z_cache.as_ref()) {
|
||||||
let _ = chunk.set(lpos, block);
|
let _ = chunk.set(lpos, block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,9 +120,9 @@ impl WorldSim {
|
|||||||
pub fn seed_elements(&mut self) {
|
pub fn seed_elements(&mut self) {
|
||||||
let mut rng = self.rng.clone();
|
let mut rng = self.rng.clone();
|
||||||
|
|
||||||
let cell_size = 32;
|
let cell_size = 16;
|
||||||
let grid_size = WORLD_SIZE / cell_size;
|
let grid_size = WORLD_SIZE / cell_size;
|
||||||
let loc_count = 250;
|
let loc_count = 100;
|
||||||
|
|
||||||
let mut loc_grid = vec![None; grid_size.product()];
|
let mut loc_grid = vec![None; grid_size.product()];
|
||||||
let mut locations = Vec::new();
|
let mut locations = Vec::new();
|
||||||
|
Loading…
Reference in New Issue
Block a user