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.reset_gameplay": "Reset to Defaults",
"hud.settings.view_distance": "View Distance", "hud.settings.view_distance": "View Distance",
"hud.settings.lod_distance": "LoD Distance",
"hud.settings.sprites_view_distance": "Sprites View Distance", "hud.settings.sprites_view_distance": "Sprites View Distance",
"hud.settings.figures_view_distance": "Entities View Distance", "hud.settings.figures_view_distance": "Entities View Distance",
"hud.settings.maximum_fps": "Maximum FPS", "hud.settings.maximum_fps": "Maximum FPS",

View File

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

View File

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

View File

@ -1,10 +1,7 @@
use vek::*; use crate::{terrain::TerrainChunkSize, vol::RectVolSize};
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
use strum::EnumIter; use strum::EnumIter;
use crate::{ use vek::*;
terrain::TerrainChunkSize,
vol::RectVolSize,
};
// In chunks // In chunks
pub const ZONE_SIZE: u32 = 32; pub const ZONE_SIZE: u32 = 32;
@ -35,9 +32,7 @@ pub struct Zone {
pub objects: Vec<Object>, pub objects: Vec<Object>,
} }
pub fn to_wpos(wpos: i32) -> i32 { pub fn to_wpos(wpos: i32) -> i32 { wpos * (TerrainChunkSize::RECT_SIZE.x * ZONE_SIZE) as i32 }
wpos * (TerrainChunkSize::RECT_SIZE.x * ZONE_SIZE) as i32
}
pub fn from_wpos(zone_pos: i32) -> i32 { pub fn from_wpos(zone_pos: i32) -> i32 {
zone_pos.div_euclid((TerrainChunkSize::RECT_SIZE.x * ZONE_SIZE) as 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?) // Insert the world into the ECS (todo: Maybe not an Arc?)
let world = Arc::new(world); let world = Arc::new(world);
state.ecs_mut().insert(Arc::clone(&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()); state.ecs_mut().insert(index.clone());
// Set starting time for the server. // Set starting time for the server.

View File

@ -1,7 +1,7 @@
use common::lod; use common::lod;
use world::World;
use hashbrown::HashMap; use hashbrown::HashMap;
use vek::*; use vek::*;
use world::World;
static EMPTY_ZONE: lod::Zone = lod::Zone { static EMPTY_ZONE: lod::Zone = lod::Zone {
objects: Vec::new(), 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::{ use common::{
comp::Pos, comp::Pos,
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},

View File

@ -39,6 +39,9 @@ widget_ids! {
vd_slider, vd_slider,
vd_text, vd_text,
vd_value, vd_value,
ld_slider,
ld_text,
ld_value,
lod_detail_slider, lod_detail_slider,
lod_detail_text, lod_detail_text,
lod_detail_value, lod_detail_value,
@ -280,8 +283,6 @@ impl<'a> Widget for Video<'a> {
if let Some(new_val) = ImageSlider::discrete( if let Some(new_val) = ImageSlider::discrete(
self.global_state.settings.graphics.view_distance, self.global_state.settings.graphics.view_distance,
1, 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, 65,
self.imgs.slider_indicator, self.imgs.slider_indicator,
self.imgs.slider, self.imgs.slider,
@ -306,9 +307,44 @@ impl<'a> Widget for Video<'a> {
.color(TEXT_COLOR) .color(TEXT_COLOR)
.set(state.ids.vd_value, ui); .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 // Max FPS
Text::new(self.localized_strings.get("hud.settings.maximum_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_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR) .color(TEXT_COLOR)
@ -343,7 +379,7 @@ impl<'a> Widget for Video<'a> {
// Max Background FPS // Max Background FPS
Text::new(self.localized_strings.get("hud.settings.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) .right_from(state.ids.max_fps_value, 30.0)
.font_size(self.fonts.cyri.scale(14)) .font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
@ -391,7 +427,7 @@ impl<'a> Widget for Video<'a> {
// Present Mode // Present Mode
Text::new(self.localized_strings.get("hud.settings.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) .right_from(state.ids.max_background_fps_value, 30.0)
.font_size(self.fonts.cyri.scale(14)) .font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)

View File

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

View File

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

View File

@ -21,7 +21,7 @@ use super::{
mesh::Mesh, mesh::Mesh,
model::{DynamicModel, Model}, model::{DynamicModel, Model},
pipelines::{ 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, GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup,
}, },
texture::Texture, texture::Texture,

View File

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

View File

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

View File

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

View File

@ -1,19 +1,20 @@
use crate::{ use crate::{
render::{ render::{
pipelines::lod_terrain::{LodData, Vertex}, 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, scene::GlobalModel,
settings::Settings, settings::Settings,
}; };
use hashbrown::HashMap;
use client::Client; use client::Client;
use common::{ use common::{
assets::{ObjAsset, AssetExt}, assets::{AssetExt, ObjAsset},
lod,
spiral::Spiral2d, spiral::Spiral2d,
util::srgba_to_linear, util::srgba_to_linear,
lod,
}; };
use hashbrown::HashMap;
use vek::*; use vek::*;
pub struct Lod { pub struct Lod {
@ -31,11 +32,7 @@ pub fn water_color() -> Rgba<f32> {
} }
impl Lod { impl Lod {
pub fn new( pub fn new(renderer: &mut Renderer, client: &Client, settings: &Settings) -> Self {
renderer: &mut Renderer,
client: &Client,
settings: &Settings,
) -> Self {
let data = LodData::new( let data = LodData::new(
renderer, renderer,
client.world_data().chunk_size().as_(), client.world_data().chunk_size().as_(),
@ -49,8 +46,14 @@ impl Lod {
Self { Self {
zone_objects: HashMap::new(), zone_objects: HashMap::new(),
object_data: [ 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() .into_iter()
.collect(), .collect(),
@ -98,13 +101,19 @@ impl Lod {
objects objects
.into_iter() .into_iter()
.map(|(kind, instances)| { .map(|(kind, instances)| {
(kind, renderer.create_instances(&instances).expect("Renderer error?!")) (
kind,
renderer
.create_instances(&instances)
.expect("Renderer error?!"),
)
}) })
.collect() .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>) { pub fn render<'a>(&'a self, drawer: &mut FirstPassDrawer<'a>) {
@ -159,19 +168,20 @@ fn make_lod_object(
) -> Model<LodObjectVertex> { ) -> Model<LodObjectVertex> {
let model = ObjAsset::load_expect(&format!("voxygen.lod.{}", name)); let model = ObjAsset::load_expect(&format!("voxygen.lod.{}", name));
let mesh = model let mesh = model
.read().0 .read()
.0
.triangles() .triangles()
.map(|vs| { .map(|vs| {
let [a, b, c] = vs.map(|v| LodObjectVertex::new( let [a, b, c] = vs.map(|v| {
LodObjectVertex::new(
v.position().into(), v.position().into(),
v.normal().unwrap_or([0.0, 0.0, 1.0]).into(), v.normal().unwrap_or([0.0, 0.0, 1.0]).into(),
Vec3::broadcast(1.0), Vec3::broadcast(1.0),
//v.color().unwrap_or([1.0; 3]).into(), //v.color().unwrap_or([1.0; 3]).into(),
)); )
});
Tri::new(a, b, c) Tri::new(a, b, c)
}) })
.collect(); .collect();
renderer renderer.create_model(&mesh).expect("Mesh was empty!")
.create_model(&mesh)
.expect("Mesh was empty!")
} }

View File

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

View File

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

View File

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

View File

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

View File

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