working voronoi representation using custom flat interpolation in
material shader
This commit is contained in:
parent
f46536ecc8
commit
9b4d9d74fc
136
assets/shaders/vertex.wgsl
Normal file
136
assets/shaders/vertex.wgsl
Normal file
@ -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<f32>,
|
||||
@location(1) normal: vec3<f32>,
|
||||
// @location(2) cell_kind: u32
|
||||
@location(5) color: vec4<f32>,
|
||||
};
|
||||
|
||||
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<f32>,
|
||||
@location(0) world_position: vec4<f32>,
|
||||
@location(1) @interpolate(flat) world_normal: vec3<f32>,
|
||||
// @location(2) @interpolate(flat) cell_kind: u32
|
||||
@location(5) @interpolate(flat) color: vec4<f32>,
|
||||
@location(6) @interpolate(flat) instance_index: u32,
|
||||
}
|
||||
|
||||
// struct FragmentOutput {
|
||||
// @location(0) color: vec4<f32>,
|
||||
// }
|
||||
|
||||
@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<f32>(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;
|
||||
}
|
@ -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),
|
||||
|
93
src/map.rs
93
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::<CellMaterial>::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<Entity>);
|
||||
|
||||
#[derive(Component)]
|
||||
@ -57,7 +63,8 @@ pub struct MeshNeedsUpdate(bool);
|
||||
fn setup(
|
||||
mut cmds: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
// mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut materials: ResMut<Assets<CellMaterial>>,
|
||||
seed: Res<Seed>,
|
||||
) {
|
||||
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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
// 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<MapMarker>>,
|
||||
mut map: Query<(&Mesh3d, &mut MapMeshColors, &mut MeshNeedsUpdate), With<MapMarker>>,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
) {
|
||||
let (mesh, mut cols, mut needs_update) = map.single_mut();
|
||||
|
@ -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<Voronoi>,
|
||||
cells_entities: Res<CellsEntities>,
|
||||
mut cmds: Commands,
|
||||
t: Res<Time>,
|
||||
gt: Res<GameTime>,
|
||||
) {
|
||||
let (voronoi, cells_entities) = map.single();
|
||||
let mut random = thread_rng();
|
||||
let mut changes = Vec::new();
|
||||
for (cell, wealth) in cells.iter() {
|
||||
@ -172,7 +174,7 @@ pub fn expand(
|
||||
pub fn on_click(
|
||||
trigger: Trigger<Pointer<Click>>,
|
||||
mut cells: Query<&mut Cell>,
|
||||
voronoi: Query<&Voronoi>,
|
||||
voronoi: Res<Voronoi>,
|
||||
mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>,
|
||||
mut cmds: Commands,
|
||||
ca: Res<CurrentAction>,
|
||||
@ -216,7 +218,7 @@ pub fn on_click(
|
||||
},
|
||||
CurrentAction::AddAnimal(ak) => {
|
||||
if cell.kind.can_place_animal(ak) {
|
||||
let v_cell = voronoi.single().0.cell(cell.voronoi_id);
|
||||
let v_cell = voronoi.0.cell(cell.voronoi_id);
|
||||
let cell_pos = v_cell.site_position();
|
||||
cmds.spawn((
|
||||
Animal { kind: ak },
|
||||
|
18
src/map/cells/material.rs
Normal file
18
src/map/cells/material.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use bevy::pbr::{ExtendedMaterial, MaterialExtension};
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::render_resource::*;
|
||||
|
||||
#[derive(Asset, AsBindGroup, Clone, Debug, Reflect)]
|
||||
pub struct CellMaterialExtension {}
|
||||
|
||||
pub type CellMaterial = ExtendedMaterial<StandardMaterial, CellMaterialExtension>;
|
||||
|
||||
const SHADER_PATH: &str = "shaders/vertex.wgsl";
|
||||
impl MaterialExtension for CellMaterialExtension {
|
||||
fn vertex_shader() -> ShaderRef {
|
||||
SHADER_PATH.into()
|
||||
}
|
||||
fn fragment_shader() -> ShaderRef {
|
||||
SHADER_PATH.into()
|
||||
}
|
||||
}
|
@ -8,19 +8,21 @@ use bevy::{
|
||||
};
|
||||
use voronoice::Point;
|
||||
|
||||
use crate::camera::CameraMarker;
|
||||
use super::{CellsEntities, MapMarker, Voronoi};
|
||||
use crate::camera::CameraMarker;
|
||||
|
||||
pub fn picking_backend(
|
||||
cam: Query<(&Transform, Entity), With<CameraMarker>>,
|
||||
window: Query<&Window, With<PrimaryWindow>>,
|
||||
pointers: Query<(&PointerId, &PointerLocation)>,
|
||||
map: Query<(&Voronoi, &CellsEntities, &Transform), With<MapMarker>>,
|
||||
map: Query<&Transform, With<MapMarker>>,
|
||||
voronoi: Res<Voronoi>,
|
||||
cells_entities: Res<CellsEntities>,
|
||||
mut output: EventWriter<PointerHits>,
|
||||
) {
|
||||
let (cam, cam_id) = cam.single();
|
||||
let window = window.single();
|
||||
let (voronoi, cells_entities, map_pos) = map.single();
|
||||
let map_pos = map.single();
|
||||
let mut last_cell = 0;
|
||||
for (id, l) in pointers.iter() {
|
||||
if let Some(mut pos) = l.location().map(|l| l.position) {
|
||||
|
Loading…
Reference in New Issue
Block a user