1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
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::{DebugLayout, DebugPipeline, Locals as DebugLocals, Vertex as DebugVertex},
        figure::{
            BoneData as FigureBoneData, BoneMeshes, FigureLayout, FigureModel,
            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,
        rain_occlusion::Locals as RainOcclusionLocals,
        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},
        trail::Vertex as TrailVertex,
        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, UploadBatchId as UiUploadBatchId,
            Vertex as UiVertex,
        },
        FigureSpriteAtlasData, GlobalModel, Globals, GlobalsBindGroup, GlobalsLayouts, Light,
        Shadow, TerrainAtlasData,
    },
    renderer::{
        drawer::{
            DebugDrawer, DebugShadowDrawer, Drawer, FigureDrawer, FigureShadowDrawer,
            FirstPassDrawer, ParticleDrawer, PreparedUiDrawer, ShadowPassDrawer, SpriteDrawer,
            TerrainDrawer, TerrainShadowDrawer, ThirdPassDrawer, TrailDrawer,
            TransparentPassDrawer, UiDrawer, VolumetricPassDrawer, UI_PREMULTIPLY_PASS,
        },
        AltIndices, CullingMode, 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<wgpu::IndexFormat>;
}

use serde::{Deserialize, Serialize};
/// Anti-aliasing modes
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum AaMode {
    /// Fast approximate antialiasing.
    ///
    /// This is a screen-space technique, and therefore works fine with greedy
    /// meshing.
    #[default]
    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,
    /// Fast edge-detecting upscaling.
    ///
    /// Screen-space technique that attempts to reconstruct lines and edges
    /// in the original image. Useless at internal resolutions higher than 1.0x,
    /// but potentially very effective at much lower internal resolutions.
    Hqx,
    /// Fast upscaling informed by FXAA.
    ///
    /// Screen-space technique that uses a combination of FXAA and
    /// nearest-neighbour sample retargeting to produce crisp, clean upscaling.
    FxUpscale,
    /// Bilinear filtering.
    ///
    /// Linear interpolation of the color buffer in each axis to determine the
    /// pixel.
    Bilinear,
    /// Nearest-neighbour filtering.
    ///
    /// The colour of each pixel is determined by the colour of the spatially
    /// closest texel in the color buffer.
    #[serde(other)]
    None,
}

impl AaMode {
    pub fn samples(&self) -> u32 {
        match self {
            AaMode::None | AaMode::Bilinear | AaMode::Fxaa | AaMode::Hqx | AaMode::FxUpscale => 1,
            AaMode::MsaaX4 => 4,
            AaMode::MsaaX8 => 8,
            AaMode::MsaaX16 => 16,
        }
    }
}

/// Cloud modes
#[derive(Default, PartialEq, Eq, 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)]
    #[default]
    High,
}

impl CloudMode {
    pub fn is_enabled(&self) -> bool { *self != CloudMode::None }
}

/// Fluid modes
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum FluidMode {
    /// "Low" 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.
    Low,
    High,
    /// 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)]
    #[default]
    Medium,
}

/// Reflection modes
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum ReflectionMode {
    /// No or minimal reflections.
    Low,
    /// High quality reflections with screen-space raycasting and
    /// all the bells & whistles.
    #[default]
    High,
    // Medium quality screen-space reflections.
    #[serde(other)]
    Medium,
}

/// Lighting modes
#[derive(Default, PartialEq, Eq, 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)]
    #[default]
    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 TryFrom<ShadowMode> for ShadowMapMode {
    type Error = ();

    /// Get the shadow map details if they exist.
    fn try_from(value: ShadowMode) -> Result<Self, Self::Error> {
        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(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum PresentMode {
    Mailbox,
    Immediate,
    FifoRelaxed,
    #[default]
    #[serde(other)]
    Fifo, // has to be last for `#[serde(other)]`
}

impl From<PresentMode> for wgpu::PresentMode {
    fn from(mode: PresentMode) -> Self {
        match mode {
            PresentMode::Fifo => wgpu::PresentMode::Fifo,
            PresentMode::FifoRelaxed => wgpu::PresentMode::FifoRelaxed,
            PresentMode::Mailbox => wgpu::PresentMode::Mailbox,
            PresentMode::Immediate => wgpu::PresentMode::Immediate,
        }
    }
}

impl TryFrom<wgpu::PresentMode> for PresentMode {
    type Error = ();

    fn try_from(mode: wgpu::PresentMode) -> Result<Self, ()> {
        match mode {
            wgpu::PresentMode::Fifo => Ok(PresentMode::Fifo),
            wgpu::PresentMode::FifoRelaxed => Ok(PresentMode::FifoRelaxed),
            wgpu::PresentMode::Mailbox => Ok(PresentMode::Mailbox),
            wgpu::PresentMode::Immediate => Ok(PresentMode::Immediate),
            _ => Err(()),
        }
    }
}

/// Bloom factor
/// Controls fraction of output image luminosity that is blurred bloom
#[derive(Default, 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)]
    #[default]
    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.clamp(0.0, 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 reflection: ReflectionMode,
    pub fluid: FluidMode,
    pub lighting: LightingMode,
    pub shadow: ShadowMode,
    pub rain_occlusion: ShadowMapMode,
    pub bloom: BloomMode,
    /// 0.0..1.0
    pub point_glow: f32,

    pub flashing_lights_enabled: bool,

    pub experimental_shaders: HashSet<ExperimentalShader>,

    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(),
            reflection: ReflectionMode::default(),
            lighting: LightingMode::default(),
            shadow: ShadowMode::default(),
            rain_occlusion: ShadowMapMode::default(),
            bloom: BloomMode::default(),
            point_glow: 0.35,
            flashing_lights_enabled: true,
            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,
                reflection: self.reflection,
                lighting: self.lighting,
                shadow: self.shadow,
                rain_occlusion: self.rain_occlusion,
                bloom: self.bloom,
                point_glow: self.point_glow,
                flashing_lights_enabled: self.flashing_lights_enabled,
                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,
    pub cloud: CloudMode,
    fluid: FluidMode,
    reflection: ReflectionMode,
    lighting: LightingMode,
    pub shadow: ShadowMode,
    pub rain_occlusion: ShadowMapMode,
    bloom: BloomMode,
    point_glow: f32,
    flashing_lights_enabled: bool,
    experimental_shaders: HashSet<ExperimentalShader>,
}

/// 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,
    strum::EnumIter,
    strum::Display,
    strum::EnumString,
)]
pub enum ExperimentalShader {
    /// Add brick-like normal mapping to the world.
    Brickloren,
    /// Remove the default procedural noise from terrain.
    NoNoise,
    /// Add a sobel filter that draws lines in post-process by detecting edges
    /// inbetween colors. This does perform 8 times more texture samples in
    /// post-processing so there is potentially a significant performance
    /// impact especially with anti aliasing enabled.
    Sobel,
    /// Like Sobel, but on the gradient texture instead of the color texture.
    GradientSobel,
    /// Simulate 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,
    /// Don't dither color in post-processing.
    NoDither,
    /// Don't use the nonlinear srgb space for dithering color.
    NonSrgbDither,
    /// Use triangle PDF noise for dithering instead of uniform noise.
    TriangleNoiseDither,
    /// Removes as many effects (including lighting) as possible in the name of
    /// performance.
    BareMinimum,
    /// Lowers strength of the glow effect for lights near the camera.
    LowGlowNearCamera,
    /// Disable the fake voxel effect on LoD features.
    NoLodVoxels,
    /// Enable a 'pop-in' effect when loading terrain.
    TerrainPop,
    /// Display grid lines to visualize the distribution of shadow map texels
    /// for the directional light from the sun.
    DirectionalShadowMapTexelGrid,
    /// Disable rainbows
    NoRainbows,
    /// Add extra detailing to puddles.
    PuddleDetails,
    /// Show gbuffer surface normals.
    ViewNormals,
    /// Show gbuffer materials.
    ViewMaterials,
    /// Show gbuffer depth.
    ViewDepth,
    /// Rather than fading out screen-space reflections at view space borders,
    /// smear screen space to cover the reflection vector.
    SmearReflections,
    /// Apply the point shadows from cheap shadows on top of shadow mapping.
    PointShadowsWithShadowMapping,
    /// Make the UI uses nearest neighbor filtering for scaling images instead
    /// of trying to filter based on the coverage of the sampled pixels.
    UiNearestScaling,
    /// Prefer using physically-based values for various rendering parameters,
    /// where possible.
    Photorealistic,
    /// A noisy newspaper effect.
    Newspaper,
    /// A colorful dithering effect.
    ColorDithering,
    /// Cinematic color grading.
    Cinematic,
    /// Glittering snow.
    SnowGlitter,
    /// Enables optimizations when shaderc is processing shaders (currently on
    /// by default, but keep this for now in case we have to switch back to
    /// being off by default).
    EnableShadercOptimization,
    /// Disables optimizations when shaderc is processing shaders (has priority
    /// over `EnableShadercOptimization`).
    DisableShadercOptimization,
}