hexagons
This commit is contained in:
parent
58c158d2e9
commit
db1f756544
47
Cargo.lock
generated
47
Cargo.lock
generated
@ -248,6 +248,12 @@ dependencies = [
|
|||||||
"syn 2.0.75",
|
"syn 2.0.75",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.7.1"
|
version = "1.7.1"
|
||||||
@ -565,6 +571,7 @@ dependencies = [
|
|||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
"pollster",
|
"pollster",
|
||||||
|
"rand",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
@ -1094,6 +1101,15 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
|
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]]
|
[[package]]
|
||||||
name = "presser"
|
name = "presser"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -1142,6 +1158,36 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "range-alloc"
|
name = "range-alloc"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -2246,6 +2292,7 @@ version = "0.7.35"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ wgpu = "22.1"
|
|||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
pollster = "0.3"
|
pollster = "0.3"
|
||||||
bytemuck = { version = "1.17", features = [ "derive" ] }
|
bytemuck = { version = "1.17", features = [ "derive" ] }
|
||||||
|
rand = "0.8"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
console_error_panic_hook = "0.1.6"
|
console_error_panic_hook = "0.1.6"
|
||||||
|
280
src/graphics.rs
Normal file
280
src/graphics.rs
Normal file
@ -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::<Vertex>() 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::<Vertex>() 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]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
170
src/main.rs
170
src/main.rs
@ -1,162 +1,8 @@
|
|||||||
use wgpu::{include_wgsl, Device, Queue, RenderPipeline, Surface, SurfaceConfiguration};
|
mod graphics;
|
||||||
use winit::{
|
mod state;
|
||||||
event::{Event, WindowEvent}, event_loop::EventLoop, platform::web::WindowExtWebSys, window::Window
|
use state::State;
|
||||||
};
|
use graphics::Graphics;
|
||||||
|
use winit::event_loop::EventLoop;
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@ -174,11 +20,11 @@ pub fn main() {
|
|||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
{
|
{
|
||||||
let mut app = pollster::block_on(App::init(&window));
|
pollster::block_on(Graphics::init(&window, State::new())).run(event_loop);
|
||||||
app.run(event_loop);
|
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
{
|
{
|
||||||
|
use winit::platform::web::WindowExtWebSys;
|
||||||
web_sys::window()
|
web_sys::window()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.document()
|
.document()
|
||||||
@ -188,7 +34,7 @@ pub fn main() {
|
|||||||
.append_child(&window.canvas().unwrap())
|
.append_child(&window.canvas().unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
App::init(&window).await.run(event_loop);
|
Graphics::init(&window, State::new()).await.run(event_loop);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,27 @@
|
|||||||
|
struct Uniforms {
|
||||||
|
camera: vec3f,
|
||||||
|
darkness: f32
|
||||||
|
}
|
||||||
|
@group(0) @binding(0) var<uniform> uniforms : Uniforms;
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
@location(0) color: vec4f,
|
||||||
|
@builtin(position) pos: vec4f
|
||||||
|
}
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
|
fn vs_main(
|
||||||
let x = f32(i32(in_vertex_index) - 1);
|
@location(0) pos: vec3f,
|
||||||
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
|
@location(1) color: vec4f
|
||||||
return vec4<f32>(x, y, 0.0, 1.0);
|
) -> 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
|
@fragment
|
||||||
fn fs_main() -> @location(0) vec4<f32> {
|
fn fs_main(v: VertexOutput) -> @location(0) vec4f {
|
||||||
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
return v.color;
|
||||||
}
|
}
|
176
src/state.rs
Normal file
176
src/state.rs
Normal file
@ -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<char> for CellKind {
|
||||||
|
fn from(value: char) -> Self {
|
||||||
|
match value {
|
||||||
|
'v' => Self::Void,
|
||||||
|
's' => Self::Sea,
|
||||||
|
'g' => Self::Grass,
|
||||||
|
_ => panic!("Invalid cell kind")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<u8> 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<std::iter::Enumerate<std::slice::Iter<'a, Cell>>, 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<Vertex>,
|
||||||
|
pub indices: Vec<u32>,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user