diff --git a/assets/shaders/vertex.wgsl b/assets/shaders/vertex.wgsl new file mode 100644 index 0000000..ae2bb89 --- /dev/null +++ b/assets/shaders/vertex.wgsl @@ -0,0 +1,136 @@ +#import bevy_pbr::{ + pbr_types, + pbr_functions::alpha_discard, + pbr_fragment::pbr_input_from_standard_material, + decal::clustered::apply_decal_base_color, + mesh_functions, + view_transformations::position_world_to_clip +} + +#ifdef PREPASS_PIPELINE +#import bevy_pbr::{ + prepass_io::{VertexOutput, FragmentOutput}, + pbr_deferred_functions::deferred_output, +} +#else +#import bevy_pbr::{ + forward_io::{VertexOutput, FragmentOutput}, + pbr_functions, + pbr_functions::{apply_pbr_lighting, main_pass_post_lighting_processing}, + pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT, +} +#endif + +#ifdef MESHLET_MESH_MATERIAL_PASS +#import bevy_pbr::meshlet_visibility_buffer_resolve::resolve_vertex_output +#endif + +#ifdef OIT_ENABLED +#import bevy_core_pipeline::oit::oit_draw +#endif // OIT_ENABLED + +#ifdef FORWARD_DECAL +#import bevy_pbr::decal::forward::get_forward_decal_info +#endif + +struct Vertex { + @builtin(instance_index) instance_index: u32, + @location(0) position: vec3, + @location(1) normal: vec3, + // @location(2) cell_kind: u32 + @location(5) color: vec4, +}; + +struct FlatVertexOutput { + // This is `clip position` when the struct is used as a vertex stage output + // and `frag coord` when used as a fragment stage input + @builtin(position) position: vec4, + @location(0) world_position: vec4, + @location(1) @interpolate(flat) world_normal: vec3, + // @location(2) @interpolate(flat) cell_kind: u32 + @location(5) @interpolate(flat) color: vec4, + @location(6) @interpolate(flat) instance_index: u32, +} + +// struct FragmentOutput { +// @location(0) color: vec4, +// } + +@vertex +fn vertex(vert: Vertex) -> FlatVertexOutput { + var out: FlatVertexOutput; + + let mesh_world_from_local = mesh_functions::get_world_from_local(vert.instance_index); + + out.world_normal = mesh_functions::mesh_normal_local_to_world( + vert.normal, + vert.instance_index + ); + + out.world_position = mesh_functions::mesh_position_local_to_world(mesh_world_from_local, vec4(vert.position, 1.0)); + out.position = position_world_to_clip(out.world_position.xyz); + + // out.cell_kind = vert.cell_kind; + out.instance_index = vert.instance_index; + out.color = vert.color; + + return out; +} + +@fragment +fn fragment( + vertex_output: FlatVertexOutput, + @builtin(front_facing) is_front: bool +) -> FragmentOutput { + var in: VertexOutput; + in.instance_index = vertex_output.instance_index; + in.position = vertex_output.position; + in.world_normal = vertex_output.world_normal; + in.color = vertex_output.color; + + // generate a PbrInput struct from the StandardMaterial bindings + var pbr_input = pbr_input_from_standard_material(in, is_front); + + // alpha discard + pbr_input.material.base_color = alpha_discard(pbr_input.material, pbr_input.material.base_color); + + // // clustered decals + // pbr_input.material.base_color = apply_decal_base_color( + // in.world_position.xyz, + // in.position.xy, + // pbr_input.material.base_color + // ); + +#ifdef PREPASS_PIPELINE + // write the gbuffer, lighting pass id, and optionally normal and motion_vector textures + let out = deferred_output(in, pbr_input); +#else + // in forward mode, we calculate the lit color immediately, and then apply some post-lighting effects here. + // in deferred mode the lit color and these effects will be calculated in the deferred lighting shader + var out: FragmentOutput; + if (pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u { + out.color = apply_pbr_lighting(pbr_input); + } else { + out.color = pbr_input.material.base_color; + } + + // apply in-shader post processing (fog, alpha-premultiply, and also tonemapping, debanding if the camera is non-hdr) + // note this does not include fullscreen postprocessing effects like bloom. + out.color = main_pass_post_lighting_processing(pbr_input, out.color); +#endif + +#ifdef OIT_ENABLED + let alpha_mode = pbr_input.material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS; + if alpha_mode != pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE { + // The fragments will only be drawn during the oit resolve pass. + oit_draw(in.position, out.color); + discard; + } +#endif // OIT_ENABLED + +#ifdef FORWARD_DECAL + out.color.a = min(forward_decal_info.alpha, out.color.a); +#endif + + return out; +} \ No newline at end of file diff --git a/src/camera.rs b/src/camera.rs index f339737..0bce988 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -24,7 +24,6 @@ fn setup(mut cmds: Commands, window: Query<&Window>) { DirectionalLight { color: Color::WHITE, illuminance: 17000., - //shadows_enabled: true, ..Default::default() }, Transform::default().looking_to(Vec3::new(1., -1., -1.), Vec3::ZERO), diff --git a/src/map.rs b/src/map.rs index b5b20b7..ed38006 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,7 +1,9 @@ +use core::f32; use std::time::Duration; use bevy::{ asset::RenderAssetUsages, + pbr::MaterialExtension, picking::PickSet, prelude::*, render::mesh::{Indices, PrimitiveTopology}, @@ -15,7 +17,10 @@ mod cells; mod picking; pub use animals::AnimalKind; pub use cells::CellKind; -use cells::*; +use cells::{ + material::{CellMaterial, CellMaterialExtension}, + *, +}; use picking::*; pub struct Plugin; @@ -25,7 +30,8 @@ impl bevy::prelude::Plugin for Plugin { .add_systems(PreUpdate, picking_backend.in_set(PickSet::Backend)) .add_systems(Update, (update_map_mesh, cells_regeneration, expand)) .insert_resource(ClearColor(Color::srgb(0., 0., 1.))) - .insert_resource(Seed(thread_rng().gen())); + .insert_resource(Seed(thread_rng().gen())) + .add_plugins(MaterialPlugin::::default()); } } @@ -39,16 +45,16 @@ pub const SIZE: usize = 10000; #[derive(Resource)] struct Seed(u32); -#[derive(Component)] +#[derive(Resource)] pub struct Voronoi(voronoice::Voronoi); #[derive(Component)] pub struct MapMarker; #[derive(Component)] -struct MapColors(Vec<[f32; 4]>); +struct MapMeshColors(Vec<[f32; 4]>); -#[derive(Component)] +#[derive(Resource)] pub struct CellsEntities(Vec); #[derive(Component)] @@ -57,7 +63,8 @@ pub struct MeshNeedsUpdate(bool); fn setup( mut cmds: Commands, mut meshes: ResMut>, - mut materials: ResMut>, + // mut materials: ResMut>, + mut materials: ResMut>, seed: Res, ) { let mut rng = rand::rngs::SmallRng::seed_from_u64(seed.0 as u64); @@ -81,7 +88,6 @@ fn setup( let c = voronoi.cell(i); let site = c.site_position(); let z = get_altitude(&z_noise, &[site.x as f32, site.y as f32]); - info!(z); let _m = ( (moisture_noise.get([site.x, site.y]) + 1.) / 2. // Noise + [0; 1] @@ -105,27 +111,42 @@ fn setup( let mut poss = Vec::new(); let mut indices = Vec::new(); - let mut normals = Vec::new(); + // let mut normals = Vec::new(); - for (c, cd) in voronoi.iter_cells().zip(cells.iter_mut()) { - let vs = c.iter_vertices().collect::>(); - let i = poss.len(); - for v in vs.iter() { - poss.push(Vec3::new( - v.x as f32, - v.y as f32, - (get_altitude(&z_noise, &[v.x as f32, v.y as f32]) / 1.), - )); - normals.push(Vec3::new(0., 0., 1.)); - } - for v in 1..(vs.len() - 1) { - indices.extend_from_slice(&[(i + v + 1) as u32, (i + v) as u32, i as u32]); - cd.vertices.extend_from_slice(&[i, i + v, i + v + 1]); - } + for (i, pos) in voronoi.sites().iter().enumerate() { + let z = get_altitude(&z_noise, &[pos.x as f32, pos.y as f32]); + poss.push(Vec3::new(pos.x as f32, pos.y as f32, z)); + cells[i].vertices.push(i); } + for t in voronoi.triangulation().triangles.chunks_exact(3) { + indices.extend_from_slice(&[t[2] as u32, t[1] as u32, t[0] as u32]); + } + // indices.extend(voronoi.triangulation().triangles.iter().map(|t| *t as u32)); + + // for (c, cd) in voronoi.iter_cells().zip(cells.iter_mut()) { + // let vs = c.iter_vertices().collect::>(); + // let i = poss.len(); + // for v in vs.iter() { + // let z = get_altitude(&z_noise, &[v.x as f32, v.y as f32]); + // poss.push(Vec3::new(v.x as f32, v.y as f32, z)); + // // const EPSILON: f32 = 0.01; + // // let dzx = get_altitude(&z_noise, &[v.x as f32 + EPSILON, v.y as f32]) - z; + // // let nx = (f32::consts::FRAC_PI_2 - (dzx / EPSILON).atan()).cos(); + // // let dzy = get_altitude(&z_noise, &[v.x as f32, v.y as f32 + EPSILON]) - z; + // // let ny = (f32::consts::FRAC_PI_2 - (dzy / EPSILON).atan()).cos(); + + // // let nz = (1. - (nx.powi(2) + ny.powi(2))).sqrt(); + + // // normals.push(Vec3::new(nx, ny, nz)); + // } + // for v in 1..(vs.len() - 1) { + // indices.extend_from_slice(&[(i + v + 1) as u32, (i + v) as u32, i as u32]); + // cd.vertices.extend_from_slice(&[i, i + v, i + v + 1]); + // } + // } let colors = vec![[0.; 4]; poss.len()]; - let mesh = Mesh::new( + let mut mesh = Mesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -133,17 +154,26 @@ fn setup( // 3D space), for each of the corners of the parallelogram. .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, poss) .with_inserted_attribute(Mesh::ATTRIBUTE_COLOR, colors.clone()) - .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + // .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) .with_inserted_indices(Indices::U32(indices)); + mesh.compute_smooth_normals(); + mesh.generate_tangents(); + // mesh.duplicate_vertices(); + // mesh.compute_flat_normals(); + let mut cells_entities = Vec::with_capacity(cells.len()); + cmds.spawn(( Mesh3d(meshes.add(mesh)), // StandardMaterial - MeshMaterial3d(materials.add(StandardMaterial::default())), + // MeshMaterial3d(materials.add(StandardMaterial::default())), + MeshMaterial3d(materials.add(CellMaterial { + base: StandardMaterial::default(), + extension: CellMaterialExtension {}, + })), Transform::default(), - Voronoi(voronoi), - MapColors(colors), + MapMeshColors(colors), MeshNeedsUpdate(true), MapMarker, )) @@ -166,13 +196,14 @@ fn setup( cmd.observe(self::cells::on_click); cells_entities.push(cmd.id()); } - }) - .insert(CellsEntities(cells_entities)); + }); + cmds.insert_resource(Voronoi(voronoi)); + cmds.insert_resource(CellsEntities(cells_entities)); } fn update_map_mesh( cells: Query<(&Cell, Option<&Wealth>)>, - mut map: Query<(&Mesh3d, &mut MapColors, &mut MeshNeedsUpdate), With>, + mut map: Query<(&Mesh3d, &mut MapMeshColors, &mut MeshNeedsUpdate), With>, mut meshes: ResMut>, ) { let (mesh, mut cols, mut needs_update) = map.single_mut(); diff --git a/src/map/cells.rs b/src/map/cells.rs index a90739f..3aaf6fc 100644 --- a/src/map/cells.rs +++ b/src/map/cells.rs @@ -7,6 +7,8 @@ use crate::{time::GameTime, ui::CurrentAction}; use super::{animals::Animal, AnimalKind, CellsEntities, MapMarker, MeshNeedsUpdate, Voronoi}; +pub mod material; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CellKind { Sea, @@ -94,12 +96,12 @@ pub fn cells_regeneration( pub fn expand( mut cells: Query<(&mut Cell, Option<&Wealth>)>, - map: Query<(&Voronoi, &CellsEntities)>, + voronoi: Res, + cells_entities: Res, mut cmds: Commands, t: Res