tree dissolve + instances
This commit is contained in:
@@ -4,7 +4,7 @@ use glam::Vec2;
|
|||||||
use nalgebra::vector;
|
use nalgebra::vector;
|
||||||
use rapier3d::prelude::{ColliderBuilder, RigidBodyBuilder};
|
use rapier3d::prelude::{ColliderBuilder, RigidBodyBuilder};
|
||||||
|
|
||||||
use crate::components::{MeshComponent, PhysicsComponent};
|
use crate::components::{MeshComponent, PhysicsComponent, TreeInstancesComponent};
|
||||||
use crate::entity::EntityHandle;
|
use crate::entity::EntityHandle;
|
||||||
use crate::loaders::mesh::{InstanceData, InstanceRaw, Mesh};
|
use crate::loaders::mesh::{InstanceData, InstanceRaw, Mesh};
|
||||||
use crate::loaders::terrain::load_heightfield_from_exr;
|
use crate::loaders::terrain::load_heightfield_from_exr;
|
||||||
@@ -78,6 +78,7 @@ impl TerrainBundle
|
|||||||
enable_snow_light: true,
|
enable_snow_light: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
world.names.insert(entity, "Terrain".to_string());
|
||||||
|
|
||||||
if !physics_added
|
if !physics_added
|
||||||
{
|
{
|
||||||
@@ -125,7 +126,7 @@ impl TerrainBundle
|
|||||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("Tree Instance Buffer"),
|
label: Some("Tree Instance Buffer"),
|
||||||
contents: bytemuck::cast_slice(&instance_raw),
|
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 {
|
MeshComponent {
|
||||||
mesh: Rc::new(mesh),
|
mesh: Rc::new(mesh),
|
||||||
pipeline: render::Pipeline::Standard,
|
pipeline: render::Pipeline::Standard,
|
||||||
instance_buffer: Some(instance_buffer),
|
instance_buffer: Some(instance_buffer.clone()),
|
||||||
num_instances: num_instances as u32,
|
num_instances: num_instances as u32,
|
||||||
tile_scale: 4.0,
|
tile_scale: 4.0,
|
||||||
enable_dissolve: true,
|
enable_dissolve: true,
|
||||||
enable_snow_light: false,
|
enable_snow_light: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
world.tree_instances.insert(
|
||||||
|
entity,
|
||||||
|
TreeInstancesComponent::new(instances, Rc::new(instance_buffer)),
|
||||||
|
);
|
||||||
|
world.names.insert(entity, "Trees".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ pub fn render_collider_debug() -> Vec<DrawCall>
|
|||||||
enable_dissolve: false,
|
enable_dissolve: false,
|
||||||
enable_snow_light: false,
|
enable_snow_light: false,
|
||||||
displacement_bind_group: None,
|
displacement_bind_group: None,
|
||||||
|
entity: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -183,6 +184,7 @@ pub fn render_collider_debug() -> Vec<DrawCall>
|
|||||||
enable_dissolve: false,
|
enable_dissolve: false,
|
||||||
enable_snow_light: false,
|
enable_snow_light: false,
|
||||||
displacement_bind_group: None,
|
displacement_bind_group: None,
|
||||||
|
entity: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,26 @@ use bytemuck::{Pod, Zeroable};
|
|||||||
use glam::{Mat4, Quat, Vec3};
|
use glam::{Mat4, Quat, Vec3};
|
||||||
use std::path::Path;
|
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)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||||
pub struct Vertex
|
pub struct Vertex
|
||||||
@@ -113,6 +133,10 @@ pub struct Mesh
|
|||||||
pub vertex_buffer: wgpu::Buffer,
|
pub vertex_buffer: wgpu::Buffer,
|
||||||
pub index_buffer: wgpu::Buffer,
|
pub index_buffer: wgpu::Buffer,
|
||||||
pub num_indices: u32,
|
pub num_indices: u32,
|
||||||
|
pub aabb_min: Vec3,
|
||||||
|
pub aabb_max: Vec3,
|
||||||
|
pub cpu_positions: Vec<[f32; 3]>,
|
||||||
|
pub cpu_indices: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mesh
|
impl Mesh
|
||||||
@@ -121,6 +145,8 @@ impl Mesh
|
|||||||
{
|
{
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
let (aabb_min, aabb_max) = compute_aabb(vertices);
|
||||||
|
|
||||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("Vertex Buffer"),
|
label: Some("Vertex Buffer"),
|
||||||
contents: bytemuck::cast_slice(vertices),
|
contents: bytemuck::cast_slice(vertices),
|
||||||
@@ -137,6 +163,10 @@ impl Mesh
|
|||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
index_buffer,
|
index_buffer,
|
||||||
num_indices: indices.len() as u32,
|
num_indices: indices.len() as u32,
|
||||||
|
aabb_min,
|
||||||
|
aabb_max,
|
||||||
|
cpu_positions: vertices.iter().map(|v| v.position).collect(),
|
||||||
|
cpu_indices: indices.to_vec(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ use crate::systems::{
|
|||||||
camera_follow_system, camera_input_system, camera_view_matrix, physics_sync_system,
|
camera_follow_system, camera_input_system, camera_view_matrix, physics_sync_system,
|
||||||
player_input_system, render_system, rotate_system, snow_system, spotlight_sync_system,
|
player_input_system, render_system, rotate_system, snow_system, spotlight_sync_system,
|
||||||
start_camera_following, state_machine_physics_system, state_machine_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,
|
trigger_system,
|
||||||
};
|
};
|
||||||
use crate::systems::camera::stop_camera_following;
|
use crate::systems::camera::stop_camera_following;
|
||||||
@@ -295,6 +296,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>>
|
|||||||
|
|
||||||
rotate_system(&mut world, delta);
|
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);
|
let spotlights = spotlight_sync_system(&world);
|
||||||
render::update_spotlights(spotlights);
|
render::update_spotlights(spotlights);
|
||||||
|
|
||||||
|
|||||||
@@ -32,46 +32,7 @@ fn vs_main(input: VertexInput) -> VertexOutput {
|
|||||||
);
|
);
|
||||||
output.world_normal = normalize(normal_matrix * input.normal);
|
output.world_normal = normalize(normal_matrix * input.normal);
|
||||||
|
|
||||||
var dissolve_amount = 0.0;
|
output.dissolve_amount = input.instance_dissolve;
|
||||||
|
|
||||||
if uniforms.enable_dissolve == 1u {
|
|
||||||
let instance_position = vec3<f32>(
|
|
||||||
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<f32>(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.uv = input.uv;
|
output.uv = input.uv;
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
@@ -80,11 +41,10 @@ fn vs_main(input: VertexInput) -> VertexOutput {
|
|||||||
@fragment
|
@fragment
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
if uniforms.enable_dissolve == 1u && in.dissolve_amount > 0.0 {
|
if uniforms.enable_dissolve == 1u && in.dissolve_amount > 0.0 {
|
||||||
let screen_pos = in.clip_position.xy;
|
let screen_pos = in.clip_position.xy / in.clip_position.w;
|
||||||
let noise_uv = fract(screen_pos / 128.0);
|
let noise_uv = screen_pos * 0.5 + 0.5;
|
||||||
let noise_value = textureSampleLevel(blue_noise_texture, blue_noise_sampler, noise_uv, 0.0).r;
|
let noise = textureSample(blue_noise_texture, blue_noise_sampler, noise_uv * 10.0).r;
|
||||||
|
if in.dissolve_amount > noise {
|
||||||
if noise_value < in.dissolve_amount {
|
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/snow.rs
14
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 {
|
Mesh {
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
index_buffer,
|
index_buffer,
|
||||||
num_indices: indices.len() as u32,
|
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_dissolve: false,
|
||||||
enable_snow_light: true,
|
enable_snow_light: true,
|
||||||
displacement_bind_group: Some(self.displacement_bind_group.clone()),
|
displacement_bind_group: Some(self.displacement_bind_group.clone()),
|
||||||
|
entity: None,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
use crate::components::DissolveComponent;
|
use crate::loaders::mesh::InstanceRaw;
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
|
use bytemuck::cast_slice;
|
||||||
|
|
||||||
pub fn tree_dissolve_update_system(world: &mut World, delta: f32)
|
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;
|
for i in 0..tree_instances.dissolve_amounts.len()
|
||||||
dissolve.amount += diff * dissolve.transition_speed * delta;
|
{
|
||||||
dissolve.amount = dissolve.amount.clamp(0.0, 1.0);
|
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 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;
|
for (idx, instance) in tree_instances.instances.iter().enumerate()
|
||||||
let to_tree = tree_pos - camera_pos;
|
{
|
||||||
let distance_to_tree = to_tree.length();
|
let instance_pos = instance.position;
|
||||||
|
let to_instance = instance_pos - camera_pos;
|
||||||
|
let distance_to_instance = to_instance.length();
|
||||||
|
|
||||||
if distance_to_tree < distance_to_player
|
if distance_to_instance < distance_to_player
|
||||||
{
|
{
|
||||||
let projection = to_tree.dot(to_player_normalized);
|
let projection = to_instance.dot(to_player_normalized);
|
||||||
|
|
||||||
if projection > 0.0
|
if projection > 0.0 && projection <= distance_to_player
|
||||||
{
|
{
|
||||||
let perpendicular_vec = to_tree - to_player_normalized * projection;
|
let perpendicular_vec =
|
||||||
|
to_instance - to_player_normalized * projection;
|
||||||
let perp_distance = perpendicular_vec.length();
|
let perp_distance = perpendicular_vec.length();
|
||||||
|
|
||||||
let occlusion_radius = 2.5;
|
if perp_distance <= occlusion_radius
|
||||||
|
|
||||||
if perp_distance < occlusion_radius
|
|
||||||
{
|
{
|
||||||
let dissolve_amount =
|
let dissolve_amount =
|
||||||
1.0 - (perp_distance / occlusion_radius).clamp(0.0, 1.0);
|
1.0 - (perp_distance / occlusion_radius).clamp(0.0, 1.0);
|
||||||
|
tree_instances.dissolve_targets[idx] = dissolve_amount;
|
||||||
if let Some(dissolve) = world.dissolves.get_mut(tree_entity)
|
|
||||||
{
|
|
||||||
dissolve.target_amount = dissolve_amount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
let mut dissolve = DissolveComponent::new();
|
|
||||||
dissolve.target_amount = dissolve_amount;
|
|
||||||
world.dissolves.insert(tree_entity, dissolve);
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(dissolve) = world.dissolves.get_mut(tree_entity)
|
tree_instances.dissolve_targets[idx] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tree_instance_buffer_update_system(world: &mut World)
|
||||||
{
|
{
|
||||||
dissolve.target_amount = 0.0;
|
for entity in world.tree_instances.all()
|
||||||
}
|
{
|
||||||
}
|
if let Some(tree_instances) = world.tree_instances.get(entity)
|
||||||
}
|
{
|
||||||
|
let instance_data_vec: Vec<InstanceRaw> = 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),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user