pub mod bound; mod buffer; pub mod consts; mod error; pub mod instances; pub mod mesh; pub mod model; pub mod pipelines; pub mod renderer; pub mod texture; // Reexports pub use self::{ bound::Bound, buffer::Buffer, consts::Consts, error::RenderError, instances::Instances, mesh::{Mesh, Quad, Tri}, model::{DynamicModel, Model, SubModel}, pipelines::{ clouds::Locals as CloudsLocals, debug::{DebugPipeline, Locals as DebugLocals, Vertex as DebugVertex}, figure::{ BoneData as FigureBoneData, BoneMeshes, FigureLayout, FigureModel, Locals as FigureLocals, }, fluid::Vertex as FluidVertex, lod_terrain::{LodData, Vertex as LodTerrainVertex}, particle::{Instance as ParticleInstance, Vertex as ParticleVertex}, postprocess::Locals as PostProcessLocals, shadow::{Locals as ShadowLocals, PointLightMatrix}, skybox::{create_mesh as create_skybox_mesh, Vertex as SkyboxVertex}, sprite::{ Instance as SpriteInstance, SpriteGlobalsBindGroup, SpriteVerts, Vertex as SpriteVertex, VERT_PAGE_SIZE as SPRITE_VERT_PAGE_SIZE, }, terrain::{Locals as TerrainLocals, TerrainLayout, Vertex as TerrainVertex}, ui::{ create_quad as create_ui_quad, create_quad_vert_gradient as create_ui_quad_vert_gradient, create_tri as create_ui_tri, BoundLocals as UiBoundLocals, Locals as UiLocals, Mode as UiMode, TextureBindGroup as UiTextureBindGroup, Vertex as UiVertex, }, GlobalModel, Globals, GlobalsBindGroup, GlobalsLayouts, Light, Shadow, }, renderer::{ drawer::{ DebugDrawer, Drawer, FigureDrawer, FigureShadowDrawer, FirstPassDrawer, ParticleDrawer, PreparedUiDrawer, SecondPassDrawer, ShadowPassDrawer, SpriteDrawer, TerrainDrawer, TerrainShadowDrawer, ThirdPassDrawer, UiDrawer, }, ColLightInfo, Renderer, }, texture::Texture, }; use hashbrown::HashSet; pub use wgpu::{AddressMode, FilterMode}; pub trait Vertex: Clone + bytemuck::Pod { const STRIDE: wgpu::BufferAddress; // Whether these types of verts use the quad index buffer for drawing them const QUADS_INDEX: Option; } use serde::{Deserialize, Serialize}; /// Anti-aliasing modes #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum AaMode { /// Fast approximate antialiasing. /// /// This is a screen-space technique, and therefore works fine with greedy /// meshing. Fxaa, /// Multisampling AA, up to 4 samples per pixel. /// /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will /// also struggle in the future with deferred shading, so they may be /// removed in the future. MsaaX4, /// Multisampling AA, up to 8 samples per pixel. /// /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will /// also struggle in the future with deferred shading, so they may be /// removed in the future. MsaaX8, /// Multisampling AA, up to 16 samples per pixel. /// /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will /// also struggle in the future with deferred shading, so they may be /// removed in the future. MsaaX16, #[serde(other)] None, } impl Default for AaMode { fn default() -> Self { AaMode::Fxaa } } /// Cloud modes #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum CloudMode { /// No clouds. As cheap as it gets. None, /// Clouds, but barely. Ideally, any machine should be able to handle this /// just fine. Minimal, /// Enough visual detail to be pleasing, but generally using poor-but-cheap /// approximations to derive parameters Low, /// More detail. Enough to look good in most cases. For those that value /// looks but also high framerates. Medium, /// High, but with extra compute power thrown at it to smooth out subtle /// imperfections Ultra, /// Lots of detail with good-but-costly derivation of parameters. #[serde(other)] High, } impl Default for CloudMode { fn default() -> Self { CloudMode::High } } /// Fluid modes #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum FluidMode { /// "Cheap" water. This water implements no waves, no reflections, no /// diffraction, and no light attenuation through water. As a result, /// it can be much cheaper than shiny reflection. Cheap, /// "Shiny" water. This water implements waves on the surfaces, some /// attempt at reflections, and tries to compute accurate light /// attenuation through water (this is what results in the /// colors changing as you descend into deep water). /// /// Unfortunately, the way the engine is currently set up, calculating /// accurate attenuation is a bit difficult; we use estimates from /// horizon maps for the current water altitude, which can both be off /// by up to (max_altitude / 255) meters, only has per-chunk horizontal /// resolution, and cannot handle edge cases like horizontal water (e.g. /// waterfalls) well. We are okay with the latter, and will try to fix /// the former soon. /// /// Another issue is that we don't always know whether light is *blocked*, /// which causes attenuation to be computed incorrectly; this can be /// addressed by using shadow maps (at least for terrain). #[serde(other)] Shiny, } impl Default for FluidMode { fn default() -> Self { FluidMode::Shiny } } /// Lighting modes #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum LightingMode { /// Ashikhmin-Shirley BRDF lighting model. Attempts to generate a /// physically plausible (to some extent) lighting distribution. /// /// This model may not work as well with purely directional lighting, and is /// more expensive than the other models. Ashikhmin, /// Standard Lambertian lighting model, with only diffuse reflections. The /// cheapest lighting model by a decent margin, but the performance /// difference between it and Blinn-Phong will probably only be /// significant on low-end machines that are bottlenecked on fragment /// shading. Lambertian, /// Standard Blinn-Phong shading, combing Lambertian diffuse reflections and /// specular highlights. #[serde(other)] BlinnPhong, } impl Default for LightingMode { fn default() -> Self { LightingMode::BlinnPhong } } /// Shadow map settings. #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub struct ShadowMapMode { /// Multiple of default resolution (default, which is 1.0, is currently /// the closest higher power of two above the length of the longest /// diagonal of the screen resolution, but this may change). pub resolution: f32, } impl Default for ShadowMapMode { fn default() -> Self { Self { resolution: 1.0 } } } /// Shadow modes #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum ShadowMode { /// No shadows at all. By far the cheapest option. None, /// Shadow map (render the scene from each light source, and also renders /// LOD shadows using horizon maps). Map(ShadowMapMode), /// Point shadows (draw circles under figures, up to a configured maximum; /// also render LOD shadows using horizon maps). Can be expensive on /// some machines, probably mostly due to horizon mapping; the point /// shadows are not rendered too efficiently, but that can probably /// be addressed later. #[serde(other)] // Would normally be on `Map`, but only allowed on unit variants Cheap, } impl Default for ShadowMode { fn default() -> Self { ShadowMode::Map(Default::default()) } } impl core::convert::TryFrom for ShadowMapMode { type Error = (); /// Get the shadow map details if they exist. fn try_from(value: ShadowMode) -> Result { if let ShadowMode::Map(map) = value { Ok(map) } else { Err(()) } } } impl ShadowMode { pub fn is_map(&self) -> bool { matches!(self, Self::Map(_)) } } /// Upscale mode settings. #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub struct UpscaleMode { // Determines non-UI graphics upscaling. 0.25 to 2.0. pub factor: f32, } impl Default for UpscaleMode { fn default() -> Self { Self { factor: 1.0 } } } /// Present modes /// See https://docs.rs/wgpu/0.7.0/wgpu/enum.PresentMode.html #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum PresentMode { Fifo, Mailbox, #[serde(other)] Immediate, } impl Default for PresentMode { fn default() -> Self { Self::Immediate } } impl From for wgpu::PresentMode { fn from(mode: PresentMode) -> Self { match mode { PresentMode::Fifo => wgpu::PresentMode::Fifo, PresentMode::Mailbox => wgpu::PresentMode::Mailbox, PresentMode::Immediate => wgpu::PresentMode::Immediate, } } } /// Bloom factor /// Controls fraction of output image luminosity that is blurred bloom #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum BloomFactor { Low, High, /// Max valid value is 1.0 Custom(f32), // other variant has to be placed last #[serde(other)] Medium, } impl Default for BloomFactor { fn default() -> Self { Self::Medium } } impl BloomFactor { /// Fraction of output image luminosity that is blurred bloom pub fn fraction(self) -> f32 { match self { Self::Low => 0.1, Self::Medium => 0.2, Self::High => 0.3, Self::Custom(val) => val.max(0.0).min(1.0), } } } /// Bloom settings #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub struct BloomConfig { /// Controls fraction of output image luminosity that is blurred bloom /// /// Defaults to `Medium` pub factor: BloomFactor, /// Turning this on make the bloom blur less sharply concentrated around the /// high intensity phenomena (removes adding in less blurred layers to the /// final blur) /// /// Defaults to `false` pub uniform_blur: bool, // TODO: allow configuring the blur radius and/or the number of passes } #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum BloomMode { On(BloomConfig), #[serde(other)] Off, } impl Default for BloomMode { fn default() -> Self { Self::On(BloomConfig { factor: BloomFactor::default(), uniform_blur: false, }) } } impl BloomMode { fn is_on(&self) -> bool { matches!(self, BloomMode::On(_)) } } /// Render modes #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[serde(default)] pub struct RenderMode { pub aa: AaMode, pub cloud: CloudMode, pub fluid: FluidMode, pub lighting: LightingMode, pub shadow: ShadowMode, pub bloom: BloomMode, /// 0.0..1.0 pub point_glow: f32, pub experimental_shaders: HashSet, pub upscale_mode: UpscaleMode, pub present_mode: PresentMode, pub profiler_enabled: bool, } impl Default for RenderMode { fn default() -> Self { Self { aa: AaMode::default(), cloud: CloudMode::default(), fluid: FluidMode::default(), lighting: LightingMode::default(), shadow: ShadowMode::default(), bloom: BloomMode::default(), point_glow: 0.35, experimental_shaders: HashSet::default(), upscale_mode: UpscaleMode::default(), present_mode: PresentMode::default(), profiler_enabled: false, } } } impl RenderMode { fn split(self) -> (PipelineModes, OtherModes) { ( PipelineModes { aa: self.aa, cloud: self.cloud, fluid: self.fluid, lighting: self.lighting, shadow: self.shadow, bloom: self.bloom, point_glow: self.point_glow, experimental_shaders: self.experimental_shaders, }, OtherModes { upscale_mode: self.upscale_mode, present_mode: self.present_mode, profiler_enabled: self.profiler_enabled, }, ) } } /// Render modes that require pipeline recreation (e.g. shader recompilation) /// when changed #[derive(PartialEq, Clone, Debug)] pub struct PipelineModes { aa: AaMode, cloud: CloudMode, fluid: FluidMode, lighting: LightingMode, pub shadow: ShadowMode, bloom: BloomMode, point_glow: f32, experimental_shaders: HashSet, } /// Other render modes that don't effect pipelines #[derive(PartialEq, Clone, Debug)] struct OtherModes { upscale_mode: UpscaleMode, present_mode: PresentMode, profiler_enabled: bool, } /// Experimental shader modes. /// /// You can enable these using Voxygen's `settings.ron`. See /// [here](https://book.veloren.net/players/voxygen.html#experimental-shaders) for more information. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum ExperimentalShader { /// Add brick-like normal mapping to the world. Brickloren, /// Remove the default procedural noise from terrain. NoNoise, /// Simulated a curved world. CurvedWorld, /// Adds extra detail to distant LoD (Level of Detail) terrain procedurally. ProceduralLodDetail, /// Add a warping effect when underwater. Underwarper, /// Remove caustics from underwater terrain when shiny water is enabled. NoCaustics, }