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",
|
||||
]
|
||||
|
||||
[[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",
|
||||
]
|
||||
|
||||
|
@ -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"
|
||||
|
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};
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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
|
||||
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
|
||||
let x = f32(i32(in_vertex_index) - 1);
|
||||
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
|
||||
return vec4<f32>(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<f32> {
|
||||
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||
fn fs_main(v: VertexOutput) -> @location(0) vec4f {
|
||||
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