|
| 1 | +/*! Rendering. |
| 2 | + * Depends on parts of feldspar's rendering pipeline. |
| 3 | + */ |
| 4 | + |
| 5 | +use baustein::indices::{to_i32_arr, ChunkIndex}; |
| 6 | +use baustein::prefab::{ PaletteIdChunk, PaletteVoxel, World }; |
| 7 | +use baustein::re; |
| 8 | +use baustein::traits::{IterableSpace, Space}; |
| 9 | +use baustein::world::{ FlatPaddedGridCuboid, View }; |
| 10 | + |
| 11 | +use bevy::app; |
| 12 | +use bevy::prelude::*; |
| 13 | +use block_mesh; |
| 14 | +use block_mesh::{visible_block_faces, UnitQuadBuffer, MergeVoxel, RIGHT_HANDED_Y_UP_CONFIG, UnorientedUnitQuad}; |
| 15 | +use feldspar::bb::mesh::PosNormMesh; |
| 16 | +use feldspar::prelude::{ |
| 17 | + spawn_array_material, ArrayMaterial, SdfVoxelPalette, VoxelRenderAssets, VoxelType, VoxelTypeInfo, VoxelMaterial, |
| 18 | +}; |
| 19 | +use feldspar::renderer::create_voxel_mesh_bundle; |
| 20 | +use float_ord::FloatOrd; |
| 21 | +use std::cmp; |
| 22 | + |
| 23 | +use crate::stress::StressVoxel; |
| 24 | + |
| 25 | +// Older version needed for block_mesh |
| 26 | +type BlockMeshShape = block_mesh::ndshape::ConstShape3u32::<18, 18, 18>; |
| 27 | + |
| 28 | + |
| 29 | +/// Requires: `LoadingTexture` resource. |
| 30 | +pub struct Plugin; |
| 31 | + |
| 32 | +impl app::Plugin for Plugin { |
| 33 | + fn build(&self, app: &mut AppBuilder) { |
| 34 | + app |
| 35 | + .insert_resource(SdfVoxelPalette::new(vec![ |
| 36 | + VoxelTypeInfo { |
| 37 | + is_empty: true, |
| 38 | + material: VoxelMaterial::NULL, |
| 39 | + }, |
| 40 | + VoxelTypeInfo { |
| 41 | + is_empty: false, |
| 42 | + material: VoxelMaterial(0), |
| 43 | + }, |
| 44 | + VoxelTypeInfo { |
| 45 | + is_empty: false, |
| 46 | + material: VoxelMaterial(1), |
| 47 | + }, |
| 48 | + VoxelTypeInfo { |
| 49 | + is_empty: false, |
| 50 | + material: VoxelMaterial(2), |
| 51 | + }, |
| 52 | + VoxelTypeInfo { |
| 53 | + is_empty: false, |
| 54 | + material: VoxelMaterial(3), |
| 55 | + }, |
| 56 | + ])) |
| 57 | + .add_state(TextureState::Loading) |
| 58 | + .add_system_set( |
| 59 | + SystemSet::on_enter(TextureState::Loading) |
| 60 | + .with_system(start_loading_render_assets.system()), |
| 61 | + ) |
| 62 | + .add_system_set( |
| 63 | + SystemSet::on_update(TextureState::Loading) |
| 64 | + .with_system(wait_for_assets_loaded.system()), |
| 65 | + ) |
| 66 | + ; |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | + |
| 71 | +/// Stores the material to be used for rendering. |
| 72 | +/// Get one from `spawn_array_material`. |
| 73 | +#[derive(Default)] |
| 74 | +pub struct MeshMaterial(pub Handle<ArrayMaterial>); |
| 75 | + |
| 76 | +impl From<Handle<ArrayMaterial>> for MeshMaterial { |
| 77 | + fn from(v: Handle<ArrayMaterial>) -> Self { |
| 78 | + Self(v) |
| 79 | + } |
| 80 | +} |
| 81 | + |
| 82 | +/// The value of stress. |
| 83 | +#[derive(Clone, Copy)] |
| 84 | +enum Voxel { |
| 85 | + Empty, |
| 86 | + Stressed(f32), |
| 87 | +} |
| 88 | + |
| 89 | +impl Default for Voxel { |
| 90 | + fn default() -> Self { |
| 91 | + Voxel::Empty |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +impl block_mesh::Voxel for Voxel { |
| 96 | + fn is_empty(&self) -> bool { |
| 97 | + match self { |
| 98 | + Voxel::Empty => true, |
| 99 | + _ => false, |
| 100 | + } |
| 101 | + } |
| 102 | + fn is_opaque(&self) -> bool { !self.is_empty() } |
| 103 | +} |
| 104 | + |
| 105 | +type StressChunk = FlatPaddedGridCuboid<Voxel, re::ConstAnyShape<18, 18, 18>>; |
| 106 | + |
| 107 | +/// Contains analyzed data to visualize. |
| 108 | +pub struct Analyzed(StressChunk); |
| 109 | + |
| 110 | +/// To track which parts should be despawned and when |
| 111 | +pub struct StressMesh; |
| 112 | + |
| 113 | +pub fn generate_meshes( |
| 114 | + mut commands: Commands, |
| 115 | + mesh_material: Res<MeshMaterial>, |
| 116 | + spaces: Query<(&Analyzed, &Transform)>, |
| 117 | + mut meshes: ResMut<Assets<Mesh>>, |
| 118 | + chunk_meshes: Query<Entity, With<StressMesh>>, |
| 119 | +) { |
| 120 | + // Get rid of all meshes |
| 121 | + for cm in chunk_meshes.iter() { |
| 122 | + commands.entity(cm).despawn() |
| 123 | + } |
| 124 | + // And create the occupied ones again. |
| 125 | + // Wasteful, I know. I'm testing! |
| 126 | + for (space, transform) in spaces.iter() { |
| 127 | + let quads = generate_buffer_fast(&space.0); |
| 128 | + let material_lookup = |quad: &UnorientedUnitQuad| { |
| 129 | + let material = to_material(space.0.get(to_i32_arr(quad.minimum).into())); |
| 130 | + [material, material, material, material] |
| 131 | + }; |
| 132 | + let mesh = mesh_from_quads(quads, &space.0, material_lookup); |
| 133 | + if let Some((mesh, materials)) = mesh { |
| 134 | + commands |
| 135 | + .spawn_bundle( |
| 136 | + create_voxel_mesh_bundle( |
| 137 | + mesh, |
| 138 | + materials, |
| 139 | + mesh_material.0.clone(), |
| 140 | + &mut meshes, |
| 141 | + ) |
| 142 | + ) |
| 143 | + // This won't work within a hierarchy |
| 144 | + .insert(transform.clone()) |
| 145 | + .insert(StressMesh) |
| 146 | + ; |
| 147 | + } |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | +/// Takes advantage of data structures |
| 152 | +/// that already have samples in an array. |
| 153 | +fn generate_buffer_fast<V, Shape>( |
| 154 | + view: &FlatPaddedGridCuboid<V, Shape>, |
| 155 | +) -> UnitQuadBuffer |
| 156 | + where |
| 157 | + V: block_mesh::Voxel + Default + Copy, |
| 158 | + Shape: re::ConstShape, |
| 159 | +{ |
| 160 | + let faces = RIGHT_HANDED_Y_UP_CONFIG.faces; |
| 161 | + |
| 162 | + let samples = view.get_samples(); |
| 163 | + let mut buffer = UnitQuadBuffer::new(); |
| 164 | + |
| 165 | + visible_block_faces( |
| 166 | + samples, |
| 167 | + &BlockMeshShape {}, |
| 168 | + [0, 0, 0], |
| 169 | + [ |
| 170 | + <Shape as re::ConstShape>::ARRAY[0] as u32 - 1, |
| 171 | + <Shape as re::ConstShape>::ARRAY[1] as u32 - 1, |
| 172 | + <Shape as re::ConstShape>::ARRAY[2] as u32 - 1, |
| 173 | + ], |
| 174 | + &faces, |
| 175 | + &mut buffer, |
| 176 | + ); |
| 177 | + buffer |
| 178 | +} |
| 179 | + |
| 180 | +pub fn mesh_from_quads<S, V, M, F>( |
| 181 | + buffer: UnitQuadBuffer, |
| 182 | + view: &S, |
| 183 | + mut vertex_map: F, |
| 184 | +) -> Option<(PosNormMesh, Vec<M>)> |
| 185 | +where |
| 186 | + M: Clone, |
| 187 | + S: Space<Voxel=V>, |
| 188 | + F: FnMut(&UnorientedUnitQuad) -> [M; 4], |
| 189 | +{ |
| 190 | + if buffer.num_quads() == 0 { |
| 191 | + None |
| 192 | + } else { |
| 193 | + // Build mesh from quads |
| 194 | + let num_indices = buffer.num_quads() * 6; |
| 195 | + let num_vertices = buffer.num_quads() * 4; |
| 196 | + let mut indices = Vec::with_capacity(num_indices); |
| 197 | + let mut positions = Vec::with_capacity(num_vertices); |
| 198 | + let mut normals = Vec::with_capacity(num_vertices); |
| 199 | + let mut vertex_metadata = Vec::with_capacity(num_vertices); |
| 200 | + let faces = RIGHT_HANDED_Y_UP_CONFIG.faces; |
| 201 | + for (group, face) in buffer.groups.iter().zip(faces.iter()) { |
| 202 | + for quad in group.into_iter() { |
| 203 | + indices.extend_from_slice(&face.quad_mesh_indices(positions.len() as u32)); |
| 204 | + positions.extend_from_slice(&face.quad_mesh_positions(&((*quad).into()), 1.0)); |
| 205 | + normals.extend_from_slice(&face.quad_mesh_normals()); |
| 206 | + |
| 207 | + vertex_metadata.extend_from_slice(&vertex_map(&quad)); |
| 208 | + } |
| 209 | + } |
| 210 | + |
| 211 | + Some(( |
| 212 | + PosNormMesh { |
| 213 | + positions, |
| 214 | + normals, |
| 215 | + indices, |
| 216 | + }, |
| 217 | + vertex_metadata, |
| 218 | + )) |
| 219 | + } |
| 220 | +} |
| 221 | + |
| 222 | +fn to_material(v: Voxel) -> [u8; 4] { |
| 223 | + match v { |
| 224 | + Voxel::Empty => [0; 4], |
| 225 | + Voxel::Stressed(v) => { |
| 226 | + let v = FloatOrd(v); |
| 227 | + let max = 256.0; |
| 228 | + let stress = cmp::max(v, FloatOrd(0.0)); |
| 229 | + let stress = cmp::min(v, FloatOrd(max)).0; |
| 230 | + [stress as u8, (max - stress) as u8, 0, 0] |
| 231 | + }, |
| 232 | + } |
| 233 | +} |
| 234 | + |
| 235 | +// Texture loading |
| 236 | + |
| 237 | +#[derive(Clone, PartialEq, Eq, Debug, Hash)] |
| 238 | +pub enum TextureState { |
| 239 | + Loading, |
| 240 | + Ready, |
| 241 | +} |
| 242 | + |
| 243 | +/// A unique type for a resource containing the texture handle for this module. |
| 244 | +pub struct LoadingTexture(pub Handle<Texture>); |
| 245 | + |
| 246 | +fn start_loading_render_assets(mut commands: Commands, asset_server: Res<AssetServer>) { |
| 247 | + commands.insert_resource(LoadingTexture( |
| 248 | + asset_server.load("stress.png"), |
| 249 | + )); |
| 250 | +} |
| 251 | + |
| 252 | +// From feldspar |
| 253 | +fn wait_for_assets_loaded( |
| 254 | + mut commands: Commands, |
| 255 | + loading_texture: Res<LoadingTexture>, |
| 256 | + mut textures: ResMut<Assets<Texture>>, |
| 257 | + mut array_materials: ResMut<Assets<ArrayMaterial>>, |
| 258 | + mut state: ResMut<State<TextureState>>, |
| 259 | +) { |
| 260 | + if textures.get(&loading_texture.0).is_some() { |
| 261 | + let params = VoxelRenderAssets { |
| 262 | + mesh_base_color: loading_texture.0.clone(), |
| 263 | + image_count: 4, |
| 264 | + }; |
| 265 | + spawn_array_material::<MeshMaterial>(¶ms, commands, array_materials, textures); |
| 266 | + state.set(TextureState::Ready).unwrap(); |
| 267 | + } |
| 268 | +} |
0 commit comments