Skip to content

Commit

Permalink
Properly bound the water of a map
Browse files Browse the repository at this point in the history
This removes the depth hack of the water SDF function and instead reads the water bound from the tiles that would be turned into a water mesh in a traditional renderer.
  • Loading branch information
hasenbanck committed Dec 15, 2024
1 parent b7432ca commit 90fae2d
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 24 deletions.
2 changes: 2 additions & 0 deletions korangar/src/graphics/instruction.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -68,6 +69,7 @@ pub struct Uniforms {
#[derive(Clone, Debug)]
pub struct WaterInstruction<'a> {
pub water_texture: &'a Texture,
pub water_bounds: Rectangle<f32>,
pub texture_repeat: f32,
pub water_level: f32,
pub wave_amplitude: f32,
Expand Down
18 changes: 10 additions & 8 deletions korangar/src/graphics/passes/water/shader/wave.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct DirectionalLightUniforms {
}

struct WaterWaveUniforms {
water_bounds: vec4<f32>,
texture_repeat: f32,
water_level: f32,
wave_amplitude: f32,
Expand Down Expand Up @@ -104,11 +105,17 @@ fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
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);
}
}
Expand All @@ -129,11 +136,6 @@ fn find_wave_intersection(ray_origin: vec3<f32>, ray_direction: vec3<f32>, 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) {
Expand Down
18 changes: 10 additions & 8 deletions korangar/src/graphics/passes/water/shader/wave_msaa.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct DirectionalLightUniforms {
}

struct WaterWaveUniforms {
water_bounds: vec4<f32>,
texture_repeat: f32,
water_level: f32,
wave_amplitude: f32,
Expand Down Expand Up @@ -105,11 +106,17 @@ fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
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);
}
}
Expand All @@ -130,11 +137,6 @@ fn find_wave_intersection(ray_origin: vec3<f32>, ray_direction: vec3<f32>, 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) {
Expand Down
4 changes: 4 additions & 0 deletions korangar/src/graphics/passes/water/wave.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 16 additions & 3 deletions korangar/src/loaders/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand Down
51 changes: 47 additions & 4 deletions korangar/src/loaders/map/vertices.rs
Original file line number Diff line number Diff line change
@@ -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")]
Expand All @@ -19,9 +20,12 @@ pub enum Heights {
LowerRight,
}

pub fn ground_vertices(ground_data: &GroundData) -> Vec<NativeModelVertex> {
pub fn ground_vertices(ground_data: &GroundData, water_level: f32) -> (Vec<NativeModelVertex>, Rectangle<f32>) {
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;
Expand Down Expand Up @@ -151,11 +155,32 @@ pub fn ground_vertices(ground_data: &GroundData) -> Vec<NativeModelVertex> {
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(
Expand Down Expand Up @@ -320,3 +345,21 @@ pub fn neighbor_tile_index(surface_type: SurfaceType) -> Vector2<usize> {
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()
}
}
8 changes: 7 additions & 1 deletion korangar/src/world/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -118,6 +118,7 @@ pub struct Map {
width: usize,
height: usize,
water_settings: Option<WaterSettings>,
water_bounds: Rectangle<f32>,
light_settings: LightSettings,
tiles: Vec<Tile>,
ground_vertex_offset: usize,
Expand All @@ -141,6 +142,10 @@ pub struct Map {
}

impl Map {
pub fn water_bounds(&self) -> Rectangle<f32> {
self.water_bounds
}

pub fn x_in_bounds(&self, x: usize) -> bool {
x <= self.width
}
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions korangar_util/src/rectangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,9 @@ impl<N: BaseNum> PartialEq for Rectangle<N> {
self.min == other.min && self.max == other.max
}
}

impl<N> From<Rectangle<N>> for [N; 4] {
fn from(value: Rectangle<N>) -> Self {
[value.min.x, value.min.y, value.max.x, value.max.y]
}
}

0 comments on commit 90fae2d

Please sign in to comment.