diff --git a/Cargo.lock b/Cargo.lock index 72cea9c..1eeca14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,6 +248,12 @@ dependencies = [ "syn 2.0.75", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.7.1" @@ -565,6 +571,7 @@ dependencies = [ "env_logger", "log", "pollster", + "rand", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1094,6 +1101,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "presser" version = "0.3.1" @@ -1142,6 +1158,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "range-alloc" version = "0.1.3" @@ -2246,6 +2292,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] diff --git a/Cargo.toml b/Cargo.toml index 8f306e6..5f6c946 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ wgpu = "22.1" cfg-if = "1" pollster = "0.3" bytemuck = { version = "1.17", features = [ "derive" ] } +rand = "0.8" [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = "0.1.6" diff --git a/src/graphics.rs b/src/graphics.rs new file mode 100644 index 0000000..d4b6101 --- /dev/null +++ b/src/graphics.rs @@ -0,0 +1,280 @@ +use bytemuck::{Pod, Zeroable}; +use wgpu::{include_wgsl, util::DeviceExt, BindGroup, Buffer, Device, Queue, RenderPipeline, Surface, SurfaceConfiguration, VertexBufferLayout}; +use winit::{ + event::{ElementState, Event, MouseButton, WindowEvent}, event_loop::EventLoop, window::Window +}; + +use crate::state::State; + +#[repr(C)] +#[derive(Clone, Copy, Zeroable, Pod, Debug)] +pub struct Vertex { + pub pos: [f32; 3], + pub color: [f32; 4] +} +impl Vertex { + const DESC: VertexBufferLayout<'static> = VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x4], + }; +} + +#[repr(C)] +#[derive(Clone, Copy, Zeroable, Pod, Debug)] +pub struct Uniforms { + // [x, y, zoom] + pub camera: [f32; 3], + pub darkness: f32 +} +impl Default for Uniforms { + fn default() -> Self { + Self { + camera: [0., 0., 1.], + darkness: 0. + } + } +} +impl Uniforms { + const DESC: VertexBufferLayout<'static> = VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32], + }; +} + +pub struct Graphics<'a> { + state: State, + window: &'a Window, + surface_config: SurfaceConfiguration, + surface: Surface<'a>, + device: Device, + render_pipeline: RenderPipeline, + queue: Queue, + vertex_buf: Buffer, + index_buf: Buffer, + uniforms_buf: Buffer, + uniforms_bind_group: BindGroup +} +impl<'a> Graphics<'a> { + pub async fn init(window: &'a Window, state: State) -> Self { + let mut size = window.inner_size(); + size.width = size.width.max(1); + size.height = size.height.max(1); + + let instance = wgpu::Instance::default(); + + let surface = instance.create_surface(window).unwrap(); + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + force_fallback_adapter: false, + // Request an adapter which can render to our surface + compatible_surface: Some(&surface), + }) + .await + .expect("Failed to find an appropriate adapter"); + + // Create the logical device and command queue + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::empty(), + // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain. + required_limits: wgpu::Limits::default() + .using_resolution(adapter.limits()), + memory_hints: wgpu::MemoryHints::MemoryUsage, + }, + None, + ) + .await + .expect("Failed to create device"); + + let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(&state.vertices), + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + }); + + let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(&state.indices), + usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, + }); + + let uniforms_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Uniforms Buffer"), + contents: bytemuck::cast_slice(&[state.uniforms]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + let uniforms_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + } + ], + label: Some("Uniforms Bind Group Layout"), + }); + let uniforms_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &uniforms_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: uniforms_buf.as_entire_binding(), + } + ], + label: Some("Uniforms Bind Group"), + }); + + + // Load the shaders from disk + let shader = device.create_shader_module(include_wgsl!("shader.wgsl")); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[ + &uniforms_bind_group_layout + ], + push_constant_ranges: &[], + }); + + let swapchain_capabilities = surface.get_capabilities(&adapter); + let swapchain_format = swapchain_capabilities.formats[0]; + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[ + Vertex::DESC + ], + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + compilation_options: Default::default(), + targets: &[Some(swapchain_format.into())], + }), + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + cache: None, + }); + + let surface_config = surface + .get_default_config(&adapter, size.width, size.height) + .unwrap(); + surface.configure(&device, &surface_config); + + Self { + state, + window, + surface_config, + surface, + device, + render_pipeline, + queue, + vertex_buf, + index_buf, + uniforms_buf, + uniforms_bind_group + } + } + pub fn run(&mut self, event_loop: EventLoop<()>) { + event_loop.run(move |event, target| { + // Have the closure take ownership of the resources. + // `event_loop.run` never returns, therefore we must do this to ensure + // the resources are properly cleaned up. + let _ = &self; + + match event { + Event::WindowEvent { + event: WindowEvent::Resized(new_size), + .. + } => { + // Reconfigure the surface with the new size + self.surface_config.width = new_size.width.max(1); + self.surface_config.height = new_size.height.max(1); + self.surface.configure(&self.device, &self.surface_config); + // On macos the window needs to be redrawn manually after resizing + self.window.request_redraw(); + }, + Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } => { + self.update(); + self.render(); + }, + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => target.exit(), + Event::AboutToWait => { + // RedrawRequested will only trigger once unless we manually + // request it. + self.window.request_redraw(); + }, + e => self.state.input(e) + } + + }) + .unwrap(); + } + fn render(&self) { + let frame = self.surface + .get_current_texture() + .expect("Failed to acquire next swap chain texture"); + let view = frame + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = + self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: None, + }); + { + let mut rpass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + rpass.set_pipeline(&self.render_pipeline); + rpass.set_bind_group(0, &self.uniforms_bind_group, &[]); + rpass.set_vertex_buffer(0, self.vertex_buf.slice(..)); + rpass.set_index_buffer(self.index_buf.slice(..), wgpu::IndexFormat::Uint32); + rpass.draw_indexed(0..self.state.indices.len() as u32, 0, 0..1); + } + + self.queue.submit(Some(encoder.finish())); + frame.present(); + } + fn update(&mut self) { + self.state.update(); + self.queue.write_buffer(&self.vertex_buf, 0, bytemuck::cast_slice(&self.state.vertices)); + self.queue.write_buffer(&self.index_buf, 0, bytemuck::cast_slice(&self.state.indices)); + self.queue.write_buffer(&self.uniforms_buf, 0, bytemuck::cast_slice(&[self.state.uniforms])); + } +} + diff --git a/src/main.rs b/src/main.rs index decb618..e7de5e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,162 +1,8 @@ -use wgpu::{include_wgsl, Device, Queue, RenderPipeline, Surface, SurfaceConfiguration}; -use winit::{ - event::{Event, WindowEvent}, event_loop::EventLoop, platform::web::WindowExtWebSys, window::Window -}; - -struct App<'a> { - window: &'a Window, - surface_config: SurfaceConfiguration, - surface: Surface<'a>, - device: Device, - render_pipeline: RenderPipeline, - queue: Queue -} -impl<'a> App<'a> { - async fn init(window: &'a Window) -> Self { - let mut size = window.inner_size(); - size.width = size.width.max(1); - size.height = size.height.max(1); - - let instance = wgpu::Instance::default(); - - let surface = instance.create_surface(window).unwrap(); - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - force_fallback_adapter: false, - // Request an adapter which can render to our surface - compatible_surface: Some(&surface), - }) - .await - .expect("Failed to find an appropriate adapter"); - - // Create the logical device and command queue - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - required_features: wgpu::Features::empty(), - // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain. - required_limits: wgpu::Limits::downlevel_webgl2_defaults() - .using_resolution(adapter.limits()), - memory_hints: wgpu::MemoryHints::MemoryUsage, - }, - None, - ) - .await - .expect("Failed to create device"); - - // Load the shaders from disk - let shader = device.create_shader_module(include_wgsl!("shader.wgsl")); - - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: None, - bind_group_layouts: &[], - push_constant_ranges: &[], - }); - - let swapchain_capabilities = surface.get_capabilities(&adapter); - let swapchain_format = swapchain_capabilities.formats[0]; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: None, - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[], - compilation_options: Default::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - compilation_options: Default::default(), - targets: &[Some(swapchain_format.into())], - }), - primitive: wgpu::PrimitiveState::default(), - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - cache: None, - }); - - let surface_config = surface - .get_default_config(&adapter, size.width, size.height) - .unwrap(); - surface.configure(&device, &surface_config); - - Self { - window, - surface_config, - surface, - device, - render_pipeline, - queue - } - } - fn run(&mut self, event_loop: EventLoop<()>) { - event_loop.run(move |event, target| { - // Have the closure take ownership of the resources. - // `event_loop.run` never returns, therefore we must do this to ensure - // the resources are properly cleaned up. - let _ = &self; - - if let Event::WindowEvent { - window_id: _, - event, - } = event - { - match event { - WindowEvent::Resized(new_size) => { - // Reconfigure the surface with the new size - self.surface_config.width = new_size.width.max(1); - self.surface_config.height = new_size.height.max(1); - self.surface.configure(&self.device, &self.surface_config); - // On macos the window needs to be redrawn manually after resizing - self.window.request_redraw(); - } - WindowEvent::RedrawRequested => { - let frame = self.surface - .get_current_texture() - .expect("Failed to acquire next swap chain texture"); - let view = frame - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let mut encoder = - self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: None, - }); - { - let mut rpass = - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - rpass.set_pipeline(&self.render_pipeline); - rpass.draw(0..3, 0..1); - } - - self.queue.submit(Some(encoder.finish())); - frame.present(); - } - WindowEvent::CloseRequested => target.exit(), - _ => {} - }; - } - }) - .unwrap(); - } -} +mod graphics; +mod state; +use state::State; +use graphics::Graphics; +use winit::event_loop::EventLoop; pub fn main() { #[cfg(target_arch = "wasm32")] @@ -174,11 +20,11 @@ pub fn main() { #[cfg(not(target_arch = "wasm32"))] { - let mut app = pollster::block_on(App::init(&window)); - app.run(event_loop); + pollster::block_on(Graphics::init(&window, State::new())).run(event_loop); } #[cfg(target_arch = "wasm32")] { + use winit::platform::web::WindowExtWebSys; web_sys::window() .unwrap() .document() @@ -188,7 +34,7 @@ pub fn main() { .append_child(&window.canvas().unwrap()) .unwrap(); wasm_bindgen_futures::spawn_local(async move { - App::init(&window).await.run(event_loop); + Graphics::init(&window, State::new()).await.run(event_loop); }); } } \ No newline at end of file diff --git a/src/shader.wgsl b/src/shader.wgsl index 859ffa4..4230ead 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -1,11 +1,27 @@ +struct Uniforms { + camera: vec3f, + darkness: f32 +} +@group(0) @binding(0) var uniforms : Uniforms; + +struct VertexOutput { + @location(0) color: vec4f, + @builtin(position) pos: vec4f +} + @vertex -fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4 { - let x = f32(i32(in_vertex_index) - 1); - let y = f32(i32(in_vertex_index & 1u) * 2 - 1); - return vec4(x, y, 0.0, 1.0); +fn vs_main( + @location(0) pos: vec3f, + @location(1) color: vec4f +) -> VertexOutput { + var out: VertexOutput; + out.color = color; + // out.color[3] -= uniforms.darkness; + out.pos = vec4f(pos.xy-uniforms.camera.xy, pos.z, uniforms.camera.z); + return out; } @fragment -fn fs_main() -> @location(0) vec4 { - return vec4(1.0, 0.0, 0.0, 1.0); +fn fs_main(v: VertexOutput) -> @location(0) vec4f { + return v.color; } \ No newline at end of file diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..e6e2d98 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,176 @@ +use std::time::Instant; +use rand::prelude::*; +use winit::event::{DeviceEvent, ElementState, Event, MouseButton, MouseScrollDelta, WindowEvent}; + +use crate::graphics::{Uniforms, Vertex}; + +pub const SQRT_3: f32 = 1.732050807568877293527446341505872367; + +#[repr(u8)] +#[derive(Debug, Clone, Copy)] +enum CellKind { + Void, + Sea, + Grass, +} +impl CellKind { + const VALID_CHARS: [char; 3] = ['v', 's', 'g']; + fn color(&self) -> [f32; 4] { + match self { + Self::Void => [0.; 4], + Self::Sea => [0., 0., 1., 1.], + Self::Grass => [0., 1., 0., 1.] + } + } +} +impl From for CellKind { + fn from(value: char) -> Self { + match value { + 'v' => Self::Void, + 's' => Self::Sea, + 'g' => Self::Grass, + _ => panic!("Invalid cell kind") + } + } +} +impl From for CellKind { + fn from(value: u8) -> Self { + match value { + 0 => Self::Void, + 1 => Self::Sea, + 2 => Self::Grass, + _ => panic!("Invalid cell kind") + } + } +} + +struct Cell { + kind: CellKind +} +impl Cell { + const RADIUS: f32 = 1.; + fn new(kind: CellKind) -> Self { + Self { + kind + } + } +} + +struct Map { + cells: [Cell; Self::SIZE] +} +impl Map { + const HEIGHT: usize = 10; + const WIDTH: usize = 10; + const SIZE: usize = Self::HEIGHT*Self::WIDTH; + fn new() -> Self { + std::array::from_fn(|_| thread_rng().gen_range(1..=2)).into() + // "sgssv + // ggsvg + // gsvvs + // vgsgs + // ssggs".into() + } + fn enumerate<'a>(&'a self) -> std::iter::Map>, fn((usize, &Cell)) -> ([usize; 2], &Cell)> { + self.cells.iter().enumerate().map(|(i, c)| ([i % Self::HEIGHT, i / Self::WIDTH], c)) + } +} +impl From<&str> for Map { + fn from(value: &str) -> Self { + let mut chars = value.chars().filter(|c| CellKind::VALID_CHARS.contains(c)); + let cells = std::array::from_fn(|_| Cell::new(chars.next().expect("Invalid map size").into())); + Self { cells } + } +} +impl From<[u8; Map::SIZE]> for Map { + fn from(value: [u8; Map::SIZE]) -> Self { + Self { cells: value.map(|c| Cell::new(c.into())) } + } +} + +pub struct State { + pub vertices: Vec, + pub indices: Vec, + pub uniforms: Uniforms, + start: Instant, + map: Map +} +impl State { + pub fn new() -> Self { + let mut s = Self { + vertices: vec![], + indices: vec![], + uniforms: Uniforms::default(), + start: Instant::now(), + map: Map::new() + }; + s.update(); + s + } + pub fn input(&mut self, event: Event<()>) { + match event { + Event::WindowEvent { event: WindowEvent::MouseInput { state, button, ..}, ..} => { + if let state = ElementState::Pressed { + self.uniforms.camera[2] += match button { + MouseButton::Left => 0.1, + MouseButton::Right => -0.1, + _ => 0. + }; + } + }, + // Event::WindowEvent { event: WindowEvent::MouseWheel { delta, ..}, ..} => { + // self.uniforms.camera[2] -= match delta { + // MouseScrollDelta::PixelDelta(pos) => pos.y as f32, + // MouseScrollDelta::LineDelta(_, y) => y + // }; + // }, + Event::DeviceEvent { event: DeviceEvent::MouseWheel { delta }, ..} => { + self.uniforms.camera[2] += match delta { + MouseScrollDelta::PixelDelta(pos) => pos.y as f32, + MouseScrollDelta::LineDelta(_, y) => y + }; + }, + _ => {} + } + } + pub fn update(&mut self) { + self.vertices = Vec::with_capacity(self.map.cells.len()*6); + self.indices = Vec::with_capacity(self.map.cells.len()*12); + + for ([x, y], c) in self.map.enumerate() { + let x = x as f32; + let y = y as f32; + let i = self.vertices.len(); + let color = c.kind.color(); + let center = [(0.5+x+((y%2.)*0.5)) * (SQRT_3*Cell::RADIUS), -(0.5+y)*(1.5*Cell::RADIUS)]; + // self.vertices.push(Vertex { pos: [center[0], center[1], 0.], color }); + // self.vertices.push(Vertex { pos: [center[0]+0.1, center[1]+0.1, 0.], color }); + // self.vertices.push(Vertex { pos: [center[0]-0.1, center[1]+0.1, 0.], color }); + self.vertices.push(Vertex { pos: [center[0], center[1]+Cell::RADIUS, 0.], color: color.clone() }); + self.vertices.push(Vertex { pos: [center[0]-(0.5*SQRT_3*Cell::RADIUS), center[1]+(0.5*Cell::RADIUS), 0.], color: color.clone() }); + self.vertices.push(Vertex { pos: [center[0]+(0.5*SQRT_3*Cell::RADIUS), center[1]+(0.5*Cell::RADIUS), 0.], color: color.clone() }); + self.vertices.push(Vertex { pos: [center[0]-(0.5*SQRT_3*Cell::RADIUS), center[1]-(0.5*Cell::RADIUS), 0.], color: color.clone() }); + self.vertices.push(Vertex { pos: [center[0]+(0.5*SQRT_3*Cell::RADIUS), center[1]-(0.5*Cell::RADIUS), 0.], color: color.clone() }); + self.vertices.push(Vertex { pos: [center[0], center[1]-Cell::RADIUS, 0.], color: color.clone() }); + + self.indices.push(i as u32); + self.indices.push((i+1) as u32); + self.indices.push((i+2) as u32); + + self.indices.push((i+1) as u32); + self.indices.push((i+3) as u32); + self.indices.push((i+2) as u32); + + self.indices.push((i+3) as u32); + self.indices.push((i+4) as u32); + self.indices.push((i+2) as u32); + + self.indices.push((i+3) as u32); + self.indices.push((i+5) as u32); + self.indices.push((i+4) as u32); + } + + // dbg!(&self.vertices, &self.indices, &self.uniforms); + // dbg!(self.vertices.len(), self.indices.len(), &self.uniforms); + } +} \ No newline at end of file