tree dissolve + instances
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ pub fn render_collider_debug() -> Vec<DrawCall>
|
||||
enable_dissolve: false,
|
||||
enable_snow_light: false,
|
||||
displacement_bind_group: None,
|
||||
entity: None,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -183,6 +184,7 @@ pub fn render_collider_debug() -> Vec<DrawCall>
|
||||
enable_dissolve: false,
|
||||
enable_snow_light: false,
|
||||
displacement_bind_group: None,
|
||||
entity: None,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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<u32>,
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<dyn std::error::Error>>
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -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<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.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<f32> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -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<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