diff --git a/crates/bevy_tiles/src/coords.rs b/crates/bevy_tiles/src/coords.rs index ffa6a28..a956f8b 100644 --- a/crates/bevy_tiles/src/coords.rs +++ b/crates/bevy_tiles/src/coords.rs @@ -1,3 +1,5 @@ +use bevy::log::debug; + /// Calculate the coordinate of a chunk from a given tile coordinate and chunk size #[inline] pub fn calculate_chunk_coordinate( @@ -5,7 +7,11 @@ pub fn calculate_chunk_coordinate( chunk_size: usize, ) -> [isize; N] { for i in tile_c.iter_mut() { - *i = *i / (chunk_size as isize) - if *i < 0 { 1 } else { 0 } + if *i < 0 { + *i = (*i + 1) / (chunk_size as isize) - 1; + } else { + *i /= chunk_size as isize; + } } tile_c } @@ -173,6 +179,7 @@ mod tests { #[case(16, [15, 15], 255)] #[case(16, [-1, -1], 255)] #[case(16, [-16, -16], 0)] + #[case(8, [-8, -0], 0)] fn tile_index_test( #[case] chunk_size: usize, #[case] tile_c: [isize; 2], diff --git a/crates/bevy_tiles_render/examples/basic_2d.rs b/crates/bevy_tiles_render/examples/basic_2d.rs deleted file mode 100644 index f724cb6..0000000 --- a/crates/bevy_tiles_render/examples/basic_2d.rs +++ /dev/null @@ -1,119 +0,0 @@ -use bevy::{prelude::*, sprite::SpriteBundle, DefaultPlugins}; -use bevy_tiles::prelude::*; -use bevy_tiles_render::{maps::TilemapTextureSize, prelude::*, tiles::TileTextureIndex}; - -fn main() { - App::new() - .add_plugins(DefaultPlugins) - .add_plugins((TilesPlugin, TilesRenderPlugin)) - .add_systems(Startup, spawn) - .add_systems(Update, move_character) - .add_systems(PostUpdate, sync_tile_transforms) - .run(); -} - -#[derive(Component)] -struct Block; - -#[derive(Component)] -struct Character; - -struct GameLayer; - -impl TileMapLabel for GameLayer { - const CHUNK_SIZE: usize = 16; -} - -fn spawn(mut commands: Commands, asset_server: Res) { - let block = asset_server.load("block.png"); - let grass = asset_server.load("ground.png"); - let dirt = asset_server.load("grass.png"); - let character = asset_server.load("character.png"); - - commands.spawn(Camera2dBundle::default()); - let mut tile_commands = commands.tiles::(); - - tile_commands.spawn_map(TilemapBundle { - textures: vec![block, dirt, grass].into(), - tile_size: 16.0.into(), - grid_size: 16.0.into(), - spacing: 0.0.into(), - }); - - // fill the outside with grass - tile_commands.spawn_tile_batch(CoordIterator::new([-100, -100], [100, 100]), move |_| { - TileBundle { - tile_texture_index: TileTextureIndex(1), - ..Default::default() - } - }); - - // spawn a 10 * 10 room - tile_commands.spawn_tile_batch(CoordIterator::new([-5, -5], [5, 5]), move |_| { - ( - Block, - TileBundle { - tile_texture_index: TileTextureIndex(0), - ..Default::default() - }, - ) - }); - - // fill the inside with dirt - tile_commands.spawn_tile_batch(CoordIterator::new([-4, -4], [4, 4]), move |_| TileBundle { - tile_texture_index: TileTextureIndex(1), - ..Default::default() - }); - - // spawn a player (player is not part of the tilemap rendering, just a normal bevy sprite) - tile_commands.spawn_tile( - [0, 0], - ( - Character, - TileTransformSync, - SpriteBundle { - texture: character, - ..Default::default() - }, - ), - ); -} - -fn move_character( - keyboard_input: Res>, - mut commands: Commands, - character: TileQuery>, - walls: TileQuery>, -) { - let mut tile_commands = commands.tiles::(); - - let mut x = if keyboard_input.just_pressed(KeyCode::A) { - -1 - } else { - 0 - }; - x += if keyboard_input.just_pressed(KeyCode::D) { - 1 - } else { - 0 - }; - - let mut y = if keyboard_input.just_pressed(KeyCode::W) { - 1 - } else { - 0 - }; - - y -= if keyboard_input.just_pressed(KeyCode::S) { - 1 - } else { - 0 - }; - - let char_c = character.get_single().unwrap(); - let new_coord = [char_c[0] + x, char_c[1] + y]; - - if (x != 0 || y != 0) && walls.get_at(new_coord).is_none() { - tile_commands.move_tile(**char_c, new_coord); - } -} diff --git a/crates/bevy_tiles_render/examples/hello_tile.rs b/crates/bevy_tiles_render/examples/hello_tile.rs index 94fba1c..be06bd5 100644 --- a/crates/bevy_tiles_render/examples/hello_tile.rs +++ b/crates/bevy_tiles_render/examples/hello_tile.rs @@ -1,6 +1,7 @@ use bevy::{ app::{App, Startup}, core_pipeline::core_2d::Camera2dBundle, + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, ecs::system::Commands, math::{Quat, Vec3}, transform::{ @@ -9,7 +10,9 @@ use bevy::{ }, DefaultPlugins, }; -use bevy_tiles::{commands::TileCommandExt, maps::TileMapLabel, TilesPlugin}; +use bevy_tiles::{ + commands::TileCommandExt, coords::CoordIterator, maps::TileMapLabel, TilesPlugin, +}; use bevy_tiles_render::{ maps::{TileGridSize, TileMapRenderer, TileMapRenderingBundle, TileSize}, TilesRenderPlugin, @@ -17,7 +20,11 @@ use bevy_tiles_render::{ fn main() { App::new() - .add_plugins(DefaultPlugins) + .add_plugins(( + DefaultPlugins, + FrameTimeDiagnosticsPlugin, + LogDiagnosticsPlugin::default(), + )) .add_plugins((TilesPlugin, TilesRenderPlugin)) .add_systems(Startup, spawn) .run(); @@ -26,23 +33,18 @@ fn main() { struct GameLayer; impl TileMapLabel for GameLayer { - const CHUNK_SIZE: usize = 16; + const CHUNK_SIZE: usize = 8; } fn spawn(mut commands: Commands) { let mut tile_commands = commands.tiles::(); tile_commands.spawn_map(TileMapRenderingBundle { tile_size: TileSize(16.0), - grid_size: TileGridSize(24.0), + grid_size: TileGridSize(18.0), ..Default::default() }); - for i in -10..10 { - tile_commands.spawn_tile([0, 10 * i], ()); - } - for i in -10..10 { - tile_commands.spawn_tile([10 * i, 0], ()); - } + tile_commands.spawn_tile_batch(CoordIterator::new([-100, -100], [100, 100]), move |_| ()); commands.spawn(Camera2dBundle { ..Default::default() diff --git a/crates/bevy_tiles_render/src/bindings.rs b/crates/bevy_tiles_render/src/bindings.rs index 8e72fc5..6109411 100644 --- a/crates/bevy_tiles_render/src/bindings.rs +++ b/crates/bevy_tiles_render/src/bindings.rs @@ -2,19 +2,25 @@ use std::num::NonZeroU64; use bevy::{ ecs::{component::Component, world::FromWorld}, + log::debug, math::{Affine3, Vec2, Vec4}, render::{ render_resource::{ BindGroup, BindGroupEntries, BindGroupLayout, BindGroupLayoutDescriptor, - BindGroupLayoutEntry, BindingType, Buffer, BufferBindingType, BufferInitDescriptor, - BufferUsages, ShaderSize, ShaderStages, ShaderType, UniformBuffer, + BindGroupLayoutEntry, BindingType, Buffer, BufferAddress, BufferBindingType, + BufferDescriptor, BufferInitDescriptor, BufferUsages, CommandEncoder, ShaderSize, + ShaderStages, ShaderType, StorageBuffer, UniformBuffer, }, renderer::{RenderDevice, RenderQueue}, }, transform::components::GlobalTransform, }; -use crate::{buffer_helpers::*, maps::internal::MapInfo}; +use crate::{ + buffer_helpers::*, + chunk::{self, internal::ChunkUniforms}, + maps::internal::MapInfo, +}; #[derive(Component)] pub struct ChunkBatchBindGroups { @@ -22,6 +28,98 @@ pub struct ChunkBatchBindGroups { pub chunk_bind_group: BindGroup, } +/// Contains all the data for an individual chunk that can be +/// consolidated into the batch buffers. +#[derive(Component)] +pub struct ChunkBuffer { + pub chunk_offset: Vec2, + pub tile_instances: GpuStorageBuffer, +} + +impl ChunkBuffer { + pub fn new(chunk_uniforms: &ChunkUniforms) -> Self { + Self { + chunk_offset: Vec2::from(&chunk_uniforms.chunk_coord), + tile_instances: GpuStorageBuffer::::from(chunk_uniforms.tile_instances.clone()), + } + } + + pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) { + self.tile_instances.write_buffer(device, queue); + } +} + +#[derive(Component)] +pub struct ChunkBatchBuffer { + total_chunk_size: u64, + batch_size: u64, + pub chunk_offsets: GpuStorageBuffer, + pub tile_instances: Buffer, +} + +impl ChunkBatchBuffer { + pub fn with_size_no_default_values( + batch_size: usize, + chunk_size: usize, + device: &RenderDevice, + ) -> Self { + let total_chunk_size = chunk_size as u64 * chunk_size as u64; + Self { + total_chunk_size, + batch_size: batch_size as u64, + chunk_offsets: GpuStorageBuffer::::default(), + tile_instances: device.create_buffer(&BufferDescriptor { + label: None, + size: total_chunk_size * batch_size as u64 * u32::SHADER_SIZE.get(), + usage: BufferUsages::COPY_DST | BufferUsages::STORAGE, + mapped_at_creation: false, + }), + } + } + + /// # Note + /// after call push, write_buffer needs to be called as well as using the commands + /// from the command encoders to finish the copying. + pub fn push(&mut self, command_encoder: &mut CommandEncoder, chunk_buffer: &ChunkBuffer) { + let index = self.chunk_offsets.push(chunk_buffer.chunk_offset); + command_encoder.copy_buffer_to_buffer( + chunk_buffer.tile_instances.gpu_buffer().unwrap(), + 0, + &self.tile_instances, + index.get() as u64 * self.total_chunk_size * u32::SHADER_SIZE.get(), + self.total_chunk_size * u32::SHADER_SIZE.get(), + ) + } + + pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) { + self.chunk_offsets.write_buffer(device, queue); + } + + pub fn bindings(&self) -> BindGroupEntries<2> { + BindGroupEntries::with_indices(( + (0, self.chunk_offsets.binding().unwrap()), + (1, self.tile_instances.as_entire_binding()), + )) + } + + pub fn layout_entries() -> Vec { + vec![ + // off_sets + GpuStorageBuffer::::binding_layout(0, ShaderStages::VERTEX_FRAGMENT), + BindGroupLayoutEntry { + binding: 1, + visibility: ShaderStages::VERTEX_FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: Some(u32::min_size()), + }, + count: None, + }, + ] + } +} + #[derive(Component)] pub struct MapBatchBuffer { chunk_size: UniformBuffer, @@ -148,10 +246,7 @@ impl FromWorld for ChunkBatchBindGroupLayouts { let chunk_layouts = device.create_bind_group_layout(&BindGroupLayoutDescriptor { label: Some("bevy_tiles_chunk_bind_group"), - entries: &[GpuStorageBuffer::::binding_layout( - 0, - ShaderStages::VERTEX_FRAGMENT, - )], + entries: &ChunkBatchBuffer::layout_entries(), }); Self { diff --git a/crates/bevy_tiles_render/src/buffer_helpers/gpu_storage_buffer.rs b/crates/bevy_tiles_render/src/buffer_helpers/gpu_storage_buffer.rs index 01d91cb..b07df95 100644 --- a/crates/bevy_tiles_render/src/buffer_helpers/gpu_storage_buffer.rs +++ b/crates/bevy_tiles_render/src/buffer_helpers/gpu_storage_buffer.rs @@ -4,8 +4,8 @@ use bevy::{ ecs::{component::Component, system::Resource, world::FromWorld}, render::{ render_resource::{ - BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType, - GpuArrayBufferable, ShaderStages, StorageBuffer, + BindGroupLayoutEntry, BindingResource, BindingType, Buffer, BufferBindingType, + BufferUsages, GpuArrayBufferable, ShaderStages, StorageBuffer, }, renderer::{RenderDevice, RenderQueue}, }, @@ -14,16 +14,28 @@ use bevy::{ /// Stores an array of elements to be transferred to the GPU and made accessible to shaders as a read-only array. /// This is modified from bevy's GpuArrayBuffer -#[derive(Resource, Component)] pub struct GpuStorageBuffer { gpu_buffer: StorageBuffer>, buffer: Vec, } +impl From> for GpuStorageBuffer { + fn from(value: Vec) -> Self { + let mut gpu_buffer: StorageBuffer> = Default::default(); + gpu_buffer.add_usages(BufferUsages::COPY_SRC); + Self { + gpu_buffer, + buffer: value, + } + } +} + impl Default for GpuStorageBuffer { fn default() -> Self { + let mut gpu_buffer: StorageBuffer> = Default::default(); + gpu_buffer.add_usages(BufferUsages::COPY_SRC); Self { - gpu_buffer: Default::default(), + gpu_buffer, buffer: Default::default(), } } @@ -34,6 +46,10 @@ impl GpuStorageBuffer { self.buffer.clear() } + pub fn gpu_buffer(&self) -> Option<&Buffer> { + self.gpu_buffer.buffer() + } + pub fn push(&mut self, value: T) -> NonMaxU32 { let index = NonMaxU32::new(self.buffer.len() as u32).unwrap(); self.buffer.push(value); @@ -61,9 +77,8 @@ impl GpuStorageBuffer { pub fn binding(&self) -> Option { self.gpu_buffer.binding() } - - pub fn batch_size(device: &RenderDevice) -> Option { - None + pub fn insert(&mut self, index: usize, value: T) { + *self.buffer.get_mut(index).unwrap() = value; } // Fails if used on the wrong size buffer diff --git a/crates/bevy_tiles_render/src/chunk/internal.rs b/crates/bevy_tiles_render/src/chunk/internal.rs index d712892..a092d32 100644 --- a/crates/bevy_tiles_render/src/chunk/internal.rs +++ b/crates/bevy_tiles_render/src/chunk/internal.rs @@ -2,9 +2,7 @@ use bevy::{ ecs::{component::Component, entity::Entity}, prelude::Deref, }; - -#[derive(Component)] -pub struct TileInstances(Vec); +use bevy_tiles::chunks::ChunkCoord; #[derive(Component, Deref)] pub struct BatchSize(pub u32); @@ -13,7 +11,11 @@ pub struct BatchSize(pub u32); #[derive(Component, Deref, Clone)] pub struct ChunkBatch(pub Entity); -/// Holds a vector the size of the chunk that indicates whether -/// a tile exists at that index in the chunk or not. -#[derive(Component, Deref)] -pub struct ExtractedTileInstances(pub Vec); +/// Data needed to render a chunk in the batched chunk rendering pipeline. +/// This needs to be able to be instantiated in the extract stage and should +/// not have knowledge of the batch it's in. +#[derive(Component)] +pub struct ChunkUniforms { + pub chunk_coord: ChunkCoord, + pub tile_instances: Vec, +} diff --git a/crates/bevy_tiles_render/src/extract.rs b/crates/bevy_tiles_render/src/extract.rs index 01d41f1..b8ca17c 100644 --- a/crates/bevy_tiles_render/src/extract.rs +++ b/crates/bevy_tiles_render/src/extract.rs @@ -16,7 +16,7 @@ use bevy_tiles::{ }; use crate::{ - chunk::internal::ExtractedTileInstances, + chunk::internal::ChunkUniforms, maps::{internal::MapInfo, TileGridSize, TileMapRenderer, TileSize}, }; @@ -64,10 +64,10 @@ pub fn extract_chunks( let mut extracted_tile_instances = Vec::with_capacity(chunk.total_size()); for tile in chunk.get_tiles() { - if let Some(()) = tile.and_then(|tile_id| tiles.get(tile_id).ok()) { - extracted_tile_instances.push(true); + if tile.and_then(|tile_id| tiles.get(tile_id).ok()).is_some() { + extracted_tile_instances.push(1); } else { - extracted_tile_instances.push(false); + extracted_tile_instances.push(0); } } @@ -75,10 +75,10 @@ pub fn extract_chunks( extracted_chunks.push(( chunk_id, - ( - ExtractedTileInstances(extracted_tile_instances), - *chunk_coord, - ), + ChunkUniforms { + chunk_coord: *chunk_coord, + tile_instances: extracted_tile_instances, + }, )); } diff --git a/crates/bevy_tiles_render/src/lib.rs b/crates/bevy_tiles_render/src/lib.rs index 3318cd8..43b154d 100644 --- a/crates/bevy_tiles_render/src/lib.rs +++ b/crates/bevy_tiles_render/src/lib.rs @@ -7,21 +7,15 @@ use bevy::{ render::{ render_phase::AddRenderCommand, render_resource::{Shader, SpecializedRenderPipelines}, - renderer::RenderDevice, ExtractSchedule, Render, RenderApp, RenderSet, }, }; use extract::extract_chunks; -use prepare::{create_bind_groups, prepare_chunk_batch}; +use prepare::{create_bind_groups, prepare_chunk_batch, prepare_chunks}; use queue::{create_chunk_batches, queue_chunks}; -use crate::{ - bindings::MapTransformUniform, - buffer_helpers::gpu_storage_buffer::GpuStorageBuffer, - draw::{DrawChunkBatch, DrawChunks}, - pipeline::TilesChunkPipeline, -}; +use crate::{draw::DrawChunks, pipeline::TilesChunkPipeline}; mod bindings; mod buffer_helpers; @@ -57,7 +51,13 @@ impl Plugin for TilesRenderPlugin { ) .add_systems( Render, - (prepare_chunk_batch, apply_deferred, create_bind_groups) + ( + prepare_chunks, + apply_deferred, + prepare_chunk_batch, + apply_deferred, + create_bind_groups, + ) .chain() .in_set(RenderSet::Prepare), ); @@ -68,7 +68,6 @@ impl Plugin for TilesRenderPlugin { render_app.add_render_command::(); render_app - .init_resource::>() .init_resource::() .init_resource::>(); diff --git a/crates/bevy_tiles_render/src/prepare.rs b/crates/bevy_tiles_render/src/prepare.rs index 4b6aed9..17888cc 100644 --- a/crates/bevy_tiles_render/src/prepare.rs +++ b/crates/bevy_tiles_render/src/prepare.rs @@ -1,17 +1,11 @@ -use std::{ - ops::Deref, - sync::atomic::{AtomicUsize, Ordering}, -}; - use bevy::{ ecs::{ entity::Entity, system::{Commands, Query, Res}, }, log::debug, - math::Vec2, render::{ - render_resource::BindGroupEntries, + render_resource::CommandEncoderDescriptor, renderer::{RenderDevice, RenderQueue}, }, utils::hashbrown::HashMap, @@ -19,26 +13,42 @@ use bevy::{ use bevy_tiles::chunks::ChunkCoord; use crate::{ - bindings::{ChunkBatchBindGroups, MapBatchBuffer, MapTransformUniform}, - buffer_helpers::GpuStorageBuffer, - chunk::internal::{BatchSize, ChunkBatch}, + bindings::{ChunkBatchBindGroups, ChunkBatchBuffer, ChunkBuffer, MapBatchBuffer}, + chunk::internal::{BatchSize, ChunkBatch, ChunkUniforms}, maps::internal::MapInfo, pipeline::TilesChunkPipeline, }; +// Write individual chunk data to the GPU before consolidation +pub fn prepare_chunks( + mut commands: Commands, + device: Res, + queue: Res, + chunks: Query<(Entity, &ChunkUniforms)>, +) { + let chunks_iter = chunks.iter(); + let mut uniform_buffers = Vec::with_capacity(chunks_iter.len()); + for (chunk_id, chunk_uniform) in chunks_iter { + let mut buffer = ChunkBuffer::new(chunk_uniform); + buffer.write_buffer(&device, &queue); + uniform_buffers.push((chunk_id, buffer)); + } + commands.insert_or_spawn_batch(uniform_buffers); +} + // Consolidate individual chunk information into the batch entities pub fn prepare_chunk_batch( mut commands: Commands, device: Res, queue: Res, - chunks: Query<(&ChunkBatch, &ChunkCoord)>, + chunks: Query<(&ChunkBatch, &ChunkBuffer)>, chunk_batches: Query<(Entity, &BatchSize, &MapInfo)>, ) { let batch_iter = chunk_batches.iter(); let batch_count = batch_iter.len(); let mut instance_indices = HashMap::with_capacity(batch_count); - let mut transform_buffers = HashMap::with_capacity(batch_count); + let mut chunk_batch_buffers = HashMap::with_capacity(batch_count); let mut global_uniform_buffers = Vec::with_capacity(batch_count); if batch_iter.len() == 0 { @@ -52,10 +62,14 @@ pub fn prepare_chunk_batch( ); // Create all our instance buffers before we start iterating over chunks - instance_indices.insert(batch_id, AtomicUsize::new(0)); - transform_buffers.insert( + instance_indices.insert(batch_id, 0); + chunk_batch_buffers.insert( batch_id, - GpuStorageBuffer::::with_size(**batch_size as usize), + ChunkBatchBuffer::with_size_no_default_values( + **batch_size as usize, + map_info.chunk_size as usize, + &device, + ), ); // Create all our global uniforms for the batches @@ -66,44 +80,30 @@ pub fn prepare_chunk_batch( global_uniform_buffers.push((batch_id, (map_buffers))); } - chunks.par_iter().for_each(|(batch_id, chunk_coord)| { - let batch_index = instance_indices[&**batch_id].fetch_add(1, Ordering::Acquire); - add_chunk_to_batch( - **batch_id, - batch_index, - &device, - &queue, - &transform_buffers, - chunk_coord, - ) + let mut command_encoder = device.create_command_encoder(&CommandEncoderDescriptor { + label: Some("bevy_tiles_render::batch_buffer_copies"), }); - for (_, buffer) in transform_buffers.iter_mut() { + for (batch_id, chunk_buffer) in chunks.iter() { + let chunk_batch_buffer = chunk_batch_buffers.get_mut(&**batch_id).unwrap(); + chunk_batch_buffer.push(&mut command_encoder, chunk_buffer); + } + + for (_, buffer) in chunk_batch_buffers.iter_mut() { buffer.write_buffer(&device, &queue) } - commands.insert_or_spawn_batch(global_uniform_buffers); - commands.insert_or_spawn_batch(transform_buffers); -} + queue.submit([command_encoder.finish()]); -#[inline] -fn add_chunk_to_batch( - batch: Entity, - batch_index: usize, - device: &RenderDevice, - queue: &RenderQueue, - transform_buffers: &HashMap>, - chunk_coord: &ChunkCoord, -) { - // SAFETY: Each chunk can only write to one place because math - unsafe { transform_buffers[&batch].raw_insert(batch_index, Vec2::from(chunk_coord)) }; + commands.insert_or_spawn_batch(global_uniform_buffers); + commands.insert_or_spawn_batch(chunk_batch_buffers); } pub fn create_bind_groups( mut commands: Commands, device: Res, chunk_pipeline: Res, - chunk_batches: Query<(Entity, &MapBatchBuffer, &GpuStorageBuffer)>, + chunk_batches: Query<(Entity, &MapBatchBuffer, &ChunkBatchBuffer)>, ) { // Create bind groups debug!( @@ -120,7 +120,7 @@ pub fn create_bind_groups( let chunk_bind_group = device.create_bind_group( "batch_chunk_bind_group", &chunk_pipeline.chunk_batch_bind_groups.chunk_layouts, - &BindGroupEntries::single(chunk_offsets.binding().unwrap()), + &chunk_offsets.bindings(), ); debug!("Adding bind groups to batch {:?}", batch_id); diff --git a/crates/bevy_tiles_render/src/queue.rs b/crates/bevy_tiles_render/src/queue.rs index 2e4075c..310cb2a 100644 --- a/crates/bevy_tiles_render/src/queue.rs +++ b/crates/bevy_tiles_render/src/queue.rs @@ -21,7 +21,7 @@ use bevy::{ use bevy_tiles::chunks::InMap; use crate::{ - chunk::internal::{BatchSize, ChunkBatch, ExtractedTileInstances}, + chunk::internal::{BatchSize, ChunkBatch, ChunkUniforms}, draw::DrawChunks, maps::internal::MapInfo, pipeline::TilesChunkPipeline, @@ -30,7 +30,7 @@ use crate::{ pub fn create_chunk_batches( mut commands: Commands, maps: Query<((Entity, &MapInfo), Relations)>, - chunks: Query<(Entity, &ExtractedTileInstances)>, + chunks: Query<(Entity, &ChunkUniforms)>, ) { for ((map_id, map_info), edges) in maps.iter() { let max_batch_size = map_info.tile_map_renderer.batch_size; diff --git a/crates/bevy_tiles_render/src/shaders/tiles_frag.wgsl b/crates/bevy_tiles_render/src/shaders/tiles_frag.wgsl index a153a52..0f2eca6 100644 --- a/crates/bevy_tiles_render/src/shaders/tiles_frag.wgsl +++ b/crates/bevy_tiles_render/src/shaders/tiles_frag.wgsl @@ -1,3 +1,7 @@ + +@group(1) @binding(1) var chunk_size: u32; +@group(2) @binding(1) var tile_instances: array; + struct FragIn { @location(0) tile_index: u32, @location(1) chunk_index: u32, @@ -6,5 +10,9 @@ struct FragIn { /// Entry point for the fragment shader @fragment fn fs_main(in: FragIn) -> @location(0) vec4 { + let global_tile_index = in.chunk_index * chunk_size * chunk_size + in.tile_index; + if tile_instances[global_tile_index] == 0u{ + discard; + } return vec4(1.0, 1.0, 1.0, 1.0); } \ No newline at end of file diff --git a/crates/bevy_tiles_render/src/shaders/tiles_vert.wgsl b/crates/bevy_tiles_render/src/shaders/tiles_vert.wgsl index b1724da..5d3a24e 100644 --- a/crates/bevy_tiles_render/src/shaders/tiles_vert.wgsl +++ b/crates/bevy_tiles_render/src/shaders/tiles_vert.wgsl @@ -51,12 +51,13 @@ fn vs_main(v: VertIn) -> VertOut { f32(tile_index % chunk_size), f32((tile_index / chunk_size) % chunk_size) ); + let grid_offset = grid_size - tile_size; let chunk_offset = f32(chunk_size) * chunk_offsets[chunk_index]; let vertex_position = // Base tile position tile_size * (positions[v.vertex_index % 6u] + tile_offset + chunk_offset) + // Account for grid size - (grid_size - tile_size) * (tile_offset + chunk_offset); + (grid_offset) * (tile_offset + chunk_offset) + vec2(grid_offset / 2.0); let model = affine_to_square(mesh.model); let clip_position = mesh2d_position_local_to_clip(model, vec4(vertex_position, 1.0, 1.0));