Added LoD distance setting

This commit is contained in:
Joshua Barretto 2022-05-10 12:36:44 +01:00
parent add9e5f9fe
commit f35c98d1a1
21 changed files with 206 additions and 140 deletions

View File

@ -59,6 +59,7 @@
"hud.settings.reset_gameplay": "Reset to Defaults",
"hud.settings.view_distance": "View Distance",
"hud.settings.lod_distance": "LoD Distance",
"hud.settings.sprites_view_distance": "Sprites View Distance",
"hud.settings.figures_view_distance": "Entities View Distance",
"hud.settings.maximum_fps": "Maximum FPS",

View File

@ -35,10 +35,12 @@ use common::{
event::{EventBus, LocalEvent},
grid::Grid,
link::Is,
lod,
mounting::Rider,
outcome::Outcome,
recipe::RecipeBook,
resources::{PlayerEntity, TimeOfDay},
spiral::Spiral2d,
terrain::{
block::Block, map::MapConfig, neighbors, BiomeKind, SitesKind, SpriteKind, TerrainChunk,
TerrainChunkSize,
@ -46,8 +48,6 @@ use common::{
trade::{PendingTrade, SitePrices, TradeAction, TradeId, TradeResult},
uid::{Uid, UidAllocator},
vol::RectVolSize,
spiral::Spiral2d,
lod,
};
use common_base::{prof_span, span};
use common_net::{
@ -206,7 +206,7 @@ pub struct Client {
state: State,
view_distance: Option<u32>,
lod_distance: u32,
lod_distance: f32,
// TODO: move into voxygen
loaded_distance: f32,
@ -658,7 +658,7 @@ impl Client {
tick: 0,
state,
view_distance: None,
lod_distance: 4, // TODO: Make configurable
lod_distance: 2.0, // TODO: Make configurable
loaded_distance: 0.0,
pending_chunks: HashMap::new(),
@ -890,6 +890,11 @@ impl Client {
self.send_msg(ClientGeneral::SetViewDistance(view_distance));
}
pub fn set_lod_distance(&mut self, lod_distance: u32) {
let lod_distance = lod_distance.max(1).min(1000) as f32 / lod::ZONE_SIZE as f32;
self.lod_distance = lod_distance;
}
pub fn use_slot(&mut self, slot: Slot) {
self.control_action(ControlAction::InventoryAction(InventoryAction::Use(slot)))
}
@ -1004,9 +1009,7 @@ impl Client {
&self.available_recipes
}
pub fn lod_zones(&self) -> &HashMap<Vec2<i32>, lod::Zone> {
&self.lod_zones
}
pub fn lod_zones(&self) -> &HashMap<Vec2<i32>, lod::Zone> { &self.lod_zones }
/// Returns whether the specified recipe can be crafted and the sprite, if
/// any, that is required to do so.
@ -1728,11 +1731,15 @@ impl Client {
let lod_zone = pos.0.xy().map(|e| lod::from_wpos(e as i32));
// Request LoD zones that are in range
if self.lod_last_requested.map_or(true, |i| i.elapsed() > Duration::from_secs(5)) {
if self
.lod_last_requested
.map_or(true, |i| i.elapsed() > Duration::from_secs(5))
{
if let Some(rpos) = Spiral2d::new()
.take((1 + self.lod_distance * 2).pow(2) as usize)
.take((1 + self.lod_distance.ceil() as i32 * 2).pow(2) as usize)
.filter(|rpos| !self.lod_zones.contains_key(&(lod_zone + *rpos)))
.min_by_key(|rpos| rpos.magnitude_squared())
.filter(|rpos| rpos.map(|e| e as f32).magnitude() < self.lod_distance)
{
self.send_msg_err(ClientGeneral::LodZoneRequest {
key: lod_zone + rpos,
@ -1742,7 +1749,10 @@ impl Client {
}
// Cull LoD zones out of range
self.lod_zones.retain(|p, _| (*p - lod_zone).magnitude_squared() < (self.lod_distance as i32 + 1).pow(2));
self.lod_zones.retain(|p, _| {
(*p - lod_zone).map(|e| e as f32).magnitude_squared()
< (self.lod_distance + 1.0).powi(2)
});
}
Ok(())

View File

@ -7,13 +7,13 @@ use common::{
calendar::Calendar,
character::{self, CharacterItem},
comp::{self, invite::InviteKind, item::MaterialStatManifest},
lod,
outcome::Outcome,
recipe::RecipeBook,
resources::TimeOfDay,
terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
trade::{PendingTrade, SitePrices, TradeId, TradeResult},
uid::Uid,
lod,
};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};

View File

@ -1,10 +1,7 @@
use vek::*;
use serde::{Serialize, Deserialize};
use crate::{terrain::TerrainChunkSize, vol::RectVolSize};
use serde::{Deserialize, Serialize};
use strum::EnumIter;
use crate::{
terrain::TerrainChunkSize,
vol::RectVolSize,
};
use vek::*;
// In chunks
pub const ZONE_SIZE: u32 = 32;
@ -35,9 +32,7 @@ pub struct Zone {
pub objects: Vec<Object>,
}
pub fn to_wpos(wpos: i32) -> i32 {
wpos * (TerrainChunkSize::RECT_SIZE.x * ZONE_SIZE) as i32
}
pub fn to_wpos(wpos: i32) -> i32 { wpos * (TerrainChunkSize::RECT_SIZE.x * ZONE_SIZE) as i32 }
pub fn from_wpos(zone_pos: i32) -> i32 {
zone_pos.div_euclid((TerrainChunkSize::RECT_SIZE.x * ZONE_SIZE) as i32)

View File

@ -450,7 +450,9 @@ impl Server {
// Insert the world into the ECS (todo: Maybe not an Arc?)
let world = Arc::new(world);
state.ecs_mut().insert(Arc::clone(&world));
state.ecs_mut().insert(lod::Lod::from_world(&world, index.as_index_ref()));
state
.ecs_mut()
.insert(lod::Lod::from_world(&world, index.as_index_ref()));
state.ecs_mut().insert(index.clone());
// Set starting time for the server.

View File

@ -1,7 +1,7 @@
use common::lod;
use world::World;
use hashbrown::HashMap;
use vek::*;
use world::World;
static EMPTY_ZONE: lod::Zone = lod::Zone {
objects: Vec::new(),

View File

@ -1,4 +1,6 @@
use crate::{client::Client, lod::Lod, metrics::NetworkRequestMetrics, presence::Presence, ChunkRequest};
use crate::{
client::Client, lod::Lod, metrics::NetworkRequestMetrics, presence::Presence, ChunkRequest,
};
use common::{
comp::Pos,
event::{EventBus, ServerEvent},

View File

@ -39,6 +39,9 @@ widget_ids! {
vd_slider,
vd_text,
vd_value,
ld_slider,
ld_text,
ld_value,
lod_detail_slider,
lod_detail_text,
lod_detail_value,
@ -280,8 +283,6 @@ impl<'a> Widget for Video<'a> {
if let Some(new_val) = ImageSlider::discrete(
self.global_state.settings.graphics.view_distance,
1,
// FIXME: Move back to 64 once we support multiple texture atlases, or figure out a
// way to increase the size of the terrain atlas.
65,
self.imgs.slider_indicator,
self.imgs.slider,
@ -306,9 +307,44 @@ impl<'a> Widget for Video<'a> {
.color(TEXT_COLOR)
.set(state.ids.vd_value, ui);
// LoD Distance
Text::new(self.localized_strings.get("hud.settings.lod_distance"))
.down_from(state.ids.vd_slider, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.ld_text, ui);
if let Some(new_val) = ImageSlider::discrete(
self.global_state.settings.graphics.lod_distance,
0,
250,
self.imgs.slider_indicator,
self.imgs.slider,
)
.w_h(104.0, 22.0)
.down_from(state.ids.ld_text, 8.0)
.track_breadth(12.0)
.slider_length(10.0)
.pad_track((5.0, 5.0))
.set(state.ids.ld_slider, ui)
{
events.push(GraphicsChange::AdjustLodDistance(new_val));
}
Text::new(&format!(
"{}",
self.global_state.settings.graphics.lod_distance
))
.right_from(state.ids.ld_slider, 8.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.ld_value, ui);
// Max FPS
Text::new(self.localized_strings.get("hud.settings.maximum_fps"))
.down_from(state.ids.vd_slider, 10.0)
.down_from(state.ids.ld_slider, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
@ -343,7 +379,7 @@ impl<'a> Widget for Video<'a> {
// Max Background FPS
Text::new(self.localized_strings.get("hud.settings.background_fps"))
.down_from(state.ids.vd_slider, 10.0)
.down_from(state.ids.ld_slider, 10.0)
.right_from(state.ids.max_fps_value, 30.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
@ -391,7 +427,7 @@ impl<'a> Widget for Video<'a> {
// Present Mode
Text::new(self.localized_strings.get("hud.settings.present_mode"))
.down_from(state.ids.vd_slider, 10.0)
.down_from(state.ids.ld_slider, 10.0)
.right_from(state.ids.max_background_fps_value, 30.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)

View File

@ -26,6 +26,7 @@ pub use self::{
Locals as FigureLocals,
},
fluid::Vertex as FluidVertex,
lod_object::{Instance as LodObjectInstance, Vertex as LodObjectVertex},
lod_terrain::{LodData, Vertex as LodTerrainVertex},
particle::{Instance as ParticleInstance, Vertex as ParticleVertex},
postprocess::Locals as PostProcessLocals,
@ -35,10 +36,6 @@ pub use self::{
Instance as SpriteInstance, SpriteGlobalsBindGroup, SpriteVerts,
Vertex as SpriteVertex, VERT_PAGE_SIZE as SPRITE_VERT_PAGE_SIZE,
},
lod_object::{
Instance as LodObjectInstance,
Vertex as LodObjectVertex,
},
terrain::{Locals as TerrainLocals, TerrainLayout, Vertex as TerrainVertex},
trail::Vertex as TrailVertex,
ui::{

View File

@ -37,11 +37,12 @@ impl Vertex {
}
// impl Default for Vertex {
// fn default() -> Self { Self::new(Vec2::zero(), Vec3::zero(), Vec3::zero()) }
// }
// fn default() -> Self { Self::new(Vec2::zero(), Vec3::zero(),
// Vec3::zero()) } }
impl VertexTrait for Vertex {
const QUADS_INDEX: Option<wgpu::IndexFormat> = None;//Some(wgpu::IndexFormat::Uint16);
const QUADS_INDEX: Option<wgpu::IndexFormat> = None;
//Some(wgpu::IndexFormat::Uint16);
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
}
@ -53,10 +54,7 @@ pub struct Instance {
}
impl Instance {
pub fn new(
inst_pos: Vec3<f32>,
flags: common::lod::Flags,
) -> Self {
pub fn new(inst_pos: Vec3<f32>, flags: common::lod::Flags) -> Self {
Self {
inst_pos: inst_pos.into_array(),
flags: flags.bits() as u32,
@ -77,8 +75,8 @@ impl Instance {
}
// impl Default for Instance {
// fn default() -> Self { Self::new(Mat4::identity(), 0.0, 0.0, Vec3::zero(), 0, 1.0, 0.0, 0) }
// }
// fn default() -> Self { Self::new(Mat4::identity(), 0.0, 0.0,
// Vec3::zero(), 0, 1.0, 0.0, 0) } }
// TODO: ColLightsWrapper instead?
pub struct Locals;
@ -100,10 +98,7 @@ impl LodObjectPipeline {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("LoD object pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[
&global_layout.globals,
&global_layout.shadow_textures,
],
bind_group_layouts: &[&global_layout.globals, &global_layout.shadow_textures],
});
let samples = match aa_mode {

View File

@ -21,7 +21,7 @@ use super::{
mesh::Mesh,
model::{DynamicModel, Model},
pipelines::{
blit, bloom, clouds, debug, figure, postprocess, shadow, sprite, lod_object, terrain, ui,
blit, bloom, clouds, debug, figure, lod_object, postprocess, shadow, sprite, terrain, ui,
GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup,
},
texture::Texture,

View File

@ -1,8 +1,8 @@
use super::{
super::{
pipelines::{
debug, figure, lod_terrain, shadow, sprite, lod_object, terrain, ui, ColLights, GlobalModel,
GlobalsBindGroup,
debug, figure, lod_object, lod_terrain, shadow, sprite, terrain, ui, ColLights,
GlobalModel, GlobalsBindGroup,
},
texture::Texture,
},

View File

@ -4,8 +4,9 @@ use super::{
instances::Instances,
model::{DynamicModel, Model, SubModel},
pipelines::{
blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox,
sprite, lod_object, terrain, trail, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup,
blit, bloom, clouds, debug, figure, fluid, lod_object, lod_terrain, particle, shadow,
skybox, sprite, terrain, trail, ui, ColLights, GlobalsBindGroup,
ShadowTexturesBindGroup,
},
},
Renderer, ShadowMap, ShadowMapRenderer,
@ -764,9 +765,7 @@ impl<'pass> FirstPassDrawer<'pass> {
}
}
pub fn draw_lod_objects<'data: 'pass>(
&mut self,
) -> LodObjectDrawer<'_, 'pass> {
pub fn draw_lod_objects<'data: 'pass>(&mut self) -> LodObjectDrawer<'_, 'pass> {
let mut render_pass = self.render_pass.scope("lod objects", self.borrow.device);
render_pass.set_pipeline(&self.pipelines.lod_object.pipeline);
@ -934,10 +933,8 @@ impl<'pass_ref, 'pass: 'pass_ref> LodObjectDrawer<'pass_ref, 'pass> {
self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
self.render_pass
.set_vertex_buffer(1, instances.buf().slice(..));
self.render_pass.draw(
0..model.len() as u32,
0..instances.count() as u32,
);
self.render_pass
.draw(0..model.len() as u32, 0..instances.count() as u32);
}
}

View File

@ -1,8 +1,8 @@
use super::{
super::{
pipelines::{
blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow,
skybox, sprite, lod_object, terrain, trail, ui,
blit, bloom, clouds, debug, figure, fluid, lod_object, lod_terrain, particle,
postprocess, shadow, skybox, sprite, terrain, trail, ui,
},
AaMode, BloomMode, CloudMode, FluidMode, LightingMode, PipelineModes, RenderError,
ShadowMode,
@ -763,7 +763,10 @@ fn create_ingame_and_shadow_pipelines(
((debug, (skybox, figure)), (terrain, (fluid, bloom))),
((sprite, particle), (lod_terrain, (clouds, trail))),
),
(((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)), lod_object),
(
((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)),
lod_object,
),
) = pool.join(
|| pool.join(|| pool.join(j1, j2), || pool.join(j3, j4)),
|| pool.join(|| pool.join(j5, j6), j7),

View File

@ -1,19 +1,20 @@
use crate::{
render::{
pipelines::lod_terrain::{LodData, Vertex},
FirstPassDrawer, LodTerrainVertex, LodObjectVertex, Mesh, Model, Quad, Renderer, Instances, LodObjectInstance, Tri,
FirstPassDrawer, Instances, LodObjectInstance, LodObjectVertex, LodTerrainVertex, Mesh,
Model, Quad, Renderer, Tri,
},
scene::GlobalModel,
settings::Settings,
};
use hashbrown::HashMap;
use client::Client;
use common::{
assets::{ObjAsset, AssetExt},
assets::{AssetExt, ObjAsset},
lod,
spiral::Spiral2d,
util::srgba_to_linear,
lod,
};
use hashbrown::HashMap;
use vek::*;
pub struct Lod {
@ -31,11 +32,7 @@ pub fn water_color() -> Rgba<f32> {
}
impl Lod {
pub fn new(
renderer: &mut Renderer,
client: &Client,
settings: &Settings,
) -> Self {
pub fn new(renderer: &mut Renderer, client: &Client, settings: &Settings) -> Self {
let data = LodData::new(
renderer,
client.world_data().chunk_size().as_(),
@ -44,16 +41,22 @@ impl Lod {
client.world_data().lod_horizon.raw(),
settings.graphics.lod_detail.max(100).min(2500),
/* TODO: figure out how we want to do this without color borders?
* water_color().into_array().into(), */
* water_color().into_array().into(), */
);
Self {
zone_objects: HashMap::new(),
object_data: [
(lod::ObjectKind::Oak, make_lod_object("oak", renderer, &data)),
(lod::ObjectKind::Pine, make_lod_object("pine", renderer, &data)),
(
lod::ObjectKind::Oak,
make_lod_object("oak", renderer, &data),
),
(
lod::ObjectKind::Pine,
make_lod_object("pine", renderer, &data),
),
]
.into_iter()
.collect(),
.into_iter()
.collect(),
model: None,
data,
}
@ -98,13 +101,19 @@ impl Lod {
objects
.into_iter()
.map(|(kind, instances)| {
(kind, renderer.create_instances(&instances).expect("Renderer error?!"))
(
kind,
renderer
.create_instances(&instances)
.expect("Renderer error?!"),
)
})
.collect()
});
}
self.zone_objects.retain(|p, _| client.lod_zones().contains_key(p));
self.zone_objects
.retain(|p, _| client.lod_zones().contains_key(p));
}
pub fn render<'a>(&'a self, drawer: &mut FirstPassDrawer<'a>) {
@ -159,19 +168,20 @@ fn make_lod_object(
) -> Model<LodObjectVertex> {
let model = ObjAsset::load_expect(&format!("voxygen.lod.{}", name));
let mesh = model
.read().0
.read()
.0
.triangles()
.map(|vs| {
let [a, b, c] = vs.map(|v| LodObjectVertex::new(
v.position().into(),
v.normal().unwrap_or([0.0, 0.0, 1.0]).into(),
Vec3::broadcast(1.0),
//v.color().unwrap_or([1.0; 3]).into(),
));
let [a, b, c] = vs.map(|v| {
LodObjectVertex::new(
v.position().into(),
v.normal().unwrap_or([0.0, 0.0, 1.0]).into(),
Vec3::broadcast(1.0),
//v.color().unwrap_or([1.0; 3]).into(),
)
});
Tri::new(a, b, c)
})
.collect();
renderer
.create_model(&mesh)
.expect("Mesh was empty!")
renderer.create_model(&mesh).expect("Mesh was empty!")
}

View File

@ -69,6 +69,7 @@ pub enum Gameplay {
#[derive(Clone)]
pub enum Graphics {
AdjustViewDistance(u32),
AdjustLodDistance(u32),
AdjustLodDetail(u32),
AdjustSpriteRenderDistance(u32),
AdjustFigureLoDRenderDistance(u32),
@ -344,6 +345,14 @@ impl SettingsChange {
settings.graphics.view_distance = view_distance;
},
Graphics::AdjustLodDistance(lod_distance) => {
session_state
.client
.borrow_mut()
.set_lod_distance(lod_distance);
settings.graphics.lod_distance = lod_distance;
},
Graphics::AdjustLodDetail(lod_detail) => {
session_state.scene.lod.set_detail(lod_detail);

View File

@ -30,6 +30,7 @@ impl fmt::Display for Fps {
#[serde(default)]
pub struct GraphicsSettings {
pub view_distance: u32,
pub lod_distance: u32,
pub sprite_render_distance: u32,
pub particles_enabled: bool,
pub lossy_terrain_compression: bool,
@ -51,6 +52,7 @@ impl Default for GraphicsSettings {
fn default() -> Self {
Self {
view_distance: 10,
lod_distance: 100,
sprite_render_distance: 100,
particles_enabled: true,
lossy_terrain_compression: false,

View File

@ -3,8 +3,7 @@ use crate::{
block::block_from_structure,
column::ColumnGen,
util::{gen_cache::StructureGenCache, RandomPerm, Sampler, UnitChooser},
Canvas,
ColumnSample,
Canvas, ColumnSample,
};
use common::{
assets::AssetHandle,
@ -41,7 +40,7 @@ pub fn tree_valid_at(col: &ColumnSample, seed: u32) -> bool {
|| col.water_dist.map(|d| d < 8.0).unwrap_or(false)
|| col.path.map(|(d, _, _, _)| d < 12.0).unwrap_or(false)
{
return false
return false;
}
if ((seed.wrapping_mul(13)) & 0xFF) as f32 / 256.0 > col.tree_density {

View File

@ -51,12 +51,12 @@ use common::{
assets,
calendar::Calendar,
generation::{ChunkSupplement, EntityInfo},
lod,
resources::TimeOfDay,
terrain::{
Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize, TerrainGrid,
},
vol::{ReadVol, RectVolSize, WriteVol},
lod,
};
use common_net::msg::{world_msg, WorldMapMsg};
use rand::{prelude::*, Rng};
@ -467,45 +467,51 @@ impl World {
}
// Zone coordinates
pub fn get_lod_zone(
&self,
pos: Vec2<i32>,
index: IndexRef,
) -> lod::Zone {
pub fn get_lod_zone(&self, pos: Vec2<i32>, index: IndexRef) -> lod::Zone {
let min_wpos = pos.map(lod::to_wpos);
let max_wpos = (pos + 1).map(lod::to_wpos);
let mut objects = Vec::new();
objects.append(&mut self.sim()
.get_area_trees(min_wpos, max_wpos)
.filter_map(|attr| {
ColumnGen::new(self.sim()).get((attr.pos, index, self.sim().calendar.as_ref()))
.filter(|col| layer::tree::tree_valid_at(col, attr.seed))
.zip(Some(attr))
})
.filter_map(|(col, tree)| Some(lod::Object {
kind: match tree.forest_kind {
all::ForestKind::Oak => lod::ObjectKind::Oak,
all::ForestKind::Pine
| all::ForestKind::Frostpine=> lod::ObjectKind::Pine,
_ => lod::ObjectKind::Oak,
},
pos: {
let rpos = tree.pos - min_wpos;
if rpos.is_any_negative() {
return None
} else {
rpos
.map(|e| e as u16)
.with_z(self.sim().get_alt_approx(tree.pos).unwrap_or(0.0) as u16)
}
},
flags: lod::Flags::empty()
| if col.snow_cover { lod::Flags::SNOW_COVERED } else { lod::Flags::empty() }
,
}))
.collect());
objects.append(
&mut self
.sim()
.get_area_trees(min_wpos, max_wpos)
.filter_map(|attr| {
ColumnGen::new(self.sim())
.get((attr.pos, index, self.sim().calendar.as_ref()))
.filter(|col| layer::tree::tree_valid_at(col, attr.seed))
.zip(Some(attr))
})
.filter_map(|(col, tree)| {
Some(lod::Object {
kind: match tree.forest_kind {
all::ForestKind::Oak => lod::ObjectKind::Oak,
all::ForestKind::Pine | all::ForestKind::Frostpine => {
lod::ObjectKind::Pine
},
_ => lod::ObjectKind::Oak,
},
pos: {
let rpos = tree.pos - min_wpos;
if rpos.is_any_negative() {
return None;
} else {
rpos.map(|e| e as u16).with_z(
self.sim().get_alt_approx(tree.pos).unwrap_or(0.0) as u16,
)
}
},
flags: lod::Flags::empty()
| if col.snow_cover {
lod::Flags::SNOW_COVERED
} else {
lod::Flags::empty()
},
})
})
.collect(),
);
lod::Zone { objects }
}

View File

@ -2150,7 +2150,11 @@ impl WorldSim {
})
}
pub fn get_area_trees(&self, wpos_min: Vec2<i32>, wpos_max: Vec2<i32>) -> impl ParallelIterator<Item = TreeAttr> + '_ {
pub fn get_area_trees(
&self,
wpos_min: Vec2<i32>,
wpos_max: Vec2<i32>,
) -> impl ParallelIterator<Item = TreeAttr> + '_ {
self.gen_ctx
.structure_gen
.par_iter(wpos_min, wpos_max)

View File

@ -102,21 +102,19 @@ impl StructureGen2d {
let x_field = self.x_field;
let y_field = self.y_field;
let seed_field = self.seed_field;
(0..len)
.into_par_iter()
.map(move |xy| {
let index = min_index + Vec2::new((xy % xlen as u64) as i32, (xy / xlen as u64) as i32);
Self::index_to_sample_internal(
freq,
freq_offset,
spread,
spread_mul,
x_field,
y_field,
seed_field,
index,
)
})
(0..len).into_par_iter().map(move |xy| {
let index = min_index + Vec2::new((xy % xlen as u64) as i32, (xy / xlen as u64) as i32);
Self::index_to_sample_internal(
freq,
freq_offset,
spread,
spread_mul,
x_field,
y_field,
seed_field,
index,
)
})
}
}