Create window, handle simple input
This commit is contained in:
commit
bbc7c8e283
|
@ -0,0 +1 @@
|
||||||
|
/target
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 SeanOMik
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod entity;
|
||||||
|
pub mod registry;
|
||||||
|
pub mod component;
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} */
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
Loading…
Reference in New Issue