diff --git a/korangar/src/graphics/instruction.rs b/korangar/src/graphics/instruction.rs index 5b745a0c..f35fd6e0 100644 --- a/korangar/src/graphics/instruction.rs +++ b/korangar/src/graphics/instruction.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use cgmath::{Matrix4, Point3, Vector2, Vector3, Vector4}; +use korangar_util::Rectangle; use ragnarok_packets::EntityId; use wgpu::BlendFactor; @@ -68,6 +69,7 @@ pub struct Uniforms { #[derive(Clone, Debug)] pub struct WaterInstruction<'a> { pub water_texture: &'a Texture, + pub water_bounds: Rectangle, pub texture_repeat: f32, pub water_level: f32, pub wave_amplitude: f32, diff --git a/korangar/src/graphics/passes/water/shader/wave.wgsl b/korangar/src/graphics/passes/water/shader/wave.wgsl index b5a67b50..b8d97371 100644 --- a/korangar/src/graphics/passes/water/shader/wave.wgsl +++ b/korangar/src/graphics/passes/water/shader/wave.wgsl @@ -25,6 +25,7 @@ struct DirectionalLightUniforms { } struct WaterWaveUniforms { + water_bounds: vec4, texture_repeat: f32, water_level: f32, wave_amplitude: f32, @@ -104,11 +105,17 @@ fn fs_main(input: VertexOutput) -> @location(0) vec4 { wave_phase_speed = sqrt(GRAVITY / wave_frequency); let distance = find_wave_intersection(ray_origin, ray_direction, max_distance, max_wave_height); - - if (distance < 0.0 || distance > max_distance) { + let hit_position = ray_origin + ray_direction * distance; + + if (distance < 0.0 || + distance > max_distance || + hit_position.x < water_wave_uniforms.water_bounds.x || + hit_position.z < water_wave_uniforms.water_bounds.y || + hit_position.x > water_wave_uniforms.water_bounds.z || + hit_position.z > water_wave_uniforms.water_bounds.w + ) { discard; } else { - let hit_position = ray_origin + ray_direction * distance; color = calculate_wave_color(hit_position); } } @@ -129,11 +136,6 @@ fn find_wave_intersection(ray_origin: vec3, ray_direction: vec3, max_d return ray_plane_intersection(ray_origin, ray_direction, -water_wave_uniforms.water_level); } - if (max_distance >= 1e30) { - // We look outside of the scene. - return -1.0; - } - let initial_guess = ray_plane_intersection(ray_origin, ray_direction, max_wave_height); if (initial_guess < 0.0 || initial_guess > max_distance) { diff --git a/korangar/src/graphics/passes/water/shader/wave_msaa.wgsl b/korangar/src/graphics/passes/water/shader/wave_msaa.wgsl index 9c3399d0..c4be90e0 100644 --- a/korangar/src/graphics/passes/water/shader/wave_msaa.wgsl +++ b/korangar/src/graphics/passes/water/shader/wave_msaa.wgsl @@ -25,6 +25,7 @@ struct DirectionalLightUniforms { } struct WaterWaveUniforms { + water_bounds: vec4, texture_repeat: f32, water_level: f32, wave_amplitude: f32, @@ -105,11 +106,17 @@ fn fs_main(input: VertexOutput) -> @location(0) vec4 { wave_phase_speed = sqrt(GRAVITY / wave_frequency); let distance = find_wave_intersection(ray_origin, ray_direction, max_distance, max_wave_height); - - if (distance < 0.0 || distance > max_distance) { + let hit_position = ray_origin + ray_direction * distance; + + if (distance < 0.0 || + distance > max_distance || + hit_position.x < water_wave_uniforms.water_bounds.x || + hit_position.z < water_wave_uniforms.water_bounds.y || + hit_position.x > water_wave_uniforms.water_bounds.z || + hit_position.z > water_wave_uniforms.water_bounds.w + ) { discard; } else { - let hit_position = ray_origin + ray_direction * distance; color = calculate_wave_color(hit_position); } } @@ -130,11 +137,6 @@ fn find_wave_intersection(ray_origin: vec3, ray_direction: vec3, max_d return ray_plane_intersection(ray_origin, ray_direction, -water_wave_uniforms.water_level); } - if (max_distance >= 1e30) { - // We look outside of the scene. - return -1.0; - } - let initial_guess = ray_plane_intersection(ray_origin, ray_direction, max_wave_height); if (initial_guess < 0.0 || initial_guess > max_distance) { diff --git a/korangar/src/graphics/passes/water/wave.rs b/korangar/src/graphics/passes/water/wave.rs index d6eebc35..63096bb0 100644 --- a/korangar/src/graphics/passes/water/wave.rs +++ b/korangar/src/graphics/passes/water/wave.rs @@ -20,12 +20,14 @@ const DRAWER_NAME: &str = "water wave"; #[derive(Copy, Clone, Default, Pod, Zeroable)] #[repr(C)] struct WaterWaveUniforms { + water_bounds: [f32; 4], texture_repeat: f32, water_level: f32, wave_amplitude: f32, wave_speed: f32, wave_length: f32, water_opacity: f32, + padding: [u32; 2], } pub(crate) struct WaterWaveDrawer { @@ -157,12 +159,14 @@ impl Prepare for WaterWaveDrawer { fn prepare(&mut self, device: &Device, instructions: &RenderInstruction) { if let Some(instruction) = instructions.water.as_ref() { self.uniforms = WaterWaveUniforms { + water_bounds: instruction.water_bounds.into(), texture_repeat: instruction.texture_repeat, water_level: instruction.water_level, wave_amplitude: instruction.wave_amplitude, wave_speed: instruction.wave_speed, wave_length: instruction.wave_length, water_opacity: instruction.water_opacity, + padding: Default::default(), }; self.bind_group = Self::create_bind_group( device, diff --git a/korangar/src/loaders/map/mod.rs b/korangar/src/loaders/map/mod.rs index dea59073..84167c12 100644 --- a/korangar/src/loaders/map/mod.rs +++ b/korangar/src/loaders/map/mod.rs @@ -3,7 +3,7 @@ mod vertices; use std::sync::Arc; use bytemuck::Pod; -use cgmath::Vector3; +use cgmath::{Array, Point2, Vector3}; use derive_new::new; use hashbrown::HashMap; use korangar_audio::AudioEngine; @@ -85,7 +85,13 @@ impl MapLoader { #[cfg(not(feature = "debug"))] let (_, tile_picker_vertices) = generate_tile_vertices(&mut gat_data); - let ground_native_vertices = ground_vertices(&ground_data); + let water_level = -map_data + .water_settings + .as_ref() + .and_then(|settings| settings.water_level) + .unwrap_or_default(); + + let (ground_native_vertices, water_bounds) = ground_vertices(&ground_data, water_level); let ground_vertex_offset = 0; let ground_vertex_count = ground_native_vertices.len(); @@ -190,10 +196,17 @@ impl MapLoader { let light_sources_kdtree = KDTree::from_objects(&light_source_spheres); let background_music_track_name = self.audio_engine.get_track_for_map(&map_file_name); + // There are maps that don't have water tiles but have water settings. In such + // cases we will set the water settings to `None` + let water_settings = map_data + .water_settings + .filter(|_| !(water_bounds.min == Point2::from_value(f32::MAX) && water_bounds.max == Point2::from_value(f32::MIN))); + let map = Map::new( gat_data.map_width as usize, gat_data.map_height as usize, - map_data.water_settings, + water_settings, + water_bounds, map_data.light_settings, gat_data.tiles, ground_vertex_offset, diff --git a/korangar/src/loaders/map/vertices.rs b/korangar/src/loaders/map/vertices.rs index 725e6079..d4dd74b9 100644 --- a/korangar/src/loaders/map/vertices.rs +++ b/korangar/src/loaders/map/vertices.rs @@ -1,8 +1,9 @@ #[cfg(feature = "debug")] -use cgmath::{EuclideanSpace, Point2}; -use cgmath::{Point3, Vector2}; +use cgmath::EuclideanSpace; +use cgmath::{Array, Point2, Point3, Vector2}; #[cfg(feature = "debug")] use korangar_util::texture_atlas::AtlasAllocation; +use korangar_util::Rectangle; use ragnarok_formats::map::{GatData, GroundData, GroundTile, SurfaceType}; #[cfg(feature = "debug")] @@ -19,9 +20,12 @@ pub enum Heights { LowerRight, } -pub fn ground_vertices(ground_data: &GroundData) -> Vec { +pub fn ground_vertices(ground_data: &GroundData, water_level: f32) -> (Vec, Rectangle) { let mut native_ground_vertices = Vec::new(); + let mut water_bound_min = Point2::from_value(f32::MAX); + let mut water_bound_max = Point2::from_value(f32::MIN); + let width = ground_data.width as usize; let height = ground_data.height as usize; let ground_tiles = &ground_data.ground_tiles; @@ -151,11 +155,32 @@ pub fn ground_vertices(ground_data: &GroundData) -> Vec { 0.0, )); } + + if -current_tile.get_lowest_point() < water_level { + let first_position = Point2::new(x as f32 * MAP_TILE_SIZE, y as f32 * MAP_TILE_SIZE); + let second_position = Point2::new(MAP_TILE_SIZE + x as f32 * MAP_TILE_SIZE, y as f32 * MAP_TILE_SIZE); + let third_position = Point2::new( + MAP_TILE_SIZE + x as f32 * MAP_TILE_SIZE, + MAP_TILE_SIZE + y as f32 * MAP_TILE_SIZE, + ); + let fourth_position = Point2::new(x as f32 * MAP_TILE_SIZE, MAP_TILE_SIZE + y as f32 * MAP_TILE_SIZE); + + [first_position, second_position, third_position, fourth_position] + .iter() + .for_each(|position| { + water_bound_min.x = f32::min(water_bound_min.x, position.x); + water_bound_min.y = f32::min(water_bound_min.y, position.y); + water_bound_max.x = f32::max(water_bound_max.x, position.x); + water_bound_max.y = f32::max(water_bound_max.y, position.y); + }); + } } } } - native_ground_vertices + let water_bounds = Rectangle::new(water_bound_min, water_bound_max); + + (native_ground_vertices, water_bounds) } pub fn generate_tile_vertices( @@ -320,3 +345,21 @@ pub fn neighbor_tile_index(surface_type: SurfaceType) -> Vector2 { SurfaceType::Top => Vector2::new(0, 0), } } + +pub trait GroundTileExt { + fn get_lowest_point(&self) -> f32; +} + +impl GroundTileExt for GroundTile { + fn get_lowest_point(&self) -> f32 { + [ + self.lower_right_height, + self.lower_left_height, + self.upper_left_height, + self.lower_right_height, + ] + .into_iter() + .reduce(f32::max) + .unwrap() + } +} diff --git a/korangar/src/world/map/mod.rs b/korangar/src/world/map/mod.rs index 64944a81..fb772b56 100644 --- a/korangar/src/world/map/mod.rs +++ b/korangar/src/world/map/mod.rs @@ -9,7 +9,7 @@ use korangar_audio::AudioEngine; use korangar_interface::windows::PrototypeWindow; use korangar_util::collision::{Frustum, KDTree, Sphere, AABB}; use korangar_util::container::{SimpleKey, SimpleSlab}; -use korangar_util::create_simple_key; +use korangar_util::{create_simple_key, Rectangle}; #[cfg(feature = "debug")] use option_ext::OptionExt; #[cfg(feature = "debug")] @@ -118,6 +118,7 @@ pub struct Map { width: usize, height: usize, water_settings: Option, + water_bounds: Rectangle, light_settings: LightSettings, tiles: Vec, ground_vertex_offset: usize, @@ -141,6 +142,10 @@ pub struct Map { } impl Map { + pub fn water_bounds(&self) -> Rectangle { + self.water_bounds + } + pub fn x_in_bounds(&self, x: usize) -> bool { x <= self.width } @@ -295,6 +300,7 @@ impl Map { *water_instruction = Some(WaterInstruction { water_texture: &water_textures[water_texture_index as usize], + water_bounds: self.water_bounds(), texture_repeat, water_level, wave_amplitude, diff --git a/korangar_util/src/rectangle.rs b/korangar_util/src/rectangle.rs index c8101933..c878e2d2 100644 --- a/korangar_util/src/rectangle.rs +++ b/korangar_util/src/rectangle.rs @@ -46,3 +46,9 @@ impl PartialEq for Rectangle { self.min == other.min && self.max == other.max } } + +impl From> for [N; 4] { + fn from(value: Rectangle) -> Self { + [value.min.x, value.min.y, value.max.x, value.max.y] + } +}