Update graphics demo; Restructure code;
This commit is contained in:
parent
48a8fe94a2
commit
6ef261be61
18 changed files with 573 additions and 232 deletions
|
@ -3,6 +3,11 @@
|
|||
This is the changelog of Bedrock.
|
||||
See `README.md` for more information.
|
||||
|
||||
## 0.4.0-0
|
||||
|
||||
* Update graphics demo
|
||||
* Restructure code
|
||||
|
||||
## 0.3.1
|
||||
|
||||
* Add desktop entry
|
||||
|
|
17
Cargo.toml
17
Cargo.toml
|
@ -1,19 +1,20 @@
|
|||
[package]
|
||||
name = "bedrock"
|
||||
version = "0.3.1"
|
||||
version = "0.4.0-0"
|
||||
authors = ["Achernar", "Gabriel Bjørnager Jensen"]
|
||||
edition = "2024"
|
||||
repository = "ssh://git@ssh.mandelbrot.dk:bedrock"
|
||||
|
||||
[dependencies]
|
||||
ctrlc = "3.4"
|
||||
pollster = "0.4"
|
||||
rand = "0.9"
|
||||
toml = "0.8"
|
||||
wgpu = "24.0"
|
||||
winit = "0.30"
|
||||
ctrlc = "3.4"
|
||||
pollster = "0.4"
|
||||
rand = "0.9"
|
||||
toml = "0.8"
|
||||
wgpu = "24.0"
|
||||
winit = "0.30"
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
zerocopy = { version = "0.8", features = ["derive", "simd"] }
|
||||
|
||||
[workspace.lints.clippy]
|
||||
arc_with_non_send_sync = "forbid"
|
||||
|
|
14
bedrock.svg
14
bedrock.svg
|
@ -3,11 +3,11 @@
|
|||
<svg height="96" width="96" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#060606" height="100%" width="100%" x="0" y="0" /> <!-- 0% -->
|
||||
|
||||
<polygon fill="#222222" points="96,0 96,80 48,48" />
|
||||
<polygon fill="#414141" points="48,48 96,48 96,96 80,96" />
|
||||
<polygon fill="#636363" points="48,48 96,96 24,96" />
|
||||
<polygon fill="#878787" points="48,48 48,96 0,96 0,80" />
|
||||
<polygon fill="#AEAEAE" points="0,24 48,48 0,96" />
|
||||
<polygon fill="#D6D6D6" points="0,0 24,0 48,48 0,48" />
|
||||
<polygon fill="#FFFFFF" points="2,0 48,0 48,48" />
|
||||
<polygon fill="#222222" points="96,0 48,48 96,80" />
|
||||
<polygon fill="#414141" points="48,48 80,96 96,96 96,48" />
|
||||
<polygon fill="#636363" points="48,48 24,96 96,96" />
|
||||
<polygon fill="#878787" points="48,48 0,80 0,96 48,96" />
|
||||
<polygon fill="#AEAEAE" points="0,24 0,96 48,48" />
|
||||
<polygon fill="#D6D6D6" points="0,0 0,48 48,48 24,0" />
|
||||
<polygon fill="#FFFFFF" points="2,0 48,48 48,0" />
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 591 B |
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2025 Gabriel Bjørnager Jensen.
|
||||
|
||||
use crate::app::{App, Event, GraphicsContext};
|
||||
use crate::app::{App, Event};
|
||||
use crate::graphics::GraphicsContext;
|
||||
|
||||
use pollster::block_on;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{StartCause, WindowEvent};
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
|
@ -12,7 +12,7 @@ impl ApplicationHandler<Event> for App {
|
|||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
if self.graphics_context.is_some() { return };
|
||||
|
||||
let graphics_context = block_on(GraphicsContext::new(event_loop));
|
||||
let graphics_context = GraphicsContext::new(event_loop);
|
||||
self.graphics_context = Some(graphics_context);
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ impl ApplicationHandler<Event> for App {
|
|||
WindowEvent::RedrawRequested => {
|
||||
let graphics_context = self.graphics_context.as_mut().unwrap();
|
||||
|
||||
graphics_context.render();
|
||||
graphics_context.render(&self.map);
|
||||
}
|
||||
|
||||
WindowEvent::Resized(size) => {
|
||||
|
|
|
@ -8,8 +8,9 @@ mod regenerate_level;
|
|||
mod run;
|
||||
mod tick;
|
||||
|
||||
use crate::app::{Config, GraphicsContext};
|
||||
use crate::config::Config;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::graphics::GraphicsContext;
|
||||
use crate::level::Map;
|
||||
|
||||
use std::fs::{create_dir_all, write};
|
||||
|
@ -98,31 +99,8 @@ impl App {
|
|||
|
||||
eprintln!("writing test level to \"{}\"", test_level_path.display());
|
||||
|
||||
let _ = write(test_level_path, TEST_LEVEL);
|
||||
let _ = write(test_level_path, include_str!("test_level.toml"));
|
||||
|
||||
Ok(data_dir)
|
||||
}
|
||||
}
|
||||
|
||||
const TEST_LEVEL: &str =
|
||||
r#"[level]
|
||||
name = "test"
|
||||
creatour = "Achernar"
|
||||
description = "A test level."
|
||||
|
||||
[[chunk]]
|
||||
terrain_height = 0.0625
|
||||
|
||||
ground = "sand"
|
||||
|
||||
[[chunk]]
|
||||
terrain_height = 0.125
|
||||
|
||||
ground = "dirt"
|
||||
|
||||
[[chunk]]
|
||||
terrain_height = 0.25
|
||||
|
||||
ground = "stone"
|
||||
|
||||
"#;
|
||||
|
|
19
src/app/app/test_level.toml
Normal file
19
src/app/app/test_level.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[level]
|
||||
name = "test"
|
||||
creatour = "Achernar"
|
||||
description = "A test level."
|
||||
|
||||
[[chunk]]
|
||||
terrain_height = 0.0625
|
||||
|
||||
ground = "sand"
|
||||
|
||||
[[chunk]]
|
||||
terrain_height = 0.125
|
||||
|
||||
ground = "dirt"
|
||||
|
||||
[[chunk]]
|
||||
terrain_height = 0.25
|
||||
|
||||
ground = "stone"
|
|
@ -1,159 +0,0 @@
|
|||
// Copyright 2025 Gabriel Bjørnager Jensen.
|
||||
|
||||
mod render;
|
||||
|
||||
use crate::version::Version;
|
||||
|
||||
use std::pin::Pin;
|
||||
use wgpu::{
|
||||
Device,
|
||||
Instance,
|
||||
PresentMode,
|
||||
PowerPreference,
|
||||
Queue,
|
||||
RequestAdapterOptions,
|
||||
Surface,
|
||||
SurfaceConfiguration,
|
||||
SurfaceTargetUnsafe,
|
||||
TextureUsages,
|
||||
};
|
||||
use winit::dpi::PhysicalSize;
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::window::{Window, WindowAttributes};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GraphicsContext {
|
||||
surface_config: SurfaceConfiguration,
|
||||
|
||||
queue: Queue,
|
||||
device: Device,
|
||||
|
||||
surface: Surface<'static>,
|
||||
|
||||
window: Pin<Box<Window>>,
|
||||
}
|
||||
|
||||
impl GraphicsContext {
|
||||
#[must_use]
|
||||
pub async fn new(event_loop: &ActiveEventLoop) -> Self {
|
||||
eprintln!("creating graphics context");
|
||||
|
||||
let size = PhysicalSize {
|
||||
width: 0x200,
|
||||
height: 0x180,
|
||||
};
|
||||
|
||||
eprintln!("opening window");
|
||||
|
||||
let window = {
|
||||
let title = format!("Bedrock {}", Version::CURRENT);
|
||||
|
||||
let attrs = WindowAttributes::default()
|
||||
.with_inner_size(size)
|
||||
.with_min_inner_size(size)
|
||||
.with_title(&title);
|
||||
|
||||
match event_loop.create_window(attrs) {
|
||||
Ok(window) => Pin::new(Box::new(window)),
|
||||
|
||||
Err(e) => panic!("unable to open window: {e}"),
|
||||
}
|
||||
};
|
||||
|
||||
eprintln!("creating wgpu instance");
|
||||
|
||||
let instance = Instance::new(&Default::default());
|
||||
|
||||
eprintln!("creating surface");
|
||||
|
||||
let surface = unsafe {
|
||||
let target = match SurfaceTargetUnsafe::from_window(&*window) {
|
||||
Ok(target) => target,
|
||||
|
||||
Err(e) => panic!("unable to create surface target: {e}"),
|
||||
};
|
||||
|
||||
match instance.create_surface_unsafe(target) {
|
||||
Ok(surface) => surface,
|
||||
|
||||
Err(e) => panic!("unable to create surface: {e}"),
|
||||
}
|
||||
};
|
||||
|
||||
eprintln!("creating adapter");
|
||||
|
||||
let adapter = {
|
||||
let options = RequestAdapterOptions {
|
||||
power_preference: PowerPreference::LowPower,
|
||||
compatible_surface: Some(&surface),
|
||||
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
instance
|
||||
.request_adapter(&options)
|
||||
.await
|
||||
.expect("no adapter available")
|
||||
};
|
||||
|
||||
let surface_capabilities = surface.get_capabilities(&adapter);
|
||||
|
||||
let surface_format = surface_capabilities
|
||||
.formats
|
||||
.iter()
|
||||
.find(|f| f.is_srgb())
|
||||
.copied()
|
||||
.expect("unable to find srgb surface format");
|
||||
|
||||
eprintln!("creating device and queue");
|
||||
|
||||
let (device, queue) = {
|
||||
match adapter.request_device(&Default::default(), None).await {
|
||||
Ok((device, queue)) => (device, queue),
|
||||
|
||||
Err(e) => panic!("unable to find device: {e}"),
|
||||
}
|
||||
};
|
||||
|
||||
eprintln!("configuring surface");
|
||||
|
||||
let surface_config = SurfaceConfiguration {
|
||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||
format: surface_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: PresentMode::Fifo,
|
||||
desired_maximum_frame_latency: 0x2,
|
||||
alpha_mode: surface_capabilities.alpha_modes[0x0],
|
||||
view_formats: Default::default(),
|
||||
};
|
||||
|
||||
surface.configure(&device, &surface_config);
|
||||
|
||||
Self {
|
||||
surface_config,
|
||||
|
||||
queue,
|
||||
device,
|
||||
|
||||
surface,
|
||||
|
||||
window,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.surface_config.width = width;
|
||||
self.surface_config.height = height;
|
||||
|
||||
eprintln!("resizing graphics context to `{width}*{height}`");
|
||||
|
||||
self.surface.configure(&self.device, &self.surface_config);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn request_redraw(&mut self) {
|
||||
self.window.request_redraw();
|
||||
}
|
||||
}
|
|
@ -1,11 +1,7 @@
|
|||
// Copyright 2025 Gabriel Bjørnager Jensen.
|
||||
|
||||
mod app;
|
||||
mod config;
|
||||
mod event;
|
||||
mod graphics_context;
|
||||
|
||||
pub use app::App;
|
||||
pub use config::Config;
|
||||
pub use event::Event;
|
||||
pub use graphics_context::GraphicsContext;
|
||||
|
|
5
src/config/mod.rs
Normal file
5
src/config/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright 2025 Gabriel Bjørnager Jensen.
|
||||
|
||||
mod config;
|
||||
|
||||
pub use config::Config;
|
302
src/graphics/graphics_context/mod.rs
Normal file
302
src/graphics/graphics_context/mod.rs
Normal file
|
@ -0,0 +1,302 @@
|
|||
// Copyright 2025 Gabriel Bjørnager Jensen.
|
||||
|
||||
mod render;
|
||||
|
||||
use crate::graphics::{MAIN_SHADER, Vec4, Vertex};
|
||||
use crate::version::Version;
|
||||
|
||||
use pollster::block_on;
|
||||
use std::pin::Pin;
|
||||
use wgpu::{
|
||||
BlendState,
|
||||
Buffer,
|
||||
BufferUsages,
|
||||
ColorTargetState,
|
||||
ColorWrites,
|
||||
Device,
|
||||
Face,
|
||||
FragmentState,
|
||||
FrontFace,
|
||||
Instance,
|
||||
PolygonMode,
|
||||
PowerPreference,
|
||||
PresentMode,
|
||||
PrimitiveState,
|
||||
PrimitiveTopology,
|
||||
Queue,
|
||||
RenderPipeline,
|
||||
RenderPipelineDescriptor,
|
||||
RequestAdapterOptions,
|
||||
Surface,
|
||||
SurfaceConfiguration,
|
||||
SurfaceTargetUnsafe,
|
||||
TextureUsages,
|
||||
VertexState,
|
||||
};
|
||||
use wgpu::util::{BufferInitDescriptor, DeviceExt};
|
||||
use winit::dpi::PhysicalSize;
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::window::{Window, WindowAttributes};
|
||||
use zerocopy::IntoBytes;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GraphicsContext {
|
||||
surface_config: SurfaceConfiguration,
|
||||
|
||||
vertex_count: u32,
|
||||
vertex_buf: Buffer,
|
||||
|
||||
pipeline: RenderPipeline,
|
||||
|
||||
queue: Queue,
|
||||
device: Device,
|
||||
|
||||
surface: Surface<'static>,
|
||||
|
||||
window: Pin<Box<Window>>,
|
||||
}
|
||||
|
||||
impl GraphicsContext {
|
||||
#[must_use]
|
||||
pub fn new(event_loop: &ActiveEventLoop) -> Self {
|
||||
eprintln!("creating graphics context");
|
||||
|
||||
let size = PhysicalSize {
|
||||
width: 0x200,
|
||||
height: 0x180,
|
||||
};
|
||||
|
||||
eprintln!("opening window");
|
||||
|
||||
let window = {
|
||||
let title = format!("Bedrock {}", Version::CURRENT);
|
||||
|
||||
let attrs = WindowAttributes::default()
|
||||
.with_inner_size(size)
|
||||
.with_min_inner_size(size)
|
||||
.with_title(&title);
|
||||
|
||||
match event_loop.create_window(attrs) {
|
||||
Ok(window) => Pin::new(Box::new(window)),
|
||||
|
||||
Err(e) => panic!("unable to open window: {e}"),
|
||||
}
|
||||
};
|
||||
|
||||
eprintln!("creating wgpu instance");
|
||||
|
||||
let instance = Instance::new(&Default::default());
|
||||
|
||||
eprintln!("creating surface");
|
||||
|
||||
let surface = unsafe {
|
||||
let target = match SurfaceTargetUnsafe::from_window(&*window) {
|
||||
Ok(target) => target,
|
||||
|
||||
Err(e) => panic!("unable to create surface target: {e}"),
|
||||
};
|
||||
|
||||
match instance.create_surface_unsafe(target) {
|
||||
Ok(surface) => surface,
|
||||
|
||||
Err(e) => panic!("unable to create surface: {e}"),
|
||||
}
|
||||
};
|
||||
|
||||
eprintln!("creating adapter");
|
||||
|
||||
let adapter = {
|
||||
let options = RequestAdapterOptions {
|
||||
power_preference: PowerPreference::LowPower,
|
||||
compatible_surface: Some(&surface),
|
||||
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
block_on(instance.request_adapter(&options))
|
||||
.expect("no adapter available")
|
||||
};
|
||||
|
||||
let surface_capabilities = surface.get_capabilities(&adapter);
|
||||
|
||||
let surface_format = surface_capabilities
|
||||
.formats
|
||||
.iter()
|
||||
.find(|f| f.is_srgb())
|
||||
.copied()
|
||||
.expect("unable to find srgb surface format");
|
||||
|
||||
eprintln!("creating device and queue");
|
||||
|
||||
let (device, queue) = {
|
||||
match block_on(adapter.request_device(&Default::default(), None)) {
|
||||
Ok((device, queue)) => (device, queue),
|
||||
|
||||
Err(e) => panic!("unable to find device: {e}"),
|
||||
}
|
||||
};
|
||||
|
||||
eprintln!("configuring surface");
|
||||
|
||||
let surface_config = SurfaceConfiguration {
|
||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||
format: surface_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: PresentMode::Fifo,
|
||||
desired_maximum_frame_latency: 0x2,
|
||||
alpha_mode: surface_capabilities.alpha_modes[0x0],
|
||||
view_formats: Default::default(),
|
||||
};
|
||||
|
||||
surface.configure(&device, &surface_config);
|
||||
|
||||
eprintln!("creating shader module");
|
||||
|
||||
let shader = device.create_shader_module(MAIN_SHADER);
|
||||
|
||||
eprintln!("creating render pipeline");
|
||||
|
||||
let pipeline = {
|
||||
let layout = device.create_pipeline_layout(&Default::default());
|
||||
|
||||
let vertex = VertexState {
|
||||
module: &shader,
|
||||
entry_point: Some("vertex_main"),
|
||||
buffers: &[Vertex::LAYOUT],
|
||||
compilation_options: Default::default(),
|
||||
};
|
||||
|
||||
let fragment = FragmentState {
|
||||
module: &shader,
|
||||
entry_point: Some("fragment_main"),
|
||||
|
||||
targets: &[
|
||||
Some(ColorTargetState {
|
||||
format: surface_config.format,
|
||||
blend: Some(BlendState::ALPHA_BLENDING),
|
||||
write_mask: ColorWrites::ALL,
|
||||
})
|
||||
],
|
||||
|
||||
compilation_options: Default::default(),
|
||||
};
|
||||
|
||||
let primitive = PrimitiveState {
|
||||
topology: PrimitiveTopology::TriangleList,
|
||||
front_face: FrontFace::Ccw,
|
||||
cull_mode: Some(Face::Back),
|
||||
polygon_mode: PolygonMode::Fill,
|
||||
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let descriptor = RenderPipelineDescriptor {
|
||||
label: Some("main"),
|
||||
layout: Some(&layout),
|
||||
vertex,
|
||||
fragment: Some(fragment),
|
||||
primitive,
|
||||
depth_stencil: Default::default(),
|
||||
multisample: Default::default(),
|
||||
multiview: Default::default(),
|
||||
cache: Default::default(),
|
||||
};
|
||||
|
||||
device.create_render_pipeline(&descriptor)
|
||||
};
|
||||
|
||||
eprintln!("creating vertex buffer");
|
||||
|
||||
let (vertex_count, vertex_buf) = {
|
||||
let vertices = [
|
||||
Vertex {
|
||||
position: Vec4::new( 0.0, 1.0, 1.0, 1.0),
|
||||
colour: Vec4::new( 1.0, 0.0, 0.0, 1.0),
|
||||
},
|
||||
|
||||
Vertex {
|
||||
position: Vec4::new(-0.5, 0.0, 0.0, 1.0),
|
||||
colour: Vec4::new( 0.0, 1.0, 0.0, 1.0),
|
||||
},
|
||||
|
||||
Vertex {
|
||||
position: Vec4::new( 0.5, 0.0, 0.0, 1.0),
|
||||
colour: Vec4::new( 0.0, 0.0, 1.0, 1.0),
|
||||
},
|
||||
|
||||
Vertex {
|
||||
position: Vec4::new(-0.5, 0.0, 0.0, 1.0),
|
||||
colour: Vec4::new( 0.0, 1.0, 1.0, 1.0),
|
||||
},
|
||||
|
||||
Vertex {
|
||||
position: Vec4::new(-1.0, -1.0, 0.0, 1.0),
|
||||
colour: Vec4::new( 1.0, 0.0, 1.0, 1.0),
|
||||
},
|
||||
|
||||
Vertex {
|
||||
position: Vec4::new( 0.0, -1.0, 0.0, 1.0),
|
||||
colour: Vec4::new( 1.0, 1.0, 0.0, 1.0),
|
||||
},
|
||||
|
||||
Vertex {
|
||||
position: Vec4::new( 0.5, 0.0, 0.0, 1.0),
|
||||
colour: Vec4::new( 1.0, 1.0, 1.0, 1.0),
|
||||
},
|
||||
|
||||
Vertex {
|
||||
position: Vec4::new( 0.0, -1.0, 0.0, 1.0),
|
||||
colour: Vec4::new( 0.0, 0.0, 0.0, 1.0),
|
||||
},
|
||||
|
||||
Vertex {
|
||||
position: Vec4::new( 1.0, -1.0, 0.0, 1.0),
|
||||
colour: Vec4::new( 0.0, 0.0, 0.0, 1.0),
|
||||
},
|
||||
];
|
||||
|
||||
let descriptor = BufferInitDescriptor {
|
||||
label: Some("main"),
|
||||
contents: vertices.as_bytes(),
|
||||
usage: BufferUsages::VERTEX,
|
||||
};
|
||||
|
||||
let count = vertices.len() as u32;
|
||||
let buf = device.create_buffer_init(&descriptor);
|
||||
|
||||
(count, buf)
|
||||
};
|
||||
|
||||
Self {
|
||||
surface_config,
|
||||
|
||||
vertex_count,
|
||||
vertex_buf,
|
||||
|
||||
pipeline,
|
||||
|
||||
queue,
|
||||
device,
|
||||
|
||||
surface,
|
||||
|
||||
window,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.surface_config.width = width;
|
||||
self.surface_config.height = height;
|
||||
|
||||
eprintln!("resizing graphics context to `{width}*{height}`");
|
||||
|
||||
self.surface.configure(&self.device, &self.surface_config);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn request_redraw(&mut self) {
|
||||
self.window.request_redraw();
|
||||
}
|
||||
}
|
|
@ -1,30 +1,47 @@
|
|||
// Copyright 2025 Gabriel Bjørnager Jensen.
|
||||
|
||||
use crate::app::GraphicsContext;
|
||||
use crate::graphics::GraphicsContext;
|
||||
use crate::level::Map;
|
||||
|
||||
use std::f64;
|
||||
use std::iter;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use wgpu::{
|
||||
Color,
|
||||
CommandEncoderDescriptor,
|
||||
LoadOp,
|
||||
Operations,
|
||||
RenderPassColorAttachment,
|
||||
RenderPassDescriptor,
|
||||
StoreOp,
|
||||
TextureViewDescriptor,
|
||||
};
|
||||
|
||||
impl GraphicsContext {
|
||||
pub fn render(&mut self) {
|
||||
pub fn render(&mut self, _map: &Map) {
|
||||
let output = match self.surface.get_current_texture() {
|
||||
Ok(output) => output,
|
||||
|
||||
Err(e) => panic!("unable to get current texture: {e}"),
|
||||
};
|
||||
|
||||
let view = output.texture.create_view(&Default::default());
|
||||
let view = {
|
||||
let descriptor = TextureViewDescriptor {
|
||||
label: Some("main"),
|
||||
|
||||
let mut encoder = self.device.create_command_encoder(&Default::default());
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
output.texture.create_view(&descriptor)
|
||||
};
|
||||
|
||||
let mut encoder = {
|
||||
let descriptor = CommandEncoderDescriptor {
|
||||
label: Some("main"),
|
||||
};
|
||||
|
||||
self.device.create_command_encoder(&descriptor)
|
||||
};
|
||||
|
||||
let colour = {
|
||||
let time = SystemTime::now()
|
||||
|
@ -32,27 +49,38 @@ impl GraphicsContext {
|
|||
.unwrap()
|
||||
.as_secs_f64();
|
||||
|
||||
let hue = time / 4.0;
|
||||
let hue = time / 8.0;
|
||||
|
||||
hsva(hue, 1.0, 1.0, 1.0)
|
||||
};
|
||||
|
||||
let _ = encoder.begin_render_pass(&RenderPassDescriptor {
|
||||
color_attachments: &[
|
||||
Some(RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
{
|
||||
let descriptor = RenderPassDescriptor {
|
||||
label: Some("main"),
|
||||
|
||||
ops: Operations {
|
||||
load: LoadOp::Clear(colour),
|
||||
color_attachments: &[
|
||||
Some(RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
|
||||
store: StoreOp::Store,
|
||||
},
|
||||
}),
|
||||
],
|
||||
ops: Operations {
|
||||
load: LoadOp::Clear(colour),
|
||||
|
||||
..Default::default()
|
||||
});
|
||||
store: StoreOp::Store,
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut pass = encoder.begin_render_pass(&descriptor);
|
||||
|
||||
pass.set_pipeline(&self.pipeline);
|
||||
pass.set_vertex_buffer(0x0, self.vertex_buf.slice(..));
|
||||
|
||||
pass.draw(0x0..self.vertex_count, 0x0..0x1);
|
||||
}
|
||||
|
||||
self.queue.submit(iter::once(encoder.finish()));
|
||||
|
32
src/graphics/main.wgsl
Normal file
32
src/graphics/main.wgsl
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2025 Gabriel Bjørnager Jensen.
|
||||
|
||||
struct VertexInput {
|
||||
@location(0x0) position: vec4<f32>,
|
||||
@location(0x1) colour: vec4<f32>,
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip: vec4<f32>,
|
||||
@location(0x0) colour: vec4<f32>,
|
||||
};
|
||||
|
||||
@vertex
|
||||
//@must_use
|
||||
fn vertex_main(
|
||||
input: VertexInput,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
|
||||
out.clip = input.position;
|
||||
out.colour = input.colour;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
//@must_use
|
||||
fn fragment_main(
|
||||
input: VertexOutput,
|
||||
) -> @location(0x0) vec4<f32> {
|
||||
return input.colour;
|
||||
}
|
14
src/graphics/mod.rs
Normal file
14
src/graphics/mod.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2025 Gabriel Bjørnager Jensen.
|
||||
|
||||
mod graphics_context;
|
||||
mod vec4;
|
||||
mod vertex;
|
||||
|
||||
use vec4::Vec4;
|
||||
use vertex::Vertex;
|
||||
|
||||
pub use graphics_context::GraphicsContext;
|
||||
|
||||
use wgpu::{include_wgsl, ShaderModuleDescriptor};
|
||||
|
||||
const MAIN_SHADER: ShaderModuleDescriptor = include_wgsl!("main.wgsl");
|
73
src/graphics/vec4/mod.rs
Normal file
73
src/graphics/vec4/mod.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2025 Gabriel Bjørnager Jensen.
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use zerocopy::{
|
||||
FromZeros,
|
||||
Immutable,
|
||||
IntoBytes,
|
||||
KnownLayout,
|
||||
transmute,
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
type Buffer = std::arch::x86::__m128;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
type Buffer = std::arch::x86_64::__m128;
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
type Buffer = [f32; 0x4];
|
||||
|
||||
const _: () = assert!(size_of::<Buffer>() == size_of::<f32>() * 0x4);
|
||||
|
||||
#[repr(align(0x10), C)]
|
||||
#[derive(Clone, Copy, Debug, FromZeros, Immutable, IntoBytes, KnownLayout)]
|
||||
pub(super) struct Vec4(Buffer);
|
||||
|
||||
impl Vec4 {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
|
||||
let buf = [x, y, z, w];
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
{
|
||||
let buf = transmute!(buf);
|
||||
Self(buf)
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
{
|
||||
Self(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn get(self) -> (f32, f32, f32, f32) {
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
{
|
||||
let [x, y, z, w] = transmute!(self.0);
|
||||
(x, y, z, w)
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
{
|
||||
self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Vec4 {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.get() == other.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Vec4 {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.get().partial_cmp(&other.get())
|
||||
}
|
||||
}
|
44
src/graphics/vertex/mod.rs
Normal file
44
src/graphics/vertex/mod.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2025 Gabriel Bjørnager Jensen.
|
||||
|
||||
use crate::graphics::Vec4;
|
||||
|
||||
use std::mem::offset_of;
|
||||
use wgpu::{
|
||||
BufferAddress,
|
||||
VertexAttribute,
|
||||
VertexBufferLayout,
|
||||
VertexFormat,
|
||||
VertexStepMode,
|
||||
};
|
||||
use zerocopy::{FromZeros, Immutable, IntoBytes, KnownLayout};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, FromZeros, Immutable, IntoBytes, KnownLayout, PartialEq)]
|
||||
pub(super) struct Vertex {
|
||||
pub position: Vec4,
|
||||
pub colour: Vec4,
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
pub const LAYOUT: VertexBufferLayout<'_> = VertexBufferLayout {
|
||||
array_stride: size_of::<Self>() as BufferAddress,
|
||||
step_mode: VertexStepMode::Vertex,
|
||||
|
||||
attributes: &[
|
||||
VertexAttribute {
|
||||
offset: offset_of!(Self, position) as BufferAddress,
|
||||
shader_location: 0x0,
|
||||
format: VertexFormat::Float32x4,
|
||||
},
|
||||
|
||||
VertexAttribute {
|
||||
offset: offset_of!(Self, colour) as BufferAddress,
|
||||
shader_location: 0x1,
|
||||
format: VertexFormat::Float32x4,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const _: () = assert!(Vertex::LAYOUT.attributes[0x0].offset == 0x00);
|
||||
const _: () = assert!(Vertex::LAYOUT.attributes[0x1].offset == 0x10);
|
11
src/main.rs
11
src/main.rs
|
@ -1,21 +1,24 @@
|
|||
// Copyright 2025 Gabriel Bjørnager Jensen.
|
||||
|
||||
#![feature(float_gamma)]
|
||||
#![feature(thread_sleep_until)]
|
||||
|
||||
// Why is this needed?
|
||||
#![allow(clippy::module_inception)]
|
||||
|
||||
const _: () = assert!(usize::BITS >= u32::BITS);
|
||||
|
||||
mod app;
|
||||
mod config;
|
||||
mod error;
|
||||
mod graphics;
|
||||
mod level;
|
||||
mod version;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::error::Result;
|
||||
|
||||
|
||||
use std::process::exit;
|
||||
|
||||
const _: () = assert!(usize::BITS >= u32::BITS);
|
||||
|
||||
fn main() -> ! {
|
||||
let run = || -> Result<()> {
|
||||
let app = App::new()?;
|
||||
|
|
|
@ -13,9 +13,9 @@ pub struct Version {
|
|||
impl Version {
|
||||
pub const CURRENT: Self = Self {
|
||||
major: 0x0,
|
||||
minor: 0x3,
|
||||
patch: 0x1,
|
||||
pre: None,
|
||||
minor: 0x4,
|
||||
patch: 0x0,
|
||||
pre: Some(0x0),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue