diff --git a/src/bundles/terrain.rs b/src/bundles/terrain.rs index 87a0c36..d2afe0f 100644 --- a/src/bundles/terrain.rs +++ b/src/bundles/terrain.rs @@ -4,7 +4,7 @@ use glam::Vec2; use nalgebra::vector; use rapier3d::prelude::{ColliderBuilder, RigidBodyBuilder}; -use crate::components::{MeshComponent, PhysicsComponent}; +use crate::components::{MeshComponent, PhysicsComponent, TreeInstancesComponent}; use crate::entity::EntityHandle; use crate::loaders::mesh::{InstanceData, InstanceRaw, Mesh}; use crate::loaders::terrain::load_heightfield_from_exr; @@ -78,6 +78,7 @@ impl TerrainBundle enable_snow_light: true, }, ); + world.names.insert(entity, "Terrain".to_string()); if !physics_added { @@ -125,7 +126,7 @@ impl TerrainBundle device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Tree Instance Buffer"), contents: bytemuck::cast_slice(&instance_raw), - usage: wgpu::BufferUsages::VERTEX, + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, }) }); @@ -135,13 +136,18 @@ impl TerrainBundle MeshComponent { mesh: Rc::new(mesh), pipeline: render::Pipeline::Standard, - instance_buffer: Some(instance_buffer), + instance_buffer: Some(instance_buffer.clone()), num_instances: num_instances as u32, tile_scale: 4.0, enable_dissolve: true, enable_snow_light: false, }, ); + world.tree_instances.insert( + entity, + TreeInstancesComponent::new(instances, Rc::new(instance_buffer)), + ); + world.names.insert(entity, "Trees".to_string()); } } diff --git a/src/debug/collider_debug.rs b/src/debug/collider_debug.rs index 84f4bec..ede827b 100644 --- a/src/debug/collider_debug.rs +++ b/src/debug/collider_debug.rs @@ -150,6 +150,7 @@ pub fn render_collider_debug() -> Vec enable_dissolve: false, enable_snow_light: false, displacement_bind_group: None, + entity: None, }); } }); @@ -183,6 +184,7 @@ pub fn render_collider_debug() -> Vec enable_dissolve: false, enable_snow_light: false, displacement_bind_group: None, + entity: None, }); } }); diff --git a/src/loaders/mesh.rs b/src/loaders/mesh.rs index 8850fc8..b725761 100644 --- a/src/loaders/mesh.rs +++ b/src/loaders/mesh.rs @@ -2,6 +2,26 @@ use bytemuck::{Pod, Zeroable}; use glam::{Mat4, Quat, Vec3}; use std::path::Path; +fn compute_aabb(vertices: &[Vertex]) -> (Vec3, Vec3) +{ + let mut aabb_min = Vec3::splat(f32::MAX); + let mut aabb_max = Vec3::splat(f32::MIN); + for v in vertices + { + let p = Vec3::from(v.position); + aabb_min = aabb_min.min(p); + aabb_max = aabb_max.max(p); + } + if vertices.is_empty() + { + (Vec3::ZERO, Vec3::ZERO) + } + else + { + (aabb_min, aabb_max) + } +} + #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] pub struct Vertex @@ -113,6 +133,10 @@ pub struct Mesh pub vertex_buffer: wgpu::Buffer, pub index_buffer: wgpu::Buffer, pub num_indices: u32, + pub aabb_min: Vec3, + pub aabb_max: Vec3, + pub cpu_positions: Vec<[f32; 3]>, + pub cpu_indices: Vec, } impl Mesh @@ -121,6 +145,8 @@ impl Mesh { use wgpu::util::DeviceExt; + let (aabb_min, aabb_max) = compute_aabb(vertices); + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Vertex Buffer"), contents: bytemuck::cast_slice(vertices), @@ -137,6 +163,10 @@ impl Mesh vertex_buffer, index_buffer, num_indices: indices.len() as u32, + aabb_min, + aabb_max, + cpu_positions: vertices.iter().map(|v| v.position).collect(), + cpu_indices: indices.to_vec(), } } diff --git a/src/main.rs b/src/main.rs index f0d514c..bbdca0b 100755 --- a/src/main.rs +++ b/src/main.rs @@ -41,6 +41,7 @@ use crate::systems::{ camera_follow_system, camera_input_system, camera_view_matrix, physics_sync_system, player_input_system, render_system, rotate_system, snow_system, spotlight_sync_system, start_camera_following, state_machine_physics_system, state_machine_system, + tree_dissolve_update_system, tree_instance_buffer_update_system, tree_occlusion_system, trigger_system, }; use crate::systems::camera::stop_camera_following; @@ -295,6 +296,10 @@ fn main() -> Result<(), Box> rotate_system(&mut world, delta); + tree_occlusion_system(&mut world); + tree_dissolve_update_system(&mut world, delta); + tree_instance_buffer_update_system(&mut world); + let spotlights = spotlight_sync_system(&world); render::update_spotlights(spotlights); diff --git a/src/shaders/main.wesl b/src/shaders/main.wesl index bf3b187..20d3c28 100644 --- a/src/shaders/main.wesl +++ b/src/shaders/main.wesl @@ -32,46 +32,7 @@ fn vs_main(input: VertexInput) -> VertexOutput { ); output.world_normal = normalize(normal_matrix * input.normal); - var dissolve_amount = 0.0; - - if uniforms.enable_dissolve == 1u { - let instance_position = vec3( - instance_model[3][0], - instance_model[3][1], - instance_model[3][2] - ); - - let to_player = uniforms.player_position - uniforms.camera_position; - let distance_to_player = length(to_player); - - if distance_to_player > 0.01 { - let ray_dir = to_player / distance_to_player; - let ray_origin = uniforms.camera_position; - - let tree_height = 16.0; - let occlusion_radius = 6.5; - - let w = instance_position - ray_origin; - let projection_t = dot(w, ray_dir); - - if projection_t > 0.0 && projection_t < distance_to_player { - let closest_on_ray = ray_origin + ray_dir * projection_t; - - let diff = closest_on_ray - instance_position; - let height_on_trunk = clamp(diff.y, 0.0, tree_height); - let closest_on_trunk = instance_position + vec3(0.0, height_on_trunk, 0.0); - - let perp_distance = length(closest_on_trunk - closest_on_ray); - - if perp_distance < occlusion_radius { - let dissolve_t = pow(perp_distance / occlusion_radius, 0.5); - dissolve_amount = 1.0 - clamp(dissolve_t, 0.0, 1.0); - } - } - } - } - - output.dissolve_amount = dissolve_amount; + output.dissolve_amount = input.instance_dissolve; output.uv = input.uv; return output; @@ -80,11 +41,10 @@ fn vs_main(input: VertexInput) -> VertexOutput { @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { if uniforms.enable_dissolve == 1u && in.dissolve_amount > 0.0 { - let screen_pos = in.clip_position.xy; - let noise_uv = fract(screen_pos / 128.0); - let noise_value = textureSampleLevel(blue_noise_texture, blue_noise_sampler, noise_uv, 0.0).r; - - if noise_value < in.dissolve_amount { + let screen_pos = in.clip_position.xy / in.clip_position.w; + let noise_uv = screen_pos * 0.5 + 0.5; + let noise = textureSample(blue_noise_texture, blue_noise_sampler, noise_uv * 10.0).r; + if in.dissolve_amount > noise { discard; } } diff --git a/src/snow.rs b/src/snow.rs index 6f34184..d26b20d 100644 --- a/src/snow.rs +++ b/src/snow.rs @@ -505,10 +505,23 @@ impl SnowLayer }) }); + let mut aabb_min = Vec3::splat(f32::MAX); + let mut aabb_max = Vec3::splat(f32::MIN); + for v in &vertices + { + let p = Vec3::from(v.position); + aabb_min = aabb_min.min(p); + aabb_max = aabb_max.max(p); + } + Mesh { vertex_buffer, index_buffer, num_indices: indices.len() as u32, + aabb_min, + aabb_max, + cpu_positions: Vec::new(), + cpu_indices: Vec::new(), } } @@ -568,6 +581,7 @@ impl SnowLayer enable_dissolve: false, enable_snow_light: true, displacement_bind_group: Some(self.displacement_bind_group.clone()), + entity: None, }) .collect() } diff --git a/src/systems/tree_dissolve.rs b/src/systems/tree_dissolve.rs index 7f379e3..86f4a66 100644 --- a/src/systems/tree_dissolve.rs +++ b/src/systems/tree_dissolve.rs @@ -1,15 +1,21 @@ -use crate::components::DissolveComponent; +use crate::loaders::mesh::InstanceRaw; use crate::world::World; +use bytemuck::cast_slice; pub fn tree_dissolve_update_system(world: &mut World, delta: f32) { - for entity in world.dissolves.all() + for entity in world.tree_instances.all() { - if let Some(dissolve) = world.dissolves.get_mut(entity) + if let Some(tree_instances) = world.tree_instances.get_mut(entity) { - let diff = dissolve.target_amount - dissolve.amount; - dissolve.amount += diff * dissolve.transition_speed * delta; - dissolve.amount = dissolve.amount.clamp(0.0, 1.0); + for i in 0..tree_instances.dissolve_amounts.len() + { + let diff = tree_instances.dissolve_targets[i] - tree_instances.dissolve_amounts[i]; + tree_instances.dissolve_amounts[i] += + diff * tree_instances.transition_speed * delta; + tree_instances.dissolve_amounts[i] = + tree_instances.dissolve_amounts[i].clamp(0.0, 1.0); + } } } } @@ -35,52 +41,70 @@ pub fn tree_occlusion_system(world: &mut World) } let to_player_normalized = to_player.normalize(); + let occlusion_radius = 10.0; - for tree_entity in world.tree_tags.all() + for tree_entity in world.tree_instances.all() { - if let Some(tree_transform) = world.transforms.get(tree_entity) + if let Some(tree_instances) = world.tree_instances.get_mut(tree_entity) { - let tree_pos = tree_transform.position; - let to_tree = tree_pos - camera_pos; - let distance_to_tree = to_tree.length(); - - if distance_to_tree < distance_to_player + for (idx, instance) in tree_instances.instances.iter().enumerate() { - let projection = to_tree.dot(to_player_normalized); + let instance_pos = instance.position; + let to_instance = instance_pos - camera_pos; + let distance_to_instance = to_instance.length(); - if projection > 0.0 + if distance_to_instance < distance_to_player { - let perpendicular_vec = to_tree - to_player_normalized * projection; - let perp_distance = perpendicular_vec.length(); + let projection = to_instance.dot(to_player_normalized); - let occlusion_radius = 2.5; - - if perp_distance < occlusion_radius + if projection > 0.0 && projection <= distance_to_player { - let dissolve_amount = - 1.0 - (perp_distance / occlusion_radius).clamp(0.0, 1.0); + let perpendicular_vec = + to_instance - to_player_normalized * projection; + let perp_distance = perpendicular_vec.length(); - if let Some(dissolve) = world.dissolves.get_mut(tree_entity) + if perp_distance <= occlusion_radius { - dissolve.target_amount = dissolve_amount; + let dissolve_amount = + 1.0 - (perp_distance / occlusion_radius).clamp(0.0, 1.0); + tree_instances.dissolve_targets[idx] = dissolve_amount; + continue; } - else - { - let mut dissolve = DissolveComponent::new(); - dissolve.target_amount = dissolve_amount; - world.dissolves.insert(tree_entity, dissolve); - } - continue; } } - } - if let Some(dissolve) = world.dissolves.get_mut(tree_entity) - { - dissolve.target_amount = 0.0; + tree_instances.dissolve_targets[idx] = 0.0; } } } } } } + +pub fn tree_instance_buffer_update_system(world: &mut World) +{ + for entity in world.tree_instances.all() + { + if let Some(tree_instances) = world.tree_instances.get(entity) + { + let instance_data_vec: Vec = tree_instances + .instances + .iter() + .enumerate() + .map(|(idx, instance)| InstanceRaw { + model: instance.to_raw().model, + dissolve_amount: tree_instances.dissolve_amounts[idx], + _padding: [0.0; 3], + }) + .collect(); + + crate::render::with_queue(|queue| { + queue.write_buffer( + &tree_instances.instance_buffer, + 0, + cast_slice(&instance_data_vec), + ); + }); + } + } +}