Create window, handle simple input

This commit is contained in:
SeanOMik 2023-03-16 17:47:36 -04:00
commit a086e5b24a
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
14 changed files with 3255 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

2317
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

26
Cargo.toml Normal file
View File

@ -0,0 +1,26 @@
[package]
name = "lyra-engine"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
shipyard = "0.6.2"
winit = "0.28.1"
#winit-modular = "0.1.1"
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] }
tracing-log = "0.1.3"
wgpu = "0.15.1"
async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] }
cfg-if = "1"
bytemuck = { version = "1.12", features = [ "derive" ] }
image = { version = "0.24", default-features = false, features = ["png", "jpeg"] }
anyhow = "1.0"
cgmath = "0.18"
tobj = { version = "3.2.1", features = [
"async",
]}
instant = "0.1"
async-trait = "0.1.65"

18
shell.nix Normal file
View File

@ -0,0 +1,18 @@
{ pkgs ? import <nixpkgs> { } }:
with pkgs;
mkShell rec {
nativeBuildInputs = [
pkg-config
openssl
wasm-pack
trunk
];
buildInputs = [
udev alsa-lib vulkan-loader
xorg.libX11 xorg.libXcursor xorg.libXi xorg.libXrandr # To use the x11 feature
libxkbcommon wayland # To use the wayland feature
];
LD_LIBRARY_PATH = lib.makeLibraryPath buildInputs;
}

6
src/ecs/component.rs Normal file
View File

@ -0,0 +1,6 @@
use std::any::Any;
pub trait Component {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}

12
src/ecs/entity.rs Normal file
View File

@ -0,0 +1,12 @@
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Entity {
pub handle: i32,
}
impl Entity {
pub fn new(handle: i32) -> Self {
Self {
handle
}
}
}

3
src/ecs/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod entity;
pub mod registry;
pub mod component;

131
src/ecs/registry.rs Normal file
View File

@ -0,0 +1,131 @@
use std::{collections::HashMap, cell::{RefCell, RefMut, Ref}, rc::Rc, borrow::BorrowMut, any::Any, ops::Deref};
use super::{entity::Entity, component::Component};
pub trait Registry {
/// Create an entity
fn create_entity(&mut self) -> Entity;
/// Remove the entity from the registry. Its components will also be destroyed
fn destroy_entity(&mut self, entity: Entity);
/// Add a component to an entity
fn entity_add_component<C: Component + 'static>(&mut self, entity: Entity, component: C) -> Option<Rc<RefCell<C>>>; // TODO: Return result if the component is already added.
/// Remove a component from an entity
fn entity_remove_component<C: Component + 'static>(&mut self, entity: Entity) -> bool;
fn entity_has_component<C: Component + 'static>(&self, entity: Entity) -> bool;
/// Get all the components that an entity has
fn entity_get_components(&self, entity: Entity) -> Option<Vec<Rc<RefCell<dyn Component>>>>;
/// Get a component of a specific type.
fn entity_get_component<C: Component + 'static>(&self, entity: Entity) -> Option<RefMut<'_, C>>;
}
pub struct BasicRegistry {
next_handle: i32,
map: HashMap<Entity, Vec<Rc<RefCell<dyn Component>>>>,
}
impl BasicRegistry {
pub fn new() -> Self {
Self {
next_handle: 0,
map: HashMap::new(),
}
}
}
impl Registry for BasicRegistry {
fn create_entity(&mut self) -> Entity {
//self.map.insert()
let entity = Entity::new(self.next_handle);
self.next_handle += 1;
self.map.insert(entity, Vec::new());
entity
}
fn destroy_entity(&mut self, entity: Entity) {
self.map.remove(&entity);
}
fn entity_add_component<C: Component + 'static>(&mut self, entity: Entity, component: C) -> Option<Rc<RefCell<C>>> {
if let Some(components) = self.map.get_mut(&entity) {
let rc = Rc::new(RefCell::new(component));
let clone = Rc::clone(&rc);
components.push(rc);
return Some(clone);
}
None
}
fn entity_remove_component<C: Component + 'static>(&mut self, entity: Entity) -> bool {
if let Some(components) = self.map.get_mut(&entity) {
let mut removed = false;
components.retain(|component| {
if removed {
return true;
}
let borrowed_comp = component.deref().borrow_mut();
let any_comp = borrowed_comp.as_any();
if any_comp.is::<C>() {
removed = true;
false
} else {
true
}
});
return removed;
}
false
}
fn entity_has_component<C: Component + 'static>(&self, entity: Entity) -> bool {
if let Some(components) = self.map.get(&entity) {
for component in components.iter() {
let borrowed_comp = component.deref().borrow_mut();
let any_comp = borrowed_comp.as_any();
if any_comp.is::<C>() {
return true;
}
}
}
false
}
fn entity_get_components(&self, entity: Entity) -> Option<Vec<Rc<RefCell<dyn Component>>>> {
if let Some(components) = self.map.get(&entity) {
let mut cloned_components = Vec::new();
for component in components.iter() {
cloned_components.push(Rc::clone(&component));
}
return Some(cloned_components);
}
None
}
fn entity_get_component<C: Component + 'static>(&self, entity: Entity) -> Option<RefMut<'_, C>> {
if let Some(components) = self.map.get(&entity) {
for component in components.iter() {
let borrowed_comp = component.deref().borrow_mut();
let any_comp = borrowed_comp.as_any();
if any_comp.is::<C>() {
return Some(RefMut::map(borrowed_comp, |c| c.as_any_mut().downcast_mut().unwrap()));
}
}
}
None
}
}

194
src/game.rs Normal file
View File

@ -0,0 +1,194 @@
use std::{sync::Arc, ops::Deref};
use async_std::{task::block_on, sync::Mutex};
use shipyard::{World, Workload};
use tracing::{metadata::LevelFilter, info, debug, Level, warn};
/* use tracing_subscriber::{filter::FilterFn, util::SubscriberInitExt};
use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt; */
use tracing_subscriber::{
layer::{Layer, SubscriberExt},
filter::FilterFn,
util::SubscriberInitExt,
};
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{EventLoop, ControlFlow}};
use crate::{renderer::{Renderer, BasicRenderer}, input_event::InputEvent};
struct GameLoop {
window: Arc<Window>,
renderer: Box<dyn Renderer>,
}
impl GameLoop {
pub async fn new(window: &Arc<Window>) -> Self {
Self {
window: Arc::clone(&window),
renderer: Box::new(BasicRenderer::create_with_window(Arc::clone(window)).await),
}
}
pub async fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.renderer.on_resize(new_size).await;
}
pub fn run_sync(&mut self, event: Event<()>, control_flow: &mut ControlFlow) {
block_on(self.run_event_loop(event, control_flow))
}
async fn update(&mut self) {
todo!()
}
async fn input_update(&mut self, event: &InputEvent) -> Option<ControlFlow> {
match event {
InputEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => {
self.on_exit();
Some(ControlFlow::Exit)
},
_ => {
debug!("Got unhandled input event: \"{:?}\"", event);
None
}
}
}
fn render_window() {
todo!()
}
fn render_item() {
todo!()
}
fn on_exit(&mut self) {
info!("On exit!");
}
pub async fn run_event_loop(&mut self, event: Event<'_, ()>, control_flow: &mut ControlFlow) {
*control_flow = ControlFlow::Poll;
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == self.window.id() => {
// If try_from failed, that means that the WindowEvent is not an
// input related event.
if let Ok(input_event) = InputEvent::try_from(event) {
if let Some(new_control) = self.input_update(&input_event).await {
*control_flow = new_control;
}
} else {
match event {
WindowEvent::CloseRequested => {
self.on_exit();
*control_flow = ControlFlow::Exit
},
WindowEvent::Resized(physical_size) => {
self.on_resize(*physical_size).await;
},
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
self.on_resize(**new_inner_size).await;
},
_ => {}
}
}
},
Event::RedrawRequested(window_id) if window_id == self.window.id() => {
match self.renderer.as_mut().render().await {
Ok(_) => {}
// Reconfigure the surface if lost
Err(wgpu::SurfaceError::Lost) => self.on_resize(self.renderer.as_ref().size()).await,
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// All other errors (Outdated, Timeout) should be resolved by the next frame
Err(e) => eprintln!("{:?}", e),
}
},
Event::MainEventsCleared => {
self.window.request_redraw();
},
_ => {}
}
}
}
pub struct Game {
}
impl Game {
/* fn lyra_engine_log_filter() {
} */
pub async fn initialize() -> Self {
let filter = FilterFn::new(|metadata| {
//metadata.
//println!("Metadata: {:?}", metadata);
metadata.module_path()
.unwrap_or_else(|| metadata.target())
.starts_with("lyra_engine") && (LevelFilter::INFO >= metadata.level().to_owned())
});
let layer = tracing_subscriber::fmt::layer();
tracing_subscriber::registry()
.with(layer.with_filter(filter))
.init();
Self {
}
}
fn update() {
todo!()
}
fn input_update() {
todo!()
}
fn render_window() {
todo!()
}
fn render_item() {
todo!()
}
fn exit() {
todo!()
}
pub async fn run(&mut self) {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
let mut g_loop = GameLoop::new(&Arc::new(window)).await;
event_loop.run(move |event, _, control_flow| {
g_loop.run_sync(event, control_flow);
});
}
}

248
src/input_event.rs Normal file
View File

@ -0,0 +1,248 @@
use winit::{event::{DeviceId, KeyboardInput, ModifiersState, MouseScrollDelta, TouchPhase, MouseButton, AxisId, Touch, WindowEvent, ElementState}, dpi::PhysicalPosition};
#[derive(Debug, PartialEq)]
pub enum InputEvent {
/// An event from the keyboard has been received.
KeyboardInput {
device_id: DeviceId,
input: KeyboardInput,
/// If true, the event was generated synthetically by winit in one of the following circumstances:
/// Synthetic key press events are generated for all keys pressed when a window gains focus.
/// Likewise, synthetic key release events are generated for all keys pressed when a window goes out of focus.
/// Currently, this is only functional on X11 and Windows
is_synthetic: bool,
},
/// The cursor has moved on the window.
CursorMoved {
device_id: DeviceId,
position: PhysicalPosition<f64>,
modifiers: ModifiersState,
},
/// The cursor has entered the window.
CursorEntered {
device_id: DeviceId,
},
/// The cursor has left the window.
CursorLeft {
device_id: DeviceId,
},
/// A mouse wheel movement or touchpad scroll occurred.
MouseWheel {
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
/// Deprecated in favor of WindowEvent::ModifiersChanged
modifiers: ModifiersState,
},
/// An mouse button press has been received.
MouseInput {
device_id: DeviceId,
state: ElementState,
button: MouseButton,
#[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"]
modifiers: ModifiersState,
},
/// Touchpad magnification event with two-finger pinch gesture.
/// Positive delta values indicate magnification (zooming in) and negative delta values indicate shrinking (zooming out).
///
/// Note: Only available on macOS
TouchpadMagnify {
device_id: DeviceId,
delta: f64,
phase: TouchPhase,
},
/// Smart magnification event.
///
/// On a Mac, smart magnification is triggered by a double tap with two fingers
/// on the trackpad and is commonly used to zoom on a certain object
/// (e.g. a paragraph of a PDF) or (sort of like a toggle) to reset any zoom.
/// The gesture is also supported in Safari, Pages, etc.
///
/// The event is general enough that its generating gesture is allowed to vary
/// across platforms. It could also be generated by another device.
///
/// Unfortunatly, neither [Windows](https://support.microsoft.com/en-us/windows/touch-gestures-for-windows-a9d28305-4818-a5df-4e2b-e5590f850741)
/// nor [Wayland](https://wayland.freedesktop.org/libinput/doc/latest/gestures.html)
/// support this gesture or any other gesture with the same effect.
///
/// ## Platform-specific
///
/// - Only available on **macOS 10.8** and later.
SmartMagnify { device_id: DeviceId },
/// Touchpad rotation event with two-finger rotation gesture.
///
/// Positive delta values indicate rotation counterclockwise and
/// negative delta values indicate rotation clockwise.
///
/// ## Platform-specific
///
/// - Only available on **macOS**.
TouchpadRotate {
device_id: DeviceId,
delta: f32,
phase: TouchPhase,
},
/// Touchpad pressure event.
///
/// At the moment, only supported on Apple forcetouch-capable macbooks.
/// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad
/// is being pressed) and stage (integer representing the click level).
TouchpadPressure {
device_id: DeviceId,
pressure: f32,
stage: i64,
},
/// Motion on some analog axis. May report data redundant to other, more specific events.
AxisMotion {
device_id: DeviceId,
axis: AxisId,
value: f64,
},
/// Touch event has been received
///
/// ## Platform-specific
///
/// - **macOS:** Unsupported.
Touch(Touch),
}
#[derive(Debug, PartialEq)]
pub enum InputEventConversionError<'a> {
FromError(&'a WindowEvent<'a>)
}
impl<'a> Into<WindowEvent<'a>> for InputEvent {
fn into(self) -> WindowEvent<'a> {
match self {
InputEvent::KeyboardInput { device_id, input, is_synthetic } => WindowEvent::KeyboardInput { device_id, input, is_synthetic },
InputEvent::CursorMoved { device_id, position, modifiers } => WindowEvent::CursorMoved { device_id, position, modifiers },
InputEvent::CursorEntered { device_id } => WindowEvent::CursorEntered { device_id },
InputEvent::CursorLeft { device_id } => WindowEvent::CursorLeft { device_id },
InputEvent::MouseWheel { device_id, delta, phase, modifiers } => WindowEvent::MouseWheel { device_id, delta, phase, modifiers },
InputEvent::MouseInput { device_id, state, button, modifiers } => WindowEvent::MouseInput { device_id, state, button, modifiers },
InputEvent::TouchpadMagnify { device_id, delta, phase } => WindowEvent::TouchpadMagnify { device_id, delta, phase },
InputEvent::SmartMagnify { device_id } => WindowEvent::SmartMagnify { device_id },
InputEvent::TouchpadRotate { device_id, delta, phase } => WindowEvent::TouchpadRotate { device_id, delta, phase },
InputEvent::TouchpadPressure { device_id, pressure, stage } => WindowEvent::TouchpadPressure { device_id, pressure, stage },
InputEvent::AxisMotion { device_id, axis, value } => WindowEvent::AxisMotion { device_id, axis, value },
InputEvent::Touch(t) => WindowEvent::Touch(t),
}
}
}
impl<'a> TryFrom<&'a WindowEvent<'a>> for InputEvent {
type Error = InputEventConversionError<'a>;
fn try_from(value: &'a WindowEvent<'a>) -> Result<Self, Self::Error> {
match value {
WindowEvent::KeyboardInput { device_id, input, is_synthetic } =>
Ok(InputEvent::KeyboardInput {
device_id: device_id.clone(),
input: input.clone(),
is_synthetic: is_synthetic.clone()
}),
WindowEvent::CursorMoved { device_id, position, modifiers } =>
Ok(InputEvent::CursorMoved {
device_id: device_id.clone(),
position: position.clone(),
modifiers: modifiers.clone()
}),
WindowEvent::CursorEntered { device_id } =>
Ok(InputEvent::CursorEntered {
device_id: device_id.clone()
}),
WindowEvent::CursorLeft { device_id } =>
Ok(InputEvent::CursorLeft {
device_id: device_id.clone()
}),
WindowEvent::MouseWheel { device_id, delta, phase, modifiers } =>
Ok(InputEvent::MouseWheel {
device_id: device_id.clone(),
delta: delta.clone(),
phase: phase.clone(),
modifiers: modifiers.clone()
}),
WindowEvent::MouseInput { device_id, state, button, modifiers } =>
Ok(InputEvent::MouseInput {
device_id: device_id.clone(),
state: state.clone(),
button: button.clone(),
modifiers: modifiers.clone()
}),
WindowEvent::TouchpadMagnify { device_id, delta, phase } =>
Ok(InputEvent::TouchpadMagnify {
device_id: device_id.clone(),
delta: delta.clone(),
phase: phase.clone()
}),
WindowEvent::SmartMagnify { device_id } =>
Ok(InputEvent::SmartMagnify {
device_id: device_id.clone()
}),
WindowEvent::TouchpadRotate { device_id, delta, phase } =>
Ok(InputEvent::TouchpadRotate {
device_id: device_id.clone(),
delta: delta.clone(),
phase: phase.clone()
}),
WindowEvent::TouchpadPressure { device_id, pressure, stage } =>
Ok(InputEvent::TouchpadPressure {
device_id: device_id.clone(),
pressure: pressure.clone(),
stage: stage.clone()
}),
WindowEvent::AxisMotion { device_id, axis, value } =>
Ok(InputEvent::AxisMotion {
device_id: device_id.clone(),
axis: axis.clone(),
value: value.clone()
}),
WindowEvent::Touch(t) => Ok(InputEvent::Touch(t.clone())),
_ => Err(InputEventConversionError::FromError(value))
}
}
}
/* impl<'a> TryFrom<WindowEvent<'a>> for InputEvent {
type Error = InputEventConversionError<'a>;
fn try_from(value: WindowEvent<'a>) -> Result<Self, Self::Error> {
match value {
WindowEvent::KeyboardInput { device_id, input, is_synthetic } =>
Ok(InputEvent::KeyboardInput { device_id, input, is_synthetic }),
WindowEvent::CursorMoved { device_id, position, modifiers } =>
Ok(InputEvent::CursorMoved { device_id, position, modifiers }),
WindowEvent::CursorEntered { device_id } => Ok(InputEvent::CursorEntered { device_id }),
WindowEvent::CursorLeft { device_id } => Ok(InputEvent::CursorLeft { device_id }),
WindowEvent::MouseWheel { device_id, delta, phase, modifiers } =>
Ok(InputEvent::MouseWheel { device_id, delta, phase, modifiers }),
WindowEvent::MouseInput { device_id, state, button, modifiers } =>
Ok(InputEvent::MouseInput { device_id, state, button, modifiers }),
WindowEvent::TouchpadMagnify { device_id, delta, phase } =>
Ok(InputEvent::TouchpadMagnify { device_id, delta, phase }),
WindowEvent::SmartMagnify { device_id } => Ok(InputEvent::SmartMagnify { device_id }),
WindowEvent::TouchpadRotate { device_id, delta, phase } =>
Ok(InputEvent::TouchpadRotate { device_id, delta, phase }),
WindowEvent::TouchpadPressure { device_id, pressure, stage } =>
Ok(InputEvent::TouchpadPressure { device_id, pressure, stage }),
WindowEvent::AxisMotion { device_id, axis, value } =>
Ok(InputEvent::AxisMotion { device_id, axis, value }),
WindowEvent::Touch(t) => Ok(InputEvent::Touch(t)),
_ => Err(InputEventConversionError::FromError(value))
}
}
} */

62
src/main.rs Normal file
View File

@ -0,0 +1,62 @@
mod system;
mod game;
mod renderer;
//mod ecs;
mod input_event;
use game::Game;
use shipyard::{Component, World, EntitiesViewMut, ViewMut, Get, Workload};
use shipyard::IntoWorkload;
//use ecs::{registry::Registry, component::Component};
#[derive(Component, Debug)]
struct Point2d {
x: i32,
y: i32,
}
impl Point2d {
pub fn new(x: i32, y: i32) -> Self {
Self {
x,
y,
}
}
}
#[async_std::main]
async fn main() {
let mut world = World::new();
let entity = world.add_entity(Point2d::new(10, 10));
let entity2 = world.add_entity(Point2d::new(846, 2188));
world.run(
|mut vm_point: ViewMut<Point2d>| {
let mut p = (&mut vm_point).get(entity).unwrap();
println!("Point: {:?}", p);
p.x = 100;
println!("Point after: {:?}", p);
drop(p);
let mut p = (&mut vm_point).get(entity2).unwrap();
println!("Point: {:?}", p);
p.x = 1000;
println!("Point after: {:?}", p);
//let mut vm_
//println!("Point2d: {:?}", vm_point.get);
/* let empty_entity = entities.add_entity((), ());
let single_component = entities.add_entity(&mut vm_pos, Pos::new());
let multiple_components =
entities.add_entity((&mut vm_pos, &mut vm_vel), (Pos::new(), Vel::new())); */
},
);
let mut game = Game::initialize().await;
game.run().await;
}

204
src/renderer.rs Normal file
View File

@ -0,0 +1,204 @@
use std::sync::Arc;
use async_trait::async_trait;
//use winit::{window::Window, event::WindowEvent};
//use winit::window::Window;
use winit::{window::Window, event::WindowEvent};
#[async_trait]
pub trait Renderer {
//fn new(surface: wgpu::Surface, config: wgpu::SurfaceConfiguration, device: wgpu::Device)
//async fn create_with_window(window: Window) -> Self;
async fn render(&mut self) -> Result<(), wgpu::SurfaceError>;
async fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>);
fn size(&self) -> winit::dpi::PhysicalSize<u32>;
}
pub struct BasicRenderer {
pub surface: wgpu::Surface,
pub device: wgpu::Device,
pub queue: wgpu::Queue,
pub config: wgpu::SurfaceConfiguration,
pub size: winit::dpi::PhysicalSize<u32>,
pub window: Arc<Window>,
pub clear_color: wgpu::Color,
pub render_pipeline: wgpu::RenderPipeline,
}
impl BasicRenderer {
pub async fn create_with_window(window: Arc<Window>) -> BasicRenderer {
let size = window.inner_size();
// Get a GPU handle
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
dx12_shader_compiler: Default::default(),
});
// This should be safe since surface will live as long as the window that created it
let surface = unsafe { instance.create_surface(window.as_ref()) }.unwrap();
let adapter = instance.request_adapter(
&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
compatible_surface: Some(&surface),
force_fallback_adapter: false,
},
).await.unwrap();
let (device, queue) = adapter.request_device(
&wgpu::DeviceDescriptor {
features: wgpu::Features::empty(),
// WebGL does not support all wgpu features.
// Not sure if the engine will ever completely support WASM,
// but its here just in case
limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
},
label: None,
},
None,
).await.unwrap();
let surface_caps = surface.get_capabilities(&adapter);
let surface_format = surface_caps.formats.iter()
.copied()
.filter(|f| f.describe().srgb)
.next()
.unwrap_or(surface_caps.formats[0]);
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface_format,
width: size.width,
height: size.height,
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
};
surface.configure(&device, &config);
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Shader"),
source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
});
// Create a render pipeline. At some point this will be created by something else and given to the renderer
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: config.format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
// Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE
polygon_mode: wgpu::PolygonMode::Fill,
// Requires Features::DEPTH_CLIP_CONTROL
unclipped_depth: false,
// Requires Features::CONSERVATIVE_RASTERIZATION
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
});
Self {
window,
surface,
device,
queue,
config,
size,
clear_color: wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
},
render_pipeline
}
}
}
#[async_trait]
impl Renderer for BasicRenderer {
async fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
let output = self.surface.get_current_texture()?;
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Basic Renderer's Encoder")
});
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Basic Renderer's Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(self.clear_color),
store: true,
},
})],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&self.render_pipeline);
render_pass.draw(0..3, 0..1);
}
self.queue.submit(std::iter::once(encoder.finish()));
output.present();
Ok(())
}
async fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
if new_size.width > 0 && new_size.height > 0 {
self.size = new_size;
self.config.width = new_size.width;
self.config.height = new_size.height;
self.surface.configure(&self.device, &self.config);
}
}
fn size(&self) -> winit::dpi::PhysicalSize<u32> {
self.size
}
}

23
src/shader.wgsl Normal file
View File

@ -0,0 +1,23 @@
// Vertex shader
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
};
@vertex
fn vs_main(
@builtin(vertex_index) in_vertex_index: u32,
) -> VertexOutput {
var out: VertexOutput;
let x = f32(1 - i32(in_vertex_index)) * 0.5;
let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5;
out.clip_position = vec4<f32>(x, y, 0.0, 1.0);
return out;
}
// Fragment shader
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(0.3, 0.2, 0.1, 1.0);
}

10
src/system.rs Normal file
View File

@ -0,0 +1,10 @@
use std::time::Duration;
trait System {
fn new();
fn update(dt: Duration);
fn input_update(dt: Duration);
fn render(interopolate_alpha: f32, dt: Duration);
fn destroy();
}