diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2026-08-15 14:17:28 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2026-08-15 14:17:28 -0700 |
| commit | c94749cc88080ef7fef7b285f9072ea26d18e1eb (patch) | |
| tree | ba20a6e1fe918934c94a4e5c3f7370d6c6ce97d8 | |
| parent | b510f5fcf5335c5e26d65ca96d7ec16c2f050f5d (diff) | |
wip, working with native resolution
| -rw-r--r-- | src/camera.rs | 22 | ||||
| -rw-r--r-- | src/config.rs | 2 | ||||
| -rw-r--r-- | src/main.rs | 149 | ||||
| -rw-r--r-- | src/sim/chunk.rs | 2 | ||||
| -rw-r--r-- | src/sim/materials/mod.rs | 2 | ||||
| -rw-r--r-- | src/sim/sim.rs | 25 | ||||
| -rw-r--r-- | src/sim/world.rs | 10 |
7 files changed, 142 insertions, 70 deletions
diff --git a/src/camera.rs b/src/camera.rs index 3713b98..55a698c 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -8,7 +8,7 @@ pub struct Camera { // centre coords pub x: f64, pub y: f64, - // zoom scale factor, 1.0 = board <-> frame + // zoom scale factor, 1.0 = board <-> window pub zoom: f64, } @@ -56,14 +56,14 @@ impl Camera { pub fn write_frame_view(&self, frame: &mut [u8], world: &World) { puffin::profile_function!(); - for i in 0..frame.len() / 4 { - let idx = i * 4; - frame[idx] = (i / 1000) as u8; - frame[idx + 1] = 0x00; - frame[idx + 2] = 0x00; - frame[idx + 3] = 0xFF; - } - return; + // for i in 0..frame.len() / 4 { + // let idx = i * 4; + // frame[idx] = (i / 1000) as u8; + // frame[idx + 1] = 0x00; + // frame[idx + 2] = 0x00; + // frame[idx + 3] = 0xFF; + // } + // return; const BG: [u8; 4] = [0x00, 0x00, 0x00, 0xFF]; for px in frame.chunks_exact_mut(4) { @@ -74,7 +74,7 @@ impl Camera { let (xl, yl) = self.screen_position_to_world(0.0, 0.0); let (xu, yu) = self.screen_position_to_world(1600 as f64, 1200 as f64); - let c = CHUNK_SIZE as i32; + let c = CHUNK_SIZE; let cx0 = (xl.floor() as i32).div_euclid(c); let cx1 = (xu.ceil() as i32).div_euclid(c); let cy0 = (yl.floor() as i32).div_euclid(c); @@ -102,7 +102,7 @@ impl Camera { yl: f64, scale: f64, ) { - let c = CHUNK_SIZE as i32; + let c = CHUNK_SIZE; let w = 1600 as i32; let h = 1200 as i32; diff --git a/src/config.rs b/src/config.rs index 2e656d4..8e8f7a1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,6 @@ pub const WINDOW_TITLE: &str = "pxs"; -pub const CHUNK_SIZE: u32 = 32; +pub const CHUNK_SIZE: i32 = 32; pub const CELLS_IN_CHUNK: usize = (CHUNK_SIZE * CHUNK_SIZE) as usize; pub const CAMERA_MOVEMENT_SPEED: f32 = 10.0; diff --git a/src/main.rs b/src/main.rs index 483bfb2..d248a75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use egui::Id; use egui_wgpu::{RendererOptions, ScreenDescriptor}; use egui_winit::egui::{self, Context}; use futures::executor; +use rand::random_range; use std::{ sync::Arc, time::{Duration, Instant}, @@ -26,7 +27,7 @@ use winit::{ use crate::{ camera::Camera, config::WINDOW_TITLE, - sim::{materials::MaterialId, sim::sim_tick, world::World}, + sim::{cell::Cell, materials::MaterialId, sim::sim_tick, world::World}, ui::draw_egui, }; @@ -56,6 +57,11 @@ struct Diagnostics { fps: f32, } +struct RendererChunk { + texture: wgpu::Texture, + bind_group: wgpu::BindGroup, +} + struct RendererState { window: Arc<Window>, surface: wgpu::Surface<'static>, @@ -69,6 +75,8 @@ struct RendererState { egui_state: egui_winit::State, egui_renderer: egui_wgpu::Renderer, + // world pixels + renderer_chunks: Vec<RendererChunk>, pixels_pipeline: wgpu::RenderPipeline, pixels_bind_group: wgpu::BindGroup, texture: wgpu::Texture, @@ -168,13 +176,6 @@ impl RendererState { }], }); - let pixels_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: None, - immediate_size: 0, - bind_group_layouts: &[Some(&pixels_bind_group_layout)], - }); - let pixels_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, layout: &pixels_bind_group_layout, @@ -197,6 +198,13 @@ impl RendererState { source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), }); + let pixels_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + immediate_size: 0, + bind_group_layouts: &[Some(&pixels_bind_group_layout)], + }); + let pixels_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, layout: Some(&pixels_pipeline_layout), @@ -231,6 +239,67 @@ impl RendererState { cache: None, }); + let mut renderer_chunks: Vec<RendererChunk> = Vec::new(); + + // match number of world chunks + // TODO refactor so that this implicit + for _ in -10..10 { + for _ in -10..10 { + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: None, + mip_level_count: 1, + sample_count: 1, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + format: wgpu_types::TextureFormat::Rgba8UnormSrgb, + size: wgpu::Extent3d { + width: size.width, + height: size.height, + depth_or_array_layers: 1, + }, + dimension: wgpu::TextureDimension::D2, + view_formats: &[], + }); + + let bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: None, + entries: &[wgpu::BindGroupLayoutEntry { + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + binding: 0, + count: None, + visibility: wgpu::ShaderStages::FRAGMENT, + }], + }); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&texture.create_view( + &wgpu::TextureViewDescriptor { + dimension: Some(wgpu::TextureViewDimension::D2), + usage: Some( + wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::COPY_DST, + ), + ..wgpu::TextureViewDescriptor::default() + }, + )), + }], + }); + + renderer_chunks.push(RendererChunk { + texture, + bind_group, + }) + } + } + RendererState { window, surface, @@ -243,6 +312,7 @@ impl RendererState { egui_state, egui_renderer, + renderer_chunks: Vec::new(), texture, frame_view: vec![0; size.width as usize * size.height as usize * 4], pixels_pipeline, @@ -576,11 +646,9 @@ impl ApplicationHandler for App { } WindowEvent::CursorMoved { position, .. } => { self.input.last_mouse_pos_on_screen = Some((position.x, position.y)); - // self.input.last_mouse_pos_on_board = pixels - // .window_pos_to_pixel((position.x as f32, position.y as f32)) - // .map(|v| camera.screen_position_to_world(v.0 as f64, v.1 as f64)) - // .map(|v| (v.0 as i32, v.1 as i32)) - // .ok(); + let world_pos = camera.screen_position_to_world(position.x, position.y); + self.input.last_mouse_pos_on_board = + Some((world_pos.0 as i32, world_pos.1 as i32)) } WindowEvent::MouseInput { state, button, .. } => { if button == MouseButton::Left { @@ -611,35 +679,34 @@ impl ApplicationHandler for App { camera.handle_camera_input(&self.input, delta_time); // // --TEST DRAWING-- - // if self.input.is_lmb_pressed - // && let Some(lm) = self.input.last_mouse_pos_on_board - // { - // // start with the bounding box of the drawing brush circle + some margin - // // clamp the bounding box to the board sie - // // TODO better way to do this without unwrap? - // let bb_xl = lm.0 - self.config.brush_radius as i32; - // let bb_xu = lm.0 + self.config.brush_radius as i32; - // let bb_yl = lm.1 - self.config.brush_radius as i32; - // let bb_yu = lm.1 + self.config.brush_radius as i32; + if self.input.is_lmb_pressed + && let Some(lm) = self.input.last_mouse_pos_on_board + { + // start with the bounding box of the drawing brush circle + some margin + // clamp the bounding box to the board sie + let bb_xl = lm.0 - self.config.brush_radius as i32; + let bb_xu = lm.0 + self.config.brush_radius as i32; + let bb_yl = lm.1 - self.config.brush_radius as i32; + let bb_yu = lm.1 + self.config.brush_radius as i32; - // // for each point, check if the distance is less than the brush size and write the pixel - // for x in bb_xl..bb_xu { - // for y in bb_yl..bb_yu { - // let r = random_range(0.0..1.0); - // if ((x - lm.0).pow(2) + (y - lm.1).pow(2)) - // < (self.config.brush_radius as i32).pow(2) - // && r > 0.9 - // { - // let mut cell = Cell::from_material(self.config.brush_material); - // cell.flags = (self.sim_seqno as u8) & 0b1; - // world.set_cell_from_game_position( - // x, y, cell, // wake the chunk - // false, - // ); - // } - // } - // } - // } + // for each point, check if the distance is less than the brush size and write the pixel + for x in bb_xl..bb_xu { + for y in bb_yl..bb_yu { + let r = random_range(0.0..1.0); + if ((x - lm.0).pow(2) + (y - lm.1).pow(2)) + < (self.config.brush_radius as i32).pow(2) + && r > 0.9 + { + let mut cell = Cell::from_material(self.config.brush_material); + cell.flags = (self.sim_seqno as u8) & 0b1; + world.set_cell_from_game_position( + x, y, cell, // wake the chunk + false, + ); + } + } + } + } // TODO check if we need to run another sim tick given the sim speed + delta_time // SIM logic diff --git a/src/sim/chunk.rs b/src/sim/chunk.rs index cf4179a..1980932 100644 --- a/src/sim/chunk.rs +++ b/src/sim/chunk.rs @@ -6,6 +6,7 @@ use crate::{ pub struct Chunk { pub cells: Box<[Cell; CELLS_IN_CHUNK as usize]>, pub sleeping: bool, + pub needs_texture_update: bool, } impl Chunk { @@ -22,6 +23,7 @@ impl Chunk { Chunk { cells: Box::new([Cell::void(); CELLS_IN_CHUNK]), sleeping: true, + needs_texture_update: true, } } } diff --git a/src/sim/materials/mod.rs b/src/sim/materials/mod.rs index 581ce31..d2d0e0e 100644 --- a/src/sim/materials/mod.rs +++ b/src/sim/materials/mod.rs @@ -24,7 +24,7 @@ pub struct MaterialDef { static MATERIALS: [MaterialDef; 5] = [ MaterialDef { name: "Void", - color: (0xFF, 0xFF, 0x00, 0xFF), + color: (0x00, 0x00, 0x00, 0x00), density: 0, sim_update: None, }, diff --git a/src/sim/sim.rs b/src/sim/sim.rs index 6e0029e..ef2fc80 100644 --- a/src/sim/sim.rs +++ b/src/sim/sim.rs @@ -41,12 +41,12 @@ pub struct UpdateCtx<'a, 'b, 'c> { } fn get_cell(chunks: &[Option<&mut Chunk>; 9], x: i32, y: i32) -> Option<Cell> { - let dcx = x.div_euclid(CHUNK_SIZE as i32); - let dcy = y.div_euclid(CHUNK_SIZE as i32); + let dcx = x.div_euclid(CHUNK_SIZE); + let dcy = y.div_euclid(CHUNK_SIZE); if dcx != 0 || dcy != 0 { // in a different chunk - let nc_x = x.rem_euclid(CHUNK_SIZE as i32) as u8; - let nc_y = y.rem_euclid(CHUNK_SIZE as i32) as u8; + let nc_x = x.rem_euclid(CHUNK_SIZE) as u8; + let nc_y = y.rem_euclid(CHUNK_SIZE) as u8; return if let Some(chunk) = &chunks[(dcx + 1 + (dcy + 1) * 3) as usize] { Some(chunk.get_cell_at_local_position(nc_x, nc_y)) @@ -63,19 +63,21 @@ fn get_cell(chunks: &[Option<&mut Chunk>; 9], x: i32, y: i32) -> Option<Cell> { } pub fn set_cell(chunks: &mut [Option<&mut Chunk>; 9], x: i32, y: i32, cell: Cell) { - let dcx = x.div_euclid(CHUNK_SIZE as i32); - let dcy = y.div_euclid(CHUNK_SIZE as i32); + let dcx = x.div_euclid(CHUNK_SIZE); + let dcy = y.div_euclid(CHUNK_SIZE); if dcx != 0 || dcy != 0 { // in a different chunk - let nc_x = x.rem_euclid(CHUNK_SIZE as i32) as u8; - let nc_y = y.rem_euclid(CHUNK_SIZE as i32) as u8; + let nc_x = x.rem_euclid(CHUNK_SIZE) as u8; + let nc_y = y.rem_euclid(CHUNK_SIZE) as u8; if let Some(chunk) = &mut chunks[(dcx + 1 + (dcy + 1) * 3) as usize] { chunk.set_cell_at_local_position(nc_x, nc_y, cell); + chunk.needs_texture_update = true; } } else { if let Some(target) = &mut chunks[4] { target.set_cell_at_local_position(x as u8, y as u8, cell); + target.needs_texture_update = true; } } } @@ -118,12 +120,12 @@ pub fn sim_tick_chunk(chunks: &mut [Option<&mut Chunk>; 9], seqno: u64) { let seqno_parity = (seqno as u8) & 0b1; if chunks[4].is_some() { - for y in (0..CHUNK_SIZE as i32).rev() { - for i in 0..CHUNK_SIZE as i32 { + for y in (0..CHUNK_SIZE).rev() { + for i in 0..CHUNK_SIZE { let x = if seqno_parity == 0 { i } else { - (CHUNK_SIZE as i32) - i - 1 + (CHUNK_SIZE) - i - 1 }; let mut cell = get_cell(chunks, x, y).unwrap(); @@ -213,6 +215,7 @@ pub fn sim_tick(world: &mut World, seqno: u64, use_threading: bool) { }; if use_threading { + // TODO use forte color.par_iter().for_each(chunk_closure); } else { color.iter().for_each(chunk_closure); diff --git a/src/sim/world.rs b/src/sim/world.rs index 7775744..bdb1286 100644 --- a/src/sim/world.rs +++ b/src/sim/world.rs @@ -17,12 +17,12 @@ impl World { ( ( // TODO is this cast expensive? - x.div_euclid(CHUNK_SIZE as i32), - y.div_euclid(CHUNK_SIZE as i32), + x.div_euclid(CHUNK_SIZE), + y.div_euclid(CHUNK_SIZE), ), ( - x.rem_euclid(CHUNK_SIZE as i32) as u8, - y.rem_euclid(CHUNK_SIZE as i32) as u8, + x.rem_euclid(CHUNK_SIZE) as u8, + y.rem_euclid(CHUNK_SIZE) as u8, ), ) } @@ -64,7 +64,7 @@ impl World { }; for y in -10..10 { - for x in -50..50 { + for x in -10..10 { world.insert(x, y, Chunk::void()); } } |
